summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/dex/dex_to_dex_compiler.cc6
-rw-r--r--dex2oat/dex2oat_test.cc89
-rw-r--r--dex2oat/linker/oat_writer.cc65
-rw-r--r--dexlayout/dexlayout_test.cc25
-rw-r--r--libdexfile/dex/compact_offset_table.cc16
-rw-r--r--libdexfile/dex/compact_offset_table.h10
-rw-r--r--libdexfile/dex/compact_offset_table_test.cc10
-rw-r--r--runtime/base/unix_file/fd_file.cc6
-rw-r--r--runtime/class_linker.cc25
-rw-r--r--runtime/common_runtime_test.h28
-rw-r--r--runtime/intern_table.cc2
-rw-r--r--runtime/jit/jit.cc4
-rw-r--r--runtime/native/dalvik_system_VMDebug.cc2
-rw-r--r--runtime/native/java_lang_Class.cc2
-rw-r--r--runtime/native/java_lang_String.cc4
-rw-r--r--runtime/native/java_lang_System.cc37
-rw-r--r--runtime/native/java_lang_reflect_Array.cc2
-rw-r--r--runtime/native/java_lang_reflect_Executable.cc8
-rw-r--r--runtime/native/java_lang_reflect_Field.cc4
-rw-r--r--runtime/native/java_lang_reflect_Method.cc2
-rw-r--r--runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc2
-rw-r--r--runtime/native/sun_misc_Unsafe.cc4
-rw-r--r--runtime/quicken_info.h56
-rw-r--r--runtime/vdex_file.cc66
-rw-r--r--runtime/vdex_file.h10
-rw-r--r--test/676-proxy-jit-at-first-use/expected.txt1
-rw-r--r--test/676-proxy-jit-at-first-use/info.txt1
-rw-r--r--test/676-proxy-jit-at-first-use/run19
-rw-r--r--test/676-proxy-jit-at-first-use/src/Main.java39
-rw-r--r--test/676-resolve-field-type/expected.txt1
-rw-r--r--test/676-resolve-field-type/info.txt2
-rw-r--r--test/676-resolve-field-type/src-art/Foo.java19
-rw-r--r--test/676-resolve-field-type/src-art/Main.java31
-rw-r--r--test/676-resolve-field-type/src-ex/ChildClass.java72
-rw-r--r--test/knownfailures.json5
-rw-r--r--tools/Android.bp8
36 files changed, 461 insertions, 222 deletions
diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc
index 9f0aaa4e10..0caf1b14a6 100644
--- a/compiler/dex/dex_to_dex_compiler.cc
+++ b/compiler/dex/dex_to_dex_compiler.cc
@@ -341,10 +341,8 @@ std::vector<uint8_t> DexToDexCompiler::CompilationState::Compile() {
DCHECK_EQ(quicken_index_, existing_quicken_info_.NumIndices());
}
- if (GetQuickenedInfo().empty()) {
- // No need to create a CompiledMethod if there are no quickened opcodes.
- return std::vector<uint8_t>();
- }
+ // Even if there are no indicies, generate an empty quicken info so that we know the method was
+ // quickened.
std::vector<uint8_t> quicken_data;
if (kIsDebugBuild) {
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index 6d3658f789..b16c56a3ad 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -997,7 +997,7 @@ class Dex2oatUnquickenTest : public Dex2oatTest {
if (class_it.IsAtMethod() && class_it.GetMethodCodeItem() != nullptr) {
for (const DexInstructionPcPair& inst :
CodeItemInstructionAccessor(*dex_file, class_it.GetMethodCodeItem())) {
- ASSERT_FALSE(inst->IsQuickened()) << output_;
+ ASSERT_FALSE(inst->IsQuickened()) << inst->Opcode() << " " << output_;
}
}
}
@@ -1871,4 +1871,91 @@ TEST_F(Dex2oatTest, DontExtract) {
EXPECT_TRUE(found_fast_verify) << "Expected to find " << kFastVerifyString << "\n" << output_;
}
+// Test that dex files with quickened opcodes aren't dequickened.
+TEST_F(Dex2oatTest, QuickenedInput) {
+ std::string error_msg;
+ ScratchFile temp_dex;
+ MutateDexFile(temp_dex.GetFile(), GetTestDexFileName("ManyMethods"), [] (DexFile* dex) {
+ bool mutated_successfully = false;
+ // Change the dex instructions to make an opcode that spans past the end of the code item.
+ for (size_t i = 0; i < dex->NumClassDefs(); ++i) {
+ const DexFile::ClassDef& def = dex->GetClassDef(i);
+ const uint8_t* data = dex->GetClassData(def);
+ if (data == nullptr) {
+ continue;
+ }
+ ClassDataItemIterator it(*dex, data);
+ it.SkipAllFields();
+ while (it.HasNextMethod()) {
+ DexFile::CodeItem* item = const_cast<DexFile::CodeItem*>(it.GetMethodCodeItem());
+ if (item != nullptr) {
+ CodeItemInstructionAccessor instructions(*dex, item);
+ // Make a quickened instruction that doesn't run past the end of the code item.
+ if (instructions.InsnsSizeInCodeUnits() > 2) {
+ const_cast<Instruction&>(instructions.InstructionAt(0)).SetOpcode(
+ Instruction::IGET_BYTE_QUICK);
+ mutated_successfully = true;
+ }
+ }
+ it.Next();
+ }
+ }
+ CHECK(mutated_successfully)
+ << "Failed to find candidate code item with only one code unit in last instruction.";
+ });
+
+ std::string dex_location = temp_dex.GetFilename();
+ std::string odex_location = GetOdexDir() + "/quickened.odex";
+ std::string vdex_location = GetOdexDir() + "/quickened.vdex";
+ std::unique_ptr<File> vdex_output(OS::CreateEmptyFile(vdex_location.c_str()));
+ // Quicken the dex
+ {
+ std::string input_vdex = "--input-vdex-fd=-1";
+ std::string output_vdex = StringPrintf("--output-vdex-fd=%d", vdex_output->Fd());
+ GenerateOdexForTest(dex_location,
+ odex_location,
+ CompilerFilter::kQuicken,
+ // Disable cdex since we want to compare against the original dex file
+ // after unquickening.
+ { input_vdex, output_vdex, kDisableCompactDex },
+ /* expect_success */ true,
+ /* use_fd */ true);
+ }
+ // Unquicken by running the verify compiler filter on the vdex file and verify it matches.
+ std::string odex_location2 = GetOdexDir() + "/unquickened.odex";
+ std::string vdex_location2 = GetOdexDir() + "/unquickened.vdex";
+ std::unique_ptr<File> vdex_unquickened(OS::CreateEmptyFile(vdex_location2.c_str()));
+ {
+ std::string input_vdex = StringPrintf("--input-vdex-fd=%d", vdex_output->Fd());
+ std::string output_vdex = StringPrintf("--output-vdex-fd=%d", vdex_unquickened->Fd());
+ GenerateOdexForTest(dex_location,
+ odex_location2,
+ CompilerFilter::kVerify,
+ // Disable cdex to avoid needing to write out the shared section.
+ { input_vdex, output_vdex, kDisableCompactDex },
+ /* expect_success */ true,
+ /* use_fd */ true);
+ }
+ ASSERT_EQ(vdex_unquickened->Flush(), 0) << "Could not flush and close vdex file";
+ ASSERT_TRUE(success_);
+ {
+ // Check that hte vdex has one dex and compare it to the original one.
+ std::unique_ptr<VdexFile> vdex(VdexFile::Open(vdex_location2.c_str(),
+ /*writable*/ false,
+ /*low_4gb*/ false,
+ /*unquicken*/ false,
+ &error_msg));
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+ bool result = vdex->OpenAllDexFiles(&dex_files, &error_msg);
+ ASSERT_TRUE(result) << error_msg;
+ ASSERT_EQ(dex_files.size(), 1u) << error_msg;
+ ScratchFile temp;
+ ASSERT_TRUE(temp.GetFile()->WriteFully(dex_files[0]->Begin(), dex_files[0]->Size()));
+ ASSERT_EQ(temp.GetFile()->Flush(), 0) << "Could not flush extracted dex";
+ EXPECT_EQ(temp.GetFile()->Compare(temp_dex.GetFile()), 0);
+ }
+ ASSERT_EQ(vdex_output->FlushCloseOrErase(), 0) << "Could not flush and close";
+ ASSERT_EQ(vdex_unquickened->FlushCloseOrErase(), 0) << "Could not flush and close";
+}
+
} // namespace art
diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc
index ee872db558..ff0729f6e3 100644
--- a/dex2oat/linker/oat_writer.cc
+++ b/dex2oat/linker/oat_writer.cc
@@ -2631,45 +2631,37 @@ class OatWriter::WriteQuickeningInfoMethodVisitor {
out_(out) {}
bool VisitDexMethods(const std::vector<const DexFile*>& dex_files) {
- std::vector<uint8_t> empty_quicken_info;
- {
- // Since we need to be able to access by dex method index, put a one byte empty quicken info
- // for any method that isn't quickened.
- QuickenInfoTable::Builder empty_info(&empty_quicken_info, /*num_elements*/ 0u);
- CHECK(!empty_quicken_info.empty());
- }
+ // Map of offsets for quicken info related to method indices.
+ SafeMap<const uint8_t*, uint32_t> offset_map;
+ // Use method index order to minimize the encoded size of the offset table.
for (const DexFile* dex_file : dex_files) {
std::vector<uint32_t>* const offsets =
&quicken_info_offset_indices_.Put(dex_file, std::vector<uint32_t>())->second;
-
- // Every method needs an index in the table.
for (uint32_t method_idx = 0; method_idx < dex_file->NumMethodIds(); ++method_idx) {
- ArrayRef<const uint8_t> map(empty_quicken_info);
-
- // Use the existing quicken info if it exists.
+ uint32_t offset = 0u;
MethodReference method_ref(dex_file, method_idx);
CompiledMethod* compiled_method = writer_->compiler_driver_->GetCompiledMethod(method_ref);
if (compiled_method != nullptr && HasQuickeningInfo(compiled_method)) {
- map = compiled_method->GetVmapTable();
- }
-
- // The current approach prevents deduplication of quicken infos since each method index
- // has one unique quicken info. Deduplication does not provide much savings for dex indices
- // since they are rarely duplicated.
- const uint32_t length = map.size() * sizeof(map.front());
-
- // Record each index if required. written_bytes_ is the offset from the start of the
- // quicken info data.
- if (QuickenInfoOffsetTableAccessor::IsCoveredIndex(method_idx)) {
- offsets->push_back(written_bytes_);
- }
-
- if (!out_->WriteFully(map.data(), length)) {
- PLOG(ERROR) << "Failed to write quickening info for " << method_ref.PrettyMethod()
- << " to " << out_->GetLocation();
- return false;
+ ArrayRef<const uint8_t> map = compiled_method->GetVmapTable();
+
+ // Record each index if required. written_bytes_ is the offset from the start of the
+ // quicken info data.
+ // May be already inserted for deduplicate items.
+ // Add offset of one to make sure 0 represents unused.
+ auto pair = offset_map.emplace(map.data(), written_bytes_ + 1);
+ offset = pair.first->second;
+ // Write out the map if it's not already written.
+ if (pair.second) {
+ const uint32_t length = map.size() * sizeof(map.front());
+ if (!out_->WriteFully(map.data(), length)) {
+ PLOG(ERROR) << "Failed to write quickening info for " << method_ref.PrettyMethod()
+ << " to " << out_->GetLocation();
+ return false;
+ }
+ written_bytes_ += length;
+ }
}
- written_bytes_ += length;
+ offsets->push_back(offset);
}
}
return true;
@@ -2683,12 +2675,10 @@ class OatWriter::WriteQuickeningInfoMethodVisitor {
return quicken_info_offset_indices_;
}
-
private:
OatWriter* const writer_;
OutputStream* const out_;
size_t written_bytes_ = 0u;
- // Map of offsets for quicken info related to method indices.
SafeMap<const DexFile*, std::vector<uint32_t>> quicken_info_offset_indices_;
};
@@ -2712,14 +2702,11 @@ class OatWriter::WriteQuickeningInfoOffsetsMethodVisitor {
const std::vector<uint32_t>* const offsets = &it->second;
const uint32_t current_offset = start_offset_ + written_bytes_;
- CHECK_ALIGNED_PARAM(current_offset, QuickenInfoOffsetTableAccessor::Alignment());
+ CHECK_ALIGNED_PARAM(current_offset, CompactOffsetTable::kAlignment);
// Generate and write the data.
std::vector<uint8_t> table_data;
- QuickenInfoOffsetTableAccessor::Builder builder(&table_data);
- for (uint32_t offset : *offsets) {
- builder.AddOffset(offset);
- }
+ CompactOffsetTable::Build(*offsets, &table_data);
// Store the offset since we need to put those after the dex file. Table offsets are relative
// to the start of the quicken info section.
@@ -2780,7 +2767,7 @@ bool OatWriter::WriteQuickeningInfo(OutputStream* vdex_out) {
uint32_t quicken_info_offset = write_quicken_info_visitor.GetNumberOfWrittenBytes();
current_offset = current_offset + quicken_info_offset;
uint32_t before_offset = current_offset;
- current_offset = RoundUp(current_offset, QuickenInfoOffsetTableAccessor::Alignment());
+ current_offset = RoundUp(current_offset, CompactOffsetTable::kAlignment);
const size_t extra_bytes = current_offset - before_offset;
quicken_info_offset += extra_bytes;
actual_offset = vdex_out->Seek(current_offset, kSeekSet);
diff --git a/dexlayout/dexlayout_test.cc b/dexlayout/dexlayout_test.cc
index d9a93ddf36..981f9010ee 100644
--- a/dexlayout/dexlayout_test.cc
+++ b/dexlayout/dexlayout_test.cc
@@ -321,31 +321,6 @@ class DexLayoutTest : public CommonRuntimeTest {
return true;
}
- template <typename Mutator>
- bool MutateDexFile(File* output_dex, const std::string& input_jar, const Mutator& mutator) {
- std::vector<std::unique_ptr<const DexFile>> dex_files;
- std::string error_msg;
- const ArtDexFileLoader dex_file_loader;
- CHECK(dex_file_loader.Open(input_jar.c_str(),
- input_jar.c_str(),
- /*verify*/ true,
- /*verify_checksum*/ true,
- &error_msg,
- &dex_files)) << error_msg;
- EXPECT_EQ(dex_files.size(), 1u) << "Only one input dex is supported";
- for (const std::unique_ptr<const DexFile>& dex : dex_files) {
- CHECK(dex->EnableWrite()) << "Failed to enable write";
- mutator(const_cast<DexFile*>(dex.get()));
- if (!output_dex->WriteFully(dex->Begin(), dex->Size())) {
- return false;
- }
- }
- if (output_dex->Flush() != 0) {
- PLOG(FATAL) << "Could not flush the output file.";
- }
- return true;
- }
-
// Create a profile with some subset of methods and classes.
void CreateProfile(const std::string& input_dex,
const std::string& out_profile,
diff --git a/libdexfile/dex/compact_offset_table.cc b/libdexfile/dex/compact_offset_table.cc
index 8cee0e376a..60a7b61d11 100644
--- a/libdexfile/dex/compact_offset_table.cc
+++ b/libdexfile/dex/compact_offset_table.cc
@@ -30,6 +30,11 @@ CompactOffsetTable::Accessor::Accessor(const uint8_t* data_begin,
minimum_offset_(minimum_offset),
data_begin_(data_begin) {}
+CompactOffsetTable::Accessor::Accessor(const uint8_t* data_begin)
+ : Accessor(data_begin + 2 * sizeof(uint32_t),
+ reinterpret_cast<const uint32_t*>(data_begin)[0],
+ reinterpret_cast<const uint32_t*>(data_begin)[1]) {}
+
uint32_t CompactOffsetTable::Accessor::GetOffset(uint32_t index) const {
const uint32_t offset = table_[index / kElementsPerIndex];
const size_t bit_index = index % kElementsPerIndex;
@@ -56,6 +61,17 @@ uint32_t CompactOffsetTable::Accessor::GetOffset(uint32_t index) const {
}
void CompactOffsetTable::Build(const std::vector<uint32_t>& offsets,
+ std::vector<uint8_t>* out_data) {
+ static constexpr size_t kNumOffsets = 2;
+ uint32_t out_offsets[kNumOffsets] = {};
+ CompactOffsetTable::Build(offsets, out_data, &out_offsets[0], &out_offsets[1]);
+ // Write the offsets at the start of the debug info.
+ out_data->insert(out_data->begin(),
+ reinterpret_cast<const uint8_t*>(&out_offsets[0]),
+ reinterpret_cast<const uint8_t*>(&out_offsets[kNumOffsets]));
+}
+
+void CompactOffsetTable::Build(const std::vector<uint32_t>& offsets,
std::vector<uint8_t>* out_data,
uint32_t* out_min_offset,
uint32_t* out_table_offset) {
diff --git a/libdexfile/dex/compact_offset_table.h b/libdexfile/dex/compact_offset_table.h
index 17e6bb41ed..ec759e200d 100644
--- a/libdexfile/dex/compact_offset_table.h
+++ b/libdexfile/dex/compact_offset_table.h
@@ -37,9 +37,10 @@ class CompactOffsetTable {
class Accessor {
public:
- Accessor(const uint8_t* data_begin,
- uint32_t minimum_offset,
- uint32_t table_offset);
+ // Read the minimum and table offsets from the data pointer.
+ explicit Accessor(const uint8_t* data_begin);
+
+ Accessor(const uint8_t* data_begin, uint32_t minimum_offset, uint32_t table_offset);
// Return the offset for the index.
uint32_t GetOffset(uint32_t index) const;
@@ -50,6 +51,9 @@ class CompactOffsetTable {
const uint8_t* const data_begin_;
};
+ // Version that also serializes the min offset and table offset.
+ static void Build(const std::vector<uint32_t>& offsets, std::vector<uint8_t>* out_data);
+
// Returned offsets are all relative to out_min_offset.
static void Build(const std::vector<uint32_t>& offsets,
std::vector<uint8_t>* out_data,
diff --git a/libdexfile/dex/compact_offset_table_test.cc b/libdexfile/dex/compact_offset_table_test.cc
index 7eb01569f5..724978dc9e 100644
--- a/libdexfile/dex/compact_offset_table_test.cc
+++ b/libdexfile/dex/compact_offset_table_test.cc
@@ -74,6 +74,16 @@ TEST(CompactOffsetTableTest, TestBuildAndAccess) {
<< " table size " << data.size()
<< " sorted table size " << sorted_data.size();
}
+
+ // Test constructor and accessor that serialize/read offsets.
+ {
+ std::vector<uint8_t> data2;
+ CompactOffsetTable::Build(offsets, /*out*/ &data2);
+ CompactOffsetTable::Accessor accessor2(&data2[0]);
+ for (size_t i = 0; i < offsets.size(); ++i) {
+ EXPECT_EQ(offsets[i], accessor2.GetOffset(i));
+ }
+ }
}
} // namespace art
diff --git a/runtime/base/unix_file/fd_file.cc b/runtime/base/unix_file/fd_file.cc
index 37f239da23..f9da178de8 100644
--- a/runtime/base/unix_file/fd_file.cc
+++ b/runtime/base/unix_file/fd_file.cc
@@ -459,12 +459,13 @@ int FdFile::Compare(FdFile* other) {
static const size_t kBufferSize = 4096;
std::unique_ptr<uint8_t[]> buffer1(new uint8_t[kBufferSize]);
std::unique_ptr<uint8_t[]> buffer2(new uint8_t[kBufferSize]);
+ size_t offset = 0;
while (length > 0) {
size_t len = std::min(kBufferSize, static_cast<size_t>(length));
- if (!ReadFully(&buffer1[0], len)) {
+ if (!PreadFully(&buffer1[0], len, offset)) {
return -1;
}
- if (!other->ReadFully(&buffer2[0], len)) {
+ if (!other->PreadFully(&buffer2[0], len, offset)) {
return 1;
}
int result = memcmp(&buffer1[0], &buffer2[0], len);
@@ -472,6 +473,7 @@ int FdFile::Compare(FdFile* other) {
return result;
}
length -= len;
+ offset += len;
}
return 0;
}
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 9c0cceeb4b..ad2e7a77dd 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -7958,17 +7958,18 @@ ArtMethod* ClassLinker::FindResolvedMethod(ObjPtr<mirror::Class> klass,
// In case of jmvti, the dex file gets verified before being registered, so first
// check if it's registered before checking class tables.
const DexFile& dex_file = *dex_cache->GetDexFile();
- CHECK(!IsDexFileRegistered(Thread::Current(), dex_file) ||
- FindClassTable(Thread::Current(), dex_cache) == ClassTableForClassLoader(class_loader))
+ DCHECK(!IsDexFileRegistered(Thread::Current(), dex_file) ||
+ FindClassTable(Thread::Current(), dex_cache) == ClassTableForClassLoader(class_loader))
<< "DexFile referrer: " << dex_file.GetLocation()
<< " ClassLoader: " << DescribeLoaders(class_loader, "");
// Be a good citizen and update the dex cache to speed subsequent calls.
dex_cache->SetResolvedMethod(method_idx, resolved, image_pointer_size_);
- const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx);
- DCHECK(LookupResolvedType(method_id.class_idx_, dex_cache, class_loader) != nullptr)
- << "Method: " << resolved->PrettyMethod() << ", "
- << "Class: " << klass->PrettyClass() << " (" << klass->GetStatus() << "), "
- << "DexFile referrer: " << dex_file.GetLocation();
+ // Disable the following invariant check as the verifier breaks it. b/73760543
+ // const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx);
+ // DCHECK(LookupResolvedType(method_id.class_idx_, dex_cache, class_loader) != nullptr)
+ // << "Method: " << resolved->PrettyMethod() << ", "
+ // << "Class: " << klass->PrettyClass() << " (" << klass->GetStatus() << "), "
+ // << "DexFile referrer: " << dex_file.GetLocation();
}
return resolved;
}
@@ -8000,13 +8001,9 @@ ArtMethod* ClassLinker::ResolveMethod(uint32_t method_idx,
DCHECK(resolved->GetDeclaringClassUnchecked() != nullptr) << resolved->GetDexMethodIndex();
klass = LookupResolvedType(method_id.class_idx_, dex_cache.Get(), class_loader.Get());
if (UNLIKELY(klass == nullptr)) {
- const char* descriptor = dex_file.StringByTypeIdx(method_id.class_idx_);
- LOG(FATAL) << "Check failed: klass != nullptr Bug: 64759619 Method: "
- << resolved->PrettyMethod() << ";" << resolved
- << "/0x" << std::hex << resolved->GetAccessFlags()
- << " ReferencedClass: " << descriptor
- << " DexFile referrer: " << dex_file.GetLocation()
- << " ClassLoader: " << DescribeLoaders(class_loader.Get(), descriptor);
+ // We normaly should not end up here. However the verifier currently doesn't guarantee
+ // the invariant of having the klass in the class table. b/73760543
+ klass = ResolveType(method_id.class_idx_, dex_cache, class_loader);
}
} else {
// The method was not in the DexCache, resolve the declaring class.
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index b2b4d545cb..85b0dbb43c 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -26,6 +26,8 @@
#include "arch/instruction_set.h"
#include "base/mutex.h"
+#include "base/unix_file/fd_file.h"
+#include "dex/art_dex_file_loader.h"
#include "dex/compact_dex_level.h"
#include "globals.h"
// TODO: Add inl file and avoid including inl.
@@ -119,6 +121,32 @@ class CommonRuntimeTestImpl {
// A helper to set up a small heap (4M) to make FillHeap faster.
static void SetUpRuntimeOptionsForFillHeap(RuntimeOptions *options);
+ template <typename Mutator>
+ bool MutateDexFile(File* output_dex, const std::string& input_jar, const Mutator& mutator) {
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+ std::string error_msg;
+ const ArtDexFileLoader dex_file_loader;
+ CHECK(dex_file_loader.Open(input_jar.c_str(),
+ input_jar.c_str(),
+ /*verify*/ true,
+ /*verify_checksum*/ true,
+ &error_msg,
+ &dex_files)) << error_msg;
+ EXPECT_EQ(dex_files.size(), 1u) << "Only one input dex is supported";
+ const std::unique_ptr<const DexFile>& dex = dex_files[0];
+ CHECK(dex->EnableWrite()) << "Failed to enable write";
+ DexFile* dex_file = const_cast<DexFile*>(dex.get());
+ mutator(dex_file);
+ const_cast<DexFile::Header&>(dex_file->GetHeader()).checksum_ = dex_file->CalculateChecksum();
+ if (!output_dex->WriteFully(dex->Begin(), dex->Size())) {
+ return false;
+ }
+ if (output_dex->Flush() != 0) {
+ PLOG(FATAL) << "Could not flush the output file.";
+ }
+ return true;
+ }
+
protected:
// Allow subclases such as CommonCompilerTest to add extra options.
virtual void SetUpRuntimeOptions(RuntimeOptions* options ATTRIBUTE_UNUSED) {}
diff --git a/runtime/intern_table.cc b/runtime/intern_table.cc
index 4b964f648b..2db8815fdd 100644
--- a/runtime/intern_table.cc
+++ b/runtime/intern_table.cc
@@ -371,7 +371,7 @@ size_t InternTable::Table::AddTableFromMemory(const uint8_t* ptr) {
return read_count;
}
// TODO: Disable this for app images if app images have intern tables.
- static constexpr bool kCheckDuplicates = true;
+ static constexpr bool kCheckDuplicates = kIsDebugBuild;
if (kCheckDuplicates) {
for (GcRoot<mirror::String>& string : set) {
CHECK(Find(string.Read()) == nullptr) << "Already found " << string.Read()->ToModifiedUtf8();
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index 1baa613bb5..6d99ad0046 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -718,7 +718,9 @@ void Jit::MethodEntered(Thread* thread, ArtMethod* method) {
Runtime* runtime = Runtime::Current();
if (UNLIKELY(runtime->UseJitCompilation() && runtime->GetJit()->JitAtFirstUse())) {
// The compiler requires a ProfilingInfo object.
- ProfilingInfo::Create(thread, method, /* retry_allocation */ true);
+ ProfilingInfo::Create(thread,
+ method->GetInterfaceMethodIfProxy(kRuntimePointerSize),
+ /* retry_allocation */ true);
JitCompileTask compile_task(method, JitCompileTask::kCompile);
compile_task.Run(thread);
return;
diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc
index 6da34bcc60..fc9426650e 100644
--- a/runtime/native/dalvik_system_VMDebug.cc
+++ b/runtime/native/dalvik_system_VMDebug.cc
@@ -442,7 +442,7 @@ enum class VMDebugRuntimeStatId {
kNumRuntimeStats,
};
-static jobject VMDebug_getRuntimeStatInternal(JNIEnv* env, jclass, jint statId) {
+static jstring VMDebug_getRuntimeStatInternal(JNIEnv* env, jclass, jint statId) {
gc::Heap* heap = Runtime::Current()->GetHeap();
switch (static_cast<VMDebugRuntimeStatId>(statId)) {
case VMDebugRuntimeStatId::kArtGcGcCount: {
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index 38a4a3bfb0..25d50376de 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -533,7 +533,7 @@ static jobjectArray Class_getDeclaredConstructorsInternal(
}
static jobject Class_getDeclaredMethodInternal(JNIEnv* env, jobject javaThis,
- jobject name, jobjectArray args) {
+ jstring name, jobjectArray args) {
ScopedFastNativeObjectAccess soa(env);
DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize);
DCHECK(!Runtime::Current()->IsActiveTransaction());
diff --git a/runtime/native/java_lang_String.cc b/runtime/native/java_lang_String.cc
index 9295ff7071..b5aea7ca7c 100644
--- a/runtime/native/java_lang_String.cc
+++ b/runtime/native/java_lang_String.cc
@@ -37,7 +37,7 @@ static jchar String_charAt(JNIEnv* env, jobject java_this, jint index) {
return soa.Decode<mirror::String>(java_this)->CharAt(index);
}
-static jint String_compareTo(JNIEnv* env, jobject java_this, jobject java_rhs) {
+static jint String_compareTo(JNIEnv* env, jobject java_this, jstring java_rhs) {
ScopedFastNativeObjectAccess soa(env);
if (UNLIKELY(java_rhs == nullptr)) {
ThrowNullPointerException("rhs == null");
@@ -48,7 +48,7 @@ static jint String_compareTo(JNIEnv* env, jobject java_this, jobject java_rhs) {
}
}
-static jstring String_concat(JNIEnv* env, jobject java_this, jobject java_string_arg) {
+static jstring String_concat(JNIEnv* env, jobject java_this, jstring java_string_arg) {
ScopedFastNativeObjectAccess soa(env);
if (UNLIKELY(java_string_arg == nullptr)) {
ThrowNullPointerException("string arg == null");
diff --git a/runtime/native/java_lang_System.cc b/runtime/native/java_lang_System.cc
index 553cbeaabc..390f026588 100644
--- a/runtime/native/java_lang_System.cc
+++ b/runtime/native/java_lang_System.cc
@@ -182,50 +182,55 @@ inline void System_arraycopyTUnchecked(JNIEnv* env, jobject javaSrc, jint srcPos
AsPrimitiveArray<T>(dstArray)->Memmove(dstPos, AsPrimitiveArray<T>(srcArray), srcPos, count);
}
-static void System_arraycopyCharUnchecked(JNIEnv* env, jclass, jobject javaSrc, jint srcPos,
- jobject javaDst, jint dstPos, jint count) {
+static void System_arraycopyCharUnchecked(JNIEnv* env, jclass, jcharArray javaSrc, jint srcPos,
+ jcharArray javaDst, jint dstPos, jint count) {
System_arraycopyTUnchecked<mirror::CharArray, Primitive::kPrimChar>(env, javaSrc, srcPos,
javaDst, dstPos, count);
}
-static void System_arraycopyByteUnchecked(JNIEnv* env, jclass, jobject javaSrc, jint srcPos,
- jobject javaDst, jint dstPos, jint count) {
+static void System_arraycopyByteUnchecked(JNIEnv* env, jclass, jbyteArray javaSrc, jint srcPos,
+ jbyteArray javaDst, jint dstPos, jint count) {
System_arraycopyTUnchecked<mirror::ByteArray, Primitive::kPrimByte>(env, javaSrc, srcPos,
javaDst, dstPos, count);
}
-static void System_arraycopyShortUnchecked(JNIEnv* env, jclass, jobject javaSrc, jint srcPos,
- jobject javaDst, jint dstPos, jint count) {
+static void System_arraycopyShortUnchecked(JNIEnv* env, jclass, jshortArray javaSrc, jint srcPos,
+ jshortArray javaDst, jint dstPos, jint count) {
System_arraycopyTUnchecked<mirror::ShortArray, Primitive::kPrimShort>(env, javaSrc, srcPos,
javaDst, dstPos, count);
}
-static void System_arraycopyIntUnchecked(JNIEnv* env, jclass, jobject javaSrc, jint srcPos,
- jobject javaDst, jint dstPos, jint count) {
+static void System_arraycopyIntUnchecked(JNIEnv* env, jclass, jintArray javaSrc, jint srcPos,
+ jintArray javaDst, jint dstPos, jint count) {
System_arraycopyTUnchecked<mirror::IntArray, Primitive::kPrimInt>(env, javaSrc, srcPos,
javaDst, dstPos, count);
}
-static void System_arraycopyLongUnchecked(JNIEnv* env, jclass, jobject javaSrc, jint srcPos,
- jobject javaDst, jint dstPos, jint count) {
+static void System_arraycopyLongUnchecked(JNIEnv* env, jclass, jlongArray javaSrc, jint srcPos,
+ jlongArray javaDst, jint dstPos, jint count) {
System_arraycopyTUnchecked<mirror::LongArray, Primitive::kPrimLong>(env, javaSrc, srcPos,
javaDst, dstPos, count);
}
-static void System_arraycopyFloatUnchecked(JNIEnv* env, jclass, jobject javaSrc, jint srcPos,
- jobject javaDst, jint dstPos, jint count) {
+static void System_arraycopyFloatUnchecked(JNIEnv* env, jclass, jfloatArray javaSrc, jint srcPos,
+ jfloatArray javaDst, jint dstPos, jint count) {
System_arraycopyTUnchecked<mirror::FloatArray, Primitive::kPrimFloat>(env, javaSrc, srcPos,
javaDst, dstPos, count);
}
-static void System_arraycopyDoubleUnchecked(JNIEnv* env, jclass, jobject javaSrc, jint srcPos,
- jobject javaDst, jint dstPos, jint count) {
+static void System_arraycopyDoubleUnchecked(JNIEnv* env, jclass, jdoubleArray javaSrc, jint srcPos,
+ jdoubleArray javaDst, jint dstPos, jint count) {
System_arraycopyTUnchecked<mirror::DoubleArray, Primitive::kPrimDouble>(env, javaSrc, srcPos,
javaDst, dstPos, count);
}
-static void System_arraycopyBooleanUnchecked(JNIEnv* env, jclass, jobject javaSrc, jint srcPos,
- jobject javaDst, jint dstPos, jint count) {
+static void System_arraycopyBooleanUnchecked(JNIEnv* env,
+ jclass,
+ jbooleanArray javaSrc,
+ jint srcPos,
+ jbooleanArray javaDst,
+ jint dstPos,
+ jint count) {
System_arraycopyTUnchecked<mirror::BooleanArray, Primitive::kPrimBoolean>(env, javaSrc, srcPos,
javaDst, dstPos, count);
}
diff --git a/runtime/native/java_lang_reflect_Array.cc b/runtime/native/java_lang_reflect_Array.cc
index 12d400895c..d28f74158e 100644
--- a/runtime/native/java_lang_reflect_Array.cc
+++ b/runtime/native/java_lang_reflect_Array.cc
@@ -31,7 +31,7 @@
namespace art {
static jobject Array_createMultiArray(
- JNIEnv* env, jclass, jclass javaElementClass, jobject javaDimArray) {
+ JNIEnv* env, jclass, jclass javaElementClass, jintArray javaDimArray) {
ScopedFastNativeObjectAccess soa(env);
DCHECK(javaElementClass != nullptr);
StackHandleScope<2> hs(soa.Self());
diff --git a/runtime/native/java_lang_reflect_Executable.cc b/runtime/native/java_lang_reflect_Executable.cc
index e37c14b41c..a5e70affa5 100644
--- a/runtime/native/java_lang_reflect_Executable.cc
+++ b/runtime/native/java_lang_reflect_Executable.cc
@@ -249,14 +249,14 @@ static jint Executable_compareMethodParametersInternal(JNIEnv* env,
return 0;
}
-static jobject Executable_getMethodNameInternal(JNIEnv* env, jobject javaMethod) {
+static jstring Executable_getMethodNameInternal(JNIEnv* env, jobject javaMethod) {
ScopedFastNativeObjectAccess soa(env);
ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod);
method = method->GetInterfaceMethodIfProxy(kRuntimePointerSize);
- return soa.AddLocalReference<jobject>(method->GetNameAsString(soa.Self()));
+ return soa.AddLocalReference<jstring>(method->GetNameAsString(soa.Self()));
}
-static jobject Executable_getMethodReturnTypeInternal(JNIEnv* env, jobject javaMethod) {
+static jclass Executable_getMethodReturnTypeInternal(JNIEnv* env, jobject javaMethod) {
ScopedFastNativeObjectAccess soa(env);
ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod);
method = method->GetInterfaceMethodIfProxy(kRuntimePointerSize);
@@ -266,7 +266,7 @@ static jobject Executable_getMethodReturnTypeInternal(JNIEnv* env, jobject javaM
return nullptr;
}
- return soa.AddLocalReference<jobject>(return_type);
+ return soa.AddLocalReference<jclass>(return_type);
}
// TODO: Move this to mirror::Class ? Other mirror types that commonly appear
diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc
index f990c0421d..688ae1977e 100644
--- a/runtime/native/java_lang_reflect_Field.cc
+++ b/runtime/native/java_lang_reflect_Field.cc
@@ -460,10 +460,10 @@ static jlong Field_getArtField(JNIEnv* env, jobject javaField) {
return reinterpret_cast<jlong>(field);
}
-static jobject Field_getNameInternal(JNIEnv* env, jobject javaField) {
+static jstring Field_getNameInternal(JNIEnv* env, jobject javaField) {
ScopedFastNativeObjectAccess soa(env);
ArtField* field = soa.Decode<mirror::Field>(javaField)->GetArtField();
- return soa.AddLocalReference<jobject>(
+ return soa.AddLocalReference<jstring>(
field->GetStringName(soa.Self(), true /* resolve */));
}
diff --git a/runtime/native/java_lang_reflect_Method.cc b/runtime/native/java_lang_reflect_Method.cc
index b604dc0fa1..4355c06acd 100644
--- a/runtime/native/java_lang_reflect_Method.cc
+++ b/runtime/native/java_lang_reflect_Method.cc
@@ -82,7 +82,7 @@ static jobjectArray Method_getExceptionTypes(JNIEnv* env, jobject javaMethod) {
}
static jobject Method_invoke(JNIEnv* env, jobject javaMethod, jobject javaReceiver,
- jobject javaArgs) {
+ jobjectArray javaArgs) {
ScopedFastNativeObjectAccess soa(env);
return InvokeMethod(soa, javaMethod, javaReceiver, javaArgs);
}
diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
index 836637f4f0..fbee7b31a3 100644
--- a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
+++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
@@ -162,7 +162,7 @@ static jbyteArray DdmVmInternal_getThreadStats(JNIEnv* env, jclass) {
return result;
}
-static jint DdmVmInternal_heapInfoNotify(JNIEnv* env, jclass, jint when) {
+static jboolean DdmVmInternal_heapInfoNotify(JNIEnv* env, jclass, jint when) {
ScopedFastNativeObjectAccess soa(env);
return Dbg::DdmHandleHpifChunk(static_cast<Dbg::HpifWhen>(when));
}
diff --git a/runtime/native/sun_misc_Unsafe.cc b/runtime/native/sun_misc_Unsafe.cc
index b2bdeed5cb..1af65a371b 100644
--- a/runtime/native/sun_misc_Unsafe.cc
+++ b/runtime/native/sun_misc_Unsafe.cc
@@ -198,14 +198,14 @@ static void Unsafe_putOrderedObject(JNIEnv* env, jobject, jobject javaObj, jlong
obj->SetFieldObject<false>(MemberOffset(offset), newValue);
}
-static jint Unsafe_getArrayBaseOffsetForComponentType(JNIEnv* env, jclass, jobject component_class) {
+static jint Unsafe_getArrayBaseOffsetForComponentType(JNIEnv* env, jclass, jclass component_class) {
ScopedFastNativeObjectAccess soa(env);
ObjPtr<mirror::Class> component = soa.Decode<mirror::Class>(component_class);
Primitive::Type primitive_type = component->GetPrimitiveType();
return mirror::Array::DataOffset(Primitive::ComponentSize(primitive_type)).Int32Value();
}
-static jint Unsafe_getArrayIndexScaleForComponentType(JNIEnv* env, jclass, jobject component_class) {
+static jint Unsafe_getArrayIndexScaleForComponentType(JNIEnv* env, jclass, jclass component_class) {
ScopedFastNativeObjectAccess soa(env);
ObjPtr<mirror::Class> component = soa.Decode<mirror::Class>(component_class);
Primitive::Type primitive_type = component->GetPrimitiveType();
diff --git a/runtime/quicken_info.h b/runtime/quicken_info.h
index 32f70054ba..f20aa0cef5 100644
--- a/runtime/quicken_info.h
+++ b/runtime/quicken_info.h
@@ -18,66 +18,12 @@
#define ART_RUNTIME_QUICKEN_INFO_H_
#include "base/array_ref.h"
+#include "dex/compact_offset_table.h"
#include "dex/dex_instruction.h"
#include "leb128.h"
namespace art {
-// Table for getting the offset of quicken info. Doesn't have one slot for each index, so a
-// combination of iteration and indexing is required to get the quicken info for a given dex method
-// index.
-class QuickenInfoOffsetTableAccessor {
- public:
- using TableType = uint32_t;
- static constexpr uint32_t kElementsPerIndex = 16;
-
- class Builder {
- public:
- explicit Builder(std::vector<uint8_t>* out_data) : out_data_(out_data) {}
-
- void AddOffset(uint32_t index) {
- out_data_->insert(out_data_->end(),
- reinterpret_cast<const uint8_t*>(&index),
- reinterpret_cast<const uint8_t*>(&index + 1));
- }
-
- private:
- std::vector<uint8_t>* const out_data_;
- };
-
- // The table only covers every kElementsPerIndex indices.
- static bool IsCoveredIndex(uint32_t index) {
- return index % kElementsPerIndex == 0;
- }
-
- QuickenInfoOffsetTableAccessor(const ArrayRef<const uint8_t>& data, uint32_t max_index)
- : table_(ArrayRef<const TableType>::Cast(data).SubArray(
- 0,
- RoundUp(max_index, kElementsPerIndex) / kElementsPerIndex)) {}
-
- size_t SizeInBytes() const {
- return NumIndices() * sizeof(table_[0]);
- }
-
- uint32_t NumIndices() const {
- return table_.size();
- }
-
- // Returns the offset for the index at or before the desired index. If the offset is for an index
- // before the desired one, remainder is how many elements to traverse to reach the desired index.
- TableType ElementOffset(uint32_t index, uint32_t* remainder) const {
- *remainder = index % kElementsPerIndex;
- return table_[index / kElementsPerIndex];
- }
-
- static uint32_t Alignment() {
- return alignof(TableType);
- }
-
- private:
- const ArrayRef<const TableType> table_;
-};
-
// QuickenInfoTable is a table of 16 bit dex indices. There is one slot for every instruction that
// is possibly dequickenable.
class QuickenInfoTable {
diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc
index 443c35f979..34b9fcc8a4 100644
--- a/runtime/vdex_file.cc
+++ b/runtime/vdex_file.cc
@@ -222,41 +222,27 @@ uint32_t VdexFile::GetQuickeningInfoTableOffset(const uint8_t* source_dex_begin)
return reinterpret_cast<const QuickeningTableOffsetType*>(source_dex_begin)[-1];
}
-QuickenInfoOffsetTableAccessor VdexFile::GetQuickenInfoOffsetTable(
+CompactOffsetTable::Accessor VdexFile::GetQuickenInfoOffsetTable(
const uint8_t* source_dex_begin,
- uint32_t num_method_ids,
const ArrayRef<const uint8_t>& quickening_info) const {
// The offset a is in preheader right before the dex file.
const uint32_t offset = GetQuickeningInfoTableOffset(source_dex_begin);
- return QuickenInfoOffsetTableAccessor(quickening_info.SubArray(offset), num_method_ids);
+ return CompactOffsetTable::Accessor(quickening_info.SubArray(offset).data());
}
-QuickenInfoOffsetTableAccessor VdexFile::GetQuickenInfoOffsetTable(
+CompactOffsetTable::Accessor VdexFile::GetQuickenInfoOffsetTable(
const DexFile& dex_file,
const ArrayRef<const uint8_t>& quickening_info) const {
- return GetQuickenInfoOffsetTable(dex_file.Begin(), dex_file.NumMethodIds(), quickening_info);
+ return GetQuickenInfoOffsetTable(dex_file.Begin(), quickening_info);
}
static ArrayRef<const uint8_t> GetQuickeningInfoAt(const ArrayRef<const uint8_t>& quickening_info,
uint32_t quickening_offset) {
- ArrayRef<const uint8_t> remaining = quickening_info.SubArray(quickening_offset);
+ // Subtract offset of one since 0 represents unused and cannot be in the table.
+ ArrayRef<const uint8_t> remaining = quickening_info.SubArray(quickening_offset - 1);
return remaining.SubArray(0u, QuickenInfoTable::SizeInBytes(remaining));
}
-static uint32_t GetQuickeningInfoOffset(const QuickenInfoOffsetTableAccessor& table,
- uint32_t dex_method_index,
- const ArrayRef<const uint8_t>& quickening_info) {
- DCHECK(!quickening_info.empty());
- uint32_t remainder;
- uint32_t offset = table.ElementOffset(dex_method_index, &remainder);
- // Decode the sizes for the remainder offsets (not covered by the table).
- while (remainder != 0) {
- offset += GetQuickeningInfoAt(quickening_info, offset).size();
- --remainder;
- }
- return offset;
-}
-
void VdexFile::UnquickenDexFile(const DexFile& target_dex_file,
const DexFile& source_dex_file,
bool decompile_return_instruction) const {
@@ -267,13 +253,15 @@ void VdexFile::UnquickenDexFile(const DexFile& target_dex_file,
const uint8_t* source_dex_begin,
bool decompile_return_instruction) const {
ArrayRef<const uint8_t> quickening_info = GetQuickeningInfo();
- if (quickening_info.size() == 0 && !decompile_return_instruction) {
- // Bail early if there is no quickening info and no need to decompile
- // RETURN_VOID_NO_BARRIER instructions to RETURN_VOID instructions.
+ if (quickening_info.empty()) {
+ // Bail early if there is no quickening info and no need to decompile. This means there is also
+ // no RETURN_VOID to decompile since the empty table takes a non zero amount of space.
return;
}
// Make sure to not unquicken the same code item multiple times.
std::unordered_set<const DexFile::CodeItem*> unquickened_code_item;
+ CompactOffsetTable::Accessor accessor(GetQuickenInfoOffsetTable(source_dex_begin,
+ quickening_info));
for (uint32_t i = 0; i < target_dex_file.NumClassDefs(); ++i) {
const DexFile::ClassDef& class_def = target_dex_file.GetClassDef(i);
const uint8_t* class_data = target_dex_file.GetClassData(class_def);
@@ -284,21 +272,16 @@ void VdexFile::UnquickenDexFile(const DexFile& target_dex_file,
if (class_it.IsAtMethod()) {
const DexFile::CodeItem* code_item = class_it.GetMethodCodeItem();
if (code_item != nullptr && unquickened_code_item.emplace(code_item).second) {
- ArrayRef<const uint8_t> quicken_data;
- if (!quickening_info.empty()) {
- const uint32_t quickening_offset = GetQuickeningInfoOffset(
- GetQuickenInfoOffsetTable(source_dex_begin,
- target_dex_file.NumMethodIds(),
- quickening_info),
- class_it.GetMemberIndex(),
- quickening_info);
- quicken_data = GetQuickeningInfoAt(quickening_info, quickening_offset);
+ const uint32_t offset = accessor.GetOffset(class_it.GetMemberIndex());
+ // Offset being 0 means not quickened.
+ if (offset != 0u) {
+ ArrayRef<const uint8_t> quicken_data = GetQuickeningInfoAt(quickening_info, offset);
+ optimizer::ArtDecompileDEX(
+ target_dex_file,
+ *code_item,
+ quicken_data,
+ decompile_return_instruction);
}
- optimizer::ArtDecompileDEX(
- target_dex_file,
- *code_item,
- quicken_data,
- decompile_return_instruction);
}
}
DexFile::UnHideAccessFlags(class_it);
@@ -313,10 +296,11 @@ ArrayRef<const uint8_t> VdexFile::GetQuickenedInfoOf(const DexFile& dex_file,
if (quickening_info.empty()) {
return ArrayRef<const uint8_t>();
}
- const uint32_t quickening_offset = GetQuickeningInfoOffset(
- GetQuickenInfoOffsetTable(dex_file, quickening_info),
- dex_method_idx,
- quickening_info);
+ const uint32_t quickening_offset =
+ GetQuickenInfoOffsetTable(dex_file, quickening_info).GetOffset(dex_method_idx);
+ if (quickening_offset == 0u) {
+ return ArrayRef<const uint8_t>();
+ }
return GetQuickeningInfoAt(quickening_info, quickening_offset);
}
diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h
index 0f347952c9..d27f431cdc 100644
--- a/runtime/vdex_file.h
+++ b/runtime/vdex_file.h
@@ -22,6 +22,7 @@
#include "base/array_ref.h"
#include "base/macros.h"
+#include "dex/compact_offset_table.h"
#include "mem_map.h"
#include "os.h"
#include "quicken_info.h"
@@ -87,8 +88,8 @@ class VdexFile {
private:
static constexpr uint8_t kVdexMagic[] = { 'v', 'd', 'e', 'x' };
- // Last update: Fix separate section for compact dex data.
- static constexpr uint8_t kVdexVersion[] = { '0', '1', '7', '\0' };
+ // Last update: Change quickening info table format.
+ static constexpr uint8_t kVdexVersion[] = { '0', '1', '8', '\0' };
uint8_t magic_[4];
uint8_t version_[4];
@@ -238,13 +239,12 @@ class VdexFile {
const uint8_t* source_dex_begin,
bool decompile_return_instruction) const;
- QuickenInfoOffsetTableAccessor GetQuickenInfoOffsetTable(
+ CompactOffsetTable::Accessor GetQuickenInfoOffsetTable(
const DexFile& dex_file,
const ArrayRef<const uint8_t>& quickening_info) const;
- QuickenInfoOffsetTableAccessor GetQuickenInfoOffsetTable(
+ CompactOffsetTable::Accessor GetQuickenInfoOffsetTable(
const uint8_t* source_dex_begin,
- uint32_t num_method_ids,
const ArrayRef<const uint8_t>& quickening_info) const;
bool ContainsDexFile(const DexFile& dex_file) const;
diff --git a/test/676-proxy-jit-at-first-use/expected.txt b/test/676-proxy-jit-at-first-use/expected.txt
new file mode 100644
index 0000000000..6915b2f261
--- /dev/null
+++ b/test/676-proxy-jit-at-first-use/expected.txt
@@ -0,0 +1 @@
+Method: public abstract void Interface.foo()
diff --git a/test/676-proxy-jit-at-first-use/info.txt b/test/676-proxy-jit-at-first-use/info.txt
new file mode 100644
index 0000000000..90b683b46d
--- /dev/null
+++ b/test/676-proxy-jit-at-first-use/info.txt
@@ -0,0 +1 @@
+Regression test for "jit at first use" (-Xjitthreshold:0) crash for proxy methods. b/73718713
diff --git a/test/676-proxy-jit-at-first-use/run b/test/676-proxy-jit-at-first-use/run
new file mode 100644
index 0000000000..16c9f7607a
--- /dev/null
+++ b/test/676-proxy-jit-at-first-use/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2018 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.
+
+# Enable "jit at first use" (-Xjitthreshold:0).
+# Ensure this test is not subject to unexpected code collection.
+${RUN} "${@}" --runtime-option -Xjitthreshold:0 --runtime-option -Xjitinitialsize:32M
diff --git a/test/676-proxy-jit-at-first-use/src/Main.java b/test/676-proxy-jit-at-first-use/src/Main.java
new file mode 100644
index 0000000000..4ed773f666
--- /dev/null
+++ b/test/676-proxy-jit-at-first-use/src/Main.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ Interface i = (Interface) Proxy.newProxyInstance(Main.class.getClassLoader(),
+ new Class<?>[] { Interface.class },
+ new Handler());
+ i.foo();
+ }
+}
+
+interface Interface {
+ void foo();
+}
+
+class Handler implements InvocationHandler {
+ public Object invoke(Object proxy, Method method, Object[] args) {
+ System.out.println("Method: " + method);
+ return null;
+ }
+}
diff --git a/test/676-resolve-field-type/expected.txt b/test/676-resolve-field-type/expected.txt
new file mode 100644
index 0000000000..a965a70ed4
--- /dev/null
+++ b/test/676-resolve-field-type/expected.txt
@@ -0,0 +1 @@
+Done
diff --git a/test/676-resolve-field-type/info.txt b/test/676-resolve-field-type/info.txt
new file mode 100644
index 0000000000..a53244df23
--- /dev/null
+++ b/test/676-resolve-field-type/info.txt
@@ -0,0 +1,2 @@
+Test trying to reproduce class loader issues with the verifier.
+See comments in src-ex/ChildClass.java
diff --git a/test/676-resolve-field-type/src-art/Foo.java b/test/676-resolve-field-type/src-art/Foo.java
new file mode 100644
index 0000000000..3df74d3bc1
--- /dev/null
+++ b/test/676-resolve-field-type/src-art/Foo.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Foo {
+ public static Main mainObject;
+}
diff --git a/test/676-resolve-field-type/src-art/Main.java b/test/676-resolve-field-type/src-art/Main.java
new file mode 100644
index 0000000000..c915df876f
--- /dev/null
+++ b/test/676-resolve-field-type/src-art/Main.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2018 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 dalvik.system.PathClassLoader;
+import java.io.File;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ ClassLoader parentLoader = Main.class.getClassLoader();
+ ClassLoader childLoader = new PathClassLoader(DEX_CHILD, parentLoader);
+ Class.forName("ChildClass", true, childLoader).getDeclaredMethod("runTest").invoke(null);
+ }
+
+ private static final String DEX_CHILD =
+ new File(System.getenv("DEX_LOCATION"), "676-resolve-field-type-ex.jar").getAbsolutePath();
+
+ public static void staticMethod() {}
+}
diff --git a/test/676-resolve-field-type/src-ex/ChildClass.java b/test/676-resolve-field-type/src-ex/ChildClass.java
new file mode 100644
index 0000000000..167d4a689d
--- /dev/null
+++ b/test/676-resolve-field-type/src-ex/ChildClass.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+public class ChildClass {
+ // This method being synchronized means the SIGQUIT code in ART will call
+ // FindLocksAtDexPc (we check for the presence of try blocks),
+ // which triggered a DCHECK of an invariant.
+ public static synchronized void runTest() throws Exception {
+ Main m = Foo.mainObject;
+
+ SigQuit.doKill();
+
+ // Sleep some time to get the kill while executing this method.
+ Thread.sleep(2);
+
+ // The FindLocksAtDexPc method running with the verifier would fail when
+ // resolving this call, as the verifier didn't register Main from the field
+ // access above with the current class loader.
+ Main.staticMethod();
+ System.out.println("Done");
+ }
+
+ private final static class SigQuit {
+ private final static int sigquit;
+ private final static Method kill;
+ private final static int pid;
+
+ static {
+ int pidTemp = -1;
+ int sigquitTemp = -1;
+ Method killTemp = null;
+
+ try {
+ Class<?> osClass = Class.forName("android.system.Os");
+ Method getpid = osClass.getDeclaredMethod("getpid");
+ pidTemp = (Integer)getpid.invoke(null);
+
+ Class<?> osConstants = Class.forName("android.system.OsConstants");
+ Field sigquitField = osConstants.getDeclaredField("SIGQUIT");
+ sigquitTemp = (Integer)sigquitField.get(null);
+
+ killTemp = osClass.getDeclaredMethod("kill", int.class, int.class);
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+
+ pid = pidTemp;
+ sigquit = sigquitTemp;
+ kill = killTemp;
+ }
+
+ public static void doKill() throws Exception {
+ kill.invoke(null, pid, sigquit);
+ }
+ }
+}
diff --git a/test/knownfailures.json b/test/knownfailures.json
index ddf9098c64..fe1e31e759 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -122,6 +122,11 @@
"non-deterministic. Same for 913."]
},
{
+ "tests": ["1946-list-descriptors"],
+ "variant": "gcverify | trace",
+ "description": "This test is rather slow and gcverify or trace often cause it to timeout."
+ },
+ {
"tests": ["961-default-iface-resolution-gen",
"964-default-iface-init-gen",
"968-default-partial-compile-gen"],
diff --git a/tools/Android.bp b/tools/Android.bp
index 5093d7a45b..a7ed9bc0b0 100644
--- a/tools/Android.bp
+++ b/tools/Android.bp
@@ -19,4 +19,12 @@ python_binary_host {
srcs: [
"generate_operator_out.py",
],
+ version: {
+ py2: {
+ enabled: true,
+ },
+ py3: {
+ enabled: false,
+ },
+ },
}