summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/driver/compiler_driver-inl.h50
-rw-r--r--compiler/driver/compiler_options.h4
-rw-r--r--compiler/image_writer.cc14
-rw-r--r--compiler/image_writer.h2
-rw-r--r--compiler/optimizing/builder.cc66
-rw-r--r--compiler/optimizing/code_generator_x86_64.cc9
-rw-r--r--compiler/optimizing/code_generator_x86_64.h4
-rw-r--r--compiler/optimizing/intrinsics_arm64.cc50
-rw-r--r--compiler/optimizing/intrinsics_x86_64.cc311
-rw-r--r--compiler/optimizing/nodes.h28
-rw-r--r--dex2oat/dex2oat.cc24
-rw-r--r--runtime/atomic.h4
-rw-r--r--runtime/class_linker-inl.h24
-rw-r--r--runtime/class_linker.cc23
-rw-r--r--runtime/class_linker.h14
-rw-r--r--runtime/entrypoints/quick/quick_trampoline_entrypoints.cc11
-rw-r--r--runtime/fault_handler.cc77
-rw-r--r--runtime/fault_handler.h4
-rw-r--r--runtime/gc/heap-inl.h4
-rw-r--r--runtime/gc/space/image_space.cc14
-rw-r--r--runtime/jni_internal.h7
-rw-r--r--runtime/native/java_lang_Object.cc14
-rw-r--r--runtime/native/sun_misc_Unsafe.cc60
-rw-r--r--runtime/oat.cc4
-rw-r--r--runtime/oat.h2
-rw-r--r--runtime/oat_file.cc4
-rw-r--r--runtime/oat_file.h2
-rw-r--r--runtime/oat_file_assistant.cc22
-rw-r--r--runtime/oat_file_assistant_test.cc71
-rw-r--r--runtime/read_barrier.h1
-rw-r--r--runtime/verifier/method_verifier.cc109
-rw-r--r--runtime/verifier/reg_type-inl.h4
-rw-r--r--runtime/verifier/register_line-inl.h3
-rw-r--r--test/144-static-field-sigquit/expected.txt4
-rw-r--r--test/144-static-field-sigquit/info.txt8
-rw-r--r--test/144-static-field-sigquit/src/ClassWithStaticField.java21
-rw-r--r--test/144-static-field-sigquit/src/Main.java32
-rw-r--r--test/144-static-field-sigquit/src/SigQuit.java68
-rw-r--r--test/144-static-field-sigquit/src/SynchronizedUse.java26
-rwxr-xr-xtest/563-checker-invoke-super/build28
-rw-r--r--test/563-checker-invoke-super/expected.txt0
-rw-r--r--test/563-checker-invoke-super/info.txt2
-rw-r--r--test/563-checker-invoke-super/src/Main.java39
-rw-r--r--test/568-checker-onebit/src/Main.java8
-rw-r--r--test/800-smali/expected.txt8
-rw-r--r--test/800-smali/smali/b_26594149_1.smali26
-rw-r--r--test/800-smali/smali/b_26594149_2.smali26
-rw-r--r--test/800-smali/smali/b_26594149_3.smali28
-rw-r--r--test/800-smali/smali/b_26594149_4.smali38
-rw-r--r--test/800-smali/smali/b_26594149_5.smali28
-rw-r--r--test/800-smali/smali/b_26594149_6.smali24
-rw-r--r--test/800-smali/smali/b_26594149_7.smali30
-rw-r--r--test/800-smali/smali/b_26594149_8.smali24
-rw-r--r--test/800-smali/src/Main.java15
-rw-r--r--test/972-iface-super-multidex/expected.txt2
-rw-r--r--test/972-iface-super-multidex/info.txt3
-rw-r--r--test/972-iface-super-multidex/smali-multidex/conflictinterface.smali23
-rw-r--r--test/972-iface-super-multidex/smali-multidex/oneconflict.smali31
-rw-r--r--test/972-iface-super-multidex/smali-multidex/superinterface.smali31
-rw-r--r--test/972-iface-super-multidex/smali-multidex/twoconflict.smali31
-rw-r--r--test/972-iface-super-multidex/smali/concreteclass.smali62
-rw-r--r--test/972-iface-super-multidex/src/Main.java55
-rw-r--r--test/973-default-multidex/expected.txt1
-rw-r--r--test/973-default-multidex/info.txt5
-rw-r--r--test/973-default-multidex/smali-multidex/iface.smali40
-rw-r--r--test/973-default-multidex/smali/concreteclass.smali47
-rw-r--r--test/973-default-multidex/src/Main.java31
-rwxr-xr-xtest/etc/default-build24
-rw-r--r--tools/libcore_failures.txt6
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(&reg_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"]
}
]