diff options
69 files changed, 1677 insertions, 238 deletions
diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h index 0eb3e439ac..0d65bc7405 100644 --- a/compiler/driver/compiler_driver-inl.h +++ b/compiler/driver/compiler_driver-inl.h @@ -305,11 +305,31 @@ inline int CompilerDriver::IsFastInvoke( MethodReference* target_method, const MethodReference* devirt_target, uintptr_t* direct_code, uintptr_t* direct_method) { // Don't try to fast-path if we don't understand the caller's class. + // Referrer_class is the class that this invoke is contained in. if (UNLIKELY(referrer_class == nullptr)) { return 0; } - mirror::Class* methods_class = resolved_method->GetDeclaringClass(); - if (UNLIKELY(!referrer_class->CanAccessResolvedMethod(methods_class, resolved_method, + StackHandleScope<2> hs(soa.Self()); + // Methods_class is the class refered to by the class_idx field of the methodId the method_idx is + // pointing to. + // For example in + // .class LABC; + // .super LDEF; + // .method hi()V + // ... + // invoke-super {p0}, LDEF;->hi()V + // ... + // .end method + // the referrer_class is 'ABC' and the methods_class is DEF. Note that the methods class is 'DEF' + // even if 'DEF' inherits the method from it's superclass. + Handle<mirror::Class> methods_class(hs.NewHandle(mUnit->GetClassLinker()->ResolveType( + *target_method->dex_file, + target_method->dex_file->GetMethodId(target_method->dex_method_index).class_idx_, + dex_cache, + class_loader))); + DCHECK(methods_class.Get() != nullptr); + mirror::Class* methods_declaring_class = resolved_method->GetDeclaringClass(); + if (UNLIKELY(!referrer_class->CanAccessResolvedMethod(methods_declaring_class, resolved_method, dex_cache.Get(), target_method->dex_method_index))) { return 0; @@ -318,18 +338,31 @@ inline int CompilerDriver::IsFastInvoke( // overridden (ie is final). const bool same_dex_file = target_method->dex_file == mUnit->GetDexFile(); bool can_sharpen_virtual_based_on_type = same_dex_file && - (*invoke_type == kVirtual) && (resolved_method->IsFinal() || methods_class->IsFinal()); + (*invoke_type == kVirtual) && (resolved_method->IsFinal() || + methods_declaring_class->IsFinal()); // For invoke-super, ensure the vtable index will be correct to dispatch in the vtable of // the super class. const size_t pointer_size = InstructionSetPointerSize(GetInstructionSet()); - bool can_sharpen_super_based_on_type = same_dex_file && (*invoke_type == kSuper) && - (referrer_class != methods_class) && referrer_class->IsSubClass(methods_class) && - resolved_method->GetMethodIndex() < methods_class->GetVTableLength() && - (methods_class->GetVTableEntry( + // TODO We should be able to sharpen if we are going into the boot image as well. + bool can_sharpen_super_based_on_type = same_dex_file && + (*invoke_type == kSuper) && + !methods_class->IsInterface() && + (referrer_class != methods_declaring_class) && + referrer_class->IsSubClass(methods_declaring_class) && + resolved_method->GetMethodIndex() < methods_declaring_class->GetVTableLength() && + (methods_declaring_class->GetVTableEntry( resolved_method->GetMethodIndex(), pointer_size) == resolved_method) && resolved_method->IsInvokable(); + // TODO We should be able to sharpen if we are going into the boot image as well. + bool can_sharpen_interface_super_based_on_type = same_dex_file && + (*invoke_type == kSuper) && + methods_class->IsInterface() && + methods_class->IsAssignableFrom(referrer_class) && + resolved_method->IsInvokable(); - if (can_sharpen_virtual_based_on_type || can_sharpen_super_based_on_type) { + if (can_sharpen_virtual_based_on_type || + can_sharpen_super_based_on_type || + can_sharpen_interface_super_based_on_type) { // Sharpen a virtual call into a direct call. The method_idx is into referrer's // dex cache, check that this resolved method is where we expect it. CHECK_EQ(target_method->dex_file, mUnit->GetDexFile()); @@ -363,7 +396,6 @@ inline int CompilerDriver::IsFastInvoke( *devirt_target->dex_file, devirt_target->dex_method_index, dex_cache, class_loader, nullptr, kVirtual); } else { - StackHandleScope<1> hs(soa.Self()); auto target_dex_cache(hs.NewHandle(class_linker->RegisterDexFile( *devirt_target->dex_file, class_linker->GetOrCreateAllocatorForClassLoader(class_loader.Get())))); diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h index 39372b36b8..a220959288 100644 --- a/compiler/driver/compiler_options.h +++ b/compiler/driver/compiler_options.h @@ -116,6 +116,10 @@ class CompilerOptions FINAL { return compiler_filter_ == CompilerOptions::kVerifyNone; } + bool IsExtractOnly() const { + return compiler_filter_ == CompilerOptions::kVerifyAtRuntime; + } + size_t GetHugeMethodThreshold() const { return huge_method_threshold_; } diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index 6abc6fae8a..60dfcfb508 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -2274,13 +2274,12 @@ const ImageWriter::ImageInfo& ImageWriter::GetImageInfo(size_t index) const { return GetConstImageInfo(oat_filenames_[index]); } -void ImageWriter::UpdateOatFile(const char* oat_filename) { - std::unique_ptr<File> oat_file(OS::OpenFileForReading(oat_filename)); +void ImageWriter::UpdateOatFile(File* oat_file, const char* oat_filename) { DCHECK(oat_file != nullptr); - size_t oat_loaded_size = 0; - size_t oat_data_offset = 0; - ElfWriter::GetOatElfInformation(oat_file.get(), &oat_loaded_size, &oat_data_offset); - + if (compile_app_image_) { + CHECK_EQ(oat_filenames_.size(), 1u) << "App image should have no next image."; + return; + } ImageInfo& cur_image_info = GetImageInfo(oat_filename); // Update the oat_offset of the next image info. @@ -2289,6 +2288,9 @@ void ImageWriter::UpdateOatFile(const char* oat_filename) { it++; if (it != oat_filenames_.end()) { + size_t oat_loaded_size = 0; + size_t oat_data_offset = 0; + ElfWriter::GetOatElfInformation(oat_file, &oat_loaded_size, &oat_data_offset); // There is a following one. ImageInfo& next_image_info = GetImageInfo(*it); next_image_info.oat_offset_ = cur_image_info.oat_offset_ + oat_loaded_size; diff --git a/compiler/image_writer.h b/compiler/image_writer.h index 622eb1985b..9371d9ffa9 100644 --- a/compiler/image_writer.h +++ b/compiler/image_writer.h @@ -123,7 +123,7 @@ class ImageWriter FINAL { // Update the oat size for the given oat file. This will make the oat_offset for the next oat // file valid. - void UpdateOatFile(const char* oat_filename); + void UpdateOatFile(File* oat_file, const char* oat_filename); private: bool AllocMemory(); diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index 37218139fb..c7430e7eb6 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -798,7 +798,7 @@ static InvokeType GetInvokeTypeFromOpCode(Instruction::Code opcode) { ArtMethod* HGraphBuilder::ResolveMethod(uint16_t method_idx, InvokeType invoke_type) { ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<2> hs(soa.Self()); + StackHandleScope<3> hs(soa.Self()); ClassLinker* class_linker = dex_compilation_unit_->GetClassLinker(); Handle<mirror::ClassLoader> class_loader(hs.NewHandle( @@ -833,31 +833,56 @@ ArtMethod* HGraphBuilder::ResolveMethod(uint16_t method_idx, InvokeType invoke_t } // We have to special case the invoke-super case, as ClassLinker::ResolveMethod does not. - // We need to look at the referrer's super class vtable. + // We need to look at the referrer's super class vtable. We need to do this to know if we need to + // make this an invoke-unresolved to handle cross-dex invokes or abstract super methods, both of + // which require runtime handling. if (invoke_type == kSuper) { if (compiling_class.Get() == nullptr) { - // Invoking a super method requires knowing the actual super class. If we did not resolve - // the compiling method's declaring class (which only happens for ahead of time compilation), - // bail out. + // We could not determine the method's class we need to wait until runtime. DCHECK(Runtime::Current()->IsAotCompiler()); return nullptr; } - uint16_t vtable_index = resolved_method->GetMethodIndex(); - ArtMethod* actual_method = compiling_class->GetSuperClass()->GetVTableEntry( - vtable_index, class_linker->GetImagePointerSize()); - if (actual_method != resolved_method && - !IsSameDexFile(*actual_method->GetDexFile(), *dex_compilation_unit_->GetDexFile())) { - // TODO: The actual method could still be referenced in the current dex file, so we - // could try locating it. - // TODO: Remove the dex_file restriction. - return nullptr; - } - if (!actual_method->IsInvokable()) { - // Fail if the actual method cannot be invoked. Otherwise, the runtime resolution stub - // could resolve the callee to the wrong method. + ArtMethod* current_method = graph_->GetArtMethod(); + DCHECK(current_method != nullptr); + Handle<mirror::Class> methods_class(hs.NewHandle( + dex_compilation_unit_->GetClassLinker()->ResolveReferencedClassOfMethod(Thread::Current(), + method_idx, + current_method))); + if (methods_class.Get() == nullptr) { + // Invoking a super method requires knowing the actual super class. If we did not resolve + // the compiling method's declaring class (which only happens for ahead of time + // compilation), bail out. + DCHECK(Runtime::Current()->IsAotCompiler()); return nullptr; + } else { + ArtMethod* actual_method; + if (methods_class->IsInterface()) { + actual_method = methods_class->FindVirtualMethodForInterfaceSuper( + resolved_method, class_linker->GetImagePointerSize()); + } else { + uint16_t vtable_index = resolved_method->GetMethodIndex(); + actual_method = compiling_class->GetSuperClass()->GetVTableEntry( + vtable_index, class_linker->GetImagePointerSize()); + } + if (actual_method != resolved_method && + !IsSameDexFile(*actual_method->GetDexFile(), *dex_compilation_unit_->GetDexFile())) { + // The back-end code generator relies on this check in order to ensure that it will not + // attempt to read the dex_cache with a dex_method_index that is not from the correct + // dex_file. If we didn't do this check then the dex_method_index will not be updated in the + // builder, which means that the code-generator (and compiler driver during sharpening and + // inliner, maybe) might invoke an incorrect method. + // TODO: The actual method could still be referenced in the current dex file, so we + // could try locating it. + // TODO: Remove the dex_file restriction. + return nullptr; + } + if (!actual_method->IsInvokable()) { + // Fail if the actual method cannot be invoked. Otherwise, the runtime resolution stub + // could resolve the callee to the wrong method. + return nullptr; + } + resolved_method = actual_method; } - resolved_method = actual_method; } // Check for incompatible class changes. The class linker has a fast path for @@ -923,7 +948,7 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction, ArtMethod* resolved_method = ResolveMethod(method_idx, invoke_type); - if (resolved_method == nullptr) { + if (UNLIKELY(resolved_method == nullptr)) { MaybeRecordStat(MethodCompilationStat::kUnresolvedMethod); HInvoke* invoke = new (arena_) HInvokeUnresolved(arena_, number_of_arguments, @@ -943,7 +968,6 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction, // Potential class initialization check, in the case of a static method call. HClinitCheck* clinit_check = nullptr; HInvoke* invoke = nullptr; - if (invoke_type == kDirect || invoke_type == kStatic || invoke_type == kSuper) { // By default, consider that the called method implicitly requires // an initialization check of its declaring method. diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 86ffb0f70d..6795488769 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -6447,8 +6447,17 @@ void InstructionCodeGeneratorX86_64::VisitPackedSwitch(HPackedSwitch* switch_ins __ jmp(temp_reg); } +void CodeGeneratorX86_64::Load32BitValue(CpuRegister dest, int32_t value) { + if (value == 0) { + __ xorl(dest, dest); + } else { + __ movl(dest, Immediate(value)); + } +} + void CodeGeneratorX86_64::Load64BitValue(CpuRegister dest, int64_t value) { if (value == 0) { + // Clears upper bits too. __ xorl(dest, dest); } else if (value > 0 && IsInt<32>(value)) { // We can use a 32 bit move, as it will zero-extend and is one byte shorter. diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index 96265902ba..318087eb9c 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -478,8 +478,10 @@ class CodeGeneratorX86_64 : public CodeGenerator { Address LiteralInt32Address(int32_t v); Address LiteralInt64Address(int64_t v); - // Load a 64 bit value into a register in the most efficient manner. + // Load a 32/64 bit value into a register in the most efficient manner. + void Load32BitValue(CpuRegister dest, int32_t value); void Load64BitValue(CpuRegister dest, int64_t value); + Address LiteralCaseTable(HPackedSwitch* switch_instr); // Store a 64 bit value into a DoubleStackSlot in the most efficient manner. diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index d5ed58530d..98d041a5fc 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -275,6 +275,45 @@ void IntrinsicCodeGeneratorARM64::VisitShortReverseBytes(HInvoke* invoke) { GenReverseBytes(invoke->GetLocations(), Primitive::kPrimShort, GetVIXLAssembler()); } +static void CreateIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) { + LocationSummary* locations = new (arena) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); +} + +static void GenCompare(LocationSummary* locations, bool is_long, vixl::MacroAssembler* masm) { + Location op1 = locations->InAt(0); + Location op2 = locations->InAt(1); + Location out = locations->Out(); + + Register op1_reg = is_long ? XRegisterFrom(op1) : WRegisterFrom(op1); + Register op2_reg = is_long ? XRegisterFrom(op2) : WRegisterFrom(op2); + Register out_reg = WRegisterFrom(out); + + __ Cmp(op1_reg, op2_reg); + __ Cset(out_reg, gt); // out == +1 if GT or 0 otherwise + __ Cinv(out_reg, out_reg, lt); // out == -1 if LT or unchanged otherwise +} + +void IntrinsicLocationsBuilderARM64::VisitIntegerCompare(HInvoke* invoke) { + CreateIntIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorARM64::VisitIntegerCompare(HInvoke* invoke) { + GenCompare(invoke->GetLocations(), /* is_long */ false, GetVIXLAssembler()); +} + +void IntrinsicLocationsBuilderARM64::VisitLongCompare(HInvoke* invoke) { + CreateIntIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorARM64::VisitLongCompare(HInvoke* invoke) { + GenCompare(invoke->GetLocations(), /* is_long */ true, GetVIXLAssembler()); +} + static void GenNumberOfLeadingZeros(LocationSummary* locations, Primitive::Type type, vixl::MacroAssembler* masm) { @@ -504,15 +543,6 @@ static void GenMinMax(LocationSummary* locations, __ Csel(out_reg, op1_reg, op2_reg, is_min ? lt : gt); } -static void CreateIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) { - LocationSummary* locations = new (arena) LocationSummary(invoke, - LocationSummary::kNoCall, - kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); - locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); -} - void IntrinsicLocationsBuilderARM64::VisitMathMinIntInt(HInvoke* invoke) { CreateIntIntToIntLocations(arena_, invoke); } @@ -1507,8 +1537,6 @@ UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite) UNIMPLEMENTED_INTRINSIC(FloatIsNaN) UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) -UNIMPLEMENTED_INTRINSIC(IntegerCompare) -UNIMPLEMENTED_INTRINSIC(LongCompare) UNIMPLEMENTED_INTRINSIC(IntegerHighestOneBit) UNIMPLEMENTED_INTRINSIC(LongHighestOneBit) UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit) diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc index 6ccc5d1e01..51fa514cb6 100644 --- a/compiler/optimizing/intrinsics_x86_64.cc +++ b/compiler/optimizing/intrinsics_x86_64.cc @@ -2302,7 +2302,7 @@ static void SwapBits(CpuRegister reg, CpuRegister temp, int32_t shift, int32_t m } void IntrinsicCodeGeneratorX86_64::VisitIntegerReverse(HInvoke* invoke) { - X86_64Assembler* assembler = down_cast<X86_64Assembler*>(codegen_->GetAssembler()); + X86_64Assembler* assembler = GetAssembler(); LocationSummary* locations = invoke->GetLocations(); CpuRegister reg = locations->InAt(0).AsRegister<CpuRegister>(); @@ -2346,7 +2346,7 @@ static void SwapBits64(CpuRegister reg, CpuRegister temp, CpuRegister temp_mask, } void IntrinsicCodeGeneratorX86_64::VisitLongReverse(HInvoke* invoke) { - X86_64Assembler* assembler = down_cast<X86_64Assembler*>(codegen_->GetAssembler()); + X86_64Assembler* assembler = GetAssembler(); LocationSummary* locations = invoke->GetLocations(); CpuRegister reg = locations->InAt(0).AsRegister<CpuRegister>(); @@ -2382,7 +2382,10 @@ static void CreateBitCountLocations( locations->SetOut(Location::RequiresRegister()); } -static void GenBitCount(X86_64Assembler* assembler, HInvoke* invoke, bool is_long) { +static void GenBitCount(X86_64Assembler* assembler, + CodeGeneratorX86_64* codegen, + HInvoke* invoke, + bool is_long) { LocationSummary* locations = invoke->GetLocations(); Location src = locations->InAt(0); CpuRegister out = locations->Out().AsRegister<CpuRegister>(); @@ -2393,11 +2396,7 @@ static void GenBitCount(X86_64Assembler* assembler, HInvoke* invoke, bool is_lon value = is_long ? POPCOUNT(static_cast<uint64_t>(value)) : POPCOUNT(static_cast<uint32_t>(value)); - if (value == 0) { - __ xorl(out, out); - } else { - __ movl(out, Immediate(value)); - } + codegen->Load32BitValue(out, value); return; } @@ -2421,7 +2420,7 @@ void IntrinsicLocationsBuilderX86_64::VisitIntegerBitCount(HInvoke* invoke) { } void IntrinsicCodeGeneratorX86_64::VisitIntegerBitCount(HInvoke* invoke) { - GenBitCount(GetAssembler(), invoke, /* is_long */ false); + GenBitCount(GetAssembler(), codegen_, invoke, /* is_long */ false); } void IntrinsicLocationsBuilderX86_64::VisitLongBitCount(HInvoke* invoke) { @@ -2429,7 +2428,190 @@ void IntrinsicLocationsBuilderX86_64::VisitLongBitCount(HInvoke* invoke) { } void IntrinsicCodeGeneratorX86_64::VisitLongBitCount(HInvoke* invoke) { - GenBitCount(GetAssembler(), invoke, /* is_long */ true); + GenBitCount(GetAssembler(), codegen_, invoke, /* is_long */ true); +} + +static void CreateCompareLocations(ArenaAllocator* arena, HInvoke* invoke) { + LocationSummary* locations = new (arena) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister()); +} + +static void GenCompare(X86_64Assembler* assembler, HInvoke* invoke, bool is_long) { + LocationSummary* locations = invoke->GetLocations(); + CpuRegister src1 = locations->InAt(0).AsRegister<CpuRegister>(); + CpuRegister src2 = locations->InAt(1).AsRegister<CpuRegister>(); + CpuRegister out = locations->Out().AsRegister<CpuRegister>(); + + NearLabel is_lt, done; + + __ xorl(out, out); + + if (is_long) { + __ cmpq(src1, src2); + } else { + __ cmpl(src1, src2); + } + __ j(kEqual, &done); + __ j(kLess, &is_lt); + + __ movl(out, Immediate(1)); + __ jmp(&done); + + __ Bind(&is_lt); + __ movl(out, Immediate(-1)); + + __ Bind(&done); +} + +void IntrinsicLocationsBuilderX86_64::VisitIntegerCompare(HInvoke* invoke) { + CreateCompareLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorX86_64::VisitIntegerCompare(HInvoke* invoke) { + GenCompare(GetAssembler(), invoke, /* is_long */ false); +} + +void IntrinsicLocationsBuilderX86_64::VisitLongCompare(HInvoke* invoke) { + CreateCompareLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorX86_64::VisitLongCompare(HInvoke* invoke) { + GenCompare(GetAssembler(), invoke, /* is_long */ true); +} + +static void CreateOneBitLocations(ArenaAllocator* arena, HInvoke* invoke, bool is_high) { + LocationSummary* locations = new (arena) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::Any()); + locations->SetOut(Location::RequiresRegister()); + locations->AddTemp(is_high ? Location::RegisterLocation(RCX) // needs CL + : Location::RequiresRegister()); // any will do +} + +static void GenOneBit(X86_64Assembler* assembler, + CodeGeneratorX86_64* codegen, + HInvoke* invoke, + bool is_high, bool is_long) { + LocationSummary* locations = invoke->GetLocations(); + Location src = locations->InAt(0); + CpuRegister out = locations->Out().AsRegister<CpuRegister>(); + + if (invoke->InputAt(0)->IsConstant()) { + // Evaluate this at compile time. + int64_t value = Int64FromConstant(invoke->InputAt(0)->AsConstant()); + if (value == 0) { + __ xorl(out, out); // Clears upper bits too. + return; + } + // Nonzero value. + if (is_high) { + value = is_long ? 63 - CLZ(static_cast<uint64_t>(value)) + : 31 - CLZ(static_cast<uint32_t>(value)); + } else { + value = is_long ? CTZ(static_cast<uint64_t>(value)) + : CTZ(static_cast<uint32_t>(value)); + } + if (is_long) { + codegen->Load64BitValue(out, 1L << value); + } else { + codegen->Load32BitValue(out, 1 << value); + } + return; + } + + // Handle the non-constant cases. + CpuRegister tmp = locations->GetTemp(0).AsRegister<CpuRegister>(); + if (is_high) { + // Use architectural support: basically 1 << bsr. + if (src.IsRegister()) { + if (is_long) { + __ bsrq(tmp, src.AsRegister<CpuRegister>()); + } else { + __ bsrl(tmp, src.AsRegister<CpuRegister>()); + } + } else if (is_long) { + DCHECK(src.IsDoubleStackSlot()); + __ bsrq(tmp, Address(CpuRegister(RSP), src.GetStackIndex())); + } else { + DCHECK(src.IsStackSlot()); + __ bsrl(tmp, Address(CpuRegister(RSP), src.GetStackIndex())); + } + // BSR sets ZF if the input was zero. + NearLabel is_zero, done; + __ j(kEqual, &is_zero); + __ movl(out, Immediate(1)); // Clears upper bits too. + if (is_long) { + __ shlq(out, tmp); + } else { + __ shll(out, tmp); + } + __ jmp(&done); + __ Bind(&is_zero); + __ xorl(out, out); // Clears upper bits too. + __ Bind(&done); + } else { + // Copy input into temporary. + if (src.IsRegister()) { + if (is_long) { + __ movq(tmp, src.AsRegister<CpuRegister>()); + } else { + __ movl(tmp, src.AsRegister<CpuRegister>()); + } + } else if (is_long) { + DCHECK(src.IsDoubleStackSlot()); + __ movq(tmp, Address(CpuRegister(RSP), src.GetStackIndex())); + } else { + DCHECK(src.IsStackSlot()); + __ movl(tmp, Address(CpuRegister(RSP), src.GetStackIndex())); + } + // Do the bit twiddling: basically tmp & -tmp; + if (is_long) { + __ movq(out, tmp); + __ negq(tmp); + __ andq(out, tmp); + } else { + __ movl(out, tmp); + __ negl(tmp); + __ andl(out, tmp); + } + } +} + +void IntrinsicLocationsBuilderX86_64::VisitIntegerHighestOneBit(HInvoke* invoke) { + CreateOneBitLocations(arena_, invoke, /* is_high */ true); +} + +void IntrinsicCodeGeneratorX86_64::VisitIntegerHighestOneBit(HInvoke* invoke) { + GenOneBit(GetAssembler(), codegen_, invoke, /* is_high */ true, /* is_long */ false); +} + +void IntrinsicLocationsBuilderX86_64::VisitLongHighestOneBit(HInvoke* invoke) { + CreateOneBitLocations(arena_, invoke, /* is_high */ true); +} + +void IntrinsicCodeGeneratorX86_64::VisitLongHighestOneBit(HInvoke* invoke) { + GenOneBit(GetAssembler(), codegen_, invoke, /* is_high */ true, /* is_long */ true); +} + +void IntrinsicLocationsBuilderX86_64::VisitIntegerLowestOneBit(HInvoke* invoke) { + CreateOneBitLocations(arena_, invoke, /* is_high */ false); +} + +void IntrinsicCodeGeneratorX86_64::VisitIntegerLowestOneBit(HInvoke* invoke) { + GenOneBit(GetAssembler(), codegen_, invoke, /* is_high */ false, /* is_long */ false); +} + +void IntrinsicLocationsBuilderX86_64::VisitLongLowestOneBit(HInvoke* invoke) { + CreateOneBitLocations(arena_, invoke, /* is_high */ false); +} + +void IntrinsicCodeGeneratorX86_64::VisitLongLowestOneBit(HInvoke* invoke) { + GenOneBit(GetAssembler(), codegen_, invoke, /* is_high */ false, /* is_long */ true); } static void CreateLeadingZeroLocations(ArenaAllocator* arena, HInvoke* invoke) { @@ -2440,7 +2622,9 @@ static void CreateLeadingZeroLocations(ArenaAllocator* arena, HInvoke* invoke) { locations->SetOut(Location::RequiresRegister()); } -static void GenLeadingZeros(X86_64Assembler* assembler, HInvoke* invoke, bool is_long) { +static void GenLeadingZeros(X86_64Assembler* assembler, + CodeGeneratorX86_64* codegen, + HInvoke* invoke, bool is_long) { LocationSummary* locations = invoke->GetLocations(); Location src = locations->InAt(0); CpuRegister out = locations->Out().AsRegister<CpuRegister>(); @@ -2454,11 +2638,7 @@ static void GenLeadingZeros(X86_64Assembler* assembler, HInvoke* invoke, bool is } else { value = is_long ? CLZ(static_cast<uint64_t>(value)) : CLZ(static_cast<uint32_t>(value)); } - if (value == 0) { - __ xorl(out, out); - } else { - __ movl(out, Immediate(value)); - } + codegen->Load32BitValue(out, value); return; } @@ -2497,8 +2677,7 @@ void IntrinsicLocationsBuilderX86_64::VisitIntegerNumberOfLeadingZeros(HInvoke* } void IntrinsicCodeGeneratorX86_64::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) { - X86_64Assembler* assembler = down_cast<X86_64Assembler*>(codegen_->GetAssembler()); - GenLeadingZeros(assembler, invoke, /* is_long */ false); + GenLeadingZeros(GetAssembler(), codegen_, invoke, /* is_long */ false); } void IntrinsicLocationsBuilderX86_64::VisitLongNumberOfLeadingZeros(HInvoke* invoke) { @@ -2506,8 +2685,7 @@ void IntrinsicLocationsBuilderX86_64::VisitLongNumberOfLeadingZeros(HInvoke* inv } void IntrinsicCodeGeneratorX86_64::VisitLongNumberOfLeadingZeros(HInvoke* invoke) { - X86_64Assembler* assembler = down_cast<X86_64Assembler*>(codegen_->GetAssembler()); - GenLeadingZeros(assembler, invoke, /* is_long */ true); + GenLeadingZeros(GetAssembler(), codegen_, invoke, /* is_long */ true); } static void CreateTrailingZeroLocations(ArenaAllocator* arena, HInvoke* invoke) { @@ -2518,7 +2696,9 @@ static void CreateTrailingZeroLocations(ArenaAllocator* arena, HInvoke* invoke) locations->SetOut(Location::RequiresRegister()); } -static void GenTrailingZeros(X86_64Assembler* assembler, HInvoke* invoke, bool is_long) { +static void GenTrailingZeros(X86_64Assembler* assembler, + CodeGeneratorX86_64* codegen, + HInvoke* invoke, bool is_long) { LocationSummary* locations = invoke->GetLocations(); Location src = locations->InAt(0); CpuRegister out = locations->Out().AsRegister<CpuRegister>(); @@ -2532,11 +2712,7 @@ static void GenTrailingZeros(X86_64Assembler* assembler, HInvoke* invoke, bool i } else { value = is_long ? CTZ(static_cast<uint64_t>(value)) : CTZ(static_cast<uint32_t>(value)); } - if (value == 0) { - __ xorl(out, out); - } else { - __ movl(out, Immediate(value)); - } + codegen->Load32BitValue(out, value); return; } @@ -2570,8 +2746,7 @@ void IntrinsicLocationsBuilderX86_64::VisitIntegerNumberOfTrailingZeros(HInvoke* } void IntrinsicCodeGeneratorX86_64::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) { - X86_64Assembler* assembler = down_cast<X86_64Assembler*>(codegen_->GetAssembler()); - GenTrailingZeros(assembler, invoke, /* is_long */ false); + GenTrailingZeros(GetAssembler(), codegen_, invoke, /* is_long */ false); } void IntrinsicLocationsBuilderX86_64::VisitLongNumberOfTrailingZeros(HInvoke* invoke) { @@ -2579,8 +2754,75 @@ void IntrinsicLocationsBuilderX86_64::VisitLongNumberOfTrailingZeros(HInvoke* in } void IntrinsicCodeGeneratorX86_64::VisitLongNumberOfTrailingZeros(HInvoke* invoke) { - X86_64Assembler* assembler = down_cast<X86_64Assembler*>(codegen_->GetAssembler()); - GenTrailingZeros(assembler, invoke, /* is_long */ true); + GenTrailingZeros(GetAssembler(), codegen_, invoke, /* is_long */ true); +} + +static void CreateSignLocations(ArenaAllocator* arena, HInvoke* invoke) { + LocationSummary* locations = new (arena) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::Any()); + locations->SetOut(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); // Need a writeable register. +} + +static void GenSign(X86_64Assembler* assembler, + CodeGeneratorX86_64* codegen, + HInvoke* invoke, bool is_long) { + LocationSummary* locations = invoke->GetLocations(); + Location src = locations->InAt(0); + CpuRegister out = locations->Out().AsRegister<CpuRegister>(); + + if (invoke->InputAt(0)->IsConstant()) { + // Evaluate this at compile time. + int64_t value = Int64FromConstant(invoke->InputAt(0)->AsConstant()); + codegen->Load32BitValue(out, value == 0 ? 0 : (value > 0 ? 1 : -1)); + return; + } + + // Copy input into temporary. + CpuRegister tmp = locations->GetTemp(0).AsRegister<CpuRegister>(); + if (src.IsRegister()) { + if (is_long) { + __ movq(tmp, src.AsRegister<CpuRegister>()); + } else { + __ movl(tmp, src.AsRegister<CpuRegister>()); + } + } else if (is_long) { + DCHECK(src.IsDoubleStackSlot()); + __ movq(tmp, Address(CpuRegister(RSP), src.GetStackIndex())); + } else { + DCHECK(src.IsStackSlot()); + __ movl(tmp, Address(CpuRegister(RSP), src.GetStackIndex())); + } + + // Do the bit twiddling: basically tmp >> 63/31 | -tmp >>> 63/31 for long/int. + if (is_long) { + __ movq(out, tmp); + __ sarq(out, Immediate(63)); + __ negq(tmp); + __ shrq(tmp, Immediate(63)); + __ orq(out, tmp); + } else { + __ movl(out, tmp); + __ sarl(out, Immediate(31)); + __ negl(tmp); + __ shrl(tmp, Immediate(31)); + __ orl(out, tmp); + } +} + +void IntrinsicLocationsBuilderX86_64::VisitIntegerSignum(HInvoke* invoke) { + CreateSignLocations(arena_, invoke); +} +void IntrinsicCodeGeneratorX86_64::VisitIntegerSignum(HInvoke* invoke) { + GenSign(GetAssembler(), codegen_, invoke, /* is_long */ false); +} +void IntrinsicLocationsBuilderX86_64::VisitLongSignum(HInvoke* invoke) { + CreateSignLocations(arena_, invoke); +} +void IntrinsicCodeGeneratorX86_64::VisitLongSignum(HInvoke* invoke) { + GenSign(GetAssembler(), codegen_, invoke, /* is_long */ true); } // Unimplemented intrinsics. @@ -2598,15 +2840,6 @@ UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite) UNIMPLEMENTED_INTRINSIC(FloatIsNaN) UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) -UNIMPLEMENTED_INTRINSIC(IntegerCompare) -UNIMPLEMENTED_INTRINSIC(LongCompare) -UNIMPLEMENTED_INTRINSIC(IntegerHighestOneBit) -UNIMPLEMENTED_INTRINSIC(LongHighestOneBit) -UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit) -UNIMPLEMENTED_INTRINSIC(LongLowestOneBit) -UNIMPLEMENTED_INTRINSIC(IntegerSignum) -UNIMPLEMENTED_INTRINSIC(LongSignum) - // Rotate operations are handled as HRor instructions. UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft) UNIMPLEMENTED_INTRINSIC(IntegerRotateRight) diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index d90c1fb335..b8083477cf 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -4450,9 +4450,6 @@ class HTypeConversion : public HExpression<1> { Primitive::Type GetInputType() const { return GetInput()->GetType(); } Primitive::Type GetResultType() const { return GetType(); } - // Required by the x86, ARM, MIPS and MIPS64 code generators when producing calls - // to the runtime. - bool CanBeMoved() const OVERRIDE { return true; } bool InstructionDataEquals(HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; } @@ -4819,8 +4816,7 @@ class HArraySet : public HTemplateInstruction<3> { } bool NeedsEnvironment() const OVERRIDE { - // We currently always call a runtime method to catch array store - // exceptions. + // We call a runtime method to throw ArrayStoreException. return needs_type_check_; } @@ -5131,11 +5127,13 @@ class HLoadString : public HExpression<1> { uint32_t GetStringIndex() const { return string_index_; } - // TODO: Can we deopt or debug when we resolve a string? - bool NeedsEnvironment() const OVERRIDE { return false; } + // Will call the runtime if the string is not already in the dex cache. + bool NeedsEnvironment() const OVERRIDE { return !IsInDexCache(); } + bool NeedsDexCacheOfDeclaringClass() const OVERRIDE { return true; } bool CanBeNull() const OVERRIDE { return false; } bool IsInDexCache() const { return is_in_dex_cache_; } + bool CanThrow() const OVERRIDE { return !IsInDexCache(); } static SideEffects SideEffectsForArchRuntimeCalls() { return SideEffects::CanTriggerGC(); @@ -5465,7 +5463,7 @@ class HInstanceOf : public HExpression<2> { } bool NeedsEnvironment() const OVERRIDE { - return false; + return CanCallRuntime(check_kind_); } bool IsExactCheck() const { return check_kind_ == TypeCheckKind::kExactCheck; } @@ -5476,11 +5474,13 @@ class HInstanceOf : public HExpression<2> { bool MustDoNullCheck() const { return must_do_null_check_; } void ClearMustDoNullCheck() { must_do_null_check_ = false; } + static bool CanCallRuntime(TypeCheckKind check_kind) { + // Mips currently does runtime calls for any other checks. + return check_kind != TypeCheckKind::kExactCheck; + } + static SideEffects SideEffectsForArchRuntimeCalls(TypeCheckKind check_kind) { - return (check_kind == TypeCheckKind::kExactCheck) - ? SideEffects::None() - // Mips currently does runtime calls for any other checks. - : SideEffects::CanTriggerGC(); + return CanCallRuntime(check_kind) ? SideEffects::CanTriggerGC() : SideEffects::None(); } DECLARE_INSTRUCTION(InstanceOf); @@ -5605,8 +5605,8 @@ class HMonitorOperation : public HTemplateInstruction<1> { SetRawInputAt(0, object); } - // Instruction may throw a Java exception, so we need an environment. - bool NeedsEnvironment() const OVERRIDE { return CanThrow(); } + // Instruction may go into runtime, so we need an environment. + bool NeedsEnvironment() const OVERRIDE { return true; } bool CanThrow() const OVERRIDE { // Verifier guarantees that monitor-exit cannot throw. diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index f56fc385af..8e80961e43 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1055,6 +1055,9 @@ class Dex2Oat FINAL { key_value_store_->Put( OatHeader::kDebuggableKey, compiler_options_->debuggable_ ? OatHeader::kTrueValue : OatHeader::kFalseValue); + key_value_store_->Put( + OatHeader::kExtractOnlyKey, + compiler_options_->IsExtractOnly() ? OatHeader::kTrueValue : OatHeader::kFalseValue); } // Parse the arguments from the command line. In case of an unrecognized option or impossible @@ -1332,7 +1335,13 @@ class Dex2Oat FINAL { return false; } - { + if (compiler_options_->IsExtractOnly()) { + // ExtractOnly oat files only contain non-quickened DEX code and are + // therefore independent of the image file. + image_file_location_oat_checksum_ = 0u; + image_file_location_oat_data_begin_ = 0u; + image_patch_delta_ = 0; + } else { TimingLogger::ScopedTiming t3("Loading image checksum", timings_); std::vector<gc::space::ImageSpace*> image_spaces = Runtime::Current()->GetHeap()->GetBootImageSpaces(); @@ -1709,7 +1718,10 @@ class Dex2Oat FINAL { if (IsImage()) { // Update oat estimates. - UpdateImageWriter(i); + DCHECK(image_writer_ != nullptr); + DCHECK_LT(i, oat_filenames_.size()); + + image_writer_->UpdateOatFile(oat_file.get(), oat_filenames_[i]); } VLOG(compiler) << "Oat file written successfully: " << oat_filenames_[i]; @@ -2351,14 +2363,6 @@ class Dex2Oat FINAL { return res.substr(0, penultimate_slash) + res.substr(last_slash); } - // Update the estimate for the oat file with the given index. - void UpdateImageWriter(size_t index) { - DCHECK(image_writer_ != nullptr); - DCHECK_LT(index, oat_filenames_.size()); - - image_writer_->UpdateOatFile(oat_filenames_[index]); - } - std::unique_ptr<CompilerOptions> compiler_options_; Compiler::Kind compiler_kind_; diff --git a/runtime/atomic.h b/runtime/atomic.h index 0faa3c69c6..d4a7f37bc6 100644 --- a/runtime/atomic.h +++ b/runtime/atomic.h @@ -278,6 +278,10 @@ class PACKED(sizeof(T)) Atomic : public std::atomic<T> { return this->fetch_add(value, std::memory_order_seq_cst); // Return old_value. } + T FetchAndAddRelaxed(const T value) { + return this->fetch_add(value, std::memory_order_relaxed); // Return old_value. + } + T FetchAndSubSequentiallyConsistent(const T value) { return this->fetch_sub(value, std::memory_order_seq_cst); // Return old value. } diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h index ea1afa8203..7e8a4a4fcd 100644 --- a/runtime/class_linker-inl.h +++ b/runtime/class_linker-inl.h @@ -116,6 +116,30 @@ inline ArtMethod* ClassLinker::GetResolvedMethod(uint32_t method_idx, ArtMethod* return resolved_method; } +inline mirror::Class* ClassLinker::ResolveReferencedClassOfMethod(Thread* self, + uint32_t method_idx, + ArtMethod* referrer) { + // NB: We cannot simply use `GetResolvedMethod(method_idx, ...)->GetDeclaringClass()`. This is + // because if we did so than an invoke-super could be incorrectly dispatched in cases where + // GetMethodId(method_idx).class_idx_ refers to a non-interface, non-direct-superclass + // (super*-class?) of the referrer and the direct superclass of the referrer contains a concrete + // implementation of the method. If this class's implementation of the method is copied from an + // interface (either miranda, default or conflict) we would incorrectly assume that is what we + // want to invoke on, instead of the 'concrete' implementation that the direct superclass + // contains. + mirror::Class* declaring_class = referrer->GetDeclaringClass(); + StackHandleScope<2> hs(self); + Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(declaring_class->GetDexCache())); + const DexFile* dex_file = h_dex_cache->GetDexFile(); + const DexFile::MethodId& method = dex_file->GetMethodId(method_idx); + mirror::Class* resolved_type = h_dex_cache->GetResolvedType(method.class_idx_); + if (UNLIKELY(resolved_type == nullptr)) { + Handle<mirror::ClassLoader> class_loader(hs.NewHandle(declaring_class->GetClassLoader())); + resolved_type = ResolveType(*dex_file, method.class_idx_, h_dex_cache, class_loader); + } + return resolved_type; +} + template <ClassLinker::ResolveMode kResolveMode> inline ArtMethod* ClassLinker::ResolveMethod(Thread* self, uint32_t method_idx, diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 0e67f49a09..5ef199cb60 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -164,10 +164,12 @@ void ClassLinker::ThrowEarlierClassFailure(mirror::Class* c, bool wrap_in_no_cla if (!runtime->IsAotCompiler()) { // Give info if this occurs at runtime. std::string extra; if (c->GetVerifyError() != nullptr) { - mirror::Class* descr_from = c->GetVerifyError()->IsClass() - ? c->GetVerifyError()->AsClass() - : c->GetVerifyError()->GetClass(); - extra = PrettyDescriptor(descr_from); + mirror::Object* verify_error = c->GetVerifyError(); + if (verify_error->IsClass()) { + extra = PrettyDescriptor(verify_error->AsClass()); + } else { + extra = verify_error->AsThrowable()->Dump(); + } } LOG(INFO) << "Rejecting re-init on previously-failed class " << PrettyClass(c) << ": " << extra; } @@ -3664,7 +3666,7 @@ void ClassLinker::VerifyClass(Thread* self, Handle<mirror::Class> klass) { } self->AssertNoPendingException(); // Make sure all classes referenced by catch blocks are resolved. - ResolveClassExceptionHandlerTypes(dex_file, klass); + ResolveClassExceptionHandlerTypes(klass); if (verifier_failure == verifier::MethodVerifier::kNoFailure) { // Even though there were no verifier failures we need to respect whether the super-class and // super-default-interfaces were verified or requiring runtime reverification. @@ -3800,17 +3802,16 @@ bool ClassLinker::VerifyClassUsingOatFile(const DexFile& dex_file, UNREACHABLE(); } -void ClassLinker::ResolveClassExceptionHandlerTypes(const DexFile& dex_file, - Handle<mirror::Class> klass) { +void ClassLinker::ResolveClassExceptionHandlerTypes(Handle<mirror::Class> klass) { for (ArtMethod& method : klass->GetMethods(image_pointer_size_)) { - ResolveMethodExceptionHandlerTypes(dex_file, &method); + ResolveMethodExceptionHandlerTypes(&method); } } -void ClassLinker::ResolveMethodExceptionHandlerTypes(const DexFile& dex_file, - ArtMethod* method) { +void ClassLinker::ResolveMethodExceptionHandlerTypes(ArtMethod* method) { // similar to DexVerifier::ScanTryCatchBlocks and dex2oat's ResolveExceptionsForMethod. - const DexFile::CodeItem* code_item = dex_file.GetCodeItem(method->GetCodeItemOffset()); + const DexFile::CodeItem* code_item = + method->GetDexFile()->GetCodeItem(method->GetCodeItemOffset()); if (code_item == nullptr) { return; // native or abstract method } diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 4975c29742..5176cbd3ea 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -284,6 +284,15 @@ class ClassLinker { ArtMethod* GetResolvedMethod(uint32_t method_idx, ArtMethod* referrer) SHARED_REQUIRES(Locks::mutator_lock_); + + // This returns the class referred to by GetMethodId(method_idx).class_idx_. This might be + // different then the declaring class of the resolved method due to copied + // miranda/default/conflict methods. + mirror::Class* ResolveReferencedClassOfMethod(Thread* self, + uint32_t method_idx, + ArtMethod* referrer) + SHARED_REQUIRES(Locks::mutator_lock_) + REQUIRES(!dex_lock_, !Roles::uninterruptible_); template <ResolveMode kResolveMode> ArtMethod* ResolveMethod(Thread* self, uint32_t method_idx, ArtMethod* referrer, InvokeType type) SHARED_REQUIRES(Locks::mutator_lock_) @@ -432,11 +441,10 @@ class ClassLinker { mirror::Class::Status& oat_file_class_status) SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_); - void ResolveClassExceptionHandlerTypes(const DexFile& dex_file, - Handle<mirror::Class> klass) + void ResolveClassExceptionHandlerTypes(Handle<mirror::Class> klass) SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_); - void ResolveMethodExceptionHandlerTypes(const DexFile& dex_file, ArtMethod* klass) + void ResolveMethodExceptionHandlerTypes(ArtMethod* klass) SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_); diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 638fdb4f46..24986253f7 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -1036,8 +1036,15 @@ extern "C" const void* artQuickResolutionTrampoline( } else { DCHECK_EQ(invoke_type, kSuper); CHECK(caller != nullptr) << invoke_type; - called = caller->GetDeclaringClass()->GetSuperClass()->GetVTableEntry( - called->GetMethodIndex(), sizeof(void*)); + // TODO Maybe put this into a mirror::Class function. + mirror::Class* ref_class = linker->ResolveReferencedClassOfMethod( + self, called_method.dex_method_index, caller); + if (ref_class->IsInterface()) { + called = ref_class->FindVirtualMethodForInterfaceSuper(called, sizeof(void*)); + } else { + called = caller->GetDeclaringClass()->GetSuperClass()->GetVTableEntry( + called->GetMethodIndex(), sizeof(void*)); + } } CHECK(called != nullptr) << PrettyMethod(orig_called) << " " diff --git a/runtime/fault_handler.cc b/runtime/fault_handler.cc index 52ccbeeca0..5345b890a1 100644 --- a/runtime/fault_handler.cc +++ b/runtime/fault_handler.cc @@ -146,43 +146,13 @@ void FaultManager::Shutdown() { } } -void FaultManager::HandleFault(int sig, siginfo_t* info, void* context) { - // BE CAREFUL ALLOCATING HERE INCLUDING USING LOG(...) - // - // If malloc calls abort, it will be holding its lock. - // If the handler tries to call malloc, it will deadlock. - VLOG(signals) << "Handling fault"; - if (IsInGeneratedCode(info, context, true)) { - VLOG(signals) << "in generated code, looking for handler"; - for (const auto& handler : generated_code_handlers_) { - VLOG(signals) << "invoking Action on handler " << handler; - if (handler->Action(sig, info, context)) { -#ifdef TEST_NESTED_SIGNAL - // In test mode we want to fall through to stack trace handler - // on every signal (in reality this will cause a crash on the first - // signal). - break; -#else - // We have handled a signal so it's time to return from the - // signal handler to the appropriate place. - return; -#endif - } - } - } - - // We hit a signal we didn't handle. This might be something for which - // we can give more information about so call all registered handlers to see - // if it is. - +bool FaultManager::HandleFaultByOtherHandlers(int sig, siginfo_t* info, void* context) { Thread* self = Thread::Current(); - // If ART is not running, or the thread is not attached to ART pass the - // signal on to the next handler in the chain. - if (self == nullptr || Runtime::Current() == nullptr || !Runtime::Current()->IsStarted()) { - InvokeUserSignalHandler(sig, info, context); - return; - } + DCHECK(self != nullptr); + DCHECK(Runtime::Current() != nullptr); + DCHECK(Runtime::Current()->IsStarted()); + // Now set up the nested signal handler. // TODO: add SIGSEGV back to the nested signals when we can handle running out stack gracefully. @@ -231,6 +201,7 @@ void FaultManager::HandleFault(int sig, siginfo_t* info, void* context) { break; } } + if (success) { // Save the current state and call the handlers. If anything causes a signal // our nested signal handler will be invoked and this will longjmp to the saved @@ -247,7 +218,7 @@ void FaultManager::HandleFault(int sig, siginfo_t* info, void* context) { } } fault_manager.Init(); - return; + return true; } } } else { @@ -265,6 +236,40 @@ void FaultManager::HandleFault(int sig, siginfo_t* info, void* context) { // Now put the fault manager back in place. fault_manager.Init(); + return false; +} + +void FaultManager::HandleFault(int sig, siginfo_t* info, void* context) { + // BE CAREFUL ALLOCATING HERE INCLUDING USING LOG(...) + // + // If malloc calls abort, it will be holding its lock. + // If the handler tries to call malloc, it will deadlock. + VLOG(signals) << "Handling fault"; + if (IsInGeneratedCode(info, context, true)) { + VLOG(signals) << "in generated code, looking for handler"; + for (const auto& handler : generated_code_handlers_) { + VLOG(signals) << "invoking Action on handler " << handler; + if (handler->Action(sig, info, context)) { +#ifdef TEST_NESTED_SIGNAL + // In test mode we want to fall through to stack trace handler + // on every signal (in reality this will cause a crash on the first + // signal). + break; +#else + // We have handled a signal so it's time to return from the + // signal handler to the appropriate place. + return; +#endif + } + } + + // We hit a signal we didn't handle. This might be something for which + // we can give more information about so call all registered handlers to see + // if it is. + if (HandleFaultByOtherHandlers(sig, info, context)) { + return; + } + } // Set a breakpoint in this function to catch unhandled signals. art_sigsegv_fault(); diff --git a/runtime/fault_handler.h b/runtime/fault_handler.h index 3b03a1427d..625b1e8cc6 100644 --- a/runtime/fault_handler.h +++ b/runtime/fault_handler.h @@ -62,6 +62,10 @@ class FaultManager { NO_THREAD_SAFETY_ANALYSIS; private: + // The HandleFaultByOtherHandlers function is only called by HandleFault function for generated code. + bool HandleFaultByOtherHandlers(int sig, siginfo_t* info, void* context) + NO_THREAD_SAFETY_ANALYSIS; + std::vector<FaultHandler*> generated_code_handlers_; std::vector<FaultHandler*> other_handlers_; struct sigaction oldaction_; diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h index d1ab587aea..f437830e11 100644 --- a/runtime/gc/heap-inl.h +++ b/runtime/gc/heap-inl.h @@ -145,9 +145,9 @@ inline mirror::Object* Heap::AllocObjectWithAllocator(Thread* self, WriteBarrierField(obj, mirror::Object::ClassOffset(), klass); } pre_fence_visitor(obj, usable_size); + QuasiAtomic::ThreadFenceForConstructor(); new_num_bytes_allocated = static_cast<size_t>( - num_bytes_allocated_.FetchAndAddSequentiallyConsistent(bytes_tl_bulk_allocated)) - + bytes_tl_bulk_allocated; + num_bytes_allocated_.FetchAndAddRelaxed(bytes_tl_bulk_allocated)) + bytes_tl_bulk_allocated; } if (kIsDebugBuild && Runtime::Current()->IsStarted()) { CHECK_LE(obj->SizeOf(), usable_size); diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index f6079232bf..998db5271a 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -1167,6 +1167,20 @@ ImageSpace* ImageSpace::Init(const char* image_filename, return nullptr; } + if (oat_file != nullptr) { + // If we have an oat file, check the oat file checksum. The oat file is only non-null for the + // app image case. Otherwise, we open the oat file after the image and check the checksum there. + const uint32_t oat_checksum = oat_file->GetOatHeader().GetChecksum(); + const uint32_t image_oat_checksum = image_header->GetOatChecksum(); + if (oat_checksum != image_oat_checksum) { + *error_msg = StringPrintf("Oat checksum 0x%x does not match the image one 0x%x in image %s", + oat_checksum, + image_oat_checksum, + image_filename); + return nullptr; + } + } + if (VLOG_IS_ON(startup)) { LOG(INFO) << "Dumping image sections"; for (size_t i = 0; i < ImageHeader::kSectionCount; ++i) { diff --git a/runtime/jni_internal.h b/runtime/jni_internal.h index 3429962d3f..b829934dd7 100644 --- a/runtime/jni_internal.h +++ b/runtime/jni_internal.h @@ -24,6 +24,13 @@ #define NATIVE_METHOD(className, functionName, signature) \ { #functionName, signature, reinterpret_cast<void*>(className ## _ ## functionName) } #endif + +// TODO: Can we do a better job of supporting overloading ? +#ifndef OVERLOADED_NATIVE_METHOD +#define OVERLOADED_NATIVE_METHOD(className, functionName, signature, identifier) \ + { #functionName, signature, reinterpret_cast<void*>(className ## _ ## identifier) } +#endif + #define REGISTER_NATIVE_METHODS(jni_class_name) \ RegisterNativeMethods(env, jni_class_name, gMethods, arraysize(gMethods)) diff --git a/runtime/native/java_lang_Object.cc b/runtime/native/java_lang_Object.cc index 49cacdf156..2a36059a17 100644 --- a/runtime/native/java_lang_Object.cc +++ b/runtime/native/java_lang_Object.cc @@ -20,10 +20,6 @@ #include "mirror/object-inl.h" #include "scoped_fast_native_object_access.h" -// TODO: better support for overloading. -#undef NATIVE_METHOD -#define NATIVE_METHOD(className, functionName, signature, identifier) \ - { #functionName, signature, reinterpret_cast<void*>(className ## _ ## identifier) } namespace art { @@ -58,11 +54,11 @@ static void Object_waitJI(JNIEnv* env, jobject java_this, jlong ms, jint ns) { } static JNINativeMethod gMethods[] = { - NATIVE_METHOD(Object, internalClone, "!()Ljava/lang/Object;", internalClone), - NATIVE_METHOD(Object, notify, "!()V", notify), - NATIVE_METHOD(Object, notifyAll, "!()V", notifyAll), - NATIVE_METHOD(Object, wait, "!()V", wait), - NATIVE_METHOD(Object, wait, "!(JI)V", waitJI), + NATIVE_METHOD(Object, internalClone, "!()Ljava/lang/Object;"), + NATIVE_METHOD(Object, notify, "!()V"), + NATIVE_METHOD(Object, notifyAll, "!()V"), + OVERLOADED_NATIVE_METHOD(Object, wait, "!()V", wait), + OVERLOADED_NATIVE_METHOD(Object, wait, "!(JI)V", waitJI), }; void register_java_lang_Object(JNIEnv* env) { diff --git a/runtime/native/sun_misc_Unsafe.cc b/runtime/native/sun_misc_Unsafe.cc index 8a2c7e4dbf..6ffd476edf 100644 --- a/runtime/native/sun_misc_Unsafe.cc +++ b/runtime/native/sun_misc_Unsafe.cc @@ -231,58 +231,58 @@ static void Unsafe_setMemory(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong addres memset(reinterpret_cast<void*>(static_cast<uintptr_t>(address)), value, bytes); } -static jbyte Unsafe_getByte$(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address) { +static jbyte Unsafe_getByteJ(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address) { return *reinterpret_cast<jbyte*>(address); } -static void Unsafe_putByte$(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address, jbyte value) { +static void Unsafe_putByteJB(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address, jbyte value) { *reinterpret_cast<jbyte*>(address) = value; } -static jshort Unsafe_getShort$(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address) { +static jshort Unsafe_getShortJ(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address) { return *reinterpret_cast<jshort*>(address); } -static void Unsafe_putShort$(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address, jshort value) { +static void Unsafe_putShortJS(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address, jshort value) { *reinterpret_cast<jshort*>(address) = value; } -static jchar Unsafe_getChar$(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address) { +static jchar Unsafe_getCharJ(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address) { return *reinterpret_cast<jchar*>(address); } -static void Unsafe_putChar$(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address, jchar value) { +static void Unsafe_putCharJC(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address, jchar value) { *reinterpret_cast<jchar*>(address) = value; } -static jint Unsafe_getInt$(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address) { +static jint Unsafe_getIntJ(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address) { return *reinterpret_cast<jint*>(address); } -static void Unsafe_putInt$(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address, jint value) { +static void Unsafe_putIntJI(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address, jint value) { *reinterpret_cast<jint*>(address) = value; } -static jlong Unsafe_getLong$(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address) { +static jlong Unsafe_getLongJ(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address) { return *reinterpret_cast<jlong*>(address); } -static void Unsafe_putLong$(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address, jlong value) { +static void Unsafe_putLongJJ(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address, jlong value) { *reinterpret_cast<jlong*>(address) = value; } -static jfloat Unsafe_getFloat$(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address) { +static jfloat Unsafe_getFloatJ(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address) { return *reinterpret_cast<jfloat*>(address); } -static void Unsafe_putFloat$(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address, jfloat value) { +static void Unsafe_putFloatJF(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address, jfloat value) { *reinterpret_cast<jfloat*>(address) = value; } -static jdouble Unsafe_getDouble$(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address) { +static jdouble Unsafe_getDoubleJ(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address) { return *reinterpret_cast<jdouble*>(address); } -static void Unsafe_putDouble$(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address, jdouble value) { +static void Unsafe_putDoubleJD(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address, jdouble value) { *reinterpret_cast<jdouble*>(address) = value; } @@ -499,24 +499,11 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(Unsafe, allocateMemory, "!(J)J"), NATIVE_METHOD(Unsafe, freeMemory, "!(J)V"), NATIVE_METHOD(Unsafe, setMemory, "!(JJB)V"), - NATIVE_METHOD(Unsafe, getByte$, "!(J)B"), - NATIVE_METHOD(Unsafe, putByte$, "!(JB)V"), - NATIVE_METHOD(Unsafe, getShort$, "!(J)S"), - NATIVE_METHOD(Unsafe, putShort$, "!(JS)V"), - NATIVE_METHOD(Unsafe, getChar$, "!(J)C"), - NATIVE_METHOD(Unsafe, putChar$, "!(JC)V"), - NATIVE_METHOD(Unsafe, getInt$, "!(J)I"), - NATIVE_METHOD(Unsafe, putInt$, "!(JI)V"), - NATIVE_METHOD(Unsafe, getLong$, "!(J)J"), - NATIVE_METHOD(Unsafe, putLong$, "!(JJ)V"), - NATIVE_METHOD(Unsafe, getFloat$, "!(J)F"), - NATIVE_METHOD(Unsafe, putFloat$, "!(JF)V"), - NATIVE_METHOD(Unsafe, getDouble$, "!(J)D"), - NATIVE_METHOD(Unsafe, putDouble$, "!(JD)V"), NATIVE_METHOD(Unsafe, copyMemory, "!(JJJ)V"), NATIVE_METHOD(Unsafe, copyMemoryToPrimitiveArray, "!(JLjava/lang/Object;JJ)V"), NATIVE_METHOD(Unsafe, copyMemoryFromPrimitiveArray, "!(Ljava/lang/Object;JJJ)V"), NATIVE_METHOD(Unsafe, getBoolean, "!(Ljava/lang/Object;J)Z"), + NATIVE_METHOD(Unsafe, getByte, "!(Ljava/lang/Object;J)B"), NATIVE_METHOD(Unsafe, getChar, "!(Ljava/lang/Object;J)C"), NATIVE_METHOD(Unsafe, getShort, "!(Ljava/lang/Object;J)S"), @@ -528,6 +515,23 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(Unsafe, putShort, "!(Ljava/lang/Object;JS)V"), NATIVE_METHOD(Unsafe, putFloat, "!(Ljava/lang/Object;JF)V"), NATIVE_METHOD(Unsafe, putDouble, "!(Ljava/lang/Object;JD)V"), + + // Each of the getFoo variants are overloaded with a call that operates + // directively on a native pointer. + OVERLOADED_NATIVE_METHOD(Unsafe, getByte, "!(J)B", getByteJ), + OVERLOADED_NATIVE_METHOD(Unsafe, getChar, "!(J)C", getCharJ), + OVERLOADED_NATIVE_METHOD(Unsafe, getShort, "!(J)S", getShortJ), + OVERLOADED_NATIVE_METHOD(Unsafe, getInt, "!(J)I", getIntJ), + OVERLOADED_NATIVE_METHOD(Unsafe, getLong, "!(J)J", getLongJ), + OVERLOADED_NATIVE_METHOD(Unsafe, getFloat, "!(J)F", getFloatJ), + OVERLOADED_NATIVE_METHOD(Unsafe, getDouble, "!(J)D", getDoubleJ), + OVERLOADED_NATIVE_METHOD(Unsafe, putByte, "!(JB)V", putByteJB), + OVERLOADED_NATIVE_METHOD(Unsafe, putChar, "!(JC)V", putCharJC), + OVERLOADED_NATIVE_METHOD(Unsafe, putShort, "!(JS)V", putShortJS), + OVERLOADED_NATIVE_METHOD(Unsafe, putInt, "!(JI)V", putIntJI), + OVERLOADED_NATIVE_METHOD(Unsafe, putLong, "!(JJ)V", putLongJJ), + OVERLOADED_NATIVE_METHOD(Unsafe, putFloat, "!(JF)V", putFloatJF), + OVERLOADED_NATIVE_METHOD(Unsafe, putDouble, "!(JD)V", putDoubleJD), }; void register_sun_misc_Unsafe(JNIEnv* env) { diff --git a/runtime/oat.cc b/runtime/oat.cc index c787b9adb1..4948558f84 100644 --- a/runtime/oat.cc +++ b/runtime/oat.cc @@ -466,6 +466,10 @@ bool OatHeader::IsDebuggable() const { return IsKeyEnabled(OatHeader::kDebuggableKey); } +bool OatHeader::IsExtractOnly() const { + return IsKeyEnabled(OatHeader::kExtractOnlyKey); +} + bool OatHeader::IsKeyEnabled(const char* key) const { const char* key_value = GetStoreValueByKey(key); return (key_value != nullptr && strncmp(key_value, kTrueValue, sizeof(kTrueValue)) == 0); diff --git a/runtime/oat.h b/runtime/oat.h index 989e3f9d89..fde386f37e 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -38,6 +38,7 @@ class PACKED(4) OatHeader { static constexpr const char* kDex2OatHostKey = "dex2oat-host"; static constexpr const char* kPicKey = "pic"; static constexpr const char* kDebuggableKey = "debuggable"; + static constexpr const char* kExtractOnlyKey = "extract-only"; static constexpr const char* kClassPathKey = "classpath"; static constexpr const char* kBootClassPath = "bootclasspath"; @@ -106,6 +107,7 @@ class PACKED(4) OatHeader { size_t GetHeaderSize() const; bool IsPic() const; bool IsDebuggable() const; + bool IsExtractOnly() const; private: OatHeader(InstructionSet instruction_set, diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index 8f321a092c..f912598eef 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -1221,6 +1221,10 @@ bool OatFile::IsDebuggable() const { return GetOatHeader().IsDebuggable(); } +bool OatFile::IsExtractOnly() const { + return GetOatHeader().IsExtractOnly(); +} + static constexpr char kDexClassPathEncodingSeparator = '*'; std::string OatFile::EncodeDexFileDependencies(const std::vector<const DexFile*>& dex_files) { diff --git a/runtime/oat_file.h b/runtime/oat_file.h index dbd75415a4..bcc2d33333 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -85,6 +85,8 @@ class OatFile { // Indicates whether the oat file was compiled with full debugging capability. bool IsDebuggable() const; + bool IsExtractOnly() const; + const std::string& GetLocation() const { return location_; } diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index a8f84a2bfa..6daade0648 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -461,6 +461,23 @@ bool OatFileAssistant::GivenOatFileIsOutOfDate(const OatFile& file) { } } + if (file.IsExtractOnly()) { + VLOG(oat) << "Oat file is extract-only. Image checksum test skipped."; + if (kIsDebugBuild) { + // Sanity check that no classes have compiled code. Does not test that + // the DEX code has not been quickened. + std::string error_msg; + for (const OatFile::OatDexFile* current : file.GetOatDexFiles()) { + const DexFile* const dex_file = current->OpenDexFile(&error_msg).release(); + DCHECK(dex_file != nullptr); + for (size_t i = 0, e = dex_file->NumClassDefs(); i < e; ++i) { + DCHECK_EQ(current->GetOatClass(i).GetType(), kOatClassNoneCompiled); + } + } + } + return false; + } + // Verify the image checksum const ImageInfo* image_info = GetImageInfo(); if (image_info == nullptr) { @@ -486,7 +503,10 @@ bool OatFileAssistant::GivenOatFileIsUpToDate(const OatFile& file) { return false; } - if (file.IsPic()) { + if (file.IsPic() || file.IsExtractOnly()) { + // Oat files compiled in PIC mode do not require relocation and extract-only + // oat files do not contain any compiled code. Skip the relocation test. + VLOG(oat) << "Oat relocation test skipped."; return true; } diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc index 25dcbe4c63..83d4457a1c 100644 --- a/runtime/oat_file_assistant_test.cc +++ b/runtime/oat_file_assistant_test.cc @@ -259,6 +259,26 @@ class OatFileAssistantTest : public CommonRuntimeTest { EXPECT_TRUE(odex_file->IsPic()); } + void GenerateExtractOnlyOdexForTest(const std::string& dex_location, + const std::string& odex_location) { + std::vector<std::string> args; + args.push_back("--dex-file=" + dex_location); + args.push_back("--oat-file=" + odex_location); + args.push_back("--compiler-filter=verify-at-runtime"); + std::string error_msg; + ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg; + + // Verify the odex file was generated as expected. + std::unique_ptr<OatFile> odex_file(OatFile::Open( + odex_location.c_str(), odex_location.c_str(), nullptr, nullptr, + false, dex_location.c_str(), &error_msg)); + ASSERT_TRUE(odex_file.get() != nullptr) << error_msg; + EXPECT_TRUE(odex_file->IsExtractOnly()); + EXPECT_EQ(odex_file->GetOatHeader().GetImageFileLocationOatChecksum(), 0u); + EXPECT_EQ(odex_file->GetOatHeader().GetImageFileLocationOatDataBegin(), 0u); + EXPECT_EQ(odex_file->GetOatHeader().GetImagePatchDelta(), 0); +} + private: // Reserve memory around where the image will be loaded so other memory // won't conflict when it comes time to load the image. @@ -488,6 +508,32 @@ TEST_F(OatFileAssistantTest, OatOutOfDate) { EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); } +// Case: We have a DEX file and an extract-only ODEX file out of date relative +// to the DEX file. +// Expect: The status is kDex2OatNeeded. +TEST_F(OatFileAssistantTest, ExtractOnlyOdexOutOfDate) { + std::string dex_location = GetScratchDir() + "/ExtractOnlyOdexOutOfDate.jar"; + std::string odex_location = GetOdexDir() + "/ExtractOnlyOdexOutOfDate.odex"; + + // We create a dex, generate an oat for it, then overwrite the dex with a + // different dex to make the oat out of date. + Copy(GetDexSrc1(), dex_location); + GenerateExtractOnlyOdexForTest(dex_location.c_str(), odex_location.c_str()); + Copy(GetDexSrc2(), dex_location); + + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); + EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded()); + + EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); + EXPECT_TRUE(oat_file_assistant.OdexFileExists()); + EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate()); + EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate()); + EXPECT_FALSE(oat_file_assistant.OatFileExists()); + EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate()); + EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate()); + EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); +} + // Case: We have a DEX file and an ODEX file, but no OAT file. // Expect: The status is kPatchOatNeeded. TEST_F(OatFileAssistantTest, DexOdexNoOat) { @@ -784,6 +830,31 @@ TEST_F(OatFileAssistantTest, DexPicOdexNoOat) { EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); } +// Case: We have a DEX file and a ExtractOnly ODEX file, but no OAT file. +// Expect: The status is kNoDexOptNeeded, because ExtractOnly contains no code. +TEST_F(OatFileAssistantTest, DexExtractOnlyOdexNoOat) { + std::string dex_location = GetScratchDir() + "/DexExtractOnlyOdexNoOat.jar"; + std::string odex_location = GetOdexDir() + "/DexExtractOnlyOdexNoOat.odex"; + + // Create the dex and odex files + Copy(GetDexSrc1(), dex_location); + GenerateExtractOnlyOdexForTest(dex_location, odex_location); + + // Verify the status. + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); + + EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded()); + + EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); + EXPECT_TRUE(oat_file_assistant.OdexFileExists()); + EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate()); + EXPECT_TRUE(oat_file_assistant.OdexFileIsUpToDate()); + EXPECT_FALSE(oat_file_assistant.OatFileExists()); + EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate()); + EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate()); + EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); +} + // Case: We have a DEX file and up-to-date OAT file for it. // Expect: We should load an executable dex file. TEST_F(OatFileAssistantTest, LoadOatUpToDate) { diff --git a/runtime/read_barrier.h b/runtime/read_barrier.h index 77be6cf71e..b7bd99b6f5 100644 --- a/runtime/read_barrier.h +++ b/runtime/read_barrier.h @@ -80,6 +80,7 @@ class ReadBarrier { static void AssertToSpaceInvariant(GcRootSource* gc_root_source, mirror::Object* ref) SHARED_REQUIRES(Locks::mutator_lock_); + // ALWAYS_INLINE on this caused a performance regression b/26744236. static mirror::Object* Mark(mirror::Object* obj) SHARED_REQUIRES(Locks::mutator_lock_); static mirror::Object* WhitePtr() { diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 2890a9826e..56154c63a0 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -2076,7 +2076,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { } else if (reg_type.IsConflict()) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "returning register with conflict"; } else if (reg_type.IsUninitializedTypes()) { - Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "returning uninitialized object '" + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "returning uninitialized object '" << reg_type << "'"; } else if (!reg_type.IsReferenceTypes()) { // We really do expect a reference here. @@ -2233,7 +2233,6 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { opcode_flags &= ~Instruction::kThrow; work_line_->PopMonitor(this, inst->VRegA_11x()); break; - case Instruction::CHECK_CAST: case Instruction::INSTANCE_OF: { /* @@ -2279,6 +2278,14 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { } else { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "instance-of on non-reference in v" << orig_type_reg; } + } else if (orig_type.IsUninitializedTypes()) { + if (is_checkcast) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "check-cast on uninitialized reference in v" + << orig_type_reg; + } else { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "instance-of on uninitialized reference in v" + << orig_type_reg; + } } else { if (is_checkcast) { work_line_->SetRegisterType<LockOp::kKeep>(this, inst->VRegA_21c(), res_type); @@ -2373,8 +2380,12 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { case Instruction::THROW: { const RegType& res_type = work_line_->GetRegisterType(this, inst->VRegA_11x()); if (!reg_types_.JavaLangThrowable(false).IsAssignableFrom(res_type)) { - Fail(res_type.IsUnresolvedTypes() ? VERIFY_ERROR_NO_CLASS : VERIFY_ERROR_BAD_CLASS_SOFT) - << "thrown class " << res_type << " not instanceof Throwable"; + if (res_type.IsUninitializedTypes()) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "thrown exception not initialized"; + } else { + Fail(res_type.IsUnresolvedTypes() ? VERIFY_ERROR_NO_CLASS : VERIFY_ERROR_BAD_CLASS_SOFT) + << "thrown class " << res_type << " not instanceof Throwable"; + } } break; } @@ -3596,6 +3607,7 @@ const RegType& MethodVerifier::GetCaughtExceptionType() { } else { const RegType& exception = ResolveClassAndCheckAccess(iterator.GetHandlerTypeIndex()); if (!reg_types_.JavaLangThrowable(false).IsAssignableFrom(exception)) { + DCHECK(!exception.IsUninitializedTypes()); // Comes from dex, shouldn't be uninit. if (exception.IsUnresolvedTypes()) { // We don't know enough about the type. Fail here and let runtime handle it. Fail(VERIFY_ERROR_NO_CLASS) << "unresolved exception class " << exception; @@ -3786,7 +3798,8 @@ ArtMethod* MethodVerifier::VerifyInvocationArgsFromIterator( CHECK(have_pending_hard_failure_); return nullptr; } - if (actual_arg_type.IsUninitializedReference()) { + bool is_init = false; + if (actual_arg_type.IsUninitializedTypes()) { if (res_method) { if (!res_method->IsConstructor()) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "'this' arg must be initialized"; @@ -3800,8 +3813,12 @@ ArtMethod* MethodVerifier::VerifyInvocationArgsFromIterator( return nullptr; } } + is_init = true; } - if (method_type != METHOD_INTERFACE && !actual_arg_type.IsZero()) { + const RegType& adjusted_type = is_init + ? GetRegTypeCache()->FromUninitialized(actual_arg_type) + : actual_arg_type; + if (method_type != METHOD_INTERFACE && !adjusted_type.IsZero()) { const RegType* res_method_class; // Miranda methods have the declaring interface as their declaring class, not the abstract // class. It would be wrong to use this for the type check (interface type checks are @@ -3819,10 +3836,12 @@ ArtMethod* MethodVerifier::VerifyInvocationArgsFromIterator( dex_file_->StringByTypeIdx(class_idx), false); } - if (!res_method_class->IsAssignableFrom(actual_arg_type)) { - Fail(actual_arg_type.IsUnresolvedTypes() ? VERIFY_ERROR_NO_CLASS: - VERIFY_ERROR_BAD_CLASS_SOFT) << "'this' argument '" << actual_arg_type - << "' not instance of '" << *res_method_class << "'"; + if (!res_method_class->IsAssignableFrom(adjusted_type)) { + Fail(adjusted_type.IsUnresolvedTypes() + ? VERIFY_ERROR_NO_CLASS + : VERIFY_ERROR_BAD_CLASS_SOFT) + << "'this' argument '" << actual_arg_type << "' not instance of '" + << *res_method_class << "'"; // Continue on soft failures. We need to find possible hard failures to avoid problems in // the compiler. if (have_pending_hard_failure_) { @@ -3948,11 +3967,27 @@ ArtMethod* MethodVerifier::VerifyInvocationArgs( // If we're using invoke-super(method), make sure that the executing method's class' superclass // has a vtable entry for the target method. Or the target is on a interface. if (method_type == METHOD_SUPER) { - if (res_method->GetDeclaringClass()->IsInterface()) { - // TODO Fill in this part. Verify what we can... - if (Runtime::Current()->IsAotCompiler()) { - Fail(VERIFY_ERROR_FORCE_INTERPRETER) << "Currently we only allow invoke-super in " - << "interpreter when using interface methods"; + uint16_t class_idx = dex_file_->GetMethodId(method_idx).class_idx_; + mirror::Class* reference_class = dex_cache_->GetResolvedType(class_idx); + if (reference_class == nullptr) { + Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "Unable to find referenced class from invoke-super"; + return nullptr; + } + if (reference_class->IsInterface()) { + // TODO Can we verify anything else. + if (class_idx == class_def_->class_idx_) { + Fail(VERIFY_ERROR_CLASS_CHANGE) << "Cannot invoke-super on self as interface"; + } + // TODO Revisit whether we want to allow invoke-super on direct interfaces only like the JLS + // does. + mirror::Class* this_class = GetDeclaringClass().GetClass(); + if (!reference_class->IsAssignableFrom(this_class)) { + Fail(VERIFY_ERROR_CLASS_CHANGE) + << "invoke-super in " << PrettyClass(this_class) << " in method " + << PrettyMethod(dex_method_idx_, *dex_file_) << " to method " + << PrettyMethod(method_idx, *dex_file_) << " references " + << "non-super-interface type " << PrettyClass(reference_class); + return nullptr; } } else { const RegType& super = GetDeclaringClass().GetSuperClass(®_types_); @@ -4067,7 +4102,8 @@ ArtMethod* MethodVerifier::VerifyInvokeVirtualQuickArgs(const Instruction* inst, * For an interface class, we don't do the full interface merge (see JoinClass), so we can't do a * rigorous check here (which is okay since we have to do it at runtime). */ - if (actual_arg_type.IsUninitializedReference() && !res_method->IsConstructor()) { + // Note: given an uninitialized type, this should always fail. Constructors aren't virtual. + if (actual_arg_type.IsUninitializedTypes() && !res_method->IsConstructor()) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "'this' arg must be initialized"; return nullptr; } @@ -4077,8 +4113,11 @@ ArtMethod* MethodVerifier::VerifyInvokeVirtualQuickArgs(const Instruction* inst, const RegType& res_method_class = FromClass(klass->GetDescriptor(&temp), klass, klass->CannotBeAssignedFromOtherTypes()); if (!res_method_class.IsAssignableFrom(actual_arg_type)) { - Fail(actual_arg_type.IsUnresolvedTypes() ? VERIFY_ERROR_NO_CLASS : - VERIFY_ERROR_BAD_CLASS_SOFT) << "'this' argument '" << actual_arg_type + Fail(actual_arg_type.IsUninitializedTypes() // Just overcautious - should have never + ? VERIFY_ERROR_BAD_CLASS_HARD // quickened this. + : actual_arg_type.IsUnresolvedTypes() + ? VERIFY_ERROR_NO_CLASS + : VERIFY_ERROR_BAD_CLASS_SOFT) << "'this' argument '" << actual_arg_type << "' not instance of '" << res_method_class << "'"; return nullptr; } @@ -4405,15 +4444,20 @@ ArtField* MethodVerifier::GetInstanceField(const RegType& obj_type, int field_id const RegType& field_klass = FromClass(dex_file_->GetFieldDeclaringClassDescriptor(field_id), klass, klass->CannotBeAssignedFromOtherTypes()); - if (obj_type.IsUninitializedTypes() && - (!IsConstructor() || GetDeclaringClass().Equals(obj_type) || - !field_klass.Equals(GetDeclaringClass()))) { + if (obj_type.IsUninitializedTypes()) { // Field accesses through uninitialized references are only allowable for constructors where - // the field is declared in this class - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "cannot access instance field " << PrettyField(field) - << " of a not fully initialized object within the context" - << " of " << PrettyMethod(dex_method_idx_, *dex_file_); - return nullptr; + // the field is declared in this class. + // Note: this IsConstructor check is technically redundant, as UninitializedThis should only + // appear in constructors. + if (!obj_type.IsUninitializedThisReference() || + !IsConstructor() || + !field_klass.Equals(GetDeclaringClass())) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "cannot access instance field " << PrettyField(field) + << " of a not fully initialized object within the context" + << " of " << PrettyMethod(dex_method_idx_, *dex_file_); + return nullptr; + } + return field; } else if (!field_klass.IsAssignableFrom(obj_type)) { // Trying to access C1.field1 using reference of type C2, which is neither C1 or a sub-class // of C1. For resolution to occur the declared class of the field must be compatible with @@ -4436,7 +4480,18 @@ void MethodVerifier::VerifyISFieldAccess(const Instruction* inst, const RegType& field = GetStaticField(field_idx); } else { const RegType& object_type = work_line_->GetRegisterType(this, inst->VRegB_22c()); - field = GetInstanceField(object_type, field_idx); + + // One is not allowed to access fields on uninitialized references, except to write to + // fields in the constructor (before calling another constructor). + // GetInstanceField does an assignability check which will fail for uninitialized types. + // We thus modify the type if the uninitialized reference is a "this" reference (this also + // checks at the same time that we're verifying a constructor). + bool should_adjust = (kAccType == FieldAccessType::kAccPut) && + object_type.IsUninitializedThisReference(); + const RegType& adjusted_type = should_adjust + ? GetRegTypeCache()->FromUninitialized(object_type) + : object_type; + field = GetInstanceField(adjusted_type, field_idx); if (UNLIKELY(have_pending_hard_failure_)) { return; } diff --git a/runtime/verifier/reg_type-inl.h b/runtime/verifier/reg_type-inl.h index 11a53e539d..861db3cf8c 100644 --- a/runtime/verifier/reg_type-inl.h +++ b/runtime/verifier/reg_type-inl.h @@ -93,6 +93,10 @@ inline bool RegType::AssignableFrom(const RegType& lhs, const RegType& rhs, bool return true; // All reference types can be assigned null. } else if (!rhs.IsReferenceTypes()) { return false; // Expect rhs to be a reference type. + } else if (lhs.IsUninitializedTypes() || rhs.IsUninitializedTypes()) { + // Uninitialized types are only allowed to be assigned to themselves. + // TODO: Once we have a proper "reference" super type, this needs to be extended. + return false; } else if (lhs.IsJavaLangObject()) { return true; // All reference types can be assigned to Object. } else if (!strict && !lhs.IsUnresolvedTypes() && lhs.GetClass()->IsInterface()) { diff --git a/runtime/verifier/register_line-inl.h b/runtime/verifier/register_line-inl.h index 57fb701c3c..08f85b35f8 100644 --- a/runtime/verifier/register_line-inl.h +++ b/runtime/verifier/register_line-inl.h @@ -147,6 +147,9 @@ inline bool RegisterLine::VerifyRegisterType(MethodVerifier* verifier, uint32_t if (!check_type.IsNonZeroReferenceTypes() || !src_type.IsNonZeroReferenceTypes()) { // Hard fail if one of the types is primitive, since they are concretely known. fail_type = VERIFY_ERROR_BAD_CLASS_HARD; + } else if (check_type.IsUninitializedTypes() || src_type.IsUninitializedTypes()) { + // Hard fail for uninitialized types, which don't match anything but themselves. + fail_type = VERIFY_ERROR_BAD_CLASS_HARD; } else if (check_type.IsUnresolvedTypes() || src_type.IsUnresolvedTypes()) { fail_type = VERIFY_ERROR_NO_CLASS; } else { diff --git a/test/144-static-field-sigquit/expected.txt b/test/144-static-field-sigquit/expected.txt new file mode 100644 index 0000000000..e0c3e90221 --- /dev/null +++ b/test/144-static-field-sigquit/expected.txt @@ -0,0 +1,4 @@ +Starting threads... +Performing sigquits for 5 seconds +Got date field +Joined threads diff --git a/test/144-static-field-sigquit/info.txt b/test/144-static-field-sigquit/info.txt new file mode 100644 index 0000000000..5dcfc7603b --- /dev/null +++ b/test/144-static-field-sigquit/info.txt @@ -0,0 +1,8 @@ +Regression test for ag/853775 + +Tests that unresolved classes are not put into the dex cache by the verifier. +This was potentially happening when receiving a signal while in the static +initilizer of a class and also within a synchronized block. + +This test is flaky and produces the issue rarely, but it should be good enough +to trigger occasionally with the buildbots. diff --git a/test/144-static-field-sigquit/src/ClassWithStaticField.java b/test/144-static-field-sigquit/src/ClassWithStaticField.java new file mode 100644 index 0000000000..0b2c8553f5 --- /dev/null +++ b/test/144-static-field-sigquit/src/ClassWithStaticField.java @@ -0,0 +1,21 @@ +/* + * 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.util.Date; + +public class ClassWithStaticField { + public static Date mDate = new Date(); +} diff --git a/test/144-static-field-sigquit/src/Main.java b/test/144-static-field-sigquit/src/Main.java new file mode 100644 index 0000000000..ab94da3ff3 --- /dev/null +++ b/test/144-static-field-sigquit/src/Main.java @@ -0,0 +1,32 @@ +/* + * 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 { + + public static void main(String[] args) throws Exception { + Thread thread1 = new Thread(new SigQuit()); + Thread thread2 = new Thread(new SynchronizedUse()); + + System.out.println("Starting threads..."); + thread1.start(); + Thread.sleep(2000); + thread2.start(); + + thread1.join(); + thread2.join(); + System.out.println("Joined threads"); + } +} diff --git a/test/144-static-field-sigquit/src/SigQuit.java b/test/144-static-field-sigquit/src/SigQuit.java new file mode 100644 index 0000000000..bed23e4a2d --- /dev/null +++ b/test/144-static-field-sigquit/src/SigQuit.java @@ -0,0 +1,68 @@ +/* + * 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.*; + +public class SigQuit implements Runnable { + private final static int sigquit; + private final static Method kill; + private final static int pid; + + static { + int pidTemp = -1; + int sigquitTemp = -1; + Method killTemp = null; + + try { + Class<?> osClass = Class.forName("android.system.Os"); + Method getpid = osClass.getDeclaredMethod("getpid"); + pidTemp = (Integer) getpid.invoke(null); + + Class<?> osConstants = Class.forName("android.system.OsConstants"); + Field sigquitField = osConstants.getDeclaredField("SIGQUIT"); + sigquitTemp = (Integer) sigquitField.get(null); + + killTemp = osClass.getDeclaredMethod("kill", int.class, int.class); + } catch (Exception e) { + if (!e.getClass().getName().equals("ErrnoException")) { + e.printStackTrace(System.out); + } + } + + pid = pidTemp; + sigquit = sigquitTemp; + kill = killTemp; + } + + public boolean perform() { + try { + kill.invoke(null, pid, sigquit); + } catch (Exception e) { + if (!e.getClass().getName().equals("ErrnoException")) { + e.printStackTrace(System.out); + } + } + return true; + } + + public void run() { + long endTime = System.currentTimeMillis() + 5000; + System.out.println("Performing sigquits for 5 seconds"); + while (System.currentTimeMillis() < endTime) { + perform(); + } + } +} diff --git a/test/144-static-field-sigquit/src/SynchronizedUse.java b/test/144-static-field-sigquit/src/SynchronizedUse.java new file mode 100644 index 0000000000..43af1d9c8d --- /dev/null +++ b/test/144-static-field-sigquit/src/SynchronizedUse.java @@ -0,0 +1,26 @@ +/* + * 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.util.Date; + +public class SynchronizedUse implements Runnable { + public void run() { + synchronized (this) { + Date dateField = ClassWithStaticField.mDate; + System.out.println("Got date field"); + } + } +} diff --git a/test/563-checker-invoke-super/build b/test/563-checker-invoke-super/build new file mode 100755 index 0000000000..e06193ba78 --- /dev/null +++ b/test/563-checker-invoke-super/build @@ -0,0 +1,28 @@ +#!/bin/bash +# +# 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. +# Make us exit on a failure. +# +set -e + +# Hard-wired use of experimental jack. +# TODO: fix this temporary work-around for lambdas, see b/19467889 +export USE_JACK=true +export JACK_SERVER=false +export JACK_REPOSITORY="${ANDROID_BUILD_TOP}/prebuilts/sdk/tools/jacks" + +# e.g. /foo/bar/jack-3.10.ALPHA.jar -> 3.10.ALPHA +export JACK_VERSION="$(find "$JACK_REPOSITORY" -name '*ALPHA*' | sed 's/.*jack-//g' | sed 's/[.]jar//g')" +./default-build "$@" --experimental default-methods diff --git a/test/563-checker-invoke-super/expected.txt b/test/563-checker-invoke-super/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/563-checker-invoke-super/expected.txt diff --git a/test/563-checker-invoke-super/info.txt b/test/563-checker-invoke-super/info.txt new file mode 100644 index 0000000000..23c0d2fa49 --- /dev/null +++ b/test/563-checker-invoke-super/info.txt @@ -0,0 +1,2 @@ +Tests that invoke-super's to interface methods are optimized to direct method +calls when in the same dex file. diff --git a/test/563-checker-invoke-super/src/Main.java b/test/563-checker-invoke-super/src/Main.java new file mode 100644 index 0000000000..8554dbd0ec --- /dev/null +++ b/test/563-checker-invoke-super/src/Main.java @@ -0,0 +1,39 @@ +/* + * 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. + */ + +interface IFace { + public default void $noinline$aMethod() { throw new RuntimeException("Should not be called"); } +} + +class ClassImplA implements IFace { + /// CHECK-START: void ClassImplA.testSuperInvoke() builder (after) + /// CHECK: InvokeStaticOrDirect + public void testSuperInvoke() { + IFace.super.$noinline$aMethod(); + } +} + +class ClassImplB extends ClassImplA { + /// CHECK-START: void ClassImplB.testSuperInvoke2() builder (after) + /// CHECK: InvokeStaticOrDirect + public void testSuperInvoke2() { + super.$noinline$aMethod(); + } +} + +public class Main { + public static void main(String[] args) { } +} diff --git a/test/568-checker-onebit/src/Main.java b/test/568-checker-onebit/src/Main.java index 7007c6a507..6ce4ffbd3f 100644 --- a/test/568-checker-onebit/src/Main.java +++ b/test/568-checker-onebit/src/Main.java @@ -45,6 +45,10 @@ public class Main { } public static void main(String args[]) { + // Hidden zeros. + int[] xi = new int[32]; + long[] xj = new long[64]; + expectEquals32(0x00000000, hi32(0x00000000)); expectEquals32(0x00000000, lo32(0x00000000)); expectEquals32(0x00010000, hi32(0x00010000)); @@ -55,6 +59,8 @@ public class Main { expectEquals32(0x00000001, lo32(0xFFFFFFFF)); for (int i = 0; i < 32; i++) { + expectEquals32(0, hi32(xi[i])); + expectEquals32(0, lo32(xi[i])); expectEquals32(1 << i, hi32(1 << i)); expectEquals32(1 << i, lo32(1 << i)); int expected = i < 29 ? 0x8 << i : 0x80000000; @@ -72,6 +78,8 @@ public class Main { expectEquals64(0x0000000000000001L, lo64(0xFFFFFFFFFFFFFFFFL)); for (int i = 0; i < 64; i++) { + expectEquals64(0L, hi64(xj[i])); + expectEquals64(0L, lo64(xj[i])); expectEquals64(1L << i, hi64(1L << i)); expectEquals64(1L << i, lo64(1L << i)); long expected = i < 61 ? 0x8L << i : 0x8000000000000000L; diff --git a/test/800-smali/expected.txt b/test/800-smali/expected.txt index 2e66af59e7..73ce3073ce 100644 --- a/test/800-smali/expected.txt +++ b/test/800-smali/expected.txt @@ -50,4 +50,12 @@ b/25494456 b/21869691 b/26143249 b/26579108 +b/26594149 (1) +b/26594149 (2) +b/26594149 (3) +b/26594149 (4) +b/26594149 (5) +b/26594149 (6) +b/26594149 (7) +b/26594149 (8) Done! diff --git a/test/800-smali/smali/b_26594149_1.smali b/test/800-smali/smali/b_26594149_1.smali new file mode 100644 index 0000000000..c465859312 --- /dev/null +++ b/test/800-smali/smali/b_26594149_1.smali @@ -0,0 +1,26 @@ +# 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 LB26594149_1; +.super Ljava/lang/Object; + +.method public static run()V + .registers 2 + new-instance v0, Ljava/lang/String; + + # Illegal operation. + instance-of v1, v0, Ljava/lang/String; + + return-void + .end method diff --git a/test/800-smali/smali/b_26594149_2.smali b/test/800-smali/smali/b_26594149_2.smali new file mode 100644 index 0000000000..765afe2610 --- /dev/null +++ b/test/800-smali/smali/b_26594149_2.smali @@ -0,0 +1,26 @@ +# 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 LB26594149_2; +.super Ljava/lang/Object; + +.method public static run()V + .registers 2 + new-instance v0, Ljava/lang/String; + + # Illegal operation. + check-cast v0, Ljava/lang/String; + + return-void + .end method diff --git a/test/800-smali/smali/b_26594149_3.smali b/test/800-smali/smali/b_26594149_3.smali new file mode 100644 index 0000000000..42b5675149 --- /dev/null +++ b/test/800-smali/smali/b_26594149_3.smali @@ -0,0 +1,28 @@ +# 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 LB26594149_3; +.super Ljava/lang/Object; + +.field public static field:Ljava/lang/String; + +.method public static run()V + .registers 2 + new-instance v0, Ljava/lang/String; + + # Illegal operation. + sput-object v0, LB26594149_3;->field:Ljava/lang/String; + + return-void + .end method diff --git a/test/800-smali/smali/b_26594149_4.smali b/test/800-smali/smali/b_26594149_4.smali new file mode 100644 index 0000000000..5b2f99bf37 --- /dev/null +++ b/test/800-smali/smali/b_26594149_4.smali @@ -0,0 +1,38 @@ +# 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 LB26594149_4; +.super Ljava/lang/Object; + +.field public field:Ljava/lang/String; + +.method public constructor <init>()V + .registers 4 + invoke-direct {p0}, Ljava/lang/Object;-><init>()V + return-void +.end method + +.method public static run()V + .registers 4 + + new-instance v1, LB26594149_4; + invoke-direct {v1}, LB26594149_4;-><init>()V + + new-instance v0, Ljava/lang/String; + + # Illegal operation. + iput-object v0, v1, LB26594149_4;->field:Ljava/lang/String; + + return-void + .end method diff --git a/test/800-smali/smali/b_26594149_5.smali b/test/800-smali/smali/b_26594149_5.smali new file mode 100644 index 0000000000..27d6255f82 --- /dev/null +++ b/test/800-smali/smali/b_26594149_5.smali @@ -0,0 +1,28 @@ +# 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 LB26594149_5; +.super Ljava/lang/Object; + +.method public static run()V + .registers 4 + + new-instance v0, Ljava/lang/Object; + + # Allowed operation on uninitialized objects. + monitor-enter v0 + monitor-exit v0 + + return-void + .end method diff --git a/test/800-smali/smali/b_26594149_6.smali b/test/800-smali/smali/b_26594149_6.smali new file mode 100644 index 0000000000..8d26ee8c58 --- /dev/null +++ b/test/800-smali/smali/b_26594149_6.smali @@ -0,0 +1,24 @@ +# 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 LB26594149_6; +.super Ljava/lang/Object; + +.method public static run()V + .registers 4 + + new-instance v0, Ljava/lang/Exception; + throw v0 + + .end method diff --git a/test/800-smali/smali/b_26594149_7.smali b/test/800-smali/smali/b_26594149_7.smali new file mode 100644 index 0000000000..f624d1adec --- /dev/null +++ b/test/800-smali/smali/b_26594149_7.smali @@ -0,0 +1,30 @@ +# 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 LB26594149_7; +.super Ljava/lang/Object; + +.method private static foo(Ljava/lang/Object;)V + .registers 1 + return-void +.end method + +.method public static run()V + .registers 4 + + new-instance v0, Ljava/lang/Object; + invoke-static {v0}, LB26594149_7;->foo(Ljava/lang/Object;)V + return-void + + .end method diff --git a/test/800-smali/smali/b_26594149_8.smali b/test/800-smali/smali/b_26594149_8.smali new file mode 100644 index 0000000000..e366de42eb --- /dev/null +++ b/test/800-smali/smali/b_26594149_8.smali @@ -0,0 +1,24 @@ +# 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 LB26594149_8; +.super Ljava/lang/Object; + +.method public static run()Ljava/lang/Object; + .registers 4 + + new-instance v0, Ljava/lang/Object; + return-object v0 + + .end method diff --git a/test/800-smali/src/Main.java b/test/800-smali/src/Main.java index 38aa58de77..b0eff5d0bd 100644 --- a/test/800-smali/src/Main.java +++ b/test/800-smali/src/Main.java @@ -145,6 +145,21 @@ public class Main { new AbstractMethodError(), null)); testCases.add(new TestCase("b/26579108", "B26579108", "run", null, new VerifyError(), null)); + testCases.add(new TestCase("b/26594149 (1)", "B26594149_1", "run", null, new VerifyError(), + null)); + testCases.add(new TestCase("b/26594149 (2)", "B26594149_2", "run", null, new VerifyError(), + null)); + testCases.add(new TestCase("b/26594149 (3)", "B26594149_3", "run", null, new VerifyError(), + null)); + testCases.add(new TestCase("b/26594149 (4)", "B26594149_4", "run", null, new VerifyError(), + null)); + testCases.add(new TestCase("b/26594149 (5)", "B26594149_5", "run", null, null, null)); + testCases.add(new TestCase("b/26594149 (6)", "B26594149_6", "run", null, new VerifyError(), + null)); + testCases.add(new TestCase("b/26594149 (7)", "B26594149_7", "run", null, new VerifyError(), + null)); + testCases.add(new TestCase("b/26594149 (8)", "B26594149_8", "run", null, new VerifyError(), + null)); } public void runTests() { diff --git a/test/972-iface-super-multidex/expected.txt b/test/972-iface-super-multidex/expected.txt new file mode 100644 index 0000000000..a9d31a5320 --- /dev/null +++ b/test/972-iface-super-multidex/expected.txt @@ -0,0 +1,2 @@ +SuperInterface default method called +Expected ICCE caught diff --git a/test/972-iface-super-multidex/info.txt b/test/972-iface-super-multidex/info.txt new file mode 100644 index 0000000000..f7948ad2a5 --- /dev/null +++ b/test/972-iface-super-multidex/info.txt @@ -0,0 +1,3 @@ +Smali-based tests for experimental interface default methods. + +Obviously needs to run under ART or a Java 8 Language runtime and compiler. diff --git a/test/972-iface-super-multidex/smali-multidex/conflictinterface.smali b/test/972-iface-super-multidex/smali-multidex/conflictinterface.smali new file mode 100644 index 0000000000..2c76213015 --- /dev/null +++ b/test/972-iface-super-multidex/smali-multidex/conflictinterface.smali @@ -0,0 +1,23 @@ +# +# 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 abstract interface LConflictInterface; +.super Ljava/lang/Object; +.implements LOneConflict; +.implements LTwoConflict; + +# public interface ConflictInterface extends OneConflict, TwoConflict { +# } diff --git a/test/972-iface-super-multidex/smali-multidex/oneconflict.smali b/test/972-iface-super-multidex/smali-multidex/oneconflict.smali new file mode 100644 index 0000000000..7001f02c0f --- /dev/null +++ b/test/972-iface-super-multidex/smali-multidex/oneconflict.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 abstract interface LOneConflict; +.super Ljava/lang/Object; + +# public interface OneConflict { +# public String runDefault() { +# return "OneConflict default method called"; +# } +# } + +.method public runDefault()Ljava/lang/String; +.registers 2 + # Do an invoke super on this class, to confuse runtime/compiler. + const-string v0, "OneConflict default method called" + return-object v0 +.end method diff --git a/test/972-iface-super-multidex/smali-multidex/superinterface.smali b/test/972-iface-super-multidex/smali-multidex/superinterface.smali new file mode 100644 index 0000000000..d45ecea045 --- /dev/null +++ b/test/972-iface-super-multidex/smali-multidex/superinterface.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 abstract interface LSuperInterface; +.super Ljava/lang/Object; + +# public interface SuperInterface { +# public String runDefault() { +# return "SuperInterface default method called"; +# } +# } + +.method public runDefault()Ljava/lang/String; +.registers 2 + # Do an invoke super on this class, to confuse runtime/compiler. + const-string v0, "SuperInterface default method called" + return-object v0 +.end method diff --git a/test/972-iface-super-multidex/smali-multidex/twoconflict.smali b/test/972-iface-super-multidex/smali-multidex/twoconflict.smali new file mode 100644 index 0000000000..b971b74649 --- /dev/null +++ b/test/972-iface-super-multidex/smali-multidex/twoconflict.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 abstract interface LTwoConflict; +.super Ljava/lang/Object; + +# public interface TwoConflict { +# public String runDefault() { +# return "TwoConflict default method called"; +# } +# } + +.method public runDefault()Ljava/lang/String; +.registers 2 + # Do an invoke super on this class, to confuse runtime/compiler. + const-string v0, "TwoConflict default method called" + return-object v0 +.end method diff --git a/test/972-iface-super-multidex/smali/concreteclass.smali b/test/972-iface-super-multidex/smali/concreteclass.smali new file mode 100644 index 0000000000..703da945cc --- /dev/null +++ b/test/972-iface-super-multidex/smali/concreteclass.smali @@ -0,0 +1,62 @@ +# +# 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 LConcreteClass; +.super Ljava/lang/Object; +.implements LSuperInterface; +.implements LConflictInterface; + +# public class ConcreteClass implements SuperInterface, ConflictInterface { +# public String runReal() { +# return SuperInterface.super.runDefault(); +# } +# public String runConflict() { +# return ConflictInterface.super.runDefault(); +# } +# public String runDefault() { +# return "This is the wrong class to invoke"; +# } +# } + +.method public constructor <init>()V + .registers 1 + invoke-direct {p0}, Ljava/lang/Object;-><init>()V + return-void +.end method + +.method public runConflict()Ljava/lang/String; +.registers 2 + # Do an invoke super on this class, to confuse runtime/compiler. + invoke-super {p0}, LConflictInterface;->runDefault()Ljava/lang/String; + move-result-object v0 + return-object v0 +.end method + + + +.method public runReal()Ljava/lang/String; +.registers 2 + # Do an invoke super on this class, to confuse runtime/compiler. + invoke-super {p0}, LSuperInterface;->runDefault()Ljava/lang/String; + move-result-object v0 + return-object v0 +.end method + +.method public runDefault()Ljava/lang/String; +.registers 2 + const-string v0, "This is the wrong class to invoke!" + return-object v0 +.end method diff --git a/test/972-iface-super-multidex/src/Main.java b/test/972-iface-super-multidex/src/Main.java new file mode 100644 index 0000000000..3fb3f45428 --- /dev/null +++ b/test/972-iface-super-multidex/src/Main.java @@ -0,0 +1,55 @@ +/* + * 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.*; +public class Main { + public static void main(String[] args) { + Class<?> c = null; + try { + c = Class.forName("ConcreteClass"); + } catch (Exception e) { + System.out.println("Could not load class"); + e.printStackTrace(); + return; + } + try { + Method m = c.getMethod("runReal"); + System.out.println((String)m.invoke(c.newInstance(), new Object[0])); + } catch (Exception e) { + System.out.println("Unknown exception occurred"); + e.printStackTrace(); + } + try { + Method m = c.getMethod("runConflict"); + try { + System.out.println((String)m.invoke(c.newInstance(), new Object[0])); + } catch (InvocationTargetException e) { + throw e.getCause(); + } + } catch (AbstractMethodError e) { + System.out.println("Unexpected AME caught"); + e.printStackTrace(); + } catch (NoSuchMethodError e) { + System.out.println("Unexpected NSME caught"); + e.printStackTrace(); + } catch (IncompatibleClassChangeError e) { + System.out.println("Expected ICCE caught"); + } catch (Throwable e) { + System.out.println("Unknown exception caught!"); + e.printStackTrace(); + } + } +} diff --git a/test/973-default-multidex/expected.txt b/test/973-default-multidex/expected.txt new file mode 100644 index 0000000000..b376e81554 --- /dev/null +++ b/test/973-default-multidex/expected.txt @@ -0,0 +1 @@ +STRING!!!STRING!!! diff --git a/test/973-default-multidex/info.txt b/test/973-default-multidex/info.txt new file mode 100644 index 0000000000..17c0b7d279 --- /dev/null +++ b/test/973-default-multidex/info.txt @@ -0,0 +1,5 @@ +Smali-based tests for interface default methods. + +Obviously needs to run under ART or a Java 8 Language runtime and compiler. + +Tests that we handle referenced throws across dex files. diff --git a/test/973-default-multidex/smali-multidex/iface.smali b/test/973-default-multidex/smali-multidex/iface.smali new file mode 100644 index 0000000000..fa6d27f527 --- /dev/null +++ b/test/973-default-multidex/smali-multidex/iface.smali @@ -0,0 +1,40 @@ +# +# 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 abstract interface LIface; +.super Ljava/lang/Object; + +# public interface Iface { +# public default String getTwice() { +# return getString() + getString(); +# } +# public String getString(); +# } + +.method public getTwice()Ljava/lang/String; +.locals 2 + invoke-static {p0}, Ljava/util/Objects;->requireNonNull(Ljava/lang/Object;)Ljava/lang/Object; + invoke-interface {p0}, LIface;->getString()Ljava/lang/String; + move-result-object v0 + invoke-interface {p0}, LIface;->getString()Ljava/lang/String; + move-result-object v1 + invoke-virtual {v0, v1}, Ljava/lang/String;->concat(Ljava/lang/String;)Ljava/lang/String; + move-result-object v0 + return-object v0 +.end method + +.method public abstract getString()Ljava/lang/String; +.end method diff --git a/test/973-default-multidex/smali/concreteclass.smali b/test/973-default-multidex/smali/concreteclass.smali new file mode 100644 index 0000000000..e177f2698a --- /dev/null +++ b/test/973-default-multidex/smali/concreteclass.smali @@ -0,0 +1,47 @@ +# +# 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 LConcreteClass; +.super Ljava/lang/Object; +.implements LIface; + +# public class ConcreteClass implements Iface { +# public String getString() { +# return "STRING!!!"; +# } +# public String callMethod() { +# return this.getTwice(); +# } +# } + +.method public constructor <init>()V + .registers 1 + invoke-direct {p0}, Ljava/lang/Object;-><init>()V + return-void +.end method + +.method public getString()Ljava/lang/String; +.registers 2 + const-string v0, "STRING!!!" + return-object v0 +.end method + +.method public callMethod()Ljava/lang/String; +.registers 2 + invoke-virtual {p0}, LConcreteClass;->getTwice()Ljava/lang/String; + move-result-object v0 + return-object v0 +.end method diff --git a/test/973-default-multidex/src/Main.java b/test/973-default-multidex/src/Main.java new file mode 100644 index 0000000000..b93265a5b8 --- /dev/null +++ b/test/973-default-multidex/src/Main.java @@ -0,0 +1,31 @@ +/* + * 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.*; +public class Main { + public static void main(String[] args) { + Class<?> c = null; + try { + c = Class.forName("ConcreteClass"); + Method m = c.getMethod("callMethod"); + System.out.println(m.invoke(c.newInstance(), new Object[0])); + } catch (Exception e) { + e.printStackTrace(); + System.out.println("FAILED: Could not call method"); + return; + } + } +} diff --git a/test/etc/default-build b/test/etc/default-build index 7242428f1e..6e855ec30a 100755 --- a/test/etc/default-build +++ b/test/etc/default-build @@ -42,6 +42,12 @@ else HAS_SRC_MULTIDEX=false fi +if [ -d smali-multidex ]; then + HAS_SMALI_MULTIDEX=true +else + HAS_SMALI_MULTIDEX=false +fi + if [ -d src-ex ]; then HAS_SRC_EX=true else @@ -74,6 +80,9 @@ while true; do elif [ "x$1" = "x--no-src-multidex" ]; then HAS_SRC_MULTIDEX=false shift + elif [ "x$1" = "x--no-smali-multidex" ]; then + HAS_SMALI_MULTIDEX=false + shift elif [ "x$1" = "x--no-src-ex" ]; then HAS_SRC_EX=false shift @@ -171,6 +180,19 @@ if [ "${HAS_SMALI}" = "true" ]; then fi fi +if [ "${HAS_SMALI_MULTIDEX}" = "true" ]; then + # Compile Smali classes + ${SMALI} -JXmx512m ${SMALI_ARGS} --output smali_classes2.dex `find smali-multidex -name '*.smali'` + + # Don't bother with dexmerger if we provide our own main function in a smali file. + if [ ${HAS_SRC_MULTIDEX} = "true" ]; then + ${DXMERGER} classes2.dex classes2.dex smali_classes2.dex + else + mv smali_classes2.dex classes2.dex + fi +fi + + if [ ${HAS_SRC_EX} = "true" ]; then if [ ${USE_JACK} = "true" ]; then # Rename previous "classes.dex" so it is not overwritten. @@ -198,7 +220,7 @@ if [ ${HAS_SRC_EX} = "true" ]; then fi # Create a single jar with two dex files for multidex. -if [ ${HAS_SRC_MULTIDEX} = "true" ]; then +if [ ${HAS_SRC_MULTIDEX} = "true" ] || [ ${HAS_SMALI_MULTIDEX} = "true" ]; then zip $TEST_NAME.jar classes.dex classes2.dex elif [ ${NEED_DEX} = "true" ]; then zip $TEST_NAME.jar classes.dex diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt index f0afba5228..6d67f8477d 100644 --- a/tools/libcore_failures.txt +++ b/tools/libcore_failures.txt @@ -266,11 +266,5 @@ "libcore.util.NativeAllocationRegistryTest#testNativeAllocationNoAllocatorAndNoSharedRegistry", "libcore.util.NativeAllocationRegistryTest#testNativeAllocationNoAllocatorAndSharedRegistry", "libcore.util.NativeAllocationRegistryTest#testNullArguments"] -}, -{ - description: "Expected to fail", - bug: 26869497, - result: EXEC_FAILED, - names: ["libcore.java.util.CalendarTest#testSetHourOfDayInEuropeLondon"] } ] |