diff options
39 files changed, 1052 insertions, 197 deletions
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index c87abe5664..b6ffcc54f7 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -30,6 +30,10 @@ GTEST_DEX_DIRECTORIES := \ ErroneousA \ ErroneousB \ ErroneousInit \ + ForClassLoaderA \ + ForClassLoaderB \ + ForClassLoaderC \ + ForClassLoaderD \ ExceptionHandle \ GetMethodSignature \ ImageLayoutA \ @@ -99,7 +103,7 @@ $(ART_TEST_TARGET_GTEST_VerifierDepsMulti_DEX): $(ART_TEST_GTEST_VerifierDepsMul ART_GTEST_dex2oat_environment_tests_DEX_DEPS := Main MainStripped MultiDex MultiDexModifiedSecondary Nested ART_GTEST_atomic_method_ref_map_test_DEX_DEPS := Interfaces -ART_GTEST_class_linker_test_DEX_DEPS := AllFields ErroneousA ErroneousB ErroneousInit Interfaces MethodTypes MultiDex MyClass Nested Statics StaticsFromCode +ART_GTEST_class_linker_test_DEX_DEPS := AllFields ErroneousA ErroneousB ErroneousInit ForClassLoaderA ForClassLoaderB ForClassLoaderC ForClassLoaderD Interfaces MethodTypes MultiDex MyClass Nested Statics StaticsFromCode ART_GTEST_class_table_test_DEX_DEPS := XandY ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod StaticLeafMethods ProfileTestMultiDex ART_GTEST_dex_cache_test_DEX_DEPS := Main Packages MethodTypes diff --git a/compiler/Android.bp b/compiler/Android.bp index a1269dcaf9..62226dab4d 100644 --- a/compiler/Android.bp +++ b/compiler/Android.bp @@ -429,13 +429,20 @@ art_cc_test { shared_libs: [ "libartd-compiler", - "libartd-simulator", "libvixld-arm", "libvixld-arm64", "libbacktrace", "libnativeloader", ], + + target: { + host: { + shared_libs: [ + "libartd-simulator", + ], + }, + }, } art_cc_test { diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h index a04349e392..db95bd6e03 100644 --- a/compiler/driver/compiler_driver-inl.h +++ b/compiler/driver/compiler_driver-inl.h @@ -103,16 +103,16 @@ inline std::pair<bool, bool> CompilerDriver::IsFastInstanceField( } inline ArtMethod* CompilerDriver::ResolveMethod( - ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache, - Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit, - uint32_t method_idx, InvokeType invoke_type, bool check_incompatible_class_change) { + ScopedObjectAccess& soa, + Handle<mirror::DexCache> dex_cache, + Handle<mirror::ClassLoader> class_loader, + const DexCompilationUnit* mUnit, + uint32_t method_idx, + InvokeType invoke_type) { DCHECK_EQ(class_loader.Get(), mUnit->GetClassLoader().Get()); ArtMethod* resolved_method = - check_incompatible_class_change - ? mUnit->GetClassLinker()->ResolveMethod<ClassLinker::kForceICCECheck>( - *dex_cache->GetDexFile(), method_idx, dex_cache, class_loader, nullptr, invoke_type) - : mUnit->GetClassLinker()->ResolveMethod<ClassLinker::kNoICCECheckForCache>( - *dex_cache->GetDexFile(), method_idx, dex_cache, class_loader, nullptr, invoke_type); + mUnit->GetClassLinker()->ResolveMethod<ClassLinker::kForceICCECheck>( + *dex_cache->GetDexFile(), method_idx, dex_cache, class_loader, nullptr, invoke_type); if (UNLIKELY(resolved_method == nullptr)) { DCHECK(soa.Self()->IsExceptionPending()); // Clean up any exception left by type resolution. diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index f834f30b84..bb64755c9e 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -87,6 +87,10 @@ static constexpr bool kTimeCompileMethod = !kIsDebugBuild; // Print additional info during profile guided compilation. static constexpr bool kDebugProfileGuidedCompilation = false; +// Max encoded fields allowed for initializing app image. Hardcode the number for now +// because 5000 should be large enough. +static constexpr uint32_t kMaxEncodedFields = 5000; + static double Percentage(size_t x, size_t y) { return 100.0 * (static_cast<double>(x)) / (static_cast<double>(x + y)); } @@ -2273,11 +2277,17 @@ class InitializeClassVisitor : public CompilationVisitor { } // Otherwise it's in app image but superclasses can't be initialized, no need to proceed. old_status = klass->GetStatus(); + + bool too_many_encoded_fields = false; + if (!is_boot_image && klass->NumStaticFields() > kMaxEncodedFields) { + too_many_encoded_fields = true; + } // If the class was not initialized, we can proceed to see if we can initialize static - // fields. + // fields. Limit the max number of encoded fields. if (!klass->IsInitialized() && (is_app_image || is_boot_image) && is_superclass_initialized && + !too_many_encoded_fields && manager_->GetCompiler()->IsImageClass(descriptor)) { bool can_init_static_fields = false; if (is_boot_image) { @@ -2415,30 +2425,6 @@ class InitializeClassVisitor : public CompilationVisitor { } } - bool NoPotentialInternStrings(Handle<mirror::Class> klass, - Handle<mirror::ClassLoader>* class_loader) - REQUIRES_SHARED(Locks::mutator_lock_) { - StackHandleScope<1> hs(Thread::Current()); - Handle<mirror::DexCache> h_dex_cache = hs.NewHandle(klass->GetDexCache()); - const DexFile* dex_file = h_dex_cache->GetDexFile(); - const DexFile::ClassDef* class_def = klass->GetClassDef(); - annotations::RuntimeEncodedStaticFieldValueIterator value_it(*dex_file, - &h_dex_cache, - class_loader, - manager_->GetClassLinker(), - *class_def); - - const auto jString = annotations::RuntimeEncodedStaticFieldValueIterator::kString; - for ( ; value_it.HasNext(); value_it.Next()) { - if (value_it.GetValueType() == jString) { - // We don't want cache the static encoded strings which is a potential intern. - return false; - } - } - - return true; - } - bool ResolveTypesOfMethods(Thread* self, ArtMethod* m) REQUIRES_SHARED(Locks::mutator_lock_) { auto rtn_type = m->GetReturnType(true); // return value is discarded because resolve will be done internally. @@ -2568,7 +2554,7 @@ class InitializeClassVisitor : public CompilationVisitor { } } - return NoPotentialInternStrings(klass, class_loader); + return true; } const ParallelCompilationManager* const manager_; diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index cd4f400ff4..e9e73787e4 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -254,9 +254,12 @@ class CompilerDriver { // Resolve a method. Returns null on failure, including incompatible class change. ArtMethod* ResolveMethod( - ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache, - Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit, - uint32_t method_idx, InvokeType invoke_type, bool check_incompatible_class_change = true) + ScopedObjectAccess& soa, + Handle<mirror::DexCache> dex_cache, + Handle<mirror::ClassLoader> class_loader, + const DexCompilationUnit* mUnit, + uint32_t method_idx, + InvokeType invoke_type) REQUIRES_SHARED(Locks::mutator_lock_); void ProcessedInstanceField(bool resolved); diff --git a/dexlayout/dexlayout.h b/dexlayout/dexlayout.h index 531bc98a0c..ed011d6771 100644 --- a/dexlayout/dexlayout.h +++ b/dexlayout/dexlayout.h @@ -58,7 +58,8 @@ class Options { bool show_section_headers_ = false; bool show_section_statistics_ = false; bool verbose_ = false; - bool verify_output_ = false; + // TODO: Set verify_output_ back to false by default. Was set to true for debugging b/62840842. + bool verify_output_ = true; bool visualize_pattern_ = false; OutputFormat output_format_ = kOutputPlain; const char* output_dex_directory_ = nullptr; diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 7aab9de1ed..a19085f5b5 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -55,6 +55,7 @@ #include "gc_root-inl.h" #include "gc/accounting/card_table-inl.h" #include "gc/accounting/heap_bitmap-inl.h" +#include "gc/accounting/space_bitmap-inl.h" #include "gc/heap.h" #include "gc/scoped_gc_critical_section.h" #include "gc/space/image_space.h" @@ -88,6 +89,7 @@ #include "mirror/method_handles_lookup.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" +#include "mirror/object-refvisitor-inl.h" #include "mirror/proxy.h" #include "mirror/reference-inl.h" #include "mirror/stack_trace_element.h" @@ -1193,6 +1195,63 @@ class VerifyDeclaringClassVisitor : public ArtMethodVisitor { gc::accounting::HeapBitmap* const live_bitmap_; }; +class FixupInternVisitor { + public: + ALWAYS_INLINE ObjPtr<mirror::Object> TryInsertIntern(mirror::Object* obj) const + REQUIRES_SHARED(Locks::mutator_lock_) { + if (obj != nullptr && obj->IsString()) { + const auto intern = Runtime::Current()->GetInternTable()->InternStrong(obj->AsString()); + return intern; + } + return obj; + } + + ALWAYS_INLINE void VisitRootIfNonNull( + mirror::CompressedReference<mirror::Object>* root) const + REQUIRES_SHARED(Locks::mutator_lock_) { + if (!root->IsNull()) { + VisitRoot(root); + } + } + + ALWAYS_INLINE void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const + REQUIRES_SHARED(Locks::mutator_lock_) { + root->Assign(TryInsertIntern(root->AsMirrorPtr())); + } + + // Visit Class Fields + ALWAYS_INLINE void operator()(ObjPtr<mirror::Object> obj, + MemberOffset offset, + bool is_static ATTRIBUTE_UNUSED) const + REQUIRES_SHARED(Locks::mutator_lock_) { + // There could be overlap between ranges, we must avoid visiting the same reference twice. + // Avoid the class field since we already fixed it up in FixupClassVisitor. + if (offset.Uint32Value() != mirror::Object::ClassOffset().Uint32Value()) { + // Updating images, don't do a read barrier. + // Only string fields are fixed, don't do a verify. + mirror::Object* ref = obj->GetFieldObject<mirror::Object, kVerifyNone, kWithoutReadBarrier>( + offset); + obj->SetFieldObject<false, false>(offset, TryInsertIntern(ref)); + } + } + + void operator()(ObjPtr<mirror::Class> klass ATTRIBUTE_UNUSED, + ObjPtr<mirror::Reference> ref) const + REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_) { + this->operator()(ref, mirror::Reference::ReferentOffset(), false); + } + + void operator()(mirror::Object* obj) const + REQUIRES_SHARED(Locks::mutator_lock_) { + if (obj->IsDexCache()) { + obj->VisitReferences<true, kVerifyNone, kWithoutReadBarrier>(*this, *this); + } else { + // Don't visit native roots for non-dex-cache + obj->VisitReferences<false, kVerifyNone, kWithoutReadBarrier>(*this, *this); + } + } +}; + // Copies data from one array to another array at the same position // if pred returns false. If there is a page of continuous data in // the src array for which pred consistently returns true then @@ -1285,6 +1344,7 @@ bool AppImageClassLoadersAndDexCachesHelper::Update( return false; } } + // Only add the classes to the class loader after the points where we can return false. for (size_t i = 0; i < num_dex_caches; i++) { ObjPtr<mirror::DexCache> dex_cache = dex_caches->Get(i); @@ -1448,6 +1508,21 @@ bool AppImageClassLoadersAndDexCachesHelper::Update( } } } + { + // Fixup all the literal strings happens at app images which are supposed to be interned. + ScopedTrace timing("Fixup String Intern in image and dex_cache"); + const auto& image_header = space->GetImageHeader(); + const auto bitmap = space->GetMarkBitmap(); // bitmap of objects + const uint8_t* target_base = space->GetMemMap()->Begin(); + const ImageSection& objects_section = + image_header.GetImageSection(ImageHeader::kSectionObjects); + + uintptr_t objects_begin = reinterpret_cast<uintptr_t>(target_base + objects_section.Offset()); + uintptr_t objects_end = reinterpret_cast<uintptr_t>(target_base + objects_section.End()); + + FixupInternVisitor fixup_intern_visitor; + bitmap->VisitMarkedRange(objects_begin, objects_end, fixup_intern_visitor); + } if (*out_forward_dex_cache_array) { ScopedTrace timing("Fixup ArtMethod dex cache arrays"); FixupArtMethodArrayVisitor visitor(header); @@ -2410,74 +2485,121 @@ ClassPathEntry FindInClassPath(const char* descriptor, return ClassPathEntry(nullptr, nullptr); } +// Returns true if the given class loader is either a PathClassLoader or a DexClassLoader. +// (they both have the same behaviour with respect to class lockup order) +static bool IsPathOrDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa, + Handle<mirror::ClassLoader> class_loader) + REQUIRES_SHARED(Locks::mutator_lock_) { + mirror::Class* class_loader_class = class_loader->GetClass(); + return + (class_loader_class == + soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader)) || + (class_loader_class == + soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DexClassLoader)); +} + +static bool IsDelegateLastClassLoader(ScopedObjectAccessAlreadyRunnable& soa, + Handle<mirror::ClassLoader> class_loader) + REQUIRES_SHARED(Locks::mutator_lock_) { + mirror::Class* class_loader_class = class_loader->GetClass(); + return class_loader_class == + soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DelegateLastClassLoader); +} + bool ClassLinker::FindClassInBaseDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa, Thread* self, const char* descriptor, size_t hash, Handle<mirror::ClassLoader> class_loader, ObjPtr<mirror::Class>* result) { - // Termination case: boot class-loader. + // Termination case: boot class loader. if (IsBootClassLoader(soa, class_loader.Get())) { - // The boot class loader, search the boot class path. - ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_); - if (pair.second != nullptr) { - ObjPtr<mirror::Class> klass = LookupClass(self, descriptor, hash, nullptr); - if (klass != nullptr) { - *result = EnsureResolved(self, descriptor, klass); - } else { - *result = DefineClass(self, - descriptor, - hash, - ScopedNullHandle<mirror::ClassLoader>(), - *pair.first, - *pair.second); - } - if (*result == nullptr) { - CHECK(self->IsExceptionPending()) << descriptor; - self->ClearException(); - } - } else { - *result = nullptr; - } + *result = FindClassInBootClassLoaderClassPath(self, descriptor, hash); return true; } - // Unsupported class-loader? - if (soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader) != - class_loader->GetClass()) { - // PathClassLoader is the most common case, so it's the one we check first. For secondary dex - // files, we also check DexClassLoader here. - if (soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DexClassLoader) != - class_loader->GetClass()) { - *result = nullptr; - return false; + if (IsPathOrDexClassLoader(soa, class_loader)) { + // For regular path or dex class loader the search order is: + // - parent + // - class loader dex files + + // Handles as RegisterDexFile may allocate dex caches (and cause thread suspension). + StackHandleScope<1> hs(self); + Handle<mirror::ClassLoader> h_parent(hs.NewHandle(class_loader->GetParent())); + if (!FindClassInBaseDexClassLoader(soa, self, descriptor, hash, h_parent, result)) { + return false; // One of the parents is not supported. + } + if (*result != nullptr) { + return true; // Found the class up the chain. } + + // Search the current class loader classpath. + *result = FindClassInBaseDexClassLoaderClassPath(soa, descriptor, hash, class_loader); + return true; } - // Handles as RegisterDexFile may allocate dex caches (and cause thread suspension). - StackHandleScope<4> hs(self); - Handle<mirror::ClassLoader> h_parent(hs.NewHandle(class_loader->GetParent())); - bool recursive_result = FindClassInBaseDexClassLoader(soa, - self, - descriptor, - hash, - h_parent, - result); + if (IsDelegateLastClassLoader(soa, class_loader)) { + // For delegate last, the search order is: + // - boot class path + // - class loader dex files + // - parent + *result = FindClassInBootClassLoaderClassPath(self, descriptor, hash); + if (*result != nullptr) { + return true; // The class is part of the boot class path. + } - if (!recursive_result) { - // Something wrong up the chain. - return false; + *result = FindClassInBaseDexClassLoaderClassPath(soa, descriptor, hash, class_loader); + if (*result != nullptr) { + return true; // Found the class in the current class loader + } + + // Handles as RegisterDexFile may allocate dex caches (and cause thread suspension). + StackHandleScope<1> hs(self); + Handle<mirror::ClassLoader> h_parent(hs.NewHandle(class_loader->GetParent())); + return FindClassInBaseDexClassLoader(soa, self, descriptor, hash, h_parent, result); } - if (*result != nullptr) { - // Found the class up the chain. - return true; + // Unsupported class loader. + *result = nullptr; + return false; +} + +// Finds the class in the boot class loader. +// If the class is found the method returns the resolved class. Otherwise it returns null. +ObjPtr<mirror::Class> ClassLinker::FindClassInBootClassLoaderClassPath(Thread* self, + const char* descriptor, + size_t hash) { + ObjPtr<mirror::Class> result = nullptr; + ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_); + if (pair.second != nullptr) { + ObjPtr<mirror::Class> klass = LookupClass(self, descriptor, hash, nullptr); + if (klass != nullptr) { + result = EnsureResolved(self, descriptor, klass); + } else { + result = DefineClass(self, + descriptor, + hash, + ScopedNullHandle<mirror::ClassLoader>(), + *pair.first, + *pair.second); + } + if (result == nullptr) { + CHECK(self->IsExceptionPending()) << descriptor; + self->ClearException(); + } } + return result; +} + +ObjPtr<mirror::Class> ClassLinker::FindClassInBaseDexClassLoaderClassPath( + ScopedObjectAccessAlreadyRunnable& soa, + const char* descriptor, + size_t hash, + Handle<mirror::ClassLoader> class_loader) { + CHECK(IsPathOrDexClassLoader(soa, class_loader) || IsDelegateLastClassLoader(soa, class_loader)) + << "Unexpected class loader for descriptor " << descriptor; - // Handle this step. - // Handle as if this is the child PathClassLoader. - // The class loader is a PathClassLoader which inherits from BaseDexClassLoader. - // We need to get the DexPathList and loop through it. + Thread* self = soa.Self(); ArtField* const cookie_field = jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie); ArtField* const dex_file_field = @@ -2489,10 +2611,11 @@ bool ClassLinker::FindClassInBaseDexClassLoader(ScopedObjectAccessAlreadyRunnabl // DexPathList has an array dexElements of Elements[] which each contain a dex file. ObjPtr<mirror::Object> dex_elements_obj = jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList_dexElements)-> - GetObject(dex_path_list); + GetObject(dex_path_list); // Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look // at the mCookie which is a DexFile vector. if (dex_elements_obj != nullptr) { + StackHandleScope<1> hs(self); Handle<mirror::ObjectArray<mirror::Object>> dex_elements = hs.NewHandle(dex_elements_obj->AsObjectArray<mirror::Object>()); for (int32_t i = 0; i < dex_elements->GetLength(); ++i) { @@ -2518,19 +2641,18 @@ bool ClassLinker::FindClassInBaseDexClassLoader(ScopedObjectAccessAlreadyRunnabl OatDexFile::FindClassDef(*cp_dex_file, descriptor, hash); if (dex_class_def != nullptr) { ObjPtr<mirror::Class> klass = DefineClass(self, - descriptor, - hash, - class_loader, - *cp_dex_file, - *dex_class_def); + descriptor, + hash, + class_loader, + *cp_dex_file, + *dex_class_def); if (klass == nullptr) { CHECK(self->IsExceptionPending()) << descriptor; self->ClearException(); // TODO: Is it really right to break here, and not check the other dex files? - return true; + return nullptr; } - *result = klass; - return true; + return klass; } } } @@ -2538,9 +2660,7 @@ bool ClassLinker::FindClassInBaseDexClassLoader(ScopedObjectAccessAlreadyRunnabl } self->AssertNoPendingException(); } - - // Result is still null from the parent call, no need to set it again... - return true; + return nullptr; } mirror::Class* ClassLinker::FindClass(Thread* self, @@ -8640,8 +8760,15 @@ const char* ClassLinker::GetClassRootDescriptor(ClassRoot class_root) { return descriptor; } -jobject ClassLinker::CreatePathClassLoader(Thread* self, - const std::vector<const DexFile*>& dex_files) { +jobject ClassLinker::CreateWellKnownClassLoader(Thread* self, + const std::vector<const DexFile*>& dex_files, + jclass loader_class, + jobject parent_loader) { + CHECK(self->GetJniEnv()->IsSameObject(loader_class, + WellKnownClasses::dalvik_system_PathClassLoader) || + self->GetJniEnv()->IsSameObject(loader_class, + WellKnownClasses::dalvik_system_DelegateLastClassLoader)); + // SOAAlreadyRunnable is protected, and we need something to add a global reference. // We could move the jobject to the callers, but all call-sites do this... ScopedObjectAccessUnchecked soa(self); @@ -8677,8 +8804,8 @@ jobject ClassLinker::CreatePathClassLoader(Thread* self, for (const DexFile* dex_file : dex_files) { StackHandleScope<4> hs2(self); - // CreatePathClassLoader is only used by gtests. Index 0 of h_long_array is supposed to be the - // oat file but we can leave it null. + // CreateWellKnownClassLoader is only used by gtests and compiler. + // Index 0 of h_long_array is supposed to be the oat file but we can leave it null. Handle<mirror::LongArray> h_long_array = hs2.NewHandle(mirror::LongArray::Alloc( self, kDexFileIndexStart + 1)); @@ -8713,36 +8840,44 @@ jobject ClassLinker::CreatePathClassLoader(Thread* self, // Set elements. dex_elements_field->SetObject<false>(h_dex_path_list.Get(), h_dex_elements.Get()); - // Create PathClassLoader. - Handle<mirror::Class> h_path_class_class = hs.NewHandle( - soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader)); - Handle<mirror::Object> h_path_class_loader = hs.NewHandle( - h_path_class_class->AllocObject(self)); - DCHECK(h_path_class_loader != nullptr); + // Create the class loader.. + Handle<mirror::Class> h_loader_class = hs.NewHandle(soa.Decode<mirror::Class>(loader_class)); + Handle<mirror::Object> h_class_loader = hs.NewHandle(h_loader_class->AllocObject(self)); + DCHECK(h_class_loader != nullptr); // Set DexPathList. ArtField* path_list_field = jni::DecodeArtField(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()); + path_list_field->SetObject<false>(h_class_loader.Get(), h_dex_path_list.Get()); // Make a pretend boot-classpath. // TODO: Should we scan the image? ArtField* const parent_field = mirror::Class::FindField(self, - h_path_class_loader->GetClass(), + h_class_loader->GetClass(), "parent", "Ljava/lang/ClassLoader;"); DCHECK(parent_field != nullptr); - ObjPtr<mirror::Object> boot_cl = - soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader)->AllocObject(self); - parent_field->SetObject<false>(h_path_class_loader.Get(), boot_cl); + + ObjPtr<mirror::Object> parent = (parent_loader != nullptr) + ? soa.Decode<mirror::ClassLoader>(parent_loader) + : soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader)->AllocObject(self); + parent_field->SetObject<false>(h_class_loader.Get(), parent); // Make it a global ref and return. ScopedLocalRef<jobject> local_ref( - soa.Env(), soa.Env()->AddLocalReference<jobject>(h_path_class_loader.Get())); + soa.Env(), soa.Env()->AddLocalReference<jobject>(h_class_loader.Get())); return soa.Env()->NewGlobalRef(local_ref.get()); } +jobject ClassLinker::CreatePathClassLoader(Thread* self, + const std::vector<const DexFile*>& dex_files) { + return CreateWellKnownClassLoader(self, + dex_files, + WellKnownClasses::dalvik_system_PathClassLoader, + nullptr); +} + void ClassLinker::DropFindArrayClassCache() { std::fill_n(find_array_class_cache_, kFindArrayCacheSize, GcRoot<mirror::Class>(nullptr)); find_array_class_cache_next_victim_ = 0; diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 1e8125eb05..de1fefd20e 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -553,8 +553,24 @@ class ClassLinker { REQUIRES(!Locks::classlinker_classes_lock_) REQUIRES_SHARED(Locks::mutator_lock_); - // Creates a GlobalRef PathClassLoader that can be used to load classes from the given dex files. + // Creates a GlobalRef PathClassLoader or DelegateLastClassLoader (specified by loader_class) + // that can be used to load classes from the given dex files. The parent of the class loader + // will be set to `parent_loader`. If `parent_loader` is null the parent will be + // the boot class loader. + // If class_loader points to a different class than PathClassLoader or DelegateLastClassLoader + // this method will abort. // Note: the objects are not completely set up. Do not use this outside of tests and the compiler. + jobject CreateWellKnownClassLoader(Thread* self, + const std::vector<const DexFile*>& dex_files, + jclass loader_class, + jobject parent_loader) + REQUIRES_SHARED(Locks::mutator_lock_) + REQUIRES(!Locks::dex_lock_); + + // Calls CreateWellKnownClassLoader(self, + // dex_files, + // WellKnownClasses::dalvik_system_PathClassLoader, + // nullptr) jobject CreatePathClassLoader(Thread* self, const std::vector<const DexFile*>& dex_files) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_); @@ -819,6 +835,27 @@ class ClassLinker { REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_); + // Finds the class in the classpath of the given class loader. It only searches the class loader + // dex files and does not recurse into its parent. + // The method checks that the provided class loader is either a PathClassLoader or a + // DexClassLoader. + // If the class is found the method returns the resolved class. Otherwise it returns null. + ObjPtr<mirror::Class> FindClassInBaseDexClassLoaderClassPath( + ScopedObjectAccessAlreadyRunnable& soa, + const char* descriptor, + size_t hash, + Handle<mirror::ClassLoader> class_loader) + REQUIRES_SHARED(Locks::mutator_lock_) + REQUIRES(!Locks::dex_lock_); + + // Finds the class in the boot class loader. + // If the class is found the method returns the resolved class. Otherwise it returns null. + ObjPtr<mirror::Class> FindClassInBootClassLoaderClassPath(Thread* self, + const char* descriptor, + size_t hash) + REQUIRES_SHARED(Locks::mutator_lock_) + REQUIRES(!Locks::dex_lock_); + // Finds a class by its descriptor, returning NULL if it isn't wasn't loaded // by the given 'class_loader'. Uses the provided hash for the descriptor. mirror::Class* LookupClass(Thread* self, diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index 684a261cca..03cc6c59c4 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -1533,4 +1533,110 @@ TEST_F(ClassLinkerMethodHandlesTest, TestResolveMethodTypes) { ASSERT_TRUE(method1_type.Get() != method2_type.Get()); } +// Verify that ClassLinker's CreateWellknownClassLoader works as expected +// by creating a chain of class loaders with various dex files. +TEST_F(ClassLinkerTest, CreateWellKnownClassLoader) { + // LoadDexIn*ClassLoader methods already assert that the parent loader is the expected one. + // No need to check again. + jobject class_loader_a = LoadDexInPathClassLoader("MyClass", nullptr); + jobject class_loader_b = LoadDexInDelegateLastClassLoader("Nested", class_loader_a); + jobject class_loader_c = LoadDexInPathClassLoader("MultiDex", class_loader_b); + LoadDexInDelegateLastClassLoader("Interfaces", class_loader_c); +} + +class ClassLinkerClassLoaderTest : public ClassLinkerTest { + protected: + // Verifies that the class identified by the given descriptor is loaded with + // the expected_class_loader_obj when search from class_loader_to_search_obj. + // When expected_class_loader_obj is null the check will be done against BootClassLoader. + void VerifyClassResolution(const std::string& descriptor, + jobject class_loader_to_search_obj, + jobject expected_class_loader_obj, + bool should_find = true) { + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); + StackHandleScope<3> hs(self); + Handle<mirror::ClassLoader> class_loader_to_search( + hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader_to_search_obj))); + + Handle<mirror::Class> klass = hs.NewHandle( + class_linker_->FindClass(soa.Self(), descriptor.c_str(), class_loader_to_search)); + + if (!should_find) { + if (self->IsExceptionPending()) { + self->ClearException(); + } + ASSERT_TRUE(klass == nullptr); + } else if (expected_class_loader_obj == nullptr) { + ASSERT_TRUE(ClassLinker::IsBootClassLoader(soa, klass->GetClassLoader())); + } else { + ASSERT_TRUE(klass != nullptr) << descriptor; + Handle<mirror::ClassLoader> expected_class_loader( + hs.NewHandle(soa.Decode<mirror::ClassLoader>(expected_class_loader_obj))); + ASSERT_EQ(klass->GetClassLoader(), expected_class_loader.Get()); + } + } +}; + +TEST_F(ClassLinkerClassLoaderTest, CreatePathClassLoader) { + jobject class_loader_a = LoadDexInPathClassLoader("ForClassLoaderA", nullptr); + VerifyClassResolution("LDefinedInA;", class_loader_a, class_loader_a); + VerifyClassResolution("Ljava/lang/String;", class_loader_a, nullptr); + VerifyClassResolution("LDefinedInB;", class_loader_a, nullptr, /*should_find*/ false); +} + +TEST_F(ClassLinkerClassLoaderTest, CreateDelegateLastClassLoader) { + jobject class_loader_a = LoadDexInDelegateLastClassLoader("ForClassLoaderA", nullptr); + VerifyClassResolution("LDefinedInA;", class_loader_a, class_loader_a); + VerifyClassResolution("Ljava/lang/String;", class_loader_a, nullptr); + VerifyClassResolution("LDefinedInB;", class_loader_a, nullptr, /*should_find*/ false); +} + +TEST_F(ClassLinkerClassLoaderTest, CreateClassLoaderChain) { + // The chain is + // ClassLoaderA (PathClassLoader, defines: A, AB, AC, AD) + // ^ + // | + // ClassLoaderB (DelegateLastClassLoader, defines: B, AB, BC, BD) + // ^ + // | + // ClassLoaderC (PathClassLoader, defines: C, AC, BC, CD) + // ^ + // | + // ClassLoaderD (DelegateLastClassLoader, defines: D, AD, BD, CD) + + jobject class_loader_a = LoadDexInPathClassLoader("ForClassLoaderA", nullptr); + jobject class_loader_b = LoadDexInDelegateLastClassLoader("ForClassLoaderB", class_loader_a); + jobject class_loader_c = LoadDexInPathClassLoader("ForClassLoaderC", class_loader_b); + jobject class_loader_d = LoadDexInDelegateLastClassLoader("ForClassLoaderD", class_loader_c); + + // Verify exclusive classes (present in only one class loader). + VerifyClassResolution("LDefinedInD;", class_loader_d, class_loader_d); + VerifyClassResolution("LDefinedInC;", class_loader_d, class_loader_c); + VerifyClassResolution("LDefinedInB;", class_loader_d, class_loader_b); + VerifyClassResolution("LDefinedInA;", class_loader_d, class_loader_a); + + // Verify classes that are defined in multiple classloader. + + // Classes defined in B should be found in B even if they are defined in A or C because + // B is a DelegateLastClassLoader. + VerifyClassResolution("LDefinedInAB;", class_loader_d, class_loader_b); + VerifyClassResolution("LDefinedInABC;", class_loader_d, class_loader_b); + VerifyClassResolution("LDefinedInBC;", class_loader_d, class_loader_b); + + // Classes defined in D should be found in D even if they are defined in parent class loaders + // as well because D is a DelegateLastClassLoader. + VerifyClassResolution("LDefinedInAD;", class_loader_d, class_loader_d); + VerifyClassResolution("LDefinedInBD;", class_loader_d, class_loader_d); + VerifyClassResolution("LDefinedInCD;", class_loader_d, class_loader_d); + + + // Classes not defined in the DelegateLastClassLoaders (i.e. D or B) should be found + // in the top parent. + VerifyClassResolution("LDefinedInAC;", class_loader_d, class_loader_a); + + // Sanity check that we don't find an undefined class. + VerifyClassResolution("LNotDefined;", class_loader_d, nullptr, /*should_find*/ false); +} + } // namespace art diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc index 6441a44e6e..659c7e4950 100644 --- a/runtime/common_runtime_test.cc +++ b/runtime/common_runtime_test.cc @@ -680,19 +680,66 @@ jobject CommonRuntimeTestImpl::LoadMultiDex(const char* first_dex_name, } jobject CommonRuntimeTestImpl::LoadDex(const char* dex_name) { - std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles(dex_name); + jobject class_loader = LoadDexInPathClassLoader(dex_name, nullptr); + Thread::Current()->SetClassLoaderOverride(class_loader); + return class_loader; +} + +jobject CommonRuntimeTestImpl::LoadDexInWellKnownClassLoader(const std::string& dex_name, + jclass loader_class, + jobject parent_loader) { + std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles(dex_name.c_str()); std::vector<const DexFile*> class_path; CHECK_NE(0U, dex_files.size()); for (auto& dex_file : 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; + ScopedObjectAccess soa(self); + + jobject result = Runtime::Current()->GetClassLinker()->CreateWellKnownClassLoader( + self, + class_path, + loader_class, + parent_loader); + + { + // Verify we build the correct chain. + + ObjPtr<mirror::ClassLoader> actual_class_loader = soa.Decode<mirror::ClassLoader>(result); + // Verify that the result has the correct class. + CHECK_EQ(soa.Decode<mirror::Class>(loader_class), actual_class_loader->GetClass()); + // Verify that the parent is not null. The boot class loader will be set up as a + // proper object. + ObjPtr<mirror::ClassLoader> actual_parent(actual_class_loader->GetParent()); + CHECK(actual_parent != nullptr); + + if (parent_loader != nullptr) { + // We were given a parent. Verify that it's what we expect. + ObjPtr<mirror::ClassLoader> expected_parent = soa.Decode<mirror::ClassLoader>(parent_loader); + CHECK_EQ(expected_parent, actual_parent); + } else { + // No parent given. The parent must be the BootClassLoader. + CHECK(Runtime::Current()->GetClassLinker()->IsBootClassLoader(soa, actual_parent)); + } + } + + return result; +} + +jobject CommonRuntimeTestImpl::LoadDexInPathClassLoader(const std::string& dex_name, + jobject parent_loader) { + return LoadDexInWellKnownClassLoader(dex_name, + WellKnownClasses::dalvik_system_PathClassLoader, + parent_loader); +} + +jobject CommonRuntimeTestImpl::LoadDexInDelegateLastClassLoader(const std::string& dex_name, + jobject parent_loader) { + return LoadDexInWellKnownClassLoader(dex_name, + WellKnownClasses::dalvik_system_DelegateLastClassLoader, + parent_loader); } std::string CommonRuntimeTestImpl::GetCoreFileLocation(const char* suffix) { diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h index 3b3e6c5321..5893573bdd 100644 --- a/runtime/common_runtime_test.h +++ b/runtime/common_runtime_test.h @@ -134,10 +134,20 @@ class CommonRuntimeTestImpl { std::unique_ptr<const DexFile> OpenTestDexFile(const char* name); + // Loads the test dex file identified by the given dex_name into a PathClassLoader. + // Returns the created class loader. jobject LoadDex(const char* dex_name) REQUIRES_SHARED(Locks::mutator_lock_); + // Loads the test dex file identified by the given first_dex_name and second_dex_name + // into a PathClassLoader. Returns the created class loader. jobject LoadMultiDex(const char* first_dex_name, const char* second_dex_name) REQUIRES_SHARED(Locks::mutator_lock_); + jobject LoadDexInPathClassLoader(const std::string& dex_name, jobject parent_loader); + jobject LoadDexInDelegateLastClassLoader(const std::string& dex_name, jobject parent_loader); + jobject LoadDexInWellKnownClassLoader(const std::string& dex_name, + jclass loader_class, + jobject parent_loader); + std::string android_data_; std::string dalvik_cache_; diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h index 37734e8afb..6547299853 100644 --- a/runtime/entrypoints/entrypoint_utils-inl.h +++ b/runtime/entrypoints/entrypoint_utils-inl.h @@ -376,6 +376,7 @@ inline ArtField* FindFieldFromCode(uint32_t field_idx, mirror::Class* referring_class = referrer->GetDeclaringClass(); if (UNLIKELY(!referring_class->CheckResolvedFieldAccess(fields_class, resolved_field, + referrer->GetDexCache(), field_idx))) { DCHECK(self->IsExceptionPending()); // Throw exception and unwind. return nullptr; // Failure. @@ -461,9 +462,11 @@ inline ArtMethod* FindMethodFromCode(uint32_t method_idx, } else if (access_check) { mirror::Class* methods_class = resolved_method->GetDeclaringClass(); bool can_access_resolved_method = - referrer->GetDeclaringClass()->CheckResolvedMethodAccess<type>(methods_class, - resolved_method, - method_idx); + referrer->GetDeclaringClass()->CheckResolvedMethodAccess(methods_class, + resolved_method, + referrer->GetDexCache(), + method_idx, + type); if (UNLIKELY(!can_access_resolved_method)) { DCHECK(self->IsExceptionPending()); // Throw exception and unwind. return nullptr; // Failure. @@ -662,7 +665,7 @@ inline ArtField* FindFieldFast(uint32_t field_idx, ArtMethod* referrer, FindFiel return nullptr; } } - mirror::Class* referring_class = referrer->GetDeclaringClass(); + ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass(); if (UNLIKELY(!referring_class->CanAccess(fields_class) || !referring_class->CanAccessMember(fields_class, resolved_field->GetAccessFlags()) || (is_set && resolved_field->IsFinal() && (fields_class != referring_class)))) { @@ -677,18 +680,17 @@ inline ArtField* FindFieldFast(uint32_t field_idx, ArtMethod* referrer, FindFiel } // Fast path method resolution that can't throw exceptions. +template <InvokeType type, bool access_check> inline ArtMethod* FindMethodFast(uint32_t method_idx, ObjPtr<mirror::Object> this_object, - ArtMethod* referrer, - bool access_check, - InvokeType type) { + ArtMethod* referrer) { ScopedAssertNoThreadSuspension ants(__FUNCTION__); if (UNLIKELY(this_object == nullptr && type != kStatic)) { return nullptr; } - mirror::Class* referring_class = referrer->GetDeclaringClass(); - ArtMethod* resolved_method = - referrer->GetDexCache()->GetResolvedMethod(method_idx, kRuntimePointerSize); + ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass(); + ObjPtr<mirror::DexCache> dex_cache = referrer->GetDexCache(); + ArtMethod* resolved_method = dex_cache->GetResolvedMethod(method_idx, kRuntimePointerSize); if (UNLIKELY(resolved_method == nullptr)) { return nullptr; } @@ -698,7 +700,7 @@ inline ArtMethod* FindMethodFast(uint32_t method_idx, if (UNLIKELY(icce)) { return nullptr; } - mirror::Class* methods_class = resolved_method->GetDeclaringClass(); + ObjPtr<mirror::Class> methods_class = resolved_method->GetDeclaringClass(); if (UNLIKELY(!referring_class->CanAccess(methods_class) || !referring_class->CanAccessMember(methods_class, resolved_method->GetAccessFlags()))) { @@ -713,7 +715,6 @@ inline ArtMethod* FindMethodFast(uint32_t method_idx, return resolved_method; } else if (type == kSuper) { // TODO This lookup is rather slow. - ObjPtr<mirror::DexCache> dex_cache = referrer->GetDexCache(); dex::TypeIndex method_type_idx = dex_cache->GetDexFile()->GetMethodId(method_idx).class_idx_; ObjPtr<mirror::Class> method_reference_class = ClassLinker::LookupResolvedType( method_type_idx, dex_cache, referrer->GetClassLoader()); @@ -727,7 +728,7 @@ inline ArtMethod* FindMethodFast(uint32_t method_idx, if (!method_reference_class->IsAssignableFrom(referring_class)) { return nullptr; } - mirror::Class* super_class = referring_class->GetSuperClass(); + ObjPtr<mirror::Class> super_class = referring_class->GetSuperClass(); if (resolved_method->GetMethodIndex() >= super_class->GetVTableLength()) { // The super class does not have the method. return nullptr; diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h index eed08aabad..fe85887f05 100644 --- a/runtime/entrypoints/entrypoint_utils.h +++ b/runtime/entrypoints/entrypoint_utils.h @@ -137,11 +137,10 @@ inline ArtField* FindFieldFast(uint32_t field_idx, REQUIRES_SHARED(Locks::mutator_lock_); // Fast path method resolution that can't throw exceptions. +template <InvokeType type, bool access_check> inline ArtMethod* FindMethodFast(uint32_t method_idx, ObjPtr<mirror::Object> this_object, - ArtMethod* referrer, - bool access_check, - InvokeType type) + ArtMethod* referrer) REQUIRES_SHARED(Locks::mutator_lock_); inline mirror::Class* ResolveVerifyAndClinit(dex::TypeIndex type_idx, diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 2c99aeba88..36885d8a1f 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -2363,7 +2363,7 @@ extern "C" uint64_t artQuickGenericJniEndTrampoline(Thread* self, // It is valid to use this, as at the usage points here (returns from C functions) we are assuming // to hold the mutator lock (see REQUIRES_SHARED(Locks::mutator_lock_) annotations). -template<InvokeType type, bool access_check> +template <InvokeType type, bool access_check> static TwoWordReturn artInvokeCommon(uint32_t method_idx, ObjPtr<mirror::Object> this_object, Thread* self, @@ -2371,7 +2371,7 @@ static TwoWordReturn artInvokeCommon(uint32_t method_idx, ScopedQuickEntrypointChecks sqec(self); DCHECK_EQ(*sp, Runtime::Current()->GetCalleeSaveMethod(CalleeSaveType::kSaveRefsAndArgs)); ArtMethod* caller_method = QuickArgumentVisitor::GetCallingMethod(sp); - ArtMethod* method = FindMethodFast(method_idx, this_object, caller_method, access_check, type); + ArtMethod* method = FindMethodFast<type, access_check>(method_idx, this_object, caller_method); if (UNLIKELY(method == nullptr)) { const DexFile* dex_file = caller_method->GetDeclaringClass()->GetDexCache()->GetDexFile(); uint32_t shorty_len; diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index b57e2b2b40..0687b753d8 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -951,13 +951,20 @@ static inline bool DoCallCommon(ArtMethod* called_method, // Test whether to use the interpreter or compiler entrypoint, and save that result to pass to // PerformCall. A deoptimization could occur at any time, and we shouldn't change which // entrypoint to use once we start building the shadow frame. - bool use_interpreter_entrypoint = ClassLinker::ShouldUseInterpreterEntrypoint( - called_method, called_method->GetEntryPointFromQuickCompiledCode()); + + // For unstarted runtimes, always use the interpreter entrypoint. This fixes the case where we are + // doing cross compilation. Note that GetEntryPointFromQuickCompiledCode doesn't use the image + // pointer size here and this may case an overflow if it is called from the compiler. b/62402160 + const bool use_interpreter_entrypoint = !Runtime::Current()->IsStarted() || + ClassLinker::ShouldUseInterpreterEntrypoint( + called_method, + called_method->GetEntryPointFromQuickCompiledCode()); if (LIKELY(code_item != nullptr)) { // When transitioning to compiled code, space only needs to be reserved for the input registers. // The rest of the frame gets discarded. This also prevents accessing the called method's code // item, saving memory by keeping code items of compiled code untouched. - if (Runtime::Current()->IsStarted() && !use_interpreter_entrypoint) { + if (!use_interpreter_entrypoint) { + DCHECK(!Runtime::Current()->IsAotCompiler()) << "Compiler should use interpreter entrypoint"; num_regs = number_of_inputs; } else { num_regs = code_item->registers_size_; diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index 12baf387d2..419a4db0fc 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -99,7 +99,7 @@ inline DexCache* Class::GetDexCache() { inline uint32_t Class::GetCopiedMethodsStartOffset() { // Object::GetFieldShort returns an int16_t value, but // Class::copied_methods_offset_ is an uint16_t value; cast the - // latter to int16_t before returning it as an uint32_t value, so + // latter to uint16_t before returning it as an uint32_t value, so // that uint16_t values between 2^15 and 2^16-1 are correctly // handled. return static_cast<uint16_t>( @@ -113,7 +113,7 @@ inline uint32_t Class::GetDirectMethodsStartOffset() { inline uint32_t Class::GetVirtualMethodsStartOffset() { // Object::GetFieldShort returns an int16_t value, but // Class::virtual_method_offset_ is an uint16_t value; cast the - // latter to int16_t before returning it as an uint32_t value, so + // latter to uint16_t before returning it as an uint32_t value, so // that uint16_t values between 2^15 and 2^16-1 are correctly // handled. return static_cast<uint16_t>( @@ -410,25 +410,24 @@ inline bool Class::IsAssignableFromArray(ObjPtr<Class> src) { return IsArrayAssignableFromArray(src); } -template <bool throw_on_failure, bool use_referrers_cache> +template <bool throw_on_failure> inline bool Class::ResolvedFieldAccessTest(ObjPtr<Class> access_to, ArtField* field, - uint32_t field_idx, - ObjPtr<DexCache> dex_cache) { - DCHECK_EQ(use_referrers_cache, dex_cache == nullptr); + ObjPtr<DexCache> dex_cache, + uint32_t field_idx) { + DCHECK(dex_cache != nullptr); if (UNLIKELY(!this->CanAccess(access_to))) { // The referrer class can't access the field's declaring class but may still be able // to access the field if the FieldId specifies an accessible subclass of the declaring // class rather than the declaring class itself. - ObjPtr<DexCache> referrer_dex_cache = use_referrers_cache ? this->GetDexCache() : dex_cache; - dex::TypeIndex class_idx = referrer_dex_cache->GetDexFile()->GetFieldId(field_idx).class_idx_; + dex::TypeIndex class_idx = dex_cache->GetDexFile()->GetFieldId(field_idx).class_idx_; // The referenced class has already been resolved with the field, but may not be in the dex // cache. Use LookupResolveType here to search the class table if it is not in the dex cache. // should be no thread suspension due to the class being resolved. ObjPtr<Class> dex_access_to = Runtime::Current()->GetClassLinker()->LookupResolvedType( - *referrer_dex_cache->GetDexFile(), + *dex_cache->GetDexFile(), class_idx, - referrer_dex_cache, + dex_cache, access_to->GetClassLoader()); DCHECK(dex_access_to != nullptr); if (UNLIKELY(!this->CanAccess(dex_access_to))) { @@ -447,25 +446,25 @@ inline bool Class::ResolvedFieldAccessTest(ObjPtr<Class> access_to, return false; } -template <bool throw_on_failure, bool use_referrers_cache, InvokeType throw_invoke_type> +template <bool throw_on_failure> inline bool Class::ResolvedMethodAccessTest(ObjPtr<Class> access_to, ArtMethod* method, + ObjPtr<DexCache> dex_cache, uint32_t method_idx, - ObjPtr<DexCache> dex_cache) { - static_assert(throw_on_failure || throw_invoke_type == kStatic, "Non-default throw invoke type"); - DCHECK_EQ(use_referrers_cache, dex_cache == nullptr); + InvokeType throw_invoke_type) { + DCHECK(throw_on_failure || throw_invoke_type == kStatic); + DCHECK(dex_cache != nullptr); if (UNLIKELY(!this->CanAccess(access_to))) { // The referrer class can't access the method's declaring class but may still be able // to access the method if the MethodId specifies an accessible subclass of the declaring // class rather than the declaring class itself. - ObjPtr<DexCache> referrer_dex_cache = use_referrers_cache ? this->GetDexCache() : dex_cache; - dex::TypeIndex class_idx = referrer_dex_cache->GetDexFile()->GetMethodId(method_idx).class_idx_; + dex::TypeIndex class_idx = dex_cache->GetDexFile()->GetMethodId(method_idx).class_idx_; // The referenced class has already been resolved with the method, but may not be in the dex // cache. ObjPtr<Class> dex_access_to = Runtime::Current()->GetClassLinker()->LookupResolvedType( - *referrer_dex_cache->GetDexFile(), + *dex_cache->GetDexFile(), class_idx, - referrer_dex_cache, + dex_cache, access_to->GetClassLoader()); DCHECK(dex_access_to != nullptr); if (UNLIKELY(!this->CanAccess(dex_access_to))) { @@ -491,30 +490,30 @@ inline bool Class::CanAccessResolvedField(ObjPtr<Class> access_to, ArtField* field, ObjPtr<DexCache> dex_cache, uint32_t field_idx) { - return ResolvedFieldAccessTest<false, false>(access_to, field, field_idx, dex_cache); + return ResolvedFieldAccessTest<false>(access_to, field, dex_cache, field_idx); } inline bool Class::CheckResolvedFieldAccess(ObjPtr<Class> access_to, ArtField* field, + ObjPtr<DexCache> dex_cache, uint32_t field_idx) { - return ResolvedFieldAccessTest<true, true>(access_to, field, field_idx, nullptr); + return ResolvedFieldAccessTest<true>(access_to, field, dex_cache, field_idx); } inline bool Class::CanAccessResolvedMethod(ObjPtr<Class> access_to, ArtMethod* method, ObjPtr<DexCache> dex_cache, uint32_t method_idx) { - return ResolvedMethodAccessTest<false, false, kStatic>(access_to, method, method_idx, dex_cache); + return ResolvedMethodAccessTest<false>(access_to, method, dex_cache, method_idx, kStatic); } -template <InvokeType throw_invoke_type> inline bool Class::CheckResolvedMethodAccess(ObjPtr<Class> access_to, ArtMethod* method, - uint32_t method_idx) { - return ResolvedMethodAccessTest<true, true, throw_invoke_type>(access_to, - method, - method_idx, - nullptr); + ObjPtr<DexCache> dex_cache, + uint32_t method_idx, + InvokeType throw_invoke_type) { + return ResolvedMethodAccessTest<true>( + access_to, method, dex_cache, method_idx, throw_invoke_type); } inline bool Class::IsSubClass(ObjPtr<Class> klass) { diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index 61d6e05416..00498bc30a 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -643,7 +643,10 @@ class MANAGED Class FINAL : public Object { ObjPtr<DexCache> dex_cache, uint32_t field_idx) REQUIRES_SHARED(Locks::mutator_lock_); - bool CheckResolvedFieldAccess(ObjPtr<Class> access_to, ArtField* field, uint32_t field_idx) + bool CheckResolvedFieldAccess(ObjPtr<Class> access_to, + ArtField* field, + ObjPtr<DexCache> dex_cache, + uint32_t field_idx) REQUIRES_SHARED(Locks::mutator_lock_); // Can this class access a resolved method? @@ -654,10 +657,11 @@ class MANAGED Class FINAL : public Object { ObjPtr<DexCache> dex_cache, uint32_t method_idx) REQUIRES_SHARED(Locks::mutator_lock_); - template <InvokeType throw_invoke_type> bool CheckResolvedMethodAccess(ObjPtr<Class> access_to, ArtMethod* resolved_method, - uint32_t method_idx) + ObjPtr<DexCache> dex_cache, + uint32_t method_idx, + InvokeType throw_invoke_type) REQUIRES_SHARED(Locks::mutator_lock_); bool IsSubClass(ObjPtr<Class> klass) REQUIRES_SHARED(Locks::mutator_lock_); @@ -1352,18 +1356,19 @@ class MANAGED Class FINAL : public Object { uint32_t end_offset) REQUIRES_SHARED(Locks::mutator_lock_); - template <bool throw_on_failure, bool use_referrers_cache> + template <bool throw_on_failure> bool ResolvedFieldAccessTest(ObjPtr<Class> access_to, ArtField* field, - uint32_t field_idx, - ObjPtr<DexCache> dex_cache) + ObjPtr<DexCache> dex_cache, + uint32_t field_idx) REQUIRES_SHARED(Locks::mutator_lock_); - template <bool throw_on_failure, bool use_referrers_cache, InvokeType throw_invoke_type> + template <bool throw_on_failure> bool ResolvedMethodAccessTest(ObjPtr<Class> access_to, ArtMethod* resolved_method, + ObjPtr<DexCache> dex_cache, uint32_t method_idx, - ObjPtr<DexCache> dex_cache) + InvokeType throw_invoke_type) REQUIRES_SHARED(Locks::mutator_lock_); bool Implements(ObjPtr<Class> klass) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/mirror/dex_cache_test.cc b/runtime/mirror/dex_cache_test.cc index a110ed7f6b..5b1ba8d010 100644 --- a/runtime/mirror/dex_cache_test.cc +++ b/runtime/mirror/dex_cache_test.cc @@ -106,12 +106,12 @@ TEST_F(DexCacheTest, TestResolvedFieldAccess) { EXPECT_NE(klass1->NumStaticFields(), 0u); for (ArtField& field : klass2->GetSFields()) { - EXPECT_FALSE(( - klass1->ResolvedFieldAccessTest</*throw_on_failure*/ false, - /*use_referrers_cache*/ false>(klass2.Get(), - &field, - field.GetDexFieldIndex(), - klass1->GetDexCache()))); + EXPECT_FALSE( + klass1->ResolvedFieldAccessTest</*throw_on_failure*/ false>( + klass2.Get(), + &field, + klass1->GetDexCache(), + field.GetDexFieldIndex())); } } diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc index 21239fe3de..505e844c92 100644 --- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc +++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc @@ -913,12 +913,12 @@ class JvmtiFunctions { } static jvmtiError GetBytecodes(jvmtiEnv* env, - jmethodID method ATTRIBUTE_UNUSED, - jint* bytecode_count_ptr ATTRIBUTE_UNUSED, - unsigned char** bytecodes_ptr ATTRIBUTE_UNUSED) { + jmethodID method, + jint* bytecode_count_ptr, + unsigned char** bytecodes_ptr) { ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_get_bytecodes); - return ERR(NOT_IMPLEMENTED); + return MethodUtil::GetBytecodes(env, method, bytecode_count_ptr, bytecodes_ptr); } static jvmtiError IsMethodNative(jvmtiEnv* env, jmethodID method, jboolean* is_native_ptr) { diff --git a/runtime/openjdkjvmti/art_jvmti.h b/runtime/openjdkjvmti/art_jvmti.h index 2d5d527e68..c63e50252b 100644 --- a/runtime/openjdkjvmti/art_jvmti.h +++ b/runtime/openjdkjvmti/art_jvmti.h @@ -216,7 +216,7 @@ const jvmtiCapabilities kPotentialCapabilities = { .can_tag_objects = 1, .can_generate_field_modification_events = 1, .can_generate_field_access_events = 1, - .can_get_bytecodes = 0, + .can_get_bytecodes = 1, .can_get_synthetic_attribute = 1, .can_get_owned_monitor_info = 0, .can_get_current_contended_monitor = 0, diff --git a/runtime/openjdkjvmti/ti_method.cc b/runtime/openjdkjvmti/ti_method.cc index beb639e208..9b5b964a4d 100644 --- a/runtime/openjdkjvmti/ti_method.cc +++ b/runtime/openjdkjvmti/ti_method.cc @@ -91,6 +91,40 @@ void MethodUtil::Unregister() { runtime->GetRuntimeCallbacks()->RemoveMethodCallback(&gMethodCallback); } +jvmtiError MethodUtil::GetBytecodes(jvmtiEnv* env, + jmethodID method, + jint* size_ptr, + unsigned char** bytecode_ptr) { + if (method == nullptr) { + return ERR(INVALID_METHODID); + } + art::ArtMethod* art_method = art::jni::DecodeArtMethod(method); + + if (art_method->IsNative()) { + return ERR(NATIVE_METHOD); + } + + if (size_ptr == nullptr || bytecode_ptr == nullptr) { + return ERR(NULL_POINTER); + } + + art::ScopedObjectAccess soa(art::Thread::Current()); + const art::DexFile::CodeItem* code_item = art_method->GetCodeItem(); + if (code_item == nullptr) { + *size_ptr = 0; + *bytecode_ptr = nullptr; + return OK; + } + // 2 bytes per instruction for dex code. + *size_ptr = code_item->insns_size_in_code_units_ * 2; + jvmtiError err = env->Allocate(*size_ptr, bytecode_ptr); + if (err != OK) { + return err; + } + memcpy(*bytecode_ptr, code_item->insns_, *size_ptr); + return OK; +} + jvmtiError MethodUtil::GetArgumentsSize(jvmtiEnv* env ATTRIBUTE_UNUSED, jmethodID method, jint* size_ptr) { diff --git a/runtime/openjdkjvmti/ti_method.h b/runtime/openjdkjvmti/ti_method.h index cc161c8fed..d95a81b63b 100644 --- a/runtime/openjdkjvmti/ti_method.h +++ b/runtime/openjdkjvmti/ti_method.h @@ -44,6 +44,11 @@ class MethodUtil { static void Register(EventHandler* event_handler); static void Unregister(); + static jvmtiError GetBytecodes(jvmtiEnv* env, + jmethodID method, + jint* count_ptr, + unsigned char** bytecodes); + static jvmtiError GetArgumentsSize(jvmtiEnv* env, jmethodID method, jint* size_ptr); static jvmtiError GetMaxLocals(jvmtiEnv* env, jmethodID method, jint* max_ptr); diff --git a/runtime/thread.cc b/runtime/thread.cc index 4669e167a2..36ecd3398c 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -573,7 +573,14 @@ void Thread::InstallImplicitProtection() { // Use a large local volatile array to ensure a large frame size. Do not use anything close // to a full page for ASAN. It would be nice to ensure the frame size is at most a page, but // there is no pragma support for this. - volatile char space[kPageSize - 256]; + // Note: for ASAN we need to shrink the array a bit, as there's other overhead. + constexpr size_t kAsanMultiplier = +#ifdef ADDRESS_SANITIZER + 2u; +#else + 1u; +#endif + volatile char space[kPageSize - (kAsanMultiplier * 256)]; char sink ATTRIBUTE_UNUSED = space[zero]; if (reinterpret_cast<uintptr_t>(space) >= target + kPageSize) { Touch(target); diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index 24f194b5ee..8d505e2582 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -37,6 +37,7 @@ namespace art { jclass WellKnownClasses::dalvik_annotation_optimization_CriticalNative; jclass WellKnownClasses::dalvik_annotation_optimization_FastNative; jclass WellKnownClasses::dalvik_system_BaseDexClassLoader; +jclass WellKnownClasses::dalvik_system_DelegateLastClassLoader; jclass WellKnownClasses::dalvik_system_DexClassLoader; jclass WellKnownClasses::dalvik_system_DexFile; jclass WellKnownClasses::dalvik_system_DexPathList; @@ -270,6 +271,7 @@ void WellKnownClasses::Init(JNIEnv* env) { 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_DelegateLastClassLoader = CacheClass(env, "dalvik/system/DelegateLastClassLoader"); dalvik_system_DexClassLoader = CacheClass(env, "dalvik/system/DexClassLoader"); dalvik_system_DexFile = CacheClass(env, "dalvik/system/DexFile"); dalvik_system_DexPathList = CacheClass(env, "dalvik/system/DexPathList"); diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h index c18473197b..c5a16c1c76 100644 --- a/runtime/well_known_classes.h +++ b/runtime/well_known_classes.h @@ -47,6 +47,7 @@ struct WellKnownClasses { static jclass dalvik_annotation_optimization_CriticalNative; static jclass dalvik_annotation_optimization_FastNative; static jclass dalvik_system_BaseDexClassLoader; + static jclass dalvik_system_DelegateLastClassLoader; static jclass dalvik_system_DexClassLoader; static jclass dalvik_system_DexFile; static jclass dalvik_system_DexPathList; diff --git a/test/141-class-unload/src/Main.java b/test/141-class-unload/src/Main.java index 9072c8b538..3cfe0064fc 100644 --- a/test/141-class-unload/src/Main.java +++ b/test/141-class-unload/src/Main.java @@ -65,7 +65,8 @@ public class Main { String line; int count = 0; while ((line = reader.readLine()) != null) { - if (line.contains("@141-class-unload-ex.jar")) { + if (line.contains("141-class-unload-ex.odex") || + line.contains("141-class-unload-ex.vdex")) { System.out.println(line); ++count; } diff --git a/test/1901-get-bytecodes/bytecodes.cc b/test/1901-get-bytecodes/bytecodes.cc new file mode 100644 index 0000000000..edcb734788 --- /dev/null +++ b/test/1901-get-bytecodes/bytecodes.cc @@ -0,0 +1,63 @@ +/* + * 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 <iostream> +#include <pthread.h> +#include <stdio.h> +#include <vector> + +#include "android-base/logging.h" +#include "jni.h" +#include "scoped_local_ref.h" +#include "scoped_primitive_array.h" + +#include "jvmti.h" + +// Test infrastructure +#include "jvmti_helper.h" +#include "test_env.h" + +namespace art { +namespace Test1901Bytecodes { + +extern "C" JNIEXPORT jbyteArray JNICALL Java_art_Test1901_getBytecodes(JNIEnv* env, + jclass, + jobject jmethod) { + jmethodID method = env->FromReflectedMethod(jmethod); + if (env->ExceptionCheck()) { + return nullptr; + } + unsigned char* bytecodes = nullptr; + jint bytecodes_size = 0; + if (JvmtiErrorToException(env, + jvmti_env, + jvmti_env->GetBytecodes(method, &bytecodes_size, &bytecodes))) { + return nullptr; + } + jbyteArray out = env->NewByteArray(bytecodes_size); + if (env->ExceptionCheck()) { + return nullptr; + } else if (bytecodes_size == 0) { + return out; + } + jbyte* bytes = env->GetByteArrayElements(out, /* is_copy */ nullptr); + memcpy(bytes, bytecodes, bytecodes_size); + env->ReleaseByteArrayElements(out, bytes, 0); + return out; +} + +} // namespace Test1901Bytecodes +} // namespace art diff --git a/test/1901-get-bytecodes/expected.txt b/test/1901-get-bytecodes/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/1901-get-bytecodes/expected.txt diff --git a/test/1901-get-bytecodes/info.txt b/test/1901-get-bytecodes/info.txt new file mode 100644 index 0000000000..c8c91893e5 --- /dev/null +++ b/test/1901-get-bytecodes/info.txt @@ -0,0 +1,3 @@ +Tests basic functions in the jvmti plugin. + +Tests that the GetBytecodes function works as expected. diff --git a/test/1901-get-bytecodes/run b/test/1901-get-bytecodes/run new file mode 100755 index 0000000000..c6e62ae6cd --- /dev/null +++ b/test/1901-get-bytecodes/run @@ -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-run "$@" --jvmti diff --git a/test/1901-get-bytecodes/src/Main.java b/test/1901-get-bytecodes/src/Main.java new file mode 100644 index 0000000000..d37fcdb0cb --- /dev/null +++ b/test/1901-get-bytecodes/src/Main.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + public static void main(String[] args) throws Exception { + art.Test1901.run(); + } +} diff --git a/test/1901-get-bytecodes/src/art/Test1901.java b/test/1901-get-bytecodes/src/art/Test1901.java new file mode 100644 index 0000000000..6940491e3c --- /dev/null +++ b/test/1901-get-bytecodes/src/art/Test1901.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package art; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.Base64; + +public class Test1901 { + // Class & Dex file containing the following class. + // Using this representation to prevent any changes to the compiler or the file formats from + // changing the output of this test. + // + // package art; + // public class Target { + // public void doNothing() { + // return; + // } + // + // public static void staticNothing() { + // return; + // } + // + // public void sayHi() { + // System.out.println("hello"); + // } + // } + public static byte[] CLASS_BYTES = Base64.getDecoder().decode( + "yv66vgAAADQAHgoABgAQCQARABIIABMKABQAFQcAFgcAFwEABjxpbml0PgEAAygpVgEABENvZGUB" + + "AA9MaW5lTnVtYmVyVGFibGUBAAlkb05vdGhpbmcBAA1zdGF0aWNOb3RoaW5nAQAFc2F5SGkBAApT" + + "b3VyY2VGaWxlAQALVGFyZ2V0LmphdmEMAAcACAcAGAwAGQAaAQAFaGVsbG8HABsMABwAHQEACmFy" + + "dC9UYXJnZXQBABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxq" + + "YXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExq" + + "YXZhL2xhbmcvU3RyaW5nOylWACEABQAGAAAAAAAEAAEABwAIAAEACQAAAB0AAQABAAAABSq3AAGx" + + "AAAAAQAKAAAABgABAAAAAgABAAsACAABAAkAAAAZAAAAAQAAAAGxAAAAAQAKAAAABgABAAAABAAJ" + + "AAwACAABAAkAAAAZAAAAAAAAAAGxAAAAAQAKAAAABgABAAAACAABAA0ACAABAAkAAAAlAAIAAQAA" + + "AAmyAAISA7YABLEAAAABAAoAAAAKAAIAAAAMAAgADQABAA4AAAACAA8="); + public static byte[] DEX_BYTES = Base64.getDecoder().decode( + "ZGV4CjAzNQAbYkxNjiZ8a+fNWF4smR2+uXbrq88/FNoYAwAAcAAAAHhWNBIAAAAAAAAAAHgCAAAP" + + "AAAAcAAAAAYAAACsAAAAAgAAAMQAAAABAAAA3AAAAAYAAADkAAAAAQAAABQBAADkAQAANAEAAJoB" + + "AACiAQAAsAEAAMcBAADbAQAA7wEAAAMCAAAQAgAAEwIAABcCAAAiAgAAKQIAAC4CAAA3AgAAPgIA" + + "AAEAAAACAAAAAwAAAAQAAAAFAAAABwAAAAcAAAAFAAAAAAAAAAgAAAAFAAAAlAEAAAQAAQALAAAA" + + "AAAAAAAAAAAAAAAACQAAAAAAAAANAAAAAAAAAA4AAAABAAEADAAAAAIAAAAAAAAAAAAAAAEAAAAC" + + "AAAAAAAAAAYAAAAAAAAAYgIAAAAAAAABAAEAAQAAAE0CAAAEAAAAcBAFAAAADgAAAAAAAAAAAFIC" + + "AAABAAAADgAAAAEAAQAAAAAAVwIAAAEAAAAOAAAAAwABAAIAAABcAgAACAAAAGIAAAAaAQoAbiAE" + + "ABAADgABAAAAAwAGPGluaXQ+AAxMYXJ0L1RhcmdldDsAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwAS" + + "TGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xhbmcvU3lzdGVt" + + "OwALVGFyZ2V0LmphdmEAAVYAAlZMAAlkb05vdGhpbmcABWhlbGxvAANvdXQAB3ByaW50bG4ABXNh" + + "eUhpAA1zdGF0aWNOb3RoaW5nAAIABw4ACAAHDgAEAAcOAAwABw54AAAAAgIAgYAEtAIDCcwCAQHg" + + "AgEB9AINAAAAAAAAAAEAAAAAAAAAAQAAAA8AAABwAAAAAgAAAAYAAACsAAAAAwAAAAIAAADEAAAA" + + "BAAAAAEAAADcAAAABQAAAAYAAADkAAAABgAAAAEAAAAUAQAAASAAAAQAAAA0AQAAARAAAAEAAACU" + + "AQAAAiAAAA8AAACaAQAAAyAAAAQAAABNAgAAACAAAAEAAABiAgAAABAAAAEAAAB4AgAA"); + + public static byte[][] DO_NOTHING_BYTECODES = new byte[][] { + // Dex Bytecodes for doNothing + // 0e00 |0000: return-void + new byte[] { 14, 0 }, + // Java bytecodes + // 0: return + new byte[] { -79 }, + }; + + public static byte[][] STATIC_NOTHING_BYTECODES = new byte[][] { + // Dex Bytecodes for staticNothing + // 0e00 |0000: return-void + new byte[] { 14, 0 }, + // Java bytecodes + // 0: return + new byte[] { -79 }, + }; + + public static byte[][] SAY_HI_NOTHING_BYTECODES = new byte[][] { + // Dex Bytecodes for sayHi + // 6200 0000 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0000 + // 1a01 0a00 |0002: const-string v1, "hello" // string@000a + // 6e20 0400 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0004 + // 0e00 |0007: return-void + new byte[] { 98, 0, 0, 0, 26, 1, 10, 0, 110, 32, 4, 0, 16, 0, 14, 0 }, + // Java bytecodes + // 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; + // 3: ldc #3 // String hello + // 5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V + // 8: return + new byte[] { -78, 0, 2, 18, 3, -74, 0, 4, -79 }, + }; + + public static ClassLoader getClassLoader() throws Exception { + try { + Class<?> class_loader_class = Class.forName("dalvik.system.InMemoryDexClassLoader"); + Constructor<?> ctor = class_loader_class.getConstructor(ByteBuffer.class, ClassLoader.class); + // We are on art since we got the InMemoryDexClassLoader. + return (ClassLoader)ctor.newInstance( + ByteBuffer.wrap(DEX_BYTES), Test1901.class.getClassLoader()); + } catch (ClassNotFoundException e) { + // Running on RI. + return new ClassLoader(Test1901.class.getClassLoader()) { + protected Class<?> findClass(String name) throws ClassNotFoundException { + if (name.equals("art.Target")) { + return defineClass(name, CLASS_BYTES, 0, CLASS_BYTES.length); + } else { + return super.findClass(name); + } + } + }; + } + } + + public static void CheckMethodBytes(Method m, byte[][] possible_bytecodes) { + byte[] real_codes = getBytecodes(m); + for (byte[] pos : possible_bytecodes) { + if (Arrays.equals(pos, real_codes)) { + return; + } + } + System.out.println("Unexpected bytecodes for " + m); + System.out.println("Received: " + Arrays.toString(real_codes)); + System.out.println("Expected one of:"); + for (byte[] pos : possible_bytecodes) { + System.out.println("\t" + Arrays.toString(pos)); + } + } + + public static void run() throws Exception { + Class<?> target = getClassLoader().loadClass("art.Target"); + CheckMethodBytes(target.getDeclaredMethod("doNothing"), DO_NOTHING_BYTECODES); + CheckMethodBytes(target.getDeclaredMethod("staticNothing"), STATIC_NOTHING_BYTECODES); + CheckMethodBytes(target.getDeclaredMethod("sayHi"), SAY_HI_NOTHING_BYTECODES); + } + + public static native byte[] getBytecodes(Method m); +} diff --git a/test/596-app-images/src/Main.java b/test/596-app-images/src/Main.java index 8ee3c888b0..88d95f4162 100644 --- a/test/596-app-images/src/Main.java +++ b/test/596-app-images/src/Main.java @@ -14,6 +14,10 @@ * limitations under the License. */ +import java.lang.reflect.Field; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; + class Main { static class Inner { final public static int abc = 10; @@ -46,13 +50,76 @@ class Main { if (!checkInitialized(StaticFieldsInit.class)) System.out.println("StaticFieldsInit class is not initialized!"); - if (checkInitialized(StaticInternString.class)) - System.out.println("StaticInternString class is initialized!"); + if (!checkInitialized(StaticInternString.class)) + System.out.println("StaticInternString class is not initialized!"); + + StringBuffer sb = new StringBuffer(); + sb.append("java."); + sb.append("abc."); + sb.append("Action"); + + String tmp = sb.toString(); + String intern = tmp.intern(); + + assertNotEqual(tmp, intern, "Dynamically constructed String, not interned."); + assertEqual(intern, StaticInternString.intent, "Static encoded literal String not interned."); + assertEqual(BootInternedString.boot, BootInternedString.boot.intern(), + "Static encoded literal String not moved back to runtime intern table."); + + try { + Field f = StaticInternString.class.getDeclaredField("intent"); + assertEqual(intern, f.get(null), "String Literals are not interned properly."); + + } catch (Exception e) { + System.out.println("Exception"); + } + + assertEqual(StaticInternString.getIntent(), StaticInternString2.getIntent(), + "String Literals are not intenred properly, App image static strings duplicated."); + + // reload the class StaticInternString, check whether static strings interned properly + final String DEX_FILE = System.getenv("DEX_LOCATION") + "/596-app-images.jar"; + final String LIBRARY_SEARCH_PATH = System.getProperty("java.library.path"); + + try { + Class<?> pathClassLoader = Class.forName("dalvik.system.PathClassLoader"); + if (pathClassLoader == null) { + throw new AssertionError("Counldn't find path class loader class"); + } + Constructor<?> ctor = + pathClassLoader.getDeclaredConstructor(String.class, String.class, ClassLoader.class); + ClassLoader loader = (ClassLoader) ctor.newInstance( + DEX_FILE, LIBRARY_SEARCH_PATH, null); + + Class<?> staticInternString = loader.loadClass("StaticInternString"); + + if (!checkAppImageContains(staticInternString)) { + System.out.println("Not loaded again."); + } + Method getIntent = staticInternString.getDeclaredMethod("getIntent"); + + assertEqual(StaticInternString.getIntent(), getIntent.invoke(staticInternString), + "Dynamically loaded app image's literal strings not interned properly."); + } catch (Exception e) { + e.printStackTrace(System.out); + } + } public static native boolean checkAppImageLoaded(); public static native boolean checkAppImageContains(Class<?> klass); public static native boolean checkInitialized(Class<?> klass); + + public static void assertEqual(Object a, Object b, String msg) { + if (a != b) + System.out.println(msg); + } + + public static void assertNotEqual(Object a, Object b, String msg) { + if (a == b) + System.out.println(msg); + } + } class StaticFields{ @@ -68,6 +135,21 @@ class StaticFieldsInit{ } class StaticInternString { - final public static String intern = "java.abc.Action"; + final public static String intent = "java.abc.Action"; + static public String getIntent() { + return intent; + } +} + +class BootInternedString { + final public static String boot = "double"; +} + +class StaticInternString2 { + final public static String intent = "java.abc.Action"; + + static String getIntent() { + return intent; + } } diff --git a/test/Android.bp b/test/Android.bp index f893531c41..7d7afa5044 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -286,6 +286,7 @@ art_cc_defaults { "992-source-data/source_file.cc", "993-breakpoints/breakpoints.cc", "996-breakpoint-obsolete/obsolete_breakpoints.cc", + "1901-get-bytecodes/bytecodes.cc", ], shared_libs: [ "libbase", @@ -445,6 +446,7 @@ art_cc_test_library { "art_debug_defaults", "art_defaults", ], + header_libs: ["libnativebridge-dummy-headers"], srcs: ["115-native-bridge/nativebridge.cc"], target: { android: { diff --git a/test/ForClassLoaderA/Classes.java b/test/ForClassLoaderA/Classes.java new file mode 100644 index 0000000000..a65ef64161 --- /dev/null +++ b/test/ForClassLoaderA/Classes.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class DefinedInA { +} + +class DefinedInAB { +} + +class DefinedInABC { +} + +class DefinedInAC { +} + +class DefinedInAD { +} + diff --git a/test/ForClassLoaderB/Classes.java b/test/ForClassLoaderB/Classes.java new file mode 100644 index 0000000000..8c85ed5fc0 --- /dev/null +++ b/test/ForClassLoaderB/Classes.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class DefinedInB { +} + +class DefinedInAB { +} + +class DefinedInABC { +} + +class DefinedInBC { +} + +class DefinedInBD { +} diff --git a/test/ForClassLoaderC/Classes.java b/test/ForClassLoaderC/Classes.java new file mode 100644 index 0000000000..7b9e83ffff --- /dev/null +++ b/test/ForClassLoaderC/Classes.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class DefinedInC { +} + +class DefinedInAC { +} + +class DefinedInABC { +} + +class DefinedInBC { +} + +class DefinedInCD { +} diff --git a/test/ForClassLoaderD/Classes.java b/test/ForClassLoaderD/Classes.java new file mode 100644 index 0000000000..b34177f05f --- /dev/null +++ b/test/ForClassLoaderD/Classes.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class DefinedInD { +} + +class DefinedInAD { +} + +class DefinedInBD { +} + +class DefinedInCD { +} diff --git a/test/knownfailures.json b/test/knownfailures.json index d3d92c651e..7b801e29ce 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -645,6 +645,11 @@ "env_vars": {"SANITIZE_HOST": "address"} }, { + "tests": "141-class-unload", + "description": "Idk why this fails", + "env_vars": {"SANITIZE_HOST": "address"} + }, + { "tests": ["988-method-trace"], "variant": "redefine-stress | jvmti-stress", "description": "Test disabled due to redefine-stress disabling intrinsics which changes the trace output slightly." |