summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build/Android.gtest.mk2
-rw-r--r--compiler/image_writer.cc15
-rw-r--r--compiler/oat_writer.cc2
-rw-r--r--compiler/optimizing/optimizing_compiler.cc9
-rw-r--r--compiler/verifier_deps_test.cc429
-rw-r--r--oatdump/oatdump.cc23
-rw-r--r--runtime/arch/stub_test.cc52
-rw-r--r--runtime/arch/x86_64/quick_entrypoints_x86_64.S27
-rw-r--r--runtime/asm_support.h3
-rw-r--r--runtime/class_linker.cc6
-rw-r--r--runtime/common_runtime_test.cc25
-rw-r--r--runtime/common_runtime_test.h2
-rw-r--r--runtime/common_throws.cc8
-rw-r--r--runtime/generated/asm_support_gen.h2
-rw-r--r--runtime/intern_table.cc69
-rw-r--r--runtime/intern_table.h49
-rw-r--r--runtime/intern_table_test.cc20
-rw-r--r--runtime/interpreter/interpreter_common.cc2
-rw-r--r--runtime/mirror/class-inl.h5
-rw-r--r--runtime/mirror/class.h6
-rw-r--r--runtime/mirror/dex_cache-inl.h11
-rw-r--r--runtime/mirror/method_type.cc19
-rw-r--r--runtime/mirror/method_type.h4
-rw-r--r--runtime/mirror/string-inl.h2
-rw-r--r--runtime/mirror/string.h2
-rw-r--r--runtime/oat_file_manager.cc2
-rw-r--r--runtime/openjdkjvmti/Android.bp3
-rw-r--r--runtime/openjdkjvmti/OpenjdkJvmTi.cc11
-rw-r--r--runtime/openjdkjvmti/art_jvmti.h13
-rw-r--r--runtime/openjdkjvmti/ti_class.cc73
-rw-r--r--runtime/openjdkjvmti/ti_class.h50
-rw-r--r--runtime/openjdkjvmti/ti_heap.cc (renamed from runtime/openjdkjvmti/heap.cc)8
-rw-r--r--runtime/openjdkjvmti/ti_heap.h (renamed from runtime/openjdkjvmti/heap.h)8
-rw-r--r--runtime/openjdkjvmti/ti_method.cc56
-rw-r--r--runtime/openjdkjvmti/ti_method.h8
-rw-r--r--runtime/runtime.cc10
-rw-r--r--runtime/runtime.h10
-rw-r--r--runtime/transaction.cc32
-rw-r--r--runtime/transaction.h19
-rw-r--r--runtime/vdex_file.h13
-rw-r--r--runtime/verifier/verifier_deps.cc414
-rw-r--r--runtime/verifier/verifier_deps.h82
-rw-r--r--runtime/well_known_classes.cc6
-rw-r--r--runtime/well_known_classes.h3
-rw-r--r--test/910-methods/expected.txt11
-rw-r--r--test/910-methods/methods.cc32
-rw-r--r--test/910-methods/src/Main.java33
-rwxr-xr-xtest/912-classes/build17
-rw-r--r--test/912-classes/classes.cc82
-rw-r--r--test/912-classes/classes.h30
-rw-r--r--test/912-classes/expected.txt7
-rw-r--r--test/912-classes/info.txt1
-rwxr-xr-xtest/912-classes/run43
-rw-r--r--test/912-classes/src/Main.java61
-rwxr-xr-xtest/913-heaps/build17
-rw-r--r--test/913-heaps/expected.txt2
-rw-r--r--test/913-heaps/heaps.cc53
-rw-r--r--test/913-heaps/heaps.h30
-rw-r--r--test/913-heaps/info.txt1
-rwxr-xr-xtest/913-heaps/run43
-rw-r--r--test/913-heaps/src/Main.java57
-rw-r--r--test/956-methodhandles/expected.txt1
-rw-r--r--test/956-methodhandles/src/Main.java135
-rw-r--r--test/957-methodhandle-transforms/expected.txt51
-rw-r--r--test/957-methodhandle-transforms/src/Main.java313
-rwxr-xr-xtest/958-methodhandle-emulated-stackframe/build25
-rw-r--r--test/958-methodhandle-emulated-stackframe/expected.txt32
-rw-r--r--test/958-methodhandle-emulated-stackframe/info.txt5
-rwxr-xr-xtest/958-methodhandle-emulated-stackframe/run20
-rw-r--r--test/958-methodhandle-emulated-stackframe/src/Main.java175
-rw-r--r--test/Android.bp2
-rw-r--r--test/Android.run-test.mk5
-rw-r--r--test/common/runtime_state.cc9
-rwxr-xr-xtest/etc/run-test-jar6
-rw-r--r--test/ti-agent/common_load.cc4
-rw-r--r--tools/cpp-define-generator/constant_class.def1
-rwxr-xr-xtools/cpp-define-generator/generate-asm-support2
77 files changed, 2502 insertions, 419 deletions
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 4f273e5979..c785befda0 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -105,7 +105,7 @@ ART_GTEST_profile_compilation_info_test_DEX_DEPS := ProfileTestMultiDex
ART_GTEST_stub_test_DEX_DEPS := AllFields
ART_GTEST_transaction_test_DEX_DEPS := Transaction
ART_GTEST_type_lookup_table_test_DEX_DEPS := Lookup
-ART_GTEST_verifier_deps_test_DEX_DEPS := VerifierDeps
+ART_GTEST_verifier_deps_test_DEX_DEPS := VerifierDeps MultiDex
# The elf writer test has dependencies on core.oat.
ART_GTEST_elf_writer_test_HOST_DEPS := $(HOST_CORE_IMAGE_optimizing_no-pic_64) $(HOST_CORE_IMAGE_optimizing_no-pic_32)
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index d1ac139912..59f339a9a2 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -963,21 +963,21 @@ void ImageWriter::DumpImageClasses() {
mirror::String* ImageWriter::FindInternedString(mirror::String* string) {
Thread* const self = Thread::Current();
for (const ImageInfo& image_info : image_infos_) {
- mirror::String* const found = image_info.intern_table_->LookupStrong(self, string);
+ ObjPtr<mirror::String> const found = image_info.intern_table_->LookupStrong(self, string);
DCHECK(image_info.intern_table_->LookupWeak(self, string) == nullptr)
<< string->ToModifiedUtf8();
if (found != nullptr) {
- return found;
+ return found.Ptr();
}
}
if (compile_app_image_) {
Runtime* const runtime = Runtime::Current();
- mirror::String* found = runtime->GetInternTable()->LookupStrong(self, string);
+ ObjPtr<mirror::String> found = runtime->GetInternTable()->LookupStrong(self, string);
// If we found it in the runtime intern table it could either be in the boot image or interned
// during app image compilation. If it was in the boot image return that, otherwise return null
// since it belongs to another image space.
- if (found != nullptr && runtime->GetHeap()->ObjectIsInBootImageSpace(found)) {
- return found;
+ if (found != nullptr && runtime->GetHeap()->ObjectIsInBootImageSpace(found.Ptr())) {
+ return found.Ptr();
}
DCHECK(runtime->GetInternTable()->LookupWeak(self, string) == nullptr)
<< string->ToModifiedUtf8();
@@ -1088,7 +1088,8 @@ mirror::Object* ImageWriter::TryAssignBinSlot(WorkStack& work_stack,
mirror::String* interned = FindInternedString(obj->AsString());
if (interned == nullptr) {
// Not in another image space, insert to our table.
- interned = GetImageInfo(oat_index).intern_table_->InternStrongImageString(obj->AsString());
+ interned =
+ GetImageInfo(oat_index).intern_table_->InternStrongImageString(obj->AsString()).Ptr();
DCHECK_EQ(interned, obj);
}
} else if (obj->IsDexCache()) {
@@ -1448,7 +1449,7 @@ void ImageWriter::CalculateNewObjectOffsets() {
for (size_t i = 0, count = dex_file->NumStringIds(); i < count; ++i) {
uint32_t utf16_length;
const char* utf8_data = dex_file->StringDataAndUtf16LengthByIdx(i, &utf16_length);
- mirror::String* string = intern_table->LookupStrong(self, utf16_length, utf8_data);
+ mirror::String* string = intern_table->LookupStrong(self, utf16_length, utf8_data).Ptr();
TryAssignBinSlot(work_stack, string, oat_index);
}
}
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 6cbca7a6dc..f9173f50a6 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -1759,7 +1759,7 @@ bool OatWriter::WriteVerifierDeps(OutputStream* vdex_out, verifier::VerifierDeps
}
std::vector<uint8_t> buffer;
- verifier_deps->Encode(&buffer);
+ verifier_deps->Encode(*dex_files_, &buffer);
if (!vdex_out->WriteFully(buffer.data(), buffer.size())) {
PLOG(ERROR) << "Failed to write verifier deps."
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 8c769270b1..19fd6f95c3 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -170,6 +170,15 @@ class PassObserver : public ValueObject {
if (visualizer_enabled_) {
MutexLock mu(Thread::Current(), visualizer_dump_mutex_);
*visualizer_output_ << visualizer_oss_.str();
+ // The destructor of `visualizer_output_` is normally
+ // responsible for flushing (and closing) the stream, but it
+ // won't be invoked during fast exits in non-debug mode -- see
+ // art::Dex2Oat::~Dex2Oat, which explicitly abandons some
+ // objects (such as the compiler driver) in non-debug mode, to
+ // avoid the cost of destructing them. Therefore we explicitly
+ // flush the stream here to prevent truncated CFG visualizer
+ // files.
+ visualizer_output_->flush();
}
}
diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc
index 9664e43641..8d2a0e7860 100644
--- a/compiler/verifier_deps_test.cc
+++ b/compiler/verifier_deps_test.cc
@@ -79,17 +79,24 @@ class VerifierDepsTest : public CommonCompilerTest {
callbacks->SetVerifierDeps(verifier_deps_.get());
}
- void LoadDexFile(ScopedObjectAccess* soa) REQUIRES_SHARED(Locks::mutator_lock_) {
- class_loader_ = LoadDex("VerifierDeps");
- std::vector<const DexFile*> dex_files = GetDexFiles(class_loader_);
- CHECK_EQ(dex_files.size(), 1u);
- dex_file_ = dex_files.front();
-
- SetVerifierDeps(dex_files);
-
- ObjPtr<mirror::ClassLoader> loader = soa->Decode<mirror::ClassLoader>(class_loader_);
- class_linker_->RegisterDexFile(*dex_file_, loader.Ptr());
+ void LoadDexFile(ScopedObjectAccess* soa, const char* name1, const char* name2 = nullptr)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ class_loader_ = (name2 == nullptr) ? LoadDex(name1) : LoadMultiDex(name1, name2);
+ dex_files_ = GetDexFiles(class_loader_);
+ primary_dex_file_ = dex_files_.front();
+
+ SetVerifierDeps(dex_files_);
+ StackHandleScope<1> hs(soa->Self());
+ Handle<mirror::ClassLoader> loader =
+ hs.NewHandle(soa->Decode<mirror::ClassLoader>(class_loader_));
+ for (const DexFile* dex_file : dex_files_) {
+ class_linker_->RegisterDexFile(*dex_file, loader.Get());
+ }
+ }
+ void LoadDexFile(ScopedObjectAccess* soa) REQUIRES_SHARED(Locks::mutator_lock_) {
+ LoadDexFile(soa, "VerifierDeps");
+ CHECK_EQ(dex_files_.size(), 1u);
klass_Main_ = FindClassByName("LMain;", soa);
CHECK(klass_Main_ != nullptr);
}
@@ -98,16 +105,16 @@ class VerifierDepsTest : public CommonCompilerTest {
ScopedObjectAccess soa(Thread::Current());
LoadDexFile(&soa);
- StackHandleScope<2> hs(Thread::Current());
+ StackHandleScope<2> hs(soa.Self());
Handle<mirror::ClassLoader> class_loader_handle(
hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader_)));
Handle<mirror::DexCache> dex_cache_handle(hs.NewHandle(klass_Main_->GetDexCache()));
const DexFile::ClassDef* class_def = klass_Main_->GetClassDef();
- const uint8_t* class_data = dex_file_->GetClassData(*class_def);
+ const uint8_t* class_data = primary_dex_file_->GetClassData(*class_def);
CHECK(class_data != nullptr);
- ClassDataItemIterator it(*dex_file_, class_data);
+ ClassDataItemIterator it(*primary_dex_file_, class_data);
while (it.HasNextStaticField() || it.HasNextInstanceField()) {
it.Next();
}
@@ -115,7 +122,7 @@ class VerifierDepsTest : public CommonCompilerTest {
ArtMethod* method = nullptr;
while (it.HasNextDirectMethod()) {
ArtMethod* resolved_method = class_linker_->ResolveMethod<ClassLinker::kNoICCECheckForCache>(
- *dex_file_,
+ *primary_dex_file_,
it.GetMemberIndex(),
dex_cache_handle,
class_loader_handle,
@@ -131,7 +138,7 @@ class VerifierDepsTest : public CommonCompilerTest {
CHECK(method != nullptr);
MethodVerifier verifier(Thread::Current(),
- dex_file_,
+ primary_dex_file_,
dex_cache_handle,
class_loader_handle,
*class_def,
@@ -148,19 +155,16 @@ class VerifierDepsTest : public CommonCompilerTest {
return !verifier.HasFailures();
}
- void VerifyDexFile() {
+ void VerifyDexFile(const char* multidex = nullptr) {
std::string error_msg;
{
ScopedObjectAccess soa(Thread::Current());
- LoadDexFile(&soa);
+ LoadDexFile(&soa, "VerifierDeps", multidex);
}
- SetVerifierDeps({ dex_file_ });
TimingLogger timings("Verify", false, false);
- std::vector<const DexFile*> dex_files;
- dex_files.push_back(dex_file_);
compiler_options_->boot_image_ = false;
compiler_driver_->InitializeThreadPools();
- compiler_driver_->Verify(class_loader_, dex_files, &timings);
+ compiler_driver_->Verify(class_loader_, dex_files_, &timings);
}
bool TestAssignabilityRecording(const std::string& dst,
@@ -173,7 +177,7 @@ class VerifierDepsTest : public CommonCompilerTest {
DCHECK(klass_dst != nullptr);
mirror::Class* klass_src = FindClassByName(src, &soa);
DCHECK(klass_src != nullptr);
- verifier_deps_->AddAssignability(*dex_file_,
+ verifier_deps_->AddAssignability(*primary_dex_file_,
klass_dst,
klass_src,
is_strict,
@@ -182,9 +186,9 @@ class VerifierDepsTest : public CommonCompilerTest {
}
bool HasUnverifiedClass(const std::string& cls) {
- const DexFile::TypeId* type_id = dex_file_->FindTypeId(cls.c_str());
+ const DexFile::TypeId* type_id = primary_dex_file_->FindTypeId(cls.c_str());
DCHECK(type_id != nullptr);
- uint16_t index = dex_file_->GetIndexForTypeId(*type_id);
+ uint16_t index = primary_dex_file_->GetIndexForTypeId(*type_id);
MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_);
for (const auto& dex_dep : verifier_deps_->dex_deps_) {
for (uint16_t entry : dex_dep.second->unverified_classes_) {
@@ -395,8 +399,21 @@ class VerifierDepsTest : public CommonCompilerTest {
has_unverified_classes;
}
+ static std::set<VerifierDeps::MethodResolution>* GetMethods(
+ VerifierDeps::DexFileDeps* deps, MethodResolutionKind resolution_kind) {
+ if (resolution_kind == kDirectMethodResolution) {
+ return &deps->direct_methods_;
+ } else if (resolution_kind == kVirtualMethodResolution) {
+ return &deps->virtual_methods_;
+ } else {
+ DCHECK_EQ(resolution_kind, kInterfaceMethodResolution);
+ return &deps->interface_methods_;
+ }
+ }
+
std::unique_ptr<verifier::VerifierDeps> verifier_deps_;
- const DexFile* dex_file_;
+ std::vector<const DexFile*> dex_files_;
+ const DexFile* primary_dex_file_;
jobject class_loader_;
mirror::Class* klass_Main_;
};
@@ -407,21 +424,21 @@ TEST_F(VerifierDepsTest, StringToId) {
MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_);
- uint32_t id_Main1 = verifier_deps_->GetIdFromString(*dex_file_, "LMain;");
- ASSERT_LT(id_Main1, dex_file_->NumStringIds());
- ASSERT_EQ("LMain;", verifier_deps_->GetStringFromId(*dex_file_, id_Main1));
+ uint32_t id_Main1 = verifier_deps_->GetIdFromString(*primary_dex_file_, "LMain;");
+ ASSERT_LT(id_Main1, primary_dex_file_->NumStringIds());
+ ASSERT_EQ("LMain;", verifier_deps_->GetStringFromId(*primary_dex_file_, id_Main1));
- uint32_t id_Main2 = verifier_deps_->GetIdFromString(*dex_file_, "LMain;");
- ASSERT_LT(id_Main2, dex_file_->NumStringIds());
- ASSERT_EQ("LMain;", verifier_deps_->GetStringFromId(*dex_file_, id_Main2));
+ uint32_t id_Main2 = verifier_deps_->GetIdFromString(*primary_dex_file_, "LMain;");
+ ASSERT_LT(id_Main2, primary_dex_file_->NumStringIds());
+ ASSERT_EQ("LMain;", verifier_deps_->GetStringFromId(*primary_dex_file_, id_Main2));
- uint32_t id_Lorem1 = verifier_deps_->GetIdFromString(*dex_file_, "Lorem ipsum");
- ASSERT_GE(id_Lorem1, dex_file_->NumStringIds());
- ASSERT_EQ("Lorem ipsum", verifier_deps_->GetStringFromId(*dex_file_, id_Lorem1));
+ uint32_t id_Lorem1 = verifier_deps_->GetIdFromString(*primary_dex_file_, "Lorem ipsum");
+ ASSERT_GE(id_Lorem1, primary_dex_file_->NumStringIds());
+ ASSERT_EQ("Lorem ipsum", verifier_deps_->GetStringFromId(*primary_dex_file_, id_Lorem1));
- uint32_t id_Lorem2 = verifier_deps_->GetIdFromString(*dex_file_, "Lorem ipsum");
- ASSERT_GE(id_Lorem2, dex_file_->NumStringIds());
- ASSERT_EQ("Lorem ipsum", verifier_deps_->GetStringFromId(*dex_file_, id_Lorem2));
+ uint32_t id_Lorem2 = verifier_deps_->GetIdFromString(*primary_dex_file_, "Lorem ipsum");
+ ASSERT_GE(id_Lorem2, primary_dex_file_->NumStringIds());
+ ASSERT_EQ("Lorem ipsum", verifier_deps_->GetStringFromId(*primary_dex_file_, id_Lorem2));
ASSERT_EQ(id_Main1, id_Main2);
ASSERT_EQ(id_Lorem1, id_Lorem2);
@@ -1068,13 +1085,41 @@ TEST_F(VerifierDepsTest, EncodeDecode) {
ASSERT_TRUE(HasEachKindOfRecord());
std::vector<uint8_t> buffer;
- verifier_deps_->Encode(&buffer);
+ verifier_deps_->Encode(dex_files_, &buffer);
ASSERT_FALSE(buffer.empty());
- VerifierDeps decoded_deps({ dex_file_ }, ArrayRef<uint8_t>(buffer));
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
ASSERT_TRUE(verifier_deps_->Equals(decoded_deps));
}
+TEST_F(VerifierDepsTest, EncodeDecodeMulti) {
+ VerifyDexFile("MultiDex");
+
+ ASSERT_GT(NumberOfCompiledDexFiles(), 1u);
+ std::vector<uint8_t> buffer;
+ verifier_deps_->Encode(dex_files_, &buffer);
+ ASSERT_FALSE(buffer.empty());
+
+ // Create new DexFile, to mess with std::map order: the verifier deps used
+ // to iterate over the map, which doesn't guarantee insertion order. We fixed
+ // this by passing the expected order when encoding/decoding.
+ std::vector<std::unique_ptr<const DexFile>> first_dex_files = OpenTestDexFiles("VerifierDeps");
+ std::vector<std::unique_ptr<const DexFile>> second_dex_files = OpenTestDexFiles("MultiDex");
+ std::vector<const DexFile*> dex_files;
+ for (auto& dex_file : first_dex_files) {
+ dex_files.push_back(dex_file.get());
+ }
+ for (auto& dex_file : second_dex_files) {
+ dex_files.push_back(dex_file.get());
+ }
+
+ // Dump the new verifier deps to ensure it can properly read the data.
+ VerifierDeps decoded_deps(dex_files, ArrayRef<const uint8_t>(buffer));
+ std::ostringstream stream;
+ VariableIndentationOutputStream os(&stream);
+ decoded_deps.Dump(&os);
+}
+
TEST_F(VerifierDepsTest, UnverifiedClasses) {
VerifyDexFile();
ASSERT_FALSE(HasUnverifiedClass("LMyThread;"));
@@ -1088,5 +1133,311 @@ TEST_F(VerifierDepsTest, UnverifiedClasses) {
ASSERT_TRUE(HasUnverifiedClass("LMyClassWithNoSuperButFailures;"));
}
+// Returns the next resolution kind in the enum.
+static MethodResolutionKind GetNextResolutionKind(MethodResolutionKind resolution_kind) {
+ if (resolution_kind == kDirectMethodResolution) {
+ return kVirtualMethodResolution;
+ } else if (resolution_kind == kVirtualMethodResolution) {
+ return kInterfaceMethodResolution;
+ } else {
+ DCHECK_EQ(resolution_kind, kInterfaceMethodResolution);
+ return kDirectMethodResolution;
+ }
+}
+
+TEST_F(VerifierDepsTest, VerifyDeps) {
+ VerifyDexFile();
+
+ ASSERT_EQ(1u, NumberOfCompiledDexFiles());
+ ASSERT_TRUE(HasEachKindOfRecord());
+
+ // When validating, we create a new class loader, as
+ // the existing `class_loader_` may contain erroneous classes,
+ // that ClassLinker::FindClass won't return.
+
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<1> hs(soa.Self());
+ MutableHandle<mirror::ClassLoader> new_class_loader(hs.NewHandle<mirror::ClassLoader>(nullptr));
+ {
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_TRUE(verifier_deps_->Verify(new_class_loader, soa.Self()));
+ }
+
+ std::vector<uint8_t> buffer;
+ verifier_deps_->Encode(dex_files_, &buffer);
+ ASSERT_FALSE(buffer.empty());
+
+ {
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_TRUE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ }
+
+ // Fiddle with the dependencies to make sure we catch any change and fail to verify.
+
+ {
+ // Mess up with the assignable_types.
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ deps->assignable_types_.insert(*deps->unassignable_types_.begin());
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ }
+
+ {
+ // Mess up with the unassignable_types.
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ deps->unassignable_types_.insert(*deps->assignable_types_.begin());
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ }
+
+ // Mess up with classes.
+ {
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ bool found = false;
+ for (const auto& entry : deps->classes_) {
+ if (entry.IsResolved()) {
+ deps->classes_.insert(VerifierDeps::ClassResolution(
+ entry.GetDexTypeIndex(), VerifierDeps::kUnresolvedMarker));
+ found = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(found);
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ }
+
+ {
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ bool found = false;
+ for (const auto& entry : deps->classes_) {
+ if (!entry.IsResolved()) {
+ deps->classes_.insert(VerifierDeps::ClassResolution(
+ entry.GetDexTypeIndex(), VerifierDeps::kUnresolvedMarker - 1));
+ found = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(found);
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ }
+
+ {
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ bool found = false;
+ for (const auto& entry : deps->classes_) {
+ if (entry.IsResolved()) {
+ deps->classes_.insert(VerifierDeps::ClassResolution(
+ entry.GetDexTypeIndex(), entry.GetAccessFlags() - 1));
+ found = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(found);
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ }
+
+ // Mess up with fields.
+ {
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ bool found = false;
+ for (const auto& entry : deps->fields_) {
+ if (entry.IsResolved()) {
+ deps->fields_.insert(VerifierDeps::FieldResolution(entry.GetDexFieldIndex(),
+ VerifierDeps::kUnresolvedMarker,
+ entry.GetDeclaringClassIndex()));
+ found = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(found);
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ }
+
+ {
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ bool found = false;
+ for (const auto& entry : deps->fields_) {
+ if (!entry.IsResolved()) {
+ deps->fields_.insert(VerifierDeps::FieldResolution(0 /* we know there is a field there */,
+ VerifierDeps::kUnresolvedMarker - 1,
+ 0 /* we know there is a class there */));
+ found = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(found);
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ }
+
+ {
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ bool found = false;
+ for (const auto& entry : deps->fields_) {
+ if (entry.IsResolved()) {
+ deps->fields_.insert(VerifierDeps::FieldResolution(entry.GetDexFieldIndex(),
+ entry.GetAccessFlags() - 1,
+ entry.GetDeclaringClassIndex()));
+ found = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(found);
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ }
+
+ {
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ bool found = false;
+ for (const auto& entry : deps->fields_) {
+ static constexpr uint32_t kNewTypeIndex = 0;
+ if (entry.GetDeclaringClassIndex() != kNewTypeIndex) {
+ deps->fields_.insert(VerifierDeps::FieldResolution(entry.GetDexFieldIndex(),
+ entry.GetAccessFlags(),
+ kNewTypeIndex));
+ found = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(found);
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ }
+
+ // Mess up with methods.
+ for (MethodResolutionKind resolution_kind :
+ { kDirectMethodResolution, kVirtualMethodResolution, kInterfaceMethodResolution }) {
+ {
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ bool found = false;
+ std::set<VerifierDeps::MethodResolution>* methods = GetMethods(deps, resolution_kind);
+ for (const auto& entry : *methods) {
+ if (entry.IsResolved()) {
+ methods->insert(VerifierDeps::MethodResolution(entry.GetDexMethodIndex(),
+ VerifierDeps::kUnresolvedMarker,
+ entry.GetDeclaringClassIndex()));
+ found = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(found);
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ }
+
+ {
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ bool found = false;
+ std::set<VerifierDeps::MethodResolution>* methods = GetMethods(deps, resolution_kind);
+ for (const auto& entry : *methods) {
+ if (!entry.IsResolved()) {
+ methods->insert(VerifierDeps::MethodResolution(0 /* we know there is a method there */,
+ VerifierDeps::kUnresolvedMarker - 1,
+ 0 /* we know there is a class there */));
+ found = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(found);
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ }
+
+ {
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ bool found = false;
+ std::set<VerifierDeps::MethodResolution>* methods = GetMethods(deps, resolution_kind);
+ for (const auto& entry : *methods) {
+ if (entry.IsResolved()) {
+ methods->insert(VerifierDeps::MethodResolution(entry.GetDexMethodIndex(),
+ entry.GetAccessFlags() - 1,
+ entry.GetDeclaringClassIndex()));
+ found = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(found);
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ }
+
+ {
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ bool found = false;
+ std::set<VerifierDeps::MethodResolution>* methods = GetMethods(deps, resolution_kind);
+ for (const auto& entry : *methods) {
+ static constexpr uint32_t kNewTypeIndex = 0;
+ if (entry.IsResolved() && entry.GetDeclaringClassIndex() != kNewTypeIndex) {
+ methods->insert(VerifierDeps::MethodResolution(entry.GetDexMethodIndex(),
+ entry.GetAccessFlags(),
+ kNewTypeIndex));
+ found = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(found);
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ }
+
+ {
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ bool found = false;
+ std::set<VerifierDeps::MethodResolution>* methods = GetMethods(deps, resolution_kind);
+ for (const auto& entry : *methods) {
+ if (entry.IsResolved()) {
+ GetMethods(deps, GetNextResolutionKind(resolution_kind))->insert(
+ VerifierDeps::MethodResolution(entry.GetDexMethodIndex(),
+ entry.GetAccessFlags(),
+ entry.GetDeclaringClassIndex()));
+ found = true;
+ }
+ }
+ ASSERT_TRUE(found);
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ }
+
+ {
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ bool found = false;
+ std::set<VerifierDeps::MethodResolution>* methods = GetMethods(deps, resolution_kind);
+ for (const auto& entry : *methods) {
+ if (entry.IsResolved()) {
+ GetMethods(deps, GetNextResolutionKind(GetNextResolutionKind(resolution_kind)))->insert(
+ VerifierDeps::MethodResolution(entry.GetDexMethodIndex(),
+ entry.GetAccessFlags(),
+ entry.GetDeclaringClassIndex()));
+ found = true;
+ }
+ }
+ ASSERT_TRUE(found);
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ }
+ }
+}
+
} // namespace verifier
} // namespace art
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index da0db01386..3d208b5d28 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -66,6 +66,7 @@
#include "type_lookup_table.h"
#include "vdex_file.h"
#include "verifier/method_verifier.h"
+#include "verifier/verifier_deps.h"
#include "well_known_classes.h"
#include <sys/stat.h>
@@ -483,6 +484,28 @@ class OatDumper {
os << "\n";
if (!options_.dump_header_only_) {
+ VariableIndentationOutputStream vios(&os);
+ VdexFile::Header vdex_header = oat_file_.GetVdexFile()->GetHeader();
+ if (vdex_header.IsValid()) {
+ std::string error_msg;
+ std::vector<const DexFile*> dex_files;
+ for (size_t i = 0; i < oat_dex_files_.size(); i++) {
+ const DexFile* dex_file = OpenDexFile(oat_dex_files_[i], &error_msg);
+ if (dex_file == nullptr) {
+ os << "Error opening dex file: " << error_msg << std::endl;
+ return false;
+ }
+ dex_files.push_back(dex_file);
+ }
+ verifier::VerifierDeps deps(dex_files, oat_file_.GetVdexFile()->GetVerifierDepsData());
+ deps.Dump(&vios);
+ } else {
+ os << "UNRECOGNIZED vdex file, magic "
+ << vdex_header.GetMagic()
+ << ", version "
+ << vdex_header.GetVersion()
+ << "\n";
+ }
for (size_t i = 0; i < oat_dex_files_.size(); i++) {
const OatFile::OatDexFile* oat_dex_file = oat_dex_files_[i];
CHECK(oat_dex_file != nullptr);
diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc
index 4638c3fc55..c151f00289 100644
--- a/runtime/arch/stub_test.cc
+++ b/runtime/arch/stub_test.cc
@@ -819,34 +819,60 @@ TEST_F(StubTest, CheckCast) {
ScopedObjectAccess soa(self);
// garbage is created during ClassLinker::Init
- StackHandleScope<2> hs(soa.Self());
+ StackHandleScope<4> hs(soa.Self());
Handle<mirror::Class> c(
hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/Object;")));
Handle<mirror::Class> c2(
hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;")));
+ Handle<mirror::Class> list(
+ hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[Ljava/util/List;")));
+ Handle<mirror::Class> array_list(
+ hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[Ljava/util/ArrayList;")));
EXPECT_FALSE(self->IsExceptionPending());
- Invoke3(reinterpret_cast<size_t>(c.Get()), reinterpret_cast<size_t>(c.Get()), 0U,
- art_quick_check_cast, self);
-
+ Invoke3(reinterpret_cast<size_t>(c.Get()),
+ reinterpret_cast<size_t>(c.Get()),
+ 0U,
+ art_quick_check_cast,
+ self);
EXPECT_FALSE(self->IsExceptionPending());
- Invoke3(reinterpret_cast<size_t>(c2.Get()), reinterpret_cast<size_t>(c2.Get()), 0U,
- art_quick_check_cast, self);
-
+ Invoke3(reinterpret_cast<size_t>(c2.Get()),
+ reinterpret_cast<size_t>(c2.Get()),
+ 0U,
+ art_quick_check_cast,
+ self);
EXPECT_FALSE(self->IsExceptionPending());
- Invoke3(reinterpret_cast<size_t>(c.Get()), reinterpret_cast<size_t>(c2.Get()), 0U,
- art_quick_check_cast, self);
-
+ Invoke3(reinterpret_cast<size_t>(c.Get()),
+ reinterpret_cast<size_t>(c2.Get()),
+ 0U,
+ art_quick_check_cast,
+ self);
EXPECT_FALSE(self->IsExceptionPending());
- // TODO: Make the following work. But that would require correct managed frames.
+ Invoke3(reinterpret_cast<size_t>(list.Get()),
+ reinterpret_cast<size_t>(array_list.Get()),
+ 0U,
+ art_quick_check_cast,
+ self);
+ EXPECT_FALSE(self->IsExceptionPending());
- Invoke3(reinterpret_cast<size_t>(c2.Get()), reinterpret_cast<size_t>(c.Get()), 0U,
- art_quick_check_cast, self);
+ Invoke3(reinterpret_cast<size_t>(list.Get()),
+ reinterpret_cast<size_t>(c2.Get()),
+ 0U,
+ art_quick_check_cast,
+ self);
+ EXPECT_TRUE(self->IsExceptionPending());
+ self->ClearException();
+ // TODO: Make the following work. But that would require correct managed frames.
+ Invoke3(reinterpret_cast<size_t>(c2.Get()),
+ reinterpret_cast<size_t>(c.Get()),
+ 0U,
+ art_quick_check_cast,
+ self);
EXPECT_TRUE(self->IsExceptionPending());
self->ClearException();
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index afa1c0ff03..2856766fec 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -1481,6 +1481,32 @@ DEFINE_FUNCTION art_quick_unlock_object_no_inline
END_FUNCTION art_quick_unlock_object_no_inline
DEFINE_FUNCTION art_quick_check_cast
+ testl LITERAL(ACCESS_FLAGS_CLASS_IS_INTERFACE), MIRROR_CLASS_ACCESS_FLAGS_OFFSET(%rdi)
+ jz .Lnot_interface
+
+ // There are no read barriers since the iftable is immutable. There can be false negatives for
+ // the read barrier case if classes in the IfTable are in the from-space. In the case where
+ // we do not find a matching interface we call into artIsAssignableFromCode which will have
+ // read barriers.
+ movl MIRROR_CLASS_IF_TABLE_OFFSET(%rsi), %ecx
+ UNPOISON_HEAP_REF ecx
+ testl %ecx, %ecx
+ jz .Lnot_interface
+ movl MIRROR_ARRAY_LENGTH_OFFSET(%rcx), %r8d
+.Lstart_loop:
+ // Re-poison before comparing to prevent rare possible false positives. This is done inside
+ // the loop since heap poisoning is only for testing builds.
+ POISON_HEAP_REF edi
+ cmpl MIRROR_OBJECT_ARRAY_DATA_OFFSET(%rcx), %edi
+ je .Lreturn // Return if same class.
+ UNPOISON_HEAP_REF edi
+ // Go to next interface.
+ add LITERAL(COMPRESSED_REFERENCE_SIZE * 2), %rcx
+ sub LITERAL(2), %r8
+ jnz .Lstart_loop
+
+.Lnot_interface:
+ // We could check the super classes here but that is usually already checked in the caller.
PUSH rdi // Save args for exc
PUSH rsi
subq LITERAL(8), %rsp // Alignment padding.
@@ -1493,6 +1519,7 @@ DEFINE_FUNCTION art_quick_check_cast
addq LITERAL(24), %rsp // pop arguments
CFI_ADJUST_CFA_OFFSET(-24)
+.Lreturn:
ret
CFI_ADJUST_CFA_OFFSET(24 + 4 * 8) // Reset unwind info so following code unwinds.
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index cd8815b25a..1e5e127ef9 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -172,6 +172,9 @@ ADD_TEST_EQ(size_t(MIRROR_OBJECT_HEADER_SIZE), sizeof(art::mirror::Object))
#define MIRROR_CLASS_COMPONENT_TYPE_OFFSET (4 + MIRROR_OBJECT_HEADER_SIZE)
ADD_TEST_EQ(MIRROR_CLASS_COMPONENT_TYPE_OFFSET,
art::mirror::Class::ComponentTypeOffset().Int32Value())
+#define MIRROR_CLASS_IF_TABLE_OFFSET (12 + MIRROR_OBJECT_HEADER_SIZE)
+ADD_TEST_EQ(MIRROR_CLASS_IF_TABLE_OFFSET,
+ art::mirror::Class::IfTableOffset().Int32Value())
#define MIRROR_CLASS_ACCESS_FLAGS_OFFSET (64 + MIRROR_OBJECT_HEADER_SIZE)
ADD_TEST_EQ(MIRROR_CLASS_ACCESS_FLAGS_OFFSET,
art::mirror::Class::AccessFlagsOffset().Int32Value())
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 4823caa2f6..73524af0be 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1126,7 +1126,7 @@ static bool FlattenPathClassLoader(ObjPtr<mirror::ClassLoader> class_loader,
DCHECK(error_msg != nullptr);
ScopedObjectAccessUnchecked soa(Thread::Current());
ArtField* const dex_path_list_field =
- soa.DecodeField(WellKnownClasses::dalvik_system_PathClassLoader_pathList);
+ soa.DecodeField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList);
ArtField* const dex_elements_field =
soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList_dexElements);
CHECK(dex_path_list_field != nullptr);
@@ -2391,7 +2391,7 @@ bool ClassLinker::FindClassInPathClassLoader(ScopedObjectAccessAlreadyRunnable&
ArtField* const dex_file_field =
soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile);
ObjPtr<mirror::Object> dex_path_list =
- soa.DecodeField(WellKnownClasses::dalvik_system_PathClassLoader_pathList)->
+ soa.DecodeField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList)->
GetObject(class_loader.Get());
if (dex_path_list != nullptr && dex_file_field != nullptr && cookie_field != nullptr) {
// DexPathList has an array dexElements of Elements[] which each contain a dex file.
@@ -8165,7 +8165,7 @@ jobject ClassLinker::CreatePathClassLoader(Thread* self,
DCHECK(h_path_class_loader.Get() != nullptr);
// Set DexPathList.
ArtField* path_list_field =
- soa.DecodeField(WellKnownClasses::dalvik_system_PathClassLoader_pathList);
+ soa.DecodeField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList);
DCHECK(path_list_field != nullptr);
path_list_field->SetObject<false>(h_path_class_loader.Get(), h_dex_path_list.Get());
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index 5409fcbf2f..3409938c50 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -524,7 +524,7 @@ std::vector<const DexFile*> CommonRuntimeTestImpl::GetDexFiles(jobject jclass_lo
ArtField* dex_file_field =
soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile);
ObjPtr<mirror::Object> dex_path_list =
- soa.DecodeField(WellKnownClasses::dalvik_system_PathClassLoader_pathList)->
+ soa.DecodeField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList)->
GetObject(class_loader.Get());
if (dex_path_list != nullptr && dex_file_field!= nullptr && cookie_field != nullptr) {
// DexPathList has an array dexElements of Elements[] which each contain a dex file.
@@ -572,6 +572,29 @@ const DexFile* CommonRuntimeTestImpl::GetFirstDexFile(jobject jclass_loader) {
return ret;
}
+jobject CommonRuntimeTestImpl::LoadMultiDex(const char* first_dex_name,
+ const char* second_dex_name) {
+ std::vector<std::unique_ptr<const DexFile>> first_dex_files = OpenTestDexFiles(first_dex_name);
+ std::vector<std::unique_ptr<const DexFile>> second_dex_files = OpenTestDexFiles(second_dex_name);
+ std::vector<const DexFile*> class_path;
+ CHECK_NE(0U, first_dex_files.size());
+ CHECK_NE(0U, second_dex_files.size());
+ for (auto& dex_file : first_dex_files) {
+ class_path.push_back(dex_file.get());
+ loaded_dex_files_.push_back(std::move(dex_file));
+ }
+ for (auto& dex_file : second_dex_files) {
+ class_path.push_back(dex_file.get());
+ loaded_dex_files_.push_back(std::move(dex_file));
+ }
+
+ Thread* self = Thread::Current();
+ jobject class_loader = Runtime::Current()->GetClassLinker()->CreatePathClassLoader(self,
+ class_path);
+ self->SetClassLoaderOverride(class_loader);
+ return class_loader;
+}
+
jobject CommonRuntimeTestImpl::LoadDex(const char* dex_name) {
std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles(dex_name);
std::vector<const DexFile*> class_path;
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index 92934c6246..827e85e9b6 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -133,6 +133,8 @@ class CommonRuntimeTestImpl {
REQUIRES_SHARED(Locks::mutator_lock_);
jobject LoadDex(const char* dex_name) REQUIRES_SHARED(Locks::mutator_lock_);
+ jobject LoadMultiDex(const char* first_dex_name, const char* second_dex_name)
+ REQUIRES_SHARED(Locks::mutator_lock_);
std::string android_data_;
std::string dalvik_cache_;
diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc
index 0251776de9..9f0dbbbdd0 100644
--- a/runtime/common_throws.cc
+++ b/runtime/common_throws.cc
@@ -813,13 +813,11 @@ void ThrowVerifyError(ObjPtr<mirror::Class> referrer, const char* fmt, ...) {
void ThrowWrongMethodTypeException(mirror::MethodType* callee_type,
mirror::MethodType* callsite_type) {
- // TODO(narayan): Should we provide more detail here ? The RI doesn't bother.
- UNUSED(callee_type);
- UNUSED(callsite_type);
-
ThrowException("Ljava/lang/invoke/WrongMethodTypeException;",
nullptr,
- "Invalid method type for signature polymorphic call");
+ StringPrintf("Expected %s but was %s",
+ callee_type->PrettyDescriptor().c_str(),
+ callsite_type->PrettyDescriptor().c_str()).c_str());
}
} // namespace art
diff --git a/runtime/generated/asm_support_gen.h b/runtime/generated/asm_support_gen.h
index 03f5bf6dd4..6c189b0a84 100644
--- a/runtime/generated/asm_support_gen.h
+++ b/runtime/generated/asm_support_gen.h
@@ -52,6 +52,8 @@ DEFINE_CHECK_EQ(static_cast<int32_t>(MIRROR_OBJECT_LOCK_WORD_OFFSET), (static_ca
DEFINE_CHECK_EQ(static_cast<uint32_t>(MIRROR_CLASS_STATUS_INITIALIZED), (static_cast<uint32_t>((art::mirror::Class::kStatusInitialized))))
#define ACCESS_FLAGS_CLASS_IS_FINALIZABLE 0x80000000
DEFINE_CHECK_EQ(static_cast<uint32_t>(ACCESS_FLAGS_CLASS_IS_FINALIZABLE), (static_cast<uint32_t>((art::kAccClassIsFinalizable))))
+#define ACCESS_FLAGS_CLASS_IS_INTERFACE 0x200
+DEFINE_CHECK_EQ(static_cast<uint32_t>(ACCESS_FLAGS_CLASS_IS_INTERFACE), (static_cast<uint32_t>((art::kAccInterface))))
#define ACCESS_FLAGS_CLASS_IS_FINALIZABLE_BIT 0x1f
DEFINE_CHECK_EQ(static_cast<uint32_t>(ACCESS_FLAGS_CLASS_IS_FINALIZABLE_BIT), (static_cast<uint32_t>((art::MostSignificantBit(art::kAccClassIsFinalizable)))))
#define ART_METHOD_DEX_CACHE_METHODS_OFFSET_32 20
diff --git a/runtime/intern_table.cc b/runtime/intern_table.cc
index a61a1878af..7b7510979d 100644
--- a/runtime/intern_table.cc
+++ b/runtime/intern_table.cc
@@ -63,9 +63,9 @@ void InternTable::VisitRoots(RootVisitor* visitor, VisitRootFlags flags) {
strong_interns_.VisitRoots(visitor);
} else if ((flags & kVisitRootFlagNewRoots) != 0) {
for (auto& root : new_strong_intern_roots_) {
- mirror::String* old_ref = root.Read<kWithoutReadBarrier>();
+ ObjPtr<mirror::String> old_ref = root.Read<kWithoutReadBarrier>();
root.VisitRoot(visitor, RootInfo(kRootInternedString));
- mirror::String* new_ref = root.Read<kWithoutReadBarrier>();
+ ObjPtr<mirror::String> new_ref = root.Read<kWithoutReadBarrier>();
if (new_ref != old_ref) {
// The GC moved a root in the log. Need to search the strong interns and update the
// corresponding object. This is slow, but luckily for us, this may only happen with a
@@ -86,17 +86,17 @@ void InternTable::VisitRoots(RootVisitor* visitor, VisitRootFlags flags) {
// Note: we deliberately don't visit the weak_interns_ table and the immutable image roots.
}
-mirror::String* InternTable::LookupWeak(Thread* self, mirror::String* s) {
+ObjPtr<mirror::String> InternTable::LookupWeak(Thread* self, ObjPtr<mirror::String> s) {
MutexLock mu(self, *Locks::intern_table_lock_);
return LookupWeakLocked(s);
}
-mirror::String* InternTable::LookupStrong(Thread* self, mirror::String* s) {
+ObjPtr<mirror::String> InternTable::LookupStrong(Thread* self, ObjPtr<mirror::String> s) {
MutexLock mu(self, *Locks::intern_table_lock_);
return LookupStrongLocked(s);
}
-mirror::String* InternTable::LookupStrong(Thread* self,
+ObjPtr<mirror::String> InternTable::LookupStrong(Thread* self,
uint32_t utf16_length,
const char* utf8_data) {
DCHECK_EQ(utf16_length, CountModifiedUtf8Chars(utf8_data));
@@ -107,11 +107,11 @@ mirror::String* InternTable::LookupStrong(Thread* self,
return strong_interns_.Find(string);
}
-mirror::String* InternTable::LookupWeakLocked(mirror::String* s) {
+ObjPtr<mirror::String> InternTable::LookupWeakLocked(ObjPtr<mirror::String> s) {
return weak_interns_.Find(s);
}
-mirror::String* InternTable::LookupStrongLocked(mirror::String* s) {
+ObjPtr<mirror::String> InternTable::LookupStrongLocked(ObjPtr<mirror::String> s) {
return strong_interns_.Find(s);
}
@@ -121,7 +121,7 @@ void InternTable::AddNewTable() {
strong_interns_.AddNewTable();
}
-mirror::String* InternTable::InsertStrong(mirror::String* s) {
+ObjPtr<mirror::String> InternTable::InsertStrong(ObjPtr<mirror::String> s) {
Runtime* runtime = Runtime::Current();
if (runtime->IsActiveTransaction()) {
runtime->RecordStrongStringInsertion(s);
@@ -133,7 +133,7 @@ mirror::String* InternTable::InsertStrong(mirror::String* s) {
return s;
}
-mirror::String* InternTable::InsertWeak(mirror::String* s) {
+ObjPtr<mirror::String> InternTable::InsertWeak(ObjPtr<mirror::String> s) {
Runtime* runtime = Runtime::Current();
if (runtime->IsActiveTransaction()) {
runtime->RecordWeakStringInsertion(s);
@@ -142,11 +142,11 @@ mirror::String* InternTable::InsertWeak(mirror::String* s) {
return s;
}
-void InternTable::RemoveStrong(mirror::String* s) {
+void InternTable::RemoveStrong(ObjPtr<mirror::String> s) {
strong_interns_.Remove(s);
}
-void InternTable::RemoveWeak(mirror::String* s) {
+void InternTable::RemoveWeak(ObjPtr<mirror::String> s) {
Runtime* runtime = Runtime::Current();
if (runtime->IsActiveTransaction()) {
runtime->RecordWeakStringRemoval(s);
@@ -155,19 +155,22 @@ void InternTable::RemoveWeak(mirror::String* s) {
}
// Insert/remove methods used to undo changes made during an aborted transaction.
-mirror::String* InternTable::InsertStrongFromTransaction(mirror::String* s) {
+ObjPtr<mirror::String> InternTable::InsertStrongFromTransaction(ObjPtr<mirror::String> s) {
DCHECK(!Runtime::Current()->IsActiveTransaction());
return InsertStrong(s);
}
-mirror::String* InternTable::InsertWeakFromTransaction(mirror::String* s) {
+
+ObjPtr<mirror::String> InternTable::InsertWeakFromTransaction(ObjPtr<mirror::String> s) {
DCHECK(!Runtime::Current()->IsActiveTransaction());
return InsertWeak(s);
}
-void InternTable::RemoveStrongFromTransaction(mirror::String* s) {
+
+void InternTable::RemoveStrongFromTransaction(ObjPtr<mirror::String> s) {
DCHECK(!Runtime::Current()->IsActiveTransaction());
RemoveStrong(s);
}
-void InternTable::RemoveWeakFromTransaction(mirror::String* s) {
+
+void InternTable::RemoveWeakFromTransaction(ObjPtr<mirror::String> s) {
DCHECK(!Runtime::Current()->IsActiveTransaction());
RemoveWeak(s);
}
@@ -203,7 +206,9 @@ void InternTable::WaitUntilAccessible(Thread* self) {
Locks::intern_table_lock_->ExclusiveLock(self);
}
-mirror::String* InternTable::Insert(mirror::String* s, bool is_strong, bool holding_locks) {
+ObjPtr<mirror::String> InternTable::Insert(ObjPtr<mirror::String> s,
+ bool is_strong,
+ bool holding_locks) {
if (s == nullptr) {
return nullptr;
}
@@ -222,7 +227,7 @@ mirror::String* InternTable::Insert(mirror::String* s, bool is_strong, bool hold
}
}
// Check the strong table for a match.
- mirror::String* strong = LookupStrongLocked(s);
+ ObjPtr<mirror::String> strong = LookupStrongLocked(s);
if (strong != nullptr) {
return strong;
}
@@ -244,7 +249,7 @@ mirror::String* InternTable::Insert(mirror::String* s, bool is_strong, bool hold
CHECK(self->GetWeakRefAccessEnabled());
}
// There is no match in the strong table, check the weak table.
- mirror::String* weak = LookupWeakLocked(s);
+ ObjPtr<mirror::String> weak = LookupWeakLocked(s);
if (weak != nullptr) {
if (is_strong) {
// A match was found in the weak table. Promote to the strong table.
@@ -257,11 +262,11 @@ mirror::String* InternTable::Insert(mirror::String* s, bool is_strong, bool hold
return is_strong ? InsertStrong(s) : InsertWeak(s);
}
-mirror::String* InternTable::InternStrong(int32_t utf16_length, const char* utf8_data) {
+ObjPtr<mirror::String> InternTable::InternStrong(int32_t utf16_length, const char* utf8_data) {
DCHECK(utf8_data != nullptr);
Thread* self = Thread::Current();
// Try to avoid allocation.
- mirror::String* s = LookupStrong(self, utf16_length, utf8_data);
+ ObjPtr<mirror::String> s = LookupStrong(self, utf16_length, utf8_data);
if (s != nullptr) {
return s;
}
@@ -269,25 +274,25 @@ mirror::String* InternTable::InternStrong(int32_t utf16_length, const char* utf8
self, utf16_length, utf8_data));
}
-mirror::String* InternTable::InternStrong(const char* utf8_data) {
+ObjPtr<mirror::String> InternTable::InternStrong(const char* utf8_data) {
DCHECK(utf8_data != nullptr);
return InternStrong(mirror::String::AllocFromModifiedUtf8(Thread::Current(), utf8_data));
}
-mirror::String* InternTable::InternStrongImageString(mirror::String* s) {
+ObjPtr<mirror::String> InternTable::InternStrongImageString(ObjPtr<mirror::String> s) {
// May be holding the heap bitmap lock.
return Insert(s, true, true);
}
-mirror::String* InternTable::InternStrong(mirror::String* s) {
+ObjPtr<mirror::String> InternTable::InternStrong(ObjPtr<mirror::String> s) {
return Insert(s, true, false);
}
-mirror::String* InternTable::InternWeak(mirror::String* s) {
+ObjPtr<mirror::String> InternTable::InternWeak(ObjPtr<mirror::String> s) {
return Insert(s, false, false);
}
-bool InternTable::ContainsWeak(mirror::String* s) {
+bool InternTable::ContainsWeak(ObjPtr<mirror::String> s) {
return LookupWeak(Thread::Current(), s) == s;
}
@@ -314,7 +319,7 @@ std::size_t InternTable::StringHashEquals::operator()(const GcRoot<mirror::Strin
if (kIsDebugBuild) {
Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
}
- return static_cast<size_t>(root.Read()->GetHashCode());
+ return static_cast<size_t>(root.Read<kWithoutReadBarrier>()->GetHashCode());
}
bool InternTable::StringHashEquals::operator()(const GcRoot<mirror::String>& a,
@@ -322,7 +327,7 @@ bool InternTable::StringHashEquals::operator()(const GcRoot<mirror::String>& a,
if (kIsDebugBuild) {
Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
}
- return a.Read()->Equals(b.Read());
+ return a.Read<kWithoutReadBarrier>()->Equals(b.Read<kWithoutReadBarrier>());
}
bool InternTable::StringHashEquals::operator()(const GcRoot<mirror::String>& a,
@@ -330,7 +335,7 @@ bool InternTable::StringHashEquals::operator()(const GcRoot<mirror::String>& a,
if (kIsDebugBuild) {
Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
}
- mirror::String* a_string = a.Read();
+ ObjPtr<mirror::String> a_string = a.Read<kWithoutReadBarrier>();
uint32_t a_length = static_cast<uint32_t>(a_string->GetLength());
if (a_length != b.GetUtf16Length()) {
return false;
@@ -392,7 +397,7 @@ size_t InternTable::Table::WriteToMemory(uint8_t* ptr) {
return table_to_write->WriteToMemory(ptr);
}
-void InternTable::Table::Remove(mirror::String* s) {
+void InternTable::Table::Remove(ObjPtr<mirror::String> s) {
for (UnorderedSet& table : tables_) {
auto it = table.Find(GcRoot<mirror::String>(s));
if (it != table.end()) {
@@ -403,7 +408,7 @@ void InternTable::Table::Remove(mirror::String* s) {
LOG(FATAL) << "Attempting to remove non-interned string " << s->ToModifiedUtf8();
}
-mirror::String* InternTable::Table::Find(mirror::String* s) {
+ObjPtr<mirror::String> InternTable::Table::Find(ObjPtr<mirror::String> s) {
Locks::intern_table_lock_->AssertHeld(Thread::Current());
for (UnorderedSet& table : tables_) {
auto it = table.Find(GcRoot<mirror::String>(s));
@@ -414,7 +419,7 @@ mirror::String* InternTable::Table::Find(mirror::String* s) {
return nullptr;
}
-mirror::String* InternTable::Table::Find(const Utf8String& string) {
+ObjPtr<mirror::String> InternTable::Table::Find(const Utf8String& string) {
Locks::intern_table_lock_->AssertHeld(Thread::Current());
for (UnorderedSet& table : tables_) {
auto it = table.Find(string);
@@ -429,7 +434,7 @@ void InternTable::Table::AddNewTable() {
tables_.push_back(UnorderedSet());
}
-void InternTable::Table::Insert(mirror::String* s) {
+void InternTable::Table::Insert(ObjPtr<mirror::String> s) {
// Always insert the last table, the image tables are before and we avoid inserting into these
// to prevent dirty pages.
DCHECK(!tables_.empty());
diff --git a/runtime/intern_table.h b/runtime/intern_table.h
index 30ff55d33d..acb20671ab 100644
--- a/runtime/intern_table.h
+++ b/runtime/intern_table.h
@@ -57,43 +57,44 @@ class InternTable {
InternTable();
// Interns a potentially new string in the 'strong' table. May cause thread suspension.
- mirror::String* InternStrong(int32_t utf16_length, const char* utf8_data)
+ ObjPtr<mirror::String> InternStrong(int32_t utf16_length, const char* utf8_data)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
// Only used by image writer. Special version that may not cause thread suspension since the GC
// cannot be running while we are doing image writing. Maybe be called while while holding a
// lock since there will not be thread suspension.
- mirror::String* InternStrongImageString(mirror::String* s)
+ ObjPtr<mirror::String> InternStrongImageString(ObjPtr<mirror::String> s)
REQUIRES_SHARED(Locks::mutator_lock_);
// Interns a potentially new string in the 'strong' table. May cause thread suspension.
- mirror::String* InternStrong(const char* utf8_data) REQUIRES_SHARED(Locks::mutator_lock_)
+ ObjPtr<mirror::String> InternStrong(const char* utf8_data) REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Roles::uninterruptible_);
// Interns a potentially new string in the 'strong' table. May cause thread suspension.
- mirror::String* InternStrong(mirror::String* s) REQUIRES_SHARED(Locks::mutator_lock_)
+ ObjPtr<mirror::String> InternStrong(ObjPtr<mirror::String> s)
+ REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Roles::uninterruptible_);
// Interns a potentially new string in the 'weak' table. May cause thread suspension.
- mirror::String* InternWeak(mirror::String* s) REQUIRES_SHARED(Locks::mutator_lock_)
+ ObjPtr<mirror::String> InternWeak(ObjPtr<mirror::String> s) REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Roles::uninterruptible_);
void SweepInternTableWeaks(IsMarkedVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::intern_table_lock_);
- bool ContainsWeak(mirror::String* s) REQUIRES_SHARED(Locks::mutator_lock_)
+ bool ContainsWeak(ObjPtr<mirror::String> s) REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::intern_table_lock_);
// Lookup a strong intern, returns null if not found.
- mirror::String* LookupStrong(Thread* self, mirror::String* s)
+ ObjPtr<mirror::String> LookupStrong(Thread* self, ObjPtr<mirror::String> s)
REQUIRES(!Locks::intern_table_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
- mirror::String* LookupStrong(Thread* self, uint32_t utf16_length, const char* utf8_data)
+ ObjPtr<mirror::String> LookupStrong(Thread* self, uint32_t utf16_length, const char* utf8_data)
REQUIRES(!Locks::intern_table_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
// Lookup a weak intern, returns null if not found.
- mirror::String* LookupWeak(Thread* self, mirror::String* s)
+ ObjPtr<mirror::String> LookupWeak(Thread* self, ObjPtr<mirror::String> s)
REQUIRES(!Locks::intern_table_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -181,13 +182,13 @@ class InternTable {
class Table {
public:
Table();
- mirror::String* Find(mirror::String* s) REQUIRES_SHARED(Locks::mutator_lock_)
+ ObjPtr<mirror::String> Find(ObjPtr<mirror::String> s) REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(Locks::intern_table_lock_);
- mirror::String* Find(const Utf8String& string) REQUIRES_SHARED(Locks::mutator_lock_)
+ ObjPtr<mirror::String> Find(const Utf8String& string) REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(Locks::intern_table_lock_);
- void Insert(mirror::String* s) REQUIRES_SHARED(Locks::mutator_lock_)
+ void Insert(ObjPtr<mirror::String> s) REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(Locks::intern_table_lock_);
- void Remove(mirror::String* s)
+ void Remove(ObjPtr<mirror::String> s)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::intern_table_lock_);
void VisitRoots(RootVisitor* visitor)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::intern_table_lock_);
@@ -221,30 +222,30 @@ class InternTable {
// Insert if non null, otherwise return null. Must be called holding the mutator lock.
// If holding_locks is true, then we may also hold other locks. If holding_locks is true, then we
// require GC is not running since it is not safe to wait while holding locks.
- mirror::String* Insert(mirror::String* s, bool is_strong, bool holding_locks)
+ ObjPtr<mirror::String> Insert(ObjPtr<mirror::String> s, bool is_strong, bool holding_locks)
REQUIRES(!Locks::intern_table_lock_) REQUIRES_SHARED(Locks::mutator_lock_);
- mirror::String* LookupStrongLocked(mirror::String* s)
+ ObjPtr<mirror::String> LookupStrongLocked(ObjPtr<mirror::String> s)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::intern_table_lock_);
- mirror::String* LookupWeakLocked(mirror::String* s)
+ ObjPtr<mirror::String> LookupWeakLocked(ObjPtr<mirror::String> s)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::intern_table_lock_);
- mirror::String* InsertStrong(mirror::String* s)
+ ObjPtr<mirror::String> InsertStrong(ObjPtr<mirror::String> s)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::intern_table_lock_);
- mirror::String* InsertWeak(mirror::String* s)
+ ObjPtr<mirror::String> InsertWeak(ObjPtr<mirror::String> s)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::intern_table_lock_);
- void RemoveStrong(mirror::String* s)
+ void RemoveStrong(ObjPtr<mirror::String> s)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::intern_table_lock_);
- void RemoveWeak(mirror::String* s)
+ void RemoveWeak(ObjPtr<mirror::String> s)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::intern_table_lock_);
// Transaction rollback access.
- mirror::String* InsertStrongFromTransaction(mirror::String* s)
+ ObjPtr<mirror::String> InsertStrongFromTransaction(ObjPtr<mirror::String> s)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::intern_table_lock_);
- mirror::String* InsertWeakFromTransaction(mirror::String* s)
+ ObjPtr<mirror::String> InsertWeakFromTransaction(ObjPtr<mirror::String> s)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::intern_table_lock_);
- void RemoveStrongFromTransaction(mirror::String* s)
+ void RemoveStrongFromTransaction(ObjPtr<mirror::String> s)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::intern_table_lock_);
- void RemoveWeakFromTransaction(mirror::String* s)
+ void RemoveWeakFromTransaction(ObjPtr<mirror::String> s)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::intern_table_lock_);
size_t AddTableFromMemoryLocked(const uint8_t* ptr)
diff --git a/runtime/intern_table_test.cc b/runtime/intern_table_test.cc
index 74cec57498..b91d946095 100644
--- a/runtime/intern_table_test.cc
+++ b/runtime/intern_table_test.cc
@@ -193,22 +193,22 @@ TEST_F(InternTableTest, LookupStrong) {
ASSERT_NE(foo.Get(), bar.Get());
ASSERT_NE(foo.Get(), foobar.Get());
ASSERT_NE(bar.Get(), foobar.Get());
- mirror::String* lookup_foo = intern_table.LookupStrong(soa.Self(), 3, "foo");
- EXPECT_EQ(lookup_foo, foo.Get());
- mirror::String* lookup_bar = intern_table.LookupStrong(soa.Self(), 3, "bar");
- EXPECT_EQ(lookup_bar, bar.Get());
- mirror::String* lookup_foobar = intern_table.LookupStrong(soa.Self(), 6, "foobar");
- EXPECT_EQ(lookup_foobar, foobar.Get());
- mirror::String* lookup_foox = intern_table.LookupStrong(soa.Self(), 4, "foox");
+ ObjPtr<mirror::String> lookup_foo = intern_table.LookupStrong(soa.Self(), 3, "foo");
+ EXPECT_OBJ_PTR_EQ(lookup_foo, foo.Get());
+ ObjPtr<mirror::String> lookup_bar = intern_table.LookupStrong(soa.Self(), 3, "bar");
+ EXPECT_OBJ_PTR_EQ(lookup_bar, bar.Get());
+ ObjPtr<mirror::String> lookup_foobar = intern_table.LookupStrong(soa.Self(), 6, "foobar");
+ EXPECT_OBJ_PTR_EQ(lookup_foobar, foobar.Get());
+ ObjPtr<mirror::String> lookup_foox = intern_table.LookupStrong(soa.Self(), 4, "foox");
EXPECT_TRUE(lookup_foox == nullptr);
- mirror::String* lookup_fooba = intern_table.LookupStrong(soa.Self(), 5, "fooba");
+ ObjPtr<mirror::String> lookup_fooba = intern_table.LookupStrong(soa.Self(), 5, "fooba");
EXPECT_TRUE(lookup_fooba == nullptr);
- mirror::String* lookup_foobaR = intern_table.LookupStrong(soa.Self(), 6, "foobaR");
+ ObjPtr<mirror::String> lookup_foobaR = intern_table.LookupStrong(soa.Self(), 6, "foobaR");
EXPECT_TRUE(lookup_foobaR == nullptr);
// Try a hash conflict.
ASSERT_EQ(ComputeUtf16HashFromModifiedUtf8("foobar", 6),
ComputeUtf16HashFromModifiedUtf8("foobbS", 6));
- mirror::String* lookup_foobbS = intern_table.LookupStrong(soa.Self(), 6, "foobbS");
+ ObjPtr<mirror::String> lookup_foobbS = intern_table.LookupStrong(soa.Self(), 6, "foobbS");
EXPECT_TRUE(lookup_foobbS == nullptr);
}
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 179e48bab8..4fc92a3aea 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -836,7 +836,7 @@ inline bool DoInvokePolymorphic(Thread* self,
Handle<mirror::MethodType> handle_type(hs.NewHandle(method_handle->GetMethodType()));
CHECK(handle_type.Get() != nullptr);
if (UNLIKELY(is_invoke_exact && !callsite_type->IsExactMatch(handle_type.Get()))) {
- ThrowWrongMethodTypeException(callsite_type.Get(), handle_type.Get());
+ ThrowWrongMethodTypeException(handle_type.Get(), callsite_type.Get());
return false;
}
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 9992a9e29f..bbdb2af16a 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -526,8 +526,7 @@ inline ArtMethod* Class::FindVirtualMethodForVirtualOrInterface(ArtMethod* metho
template<VerifyObjectFlags kVerifyFlags,
ReadBarrierOption kReadBarrierOption>
inline IfTable* Class::GetIfTable() {
- return GetFieldObject<IfTable, kVerifyFlags, kReadBarrierOption>(
- OFFSET_OF_OBJECT_MEMBER(Class, iftable_));
+ return GetFieldObject<IfTable, kVerifyFlags, kReadBarrierOption>(IfTableOffset());
}
inline int32_t Class::GetIfTableCount() {
@@ -539,7 +538,7 @@ inline int32_t Class::GetIfTableCount() {
}
inline void Class::SetIfTable(ObjPtr<IfTable> new_iftable) {
- SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, iftable_), new_iftable);
+ SetFieldObject<false>(IfTableOffset(), new_iftable);
}
inline LengthPrefixedArray<ArtField>* Class::GetIFieldsPtr() {
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 57937950a6..57bb2ed410 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -561,7 +561,7 @@ class MANAGED Class FINAL : public Object {
// The size of java.lang.Class.class.
static uint32_t ClassClassSize(PointerSize pointer_size) {
// The number of vtable entries in java.lang.Class.
- uint32_t vtable_entries = Object::kVTableLength + 72;
+ uint32_t vtable_entries = Object::kVTableLength + 73;
return ComputeClassSize(true, vtable_entries, 0, 0, 4, 1, 0, pointer_size);
}
@@ -680,6 +680,10 @@ class MANAGED Class FINAL : public Object {
return MemberOffset(OFFSETOF_MEMBER(Class, dex_cache_));
}
+ static MemberOffset IfTableOffset() {
+ return MemberOffset(OFFSETOF_MEMBER(Class, iftable_));
+ }
+
enum {
kDumpClassFullDetail = 1,
kDumpClassClassLoader = (1 << 1),
diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h
index df3865b1fe..c7a123b580 100644
--- a/runtime/mirror/dex_cache-inl.h
+++ b/runtime/mirror/dex_cache-inl.h
@@ -174,14 +174,9 @@ inline void VisitDexCachePairs(std::atomic<DexCachePair<T>>* pairs,
// tell the compiler to treat "Read" as a template rather than a field or
// function. Otherwise, on encountering the "<" token, the compiler would
// treat "Read" as a field.
- T* before = source.object.template Read<kReadBarrierOption>();
- // TODO(narayan): This additional GC root construction and assignment
- // is unnecessary. We're already operating on a copy of the DexCachePair
- // that's in the cache.
- GcRoot<T> root(before);
- visitor.VisitRootIfNonNull(root.AddressWithoutBarrier());
- if (root.Read() != before) {
- source.object = GcRoot<T>(root.Read());
+ T* const before = source.object.template Read<kReadBarrierOption>();
+ visitor.VisitRootIfNonNull(source.object.AddressWithoutBarrier());
+ if (source.object.template Read<kReadBarrierOption>() != before) {
pairs[i].store(source, std::memory_order_relaxed);
}
}
diff --git a/runtime/mirror/method_type.cc b/runtime/mirror/method_type.cc
index 0b52931b76..9b0f87242c 100644
--- a/runtime/mirror/method_type.cc
+++ b/runtime/mirror/method_type.cc
@@ -65,6 +65,25 @@ bool MethodType::IsExactMatch(mirror::MethodType* other) REQUIRES_SHARED(Locks::
return true;
}
+std::string MethodType::PrettyDescriptor() REQUIRES_SHARED(Locks::mutator_lock_) {
+ std::ostringstream ss;
+ ss << "(";
+
+ mirror::ObjectArray<Class>* const p_types = GetPTypes();
+ const int32_t params_length = p_types->GetLength();
+ for (int32_t i = 0; i < params_length; ++i) {
+ ss << p_types->GetWithoutChecks(i)->PrettyDescriptor();
+ if (i != (params_length - 1)) {
+ ss << ", ";
+ }
+ }
+
+ ss << ")";
+ ss << GetRType()->PrettyDescriptor();
+
+ return ss.str();
+}
+
void MethodType::SetClass(Class* klass) {
CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass;
CHECK(klass != nullptr);
diff --git a/runtime/mirror/method_type.h b/runtime/mirror/method_type.h
index 5b50409cbc..fa700b68ff 100644
--- a/runtime/mirror/method_type.h
+++ b/runtime/mirror/method_type.h
@@ -56,6 +56,10 @@ class MANAGED MethodType : public Object {
// iff. they have the same return types and parameter types.
bool IsExactMatch(mirror::MethodType* other) REQUIRES_SHARED(Locks::mutator_lock_);
+ // Returns the pretty descriptor for this method type, suitable for display in
+ // exception messages and the like.
+ std::string PrettyDescriptor() REQUIRES_SHARED(Locks::mutator_lock_);
+
private:
static MemberOffset FormOffset() {
return MemberOffset(OFFSETOF_MEMBER(MethodType, form_));
diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h
index d42bb9291b..d94b39fba7 100644
--- a/runtime/mirror/string-inl.h
+++ b/runtime/mirror/string-inl.h
@@ -160,7 +160,7 @@ class SetStringCountAndValueVisitorFromString {
const int32_t offset_;
};
-inline String* String::Intern() {
+inline ObjPtr<String> String::Intern() {
return Runtime::Current()->GetInternTable()->InternWeak(this);
}
diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h
index a1b674a025..6ce75bccb6 100644
--- a/runtime/mirror/string.h
+++ b/runtime/mirror/string.h
@@ -93,7 +93,7 @@ class MANAGED String FINAL : public Object {
void SetCharAt(int32_t index, uint16_t c) REQUIRES_SHARED(Locks::mutator_lock_);
- String* Intern() REQUIRES_SHARED(Locks::mutator_lock_);
+ ObjPtr<String> Intern() REQUIRES_SHARED(Locks::mutator_lock_);
template <bool kIsInstrumented>
ALWAYS_INLINE static String* AllocFromByteArray(Thread* self, int32_t byte_length,
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index 68f71f77e6..fde0a2cc00 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -260,7 +260,7 @@ static void IterateOverPathClassLoader(
ArtField* const dex_file_field =
soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile);
ObjPtr<mirror::Object> dex_path_list =
- soa.DecodeField(WellKnownClasses::dalvik_system_PathClassLoader_pathList)->
+ soa.DecodeField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList)->
GetObject(class_loader.Get());
if (dex_path_list != nullptr && dex_file_field != nullptr && cookie_field != nullptr) {
// DexPathList has an array dexElements of Elements[] which each contain a dex file.
diff --git a/runtime/openjdkjvmti/Android.bp b/runtime/openjdkjvmti/Android.bp
index 8b6a8f95df..b323aef0db 100644
--- a/runtime/openjdkjvmti/Android.bp
+++ b/runtime/openjdkjvmti/Android.bp
@@ -18,9 +18,10 @@ cc_defaults {
defaults: ["art_defaults"],
host_supported: true,
srcs: ["events.cc",
- "heap.cc",
"object_tagging.cc",
"OpenjdkJvmTi.cc",
+ "ti_class.cc",
+ "ti_heap.cc",
"ti_method.cc",
"ti_stack.cc",
"transform.cc"],
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index 10e877a62f..d9031ea652 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -39,7 +39,6 @@
#include "art_jvmti.h"
#include "base/mutex.h"
#include "events-inl.h"
-#include "heap.h"
#include "jni_env_ext-inl.h"
#include "object_tagging.h"
#include "obj_ptr-inl.h"
@@ -47,6 +46,8 @@
#include "scoped_thread_state_change-inl.h"
#include "thread_list.h"
#include "thread-inl.h"
+#include "ti_class.h"
+#include "ti_heap.h"
#include "ti_method.h"
#include "ti_stack.h"
#include "transform.h"
@@ -345,7 +346,7 @@ class JvmtiFunctions {
}
static jvmtiError ForceGarbageCollection(jvmtiEnv* env) {
- return ERR(NOT_IMPLEMENTED);
+ return HeapUtil::ForceGarbageCollection(env);
}
static jvmtiError IterateOverObjectsReachableFromObject(
@@ -506,7 +507,7 @@ class JvmtiFunctions {
jclass klass,
char** signature_ptr,
char** generic_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return ClassUtil::GetClassSignature(env, klass, signature_ptr, generic_ptr);
}
static jvmtiError GetClassStatus(jvmtiEnv* env, jclass klass, jint* status_ptr) {
@@ -648,13 +649,13 @@ class JvmtiFunctions {
static jvmtiError GetMethodDeclaringClass(jvmtiEnv* env,
jmethodID method,
jclass* declaring_class_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::GetMethodDeclaringClass(env, method, declaring_class_ptr);
}
static jvmtiError GetMethodModifiers(jvmtiEnv* env,
jmethodID method,
jint* modifiers_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::GetMethodModifiers(env, method, modifiers_ptr);
}
static jvmtiError GetMaxLocals(jvmtiEnv* env,
diff --git a/runtime/openjdkjvmti/art_jvmti.h b/runtime/openjdkjvmti/art_jvmti.h
index 78cb9a04dc..a321124244 100644
--- a/runtime/openjdkjvmti/art_jvmti.h
+++ b/runtime/openjdkjvmti/art_jvmti.h
@@ -108,6 +108,19 @@ static inline JvmtiUniquePtr MakeJvmtiUniquePtr(jvmtiEnv* env, unsigned char* me
return JvmtiUniquePtr(mem, JvmtiDeleter(env));
}
+ALWAYS_INLINE
+static inline jvmtiError CopyString(jvmtiEnv* env, const char* src, unsigned char** copy) {
+ size_t len = strlen(src) + 1;
+ unsigned char* buf;
+ jvmtiError ret = env->Allocate(len, &buf);
+ if (ret != ERR(NONE)) {
+ return ret;
+ }
+ strcpy(reinterpret_cast<char*>(buf), src);
+ *copy = buf;
+ return ret;
+}
+
} // namespace openjdkjvmti
#endif // ART_RUNTIME_OPENJDKJVMTI_ART_JVMTI_H_
diff --git a/runtime/openjdkjvmti/ti_class.cc b/runtime/openjdkjvmti/ti_class.cc
new file mode 100644
index 0000000000..de2076aaef
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_class.cc
@@ -0,0 +1,73 @@
+/* Copyright (C) 2016 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h. The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "ti_class.h"
+
+#include "art_jvmti.h"
+#include "scoped_thread_state_change-inl.h"
+#include "thread-inl.h"
+
+namespace openjdkjvmti {
+
+jvmtiError ClassUtil::GetClassSignature(jvmtiEnv* env,
+ jclass jklass,
+ char** signature_ptr,
+ char** generic_ptr) {
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ art::ObjPtr<art::mirror::Class> klass = soa.Decode<art::mirror::Class>(jklass);
+ if (klass == nullptr) {
+ return ERR(INVALID_CLASS);
+ }
+
+ JvmtiUniquePtr sig_copy;
+ if (signature_ptr != nullptr) {
+ std::string storage;
+ const char* descriptor = klass->GetDescriptor(&storage);
+
+ unsigned char* tmp;
+ jvmtiError ret = CopyString(env, descriptor, &tmp);
+ if (ret != ERR(NONE)) {
+ return ret;
+ }
+ sig_copy = MakeJvmtiUniquePtr(env, tmp);
+ *signature_ptr = reinterpret_cast<char*>(tmp);
+ }
+
+ // TODO: Support generic signature.
+ *generic_ptr = nullptr;
+
+ // Everything is fine, release the buffers.
+ sig_copy.release();
+
+ return ERR(NONE);
+}
+
+} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_class.h b/runtime/openjdkjvmti/ti_class.h
new file mode 100644
index 0000000000..caa77d4f7d
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_class.h
@@ -0,0 +1,50 @@
+/* Copyright (C) 2016 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h. The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_CLASS_H_
+#define ART_RUNTIME_OPENJDKJVMTI_TI_CLASS_H_
+
+#include "jni.h"
+#include "jvmti.h"
+
+namespace openjdkjvmti {
+
+class ClassUtil {
+ public:
+ static jvmtiError GetClassSignature(jvmtiEnv* env,
+ jclass klass,
+ char** signature_ptr,
+ char** generic_ptr);
+};
+
+} // namespace openjdkjvmti
+
+#endif // ART_RUNTIME_OPENJDKJVMTI_TI_CLASS_H_
diff --git a/runtime/openjdkjvmti/heap.cc b/runtime/openjdkjvmti/ti_heap.cc
index 1799e1957d..6b207430ff 100644
--- a/runtime/openjdkjvmti/heap.cc
+++ b/runtime/openjdkjvmti/ti_heap.cc
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "heap.h"
+#include "ti_heap.h"
#include "art_jvmti.h"
#include "base/macros.h"
@@ -210,4 +210,10 @@ jvmtiError HeapUtil::GetLoadedClasses(jvmtiEnv* env,
return ERR(NONE);
}
+jvmtiError HeapUtil::ForceGarbageCollection(jvmtiEnv* env ATTRIBUTE_UNUSED) {
+ art::Runtime::Current()->GetHeap()->CollectGarbage(false);
+
+ return ERR(NONE);
+}
+
} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/heap.h b/runtime/openjdkjvmti/ti_heap.h
index b6becb97bb..570dd0c6ce 100644
--- a/runtime/openjdkjvmti/heap.h
+++ b/runtime/openjdkjvmti/ti_heap.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ART_RUNTIME_OPENJDKJVMTI_HEAP_H_
-#define ART_RUNTIME_OPENJDKJVMTI_HEAP_H_
+#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_HEAP_H_
+#define ART_RUNTIME_OPENJDKJVMTI_TI_HEAP_H_
#include "jvmti.h"
@@ -36,6 +36,8 @@ class HeapUtil {
const jvmtiHeapCallbacks* callbacks,
const void* user_data);
+ static jvmtiError ForceGarbageCollection(jvmtiEnv* env);
+
ObjectTagTable* GetTags() {
return tags_;
}
@@ -46,4 +48,4 @@ class HeapUtil {
} // namespace openjdkjvmti
-#endif // ART_RUNTIME_OPENJDKJVMTI_HEAP_H_
+#endif // ART_RUNTIME_OPENJDKJVMTI_TI_HEAP_H_
diff --git a/runtime/openjdkjvmti/ti_method.cc b/runtime/openjdkjvmti/ti_method.cc
index 8d943d93e7..6210936772 100644
--- a/runtime/openjdkjvmti/ti_method.cc
+++ b/runtime/openjdkjvmti/ti_method.cc
@@ -34,22 +34,11 @@
#include "art_jvmti.h"
#include "art_method-inl.h"
#include "base/enums.h"
+#include "modifiers.h"
#include "scoped_thread_state_change-inl.h"
namespace openjdkjvmti {
-static jvmtiError CopyString(jvmtiEnv* env, const char* src, unsigned char** copy) {
- size_t len = strlen(src) + 1;
- unsigned char* buf;
- jvmtiError ret = env->Allocate(len, &buf);
- if (ret != ERR(NONE)) {
- return ret;
- }
- strcpy(reinterpret_cast<char*>(buf), src);
- *copy = buf;
- return ret;
-}
-
jvmtiError MethodUtil::GetMethodName(jvmtiEnv* env,
jmethodID method,
char** name_ptr,
@@ -97,4 +86,47 @@ jvmtiError MethodUtil::GetMethodName(jvmtiEnv* env,
return ERR(NONE);
}
+jvmtiError MethodUtil::GetMethodDeclaringClass(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jmethodID method,
+ jclass* declaring_class_ptr) {
+ if (declaring_class_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ art::ArtMethod* art_method = soa.DecodeMethod(method);
+ // Note: No GetInterfaceMethodIfProxy, we want to actual class.
+
+ art::mirror::Class* klass = art_method->GetDeclaringClass();
+ *declaring_class_ptr = soa.AddLocalReference<jclass>(klass);
+
+ return ERR(NONE);
+}
+
+jvmtiError MethodUtil::GetMethodModifiers(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jmethodID method,
+ jint* modifiers_ptr) {
+ if (modifiers_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ art::ArtMethod* art_method = soa.DecodeMethod(method);
+
+ uint32_t modifiers = art_method->GetAccessFlags();
+
+ // Note: Keep this code in sync with Executable.fixMethodFlags.
+ if ((modifiers & art::kAccAbstract) != 0) {
+ modifiers &= ~art::kAccNative;
+ }
+ modifiers &= ~art::kAccSynchronized;
+ if ((modifiers & art::kAccDeclaredSynchronized) != 0) {
+ modifiers |= art::kAccSynchronized;
+ }
+ modifiers &= art::kAccJavaFlagsMask;
+
+ *modifiers_ptr = modifiers;
+ return ERR(NONE);
+}
+
} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_method.h b/runtime/openjdkjvmti/ti_method.h
index 17a37285fb..43f11f97ec 100644
--- a/runtime/openjdkjvmti/ti_method.h
+++ b/runtime/openjdkjvmti/ti_method.h
@@ -44,6 +44,14 @@ class MethodUtil {
char** name_ptr,
char** signature_ptr,
char** generic_ptr);
+
+ static jvmtiError GetMethodDeclaringClass(jvmtiEnv* env,
+ jmethodID method,
+ jclass* declaring_class_ptr);
+
+ static jvmtiError GetMethodModifiers(jvmtiEnv* env,
+ jmethodID method,
+ jint* modifiers_ptr);
};
} // namespace openjdkjvmti
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index d645c5a398..262608d0a2 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1953,31 +1953,31 @@ void Runtime::RecordWriteArray(mirror::Array* array, size_t index, uint64_t valu
preinitialization_transaction_->RecordWriteArray(array, index, value);
}
-void Runtime::RecordStrongStringInsertion(mirror::String* s) const {
+void Runtime::RecordStrongStringInsertion(ObjPtr<mirror::String> s) const {
DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
preinitialization_transaction_->RecordStrongStringInsertion(s);
}
-void Runtime::RecordWeakStringInsertion(mirror::String* s) const {
+void Runtime::RecordWeakStringInsertion(ObjPtr<mirror::String> s) const {
DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
preinitialization_transaction_->RecordWeakStringInsertion(s);
}
-void Runtime::RecordStrongStringRemoval(mirror::String* s) const {
+void Runtime::RecordStrongStringRemoval(ObjPtr<mirror::String> s) const {
DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
preinitialization_transaction_->RecordStrongStringRemoval(s);
}
-void Runtime::RecordWeakStringRemoval(mirror::String* s) const {
+void Runtime::RecordWeakStringRemoval(ObjPtr<mirror::String> s) const {
DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
preinitialization_transaction_->RecordWeakStringRemoval(s);
}
-void Runtime::RecordResolveString(mirror::DexCache* dex_cache, uint32_t string_idx) const {
+void Runtime::RecordResolveString(ObjPtr<mirror::DexCache> dex_cache, uint32_t string_idx) const {
DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
preinitialization_transaction_->RecordResolveString(dex_cache, string_idx);
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 043ff5d73e..86464abe0d 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -510,15 +510,15 @@ class Runtime {
REQUIRES_SHARED(Locks::mutator_lock_);
void RecordWriteArray(mirror::Array* array, size_t index, uint64_t value) const
REQUIRES_SHARED(Locks::mutator_lock_);
- void RecordStrongStringInsertion(mirror::String* s) const
+ void RecordStrongStringInsertion(ObjPtr<mirror::String> s) const
REQUIRES(Locks::intern_table_lock_);
- void RecordWeakStringInsertion(mirror::String* s) const
+ void RecordWeakStringInsertion(ObjPtr<mirror::String> s) const
REQUIRES(Locks::intern_table_lock_);
- void RecordStrongStringRemoval(mirror::String* s) const
+ void RecordStrongStringRemoval(ObjPtr<mirror::String> s) const
REQUIRES(Locks::intern_table_lock_);
- void RecordWeakStringRemoval(mirror::String* s) const
+ void RecordWeakStringRemoval(ObjPtr<mirror::String> s) const
REQUIRES(Locks::intern_table_lock_);
- void RecordResolveString(mirror::DexCache* dex_cache, uint32_t string_idx) const
+ void RecordResolveString(ObjPtr<mirror::DexCache> dex_cache, uint32_t string_idx) const
REQUIRES_SHARED(Locks::mutator_lock_);
void SetFaultMessage(const std::string& message) REQUIRES(!fault_message_lock_);
diff --git a/runtime/transaction.cc b/runtime/transaction.cc
index 9f8d9816f2..c5da5d2e7e 100644
--- a/runtime/transaction.cc
+++ b/runtime/transaction.cc
@@ -167,29 +167,29 @@ void Transaction::RecordWriteArray(mirror::Array* array, size_t index, uint64_t
array_log.LogValue(index, value);
}
-void Transaction::RecordResolveString(mirror::DexCache* dex_cache, uint32_t string_idx) {
+void Transaction::RecordResolveString(ObjPtr<mirror::DexCache> dex_cache, uint32_t string_idx) {
DCHECK(dex_cache != nullptr);
DCHECK_LT(string_idx, dex_cache->GetDexFile()->NumStringIds());
MutexLock mu(Thread::Current(), log_lock_);
resolve_string_logs_.push_back(ResolveStringLog(dex_cache, string_idx));
}
-void Transaction::RecordStrongStringInsertion(mirror::String* s) {
+void Transaction::RecordStrongStringInsertion(ObjPtr<mirror::String> s) {
InternStringLog log(s, InternStringLog::kStrongString, InternStringLog::kInsert);
LogInternedString(log);
}
-void Transaction::RecordWeakStringInsertion(mirror::String* s) {
+void Transaction::RecordWeakStringInsertion(ObjPtr<mirror::String> s) {
InternStringLog log(s, InternStringLog::kWeakString, InternStringLog::kInsert);
LogInternedString(log);
}
-void Transaction::RecordStrongStringRemoval(mirror::String* s) {
+void Transaction::RecordStrongStringRemoval(ObjPtr<mirror::String> s) {
InternStringLog log(s, InternStringLog::kStrongString, InternStringLog::kRemove);
LogInternedString(log);
}
-void Transaction::RecordWeakStringRemoval(mirror::String* s) {
+void Transaction::RecordWeakStringRemoval(ObjPtr<mirror::String> s) {
InternStringLog log(s, InternStringLog::kWeakString, InternStringLog::kRemove);
LogInternedString(log);
}
@@ -470,10 +470,10 @@ void Transaction::InternStringLog::Undo(InternTable* intern_table) {
case InternStringLog::kInsert: {
switch (string_kind_) {
case InternStringLog::kStrongString:
- intern_table->RemoveStrongFromTransaction(str_);
+ intern_table->RemoveStrongFromTransaction(str_.Read());
break;
case InternStringLog::kWeakString:
- intern_table->RemoveWeakFromTransaction(str_);
+ intern_table->RemoveWeakFromTransaction(str_.Read());
break;
default:
LOG(FATAL) << "Unknown interned string kind";
@@ -484,10 +484,10 @@ void Transaction::InternStringLog::Undo(InternTable* intern_table) {
case InternStringLog::kRemove: {
switch (string_kind_) {
case InternStringLog::kStrongString:
- intern_table->InsertStrongFromTransaction(str_);
+ intern_table->InsertStrongFromTransaction(str_.Read());
break;
case InternStringLog::kWeakString:
- intern_table->InsertWeakFromTransaction(str_);
+ intern_table->InsertWeakFromTransaction(str_.Read());
break;
default:
LOG(FATAL) << "Unknown interned string kind";
@@ -502,14 +502,15 @@ void Transaction::InternStringLog::Undo(InternTable* intern_table) {
}
void Transaction::InternStringLog::VisitRoots(RootVisitor* visitor) {
- visitor->VisitRoot(reinterpret_cast<mirror::Object**>(&str_), RootInfo(kRootInternedString));
+ str_.VisitRoot(visitor, RootInfo(kRootInternedString));
}
void Transaction::ResolveStringLog::Undo() {
dex_cache_.Read()->ClearString(string_idx_);
}
-Transaction::ResolveStringLog::ResolveStringLog(mirror::DexCache* dex_cache, uint32_t string_idx)
+Transaction::ResolveStringLog::ResolveStringLog(ObjPtr<mirror::DexCache> dex_cache,
+ uint32_t string_idx)
: dex_cache_(dex_cache),
string_idx_(string_idx) {
DCHECK(dex_cache != nullptr);
@@ -520,6 +521,15 @@ void Transaction::ResolveStringLog::VisitRoots(RootVisitor* visitor) {
dex_cache_.VisitRoot(visitor, RootInfo(kRootVMInternal));
}
+Transaction::InternStringLog::InternStringLog(ObjPtr<mirror::String> s,
+ StringKind kind,
+ StringOp op)
+ : str_(s),
+ string_kind_(kind),
+ string_op_(op) {
+ DCHECK(s != nullptr);
+}
+
void Transaction::ArrayLog::LogValue(size_t index, uint64_t value) {
auto it = array_values_.find(index);
if (it == array_values_.end()) {
diff --git a/runtime/transaction.h b/runtime/transaction.h
index 584dfb8986..2ec2f506fa 100644
--- a/runtime/transaction.h
+++ b/runtime/transaction.h
@@ -83,21 +83,21 @@ class Transaction FINAL {
REQUIRES_SHARED(Locks::mutator_lock_);
// Record intern string table changes.
- void RecordStrongStringInsertion(mirror::String* s)
+ void RecordStrongStringInsertion(ObjPtr<mirror::String> s)
REQUIRES(Locks::intern_table_lock_)
REQUIRES(!log_lock_);
- void RecordWeakStringInsertion(mirror::String* s)
+ void RecordWeakStringInsertion(ObjPtr<mirror::String> s)
REQUIRES(Locks::intern_table_lock_)
REQUIRES(!log_lock_);
- void RecordStrongStringRemoval(mirror::String* s)
+ void RecordStrongStringRemoval(ObjPtr<mirror::String> s)
REQUIRES(Locks::intern_table_lock_)
REQUIRES(!log_lock_);
- void RecordWeakStringRemoval(mirror::String* s)
+ void RecordWeakStringRemoval(ObjPtr<mirror::String> s)
REQUIRES(Locks::intern_table_lock_)
REQUIRES(!log_lock_);
// Record resolve string.
- void RecordResolveString(mirror::DexCache* dex_cache, uint32_t string_idx)
+ void RecordResolveString(ObjPtr<mirror::DexCache> dex_cache, uint32_t string_idx)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!log_lock_);
@@ -182,10 +182,7 @@ class Transaction FINAL {
kInsert,
kRemove
};
- InternStringLog(mirror::String* s, StringKind kind, StringOp op)
- : str_(s), string_kind_(kind), string_op_(op) {
- DCHECK(s != nullptr);
- }
+ InternStringLog(ObjPtr<mirror::String> s, StringKind kind, StringOp op);
void Undo(InternTable* intern_table)
REQUIRES_SHARED(Locks::mutator_lock_)
@@ -193,14 +190,14 @@ class Transaction FINAL {
void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_);
private:
- mirror::String* str_;
+ GcRoot<mirror::String> str_;
const StringKind string_kind_;
const StringOp string_op_;
};
class ResolveStringLog : public ValueObject {
public:
- ResolveStringLog(mirror::DexCache* dex_cache, uint32_t string_idx);
+ ResolveStringLog(ObjPtr<mirror::DexCache> dex_cache, uint32_t string_idx);
void Undo() REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h
index 28f9bb3481..edd6ffe89d 100644
--- a/runtime/vdex_file.h
+++ b/runtime/vdex_file.h
@@ -20,6 +20,7 @@
#include <stdint.h>
#include <string>
+#include "base/array_ref.h"
#include "base/macros.h"
#include "mem_map.h"
#include "os.h"
@@ -44,8 +45,11 @@ class VdexFile {
public:
Header(uint32_t dex_size, uint32_t verifier_deps_size, uint32_t quickening_info_size);
+ const char* GetMagic() const { return reinterpret_cast<const char*>(magic_); }
+ const char* GetVersion() const { return reinterpret_cast<const char*>(version_); }
bool IsMagicValid() const;
bool IsVersionValid() const;
+ bool IsValid() const { return IsMagicValid() && IsVersionValid(); }
uint32_t GetDexSize() const { return dex_size_; }
uint32_t GetVerifierDepsSize() const { return verifier_deps_size_; }
@@ -71,6 +75,15 @@ class VdexFile {
const uint8_t* End() const { return mmap_->End(); }
size_t Size() const { return mmap_->Size(); }
+ const Header& GetHeader() const {
+ return *reinterpret_cast<const Header*>(Begin());
+ }
+
+ ArrayRef<const uint8_t> GetVerifierDepsData() const {
+ return ArrayRef<const uint8_t>(
+ Begin() + sizeof(Header) + GetHeader().GetDexSize(), GetHeader().GetVerifierDepsSize());
+ }
+
private:
explicit VdexFile(MemMap* mmap) : mmap_(mmap) {}
diff --git a/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc
index 4d1e337bb1..bdf63cb45b 100644
--- a/runtime/verifier/verifier_deps.cc
+++ b/runtime/verifier/verifier_deps.cc
@@ -39,6 +39,11 @@ VerifierDeps::DexFileDeps* VerifierDeps::GetDexFileDeps(const DexFile& dex_file)
return (it == dex_deps_.end()) ? nullptr : it->second.get();
}
+const VerifierDeps::DexFileDeps* VerifierDeps::GetDexFileDeps(const DexFile& dex_file) const {
+ auto it = dex_deps_.find(&dex_file);
+ return (it == dex_deps_.end()) ? nullptr : it->second.get();
+}
+
template <typename T>
uint16_t VerifierDeps::GetAccessFlags(T* element) {
static_assert(kAccJavaFlagsMask == 0xFFFF, "Unexpected value of a constant");
@@ -95,12 +100,12 @@ uint32_t VerifierDeps::GetIdFromString(const DexFile& dex_file, const std::strin
return new_id;
}
-std::string VerifierDeps::GetStringFromId(const DexFile& dex_file, uint32_t string_id) {
+std::string VerifierDeps::GetStringFromId(const DexFile& dex_file, uint32_t string_id) const {
uint32_t num_ids_in_dex = dex_file.NumStringIds();
if (string_id < num_ids_in_dex) {
return std::string(dex_file.StringDataByIdx(string_id));
} else {
- DexFileDeps* deps = GetDexFileDeps(dex_file);
+ const DexFileDeps* deps = GetDexFileDeps(dex_file);
DCHECK(deps != nullptr);
string_id -= num_ids_in_dex;
CHECK_LT(string_id, deps->strings_.size());
@@ -108,7 +113,7 @@ std::string VerifierDeps::GetStringFromId(const DexFile& dex_file, uint32_t stri
}
}
-bool VerifierDeps::IsInClassPath(ObjPtr<mirror::Class> klass) {
+bool VerifierDeps::IsInClassPath(ObjPtr<mirror::Class> klass) const {
DCHECK(klass != nullptr);
ObjPtr<mirror::DexCache> dex_cache = klass->GetDexCache();
@@ -431,35 +436,45 @@ static inline void DecodeStringVector(const uint8_t** in,
}
}
-void VerifierDeps::Encode(std::vector<uint8_t>* buffer) const {
+void VerifierDeps::Encode(const std::vector<const DexFile*>& dex_files,
+ std::vector<uint8_t>* buffer) const {
MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_);
- for (auto& entry : dex_deps_) {
- EncodeStringVector(buffer, entry.second->strings_);
- EncodeSet(buffer, entry.second->assignable_types_);
- EncodeSet(buffer, entry.second->unassignable_types_);
- EncodeSet(buffer, entry.second->classes_);
- EncodeSet(buffer, entry.second->fields_);
- EncodeSet(buffer, entry.second->direct_methods_);
- EncodeSet(buffer, entry.second->virtual_methods_);
- EncodeSet(buffer, entry.second->interface_methods_);
- EncodeUint16Vector(buffer, entry.second->unverified_classes_);
+ for (const DexFile* dex_file : dex_files) {
+ const DexFileDeps& deps = *GetDexFileDeps(*dex_file);
+ EncodeStringVector(buffer, deps.strings_);
+ EncodeSet(buffer, deps.assignable_types_);
+ EncodeSet(buffer, deps.unassignable_types_);
+ EncodeSet(buffer, deps.classes_);
+ EncodeSet(buffer, deps.fields_);
+ EncodeSet(buffer, deps.direct_methods_);
+ EncodeSet(buffer, deps.virtual_methods_);
+ EncodeSet(buffer, deps.interface_methods_);
+ EncodeUint16Vector(buffer, deps.unverified_classes_);
}
}
-VerifierDeps::VerifierDeps(const std::vector<const DexFile*>& dex_files, ArrayRef<uint8_t> data)
+VerifierDeps::VerifierDeps(const std::vector<const DexFile*>& dex_files,
+ ArrayRef<const uint8_t> data)
: VerifierDeps(dex_files) {
+ if (data.empty()) {
+ // Return eagerly, as the first thing we expect from VerifierDeps data is
+ // the number of created strings, even if there is no dependency.
+ // Currently, only the boot image does not have any VerifierDeps data.
+ return;
+ }
const uint8_t* data_start = data.data();
const uint8_t* data_end = data_start + data.size();
- for (auto& entry : dex_deps_) {
- DecodeStringVector(&data_start, data_end, &entry.second->strings_);
- DecodeSet(&data_start, data_end, &entry.second->assignable_types_);
- DecodeSet(&data_start, data_end, &entry.second->unassignable_types_);
- DecodeSet(&data_start, data_end, &entry.second->classes_);
- DecodeSet(&data_start, data_end, &entry.second->fields_);
- DecodeSet(&data_start, data_end, &entry.second->direct_methods_);
- DecodeSet(&data_start, data_end, &entry.second->virtual_methods_);
- DecodeSet(&data_start, data_end, &entry.second->interface_methods_);
- DecodeUint16Vector(&data_start, data_end, &entry.second->unverified_classes_);
+ for (const DexFile* dex_file : dex_files) {
+ DexFileDeps* deps = GetDexFileDeps(*dex_file);
+ DecodeStringVector(&data_start, data_end, &deps->strings_);
+ DecodeSet(&data_start, data_end, &deps->assignable_types_);
+ DecodeSet(&data_start, data_end, &deps->unassignable_types_);
+ DecodeSet(&data_start, data_end, &deps->classes_);
+ DecodeSet(&data_start, data_end, &deps->fields_);
+ DecodeSet(&data_start, data_end, &deps->direct_methods_);
+ DecodeSet(&data_start, data_end, &deps->virtual_methods_);
+ DecodeSet(&data_start, data_end, &deps->interface_methods_);
+ DecodeUint16Vector(&data_start, data_end, &deps->unverified_classes_);
}
CHECK_LE(data_start, data_end);
}
@@ -504,5 +519,354 @@ bool VerifierDeps::DexFileDeps::Equals(const VerifierDeps::DexFileDeps& rhs) con
(unverified_classes_ == rhs.unverified_classes_);
}
+void VerifierDeps::Dump(VariableIndentationOutputStream* vios) const {
+ for (const auto& dep : dex_deps_) {
+ const DexFile& dex_file = *dep.first;
+ vios->Stream()
+ << "Dependencies of "
+ << dex_file.GetLocation()
+ << ":\n";
+
+ ScopedIndentation indent(vios);
+
+ for (const std::string& str : dep.second->strings_) {
+ vios->Stream() << "Extra string: " << str << "\n";
+ }
+
+ for (const TypeAssignability& entry : dep.second->assignable_types_) {
+ vios->Stream()
+ << GetStringFromId(dex_file, entry.GetSource())
+ << " must be assignable to "
+ << GetStringFromId(dex_file, entry.GetDestination())
+ << "\n";
+ }
+
+ for (const TypeAssignability& entry : dep.second->unassignable_types_) {
+ vios->Stream()
+ << GetStringFromId(dex_file, entry.GetSource())
+ << " must not be assignable to "
+ << GetStringFromId(dex_file, entry.GetDestination())
+ << "\n";
+ }
+
+ for (const ClassResolution& entry : dep.second->classes_) {
+ vios->Stream()
+ << dex_file.StringByTypeIdx(entry.GetDexTypeIndex())
+ << (entry.IsResolved() ? " must be resolved " : "must not be resolved ")
+ << " with access flags " << std::hex << entry.GetAccessFlags() << std::dec
+ << "\n";
+ }
+
+ for (const FieldResolution& entry : dep.second->fields_) {
+ const DexFile::FieldId& field_id = dex_file.GetFieldId(entry.GetDexFieldIndex());
+ vios->Stream()
+ << dex_file.GetFieldDeclaringClassDescriptor(field_id) << "->"
+ << dex_file.GetFieldName(field_id) << ":"
+ << dex_file.GetFieldTypeDescriptor(field_id)
+ << " is expected to be ";
+ if (!entry.IsResolved()) {
+ vios->Stream() << "unresolved\n";
+ } else {
+ vios->Stream()
+ << "in class "
+ << GetStringFromId(dex_file, entry.GetDeclaringClassIndex())
+ << ", and have the access flags " << std::hex << entry.GetAccessFlags() << std::dec
+ << "\n";
+ }
+ }
+
+ for (const auto& entry :
+ { std::make_pair(kDirectMethodResolution, dep.second->direct_methods_),
+ std::make_pair(kVirtualMethodResolution, dep.second->virtual_methods_),
+ std::make_pair(kInterfaceMethodResolution, dep.second->interface_methods_) }) {
+ for (const MethodResolution& method : entry.second) {
+ const DexFile::MethodId& method_id = dex_file.GetMethodId(method.GetDexMethodIndex());
+ vios->Stream()
+ << dex_file.GetMethodDeclaringClassDescriptor(method_id) << "->"
+ << dex_file.GetMethodName(method_id)
+ << dex_file.GetMethodSignature(method_id).ToString()
+ << " is expected to be ";
+ if (!method.IsResolved()) {
+ vios->Stream() << "unresolved\n";
+ } else {
+ vios->Stream()
+ << "in class "
+ << GetStringFromId(dex_file, method.GetDeclaringClassIndex())
+ << ", have the access flags " << std::hex << method.GetAccessFlags() << std::dec
+ << ", and be of kind " << entry.first
+ << "\n";
+ }
+ }
+ }
+
+ for (uint16_t type_index : dep.second->unverified_classes_) {
+ vios->Stream()
+ << dex_file.StringByTypeIdx(type_index)
+ << " is expected to be verified at runtime\n";
+ }
+ }
+}
+
+bool VerifierDeps::Verify(Handle<mirror::ClassLoader> class_loader, Thread* self) const {
+ for (const auto& entry : dex_deps_) {
+ if (!VerifyDexFile(class_loader, *entry.first, *entry.second, self)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+// TODO: share that helper with other parts of the compiler that have
+// the same lookup pattern.
+static mirror::Class* FindClassAndClearException(ClassLinker* class_linker,
+ Thread* self,
+ const char* name,
+ Handle<mirror::ClassLoader> class_loader)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ mirror::Class* result = class_linker->FindClass(self, name, class_loader);
+ if (result == nullptr) {
+ DCHECK(self->IsExceptionPending());
+ self->ClearException();
+ }
+ return result;
+}
+
+bool VerifierDeps::VerifyAssignability(Handle<mirror::ClassLoader> class_loader,
+ const DexFile& dex_file,
+ const std::set<TypeAssignability>& assignables,
+ bool expected_assignability,
+ Thread* self) const {
+ StackHandleScope<2> hs(self);
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ MutableHandle<mirror::Class> source(hs.NewHandle<mirror::Class>(nullptr));
+ MutableHandle<mirror::Class> destination(hs.NewHandle<mirror::Class>(nullptr));
+
+ for (const auto& entry : assignables) {
+ const std::string& destination_desc = GetStringFromId(dex_file, entry.GetDestination());
+ destination.Assign(
+ FindClassAndClearException(class_linker, self, destination_desc.c_str(), class_loader));
+ const std::string& source_desc = GetStringFromId(dex_file, entry.GetSource());
+ source.Assign(
+ FindClassAndClearException(class_linker, self, source_desc.c_str(), class_loader));
+
+ if (destination.Get() == nullptr) {
+ LOG(INFO) << "VerifiersDeps: Could not resolve class " << destination_desc;
+ return false;
+ }
+
+ if (source.Get() == nullptr) {
+ LOG(INFO) << "VerifierDeps: Could not resolve class " << source_desc;
+ return false;
+ }
+
+ DCHECK(destination->IsResolved() && source->IsResolved());
+ if (destination->IsAssignableFrom(source.Get()) != expected_assignability) {
+ LOG(INFO) << "VerifierDeps: Class "
+ << destination_desc
+ << (expected_assignability ? " not " : " ")
+ << "assignable from "
+ << source_desc;
+ return false;
+ }
+ }
+ return true;
+}
+
+bool VerifierDeps::VerifyClasses(Handle<mirror::ClassLoader> class_loader,
+ const DexFile& dex_file,
+ const std::set<ClassResolution>& classes,
+ Thread* self) const {
+ StackHandleScope<1> hs(self);
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ MutableHandle<mirror::Class> cls(hs.NewHandle<mirror::Class>(nullptr));
+ for (const auto& entry : classes) {
+ const char* descriptor = dex_file.StringByTypeIdx(entry.GetDexTypeIndex());
+ cls.Assign(FindClassAndClearException(class_linker, self, descriptor, class_loader));
+
+ if (entry.IsResolved()) {
+ if (cls.Get() == nullptr) {
+ LOG(INFO) << "VerifierDeps: Could not resolve class " << descriptor;
+ return false;
+ } else if (entry.GetAccessFlags() != GetAccessFlags(cls.Get())) {
+ LOG(INFO) << "VerifierDeps: Unexpected access flags on class "
+ << descriptor
+ << std::hex
+ << " (expected="
+ << entry.GetAccessFlags()
+ << ", actual="
+ << GetAccessFlags(cls.Get()) << ")"
+ << std::dec;
+ return false;
+ }
+ } else if (cls.Get() != nullptr) {
+ LOG(INFO) << "VerifierDeps: Unexpected successful resolution of class " << descriptor;
+ return false;
+ }
+ }
+ return true;
+}
+
+static std::string GetFieldDescription(const DexFile& dex_file, uint32_t index) {
+ const DexFile::FieldId& field_id = dex_file.GetFieldId(index);
+ return std::string(dex_file.GetFieldDeclaringClassDescriptor(field_id))
+ + "->"
+ + dex_file.GetFieldName(field_id)
+ + ":"
+ + dex_file.GetFieldTypeDescriptor(field_id);
+}
+
+bool VerifierDeps::VerifyFields(Handle<mirror::ClassLoader> class_loader,
+ const DexFile& dex_file,
+ const std::set<FieldResolution>& fields,
+ Thread* self) const {
+ // Check recorded fields are resolved the same way, have the same recorded class,
+ // and have the same recorded flags.
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ StackHandleScope<1> hs(self);
+ Handle<mirror::DexCache> dex_cache(
+ hs.NewHandle(class_linker->FindDexCache(self, dex_file, /* allow_failure */ false)));
+ for (const auto& entry : fields) {
+ ArtField* field = class_linker->ResolveFieldJLS(
+ dex_file, entry.GetDexFieldIndex(), dex_cache, class_loader);
+
+ if (field == nullptr) {
+ DCHECK(self->IsExceptionPending());
+ self->ClearException();
+ }
+
+ if (entry.IsResolved()) {
+ std::string expected_decl_klass = GetStringFromId(dex_file, entry.GetDeclaringClassIndex());
+ std::string temp;
+ if (field == nullptr) {
+ LOG(INFO) << "VerifierDeps: Could not resolve field "
+ << GetFieldDescription(dex_file, entry.GetDexFieldIndex());
+ return false;
+ } else if (expected_decl_klass != field->GetDeclaringClass()->GetDescriptor(&temp)) {
+ LOG(INFO) << "VerifierDeps: Unexpected declaring class for field resolution "
+ << GetFieldDescription(dex_file, entry.GetDexFieldIndex())
+ << " (expected=" << expected_decl_klass
+ << ", actual=" << field->GetDeclaringClass()->GetDescriptor(&temp) << ")";
+ return false;
+ } else if (entry.GetAccessFlags() != GetAccessFlags(field)) {
+ LOG(INFO) << "VerifierDeps: Unexpected access flags for resolved field "
+ << GetFieldDescription(dex_file, entry.GetDexFieldIndex())
+ << std::hex << " (expected=" << entry.GetAccessFlags()
+ << ", actual=" << GetAccessFlags(field) << ")" << std::dec;
+ return false;
+ }
+ } else if (field != nullptr) {
+ LOG(INFO) << "VerifierDeps: Unexpected successful resolution of field "
+ << GetFieldDescription(dex_file, entry.GetDexFieldIndex());
+ return false;
+ }
+ }
+ return true;
+}
+
+static std::string GetMethodDescription(const DexFile& dex_file, uint32_t index) {
+ const DexFile::MethodId& method_id = dex_file.GetMethodId(index);
+ return std::string(dex_file.GetMethodDeclaringClassDescriptor(method_id))
+ + "->"
+ + dex_file.GetMethodName(method_id)
+ + dex_file.GetMethodSignature(method_id).ToString();
+}
+
+bool VerifierDeps::VerifyMethods(Handle<mirror::ClassLoader> class_loader,
+ const DexFile& dex_file,
+ const std::set<MethodResolution>& methods,
+ MethodResolutionKind kind,
+ Thread* self) const {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ PointerSize pointer_size = class_linker->GetImagePointerSize();
+
+ for (const auto& entry : methods) {
+ const DexFile::MethodId& method_id = dex_file.GetMethodId(entry.GetDexMethodIndex());
+
+ const char* name = dex_file.GetMethodName(method_id);
+ const Signature signature = dex_file.GetMethodSignature(method_id);
+ const char* descriptor = dex_file.GetMethodDeclaringClassDescriptor(method_id);
+
+ mirror::Class* cls = FindClassAndClearException(class_linker, self, descriptor, class_loader);
+ if (cls == nullptr) {
+ LOG(INFO) << "VerifierDeps: Could not resolve class " << descriptor;
+ return false;
+ }
+ DCHECK(cls->IsResolved());
+ ArtMethod* method = nullptr;
+ if (kind == kDirectMethodResolution) {
+ method = cls->FindDirectMethod(name, signature, pointer_size);
+ } else if (kind == kVirtualMethodResolution) {
+ method = cls->FindVirtualMethod(name, signature, pointer_size);
+ } else {
+ DCHECK_EQ(kind, kInterfaceMethodResolution);
+ method = cls->FindInterfaceMethod(name, signature, pointer_size);
+ }
+
+ if (entry.IsResolved()) {
+ std::string temp;
+ std::string expected_decl_klass = GetStringFromId(dex_file, entry.GetDeclaringClassIndex());
+ if (method == nullptr) {
+ LOG(INFO) << "VerifierDeps: Could not resolve "
+ << kind
+ << " method "
+ << GetMethodDescription(dex_file, entry.GetDexMethodIndex());
+ return false;
+ } else if (expected_decl_klass != method->GetDeclaringClass()->GetDescriptor(&temp)) {
+ LOG(INFO) << "VerifierDeps: Unexpected declaring class for "
+ << kind
+ << " method resolution "
+ << GetMethodDescription(dex_file, entry.GetDexMethodIndex())
+ << " (expected="
+ << expected_decl_klass
+ << ", actual="
+ << method->GetDeclaringClass()->GetDescriptor(&temp)
+ << ")";
+ return false;
+ } else if (entry.GetAccessFlags() != GetAccessFlags(method)) {
+ LOG(INFO) << "VerifierDeps: Unexpected access flags for resolved "
+ << kind
+ << " method resolution "
+ << GetMethodDescription(dex_file, entry.GetDexMethodIndex())
+ << std::hex
+ << " (expected="
+ << entry.GetAccessFlags()
+ << ", actual="
+ << GetAccessFlags(method) << ")"
+ << std::dec;
+ return false;
+ }
+ } else if (method != nullptr) {
+ LOG(INFO) << "VerifierDeps: Unexpected successful resolution of "
+ << kind
+ << " method "
+ << GetMethodDescription(dex_file, entry.GetDexMethodIndex());
+ return false;
+ }
+ }
+ return true;
+}
+
+bool VerifierDeps::VerifyDexFile(Handle<mirror::ClassLoader> class_loader,
+ const DexFile& dex_file,
+ const DexFileDeps& deps,
+ Thread* self) const {
+ bool result = VerifyAssignability(
+ class_loader, dex_file, deps.assignable_types_, /* expected_assignability */ true, self);
+ result = result && VerifyAssignability(
+ class_loader, dex_file, deps.unassignable_types_, /* expected_assignability */ false, self);
+
+ result = result && VerifyClasses(class_loader, dex_file, deps.classes_, self);
+ result = result && VerifyFields(class_loader, dex_file, deps.fields_, self);
+
+ result = result && VerifyMethods(
+ class_loader, dex_file, deps.direct_methods_, kDirectMethodResolution, self);
+ result = result && VerifyMethods(
+ class_loader, dex_file, deps.virtual_methods_, kVirtualMethodResolution, self);
+ result = result && VerifyMethods(
+ class_loader, dex_file, deps.interface_methods_, kInterfaceMethodResolution, self);
+
+ return result;
+}
+
} // namespace verifier
} // namespace art
diff --git a/runtime/verifier/verifier_deps.h b/runtime/verifier/verifier_deps.h
index 9d2622de3a..eea0299f6b 100644
--- a/runtime/verifier/verifier_deps.h
+++ b/runtime/verifier/verifier_deps.h
@@ -25,6 +25,7 @@
#include "art_method.h"
#include "base/array_ref.h"
#include "base/mutex.h"
+#include "indenter.h"
#include "method_resolution_kind.h"
#include "method_verifier.h" // For MethodVerifier::FailureKind.
#include "obj_ptr.h"
@@ -50,6 +51,10 @@ class VerifierDeps {
explicit VerifierDeps(const std::vector<const DexFile*>& dex_files)
REQUIRES(!Locks::verifier_deps_lock_);
+ VerifierDeps(const std::vector<const DexFile*>& dex_files,
+ ArrayRef<const uint8_t> data)
+ REQUIRES(!Locks::verifier_deps_lock_);
+
// Record the verification status of the class at `type_idx`.
static void MaybeRecordVerificationStatus(const DexFile& dex_file,
uint16_t type_idx,
@@ -94,16 +99,24 @@ class VerifierDeps {
REQUIRES(!Locks::verifier_deps_lock_);
// Serialize the recorded dependencies and store the data into `buffer`.
- void Encode(std::vector<uint8_t>* buffer) const
+ // `dex_files` provides the order of the dex files in which the dependencies
+ // should be emitted.
+ void Encode(const std::vector<const DexFile*>& dex_files, std::vector<uint8_t>* buffer) const
REQUIRES(!Locks::verifier_deps_lock_);
+ // NO_THREAD_SAFETY_ANALYSIS as Dump iterates over dex_deps_, which is guarded by
+ // verifier_deps_lock_, but we expect Dump to be called once the deps collection is done.
+ void Dump(VariableIndentationOutputStream* vios) const
+ NO_THREAD_SAFETY_ANALYSIS;
+
+ // Verify the encoded dependencies of this `VerifierDeps`.
+ // NO_THREAD_SAFETY_ANALYSIS, as this must be called on a read-only `VerifierDeps`.
+ bool Verify(Handle<mirror::ClassLoader> class_loader, Thread* self) const
+ NO_THREAD_SAFETY_ANALYSIS;
+
private:
static constexpr uint16_t kUnresolvedMarker = static_cast<uint16_t>(-1);
- // Only used in tests to reconstruct the data structure from serialized data.
- VerifierDeps(const std::vector<const DexFile*>& dex_files, ArrayRef<uint8_t> data)
- REQUIRES(!Locks::verifier_deps_lock_);
-
using ClassResolutionBase = std::tuple<uint32_t, uint16_t>;
struct ClassResolution : public ClassResolutionBase {
ClassResolution() = default;
@@ -185,9 +198,12 @@ class VerifierDeps {
DexFileDeps* GetDexFileDeps(const DexFile& dex_file)
NO_THREAD_SAFETY_ANALYSIS;
+ const DexFileDeps* GetDexFileDeps(const DexFile& dex_file) const
+ NO_THREAD_SAFETY_ANALYSIS;
+
// Returns true if `klass` is null or not defined in any of dex files which
// were reported as being compiled.
- bool IsInClassPath(ObjPtr<mirror::Class> klass)
+ bool IsInClassPath(ObjPtr<mirror::Class> klass) const
REQUIRES_SHARED(Locks::mutator_lock_);
// Returns the index of `str`. If it is defined in `dex_file_`, this is the dex
@@ -198,13 +214,13 @@ class VerifierDeps {
REQUIRES(Locks::verifier_deps_lock_);
// Returns the string represented by `id`.
- std::string GetStringFromId(const DexFile& dex_file, uint32_t string_id)
+ std::string GetStringFromId(const DexFile& dex_file, uint32_t string_id) const
REQUIRES(Locks::verifier_deps_lock_);
// Returns the bytecode access flags of `element` (bottom 16 bits), or
// `kUnresolvedMarker` if `element` is null.
template <typename T>
- uint16_t GetAccessFlags(T* element)
+ static uint16_t GetAccessFlags(T* element)
REQUIRES_SHARED(Locks::mutator_lock_);
// Returns a string ID of the descriptor of the declaring class of `element`,
@@ -244,6 +260,54 @@ class VerifierDeps {
bool Equals(const VerifierDeps& rhs) const
REQUIRES(!Locks::verifier_deps_lock_);
+ // Verify `dex_file` according to the `deps`, that is going over each
+ // `DexFileDeps` field, and checking that the recorded information still
+ // holds.
+ bool VerifyDexFile(Handle<mirror::ClassLoader> class_loader,
+ const DexFile& dex_file,
+ const DexFileDeps& deps,
+ Thread* self) const
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(Locks::verifier_deps_lock_);
+
+ bool VerifyAssignability(Handle<mirror::ClassLoader> class_loader,
+ const DexFile& dex_file,
+ const std::set<TypeAssignability>& assignables,
+ bool expected_assignability,
+ Thread* self) const
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(Locks::verifier_deps_lock_);
+
+ // Verify that the set of resolved classes at the point of creation
+ // of this `VerifierDeps` is still the same.
+ bool VerifyClasses(Handle<mirror::ClassLoader> class_loader,
+ const DexFile& dex_file,
+ const std::set<ClassResolution>& classes,
+ Thread* self) const
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(Locks::verifier_deps_lock_);
+
+ // Verify that the set of resolved fields at the point of creation
+ // of this `VerifierDeps` is still the same, and each field resolves to the
+ // same field holder and access flags.
+ bool VerifyFields(Handle<mirror::ClassLoader> class_loader,
+ const DexFile& dex_file,
+ const std::set<FieldResolution>& classes,
+ Thread* self) const
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(Locks::verifier_deps_lock_);
+
+ // Verify that the set of resolved methods at the point of creation
+ // of this `VerifierDeps` is still the same, and each method resolves to the
+ // same method holder, access flags, and invocation kind.
+ bool VerifyMethods(Handle<mirror::ClassLoader> class_loader,
+ const DexFile& dex_file,
+ const std::set<MethodResolution>& methods,
+ MethodResolutionKind kind,
+ Thread* self) const
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(Locks::verifier_deps_lock_);
+
// Map from DexFiles into dependencies collected from verification of their methods.
std::map<const DexFile*, std::unique_ptr<DexFileDeps>> dex_deps_
GUARDED_BY(Locks::verifier_deps_lock_);
@@ -251,6 +315,8 @@ class VerifierDeps {
friend class VerifierDepsTest;
ART_FRIEND_TEST(VerifierDepsTest, StringToId);
ART_FRIEND_TEST(VerifierDepsTest, EncodeDecode);
+ ART_FRIEND_TEST(VerifierDepsTest, EncodeDecodeMulti);
+ ART_FRIEND_TEST(VerifierDepsTest, VerifyDeps);
};
} // namespace verifier
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 153c7ef59e..0f4ae521ec 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -34,6 +34,7 @@ namespace art {
jclass WellKnownClasses::com_android_dex_Dex;
jclass WellKnownClasses::dalvik_annotation_optimization_CriticalNative;
jclass WellKnownClasses::dalvik_annotation_optimization_FastNative;
+jclass WellKnownClasses::dalvik_system_BaseDexClassLoader;
jclass WellKnownClasses::dalvik_system_DexFile;
jclass WellKnownClasses::dalvik_system_DexPathList;
jclass WellKnownClasses::dalvik_system_DexPathList__Element;
@@ -108,7 +109,7 @@ jmethodID WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_dispatch;
jfieldID WellKnownClasses::dalvik_system_DexFile_cookie;
jfieldID WellKnownClasses::dalvik_system_DexFile_fileName;
-jfieldID WellKnownClasses::dalvik_system_PathClassLoader_pathList;
+jfieldID WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList;
jfieldID WellKnownClasses::dalvik_system_DexPathList_dexElements;
jfieldID WellKnownClasses::dalvik_system_DexPathList__Element_dexFile;
jfieldID WellKnownClasses::java_lang_Thread_daemon;
@@ -264,6 +265,7 @@ void WellKnownClasses::Init(JNIEnv* env) {
dalvik_annotation_optimization_CriticalNative =
CacheClass(env, "dalvik/annotation/optimization/CriticalNative");
dalvik_annotation_optimization_FastNative = CacheClass(env, "dalvik/annotation/optimization/FastNative");
+ dalvik_system_BaseDexClassLoader = CacheClass(env, "dalvik/system/BaseDexClassLoader");
dalvik_system_DexFile = CacheClass(env, "dalvik/system/DexFile");
dalvik_system_DexPathList = CacheClass(env, "dalvik/system/DexPathList");
dalvik_system_DexPathList__Element = CacheClass(env, "dalvik/system/DexPathList$Element");
@@ -332,9 +334,9 @@ void WellKnownClasses::Init(JNIEnv* env) {
org_apache_harmony_dalvik_ddmc_DdmServer_broadcast = CacheMethod(env, org_apache_harmony_dalvik_ddmc_DdmServer, true, "broadcast", "(I)V");
org_apache_harmony_dalvik_ddmc_DdmServer_dispatch = CacheMethod(env, org_apache_harmony_dalvik_ddmc_DdmServer, true, "dispatch", "(I[BII)Lorg/apache/harmony/dalvik/ddmc/Chunk;");
+ dalvik_system_BaseDexClassLoader_pathList = CacheField(env, dalvik_system_BaseDexClassLoader, false, "pathList", "Ldalvik/system/DexPathList;");
dalvik_system_DexFile_cookie = CacheField(env, dalvik_system_DexFile, false, "mCookie", "Ljava/lang/Object;");
dalvik_system_DexFile_fileName = CacheField(env, dalvik_system_DexFile, false, "mFileName", "Ljava/lang/String;");
- dalvik_system_PathClassLoader_pathList = CacheField(env, dalvik_system_PathClassLoader, false, "pathList", "Ldalvik/system/DexPathList;");
dalvik_system_DexPathList_dexElements = CacheField(env, dalvik_system_DexPathList, false, "dexElements", "[Ldalvik/system/DexPathList$Element;");
dalvik_system_DexPathList__Element_dexFile = CacheField(env, dalvik_system_DexPathList__Element, false, "dexFile", "Ldalvik/system/DexFile;");
java_lang_Thread_daemon = CacheField(env, java_lang_Thread, false, "daemon", "Z");
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index 2fb5bb471d..d07977a493 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -47,6 +47,7 @@ struct WellKnownClasses {
static jclass com_android_dex_Dex;
static jclass dalvik_annotation_optimization_CriticalNative;
static jclass dalvik_annotation_optimization_FastNative;
+ static jclass dalvik_system_BaseDexClassLoader;
static jclass dalvik_system_DexFile;
static jclass dalvik_system_DexPathList;
static jclass dalvik_system_DexPathList__Element;
@@ -119,11 +120,11 @@ struct WellKnownClasses {
static jmethodID org_apache_harmony_dalvik_ddmc_DdmServer_broadcast;
static jmethodID org_apache_harmony_dalvik_ddmc_DdmServer_dispatch;
+ static jfieldID dalvik_system_BaseDexClassLoader_pathList;
static jfieldID dalvik_system_DexFile_cookie;
static jfieldID dalvik_system_DexFile_fileName;
static jfieldID dalvik_system_DexPathList_dexElements;
static jfieldID dalvik_system_DexPathList__Element_dexFile;
- static jfieldID dalvik_system_PathClassLoader_pathList;
static jfieldID java_lang_reflect_Executable_artMethod;
static jfieldID java_lang_reflect_Proxy_h;
static jfieldID java_lang_Thread_daemon;
diff --git a/test/910-methods/expected.txt b/test/910-methods/expected.txt
index b305d0f8ba..9a747994fe 100644
--- a/test/910-methods/expected.txt
+++ b/test/910-methods/expected.txt
@@ -1,4 +1,15 @@
[toString, ()Ljava/lang/String;, null]
+class java.lang.Object
+1
[charAt, (I)C, null]
+class java.lang.String
+257
[sqrt, (D)D, null]
+class java.lang.Math
+265
[add, (Ljava/lang/Object;)Z, null]
+interface java.util.List
+1025
+[run, ()V, null]
+class $Proxy0
+17
diff --git a/test/910-methods/methods.cc b/test/910-methods/methods.cc
index fa910c1aeb..cc6ad6793b 100644
--- a/test/910-methods/methods.cc
+++ b/test/910-methods/methods.cc
@@ -75,6 +75,38 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getMethodName(
return ret;
}
+extern "C" JNIEXPORT jclass JNICALL Java_Main_getMethodDeclaringClass(
+ JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jobject method) {
+ jmethodID id = env->FromReflectedMethod(method);
+
+ jclass declaring_class;
+ jvmtiError result = jvmti_env->GetMethodDeclaringClass(id, &declaring_class);
+ if (result != JVMTI_ERROR_NONE) {
+ char* err;
+ jvmti_env->GetErrorName(result, &err);
+ printf("Failure running GetMethodDeclaringClass: %s\n", err);
+ return nullptr;
+ }
+
+ return declaring_class;
+}
+
+extern "C" JNIEXPORT jint JNICALL Java_Main_getMethodModifiers(
+ JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jobject method) {
+ jmethodID id = env->FromReflectedMethod(method);
+
+ jint modifiers;
+ jvmtiError result = jvmti_env->GetMethodModifiers(id, &modifiers);
+ if (result != JVMTI_ERROR_NONE) {
+ char* err;
+ jvmti_env->GetErrorName(result, &err);
+ printf("Failure running GetMethodModifiers: %s\n", err);
+ return 0;
+ }
+
+ return modifiers;
+}
+
// Don't do anything
jint OnLoad(JavaVM* vm,
char* options ATTRIBUTE_UNUSED,
diff --git a/test/910-methods/src/Main.java b/test/910-methods/src/Main.java
index 1af30ba878..3459134ea2 100644
--- a/test/910-methods/src/Main.java
+++ b/test/910-methods/src/Main.java
@@ -15,6 +15,7 @@
*/
import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
import java.util.Arrays;
public class Main {
@@ -29,15 +30,47 @@ public class Main {
testMethod("java.lang.String", "charAt", int.class);
testMethod("java.lang.Math", "sqrt", double.class);
testMethod("java.util.List", "add", Object.class);
+
+ testMethod(getProxyClass(), "run");
+ }
+
+ private static Class<?> proxyClass = null;
+
+ private static Class<?> getProxyClass() throws Exception {
+ if (proxyClass != null) {
+ return proxyClass;
+ }
+
+ proxyClass = Proxy.getProxyClass(Main.class.getClassLoader(), new Class[] { Runnable.class });
+ return proxyClass;
}
private static void testMethod(String className, String methodName, Class<?>... types)
throws Exception {
Class<?> base = Class.forName(className);
+ testMethod(base, methodName, types);
+ }
+
+ private static void testMethod(Class<?> base, String methodName, Class<?>... types)
+ throws Exception {
Method m = base.getDeclaredMethod(methodName, types);
String[] result = getMethodName(m);
System.out.println(Arrays.toString(result));
+
+ Class<?> declClass = getMethodDeclaringClass(m);
+ if (base != declClass) {
+ throw new RuntimeException("Declaring class not equal: " + base + " vs " + declClass);
+ }
+ System.out.println(declClass);
+
+ int modifiers = getMethodModifiers(m);
+ if (modifiers != m.getModifiers()) {
+ throw new RuntimeException("Modifiers not equal: " + m.getModifiers() + " vs " + modifiers);
+ }
+ System.out.println(modifiers);
}
private static native String[] getMethodName(Method m);
+ private static native Class<?> getMethodDeclaringClass(Method m);
+ private static native int getMethodModifiers(Method m);
}
diff --git a/test/912-classes/build b/test/912-classes/build
new file mode 100755
index 0000000000..898e2e54a2
--- /dev/null
+++ b/test/912-classes/build
@@ -0,0 +1,17 @@
+#!/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/912-classes/classes.cc b/test/912-classes/classes.cc
new file mode 100644
index 0000000000..4bf329c8e1
--- /dev/null
+++ b/test/912-classes/classes.cc
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2013 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 "classes.h"
+
+#include <stdio.h>
+
+#include "base/macros.h"
+#include "jni.h"
+#include "openjdkjvmti/jvmti.h"
+#include "ScopedLocalRef.h"
+
+#include "ti-agent/common_load.h"
+
+namespace art {
+namespace Test912Classes {
+
+extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getClassSignature(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
+ char* sig;
+ char* gen;
+ jvmtiError result = jvmti_env->GetClassSignature(klass, &sig, &gen);
+ if (result != JVMTI_ERROR_NONE) {
+ char* err;
+ jvmti_env->GetErrorName(result, &err);
+ printf("Failure running GetClassSignature: %s\n", err);
+ return nullptr;
+ }
+
+ ScopedLocalRef<jclass> obj_class(env, env->FindClass("java/lang/String"));
+ if (obj_class.get() == nullptr) {
+ return nullptr;
+ }
+
+ jobjectArray ret = env->NewObjectArray(2, obj_class.get(), nullptr);
+ if (ret == nullptr) {
+ return ret;
+ }
+
+ ScopedLocalRef<jstring> sig_str(env, sig == nullptr ? nullptr : env->NewStringUTF(sig));
+ ScopedLocalRef<jstring> gen_str(env, gen == nullptr ? nullptr : env->NewStringUTF(gen));
+
+ env->SetObjectArrayElement(ret, 0, sig_str.get());
+ env->SetObjectArrayElement(ret, 1, gen_str.get());
+
+ // Need to deallocate the strings.
+ if (sig != nullptr) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(sig));
+ }
+ if (gen != nullptr) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(gen));
+ }
+
+ return ret;
+}
+
+// Don't do anything
+jint OnLoad(JavaVM* vm,
+ char* options ATTRIBUTE_UNUSED,
+ void* reserved ATTRIBUTE_UNUSED) {
+ if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
+ printf("Unable to get jvmti env!\n");
+ return 1;
+ }
+ return 0;
+}
+
+} // namespace Test912Classes
+} // namespace art
diff --git a/test/912-classes/classes.h b/test/912-classes/classes.h
new file mode 100644
index 0000000000..62fb203356
--- /dev/null
+++ b/test/912-classes/classes.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_TEST_912_CLASSES_CLASSES_H_
+#define ART_TEST_912_CLASSES_CLASSES_H_
+
+#include <jni.h>
+
+namespace art {
+namespace Test912Classes {
+
+jint OnLoad(JavaVM* vm, char* options, void* reserved);
+
+} // namespace Test912Classes
+} // namespace art
+
+#endif // ART_TEST_912_CLASSES_CLASSES_H_
diff --git a/test/912-classes/expected.txt b/test/912-classes/expected.txt
new file mode 100644
index 0000000000..71b22f4d0b
--- /dev/null
+++ b/test/912-classes/expected.txt
@@ -0,0 +1,7 @@
+[Ljava/lang/Object;, null]
+[Ljava/lang/String;, null]
+[Ljava/lang/Math;, null]
+[Ljava/util/List;, null]
+[L$Proxy0;, null]
+[I, null]
+[[D, null]
diff --git a/test/912-classes/info.txt b/test/912-classes/info.txt
new file mode 100644
index 0000000000..875a5f6ec1
--- /dev/null
+++ b/test/912-classes/info.txt
@@ -0,0 +1 @@
+Tests basic functions in the jvmti plugin.
diff --git a/test/912-classes/run b/test/912-classes/run
new file mode 100755
index 0000000000..64bbb987a1
--- /dev/null
+++ b/test/912-classes/run
@@ -0,0 +1,43 @@
+#!/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.
+
+plugin=libopenjdkjvmtid.so
+agent=libtiagentd.so
+lib=tiagentd
+if [[ "$@" == *"-O"* ]]; then
+ agent=libtiagent.so
+ plugin=libopenjdkjvmti.so
+ lib=tiagent
+fi
+
+if [[ "$@" == *"--jvm"* ]]; then
+ arg="jvm"
+else
+ arg="art"
+fi
+
+if [[ "$@" != *"--debuggable"* ]]; then
+ other_args=" -Xcompiler-option --debuggable "
+else
+ other_args=""
+fi
+
+./default-run "$@" --experimental agents \
+ --experimental runtime-plugins \
+ --runtime-option -agentpath:${agent}=912-classes,${arg} \
+ --android-runtime-option -Xplugin:${plugin} \
+ ${other_args} \
+ --args ${lib}
diff --git a/test/912-classes/src/Main.java b/test/912-classes/src/Main.java
new file mode 100644
index 0000000000..025584ec0a
--- /dev/null
+++ b/test/912-classes/src/Main.java
@@ -0,0 +1,61 @@
+/*
+ * 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.Proxy;
+import java.util.Arrays;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[1]);
+
+ doTest();
+ }
+
+ public static void doTest() throws Exception {
+ testClass("java.lang.Object");
+ testClass("java.lang.String");
+ testClass("java.lang.Math");
+ testClass("java.util.List");
+
+ testClass(getProxyClass());
+
+ testClass(int.class);
+ testClass(double[].class);
+ }
+
+ private static Class<?> proxyClass = null;
+
+ private static Class<?> getProxyClass() throws Exception {
+ if (proxyClass != null) {
+ return proxyClass;
+ }
+
+ proxyClass = Proxy.getProxyClass(Main.class.getClassLoader(), new Class[] { Runnable.class });
+ return proxyClass;
+ }
+
+ private static void testClass(String className) throws Exception {
+ Class<?> base = Class.forName(className);
+ testClass(base);
+ }
+
+ private static void testClass(Class<?> base) throws Exception {
+ String[] result = getClassSignature(base);
+ System.out.println(Arrays.toString(result));
+ }
+
+ private static native String[] getClassSignature(Class<?> c);
+}
diff --git a/test/913-heaps/build b/test/913-heaps/build
new file mode 100755
index 0000000000..898e2e54a2
--- /dev/null
+++ b/test/913-heaps/build
@@ -0,0 +1,17 @@
+#!/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/913-heaps/expected.txt b/test/913-heaps/expected.txt
new file mode 100644
index 0000000000..77791a4efe
--- /dev/null
+++ b/test/913-heaps/expected.txt
@@ -0,0 +1,2 @@
+---
+true true
diff --git a/test/913-heaps/heaps.cc b/test/913-heaps/heaps.cc
new file mode 100644
index 0000000000..437779a5f4
--- /dev/null
+++ b/test/913-heaps/heaps.cc
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2013 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 "heaps.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include "base/macros.h"
+#include "jni.h"
+#include "openjdkjvmti/jvmti.h"
+
+#include "ti-agent/common_load.h"
+
+namespace art {
+namespace Test913Heaps {
+
+extern "C" JNIEXPORT void JNICALL Java_Main_forceGarbageCollection(JNIEnv* env ATTRIBUTE_UNUSED,
+ jclass klass ATTRIBUTE_UNUSED) {
+ jvmtiError ret = jvmti_env->ForceGarbageCollection();
+ if (ret != JVMTI_ERROR_NONE) {
+ char* err;
+ jvmti_env->GetErrorName(ret, &err);
+ printf("Error forcing a garbage collection: %s\n", err);
+ }
+}
+
+// Don't do anything
+jint OnLoad(JavaVM* vm,
+ char* options ATTRIBUTE_UNUSED,
+ void* reserved ATTRIBUTE_UNUSED) {
+ if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
+ printf("Unable to get jvmti env!\n");
+ return 1;
+ }
+ return 0;
+}
+
+} // namespace Test913Heaps
+} // namespace art
diff --git a/test/913-heaps/heaps.h b/test/913-heaps/heaps.h
new file mode 100644
index 0000000000..bd828aca33
--- /dev/null
+++ b/test/913-heaps/heaps.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_TEST_913_HEAPS_HEAPS_H_
+#define ART_TEST_913_HEAPS_HEAPS_H_
+
+#include <jni.h>
+
+namespace art {
+namespace Test913Heaps {
+
+jint OnLoad(JavaVM* vm, char* options, void* reserved);
+
+} // namespace Test913Heaps
+} // namespace art
+
+#endif // ART_TEST_913_HEAPS_HEAPS_H_
diff --git a/test/913-heaps/info.txt b/test/913-heaps/info.txt
new file mode 100644
index 0000000000..875a5f6ec1
--- /dev/null
+++ b/test/913-heaps/info.txt
@@ -0,0 +1 @@
+Tests basic functions in the jvmti plugin.
diff --git a/test/913-heaps/run b/test/913-heaps/run
new file mode 100755
index 0000000000..7bd8cbd1dd
--- /dev/null
+++ b/test/913-heaps/run
@@ -0,0 +1,43 @@
+#!/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.
+
+plugin=libopenjdkjvmtid.so
+agent=libtiagentd.so
+lib=tiagentd
+if [[ "$@" == *"-O"* ]]; then
+ agent=libtiagent.so
+ plugin=libopenjdkjvmti.so
+ lib=tiagent
+fi
+
+if [[ "$@" == *"--jvm"* ]]; then
+ arg="jvm"
+else
+ arg="art"
+fi
+
+if [[ "$@" != *"--debuggable"* ]]; then
+ other_args=" -Xcompiler-option --debuggable "
+else
+ other_args=""
+fi
+
+./default-run "$@" --experimental agents \
+ --experimental runtime-plugins \
+ --runtime-option -agentpath:${agent}=913-heaps,${arg} \
+ --android-runtime-option -Xplugin:${plugin} \
+ ${other_args} \
+ --args ${lib}
diff --git a/test/913-heaps/src/Main.java b/test/913-heaps/src/Main.java
new file mode 100644
index 0000000000..4d77a482b7
--- /dev/null
+++ b/test/913-heaps/src/Main.java
@@ -0,0 +1,57 @@
+/*
+ * 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.ArrayList;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[1]);
+
+ doTest();
+ }
+
+ public static void doTest() throws Exception {
+ setupGcCallback();
+
+ enableGcTracking(true);
+ run();
+ enableGcTracking(false);
+ }
+
+ private static void run() {
+ clearStats();
+ forceGarbageCollection();
+ printStats();
+ }
+
+ private static void clearStats() {
+ getGcStarts();
+ getGcFinishes();
+ }
+
+ private static void printStats() {
+ System.out.println("---");
+ int s = getGcStarts();
+ int f = getGcFinishes();
+ System.out.println((s > 0) + " " + (f > 0));
+ }
+
+ private static native void setupGcCallback();
+ private static native void enableGcTracking(boolean enable);
+ private static native int getGcStarts();
+ private static native int getGcFinishes();
+ private static native void forceGarbageCollection();
+}
diff --git a/test/956-methodhandles/expected.txt b/test/956-methodhandles/expected.txt
index ddc1cb013e..ad1c43c490 100644
--- a/test/956-methodhandles/expected.txt
+++ b/test/956-methodhandles/expected.txt
@@ -3,3 +3,4 @@ foo_A
foo_A
foo_B
privateRyan_D
+Received exception: Expected (java.lang.String, java.lang.String)java.lang.String but was (java.lang.String, java.lang.Object)void
diff --git a/test/956-methodhandles/src/Main.java b/test/956-methodhandles/src/Main.java
index 42265a9b87..44c0447771 100644
--- a/test/956-methodhandles/src/Main.java
+++ b/test/956-methodhandles/src/Main.java
@@ -57,6 +57,8 @@ public class Main {
public static void main(String[] args) throws Throwable {
testfindSpecial_invokeSuperBehaviour();
testfindSpecial_invokeDirectBehaviour();
+ testExceptionDetailMessages();
+ testfindVirtual();
}
public static void testfindSpecial_invokeSuperBehaviour() throws Throwable {
@@ -127,6 +129,139 @@ public class Main {
} catch (IllegalAccessException expected) {
}
}
+
+ public static void testExceptionDetailMessages() throws Throwable {
+ MethodHandle handle = MethodHandles.lookup().findVirtual(String.class, "concat",
+ MethodType.methodType(String.class, String.class));
+
+ try {
+ handle.invokeExact("a", new Object());
+ System.out.println("invokeExact(\"a\", new Object()) unexpectedly succeeded.");
+ } catch (WrongMethodTypeException ex) {
+ System.out.println("Received exception: " + ex.getMessage());
+ }
+ }
+
+ public interface Foo {
+ public String foo();
+ }
+
+ public interface Bar extends Foo {
+ public String bar();
+ }
+
+ public static class BarSuper {
+ public String superPublicMethod() {
+ return "superPublicMethod";
+ }
+
+ public String superProtectedMethod() {
+ return "superProtectedMethod";
+ }
+
+ String superPackageMethod() {
+ return "superPackageMethod";
+ }
+ }
+
+ public static class BarImpl extends BarSuper implements Bar {
+ public BarImpl() {
+ }
+
+ @Override
+ public String foo() {
+ return "foo";
+ }
+
+ @Override
+ public String bar() {
+ return "bar";
+ }
+
+ private String privateMethod() { return "privateMethod"; }
+
+ public static String staticMethod() { return null; }
+
+ static final MethodHandles.Lookup lookup = MethodHandles.lookup();
+ }
+
+ public static void testfindVirtual() throws Throwable {
+ // Virtual lookups on static methods should not succeed.
+ try {
+ MethodHandles.lookup().findVirtual(
+ BarImpl.class, "staticMethod", MethodType.methodType(String.class));
+ System.out.println("findVirtual(staticMethod) unexpectedly succeeded");
+ } catch (IllegalAccessException expected) {
+ }
+
+ // Virtual lookups on private methods should not succeed, unless the Lookup
+ // context had sufficient privileges.
+ try {
+ MethodHandles.lookup().findVirtual(
+ BarImpl.class, "privateMethod", MethodType.methodType(String.class));
+ System.out.println("findVirtual(privateMethod) unexpectedly succeeded");
+ } catch (IllegalAccessException expected) {
+ }
+
+ // Virtual lookup on a private method with a context that *does* have sufficient
+ // privileges.
+ MethodHandle mh = BarImpl.lookup.findVirtual(
+ BarImpl.class, "privateMethod", MethodType.methodType(String.class));
+ String str = (String) mh.invoke(new BarImpl());
+ if (!"privateMethod".equals(str)) {
+ System.out.println("Unexpected return value for BarImpl#privateMethod: " + str);
+ }
+
+ // Find virtual must find interface methods defined by interfaces implemented
+ // by the class.
+ mh = MethodHandles.lookup().findVirtual(BarImpl.class, "foo",
+ MethodType.methodType(String.class));
+ str = (String) mh.invoke(new BarImpl());
+ if (!"foo".equals(str)) {
+ System.out.println("Unexpected return value for BarImpl#foo: " + str);
+ }
+
+ // .. and their super-interfaces.
+ mh = MethodHandles.lookup().findVirtual(BarImpl.class, "bar",
+ MethodType.methodType(String.class));
+ str = (String) mh.invoke(new BarImpl());
+ if (!"bar".equals(str)) {
+ System.out.println("Unexpected return value for BarImpl#bar: " + str);
+ }
+
+ // TODO(narayan): Fix this case, we're using the wrong ArtMethod for the
+ // invoke resulting in a failing check in the interpreter.
+ //
+ // mh = MethodHandles.lookup().findVirtual(Bar.class, "bar",
+ // MethodType.methodType(String.class));
+ // str = (String) mh.invoke(new BarImpl());
+ // if (!"bar".equals(str)) {
+ // System.out.println("Unexpected return value for BarImpl#bar: " + str);
+ // }
+
+ // We should also be able to lookup public / protected / package methods in
+ // the super class, given sufficient access privileges.
+ mh = MethodHandles.lookup().findVirtual(BarImpl.class, "superPublicMethod",
+ MethodType.methodType(String.class));
+ str = (String) mh.invoke(new BarImpl());
+ if (!"superPublicMethod".equals(str)) {
+ System.out.println("Unexpected return value for BarImpl#superPublicMethod: " + str);
+ }
+
+ mh = MethodHandles.lookup().findVirtual(BarImpl.class, "superProtectedMethod",
+ MethodType.methodType(String.class));
+ str = (String) mh.invoke(new BarImpl());
+ if (!"superProtectedMethod".equals(str)) {
+ System.out.println("Unexpected return value for BarImpl#superProtectedMethod: " + str);
+ }
+
+ mh = MethodHandles.lookup().findVirtual(BarImpl.class, "superPackageMethod",
+ MethodType.methodType(String.class));
+ str = (String) mh.invoke(new BarImpl());
+ if (!"superPackageMethod".equals(str)) {
+ System.out.println("Unexpected return value for BarImpl#superPackageMethod: " + str);
+ }
+ }
}
diff --git a/test/957-methodhandle-transforms/expected.txt b/test/957-methodhandle-transforms/expected.txt
index 73a34bc41b..fc4fdd6070 100644
--- a/test/957-methodhandle-transforms/expected.txt
+++ b/test/957-methodhandle-transforms/expected.txt
@@ -1,35 +1,16 @@
----
--- testDelegation
----
-boolean: false
-char: h
-short: 56
-int: 72
-long: 2147483689
-float: 0.56
-double: 100.0
-String: hello
-Object: goodbye
-boolean: false
-char: h
-short: 56
-int: 72
-long: 73
-float: 0.56
-double: 100.0
-String: hello
-Object: goodbye
-true
-true
-a
-a
-42
-42
-43
-43
-43.0
-43.0
-43.0
-43.0
-plank
-plank
+Message: foo, Message2: 42
+Message: foo, Message2: 42
+Message: foo, Message2: 42
+Message: foo, Message2: 42
+Message: foo, Message2: 42
+Message: foo, Message2: 42
+Target: Arg1: foo, Arg2: 42
+Target: Arg1: foo, Arg2: 42
+Handler: java.lang.IllegalArgumentException: exceptionMessage, Arg1: foo, Arg2: 42, ExMsg: exceptionMessage
+Handler: java.lang.IllegalArgumentException: exceptionMessage, Arg1: foo, Arg2: 42, ExMsg: exceptionMessage
+Handler: java.lang.IllegalArgumentException: exceptionMessage, Arg1: foo
+Handler: java.lang.IllegalArgumentException: exceptionMessage, Arg1: foo
+target: target, 42, 56
+target: target, 42, 56
+fallback: fallback, 42, 56
+target: target, 42, 56
diff --git a/test/957-methodhandle-transforms/src/Main.java b/test/957-methodhandle-transforms/src/Main.java
index e9d313b874..4a27086924 100644
--- a/test/957-methodhandle-transforms/src/Main.java
+++ b/test/957-methodhandle-transforms/src/Main.java
@@ -19,182 +19,229 @@ import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import java.lang.invoke.WrongMethodTypeException;
-import java.lang.invoke.Transformers.Transformer;
-
-import dalvik.system.EmulatedStackFrame;
public class Main {
-
- public static void testDelegate_allTypes(boolean z, char a, short b, int c, long d,
- float e, double f, String g, Object h) {
- System.out.println("boolean: " + z);
- System.out.println("char: " + a);
- System.out.println("short: " + b);
- System.out.println("int: " + c);
- System.out.println("long: " + d);
- System.out.println("float: " + e);
- System.out.println("double: " + f);
- System.out.println("String: " + g);
- System.out.println("Object: " + h);
- }
-
- public static boolean testDelegate_returnBoolean() {
- return true;
- }
-
- public static char testDelegate_returnChar() {
- return 'a';
- }
-
- public static int testDelegate_returnInt() {
- return 42;
+ public static void main(String[] args) throws Throwable {
+ testThrowException();
+ testDropArguments();
+ testCatchException();
+ testGuardWithTest();
}
- public static long testDelegate_returnLong() {
- return 43;
- }
+ public static void testThrowException() throws Throwable {
+ MethodHandle handle = MethodHandles.throwException(String.class,
+ IllegalArgumentException.class);
- public static float testDelegate_returnFloat() {
- return 43.0f;
- }
+ if (handle.type().returnType() != String.class) {
+ System.out.println("Unexpected return type for handle: " + handle +
+ " [ " + handle.type() + "]");
+ }
- public static double testDelegate_returnDouble() {
- return 43.0;
+ final IllegalArgumentException iae = new IllegalArgumentException("boo!");
+ try {
+ handle.invoke(iae);
+ System.out.println("Expected an exception of type: java.lang.IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ if (expected != iae) {
+ System.out.println("Wrong exception: expected " + iae + " but was " + expected);
+ }
+ }
}
- public static String testDelegate_returnString() {
- return "plank";
+ public static void dropArguments_delegate(String message, long message2) {
+ System.out.println("Message: " + message + ", Message2: " + message2);
}
- public static class DelegatingTransformer extends Transformer {
- private final MethodHandle delegate;
+ public static void testDropArguments() throws Throwable {
+ MethodHandle delegate = MethodHandles.lookup().findStatic(Main.class,
+ "dropArguments_delegate",
+ MethodType.methodType(void.class, new Class<?>[] { String.class, long.class }));
- public DelegatingTransformer(MethodHandle delegate) {
- super(delegate.type());
- this.delegate = delegate;
- }
+ MethodHandle transform = MethodHandles.dropArguments(delegate, 0, int.class, Object.class);
- @Override
- public void transform(EmulatedStackFrame stackFrame) throws Throwable {
- delegate.invoke(stackFrame);
+ // The transformer will accept two additional arguments at position zero.
+ try {
+ transform.invokeExact("foo", 42l);
+ fail();
+ } catch (WrongMethodTypeException expected) {
}
- }
-
- public static void main(String[] args) throws Throwable {
- testThrowException();
-
- testDelegation();
- }
- public static void testDelegation() throws Throwable {
- System.out.println("---");
- System.out.println("-- testDelegation");
- System.out.println("---");
+ transform.invokeExact(45, new Object(), "foo", 42l);
+ transform.invoke(45, new Object(), "foo", 42l);
- MethodHandle specialFunctionHandle = MethodHandles.lookup().findStatic(
- Main.class, "testDelegate_allTypes", MethodType.methodType(void.class,
- new Class<?>[] { boolean.class, char.class, short.class, int.class, long.class,
- float.class, double.class, String.class, Object.class }));
+ // Additional arguments at position 1.
+ transform = MethodHandles.dropArguments(delegate, 1, int.class, Object.class);
+ transform.invokeExact("foo", 45, new Object(), 42l);
+ transform.invoke("foo", 45, new Object(), 42l);
- DelegatingTransformer delegate = new DelegatingTransformer(specialFunctionHandle);
+ // Additional arguments at position 2.
+ transform = MethodHandles.dropArguments(delegate, 2, int.class, Object.class);
+ transform.invokeExact("foo", 42l, 45, new Object());
+ transform.invoke("foo", 42l, 45, new Object());
- // Test an exact invoke.
- //
- // Note that the shorter form below doesn't work and must be
- // investigated on the jack side : b/32536744
- //
- // delegate.invokeExact(false, 'h', (short) 56, 72, Integer.MAX_VALUE + 42l,
- // 0.56f, 100.0d, "hello", (Object) "goodbye");
+ // Note that we still perform argument conversions even for the arguments that
+ // are subsequently dropped.
+ try {
+ transform.invoke("foo", 42l, 45l, new Object());
+ fail();
+ } catch (WrongMethodTypeException expected) {
+ } catch (IllegalArgumentException expected) {
+ // TODO(narayan): We currently throw the wrong type of exception here,
+ // it's IAE and should be WMTE instead.
+ }
- Object obj = "goodbye";
- delegate.invokeExact(false, 'h', (short) 56, 72, Integer.MAX_VALUE + 42l,
- 0.56f, 100.0d, "hello", obj);
+ // Invalid argument location, should not be allowed.
+ try {
+ MethodHandles.dropArguments(delegate, -1, int.class, Object.class);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
- // Test a non exact invoke with one int -> long conversion and a float -> double
- // conversion.
- delegate.invoke(false, 'h', (short) 56, 72, 73,
- 0.56f, 100.0f, "hello", "goodbye");
+ // Invalid argument location, should not be allowed.
+ try {
+ MethodHandles.dropArguments(delegate, 3, int.class, Object.class);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
- // Should throw a WrongMethodTypeException if the types don't align.
try {
- delegate.invoke(false);
- throw new AssertionError("Call to invoke unexpectedly succeeded");
- } catch (WrongMethodTypeException expected) {
+ MethodHandles.dropArguments(delegate, 1, void.class);
+ fail();
+ } catch (IllegalArgumentException expected) {
}
+ }
- // Test return values.
+ public static String testCatchException_target(String arg1, long arg2, String exceptionMessage)
+ throws Throwable {
+ if (exceptionMessage != null) {
+ throw new IllegalArgumentException(exceptionMessage);
+ }
- // boolean.
- MethodHandle returner = MethodHandles.lookup().findStatic(
- Main.class, "testDelegate_returnBoolean", MethodType.methodType(boolean.class));
- delegate = new DelegatingTransformer(returner);
+ System.out.println("Target: Arg1: " + arg1 + ", Arg2: " + arg2);
+ return "target";
+ }
- System.out.println((boolean) delegate.invoke());
- System.out.println((boolean) delegate.invokeExact());
+ public static String testCatchException_handler(IllegalArgumentException iae, String arg1, long arg2,
+ String exMsg) {
+ System.out.println("Handler: " + iae + ", Arg1: " + arg1 + ", Arg2: " + arg2 + ", ExMsg: " + exMsg);
+ return "handler1";
+ }
- // char.
- returner = MethodHandles.lookup().findStatic(
- Main.class, "testDelegate_returnChar", MethodType.methodType(char.class));
- delegate = new DelegatingTransformer(returner);
+ public static String testCatchException_handler2(IllegalArgumentException iae, String arg1) {
+ System.out.println("Handler: " + iae + ", Arg1: " + arg1);
+ return "handler2";
+ }
- System.out.println((char) delegate.invoke());
- System.out.println((char) delegate.invokeExact());
+ public static void testCatchException() throws Throwable {
+ MethodHandle target = MethodHandles.lookup().findStatic(Main.class,
+ "testCatchException_target",
+ MethodType.methodType(String.class, new Class<?>[] { String.class, long.class, String.class }));
+
+ MethodHandle handler = MethodHandles.lookup().findStatic(Main.class,
+ "testCatchException_handler",
+ MethodType.methodType(String.class, new Class<?>[] { IllegalArgumentException.class,
+ String.class, long.class, String.class }));
+
+ MethodHandle adapter = MethodHandles.catchException(target, IllegalArgumentException.class,
+ handler);
+
+ String returnVal = null;
+
+ // These two should end up calling the target always. We're passing a null exception
+ // message here, which means the target will not throw.
+ returnVal = (String) adapter.invoke("foo", 42, null);
+ assertEquals("target", returnVal);
+ returnVal = (String) adapter.invokeExact("foo", 42l, (String) null);
+ assertEquals("target", returnVal);
+
+ // We're passing a non-null exception message here, which means the target will throw,
+ // which in turn means that the handler must be called for the next two invokes.
+ returnVal = (String) adapter.invoke("foo", 42, "exceptionMessage");
+ assertEquals("handler1", returnVal);
+ returnVal = (String) adapter.invokeExact("foo", 42l, "exceptionMessage");
+ assertEquals("handler1", returnVal);
+
+ handler = MethodHandles.lookup().findStatic(Main.class,
+ "testCatchException_handler2",
+ MethodType.methodType(String.class, new Class<?>[] { IllegalArgumentException.class,
+ String.class }));
+ adapter = MethodHandles.catchException(target, IllegalArgumentException.class, handler);
+
+ returnVal = (String) adapter.invoke("foo", 42, "exceptionMessage");
+ assertEquals("handler2", returnVal);
+ returnVal = (String) adapter.invokeExact("foo", 42l, "exceptionMessage");
+ assertEquals("handler2", returnVal);
+
+ // Test that the type of the invoke doesn't matter. Here we call
+ // IllegalArgumentException.toString() on the exception that was thrown by
+ // the target.
+ handler = MethodHandles.lookup().findVirtual(IllegalArgumentException.class,
+ "toString", MethodType.methodType(String.class));
+ adapter = MethodHandles.catchException(target, IllegalArgumentException.class, handler);
+
+ returnVal = (String) adapter.invoke("foo", 42, "exceptionMessage");
+ assertEquals("java.lang.IllegalArgumentException: exceptionMessage", returnVal);
+ returnVal = (String) adapter.invokeExact("foo", 42l, "exceptionMessage2");
+ assertEquals("java.lang.IllegalArgumentException: exceptionMessage2", returnVal);
+ }
- // int.
- returner = MethodHandles.lookup().findStatic(
- Main.class, "testDelegate_returnInt", MethodType.methodType(int.class));
- delegate = new DelegatingTransformer(returner);
+ public static boolean testGuardWithTest_test(String arg1, long arg2) {
+ return "target".equals(arg1) && 42 == arg2;
+ }
- System.out.println((int) delegate.invoke());
- System.out.println((int) delegate.invokeExact());
+ public static String testGuardWithTest_target(String arg1, long arg2, int arg3) {
+ System.out.println("target: " + arg1 + ", " + arg2 + ", " + arg3);
+ return "target";
+ }
- // long.
- returner = MethodHandles.lookup().findStatic(
- Main.class, "testDelegate_returnLong", MethodType.methodType(long.class));
- delegate = new DelegatingTransformer(returner);
+ public static String testGuardWithTest_fallback(String arg1, long arg2, int arg3) {
+ System.out.println("fallback: " + arg1 + ", " + arg2 + ", " + arg3);
+ return "fallback";
+ }
- System.out.println((long) delegate.invoke());
- System.out.println((long) delegate.invokeExact());
+ public static void testGuardWithTest() throws Throwable {
+ MethodHandle test = MethodHandles.lookup().findStatic(Main.class,
+ "testGuardWithTest_test",
+ MethodType.methodType(boolean.class, new Class<?>[] { String.class, long.class }));
- // float.
- returner = MethodHandles.lookup().findStatic(
- Main.class, "testDelegate_returnFloat", MethodType.methodType(float.class));
- delegate = new DelegatingTransformer(returner);
+ final MethodType type = MethodType.methodType(String.class,
+ new Class<?>[] { String.class, long.class, int.class });
- System.out.println((float) delegate.invoke());
- System.out.println((float) delegate.invokeExact());
+ final MethodHandle target = MethodHandles.lookup().findStatic(Main.class,
+ "testGuardWithTest_target", type);
+ final MethodHandle fallback = MethodHandles.lookup().findStatic(Main.class,
+ "testGuardWithTest_fallback", type);
- // double.
- returner = MethodHandles.lookup().findStatic(
- Main.class, "testDelegate_returnDouble", MethodType.methodType(double.class));
- delegate = new DelegatingTransformer(returner);
+ MethodHandle adapter = MethodHandles.guardWithTest(test, target, fallback);
- System.out.println((double) delegate.invoke());
- System.out.println((double) delegate.invokeExact());
+ String returnVal = null;
- // references.
- returner = MethodHandles.lookup().findStatic(
- Main.class, "testDelegate_returnString", MethodType.methodType(String.class));
- delegate = new DelegatingTransformer(returner);
+ returnVal = (String) adapter.invoke("target", 42, 56);
+ assertEquals("target", returnVal);
+ returnVal = (String) adapter.invokeExact("target", 42l, 56);
+ assertEquals("target", returnVal);
- System.out.println((String) delegate.invoke());
- System.out.println((String) delegate.invokeExact());
+ returnVal = (String) adapter.invoke("fallback", 42l, 56);
+ assertEquals("fallback", returnVal);
+ returnVal = (String) adapter.invokeExact("target", 42l, 56);
+ assertEquals("target", returnVal);
}
- public static void testThrowException() throws Throwable {
- MethodHandle handle = MethodHandles.throwException(String.class,
- IllegalArgumentException.class);
+ public static void fail() {
+ System.out.println("FAIL");
+ Thread.dumpStack();
+ }
- if (handle.type().returnType() != String.class) {
- System.out.println("Unexpected return type for handle: " + handle +
- " [ " + handle.type() + "]");
+ public static void assertEquals(String s1, String s2) {
+ if (s1 == s2) {
+ return;
}
- try {
- handle.invoke();
- System.out.println("Expected an exception of type: java.lang.IllegalArgumentException");
- } catch (IllegalArgumentException expected) {
+ if (s1 != null && s2 != null && s1.equals(s2)) {
+ return;
}
+
+ throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2);
}
}
diff --git a/test/958-methodhandle-emulated-stackframe/build b/test/958-methodhandle-emulated-stackframe/build
new file mode 100755
index 0000000000..a423ca6b4e
--- /dev/null
+++ b/test/958-methodhandle-emulated-stackframe/build
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# make us exit on a failure
+set -e
+
+if [[ $@ != *"--jvm"* ]]; then
+ # Don't do anything with jvm.
+ export USE_JACK=true
+fi
+
+./default-build "$@" --experimental method-handles
diff --git a/test/958-methodhandle-emulated-stackframe/expected.txt b/test/958-methodhandle-emulated-stackframe/expected.txt
new file mode 100644
index 0000000000..5f3825962d
--- /dev/null
+++ b/test/958-methodhandle-emulated-stackframe/expected.txt
@@ -0,0 +1,32 @@
+boolean: false
+char: h
+short: 56
+int: 72
+long: 2147483689
+float: 0.56
+double: 100.0
+String: hello
+Object: goodbye
+boolean: false
+char: h
+short: 56
+int: 72
+long: 73
+float: 0.56
+double: 100.0
+String: hello
+Object: goodbye
+true
+true
+a
+a
+42
+42
+43
+43
+43.0
+43.0
+43.0
+43.0
+plank
+plank
diff --git a/test/958-methodhandle-emulated-stackframe/info.txt b/test/958-methodhandle-emulated-stackframe/info.txt
new file mode 100644
index 0000000000..bec2324e76
--- /dev/null
+++ b/test/958-methodhandle-emulated-stackframe/info.txt
@@ -0,0 +1,5 @@
+Tests for dalvik.system.EmulatedStackFrame, which is used to implement
+MethodHandle transformations. This is a separate test because it tests
+an implementation detail and hence cannot be used with --mode=jvm.
+
+NOTE: needs to run under ART or a Java 8 Language runtime and compiler.
diff --git a/test/958-methodhandle-emulated-stackframe/run b/test/958-methodhandle-emulated-stackframe/run
new file mode 100755
index 0000000000..a9f182288c
--- /dev/null
+++ b/test/958-methodhandle-emulated-stackframe/run
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# make us exit on a failure
+set -e
+
+./default-run "$@" --experimental method-handles
diff --git a/test/958-methodhandle-emulated-stackframe/src/Main.java b/test/958-methodhandle-emulated-stackframe/src/Main.java
new file mode 100644
index 0000000000..f739d47d08
--- /dev/null
+++ b/test/958-methodhandle-emulated-stackframe/src/Main.java
@@ -0,0 +1,175 @@
+/*
+ * 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.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.invoke.MethodType;
+import java.lang.invoke.WrongMethodTypeException;
+import java.lang.invoke.Transformers.Transformer;
+
+import dalvik.system.EmulatedStackFrame;
+
+public class Main {
+
+ public static void testDelegate_allTypes(boolean z, char a, short b, int c, long d,
+ float e, double f, String g, Object h) {
+ System.out.println("boolean: " + z);
+ System.out.println("char: " + a);
+ System.out.println("short: " + b);
+ System.out.println("int: " + c);
+ System.out.println("long: " + d);
+ System.out.println("float: " + e);
+ System.out.println("double: " + f);
+ System.out.println("String: " + g);
+ System.out.println("Object: " + h);
+ }
+
+ public static boolean testDelegate_returnBoolean() {
+ return true;
+ }
+
+ public static char testDelegate_returnChar() {
+ return 'a';
+ }
+
+ public static int testDelegate_returnInt() {
+ return 42;
+ }
+
+ public static long testDelegate_returnLong() {
+ return 43;
+ }
+
+ public static float testDelegate_returnFloat() {
+ return 43.0f;
+ }
+
+ public static double testDelegate_returnDouble() {
+ return 43.0;
+ }
+
+ public static String testDelegate_returnString() {
+ return "plank";
+ }
+
+ public static class DelegatingTransformer extends Transformer {
+ private final MethodHandle delegate;
+
+ public DelegatingTransformer(MethodHandle delegate) {
+ super(delegate.type());
+ this.delegate = delegate;
+ }
+
+ @Override
+ public void transform(EmulatedStackFrame stackFrame) throws Throwable {
+ delegate.invoke(stackFrame);
+ }
+ }
+
+ public static void main(String[] args) throws Throwable {
+ MethodHandle specialFunctionHandle = MethodHandles.lookup().findStatic(
+ Main.class, "testDelegate_allTypes", MethodType.methodType(void.class,
+ new Class<?>[] { boolean.class, char.class, short.class, int.class, long.class,
+ float.class, double.class, String.class, Object.class }));
+
+ DelegatingTransformer delegate = new DelegatingTransformer(specialFunctionHandle);
+
+ // Test an exact invoke.
+ //
+ // Note that the shorter form below doesn't work and must be
+ // investigated on the jack side : b/32536744
+ //
+ // delegate.invokeExact(false, 'h', (short) 56, 72, Integer.MAX_VALUE + 42l,
+ // 0.56f, 100.0d, "hello", (Object) "goodbye");
+
+ Object obj = "goodbye";
+ delegate.invokeExact(false, 'h', (short) 56, 72, Integer.MAX_VALUE + 42l,
+ 0.56f, 100.0d, "hello", obj);
+
+ // Test a non exact invoke with one int -> long conversion and a float -> double
+ // conversion.
+ delegate.invoke(false, 'h', (short) 56, 72, 73,
+ 0.56f, 100.0f, "hello", "goodbye");
+
+ // Should throw a WrongMethodTypeException if the types don't align.
+ try {
+ delegate.invoke(false);
+ throw new AssertionError("Call to invoke unexpectedly succeeded");
+ } catch (WrongMethodTypeException expected) {
+ }
+
+ // Test return values.
+
+ // boolean.
+ MethodHandle returner = MethodHandles.lookup().findStatic(
+ Main.class, "testDelegate_returnBoolean", MethodType.methodType(boolean.class));
+ delegate = new DelegatingTransformer(returner);
+
+ System.out.println((boolean) delegate.invoke());
+ System.out.println((boolean) delegate.invokeExact());
+
+ // char.
+ returner = MethodHandles.lookup().findStatic(
+ Main.class, "testDelegate_returnChar", MethodType.methodType(char.class));
+ delegate = new DelegatingTransformer(returner);
+
+ System.out.println((char) delegate.invoke());
+ System.out.println((char) delegate.invokeExact());
+
+ // int.
+ returner = MethodHandles.lookup().findStatic(
+ Main.class, "testDelegate_returnInt", MethodType.methodType(int.class));
+ delegate = new DelegatingTransformer(returner);
+
+ System.out.println((int) delegate.invoke());
+ System.out.println((int) delegate.invokeExact());
+
+ // long.
+ returner = MethodHandles.lookup().findStatic(
+ Main.class, "testDelegate_returnLong", MethodType.methodType(long.class));
+ delegate = new DelegatingTransformer(returner);
+
+ System.out.println((long) delegate.invoke());
+ System.out.println((long) delegate.invokeExact());
+
+ // float.
+ returner = MethodHandles.lookup().findStatic(
+ Main.class, "testDelegate_returnFloat", MethodType.methodType(float.class));
+ delegate = new DelegatingTransformer(returner);
+
+ System.out.println((float) delegate.invoke());
+ System.out.println((float) delegate.invokeExact());
+
+ // double.
+ returner = MethodHandles.lookup().findStatic(
+ Main.class, "testDelegate_returnDouble", MethodType.methodType(double.class));
+ delegate = new DelegatingTransformer(returner);
+
+ System.out.println((double) delegate.invoke());
+ System.out.println((double) delegate.invokeExact());
+
+ // references.
+ returner = MethodHandles.lookup().findStatic(
+ Main.class, "testDelegate_returnString", MethodType.methodType(String.class));
+ delegate = new DelegatingTransformer(returner);
+
+ System.out.println((String) delegate.invoke());
+ System.out.println((String) delegate.invokeExact());
+ }
+}
+
+
diff --git a/test/Android.bp b/test/Android.bp
index 37e1fc0dd7..bdb7f80eb0 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -255,6 +255,8 @@ art_cc_defaults {
"909-attach-agent/attach.cc",
"910-methods/methods.cc",
"911-get-stack-trace/stack_trace.cc",
+ "912-classes/classes.cc",
+ "913-heaps/heaps.cc",
],
shared_libs: [
"libbase",
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index cfb05f930e..ae569f91a3 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -278,6 +278,8 @@ TEST_ART_BROKEN_TARGET_TESTS += \
909-attach-agent \
910-methods \
911-get-stack-trace \
+ 912-classes \
+ 913-heaps \
ifneq (,$(filter target,$(TARGET_TYPES)))
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -373,13 +375,14 @@ TEST_ART_BROKEN_INTERPRETER_ACCESS_CHECK_TESTS :=
# * 137-cfi needs to unwind a second forked process. We're using a primitive sleep to wait till we
# hope the second process got into the expected state. The slowness of gcstress makes this bad.
# * 908-gc-start-finish expects GCs only to be run at clear points. The reduced heap size makes
-# this non-deterministic.
+# this non-deterministic. Same for 913.
# * 961-default-iface-resolution-gen and 964-default-iface-init-genare very long tests that often
# will take more than the timeout to run when gcstress is enabled. This is because gcstress
# slows down allocations significantly which these tests do a lot.
TEST_ART_BROKEN_GCSTRESS_RUN_TESTS := \
137-cfi \
908-gc-start-finish \
+ 913-heaps \
961-default-iface-resolution-gen \
964-default-iface-init-gen \
diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc
index 42481483f4..a2eb370326 100644
--- a/test/common/runtime_state.cc
+++ b/test/common/runtime_state.cc
@@ -128,9 +128,10 @@ extern "C" JNIEXPORT void JNICALL Java_Main_ensureJitCompiled(JNIEnv* env,
return;
}
+ Thread* self = Thread::Current();
ArtMethod* method = nullptr;
{
- ScopedObjectAccess soa(Thread::Current());
+ ScopedObjectAccess soa(self);
ScopedUtfChars chars(env, method_name);
CHECK(chars.c_str() != nullptr);
@@ -147,11 +148,11 @@ extern "C" JNIEXPORT void JNICALL Java_Main_ensureJitCompiled(JNIEnv* env,
} else {
// Sleep to yield to the compiler thread.
usleep(1000);
- ScopedObjectAccess soa(Thread::Current());
+ ScopedObjectAccess soa(self);
// Make sure there is a profiling info, required by the compiler.
- ProfilingInfo::Create(soa.Self(), method, /* retry_allocation */ true);
+ ProfilingInfo::Create(self, method, /* retry_allocation */ true);
// Will either ensure it's compiled or do the compilation itself.
- jit->CompileMethod(method, soa.Self(), /* osr */ false);
+ jit->CompileMethod(method, self, /* osr */ false);
}
}
}
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index 3535f323d0..e769812ebd 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -148,7 +148,7 @@ while true; do
SECONDARY_DEX=":$DEX_LOCATION/$TEST_NAME-ex.jar"
# Enable cfg-append to make sure we get the dump for both dex files.
# (otherwise the runtime compilation of the secondary dex will overwrite
- # the dump of the first one)
+ # the dump of the first one).
FLAGS="${FLAGS} -Xcompiler-option --dump-cfg-append"
COMPILE_FLAGS="${COMPILE_FLAGS} --dump-cfg-append"
shift
@@ -364,8 +364,8 @@ fi
if [ "$JIT" = "y" ]; then
INT_OPTS="-Xusejit:true"
if [ "$VERIFY" = "y" ] ; then
- INT_OPTS="${INT_OPTS} -Xcompiler-option --compiler-filter=verify-at-runtime"
- COMPILE_FLAGS="${COMPILE_FLAGS} --compiler-filter=verify-at-runtime"
+ INT_OPTS="${INT_OPTS} -Xcompiler-option --compiler-filter=interpret-only"
+ COMPILE_FLAGS="${COMPILE_FLAGS} --compiler-filter=interpret-only"
else
INT_OPTS="${INT_OPTS} -Xcompiler-option --compiler-filter=verify-none"
COMPILE_FLAGS="${COMPILE_FLAGS} --compiler-filter=verify-none"
diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc
index 7faf700b6e..a959482772 100644
--- a/test/ti-agent/common_load.cc
+++ b/test/ti-agent/common_load.cc
@@ -35,6 +35,8 @@
#include "909-attach-agent/attach.h"
#include "910-methods/methods.h"
#include "911-get-stack-trace/stack_trace.h"
+#include "912-classes/classes.h"
+#include "913-heaps/heaps.h"
namespace art {
@@ -62,6 +64,8 @@ AgentLib agents[] = {
{ "909-attach-agent", nullptr, Test909AttachAgent::OnAttach },
{ "910-methods", Test910Methods::OnLoad, nullptr },
{ "911-get-stack-trace", Test911GetStackTrace::OnLoad, nullptr },
+ { "912-classes", Test912Classes::OnLoad, nullptr },
+ { "913-heaps", Test913Heaps::OnLoad, nullptr },
};
static AgentLib* FindAgent(char* name) {
diff --git a/tools/cpp-define-generator/constant_class.def b/tools/cpp-define-generator/constant_class.def
index 58372f91d3..f46cd339b0 100644
--- a/tools/cpp-define-generator/constant_class.def
+++ b/tools/cpp-define-generator/constant_class.def
@@ -25,6 +25,7 @@
DEFINE_FLAG_OFFSET(MIRROR_CLASS, STATUS_INITIALIZED, art::mirror::Class::kStatusInitialized)
DEFINE_FLAG_OFFSET(ACCESS_FLAGS, CLASS_IS_FINALIZABLE, art::kAccClassIsFinalizable)
+DEFINE_FLAG_OFFSET(ACCESS_FLAGS, CLASS_IS_INTERFACE, art::kAccInterface)
// TODO: We should really have a BitPosition which also checks it's a power of 2.
DEFINE_FLAG_OFFSET(ACCESS_FLAGS, CLASS_IS_FINALIZABLE_BIT, art::MostSignificantBit(art::kAccClassIsFinalizable))
diff --git a/tools/cpp-define-generator/generate-asm-support b/tools/cpp-define-generator/generate-asm-support
index f95648b2b7..fcdf72fda9 100755
--- a/tools/cpp-define-generator/generate-asm-support
+++ b/tools/cpp-define-generator/generate-asm-support
@@ -5,4 +5,4 @@
[[ -z ${ANDROID_BUILD_TOP+x} ]] && (echo "Run source build/envsetup.sh first" >&2 && exit 1)
-cpp-define-generator-datad > ${ANDROID_BUILD_TOP}/art/runtime/generated/asm_support_gen.h
+cpp-define-generator-data > ${ANDROID_BUILD_TOP}/art/runtime/generated/asm_support_gen.h