summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build/Android.gtest.mk11
-rw-r--r--compiler/optimizing/intrinsics_arm64.cc162
-rw-r--r--runtime/Android.bp1
-rw-r--r--runtime/art_method.cc48
-rw-r--r--runtime/dex2oat_environment_test.h4
-rw-r--r--runtime/dex_file.cc40
-rw-r--r--runtime/dex_file.h21
-rw-r--r--runtime/dex_file_test.cc26
-rw-r--r--runtime/gc/space/image_space.cc86
-rw-r--r--runtime/gc/space/image_space.h11
-rw-r--r--runtime/gc/space/image_space_test.cc111
-rw-r--r--runtime/mirror/string-inl.h9
-rw-r--r--runtime/mirror/string.h4
-rw-r--r--runtime/oat.h2
-rw-r--r--runtime/oat_file.cc71
-rw-r--r--runtime/oat_file.h9
-rw-r--r--runtime/oat_file_assistant.cc163
-rw-r--r--runtime/oat_file_assistant.h24
-rw-r--r--runtime/oat_file_assistant_test.cc49
-rw-r--r--runtime/oat_file_test.cc50
-rw-r--r--runtime/openjdkjvmti/ti_redefine.cc128
-rw-r--r--runtime/openjdkjvmti/ti_redefine.h6
-rw-r--r--runtime/stack.cc12
-rw-r--r--test/021-string2/src/Main.java263
-rw-r--r--test/536-checker-intrinsic-optimization/src/Main.java6
-rwxr-xr-xtest/945-obsolete-native/build17
-rw-r--r--test/945-obsolete-native/expected.txt9
-rw-r--r--test/945-obsolete-native/info.txt1
-rw-r--r--test/945-obsolete-native/obsolete_native.cc51
-rwxr-xr-xtest/945-obsolete-native/run17
-rw-r--r--test/945-obsolete-native/src/Main.java77
-rw-r--r--test/945-obsolete-native/src/Transform.java25
-rw-r--r--test/Android.bp1
-rw-r--r--test/ti-agent/common_load.cc1
-rwxr-xr-xtools/run-libcore-tests.sh3
35 files changed, 831 insertions, 688 deletions
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 5c49f193a5..d376f291cd 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -92,7 +92,7 @@ ART_GTEST_class_linker_test_DEX_DEPS := AllFields ErroneousA ErroneousB Erroneou
ART_GTEST_class_table_test_DEX_DEPS := XandY
ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod StaticLeafMethods ProfileTestMultiDex
ART_GTEST_dex_cache_test_DEX_DEPS := Main Packages MethodTypes
-ART_GTEST_dex_file_test_DEX_DEPS := GetMethodSignature Main Nested
+ART_GTEST_dex_file_test_DEX_DEPS := GetMethodSignature Main Nested MultiDex
ART_GTEST_dex2oat_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) Statics VerifierDeps
ART_GTEST_exception_test_DEX_DEPS := ExceptionHandle
ART_GTEST_image_test_DEX_DEPS := ImageLayoutA ImageLayoutB
@@ -102,6 +102,7 @@ ART_GTEST_jni_compiler_test_DEX_DEPS := MyClassNatives
ART_GTEST_jni_internal_test_DEX_DEPS := AllFields StaticLeafMethods
ART_GTEST_oat_file_assistant_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS)
ART_GTEST_dexoptanalyzer_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS)
+ART_GTEST_image_space_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS)
ART_GTEST_oat_file_test_DEX_DEPS := Main MultiDex
ART_GTEST_oat_test_DEX_DEPS := Main
ART_GTEST_object_test_DEX_DEPS := ProtoCompare ProtoCompare2 StaticsFromCode XandY
@@ -146,6 +147,11 @@ ART_GTEST_dexoptanalyzer_test_TARGET_DEPS := \
$(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS) \
dexoptanalyzerd
+ART_GTEST_image_space_test_HOST_DEPS := \
+ $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS)
+ART_GTEST_image_space_test_TARGET_DEPS := \
+ $(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS)
+
ART_GTEST_dex2oat_test_HOST_DEPS := \
$(ART_GTEST_dex2oat_environment_tests_HOST_DEPS)
ART_GTEST_dex2oat_test_TARGET_DEPS := \
@@ -627,6 +633,9 @@ ART_GTEST_oat_file_assistant_test_TARGET_DEPS :=
ART_GTEST_dexoptanalyzer_test_DEX_DEPS :=
ART_GTEST_dexoptanalyzer_test_HOST_DEPS :=
ART_GTEST_dexoptanalyzer_test_TARGET_DEPS :=
+ART_GTEST_image_space_test_DEX_DEPS :=
+ART_GTEST_image_space_test_HOST_DEPS :=
+ART_GTEST_image_space_test_TARGET_DEPS :=
ART_GTEST_dex2oat_test_DEX_DEPS :=
ART_GTEST_dex2oat_test_HOST_DEPS :=
ART_GTEST_dex2oat_test_TARGET_DEPS :=
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index 1047d3beb6..86e54294ae 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -23,7 +23,7 @@
#include "entrypoints/quick/quick_entrypoints.h"
#include "intrinsics.h"
#include "mirror/array-inl.h"
-#include "mirror/string.h"
+#include "mirror/string-inl.h"
#include "thread.h"
#include "utils/arm64/assembler_arm64.h"
@@ -1450,16 +1450,47 @@ void IntrinsicCodeGeneratorARM64::VisitStringCompareTo(HInvoke* invoke) {
}
}
+// The cut off for unrolling the loop in String.equals() intrinsic for const strings.
+// The normal loop plus the pre-header is 9 instructions without string compression and 12
+// instructions with string compression. We can compare up to 8 bytes in 4 instructions
+// (LDR+LDR+CMP+BNE) and up to 16 bytes in 5 instructions (LDP+LDP+CMP+CCMP+BNE). Allow up
+// to 10 instructions for the unrolled loop.
+constexpr size_t kShortConstStringEqualsCutoffInBytes = 32;
+
+static const char* GetConstString(HInstruction* candidate, uint32_t* utf16_length) {
+ if (candidate->IsLoadString()) {
+ HLoadString* load_string = candidate->AsLoadString();
+ const DexFile& dex_file = load_string->GetDexFile();
+ return dex_file.StringDataAndUtf16LengthByIdx(load_string->GetStringIndex(), utf16_length);
+ }
+ return nullptr;
+}
+
void IntrinsicLocationsBuilderARM64::VisitStringEquals(HInvoke* invoke) {
LocationSummary* locations = new (arena_) LocationSummary(invoke,
LocationSummary::kNoCall,
kIntrinsified);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RequiresRegister());
- // Temporary registers to store lengths of strings and for calculations.
- locations->AddTemp(Location::RequiresRegister());
- locations->AddTemp(Location::RequiresRegister());
+ // For the generic implementation and for long const strings we need a temporary.
+ // We do not need it for short const strings, up to 8 bytes, see code generation below.
+ uint32_t const_string_length = 0u;
+ const char* const_string = GetConstString(invoke->InputAt(0), &const_string_length);
+ if (const_string == nullptr) {
+ const_string = GetConstString(invoke->InputAt(1), &const_string_length);
+ }
+ bool is_compressed =
+ mirror::kUseStringCompression &&
+ const_string != nullptr &&
+ mirror::String::DexFileStringAllASCII(const_string, const_string_length);
+ if (const_string == nullptr || const_string_length > (is_compressed ? 8u : 4u)) {
+ locations->AddTemp(Location::RequiresRegister());
+ }
+
+ // TODO: If the String.equals() is used only for an immediately following HIf, we can
+ // mark it as emitted-at-use-site and emit branches directly to the appropriate blocks.
+ // Then we shall need an extra temporary register instead of the output register.
locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
}
@@ -1473,8 +1504,7 @@ void IntrinsicCodeGeneratorARM64::VisitStringEquals(HInvoke* invoke) {
UseScratchRegisterScope scratch_scope(masm);
Register temp = scratch_scope.AcquireW();
- Register temp1 = WRegisterFrom(locations->GetTemp(0));
- Register temp2 = WRegisterFrom(locations->GetTemp(1));
+ Register temp1 = scratch_scope.AcquireW();
vixl::aarch64::Label loop;
vixl::aarch64::Label end;
@@ -1510,46 +1540,98 @@ void IntrinsicCodeGeneratorARM64::VisitStringEquals(HInvoke* invoke) {
__ B(&return_false, ne);
}
- // Load `count` fields of this and argument strings.
- __ Ldr(temp, MemOperand(str.X(), count_offset));
- __ Ldr(temp1, MemOperand(arg.X(), count_offset));
- // Check if `count` fields are equal, return false if they're not.
- // Also compares the compression style, if differs return false.
- __ Cmp(temp, temp1);
- __ B(&return_false, ne);
- // Return true if both strings are empty. Even with string compression `count == 0` means empty.
- static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
- "Expecting 0=compressed, 1=uncompressed");
- __ Cbz(temp, &return_true);
+ // Check if one of the inputs is a const string. Do not special-case both strings
+ // being const, such cases should be handled by constant folding if needed.
+ uint32_t const_string_length = 0u;
+ const char* const_string = GetConstString(invoke->InputAt(0), &const_string_length);
+ if (const_string == nullptr) {
+ const_string = GetConstString(invoke->InputAt(1), &const_string_length);
+ if (const_string != nullptr) {
+ std::swap(str, arg); // Make sure the const string is in `str`.
+ }
+ }
+ bool is_compressed =
+ mirror::kUseStringCompression &&
+ const_string != nullptr &&
+ mirror::String::DexFileStringAllASCII(const_string, const_string_length);
+
+ if (const_string != nullptr) {
+ // Load `count` field of the argument string and check if it matches the const string.
+ // Also compares the compression style, if differs return false.
+ __ Ldr(temp, MemOperand(arg.X(), count_offset));
+ __ Cmp(temp, Operand(mirror::String::GetFlaggedCount(const_string_length, is_compressed)));
+ __ B(&return_false, ne);
+ } else {
+ // Load `count` fields of this and argument strings.
+ __ Ldr(temp, MemOperand(str.X(), count_offset));
+ __ Ldr(temp1, MemOperand(arg.X(), count_offset));
+ // Check if `count` fields are equal, return false if they're not.
+ // Also compares the compression style, if differs return false.
+ __ Cmp(temp, temp1);
+ __ B(&return_false, ne);
+ }
// Assertions that must hold in order to compare strings 8 bytes at a time.
DCHECK_ALIGNED(value_offset, 8);
static_assert(IsAligned<8>(kObjectAlignment), "String of odd length is not zero padded");
- if (mirror::kUseStringCompression) {
- // For string compression, calculate the number of bytes to compare (not chars).
- // This could in theory exceed INT32_MAX, so treat temp as unsigned.
- __ Lsr(temp, temp, 1u); // Extract length.
- __ And(temp1, temp1, Operand(1)); // Extract compression flag.
- __ Lsl(temp, temp, temp1); // Calculate number of bytes to compare.
- }
-
- // Store offset of string value in preparation for comparison loop
- __ Mov(temp1, value_offset);
+ if (const_string != nullptr &&
+ const_string_length < (is_compressed ? kShortConstStringEqualsCutoffInBytes
+ : kShortConstStringEqualsCutoffInBytes / 2u)) {
+ // Load and compare the contents. Though we know the contents of the short const string
+ // at compile time, materializing constants may be more code than loading from memory.
+ int32_t offset = value_offset;
+ size_t remaining_bytes =
+ RoundUp(is_compressed ? const_string_length : const_string_length * 2u, 8u);
+ temp = temp.X();
+ temp1 = temp1.X();
+ while (remaining_bytes > 8u) {
+ Register temp2 = XRegisterFrom(locations->GetTemp(0));
+ __ Ldp(temp, temp1, MemOperand(str.X(), offset));
+ __ Ldp(temp2, out, MemOperand(arg.X(), offset));
+ __ Cmp(temp, temp2);
+ __ Ccmp(temp1, out, NoFlag, eq);
+ __ B(&return_false, ne);
+ offset += 2u * sizeof(uint64_t);
+ remaining_bytes -= 2u * sizeof(uint64_t);
+ }
+ if (remaining_bytes != 0u) {
+ __ Ldr(temp, MemOperand(str.X(), offset));
+ __ Ldr(temp1, MemOperand(arg.X(), offset));
+ __ Cmp(temp, temp1);
+ __ B(&return_false, ne);
+ }
+ } else {
+ // Return true if both strings are empty. Even with string compression `count == 0` means empty.
+ static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
+ "Expecting 0=compressed, 1=uncompressed");
+ __ Cbz(temp, &return_true);
+
+ if (mirror::kUseStringCompression) {
+ // For string compression, calculate the number of bytes to compare (not chars).
+ // This could in theory exceed INT32_MAX, so treat temp as unsigned.
+ __ And(temp1, temp, Operand(1)); // Extract compression flag.
+ __ Lsr(temp, temp, 1u); // Extract length.
+ __ Lsl(temp, temp, temp1); // Calculate number of bytes to compare.
+ }
- temp1 = temp1.X();
- temp2 = temp2.X();
- // Loop to compare strings 8 bytes at a time starting at the front of the string.
- // Ok to do this because strings are zero-padded to kObjectAlignment.
- __ Bind(&loop);
- __ Ldr(out, MemOperand(str.X(), temp1));
- __ Ldr(temp2, MemOperand(arg.X(), temp1));
- __ Add(temp1, temp1, Operand(sizeof(uint64_t)));
- __ Cmp(out, temp2);
- __ B(&return_false, ne);
- // With string compression, we have compared 8 bytes, otherwise 4 chars.
- __ Sub(temp, temp, Operand(mirror::kUseStringCompression ? 8 : 4), SetFlags);
- __ B(&loop, hi);
+ // Store offset of string value in preparation for comparison loop
+ __ Mov(temp1, value_offset);
+
+ temp1 = temp1.X();
+ Register temp2 = XRegisterFrom(locations->GetTemp(0));
+ // Loop to compare strings 8 bytes at a time starting at the front of the string.
+ // Ok to do this because strings are zero-padded to kObjectAlignment.
+ __ Bind(&loop);
+ __ Ldr(out, MemOperand(str.X(), temp1));
+ __ Ldr(temp2, MemOperand(arg.X(), temp1));
+ __ Add(temp1, temp1, Operand(sizeof(uint64_t)));
+ __ Cmp(out, temp2);
+ __ B(&return_false, ne);
+ // With string compression, we have compared 8 bytes, otherwise 4 chars.
+ __ Sub(temp, temp, Operand(mirror::kUseStringCompression ? 8 : 4), SetFlags);
+ __ B(&loop, hi);
+ }
// Return true and exit the function.
// If loop does not result in returning false, we return true.
diff --git a/runtime/Android.bp b/runtime/Android.bp
index c871301052..d3a81a9add 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -548,6 +548,7 @@ art_cc_test {
"gc/reference_queue_test.cc",
"gc/space/dlmalloc_space_static_test.cc",
"gc/space/dlmalloc_space_random_test.cc",
+ "gc/space/image_space_test.cc",
"gc/space/large_object_space_test.cc",
"gc/space/rosalloc_space_static_test.cc",
"gc/space/rosalloc_space_random_test.cc",
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index 59e6ac0ba3..4902ad42d7 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -441,56 +441,12 @@ static uint32_t GetOatMethodIndexFromMethodIndex(const DexFile& dex_file,
UNREACHABLE();
}
-// We use the method's DexFile and declaring class name to find the OatMethod for an obsolete
-// method. This is extremely slow but we need it if we want to be able to have obsolete native
-// methods since we need this to find the size of its stack frames.
-//
-// NB We could (potentially) do this differently and rely on the way the transformation is applied
-// in order to use the entrypoint to find this information. However, for debugging reasons (most
-// notably making sure that new invokes of obsolete methods fail) we choose to instead get the data
-// directly from the dex file.
-static const OatFile::OatMethod FindOatMethodFromDexFileFor(ArtMethod* method, bool* found)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- DCHECK(method->IsObsolete() && method->IsNative());
- const DexFile* dex_file = method->GetDexFile();
-
- // recreate the class_def_index from the descriptor.
- std::string descriptor_storage;
- const DexFile::TypeId* declaring_class_type_id =
- dex_file->FindTypeId(method->GetDeclaringClass()->GetDescriptor(&descriptor_storage));
- CHECK(declaring_class_type_id != nullptr);
- dex::TypeIndex declaring_class_type_index = dex_file->GetIndexForTypeId(*declaring_class_type_id);
- const DexFile::ClassDef* declaring_class_type_def =
- dex_file->FindClassDef(declaring_class_type_index);
- CHECK(declaring_class_type_def != nullptr);
- uint16_t declaring_class_def_index = dex_file->GetIndexForClassDef(*declaring_class_type_def);
-
- size_t oat_method_index = GetOatMethodIndexFromMethodIndex(*dex_file,
- declaring_class_def_index,
- method->GetDexMethodIndex());
-
- OatFile::OatClass oat_class = OatFile::FindOatClass(*dex_file,
- declaring_class_def_index,
- found);
- if (!(*found)) {
- return OatFile::OatMethod::Invalid();
- }
- return oat_class.GetOatMethod(oat_method_index);
-}
-
static const OatFile::OatMethod FindOatMethodFor(ArtMethod* method,
PointerSize pointer_size,
bool* found)
REQUIRES_SHARED(Locks::mutator_lock_) {
- if (UNLIKELY(method->IsObsolete())) {
- // We shouldn't be calling this with obsolete methods except for native obsolete methods for
- // which we need to use the oat method to figure out how large the quick frame is.
- DCHECK(method->IsNative()) << "We should only be finding the OatMethod of obsolete methods in "
- << "order to allow stack walking. Other obsolete methods should "
- << "never need to access this information.";
- DCHECK_EQ(pointer_size, kRuntimePointerSize) << "Obsolete method in compiler!";
- return FindOatMethodFromDexFileFor(method, found);
- }
+ // We shouldn't be calling this with obsolete methods.
+ DCHECK(!method->IsObsolete());
// Although we overwrite the trampoline of non-static methods, we may get here via the resolution
// method for direct methods (or virtual methods made direct).
mirror::Class* declaring_class = method->GetDeclaringClass();
diff --git a/runtime/dex2oat_environment_test.h b/runtime/dex2oat_environment_test.h
index 8b0c51c998..e58c6f541e 100644
--- a/runtime/dex2oat_environment_test.h
+++ b/runtime/dex2oat_environment_test.h
@@ -53,7 +53,7 @@ class Dex2oatEnvironmentTest : public CommonRuntimeTest {
ASSERT_EQ(0, mkdir(odex_dir_.c_str(), 0700));
// Verify the environment is as we expect
- uint32_t checksum;
+ std::vector<uint32_t> checksums;
std::string error_msg;
ASSERT_TRUE(OS::FileExists(GetSystemImageFile().c_str()))
<< "Expected pre-compiled boot image to be at: " << GetSystemImageFile();
@@ -61,7 +61,7 @@ class Dex2oatEnvironmentTest : public CommonRuntimeTest {
<< "Expected dex file to be at: " << GetDexSrc1();
ASSERT_TRUE(OS::FileExists(GetStrippedDexSrc1().c_str()))
<< "Expected stripped dex file to be at: " << GetStrippedDexSrc1();
- ASSERT_FALSE(DexFile::GetChecksum(GetStrippedDexSrc1().c_str(), &checksum, &error_msg))
+ ASSERT_FALSE(DexFile::GetMultiDexChecksums(GetStrippedDexSrc1().c_str(), &checksums, &error_msg))
<< "Expected stripped dex file to be stripped: " << GetStrippedDexSrc1();
ASSERT_TRUE(OS::FileExists(GetDexSrc2().c_str()))
<< "Expected dex file to be at: " << GetDexSrc2();
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index 02ba33ceb1..9ad4063ce1 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -72,23 +72,13 @@ struct DexFile::AnnotationValue {
uint8_t type_;
};
-bool DexFile::GetChecksum(const char* filename, uint32_t* checksum, std::string* error_msg) {
- CHECK(checksum != nullptr);
+bool DexFile::GetMultiDexChecksums(const char* filename,
+ std::vector<uint32_t>* checksums,
+ std::string* error_msg) {
+ CHECK(checksums != nullptr);
uint32_t magic;
- // Strip ":...", which is the location
- const char* zip_entry_name = kClassesDex;
- const char* file_part = filename;
- std::string file_part_storage;
-
- if (DexFile::IsMultiDexLocation(filename)) {
- file_part_storage = GetBaseLocation(filename);
- file_part = file_part_storage.c_str();
- zip_entry_name = filename + file_part_storage.size() + 1;
- DCHECK_EQ(zip_entry_name[-1], kMultiDexSeparator);
- }
-
- File fd = OpenAndReadMagic(file_part, &magic, error_msg);
+ File fd = OpenAndReadMagic(filename, &magic, error_msg);
if (fd.Fd() == -1) {
DCHECK(!error_msg->empty());
return false;
@@ -97,17 +87,25 @@ bool DexFile::GetChecksum(const char* filename, uint32_t* checksum, std::string*
std::unique_ptr<ZipArchive> zip_archive(
ZipArchive::OpenFromFd(fd.Release(), filename, error_msg));
if (zip_archive.get() == nullptr) {
- *error_msg = StringPrintf("Failed to open zip archive '%s' (error msg: %s)", file_part,
+ *error_msg = StringPrintf("Failed to open zip archive '%s' (error msg: %s)", filename,
error_msg->c_str());
return false;
}
- std::unique_ptr<ZipEntry> zip_entry(zip_archive->Find(zip_entry_name, error_msg));
+
+ uint32_t i = 0;
+ std::string zip_entry_name = GetMultiDexClassesDexName(i++);
+ std::unique_ptr<ZipEntry> zip_entry(zip_archive->Find(zip_entry_name.c_str(), error_msg));
if (zip_entry.get() == nullptr) {
- *error_msg = StringPrintf("Zip archive '%s' doesn't contain %s (error msg: %s)", file_part,
- zip_entry_name, error_msg->c_str());
+ *error_msg = StringPrintf("Zip archive '%s' doesn't contain %s (error msg: %s)", filename,
+ zip_entry_name.c_str(), error_msg->c_str());
return false;
}
- *checksum = zip_entry->GetCrc32();
+
+ do {
+ checksums->push_back(zip_entry->GetCrc32());
+ zip_entry_name = DexFile::GetMultiDexClassesDexName(i++);
+ zip_entry.reset(zip_archive->Find(zip_entry_name.c_str(), error_msg));
+ } while (zip_entry.get() != nullptr);
return true;
}
if (IsDexMagic(magic)) {
@@ -116,7 +114,7 @@ bool DexFile::GetChecksum(const char* filename, uint32_t* checksum, std::string*
if (dex_file.get() == nullptr) {
return false;
}
- *checksum = dex_file->GetHeader().checksum_;
+ checksums->push_back(dex_file->GetHeader().checksum_);
return true;
}
*error_msg = StringPrintf("Expected valid zip or dex file: '%s'", filename);
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index 20bd52b060..58b8e792ee 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -424,11 +424,18 @@ class DexFile {
struct AnnotationValue;
- // Returns the checksum of a file for comparison with GetLocationChecksum().
- // For .dex files, this is the header checksum.
- // For zip files, this is the classes.dex zip entry CRC32 checksum.
- // Return true if the checksum could be found, false otherwise.
- static bool GetChecksum(const char* filename, uint32_t* checksum, std::string* error_msg);
+ // Returns the checksums of a file for comparison with GetLocationChecksum().
+ // For .dex files, this is the single header checksum.
+ // For zip files, this is the zip entry CRC32 checksum for classes.dex and
+ // each additional multidex entry classes2.dex, classes3.dex, etc.
+ // Return true if the checksums could be found, false otherwise.
+ static bool GetMultiDexChecksums(const char* filename,
+ std::vector<uint32_t>* checksums,
+ std::string* error_msg);
+
+ // Check whether a location denotes a multidex dex file. This is a very simple check: returns
+ // whether the string contains the separator character.
+ static bool IsMultiDexLocation(const char* location);
// Opens .dex file, backed by existing memory
static std::unique_ptr<const DexFile> Open(const uint8_t* base,
@@ -1161,10 +1168,6 @@ class DexFile {
// Initialize section info for sections only found in map. Returns true on success.
void InitializeSectionsFromMapList();
- // Check whether a location denotes a multidex dex file. This is a very simple check: returns
- // whether the string contains the separator character.
- static bool IsMultiDexLocation(const char* location);
-
// The base address of the memory mapping.
const uint8_t* const begin_;
diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc
index 9dca4c0621..9131715fec 100644
--- a/runtime/dex_file_test.cc
+++ b/runtime/dex_file_test.cc
@@ -326,12 +326,32 @@ TEST_F(DexFileTest, GetLocationChecksum) {
}
TEST_F(DexFileTest, GetChecksum) {
- uint32_t checksum;
+ std::vector<uint32_t> checksums;
ScopedObjectAccess soa(Thread::Current());
std::string error_msg;
- EXPECT_TRUE(DexFile::GetChecksum(GetLibCoreDexFileNames()[0].c_str(), &checksum, &error_msg))
+ EXPECT_TRUE(DexFile::GetMultiDexChecksums(GetLibCoreDexFileNames()[0].c_str(), &checksums, &error_msg))
<< error_msg;
- EXPECT_EQ(java_lang_dex_file_->GetLocationChecksum(), checksum);
+ ASSERT_EQ(1U, checksums.size());
+ EXPECT_EQ(java_lang_dex_file_->GetLocationChecksum(), checksums[0]);
+}
+
+TEST_F(DexFileTest, GetMultiDexChecksums) {
+ std::string error_msg;
+ std::vector<uint32_t> checksums;
+ std::string multidex_file = GetTestDexFileName("MultiDex");
+ EXPECT_TRUE(DexFile::GetMultiDexChecksums(multidex_file.c_str(),
+ &checksums,
+ &error_msg)) << error_msg;
+
+ std::vector<std::unique_ptr<const DexFile>> dexes = OpenTestDexFiles("MultiDex");
+ ASSERT_EQ(2U, dexes.size());
+ ASSERT_EQ(2U, checksums.size());
+
+ EXPECT_EQ(dexes[0]->GetLocation(), DexFile::GetMultiDexLocation(0, multidex_file.c_str()));
+ EXPECT_EQ(dexes[0]->GetLocationChecksum(), checksums[0]);
+
+ EXPECT_EQ(dexes[1]->GetLocation(), DexFile::GetMultiDexLocation(1, multidex_file.c_str()));
+ EXPECT_EQ(dexes[1]->GetLocationChecksum(), checksums[1]);
}
TEST_F(DexFileTest, ClassDefs) {
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 55bd1d4736..2163a20e87 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -692,7 +692,7 @@ class ImageSpaceLoader {
if (validate_oat_file) {
TimingLogger::ScopedTiming timing("ValidateOatFile", &logger);
CHECK(space->oat_file_ != nullptr);
- if (!ValidateOatFile(*space, *space->oat_file_, error_msg)) {
+ if (!ImageSpace::ValidateOatFile(*space->oat_file_, error_msg)) {
DCHECK(!error_msg->empty());
return nullptr;
}
@@ -1387,33 +1387,6 @@ class ImageSpaceLoader {
return oat_file;
}
-
- static bool ValidateOatFile(const ImageSpace& space,
- const OatFile& oat_file,
- std::string* error_msg) {
- for (const OatFile::OatDexFile* oat_dex_file : oat_file.GetOatDexFiles()) {
- const std::string& dex_file_location = oat_dex_file->GetDexFileLocation();
- uint32_t dex_file_location_checksum;
- if (!DexFile::GetChecksum(dex_file_location.c_str(), &dex_file_location_checksum, error_msg)) {
- *error_msg = StringPrintf("Failed to get checksum of dex file '%s' referenced by image %s: "
- "%s",
- dex_file_location.c_str(),
- space.GetName(),
- error_msg->c_str());
- return false;
- }
- if (dex_file_location_checksum != oat_dex_file->GetDexFileLocationChecksum()) {
- *error_msg = StringPrintf("ValidateOatFile found checksum mismatch between oat file '%s' and "
- "dex file '%s' (0x%x != 0x%x)",
- oat_file.GetLocation().c_str(),
- dex_file_location.c_str(),
- oat_dex_file->GetDexFileLocationChecksum(),
- dex_file_location_checksum);
- return false;
- }
- }
- return true;
- }
};
static constexpr uint64_t kLowSpaceValue = 50 * MB;
@@ -1790,6 +1763,63 @@ std::string ImageSpace::GetMultiImageBootClassPath(
return bootcp_oss.str();
}
+bool ImageSpace::ValidateOatFile(const OatFile& oat_file, std::string* error_msg) {
+ for (const OatFile::OatDexFile* oat_dex_file : oat_file.GetOatDexFiles()) {
+ const std::string& dex_file_location = oat_dex_file->GetDexFileLocation();
+
+ // Skip multidex locations - These will be checked when we visit their
+ // corresponding primary non-multidex location.
+ if (DexFile::IsMultiDexLocation(dex_file_location.c_str())) {
+ continue;
+ }
+
+ std::vector<uint32_t> checksums;
+ if (!DexFile::GetMultiDexChecksums(dex_file_location.c_str(), &checksums, error_msg)) {
+ *error_msg = StringPrintf("ValidateOatFile failed to get checksums of dex file '%s' "
+ "referenced by oat file %s: %s",
+ dex_file_location.c_str(),
+ oat_file.GetLocation().c_str(),
+ error_msg->c_str());
+ return false;
+ }
+ CHECK(!checksums.empty());
+ if (checksums[0] != oat_dex_file->GetDexFileLocationChecksum()) {
+ *error_msg = StringPrintf("ValidateOatFile found checksum mismatch between oat file "
+ "'%s' and dex file '%s' (0x%x != 0x%x)",
+ oat_file.GetLocation().c_str(),
+ dex_file_location.c_str(),
+ oat_dex_file->GetDexFileLocationChecksum(),
+ checksums[0]);
+ return false;
+ }
+
+ // Verify checksums for any related multidex entries.
+ for (size_t i = 1; i < checksums.size(); i++) {
+ std::string multi_dex_location = DexFile::GetMultiDexLocation(i, dex_file_location.c_str());
+ const OatFile::OatDexFile* multi_dex = oat_file.GetOatDexFile(multi_dex_location.c_str(),
+ nullptr,
+ error_msg);
+ if (multi_dex == nullptr) {
+ *error_msg = StringPrintf("ValidateOatFile oat file '%s' is missing entry '%s'",
+ oat_file.GetLocation().c_str(),
+ multi_dex_location.c_str());
+ return false;
+ }
+
+ if (checksums[i] != multi_dex->GetDexFileLocationChecksum()) {
+ *error_msg = StringPrintf("ValidateOatFile found checksum mismatch between oat file "
+ "'%s' and dex file '%s' (0x%x != 0x%x)",
+ oat_file.GetLocation().c_str(),
+ multi_dex_location.c_str(),
+ multi_dex->GetDexFileLocationChecksum(),
+ checksums[i]);
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
void ImageSpace::ExtractMultiImageLocations(const std::string& input_image_file_name,
const std::string& boot_classpath,
std::vector<std::string>* image_file_names) {
diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h
index 489a2890fe..199bbdd00a 100644
--- a/runtime/gc/space/image_space.h
+++ b/runtime/gc/space/image_space.h
@@ -131,6 +131,17 @@ class ImageSpace : public MemMapSpace {
const std::vector<const char*>& oat_filenames,
const std::vector<const char*>& image_filenames);
+ // Returns true if the dex checksums in the given oat file match the
+ // checksums of the original dex files on disk. This is intended to be used
+ // to validate the boot image oat file, which may contain dex entries from
+ // multiple different (possibly multidex) dex files on disk. Prefer the
+ // OatFileAssistant for validating regular app oat files because the
+ // OatFileAssistant caches dex checksums that are reused to check both the
+ // oat and odex file.
+ //
+ // This function is exposed for testing purposes.
+ static bool ValidateOatFile(const OatFile& oat_file, std::string* error_msg);
+
// Return the end of the image which includes non-heap objects such as ArtMethods and ArtFields.
uint8_t* GetImageEnd() const {
return Begin() + GetImageHeader().GetImageSize();
diff --git a/runtime/gc/space/image_space_test.cc b/runtime/gc/space/image_space_test.cc
new file mode 100644
index 0000000000..7a380746a1
--- /dev/null
+++ b/runtime/gc/space/image_space_test.cc
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include "dexopt_test.h"
+
+namespace art {
+namespace gc {
+namespace space {
+
+TEST_F(DexoptTest, ValidateOatFile) {
+ std::string dex1 = GetScratchDir() + "/Dex1.jar";
+ std::string multidex1 = GetScratchDir() + "/MultiDex1.jar";
+ std::string dex2 = GetScratchDir() + "/Dex2.jar";
+ std::string oat_location = GetScratchDir() + "/Oat.oat";
+
+ Copy(GetDexSrc1(), dex1);
+ Copy(GetMultiDexSrc1(), multidex1);
+ Copy(GetDexSrc2(), dex2);
+
+ std::string error_msg;
+ std::vector<std::string> args;
+ args.push_back("--dex-file=" + dex1);
+ args.push_back("--dex-file=" + multidex1);
+ args.push_back("--dex-file=" + dex2);
+ args.push_back("--oat-file=" + oat_location);
+ ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg;
+
+ std::unique_ptr<OatFile> oat(OatFile::Open(oat_location.c_str(),
+ oat_location.c_str(),
+ nullptr,
+ nullptr,
+ false,
+ /*low_4gb*/false,
+ nullptr,
+ &error_msg));
+ ASSERT_TRUE(oat != nullptr) << error_msg;
+
+ // Originally all the dex checksums should be up to date.
+ EXPECT_TRUE(ImageSpace::ValidateOatFile(*oat, &error_msg)) << error_msg;
+
+ // Invalidate the dex1 checksum.
+ Copy(GetDexSrc2(), dex1);
+ EXPECT_FALSE(ImageSpace::ValidateOatFile(*oat, &error_msg));
+
+ // Restore the dex1 checksum.
+ Copy(GetDexSrc1(), dex1);
+ EXPECT_TRUE(ImageSpace::ValidateOatFile(*oat, &error_msg)) << error_msg;
+
+ // Invalidate the non-main multidex checksum.
+ Copy(GetMultiDexSrc2(), multidex1);
+ EXPECT_FALSE(ImageSpace::ValidateOatFile(*oat, &error_msg));
+
+ // Restore the multidex checksum.
+ Copy(GetMultiDexSrc1(), multidex1);
+ EXPECT_TRUE(ImageSpace::ValidateOatFile(*oat, &error_msg)) << error_msg;
+
+ // Invalidate the dex2 checksum.
+ Copy(GetDexSrc1(), dex2);
+ EXPECT_FALSE(ImageSpace::ValidateOatFile(*oat, &error_msg));
+
+ // restore the dex2 checksum.
+ Copy(GetDexSrc2(), dex2);
+ EXPECT_TRUE(ImageSpace::ValidateOatFile(*oat, &error_msg)) << error_msg;
+
+ // Replace the multidex file with a non-multidex file.
+ Copy(GetDexSrc1(), multidex1);
+ EXPECT_FALSE(ImageSpace::ValidateOatFile(*oat, &error_msg));
+
+ // Restore the multidex file
+ Copy(GetMultiDexSrc1(), multidex1);
+ EXPECT_TRUE(ImageSpace::ValidateOatFile(*oat, &error_msg)) << error_msg;
+
+ // Replace dex1 with a multidex file.
+ Copy(GetMultiDexSrc1(), dex1);
+ EXPECT_FALSE(ImageSpace::ValidateOatFile(*oat, &error_msg));
+
+ // Restore the dex1 file.
+ Copy(GetDexSrc1(), dex1);
+ EXPECT_TRUE(ImageSpace::ValidateOatFile(*oat, &error_msg)) << error_msg;
+
+ // Remove the dex2 file.
+ EXPECT_EQ(0, unlink(dex2.c_str()));
+ EXPECT_FALSE(ImageSpace::ValidateOatFile(*oat, &error_msg));
+
+ // Restore the dex2 file.
+ Copy(GetDexSrc2(), dex2);
+ EXPECT_TRUE(ImageSpace::ValidateOatFile(*oat, &error_msg)) << error_msg;
+
+ // Remove the multidex file.
+ EXPECT_EQ(0, unlink(multidex1.c_str()));
+ EXPECT_FALSE(ImageSpace::ValidateOatFile(*oat, &error_msg));
+}
+
+} // namespace space
+} // namespace gc
+} // namespace art
diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h
index 9b8445dc9e..c2407d7772 100644
--- a/runtime/mirror/string-inl.h
+++ b/runtime/mirror/string-inl.h
@@ -308,7 +308,7 @@ inline int32_t String::GetHashCode() {
}
template<typename MemoryType>
-bool String::AllASCII(const MemoryType* const chars, const int length) {
+inline bool String::AllASCII(const MemoryType* chars, const int length) {
static_assert(std::is_unsigned<MemoryType>::value, "Expecting unsigned MemoryType");
for (int i = 0; i < length; ++i) {
// Valid ASCII characters are in range 1..0x7f. Zero is not considered ASCII
@@ -320,6 +320,13 @@ bool String::AllASCII(const MemoryType* const chars, const int length) {
return true;
}
+inline bool String::DexFileStringAllASCII(const char* chars, const int length) {
+ // For strings from the dex file we just need to check that
+ // the terminating character is at the right position.
+ DCHECK_EQ(AllASCII(reinterpret_cast<const uint8_t*>(chars), length), chars[length] == 0);
+ return chars[length] == 0;
+}
+
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h
index 409c6c2896..38f6dd4b6f 100644
--- a/runtime/mirror/string.h
+++ b/runtime/mirror/string.h
@@ -184,7 +184,9 @@ class MANAGED String FINAL : public Object {
bool IsValueNull() REQUIRES_SHARED(Locks::mutator_lock_);
template<typename MemoryType>
- static bool AllASCII(const MemoryType* const chars, const int length);
+ static bool AllASCII(const MemoryType* chars, const int length);
+
+ static bool DexFileStringAllASCII(const char* chars, const int length);
ALWAYS_INLINE static bool IsCompressed(int32_t count) {
return GetCompressionFlagFromCount(count) == StringCompressionFlag::kCompressed;
diff --git a/runtime/oat.h b/runtime/oat.h
index a764e0eada..0f6657b7ed 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,7 +32,7 @@ class InstructionSetFeatures;
class PACKED(4) OatHeader {
public:
static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
- static constexpr uint8_t kOatVersion[] = { '1', '1', '1', '\0' }; // Revert^3 hash-based DexCache types.
+ static constexpr uint8_t kOatVersion[] = { '1', '1', '2', '\0' }; // Manual bump (Revert^3 hash-based DexCache types; stack maps).
static constexpr const char* kImageLocationKey = "image-location";
static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 528eddc306..493da271d1 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -1510,77 +1510,6 @@ std::string OatFile::EncodeDexFileDependencies(const std::vector<const DexFile*>
return out.str();
}
-bool OatFile::CheckStaticDexFileDependencies(const char* dex_dependencies, std::string* msg) {
- if (dex_dependencies == nullptr || dex_dependencies[0] == 0) {
- // No dependencies.
- return true;
- }
-
- // Assumption: this is not performance-critical. So it's OK to do this with a std::string and
- // Split() instead of manual parsing of the combined char*.
- std::vector<std::string> split;
- Split(dex_dependencies, kDexClassPathEncodingSeparator, &split);
- if (split.size() % 2 != 0) {
- // Expected pairs of location and checksum.
- *msg = StringPrintf("Odd number of elements in dependency list %s", dex_dependencies);
- return false;
- }
-
- for (auto it = split.begin(), end = split.end(); it != end; it += 2) {
- std::string& location = *it;
- std::string& checksum = *(it + 1);
- int64_t converted = strtoll(checksum.c_str(), nullptr, 10);
- if (converted == 0) {
- // Conversion error.
- *msg = StringPrintf("Conversion error for %s", checksum.c_str());
- return false;
- }
-
- uint32_t dex_checksum;
- std::string error_msg;
- if (DexFile::GetChecksum(DexFile::GetDexCanonicalLocation(location.c_str()).c_str(),
- &dex_checksum,
- &error_msg)) {
- if (converted != dex_checksum) {
- *msg = StringPrintf("Checksums don't match for %s: %" PRId64 " vs %u",
- location.c_str(), converted, dex_checksum);
- return false;
- }
- } else {
- // Problem retrieving checksum.
- // TODO: odex files?
- *msg = StringPrintf("Could not retrieve checksum for %s: %s", location.c_str(),
- error_msg.c_str());
- return false;
- }
- }
-
- return true;
-}
-
-bool OatFile::GetDexLocationsFromDependencies(const char* dex_dependencies,
- std::vector<std::string>* locations) {
- DCHECK(locations != nullptr);
- if (dex_dependencies == nullptr || dex_dependencies[0] == 0) {
- return true;
- }
-
- // Assumption: this is not performance-critical. So it's OK to do this with a std::string and
- // Split() instead of manual parsing of the combined char*.
- std::vector<std::string> split;
- Split(dex_dependencies, kDexClassPathEncodingSeparator, &split);
- if (split.size() % 2 != 0) {
- // Expected pairs of location and checksum.
- return false;
- }
-
- for (auto it = split.begin(), end = split.end(); it != end; it += 2) {
- locations->push_back(*it);
- }
-
- return true;
-}
-
OatFile::OatClass OatFile::FindOatClass(const DexFile& dex_file,
uint16_t class_def_idx,
bool* found) {
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 111755e7a1..d24283afee 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -290,15 +290,6 @@ class OatFile {
// Create a dependency list (dex locations and checksums) for the given dex files.
static std::string EncodeDexFileDependencies(const std::vector<const DexFile*>& dex_files);
- // Check the given dependency list against their dex files - thus the name "Static," this does
- // not check the class-loader environment, only whether there have been file updates.
- static bool CheckStaticDexFileDependencies(const char* dex_dependencies, std::string* msg);
-
- // Get the dex locations of a dependency list. Note: this is *not* cleaned for synthetic
- // locations of multidex files.
- static bool GetDexLocationsFromDependencies(const char* dex_dependencies,
- std::vector<std::string>* locations);
-
// Finds the associated oat class for a dex_file and descriptor. Returns an invalid OatClass on
// error and sets found to false.
static OatClass FindOatClass(const DexFile& dex_file, uint16_t class_def_idx, bool* found);
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 77cdd28d3a..5ae2fc51b7 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -38,6 +38,8 @@
namespace art {
+using android::base::StringPrintf;
+
std::ostream& operator << (std::ostream& stream, const OatFileAssistant::OatStatus status) {
switch (status) {
case OatFileAssistant::kOatCannotOpen:
@@ -264,7 +266,7 @@ std::vector<std::unique_ptr<const DexFile>> OatFileAssistant::LoadDexFiles(
const OatFile& oat_file, const char* dex_location) {
std::vector<std::unique_ptr<const DexFile>> dex_files;
- // Load the primary dex file.
+ // Load the main dex file.
std::string error_msg;
const OatFile::OatDexFile* oat_dex_file = oat_file.GetOatDexFile(
dex_location, nullptr, &error_msg);
@@ -280,12 +282,12 @@ std::vector<std::unique_ptr<const DexFile>> OatFileAssistant::LoadDexFiles(
}
dex_files.push_back(std::move(dex_file));
- // Load secondary multidex files
+ // Load the rest of the multidex entries
for (size_t i = 1; ; i++) {
- std::string secondary_dex_location = DexFile::GetMultiDexLocation(i, dex_location);
- oat_dex_file = oat_file.GetOatDexFile(secondary_dex_location.c_str(), nullptr);
+ std::string multidex_dex_location = DexFile::GetMultiDexLocation(i, dex_location);
+ oat_dex_file = oat_file.GetOatDexFile(multidex_dex_location.c_str(), nullptr);
if (oat_dex_file == nullptr) {
- // There are no more secondary dex files to load.
+ // There are no more multidex entries to load.
break;
}
@@ -300,10 +302,10 @@ std::vector<std::unique_ptr<const DexFile>> OatFileAssistant::LoadDexFiles(
}
bool OatFileAssistant::HasOriginalDexFiles() {
- // Ensure GetRequiredDexChecksum has been run so that
+ // Ensure GetRequiredDexChecksums has been run so that
// has_original_dex_files_ is initialized. We don't care about the result of
- // GetRequiredDexChecksum.
- GetRequiredDexChecksum();
+ // GetRequiredDexChecksums.
+ GetRequiredDexChecksums();
return has_original_dex_files_;
}
@@ -316,88 +318,66 @@ OatFileAssistant::OatStatus OatFileAssistant::OatFileStatus() {
}
bool OatFileAssistant::DexChecksumUpToDate(const VdexFile& file, std::string* error_msg) {
- if (file.GetHeader().GetNumberOfDexFiles() <= 0) {
- VLOG(oat) << "Vdex does not contain any dex files";
+ const std::vector<uint32_t>* required_dex_checksums = GetRequiredDexChecksums();
+ if (required_dex_checksums == nullptr) {
+ LOG(WARNING) << "Required dex checksums not found. Assuming dex checksums are up to date.";
+ return true;
+ }
+
+ uint32_t number_of_dex_files = file.GetHeader().GetNumberOfDexFiles();
+ if (required_dex_checksums->size() != number_of_dex_files) {
+ *error_msg = StringPrintf("expected %zu dex files but found %u",
+ required_dex_checksums->size(),
+ number_of_dex_files);
return false;
}
- // TODO: Use GetRequiredDexChecksum to get secondary checksums as well, not
- // just the primary. Because otherwise we may fail to see a secondary
- // checksum failure in the case when the original (multidex) files are
- // stripped but we have a newer odex file.
- const uint32_t* dex_checksum_pointer = GetRequiredDexChecksum();
- if (dex_checksum_pointer != nullptr) {
- uint32_t actual_checksum = file.GetLocationChecksum(0);
- if (*dex_checksum_pointer != actual_checksum) {
- VLOG(oat) << "Dex checksum does not match for primary dex: " << dex_location_
- << ". Expected: " << *dex_checksum_pointer
- << ", Actual: " << actual_checksum;
+ for (uint32_t i = 0; i < number_of_dex_files; i++) {
+ uint32_t expected_checksum = (*required_dex_checksums)[i];
+ uint32_t actual_checksum = file.GetLocationChecksum(i);
+ if (expected_checksum != actual_checksum) {
+ std::string dex = DexFile::GetMultiDexLocation(i, dex_location_.c_str());
+ *error_msg = StringPrintf("Dex checksum does not match for dex: %s."
+ "Expected: %u, actual: %u",
+ dex.c_str(),
+ expected_checksum,
+ actual_checksum);
return false;
}
}
- // Verify the dex checksums for any secondary multidex files
- for (uint32_t i = 1; i < file.GetHeader().GetNumberOfDexFiles(); i++) {
- std::string secondary_dex_location = DexFile::GetMultiDexLocation(i, dex_location_.c_str());
- uint32_t expected_secondary_checksum = 0;
- if (DexFile::GetChecksum(secondary_dex_location.c_str(),
- &expected_secondary_checksum,
- error_msg)) {
- uint32_t actual_secondary_checksum = file.GetLocationChecksum(i);
- if (expected_secondary_checksum != actual_secondary_checksum) {
- VLOG(oat) << "Dex checksum does not match for secondary dex: "
- << secondary_dex_location
- << ". Expected: " << expected_secondary_checksum
- << ", Actual: " << actual_secondary_checksum;
- return false;
- }
- } else {
- // If we can't get the checksum for the secondary location, we assume
- // the dex checksum is up to date for this and all other secondary dex
- // files.
- break;
- }
- }
return true;
}
bool OatFileAssistant::DexChecksumUpToDate(const OatFile& file, std::string* error_msg) {
- // Note: GetOatDexFile will return null if the dex checksum doesn't match
- // what we provide, which verifies the primary dex checksum for us.
- const uint32_t* dex_checksum_pointer = GetRequiredDexChecksum();
- const OatFile::OatDexFile* oat_dex_file = file.GetOatDexFile(
- dex_location_.c_str(), dex_checksum_pointer, error_msg);
- if (oat_dex_file == nullptr) {
+ const std::vector<uint32_t>* required_dex_checksums = GetRequiredDexChecksums();
+ if (required_dex_checksums == nullptr) {
+ LOG(WARNING) << "Required dex checksums not found. Assuming dex checksums are up to date.";
+ return true;
+ }
+
+ uint32_t number_of_dex_files = file.GetOatHeader().GetDexFileCount();
+ if (required_dex_checksums->size() != number_of_dex_files) {
+ *error_msg = StringPrintf("expected %zu dex files but found %u",
+ required_dex_checksums->size(),
+ number_of_dex_files);
return false;
}
- // Verify the dex checksums for any secondary multidex files
- for (size_t i = 1; ; i++) {
- std::string secondary_dex_location = DexFile::GetMultiDexLocation(i, dex_location_.c_str());
- const OatFile::OatDexFile* secondary_oat_dex_file
- = file.GetOatDexFile(secondary_dex_location.c_str(), nullptr);
- if (secondary_oat_dex_file == nullptr) {
- // There are no more secondary dex files to check.
- break;
+ for (uint32_t i = 0; i < number_of_dex_files; i++) {
+ std::string dex = DexFile::GetMultiDexLocation(i, dex_location_.c_str());
+ uint32_t expected_checksum = (*required_dex_checksums)[i];
+ const OatFile::OatDexFile* oat_dex_file = file.GetOatDexFile(dex.c_str(), nullptr);
+ if (oat_dex_file == nullptr) {
+ *error_msg = StringPrintf("failed to find %s in %s", dex.c_str(), file.GetLocation().c_str());
+ return false;
}
-
- uint32_t expected_secondary_checksum = 0;
- if (DexFile::GetChecksum(secondary_dex_location.c_str(),
- &expected_secondary_checksum, error_msg)) {
- uint32_t actual_secondary_checksum
- = secondary_oat_dex_file->GetDexFileLocationChecksum();
- if (expected_secondary_checksum != actual_secondary_checksum) {
- VLOG(oat) << "Dex checksum does not match for secondary dex: "
- << secondary_dex_location
- << ". Expected: " << expected_secondary_checksum
- << ", Actual: " << actual_secondary_checksum;
- return false;
- }
- } else {
- // If we can't get the checksum for the secondary location, we assume
- // the dex checksum is up to date for this and all other secondary dex
- // files.
- break;
+ uint32_t actual_checksum = oat_dex_file->GetDexFileLocationChecksum();
+ if (expected_checksum != actual_checksum) {
+ VLOG(oat) << "Dex checksum does not match for dex: " << dex
+ << ". Expected: " << expected_checksum
+ << ", Actual: " << actual_checksum;
+ return false;
}
}
return true;
@@ -710,13 +690,16 @@ std::string OatFileAssistant::ImageLocation() {
return image_spaces[0]->GetImageLocation();
}
-const uint32_t* OatFileAssistant::GetRequiredDexChecksum() {
- if (!required_dex_checksum_attempted_) {
- required_dex_checksum_attempted_ = true;
- required_dex_checksum_found_ = false;
+const std::vector<uint32_t>* OatFileAssistant::GetRequiredDexChecksums() {
+ if (!required_dex_checksums_attempted_) {
+ required_dex_checksums_attempted_ = true;
+ required_dex_checksums_found_ = false;
+ cached_required_dex_checksums_.clear();
std::string error_msg;
- if (DexFile::GetChecksum(dex_location_.c_str(), &cached_required_dex_checksum_, &error_msg)) {
- required_dex_checksum_found_ = true;
+ if (DexFile::GetMultiDexChecksums(dex_location_.c_str(),
+ &cached_required_dex_checksums_,
+ &error_msg)) {
+ required_dex_checksums_found_ = true;
has_original_dex_files_ = true;
} else {
// This can happen if the original dex file has been stripped from the
@@ -724,19 +707,23 @@ const uint32_t* OatFileAssistant::GetRequiredDexChecksum() {
VLOG(oat) << "OatFileAssistant: " << error_msg;
has_original_dex_files_ = false;
- // Get the checksum from the odex if we can.
+ // Get the checksums from the odex if we can.
const OatFile* odex_file = odex_.GetFile();
if (odex_file != nullptr) {
- const OatFile::OatDexFile* odex_dex_file
- = odex_file->GetOatDexFile(dex_location_.c_str(), nullptr);
- if (odex_dex_file != nullptr) {
- cached_required_dex_checksum_ = odex_dex_file->GetDexFileLocationChecksum();
- required_dex_checksum_found_ = true;
+ required_dex_checksums_found_ = true;
+ for (size_t i = 0; i < odex_file->GetOatHeader().GetDexFileCount(); i++) {
+ std::string dex = DexFile::GetMultiDexLocation(i, dex_location_.c_str());
+ const OatFile::OatDexFile* odex_dex_file = odex_file->GetOatDexFile(dex.c_str(), nullptr);
+ if (odex_dex_file == nullptr) {
+ required_dex_checksums_found_ = false;
+ break;
+ }
+ cached_required_dex_checksums_.push_back(odex_dex_file->GetDexFileLocationChecksum());
}
}
}
}
- return required_dex_checksum_found_ ? &cached_required_dex_checksum_ : nullptr;
+ return required_dex_checksums_found_ ? &cached_required_dex_checksums_ : nullptr;
}
const OatFileAssistant::ImageInfo* OatFileAssistant::GetImageInfo() {
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index 6d47ad2228..3ede29f5e0 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -400,13 +400,13 @@ class OatFileAssistant {
// the oat file assistant.
static std::string ImageLocation();
- // Gets the dex checksum required for an up-to-date oat file.
- // Returns dex_checksum if a required checksum was located. Returns
- // null if the required checksum was not found.
- // The caller shouldn't clean up or free the returned pointer.
- // This sets the has_original_dex_files_ field to true if a checksum was
- // found for the dex_location_ dex file.
- const uint32_t* GetRequiredDexChecksum();
+ // Gets the dex checksums required for an up-to-date oat file.
+ // Returns cached_required_dex_checksums if the required checksums were
+ // located. Returns null if the required checksums were not found. The
+ // caller shouldn't clean up or free the returned pointer. This sets the
+ // has_original_dex_files_ field to true if the checksums were found for the
+ // dex_location_ dex file.
+ const std::vector<uint32_t>* GetRequiredDexChecksums();
// Returns the loaded image info.
// Loads the image info if needed. Returns null if the image info failed
@@ -430,11 +430,11 @@ class OatFileAssistant {
// Whether we will attempt to load oat files executable.
bool load_executable_ = false;
- // Cached value of the required dex checksum.
- // This should be accessed only by the GetRequiredDexChecksum() method.
- uint32_t cached_required_dex_checksum_;
- bool required_dex_checksum_attempted_ = false;
- bool required_dex_checksum_found_;
+ // Cached value of the required dex checksums.
+ // This should be accessed only by the GetRequiredDexChecksums() method.
+ std::vector<uint32_t> cached_required_dex_checksums_;
+ bool required_dex_checksums_attempted_ = false;
+ bool required_dex_checksums_found_;
bool has_original_dex_files_;
OatFileInfo odex_;
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index f777340cfd..6cfe3d1315 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -237,16 +237,16 @@ TEST_F(OatFileAssistantTest, MultiDexOatUpToDate) {
EXPECT_EQ(2u, dex_files.size());
}
-// Case: We have a MultiDEX file where the secondary dex file is out of date.
+// Case: We have a MultiDEX file where the non-main multdex entry is out of date.
// Expect: The status is kDex2OatNeeded.
-TEST_F(OatFileAssistantTest, MultiDexSecondaryOutOfDate) {
- std::string dex_location = GetScratchDir() + "/MultiDexSecondaryOutOfDate.jar";
+TEST_F(OatFileAssistantTest, MultiDexNonMainOutOfDate) {
+ std::string dex_location = GetScratchDir() + "/MultiDexNonMainOutOfDate.jar";
// Compile code for GetMultiDexSrc1.
Copy(GetMultiDexSrc1(), dex_location);
GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed);
- // Now overwrite the dex file with GetMultiDexSrc2 so the secondary checksum
+ // Now overwrite the dex file with GetMultiDexSrc2 so the non-main checksum
// is out of date.
Copy(GetMultiDexSrc2(), dex_location);
@@ -256,6 +256,37 @@ TEST_F(OatFileAssistantTest, MultiDexSecondaryOutOfDate) {
EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
}
+// Case: We have a stripped MultiDEX file where the non-main multidex entry is
+// out of date with respect to the odex file.
+TEST_F(OatFileAssistantTest, StrippedMultiDexNonMainOutOfDate) {
+ std::string dex_location = GetScratchDir() + "/StrippedMultiDexNonMainOutOfDate.jar";
+ std::string odex_location = GetOdexDir() + "/StrippedMultiDexNonMainOutOfDate.odex";
+
+ // Compile the oat from GetMultiDexSrc1.
+ Copy(GetMultiDexSrc1(), dex_location);
+ GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed);
+
+ // Compile the odex from GetMultiDexSrc2, which has a different non-main
+ // dex checksum.
+ Copy(GetMultiDexSrc2(), dex_location);
+ GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kInterpretOnly);
+
+ // Strip the dex file.
+ Copy(GetStrippedDexSrc1(), dex_location);
+
+ OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, /*load_executable*/false);
+
+ // Because the dex file is stripped, the odex file is considered the source
+ // of truth for the dex checksums. The oat file should be considered
+ // unusable.
+ std::unique_ptr<OatFile> best_file = oat_file_assistant.GetBestOatFile();
+ ASSERT_TRUE(best_file.get() != nullptr);
+ EXPECT_EQ(best_file->GetLocation(), odex_location);
+ EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
+ EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatDexOutOfDate, oat_file_assistant.OatFileStatus());
+}
+
// Case: We have a MultiDEX file and up-to-date OAT file for it with relative
// encoded dex locations.
// Expect: The oat file status is kNoDexOptNeeded.
@@ -336,16 +367,16 @@ TEST_F(OatFileAssistantTest, VdexDexOutOfDate) {
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
}
-// Case: We have a MultiDEX (ODEX) VDEX file where the secondary dex file is
-// out of date and there is no corresponding ODEX file.
-TEST_F(OatFileAssistantTest, VdexMultiDexSecondaryOutOfDate) {
+// Case: We have a MultiDEX (ODEX) VDEX file where the non-main multidex entry
+// is out of date and there is no corresponding ODEX file.
+TEST_F(OatFileAssistantTest, VdexMultiDexNonMainOutOfDate) {
// This test case is only meaningful if vdex is enabled.
if (!kIsVdexEnabled) {
return;
}
- std::string dex_location = GetScratchDir() + "/VdexMultiDexSecondaryOutOfDate.jar";
- std::string oat_location = GetOdexDir() + "/VdexMultiDexSecondaryOutOfDate.oat";
+ std::string dex_location = GetScratchDir() + "/VdexMultiDexNonMainOutOfDate.jar";
+ std::string oat_location = GetOdexDir() + "/VdexMultiDexNonMainOutOfDate.oat";
Copy(GetMultiDexSrc1(), dex_location);
GenerateOdexForTest(dex_location, oat_location, CompilerFilter::kSpeed);
diff --git a/runtime/oat_file_test.cc b/runtime/oat_file_test.cc
index b416b9dbad..d5fe1f382a 100644
--- a/runtime/oat_file_test.cc
+++ b/runtime/oat_file_test.cc
@@ -62,54 +62,4 @@ TEST_F(OatFileTest, ResolveRelativeEncodedDexLocation) {
"/data/app/foo/base.apk", "o/base.apk"));
}
-static std::vector<const DexFile*> ToConstDexFiles(
- const std::vector<std::unique_ptr<const DexFile>>& in) {
- std::vector<const DexFile*> ret;
- for (auto& d : in) {
- ret.push_back(d.get());
- }
- return ret;
-}
-
-TEST_F(OatFileTest, DexFileDependencies) {
- std::string error_msg;
-
- // No dependencies.
- EXPECT_TRUE(OatFile::CheckStaticDexFileDependencies(nullptr, &error_msg)) << error_msg;
- EXPECT_TRUE(OatFile::CheckStaticDexFileDependencies("", &error_msg)) << error_msg;
-
- // Ill-formed dependencies.
- EXPECT_FALSE(OatFile::CheckStaticDexFileDependencies("abc", &error_msg));
- EXPECT_FALSE(OatFile::CheckStaticDexFileDependencies("abc*123*def", &error_msg));
- EXPECT_FALSE(OatFile::CheckStaticDexFileDependencies("abc*def*", &error_msg));
-
- // Unsatisfiable dependency.
- EXPECT_FALSE(OatFile::CheckStaticDexFileDependencies("abc*123*", &error_msg));
-
- // Load some dex files to be able to do a real test.
- ScopedObjectAccess soa(Thread::Current());
-
- std::vector<std::unique_ptr<const DexFile>> dex_files1 = OpenTestDexFiles("Main");
- std::vector<const DexFile*> dex_files_const1 = ToConstDexFiles(dex_files1);
- std::string encoding1 = OatFile::EncodeDexFileDependencies(dex_files_const1);
- EXPECT_TRUE(OatFile::CheckStaticDexFileDependencies(encoding1.c_str(), &error_msg))
- << error_msg << " " << encoding1;
- std::vector<std::string> split1;
- EXPECT_TRUE(OatFile::GetDexLocationsFromDependencies(encoding1.c_str(), &split1));
- ASSERT_EQ(split1.size(), 1U);
- EXPECT_EQ(split1[0], dex_files_const1[0]->GetLocation());
-
- std::vector<std::unique_ptr<const DexFile>> dex_files2 = OpenTestDexFiles("MultiDex");
- EXPECT_GT(dex_files2.size(), 1U);
- std::vector<const DexFile*> dex_files_const2 = ToConstDexFiles(dex_files2);
- std::string encoding2 = OatFile::EncodeDexFileDependencies(dex_files_const2);
- EXPECT_TRUE(OatFile::CheckStaticDexFileDependencies(encoding2.c_str(), &error_msg))
- << error_msg << " " << encoding2;
- std::vector<std::string> split2;
- EXPECT_TRUE(OatFile::GetDexLocationsFromDependencies(encoding2.c_str(), &split2));
- ASSERT_EQ(split2.size(), 2U);
- EXPECT_EQ(split2[0], dex_files_const2[0]->GetLocation());
- EXPECT_EQ(split2[1], dex_files_const2[1]->GetLocation());
-}
-
} // namespace art
diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc
index d767c33282..843fd8c8e4 100644
--- a/runtime/openjdkjvmti/ti_redefine.cc
+++ b/runtime/openjdkjvmti/ti_redefine.cc
@@ -68,66 +68,6 @@ namespace openjdkjvmti {
using android::base::StringPrintf;
-// A helper that fills in a classes obsolete_methods_ and obsolete_dex_caches_ classExt fields as
-// they are created. This ensures that we can always call any method of an obsolete ArtMethod object
-// almost as soon as they are created since the GetObsoleteDexCache method will succeed.
-class ObsoleteMap {
- public:
- art::ArtMethod* FindObsoleteVersion(art::ArtMethod* original)
- REQUIRES(art::Locks::mutator_lock_, art::Roles::uninterruptible_) {
- auto method_pair = id_map_.find(original);
- if (method_pair != id_map_.end()) {
- art::ArtMethod* res = obsolete_methods_->GetElementPtrSize<art::ArtMethod*>(
- method_pair->second, art::kRuntimePointerSize);
- DCHECK(res != nullptr);
- DCHECK_EQ(original, res->GetNonObsoleteMethod());
- return res;
- } else {
- return nullptr;
- }
- }
-
- void RecordObsolete(art::ArtMethod* original, art::ArtMethod* obsolete)
- REQUIRES(art::Locks::mutator_lock_, art::Roles::uninterruptible_) {
- DCHECK(original != nullptr);
- DCHECK(obsolete != nullptr);
- int32_t slot = next_free_slot_++;
- DCHECK_LT(slot, obsolete_methods_->GetLength());
- DCHECK(nullptr ==
- obsolete_methods_->GetElementPtrSize<art::ArtMethod*>(slot, art::kRuntimePointerSize));
- DCHECK(nullptr == obsolete_dex_caches_->Get(slot));
- obsolete_methods_->SetElementPtrSize(slot, obsolete, art::kRuntimePointerSize);
- obsolete_dex_caches_->Set(slot, original_dex_cache_);
- id_map_.insert({original, slot});
- }
-
- ObsoleteMap(art::ObjPtr<art::mirror::PointerArray> obsolete_methods,
- art::ObjPtr<art::mirror::ObjectArray<art::mirror::DexCache>> obsolete_dex_caches,
- art::ObjPtr<art::mirror::DexCache> original_dex_cache)
- : next_free_slot_(0),
- obsolete_methods_(obsolete_methods),
- obsolete_dex_caches_(obsolete_dex_caches),
- original_dex_cache_(original_dex_cache) {
- // Figure out where the first unused slot in the obsolete_methods_ array is.
- while (obsolete_methods_->GetElementPtrSize<art::ArtMethod*>(
- next_free_slot_, art::kRuntimePointerSize) != nullptr) {
- DCHECK(obsolete_dex_caches_->Get(next_free_slot_) != nullptr);
- next_free_slot_++;
- }
- // Sanity check that the same slot in obsolete_dex_caches_ is free.
- DCHECK(obsolete_dex_caches_->Get(next_free_slot_) == nullptr);
- }
-
- private:
- int32_t next_free_slot_;
- std::unordered_map<art::ArtMethod*, int32_t> id_map_;
- // Pointers to the fields in mirror::ClassExt. These can be held as ObjPtr since this is only used
- // when we have an exclusive mutator_lock_ (i.e. all threads are suspended).
- art::ObjPtr<art::mirror::PointerArray> obsolete_methods_;
- art::ObjPtr<art::mirror::ObjectArray<art::mirror::DexCache>> obsolete_dex_caches_;
- art::ObjPtr<art::mirror::DexCache> original_dex_cache_;
-};
-
// This visitor walks thread stacks and allocates and sets up the obsolete methods. It also does
// some basic sanity checks that the obsolete method is sane.
class ObsoleteMethodStackVisitor : public art::StackVisitor {
@@ -136,7 +76,7 @@ class ObsoleteMethodStackVisitor : public art::StackVisitor {
art::Thread* thread,
art::LinearAlloc* allocator,
const std::unordered_set<art::ArtMethod*>& obsoleted_methods,
- ObsoleteMap* obsolete_maps)
+ /*out*/std::unordered_map<art::ArtMethod*, art::ArtMethod*>* obsolete_maps)
: StackVisitor(thread,
/*context*/nullptr,
StackVisitor::StackWalkKind::kIncludeInlinedFrames),
@@ -154,7 +94,7 @@ class ObsoleteMethodStackVisitor : public art::StackVisitor {
art::Thread* thread,
art::LinearAlloc* allocator,
const std::unordered_set<art::ArtMethod*>& obsoleted_methods,
- ObsoleteMap* obsolete_maps)
+ /*out*/std::unordered_map<art::ArtMethod*, art::ArtMethod*>* obsolete_maps)
REQUIRES(art::Locks::mutator_lock_) {
ObsoleteMethodStackVisitor visitor(thread,
allocator,
@@ -164,7 +104,6 @@ class ObsoleteMethodStackVisitor : public art::StackVisitor {
}
bool VisitFrame() OVERRIDE REQUIRES(art::Locks::mutator_lock_) {
- art::ScopedAssertNoThreadSuspension snts("Fixing up the stack for obsolete methods.");
art::ArtMethod* old_method = GetMethod();
if (obsoleted_methods_.find(old_method) != obsoleted_methods_.end()) {
// We cannot ensure that the right dex file is used in inlined frames so we don't support
@@ -174,8 +113,9 @@ class ObsoleteMethodStackVisitor : public art::StackVisitor {
// TODO We should really support redefining intrinsics.
// We don't support intrinsics so check for them here.
DCHECK(!old_method->IsIntrinsic());
- art::ArtMethod* new_obsolete_method = obsolete_maps_->FindObsoleteVersion(old_method);
- if (new_obsolete_method == nullptr) {
+ art::ArtMethod* new_obsolete_method = nullptr;
+ auto obsolete_method_pair = obsolete_maps_->find(old_method);
+ if (obsolete_method_pair == obsolete_maps_->end()) {
// Create a new Obsolete Method and put it in the list.
art::Runtime* runtime = art::Runtime::Current();
art::ClassLinker* cl = runtime->GetClassLinker();
@@ -189,7 +129,7 @@ class ObsoleteMethodStackVisitor : public art::StackVisitor {
DCHECK_EQ(new_obsolete_method->GetDeclaringClass(), old_method->GetDeclaringClass());
new_obsolete_method->SetIsObsolete();
new_obsolete_method->SetDontCompile();
- obsolete_maps_->RecordObsolete(old_method, new_obsolete_method);
+ obsolete_maps_->insert({old_method, new_obsolete_method});
// Update JIT Data structures to point to the new method.
art::jit::Jit* jit = art::Runtime::Current()->GetJit();
if (jit != nullptr) {
@@ -197,6 +137,8 @@ class ObsoleteMethodStackVisitor : public art::StackVisitor {
// structures to keep track of the new obsolete method.
jit->GetCodeCache()->MoveObsoleteMethod(old_method, new_obsolete_method);
}
+ } else {
+ new_obsolete_method = obsolete_method_pair->second;
}
DCHECK(new_obsolete_method != nullptr);
SetMethod(new_obsolete_method);
@@ -210,9 +152,9 @@ class ObsoleteMethodStackVisitor : public art::StackVisitor {
// The set of all methods which could be obsoleted.
const std::unordered_set<art::ArtMethod*>& obsoleted_methods_;
// A map from the original to the newly allocated obsolete method for frames on this thread. The
- // values in this map are added to the obsolete_methods_ (and obsolete_dex_caches_) fields of
- // the redefined classes ClassExt as it is filled.
- ObsoleteMap* obsolete_maps_;
+ // values in this map must be added to the obsolete_methods_ (and obsolete_dex_caches_) fields of
+ // the redefined classes ClassExt by the caller.
+ std::unordered_map<art::ArtMethod*, art::ArtMethod*>* obsolete_maps_;
};
jvmtiError Redefiner::IsModifiableClass(jvmtiEnv* env ATTRIBUTE_UNUSED,
@@ -489,12 +431,11 @@ art::mirror::ByteArray* Redefiner::ClassRedefinition::AllocateOrGetOriginalDexFi
}
struct CallbackCtx {
- ObsoleteMap* obsolete_map;
art::LinearAlloc* allocator;
+ std::unordered_map<art::ArtMethod*, art::ArtMethod*> obsolete_map;
std::unordered_set<art::ArtMethod*> obsolete_methods;
- explicit CallbackCtx(ObsoleteMap* map, art::LinearAlloc* alloc)
- : obsolete_map(map), allocator(alloc) {}
+ explicit CallbackCtx(art::LinearAlloc* alloc) : allocator(alloc) {}
};
void DoAllocateObsoleteMethodsCallback(art::Thread* t, void* vdata) NO_THREAD_SAFETY_ANALYSIS {
@@ -502,7 +443,7 @@ void DoAllocateObsoleteMethodsCallback(art::Thread* t, void* vdata) NO_THREAD_SA
ObsoleteMethodStackVisitor::UpdateObsoleteFrames(t,
data->allocator,
data->obsolete_methods,
- data->obsolete_map);
+ &data->obsolete_map);
}
// This creates any ArtMethod* structures needed for obsolete methods and ensures that the stack is
@@ -513,18 +454,9 @@ void Redefiner::ClassRedefinition::FindAndAllocateObsoleteMethods(art::mirror::C
art::mirror::ClassExt* ext = art_klass->GetExtData();
CHECK(ext->GetObsoleteMethods() != nullptr);
art::ClassLinker* linker = driver_->runtime_->GetClassLinker();
- // This holds pointers to the obsolete methods map fields which are updated as needed.
- ObsoleteMap map(ext->GetObsoleteMethods(), ext->GetObsoleteDexCaches(), art_klass->GetDexCache());
- CallbackCtx ctx(&map, linker->GetAllocatorForClassLoader(art_klass->GetClassLoader()));
+ CallbackCtx ctx(linker->GetAllocatorForClassLoader(art_klass->GetClassLoader()));
// Add all the declared methods to the map
for (auto& m : art_klass->GetDeclaredMethods(art::kRuntimePointerSize)) {
- // It is possible to simply filter out some methods where they cannot really become obsolete,
- // such as native methods and keep their original (possibly optimized) implementations. We don't
- // do this, however, since we would need to mark these functions (still in the classes
- // declared_methods array) as obsolete so we will find the correct dex file to get meta-data
- // from (for example about stack-frame size). Furthermore we would be unable to get some useful
- // error checking from the interpreter which ensure we don't try to start executing obsolete
- // methods.
ctx.obsolete_methods.insert(&m);
// TODO Allow this or check in IsModifiableClass.
DCHECK(!m.IsIntrinsic());
@@ -534,6 +466,36 @@ void Redefiner::ClassRedefinition::FindAndAllocateObsoleteMethods(art::mirror::C
art::ThreadList* list = art::Runtime::Current()->GetThreadList();
list->ForEach(DoAllocateObsoleteMethodsCallback, static_cast<void*>(&ctx));
}
+ FillObsoleteMethodMap(art_klass, ctx.obsolete_map);
+}
+
+// Fills the obsolete method map in the art_klass's extData. This is so obsolete methods are able to
+// figure out their DexCaches.
+void Redefiner::ClassRedefinition::FillObsoleteMethodMap(
+ art::mirror::Class* art_klass,
+ const std::unordered_map<art::ArtMethod*, art::ArtMethod*>& obsoletes) {
+ int32_t index = 0;
+ art::mirror::ClassExt* ext_data = art_klass->GetExtData();
+ art::mirror::PointerArray* obsolete_methods = ext_data->GetObsoleteMethods();
+ art::mirror::ObjectArray<art::mirror::DexCache>* obsolete_dex_caches =
+ ext_data->GetObsoleteDexCaches();
+ int32_t num_method_slots = obsolete_methods->GetLength();
+ // Find the first empty index.
+ for (; index < num_method_slots; index++) {
+ if (obsolete_methods->GetElementPtrSize<art::ArtMethod*>(
+ index, art::kRuntimePointerSize) == nullptr) {
+ break;
+ }
+ }
+ // Make sure we have enough space.
+ CHECK_GT(num_method_slots, static_cast<int32_t>(obsoletes.size() + index));
+ CHECK(obsolete_dex_caches->Get(index) == nullptr);
+ // Fill in the map.
+ for (auto& obs : obsoletes) {
+ obsolete_methods->SetElementPtrSize(index, obs.second, art::kRuntimePointerSize);
+ obsolete_dex_caches->Set(index, art_klass->GetDexCache());
+ index++;
+ }
}
// Try and get the declared method. First try to get a virtual method then a direct method if that's
diff --git a/runtime/openjdkjvmti/ti_redefine.h b/runtime/openjdkjvmti/ti_redefine.h
index 65ee2912e2..c441377b10 100644
--- a/runtime/openjdkjvmti/ti_redefine.h
+++ b/runtime/openjdkjvmti/ti_redefine.h
@@ -155,6 +155,12 @@ class Redefiner {
void FindAndAllocateObsoleteMethods(art::mirror::Class* art_klass)
REQUIRES(art::Locks::mutator_lock_);
+ void FillObsoleteMethodMap(
+ art::mirror::Class* art_klass,
+ const std::unordered_map<art::ArtMethod*, art::ArtMethod*>& obsoletes)
+ REQUIRES(art::Locks::mutator_lock_);
+
+
// Checks that the dex file contains only the single expected class and that the top-level class
// data has not been modified in an incompatible manner.
bool CheckClass() REQUIRES_SHARED(art::Locks::mutator_lock_);
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 51a24e4e01..d7ba1d75d8 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -874,13 +874,9 @@ void StackVisitor::WalkStack(bool include_transitions) {
CHECK_EQ(GetMethod(), callee) << "Expected: " << ArtMethod::PrettyMethod(callee)
<< " Found: " << ArtMethod::PrettyMethod(GetMethod());
} else {
- // Instrumentation generally doesn't distinguish between a method's obsolete and
- // non-obsolete version.
- CHECK_EQ(instrumentation_frame.method_->GetNonObsoleteMethod(),
- GetMethod()->GetNonObsoleteMethod())
- << "Expected: "
- << ArtMethod::PrettyMethod(instrumentation_frame.method_->GetNonObsoleteMethod())
- << " Found: " << ArtMethod::PrettyMethod(GetMethod()->GetNonObsoleteMethod());
+ CHECK_EQ(instrumentation_frame.method_, GetMethod())
+ << "Expected: " << ArtMethod::PrettyMethod(instrumentation_frame.method_)
+ << " Found: " << ArtMethod::PrettyMethod(GetMethod());
}
if (num_frames_ != 0) {
// Check agreement of frame Ids only if num_frames_ is computed to avoid infinite
@@ -907,7 +903,7 @@ void StackVisitor::WalkStack(bool include_transitions) {
<< " native=" << method->IsNative()
<< std::noboolalpha
<< " entrypoints=" << method->GetEntryPointFromQuickCompiledCode()
- << "," << (method->IsNative() ? method->GetEntryPointFromJni() : nullptr)
+ << "," << method->GetEntryPointFromJni()
<< " next=" << *cur_quick_frame_;
}
diff --git a/test/021-string2/src/Main.java b/test/021-string2/src/Main.java
index df0a3ddf48..5a43a4f23f 100644
--- a/test/021-string2/src/Main.java
+++ b/test/021-string2/src/Main.java
@@ -117,6 +117,9 @@ public class Main {
" " + $noinline$equals(s0_3, s0_1) +
" " + $noinline$equals(s0_3, s0_2) +
" " + $noinline$equals(s0_3, s0_3));
+
+ testEqualsConstString();
+ testConstStringEquals();
}
public static void testCompareToAndEquals() {
@@ -539,6 +542,266 @@ public class Main {
}
}
+ public static void testEqualsConstString() {
+ Assert.assertTrue($noinline$equalsConstString0(""));
+ Assert.assertFalse($noinline$equalsConstString0("1"));
+
+ Assert.assertTrue($noinline$equalsConstString7("0123456"));
+ Assert.assertFalse($noinline$equalsConstString7("012345"));
+ Assert.assertFalse($noinline$equalsConstString7("01234567"));
+ Assert.assertFalse($noinline$equalsConstString7("012345x"));
+ Assert.assertFalse($noinline$equalsConstString7("012345\u0440"));
+
+ Assert.assertTrue($noinline$equalsConstString14("01234567890123"));
+ Assert.assertFalse($noinline$equalsConstString14("0123456789012"));
+ Assert.assertFalse($noinline$equalsConstString14("012345678901234"));
+ Assert.assertFalse($noinline$equalsConstString14("0123456789012x"));
+ Assert.assertFalse($noinline$equalsConstString14("0123456789012\u0440"));
+
+ Assert.assertTrue($noinline$equalsConstString24("012345678901234567890123"));
+ Assert.assertFalse($noinline$equalsConstString24("01234567890123456789012"));
+ Assert.assertFalse($noinline$equalsConstString24("0123456789012345678901234"));
+ Assert.assertFalse($noinline$equalsConstString24("01234567890123456789012x"));
+ Assert.assertFalse($noinline$equalsConstString24("01234567890123456789012\u0440"));
+
+ Assert.assertTrue($noinline$equalsConstString29("01234567890123456789012345678"));
+ Assert.assertFalse($noinline$equalsConstString29("0123456789012345678901234567"));
+ Assert.assertFalse($noinline$equalsConstString29("012345678901234567890123456789"));
+ Assert.assertFalse($noinline$equalsConstString29("0123456789012345678901234567x"));
+ Assert.assertFalse($noinline$equalsConstString29("0123456789012345678901234567\u0440"));
+
+ Assert.assertTrue($noinline$equalsConstString35("01234567890123456789012345678901234"));
+ Assert.assertFalse($noinline$equalsConstString35("0123456789012345678901234567890123"));
+ Assert.assertFalse($noinline$equalsConstString35("012345678901234567890123456789012345"));
+ Assert.assertFalse($noinline$equalsConstString35("0123456789012345678901234567890123x"));
+ Assert.assertFalse(
+ $noinline$equalsConstString35("0123456789012345678901234567890123\u0440"));
+
+ Assert.assertTrue($noinline$equalsConstNonAsciiString7("\u0440123456"));
+ Assert.assertFalse($noinline$equalsConstNonAsciiString7("\u044012345"));
+ Assert.assertFalse($noinline$equalsConstNonAsciiString7("\u04401234567"));
+ Assert.assertFalse($noinline$equalsConstNonAsciiString7("\u044012345x"));
+ Assert.assertFalse($noinline$equalsConstNonAsciiString7("0123456"));
+
+ Assert.assertTrue($noinline$equalsConstNonAsciiString14("\u04401234567890123"));
+ Assert.assertFalse($noinline$equalsConstNonAsciiString14("\u0440123456789012"));
+ Assert.assertFalse($noinline$equalsConstNonAsciiString14("\u044012345678901234"));
+ Assert.assertFalse($noinline$equalsConstNonAsciiString14("\u0440123456789012x"));
+ Assert.assertFalse($noinline$equalsConstNonAsciiString14("01234567890123"));
+
+ Assert.assertTrue($noinline$equalsConstNonAsciiString24("\u044012345678901234567890123"));
+ Assert.assertFalse($noinline$equalsConstNonAsciiString24("\u04401234567890123456789012"));
+ Assert.assertFalse($noinline$equalsConstNonAsciiString24("\u0440123456789012345678901234"));
+ Assert.assertFalse($noinline$equalsConstNonAsciiString24("\u04401234567890123456789012x"));
+ Assert.assertFalse($noinline$equalsConstNonAsciiString24("\012345678901234567890123"));
+
+ Assert.assertTrue(
+ $noinline$equalsConstNonAsciiString29("\u04401234567890123456789012345678"));
+ Assert.assertFalse(
+ $noinline$equalsConstNonAsciiString29("\u0440123456789012345678901234567"));
+ Assert.assertFalse(
+ $noinline$equalsConstNonAsciiString29("\u044012345678901234567890123456789"));
+ Assert.assertFalse(
+ $noinline$equalsConstNonAsciiString29("\u0440123456789012345678901234567x"));
+ Assert.assertFalse($noinline$equalsConstNonAsciiString29("01234567890123456789012345678"));
+
+ Assert.assertTrue(
+ $noinline$equalsConstNonAsciiString35("\u04401234567890123456789012345678901234"));
+ Assert.assertFalse(
+ $noinline$equalsConstNonAsciiString35("\u0440123456789012345678901234567890123"));
+ Assert.assertFalse(
+ $noinline$equalsConstNonAsciiString35("\u044012345678901234567890123456789012345"));
+ Assert.assertFalse(
+ $noinline$equalsConstNonAsciiString35("\u0440123456789012345678901234567890123x"));
+ Assert.assertFalse(
+ $noinline$equalsConstNonAsciiString35("01234567890123456789012345678901234"));
+ }
+
+ public static void testConstStringEquals() {
+ Assert.assertTrue($noinline$constString0Equals(""));
+ Assert.assertFalse($noinline$constString0Equals("1"));
+
+ Assert.assertTrue($noinline$constString7Equals("0123456"));
+ Assert.assertFalse($noinline$constString7Equals("012345"));
+ Assert.assertFalse($noinline$constString7Equals("01234567"));
+ Assert.assertFalse($noinline$constString7Equals("012345x"));
+ Assert.assertFalse($noinline$constString7Equals("012345\u0440"));
+
+ Assert.assertTrue($noinline$constString14Equals("01234567890123"));
+ Assert.assertFalse($noinline$constString14Equals("0123456789012"));
+ Assert.assertFalse($noinline$constString14Equals("012345678901234"));
+ Assert.assertFalse($noinline$constString14Equals("0123456789012x"));
+ Assert.assertFalse($noinline$constString14Equals("0123456789012\u0440"));
+
+ Assert.assertTrue($noinline$constString24Equals("012345678901234567890123"));
+ Assert.assertFalse($noinline$constString24Equals("01234567890123456789012"));
+ Assert.assertFalse($noinline$constString24Equals("0123456789012345678901234"));
+ Assert.assertFalse($noinline$constString24Equals("01234567890123456789012x"));
+ Assert.assertFalse($noinline$constString24Equals("01234567890123456789012\u0440"));
+
+ Assert.assertTrue($noinline$constString29Equals("01234567890123456789012345678"));
+ Assert.assertFalse($noinline$constString29Equals("0123456789012345678901234567"));
+ Assert.assertFalse($noinline$constString29Equals("012345678901234567890123456789"));
+ Assert.assertFalse($noinline$constString29Equals("0123456789012345678901234567x"));
+ Assert.assertFalse($noinline$constString29Equals("0123456789012345678901234567\u0440"));
+
+ Assert.assertTrue($noinline$constString35Equals("01234567890123456789012345678901234"));
+ Assert.assertFalse($noinline$constString35Equals("0123456789012345678901234567890123"));
+ Assert.assertFalse($noinline$constString35Equals("012345678901234567890123456789012345"));
+ Assert.assertFalse($noinline$constString35Equals("0123456789012345678901234567890123x"));
+ Assert.assertFalse(
+ $noinline$constString35Equals("0123456789012345678901234567890123\u0040"));
+
+ Assert.assertTrue($noinline$constNonAsciiString7Equals("\u0440123456"));
+ Assert.assertFalse($noinline$constNonAsciiString7Equals("\u044012345"));
+ Assert.assertFalse($noinline$constNonAsciiString7Equals("\u04401234567"));
+ Assert.assertFalse($noinline$constNonAsciiString7Equals("\u044012345x"));
+ Assert.assertFalse($noinline$constNonAsciiString7Equals("0123456"));
+
+ Assert.assertTrue($noinline$constNonAsciiString14Equals("\u04401234567890123"));
+ Assert.assertFalse($noinline$constNonAsciiString14Equals("\u0440123456789012"));
+ Assert.assertFalse($noinline$constNonAsciiString14Equals("\u044012345678901234"));
+ Assert.assertFalse($noinline$constNonAsciiString14Equals("\u0440123456789012x"));
+ Assert.assertFalse($noinline$constNonAsciiString14Equals("01234567890123"));
+
+ Assert.assertTrue($noinline$constNonAsciiString24Equals("\u044012345678901234567890123"));
+ Assert.assertFalse($noinline$constNonAsciiString24Equals("\u04401234567890123456789012"));
+ Assert.assertFalse($noinline$constNonAsciiString24Equals("\u0440123456789012345678901234"));
+ Assert.assertFalse($noinline$constNonAsciiString24Equals("\u04401234567890123456789012x"));
+ Assert.assertFalse($noinline$constNonAsciiString24Equals("\012345678901234567890123"));
+
+ Assert.assertTrue(
+ $noinline$constNonAsciiString29Equals("\u04401234567890123456789012345678"));
+ Assert.assertFalse(
+ $noinline$constNonAsciiString29Equals("\u0440123456789012345678901234567"));
+ Assert.assertFalse(
+ $noinline$constNonAsciiString29Equals("\u044012345678901234567890123456789"));
+ Assert.assertFalse(
+ $noinline$constNonAsciiString29Equals("\u0440123456789012345678901234567x"));
+ Assert.assertFalse($noinline$constNonAsciiString29Equals("01234567890123456789012345678"));
+
+ Assert.assertTrue(
+ $noinline$constNonAsciiString35Equals("\u04401234567890123456789012345678901234"));
+ Assert.assertFalse(
+ $noinline$constNonAsciiString35Equals("\u0440123456789012345678901234567890123"));
+ Assert.assertFalse(
+ $noinline$constNonAsciiString35Equals("\u044012345678901234567890123456789012345"));
+ Assert.assertFalse(
+ $noinline$constNonAsciiString35Equals("\u0440123456789012345678901234567890123x"));
+ Assert.assertFalse(
+ $noinline$constNonAsciiString35Equals("01234567890123456789012345678901234"));
+ }
+
+ public static boolean $noinline$equalsConstString0(String s) {
+ if (doThrow) { throw new Error(); }
+ return s.equals("");
+ }
+
+ public static boolean $noinline$equalsConstString7(String s) {
+ if (doThrow) { throw new Error(); }
+ return s.equals("0123456");
+ }
+
+ public static boolean $noinline$equalsConstString14(String s) {
+ if (doThrow) { throw new Error(); }
+ return s.equals("01234567890123");
+ }
+
+ public static boolean $noinline$equalsConstString24(String s) {
+ if (doThrow) { throw new Error(); }
+ return s.equals("012345678901234567890123");
+ }
+
+ public static boolean $noinline$equalsConstString29(String s) {
+ if (doThrow) { throw new Error(); }
+ return s.equals("01234567890123456789012345678");
+ }
+
+ public static boolean $noinline$equalsConstString35(String s) {
+ if (doThrow) { throw new Error(); }
+ return s.equals("01234567890123456789012345678901234");
+ }
+
+ public static boolean $noinline$equalsConstNonAsciiString7(String s) {
+ if (doThrow) { throw new Error(); }
+ return s.equals("\u0440123456");
+ }
+
+ public static boolean $noinline$equalsConstNonAsciiString14(String s) {
+ if (doThrow) { throw new Error(); }
+ return s.equals("\u04401234567890123");
+ }
+
+ public static boolean $noinline$equalsConstNonAsciiString24(String s) {
+ if (doThrow) { throw new Error(); }
+ return s.equals("\u044012345678901234567890123");
+ }
+
+ public static boolean $noinline$equalsConstNonAsciiString29(String s) {
+ if (doThrow) { throw new Error(); }
+ return s.equals("\u04401234567890123456789012345678");
+ }
+
+ public static boolean $noinline$equalsConstNonAsciiString35(String s) {
+ if (doThrow) { throw new Error(); }
+ return s.equals("\u04401234567890123456789012345678901234");
+ }
+
+ public static boolean $noinline$constString0Equals(String s) {
+ if (doThrow) { throw new Error(); }
+ return s.equals("");
+ }
+
+ public static boolean $noinline$constString7Equals(String s) {
+ if (doThrow) { throw new Error(); }
+ return "0123456".equals(s);
+ }
+
+ public static boolean $noinline$constString14Equals(String s) {
+ if (doThrow) { throw new Error(); }
+ return "01234567890123".equals(s);
+ }
+
+ public static boolean $noinline$constString24Equals(String s) {
+ if (doThrow) { throw new Error(); }
+ return "012345678901234567890123".equals(s);
+ }
+
+ public static boolean $noinline$constString29Equals(String s) {
+ if (doThrow) { throw new Error(); }
+ return "01234567890123456789012345678".equals(s);
+ }
+
+ public static boolean $noinline$constString35Equals(String s) {
+ if (doThrow) { throw new Error(); }
+ return "01234567890123456789012345678901234".equals(s);
+ }
+
+ public static boolean $noinline$constNonAsciiString7Equals(String s) {
+ if (doThrow) { throw new Error(); }
+ return "\u0440123456".equals(s);
+ }
+
+ public static boolean $noinline$constNonAsciiString14Equals(String s) {
+ if (doThrow) { throw new Error(); }
+ return "\u04401234567890123".equals(s);
+ }
+
+ public static boolean $noinline$constNonAsciiString24Equals(String s) {
+ if (doThrow) { throw new Error(); }
+ return "\u044012345678901234567890123".equals(s);
+ }
+
+ public static boolean $noinline$constNonAsciiString29Equals(String s) {
+ if (doThrow) { throw new Error(); }
+ return "\u04401234567890123456789012345678".equals(s);
+ }
+
+ public static boolean $noinline$constNonAsciiString35Equals(String s) {
+ if (doThrow) { throw new Error(); }
+ return "\u04401234567890123456789012345678901234".equals(s);
+ }
+
public static int $noinline$compareTo(String lhs, String rhs) {
if (doThrow) { throw new Error(); }
return lhs.compareTo(rhs);
diff --git a/test/536-checker-intrinsic-optimization/src/Main.java b/test/536-checker-intrinsic-optimization/src/Main.java
index ed7524c7ad..52f3f84406 100644
--- a/test/536-checker-intrinsic-optimization/src/Main.java
+++ b/test/536-checker-intrinsic-optimization/src/Main.java
@@ -329,7 +329,7 @@ public class Main {
/// CHECK-NOT: cbz
// Terminate the scope for the CHECK-NOT search at the reference or length comparison,
// whichever comes first.
- /// CHECK: cmp {{w.*,}} {{w.*}}
+ /// CHECK: cmp {{w.*,}} {{w.*|#.*}}
public static boolean stringArgumentNotNull(Object obj) {
obj.getClass();
return "foo".equals(obj);
@@ -380,10 +380,10 @@ public class Main {
// so repeat the check twice.
/// CHECK-NOT: ldr {{w\d+}}, [{{x\d+}}]
/// CHECK-NOT: ldr {{w\d+}}, [{{x\d+}}, #0]
- /// CHECK: cmp {{w\d+}}, {{w\d+}}
+ /// CHECK: cmp {{w\d+}}, {{w\d+|#.*}}
/// CHECK-NOT: ldr {{w\d+}}, [{{x\d+}}]
/// CHECK-NOT: ldr {{w\d+}}, [{{x\d+}}, #0]
- /// CHECK: cmp {{w\d+}}, {{w\d+}}
+ /// CHECK: cmp {{w\d+}}, {{w\d+|#.*}}
public static boolean stringArgumentIsString() {
return "foo".equals(myString);
}
diff --git a/test/945-obsolete-native/build b/test/945-obsolete-native/build
deleted file mode 100755
index 898e2e54a2..0000000000
--- a/test/945-obsolete-native/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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.
-
-./default-build "$@" --experimental agents
diff --git a/test/945-obsolete-native/expected.txt b/test/945-obsolete-native/expected.txt
deleted file mode 100644
index 83efda144d..0000000000
--- a/test/945-obsolete-native/expected.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-hello
-Not doing anything here
-goodbye
-hello
-transforming calling function
-goodbye
-Hello - Transformed
-Not doing anything here
-Goodbye - Transformed
diff --git a/test/945-obsolete-native/info.txt b/test/945-obsolete-native/info.txt
deleted file mode 100644
index c8b892cedd..0000000000
--- a/test/945-obsolete-native/info.txt
+++ /dev/null
@@ -1 +0,0 @@
-Tests basic obsolete method support
diff --git a/test/945-obsolete-native/obsolete_native.cc b/test/945-obsolete-native/obsolete_native.cc
deleted file mode 100644
index 061e7afbbc..0000000000
--- a/test/945-obsolete-native/obsolete_native.cc
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-#include <inttypes.h>
-#include <memory>
-#include <stdio.h>
-
-#include "android-base/stringprintf.h"
-
-#include "android-base/stringprintf.h"
-#include "base/logging.h"
-#include "base/macros.h"
-#include "jni.h"
-#include "openjdkjvmti/jvmti.h"
-#include "ScopedLocalRef.h"
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
-
-namespace art {
-namespace Test945ObsoleteNative {
-
-extern "C" JNIEXPORT void JNICALL Java_Main_bindTest945ObsoleteNative(
- JNIEnv* env, jclass klass ATTRIBUTE_UNUSED) {
- BindFunctions(jvmti_env, env, "Transform");
-}
-
-extern "C" JNIEXPORT void JNICALL Java_Transform_doExecute(JNIEnv* env,
- jclass klass ATTRIBUTE_UNUSED,
- jobject runnable) {
- jclass runnable_klass = env->FindClass("java/lang/Runnable");
- DCHECK(runnable_klass != nullptr);
- jmethodID run_method = env->GetMethodID(runnable_klass, "run", "()V");
- env->CallVoidMethod(runnable, run_method);
-}
-
-
-} // namespace Test945ObsoleteNative
-} // namespace art
diff --git a/test/945-obsolete-native/run b/test/945-obsolete-native/run
deleted file mode 100755
index c6e62ae6cd..0000000000
--- a/test/945-obsolete-native/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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.
-
-./default-run "$@" --jvmti
diff --git a/test/945-obsolete-native/src/Main.java b/test/945-obsolete-native/src/Main.java
deleted file mode 100644
index 5e2154e9a3..0000000000
--- a/test/945-obsolete-native/src/Main.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * 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.Base64;
-
-public class Main {
- // class Transform {
- // public void sayHi(Runnable r) {
- // System.out.println("Hello - Transformed");
- // doExecute(r);
- // System.out.println("Goodbye - Transformed");
- // }
- //
- // private static native void doExecute(Runnable r);
- // }
- private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
- "yv66vgAAADQAIgoACAASCQATABQIABUKABYAFwoABwAYCAAZBwAaBwAbAQAGPGluaXQ+AQADKClW" +
- "AQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEABXNheUhpAQAXKExqYXZhL2xhbmcvUnVubmFibGU7" +
- "KVYBAAlkb0V4ZWN1dGUBAApTb3VyY2VGaWxlAQAOVHJhbnNmb3JtLmphdmEMAAkACgcAHAwAHQAe" +
- "AQATSGVsbG8gLSBUcmFuc2Zvcm1lZAcAHwwAIAAhDAAPAA4BABVHb29kYnllIC0gVHJhbnNmb3Jt" +
- "ZWQBAAlUcmFuc2Zvcm0BABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291" +
- "dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxu" +
- "AQAVKExqYXZhL2xhbmcvU3RyaW5nOylWACAABwAIAAAAAAADAAAACQAKAAEACwAAAB0AAQABAAAA" +
- "BSq3AAGxAAAAAQAMAAAABgABAAAAEQABAA0ADgABAAsAAAA5AAIAAgAAABWyAAISA7YABCu4AAWy" +
- "AAISBrYABLEAAAABAAwAAAASAAQAAAATAAgAFAAMABUAFAAWAQoADwAOAAAAAQAQAAAAAgAR");
- private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
- "ZGV4CjAzNQB1fZcJR/opPuXacK8mIla5shH0LSg72qJYAwAAcAAAAHhWNBIAAAAAAAAAALgCAAAR" +
- "AAAAcAAAAAcAAAC0AAAAAwAAANAAAAABAAAA9AAAAAUAAAD8AAAAAQAAACQBAAAUAgAARAEAAKIB" +
- "AACqAQAAwQEAANYBAADjAQAA+gEAAA4CAAAkAgAAOAIAAEwCAABcAgAAXwIAAGMCAABuAgAAggIA" +
- "AIcCAACQAgAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAACgAAAAoAAAAGAAAAAAAAAAsAAAAGAAAA" +
- "lAEAAAsAAAAGAAAAnAEAAAUAAQAOAAAAAAAAAAAAAAAAAAEADAAAAAAAAQAQAAAAAQACAA8AAAAC" +
- "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAJAAAAAAAAAKUCAAAAAAAAAQABAAEAAACXAgAABAAAAHAQ" +
- "BAAAAA4ABAACAAIAAACcAgAAFAAAAGIAAAAbAQIAAABuIAMAEABxEAEAAwBiAAAAGwEBAAAAbiAD" +
- "ABAADgABAAAAAwAAAAEAAAAEAAY8aW5pdD4AFUdvb2RieWUgLSBUcmFuc2Zvcm1lZAATSGVsbG8g" +
- "LSBUcmFuc2Zvcm1lZAALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwASTGphdmEv" +
- "bGFuZy9PYmplY3Q7ABRMamF2YS9sYW5nL1J1bm5hYmxlOwASTGphdmEvbGFuZy9TdHJpbmc7ABJM" +
- "amF2YS9sYW5nL1N5c3RlbTsADlRyYW5zZm9ybS5qYXZhAAFWAAJWTAAJZG9FeGVjdXRlABJlbWl0" +
- "dGVyOiBqYWNrLTQuMjUAA291dAAHcHJpbnRsbgAFc2F5SGkAEQAHDgATAQAHDoc8hwAAAAIBAICA" +
- "BMQCAYoCAAIB3AIADQAAAAAAAAABAAAAAAAAAAEAAAARAAAAcAAAAAIAAAAHAAAAtAAAAAMAAAAD" +
- "AAAA0AAAAAQAAAABAAAA9AAAAAUAAAAFAAAA/AAAAAYAAAABAAAAJAEAAAEgAAACAAAARAEAAAEQ" +
- "AAACAAAAlAEAAAIgAAARAAAAogEAAAMgAAACAAAAlwIAAAAgAAABAAAApQIAAAAQAAABAAAAuAIA" +
- "AA==");
-
- public static void main(String[] args) {
- bindTest945ObsoleteNative();
- doTest(new Transform());
- }
-
- public static void doTest(Transform t) {
- t.sayHi(() -> { System.out.println("Not doing anything here"); });
- t.sayHi(() -> {
- System.out.println("transforming calling function");
- doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
- });
- t.sayHi(() -> { System.out.println("Not doing anything here"); });
- }
-
- // Transforms the class
- private static native void doCommonClassRedefinition(Class<?> target,
- byte[] classfile,
- byte[] dexfile);
-
- private static native void bindTest945ObsoleteNative();
-}
diff --git a/test/945-obsolete-native/src/Transform.java b/test/945-obsolete-native/src/Transform.java
deleted file mode 100644
index 2b7cc1b3a1..0000000000
--- a/test/945-obsolete-native/src/Transform.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * 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 Transform {
- public void sayHi(Runnable r) {
- System.out.println("hello");
- doExecute(r);
- System.out.println("goodbye");
- }
-
- private static native void doExecute(Runnable r);
-}
diff --git a/test/Android.bp b/test/Android.bp
index 00c890a834..d3244a683a 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -274,7 +274,6 @@ art_cc_defaults {
"933-misc-events/misc_events.cc",
"936-search-onload/search_onload.cc",
"944-transform-classloaders/classloader.cc",
- "945-obsolete-native/obsolete_native.cc",
],
shared_libs: [
"libbase",
diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc
index 351857d1d9..c5a93568c6 100644
--- a/test/ti-agent/common_load.cc
+++ b/test/ti-agent/common_load.cc
@@ -122,7 +122,6 @@ static AgentLib agents[] = {
{ "942-private-recursive", common_redefine::OnLoad, nullptr },
{ "943-private-recursive-jit", common_redefine::OnLoad, nullptr },
{ "944-transform-classloaders", common_redefine::OnLoad, nullptr },
- { "945-obsolete-native", common_redefine::OnLoad, nullptr },
};
static AgentLib* FindAgent(char* name) {
diff --git a/tools/run-libcore-tests.sh b/tools/run-libcore-tests.sh
index 6e123ce7e4..729a3e5ac4 100755
--- a/tools/run-libcore-tests.sh
+++ b/tools/run-libcore-tests.sh
@@ -14,9 +14,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# Exit as a stop-gap measure for b/35308152.
-exit 0
-
if [ ! -d libcore ]; then
echo "Script needs to be run at the root of the android tree"
exit 1