summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--compiler/image_writer.cc3
-rw-r--r--compiler/oat_writer.cc84
-rw-r--r--compiler/oat_writer.h1
-rw-r--r--dex2oat/dex2oat.cc16
-rw-r--r--runtime/jit/profile_saver.cc6
-rw-r--r--runtime/mirror/class-inl.h2
-rw-r--r--runtime/native/dalvik_system_DexFile.cc25
-rw-r--r--runtime/oat_file_assistant.cc4
-rw-r--r--runtime/oat_file_assistant.h4
-rw-r--r--runtime/oat_file_manager.cc30
-rw-r--r--test/601-method-access/expected.txt1
-rw-r--r--test/601-method-access/info.txt1
-rw-r--r--test/601-method-access/smali/SubClassUsingInaccessibleMethod.smali33
-rw-r--r--test/601-method-access/src/Main.java38
-rw-r--r--test/601-method-access/src/other/ProtectedClass.java24
-rw-r--r--test/601-method-access/src/other/PublicClass.java21
16 files changed, 238 insertions, 55 deletions
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index be720ad2f3..eaeacc59dd 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -437,6 +437,9 @@ void ImageWriter::PrepareDexCacheArraySlots() {
continue;
}
const DexFile* dex_file = dex_cache->GetDexFile();
+ CHECK(dex_file_oat_index_map_.find(dex_file) != dex_file_oat_index_map_.end())
+ << "Dex cache should have been pruned " << dex_file->GetLocation()
+ << "; possibly in class path";
DexCacheArraysLayout layout(target_ptr_size_, dex_file);
DCHECK(layout.Valid());
size_t oat_index = GetOatIndexForDexCache(dex_cache);
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 41d30fd539..a02c0247b7 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -63,6 +63,29 @@ const UnalignedDexFileHeader* AsUnalignedDexFileHeader(const uint8_t* raw_data)
return reinterpret_cast<const UnalignedDexFileHeader*>(raw_data);
}
+class ChecksumUpdatingOutputStream : public OutputStream {
+ public:
+ ChecksumUpdatingOutputStream(OutputStream* out, OatHeader* oat_header)
+ : OutputStream(out->GetLocation()), out_(out), oat_header_(oat_header) { }
+
+ bool WriteFully(const void* buffer, size_t byte_count) OVERRIDE {
+ oat_header_->UpdateChecksum(buffer, byte_count);
+ return out_->WriteFully(buffer, byte_count);
+ }
+
+ off_t Seek(off_t offset, Whence whence) OVERRIDE {
+ return out_->Seek(offset, whence);
+ }
+
+ bool Flush() OVERRIDE {
+ return out_->Flush();
+ }
+
+ private:
+ OutputStream* const out_;
+ OatHeader* const oat_header_;
+};
+
} // anonymous namespace
// Defines the location of the raw dex file to write.
@@ -422,13 +445,21 @@ bool OatWriter::WriteAndOpenDexFiles(
for (OatDexFile& oat_dex_file : oat_dex_files_) {
oat_dex_file.ReserveClassOffsets(this);
}
- if (!WriteOatDexFiles(rodata) ||
+ ChecksumUpdatingOutputStream checksum_updating_rodata(rodata, oat_header_.get());
+ if (!WriteOatDexFiles(&checksum_updating_rodata) ||
!ExtendForTypeLookupTables(rodata, file, size_after_type_lookup_tables) ||
!OpenDexFiles(file, verify, &dex_files_map, &dex_files) ||
!WriteTypeLookupTables(dex_files_map.get(), dex_files)) {
return false;
}
+ // Do a bulk checksum update for Dex[] and TypeLookupTable[]. Doing it piece by
+ // piece would be difficult because we're not using the OutpuStream directly.
+ if (!oat_dex_files_.empty()) {
+ size_t size = size_after_type_lookup_tables - oat_dex_files_[0].dex_file_offset_;
+ oat_header_->UpdateChecksum(dex_files_map->Begin(), size);
+ }
+
*opened_dex_files_map = std::move(dex_files_map);
*opened_dex_files = std::move(dex_files);
write_state_ = WriteState::kPrepareLayout;
@@ -996,7 +1027,7 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor {
<< PrettyMethod(it.GetMemberIndex(), *dex_file_);
const OatQuickMethodHeader& method_header =
oat_class->method_headers_[method_offsets_index_];
- if (!writer_->WriteData(out, &method_header, sizeof(method_header))) {
+ if (!out->WriteFully(&method_header, sizeof(method_header))) {
ReportWriteFailure("method header", it);
return false;
}
@@ -1063,7 +1094,7 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor {
}
}
- if (!writer_->WriteData(out, quick_code.data(), code_size)) {
+ if (!out->WriteFully(quick_code.data(), code_size)) {
ReportWriteFailure("method code", it);
return false;
}
@@ -1273,7 +1304,7 @@ class OatWriter::WriteMapMethodVisitor : public OatDexMethodVisitor {
size_t map_size = map.size() * sizeof(map[0]);
if (map_offset == offset_) {
// Write deduplicated map (code info for Optimizing or transformation info for dex2dex).
- if (UNLIKELY(!writer_->WriteData(out, map.data(), map_size))) {
+ if (UNLIKELY(!out->WriteFully(map.data(), map_size))) {
ReportWriteFailure(it);
return false;
}
@@ -1451,6 +1482,10 @@ size_t OatWriter::InitOatCodeDexFiles(size_t offset) {
bool OatWriter::WriteRodata(OutputStream* out) {
CHECK(write_state_ == WriteState::kWriteRoData);
+ // Wrap out to update checksum with each write.
+ ChecksumUpdatingOutputStream checksum_updating_out(out, oat_header_.get());
+ out = &checksum_updating_out;
+
if (!WriteClassOffsets(out)) {
LOG(ERROR) << "Failed to write class offsets to " << out->GetLocation();
return false;
@@ -1493,6 +1528,10 @@ bool OatWriter::WriteRodata(OutputStream* out) {
bool OatWriter::WriteCode(OutputStream* out) {
CHECK(write_state_ == WriteState::kWriteText);
+ // Wrap out to update checksum with each write.
+ ChecksumUpdatingOutputStream checksum_updating_out(out, oat_header_.get());
+ out = &checksum_updating_out;
+
SetMultiOatRelativePatcherAdjustment();
const size_t file_offset = oat_data_offset_;
@@ -1677,7 +1716,7 @@ size_t OatWriter::WriteCode(OutputStream* out, const size_t file_offset, size_t
uint32_t alignment_padding = aligned_offset - relative_offset; \
out->Seek(alignment_padding, kSeekCurrent); \
size_trampoline_alignment_ += alignment_padding; \
- if (!WriteData(out, field->data(), field->size())) { \
+ if (!out->WriteFully(field->data(), field->size())) { \
PLOG(ERROR) << "Failed to write " # field " to " << out->GetLocation(); \
return false; \
} \
@@ -2007,7 +2046,7 @@ bool OatWriter::WriteDexFile(OutputStream* rodata,
DCHECK(ValidateDexFileHeader(dex_file, oat_dex_file->GetLocation()));
const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(dex_file);
- if (!WriteData(rodata, dex_file, header->file_size_)) {
+ if (!rodata->WriteFully(dex_file, header->file_size_)) {
PLOG(ERROR) << "Failed to write dex file " << oat_dex_file->GetLocation()
<< " to " << rodata->GetLocation();
return false;
@@ -2187,18 +2226,13 @@ bool OatWriter::WriteCodeAlignment(OutputStream* out, uint32_t aligned_code_delt
0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u
};
DCHECK_LE(aligned_code_delta, sizeof(kPadding));
- if (UNLIKELY(!WriteData(out, kPadding, aligned_code_delta))) {
+ if (UNLIKELY(!out->WriteFully(kPadding, aligned_code_delta))) {
return false;
}
size_code_alignment_ += aligned_code_delta;
return true;
}
-bool OatWriter::WriteData(OutputStream* out, const void* data, size_t size) {
- oat_header_->UpdateChecksum(data, size);
- return out->WriteFully(data, size);
-}
-
void OatWriter::SetMultiOatRelativePatcherAdjustment() {
DCHECK(dex_files_ != nullptr);
DCHECK(relative_patcher_ != nullptr);
@@ -2268,39 +2302,37 @@ bool OatWriter::OatDexFile::Write(OatWriter* oat_writer, OutputStream* out) cons
const size_t file_offset = oat_writer->oat_data_offset_;
DCHECK_OFFSET_();
- if (!oat_writer->WriteData(out, &dex_file_location_size_, sizeof(dex_file_location_size_))) {
+ if (!out->WriteFully(&dex_file_location_size_, sizeof(dex_file_location_size_))) {
PLOG(ERROR) << "Failed to write dex file location length to " << out->GetLocation();
return false;
}
oat_writer->size_oat_dex_file_location_size_ += sizeof(dex_file_location_size_);
- if (!oat_writer->WriteData(out, dex_file_location_data_, dex_file_location_size_)) {
+ if (!out->WriteFully(dex_file_location_data_, dex_file_location_size_)) {
PLOG(ERROR) << "Failed to write dex file location data to " << out->GetLocation();
return false;
}
oat_writer->size_oat_dex_file_location_data_ += dex_file_location_size_;
- if (!oat_writer->WriteData(out,
- &dex_file_location_checksum_,
- sizeof(dex_file_location_checksum_))) {
+ if (!out->WriteFully(&dex_file_location_checksum_, sizeof(dex_file_location_checksum_))) {
PLOG(ERROR) << "Failed to write dex file location checksum to " << out->GetLocation();
return false;
}
oat_writer->size_oat_dex_file_location_checksum_ += sizeof(dex_file_location_checksum_);
- if (!oat_writer->WriteData(out, &dex_file_offset_, sizeof(dex_file_offset_))) {
+ if (!out->WriteFully(&dex_file_offset_, sizeof(dex_file_offset_))) {
PLOG(ERROR) << "Failed to write dex file offset to " << out->GetLocation();
return false;
}
oat_writer->size_oat_dex_file_offset_ += sizeof(dex_file_offset_);
- if (!oat_writer->WriteData(out, &class_offsets_offset_, sizeof(class_offsets_offset_))) {
+ if (!out->WriteFully(&class_offsets_offset_, sizeof(class_offsets_offset_))) {
PLOG(ERROR) << "Failed to write class offsets offset to " << out->GetLocation();
return false;
}
oat_writer->size_oat_dex_file_class_offsets_offset_ += sizeof(class_offsets_offset_);
- if (!oat_writer->WriteData(out, &lookup_table_offset_, sizeof(lookup_table_offset_))) {
+ if (!out->WriteFully(&lookup_table_offset_, sizeof(lookup_table_offset_))) {
PLOG(ERROR) << "Failed to write lookup table offset to " << out->GetLocation();
return false;
}
@@ -2310,7 +2342,7 @@ bool OatWriter::OatDexFile::Write(OatWriter* oat_writer, OutputStream* out) cons
}
bool OatWriter::OatDexFile::WriteClassOffsets(OatWriter* oat_writer, OutputStream* out) {
- if (!oat_writer->WriteData(out, class_offsets_.data(), GetClassOffsetsRawSize())) {
+ if (!out->WriteFully(class_offsets_.data(), GetClassOffsetsRawSize())) {
PLOG(ERROR) << "Failed to write oat class offsets for " << GetLocation()
<< " to " << out->GetLocation();
return false;
@@ -2399,13 +2431,13 @@ bool OatWriter::OatClass::Write(OatWriter* oat_writer,
OutputStream* out,
const size_t file_offset) const {
DCHECK_OFFSET_();
- if (!oat_writer->WriteData(out, &status_, sizeof(status_))) {
+ if (!out->WriteFully(&status_, sizeof(status_))) {
PLOG(ERROR) << "Failed to write class status to " << out->GetLocation();
return false;
}
oat_writer->size_oat_class_status_ += sizeof(status_);
- if (!oat_writer->WriteData(out, &type_, sizeof(type_))) {
+ if (!out->WriteFully(&type_, sizeof(type_))) {
PLOG(ERROR) << "Failed to write oat class type to " << out->GetLocation();
return false;
}
@@ -2413,20 +2445,20 @@ bool OatWriter::OatClass::Write(OatWriter* oat_writer,
if (method_bitmap_size_ != 0) {
CHECK_EQ(kOatClassSomeCompiled, type_);
- if (!oat_writer->WriteData(out, &method_bitmap_size_, sizeof(method_bitmap_size_))) {
+ if (!out->WriteFully(&method_bitmap_size_, sizeof(method_bitmap_size_))) {
PLOG(ERROR) << "Failed to write method bitmap size to " << out->GetLocation();
return false;
}
oat_writer->size_oat_class_method_bitmaps_ += sizeof(method_bitmap_size_);
- if (!oat_writer->WriteData(out, method_bitmap_->GetRawStorage(), method_bitmap_size_)) {
+ if (!out->WriteFully(method_bitmap_->GetRawStorage(), method_bitmap_size_)) {
PLOG(ERROR) << "Failed to write method bitmap to " << out->GetLocation();
return false;
}
oat_writer->size_oat_class_method_bitmaps_ += method_bitmap_size_;
}
- if (!oat_writer->WriteData(out, method_offsets_.data(), GetMethodOffsetsRawSize())) {
+ if (!out->WriteFully(method_offsets_.data(), GetMethodOffsetsRawSize())) {
PLOG(ERROR) << "Failed to write method offsets to " << out->GetLocation();
return false;
}
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index 3862798329..cc81f39f36 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -271,7 +271,6 @@ class OatWriter {
bool WriteTypeLookupTables(MemMap* opened_dex_files_map,
const std::vector<std::unique_ptr<const DexFile>>& opened_dex_files);
bool WriteCodeAlignment(OutputStream* out, uint32_t aligned_code_delta);
- bool WriteData(OutputStream* out, const void* data, size_t size);
void SetMultiOatRelativePatcherAdjustment();
enum class WriteState {
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 9f6f4530c7..cce83f32b5 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -1269,6 +1269,21 @@ class Dex2Oat FINAL {
CHECK(runtime != nullptr);
std::set<DexCacheResolvedClasses> resolved_classes(
profile_compilation_info_->GetResolvedClasses());
+
+ // Filter out class path classes since we don't want to include these in the image.
+ std::unordered_set<std::string> dex_files_locations;
+ for (const DexFile* dex_file : dex_files_) {
+ dex_files_locations.insert(dex_file->GetLocation());
+ }
+ for (auto it = resolved_classes.begin(); it != resolved_classes.end(); ) {
+ if (dex_files_locations.find(it->GetDexLocation()) == dex_files_locations.end()) {
+ VLOG(compiler) << "Removed profile samples for non-app dex file " << it->GetDexLocation();
+ it = resolved_classes.erase(it);
+ } else {
+ ++it;
+ }
+ }
+
image_classes_.reset(new std::unordered_set<std::string>(
runtime->GetClassLinker()->GetClassDescriptorsForProfileKeys(resolved_classes)));
VLOG(compiler) << "Loaded " << image_classes_->size()
@@ -2443,6 +2458,7 @@ class Dex2Oat FINAL {
bool multi_image_;
bool is_host_;
std::string android_root_;
+ // Dex files we are compiling, does not include the class path dex files.
std::vector<const DexFile*> dex_files_;
std::string no_inline_from_string_;
std::vector<jobject> dex_caches_;
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index c5d3ccd748..9822f6e851 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -120,7 +120,7 @@ void ProfileSaver::Run() {
{
MutexLock mu(self, wait_lock_);
period_condition_.Wait(self);
- sleep_time = NanoTime() - last_time_ns_saver_woke_up_;
+ sleep_time = NanoTime() - sleep_start;
}
// Check if the thread was woken up for shutdown.
if (ShuttingDown(self)) {
@@ -130,11 +130,11 @@ void ProfileSaver::Run() {
// We might have been woken up by a huge number of notifications to guarantee saving.
// If we didn't meet the minimum saving period go back to sleep (only if missed by
// a reasonable margin).
- while (kMinSavePeriodNs - sleep_time > (kMinSavePeriodNs / 10)) {
+ while (kMinSavePeriodNs * 0.9 > sleep_time) {
{
MutexLock mu(self, wait_lock_);
period_condition_.TimedWait(self, NsToMs(kMinSavePeriodNs - sleep_time), 0);
- sleep_time = NanoTime() - last_time_ns_saver_woke_up_;
+ sleep_time = NanoTime() - sleep_start;
}
// Check if the thread was woken up for shutdown.
if (ShuttingDown(self)) {
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index dfb728f00e..fcdfc88495 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -419,8 +419,6 @@ inline bool Class::ResolvedMethodAccessTest(Class* access_to, ArtMethod* method,
}
return false;
}
- DCHECK_EQ(this->CanAccessMember(access_to, method->GetAccessFlags()),
- this->CanAccessMember(dex_access_to, method->GetAccessFlags()));
}
if (LIKELY(this->CanAccessMember(access_to, method->GetAccessFlags()))) {
return true;
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 0126b4d0a4..f30f7a641e 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -475,15 +475,22 @@ static jint DexFile_getDexOptNeeded(JNIEnv* env,
// public API
static jboolean DexFile_isDexOptNeeded(JNIEnv* env, jclass, jstring javaFilename) {
- const char* instruction_set = GetInstructionSetString(kRuntimeISA);
- ScopedUtfChars filename(env, javaFilename);
- jint status = GetDexOptNeeded(
- env,
- filename.c_str(),
- instruction_set,
- "speed-profile",
- /*profile_changed*/false);
- return (status != OatFileAssistant::kNoDexOptNeeded) ? JNI_TRUE : JNI_FALSE;
+ ScopedUtfChars filename_utf(env, javaFilename);
+ if (env->ExceptionCheck()) {
+ return JNI_FALSE;
+ }
+
+ const char* filename = filename_utf.c_str();
+ if ((filename == nullptr) || !OS::FileExists(filename)) {
+ LOG(ERROR) << "DexFile_isDexOptNeeded file '" << filename << "' does not exist";
+ ScopedLocalRef<jclass> fnfe(env, env->FindClass("java/io/FileNotFoundException"));
+ const char* message = (filename == nullptr) ? "<empty file name>" : filename;
+ env->ThrowNew(fnfe.get(), message);
+ return JNI_FALSE;
+ }
+
+ OatFileAssistant oat_file_assistant(filename, kRuntimeISA, false, false);
+ return oat_file_assistant.IsUpToDate() ? JNI_FALSE : JNI_TRUE;
}
static jboolean DexFile_isValidCompilerFilter(JNIEnv* env,
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 28fd9b98cd..c998e20ea9 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -198,6 +198,10 @@ OatFileAssistant::DexOptNeeded OatFileAssistant::GetDexOptNeeded(CompilerFilter:
return HasOriginalDexFiles() ? kDex2OatNeeded : kNoDexOptNeeded;
}
+bool OatFileAssistant::IsUpToDate() {
+ return OatFileIsUpToDate() || OdexFileIsUpToDate();
+}
+
OatFileAssistant::ResultOfAttemptToUpdate
OatFileAssistant::MakeUpToDate(CompilerFilter::Filter target, std::string* error_msg) {
switch (GetDexOptNeeded(target)) {
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index 34925cdae6..63d5022cca 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -149,6 +149,10 @@ class OatFileAssistant {
// given compiler filter.
DexOptNeeded GetDexOptNeeded(CompilerFilter::Filter target_compiler_filter);
+ // Returns true if there is up-to-date code for this dex location,
+ // irrespective of the compiler filter of the up-to-date code.
+ bool IsUpToDate();
+
// Return code used when attempting to generate updated code.
enum ResultOfAttemptToUpdate {
kUpdateFailed, // We tried making the code up to date, but
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index 8bebf801c6..aff479f15e 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -588,22 +588,24 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat(
const OatFile* source_oat_file = nullptr;
- // Update the oat file on disk if we can. This may fail, but that's okay.
- // Best effort is all that matters here.
- switch (oat_file_assistant.MakeUpToDate(filter_, /*out*/ &error_msg)) {
- case OatFileAssistant::kUpdateFailed:
- LOG(WARNING) << error_msg;
- break;
+ if (!oat_file_assistant.IsUpToDate()) {
+ // Update the oat file on disk if we can. This may fail, but that's okay.
+ // Best effort is all that matters here.
+ switch (oat_file_assistant.MakeUpToDate(filter_, /*out*/ &error_msg)) {
+ case OatFileAssistant::kUpdateFailed:
+ LOG(WARNING) << error_msg;
+ break;
- case OatFileAssistant::kUpdateNotAttempted:
- // Avoid spamming the logs if we decided not to attempt making the oat
- // file up to date.
- VLOG(oat) << error_msg;
- break;
+ case OatFileAssistant::kUpdateNotAttempted:
+ // Avoid spamming the logs if we decided not to attempt making the oat
+ // file up to date.
+ VLOG(oat) << error_msg;
+ break;
- case OatFileAssistant::kUpdateSucceeded:
- // Nothing to do.
- break;
+ case OatFileAssistant::kUpdateSucceeded:
+ // Nothing to do.
+ break;
+ }
}
// Get the oat file on disk.
diff --git a/test/601-method-access/expected.txt b/test/601-method-access/expected.txt
new file mode 100644
index 0000000000..90fbab87af
--- /dev/null
+++ b/test/601-method-access/expected.txt
@@ -0,0 +1 @@
+Got expected failure
diff --git a/test/601-method-access/info.txt b/test/601-method-access/info.txt
new file mode 100644
index 0000000000..e38a336179
--- /dev/null
+++ b/test/601-method-access/info.txt
@@ -0,0 +1 @@
+Regression test for method access checks.
diff --git a/test/601-method-access/smali/SubClassUsingInaccessibleMethod.smali b/test/601-method-access/smali/SubClassUsingInaccessibleMethod.smali
new file mode 100644
index 0000000000..7a896a2b8d
--- /dev/null
+++ b/test/601-method-access/smali/SubClassUsingInaccessibleMethod.smali
@@ -0,0 +1,33 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LSubClassUsingInaccessibleMethod;
+
+.super Lother/PublicClass;
+
+.method public constructor <init>()V
+ .registers 1
+ invoke-direct {p0}, Lother/PublicClass;-><init>()V
+ return-void
+.end method
+
+# Regression test for compiler DCHECK() failure (bogus check) when referencing
+# a package-private method from an indirectly inherited package-private class,
+# using this very class as the declaring class in the MethodId, bug: 28771056.
+.method public test()I
+ .registers 2
+ invoke-virtual {p0}, LSubClassUsingInaccessibleMethod;->otherProtectedClassPackageIntInstanceMethod()I
+ move-result v0
+ return v0
+.end method
diff --git a/test/601-method-access/src/Main.java b/test/601-method-access/src/Main.java
new file mode 100644
index 0000000000..838080a506
--- /dev/null
+++ b/test/601-method-access/src/Main.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.InvocationTargetException;
+
+/*
+ * Test method access through reflection.
+ */
+public class Main {
+ public static void main(String[] args) {
+ try {
+ Class c = Class.forName("SubClassUsingInaccessibleMethod");
+ Object o = c.newInstance();
+ c.getMethod("test").invoke(o, null);
+ } catch (InvocationTargetException ite) {
+ if (ite.getCause() instanceof IllegalAccessError) {
+ System.out.println("Got expected failure");
+ } else {
+ System.out.println("Got unexpected failure " + ite.getCause());
+ }
+ } catch (Exception e) {
+ System.out.println("Got unexpected failure " + e);
+ }
+ }
+}
diff --git a/test/601-method-access/src/other/ProtectedClass.java b/test/601-method-access/src/other/ProtectedClass.java
new file mode 100644
index 0000000000..9426884c07
--- /dev/null
+++ b/test/601-method-access/src/other/ProtectedClass.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package other;
+
+// Class that cannot be accessed outside of this package.
+class ProtectedClass {
+ /* package */ int otherProtectedClassPackageIntInstanceMethod() {
+ return 28;
+ }
+}
diff --git a/test/601-method-access/src/other/PublicClass.java b/test/601-method-access/src/other/PublicClass.java
new file mode 100644
index 0000000000..d9f79610c5
--- /dev/null
+++ b/test/601-method-access/src/other/PublicClass.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package other;
+
+// Class that makes the ProtectedClass sub-classable by classes outside of package other.
+public class PublicClass extends ProtectedClass {
+}