diff options
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 |