diff options
-rw-r--r-- | runtime/art_method-inl.h | 6 | ||||
-rw-r--r-- | runtime/art_method.cc | 10 | ||||
-rw-r--r-- | runtime/art_method.h | 2 | ||||
-rw-r--r-- | runtime/dex_file_annotations.cc | 329 | ||||
-rwxr-xr-x | test/980-redefine-object/check | 20 | ||||
-rw-r--r-- | test/980-redefine-object/expected.txt | 52 | ||||
-rw-r--r-- | test/980-redefine-object/info.txt | 23 | ||||
-rw-r--r-- | test/980-redefine-object/redefine_object.cc | 54 | ||||
-rwxr-xr-x | test/980-redefine-object/run | 17 | ||||
-rw-r--r-- | test/980-redefine-object/src-ex/TestWatcher.java | 25 | ||||
-rw-r--r-- | test/980-redefine-object/src/Main.java | 390 | ||||
-rw-r--r-- | test/980-redefine-object/src/Transform.java | 17 | ||||
-rw-r--r-- | test/Android.bp | 1 | ||||
-rw-r--r-- | test/ti-agent/common_helper.cc | 7 | ||||
-rw-r--r-- | test/ti-agent/common_helper.h | 1 |
15 files changed, 831 insertions, 123 deletions
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index 685e26c78d..b47f8f0fc2 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -347,7 +347,11 @@ inline const char* ArtMethod::GetDeclaringClassSourceFile() { inline uint16_t ArtMethod::GetClassDefIndex() { DCHECK(!IsProxyMethod()); - return GetDeclaringClass()->GetDexClassDefIndex(); + if (LIKELY(!IsObsolete())) { + return GetDeclaringClass()->GetDexClassDefIndex(); + } else { + return FindObsoleteDexClassDefIndex(); + } } inline const DexFile::ClassDef& ArtMethod::GetClassDef() { diff --git a/runtime/art_method.cc b/runtime/art_method.cc index 9d74e7c92b..80a877350b 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -104,6 +104,16 @@ mirror::DexCache* ArtMethod::GetObsoleteDexCache() { UNREACHABLE(); } +uint16_t ArtMethod::FindObsoleteDexClassDefIndex() { + DCHECK(!Runtime::Current()->IsAotCompiler()) << PrettyMethod(); + DCHECK(IsObsolete()); + const DexFile* dex_file = GetDexFile(); + const dex::TypeIndex declaring_class_type = dex_file->GetMethodId(GetDexMethodIndex()).class_idx_; + const DexFile::ClassDef* class_def = dex_file->FindClassDef(declaring_class_type); + CHECK(class_def != nullptr); + return dex_file->GetIndexForClassDef(*class_def); +} + mirror::String* ArtMethod::GetNameAsString(Thread* self) { CHECK(!IsProxyMethod()); StackHandleScope<1> hs(self); diff --git a/runtime/art_method.h b/runtime/art_method.h index cd1950c0e2..2248c3bd9d 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -700,6 +700,8 @@ class ArtMethod FINAL { } ptr_sized_fields_; private: + uint16_t FindObsoleteDexClassDefIndex() REQUIRES_SHARED(Locks::mutator_lock_); + bool IsAnnotatedWith(jclass klass, uint32_t visibility); static constexpr size_t PtrSizedFieldsOffset(PointerSize pointer_size) { diff --git a/runtime/dex_file_annotations.cc b/runtime/dex_file_annotations.cc index d39ea35a90..6b9654dc49 100644 --- a/runtime/dex_file_annotations.cc +++ b/runtime/dex_file_annotations.cc @@ -41,7 +41,80 @@ struct DexFile::AnnotationValue { }; namespace { -mirror::Object* CreateAnnotationMember(Handle<mirror::Class> klass, + +// A helper class that contains all the data needed to do annotation lookup. +class ClassData { + public: + explicit ClassData(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) + : ClassData(ScopedNullHandle<mirror::Class>(), // klass + method, + *method->GetDexFile(), + &method->GetClassDef()) {} + + // Requires Scope to be able to create at least 1 handles. + template <typename Scope> + ClassData(Scope& hs, ArtField* field) REQUIRES_SHARED(Locks::mutator_lock_) + : ClassData(hs.NewHandle(field->GetDeclaringClass())) { } + + explicit ClassData(Handle<mirror::Class> klass) REQUIRES_SHARED(art::Locks::mutator_lock_) + : ClassData(klass, // klass + nullptr, // method + klass->GetDexFile(), + klass->GetClassDef()) {} + + const DexFile& GetDexFile() const REQUIRES_SHARED(Locks::mutator_lock_) { + return dex_file_; + } + + const DexFile::ClassDef* GetClassDef() const REQUIRES_SHARED(Locks::mutator_lock_) { + return class_def_; + } + + ObjPtr<mirror::DexCache> GetDexCache() const REQUIRES_SHARED(Locks::mutator_lock_) { + if (method_ != nullptr) { + return method_->GetDexCache(); + } else { + return real_klass_->GetDexCache(); + } + } + + ObjPtr<mirror::ClassLoader> GetClassLoader() const REQUIRES_SHARED(Locks::mutator_lock_) { + if (method_ != nullptr) { + return method_->GetDeclaringClass()->GetClassLoader(); + } else { + return real_klass_->GetClassLoader(); + } + } + + ObjPtr<mirror::Class> GetRealClass() const REQUIRES_SHARED(Locks::mutator_lock_) { + if (method_ != nullptr) { + return method_->GetDeclaringClass(); + } else { + return real_klass_.Get(); + } + } + + private: + ClassData(Handle<mirror::Class> klass, + ArtMethod* method, + const DexFile& dex_file, + const DexFile::ClassDef* class_def) REQUIRES_SHARED(Locks::mutator_lock_) + : real_klass_(klass), + method_(method), + dex_file_(dex_file), + class_def_(class_def) { + DCHECK((method_ == nullptr) || real_klass_.IsNull()); + } + + Handle<mirror::Class> real_klass_; + ArtMethod* method_; + const DexFile& dex_file_; + const DexFile::ClassDef* class_def_; + + DISALLOW_COPY_AND_ASSIGN(ClassData); +}; + +mirror::Object* CreateAnnotationMember(const ClassData& klass, Handle<mirror::Class> annotation_class, const uint8_t** annotation) REQUIRES_SHARED(Locks::mutator_lock_); @@ -185,9 +258,8 @@ const uint8_t* SearchEncodedAnnotation(const DexFile& dex_file, const DexFile::AnnotationSetItem* FindAnnotationSetForMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) { const DexFile* dex_file = method->GetDexFile(); - mirror::Class* klass = method->GetDeclaringClass(); const DexFile::AnnotationsDirectoryItem* annotations_dir = - dex_file->GetAnnotationsDirectory(*klass->GetClassDef()); + dex_file->GetAnnotationsDirectory(method->GetClassDef()); if (annotations_dir == nullptr) { return nullptr; } @@ -209,9 +281,8 @@ const DexFile::AnnotationSetItem* FindAnnotationSetForMethod(ArtMethod* method) const DexFile::ParameterAnnotationsItem* FindAnnotationsItemForMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) { const DexFile* dex_file = method->GetDexFile(); - mirror::Class* klass = method->GetDeclaringClass(); const DexFile::AnnotationsDirectoryItem* annotations_dir = - dex_file->GetAnnotationsDirectory(*klass->GetClassDef()); + dex_file->GetAnnotationsDirectory(method->GetClassDef()); if (annotations_dir == nullptr) { return nullptr; } @@ -230,30 +301,34 @@ const DexFile::ParameterAnnotationsItem* FindAnnotationsItemForMethod(ArtMethod* return nullptr; } -const DexFile::AnnotationSetItem* FindAnnotationSetForClass(Handle<mirror::Class> klass) +const DexFile::AnnotationSetItem* FindAnnotationSetForClass(const ClassData& klass) REQUIRES_SHARED(Locks::mutator_lock_) { - const DexFile& dex_file = klass->GetDexFile(); + const DexFile& dex_file = klass.GetDexFile(); const DexFile::AnnotationsDirectoryItem* annotations_dir = - dex_file.GetAnnotationsDirectory(*klass->GetClassDef()); + dex_file.GetAnnotationsDirectory(*klass.GetClassDef()); if (annotations_dir == nullptr) { return nullptr; } return dex_file.GetClassAnnotationSet(annotations_dir); } -mirror::Object* ProcessEncodedAnnotation(Handle<mirror::Class> klass, const uint8_t** annotation) +mirror::Object* ProcessEncodedAnnotation(const ClassData& klass, const uint8_t** annotation) REQUIRES_SHARED(Locks::mutator_lock_) { uint32_t type_index = DecodeUnsignedLeb128(annotation); uint32_t size = DecodeUnsignedLeb128(annotation); Thread* self = Thread::Current(); ScopedObjectAccessUnchecked soa(self); - StackHandleScope<2> hs(self); + StackHandleScope<4> hs(self); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); Handle<mirror::Class> annotation_class(hs.NewHandle( - class_linker->ResolveType(klass->GetDexFile(), dex::TypeIndex(type_index), klass.Get()))); + class_linker->ResolveType(klass.GetDexFile(), + dex::TypeIndex(type_index), + hs.NewHandle(klass.GetDexCache()), + hs.NewHandle(klass.GetClassLoader())))); if (annotation_class == nullptr) { - LOG(INFO) << "Unable to resolve " << klass->PrettyClass() << " annotation class " << type_index; + LOG(INFO) << "Unable to resolve " << klass.GetRealClass()->PrettyClass() + << " annotation class " << type_index; DCHECK(Thread::Current()->IsExceptionPending()); Thread::Current()->ClearException(); return nullptr; @@ -300,13 +375,13 @@ mirror::Object* ProcessEncodedAnnotation(Handle<mirror::Class> klass, const uint } template <bool kTransactionActive> -bool ProcessAnnotationValue(Handle<mirror::Class> klass, +bool ProcessAnnotationValue(const ClassData& klass, const uint8_t** annotation_ptr, DexFile::AnnotationValue* annotation_value, Handle<mirror::Class> array_class, DexFile::AnnotationResultStyle result_style) REQUIRES_SHARED(Locks::mutator_lock_) { - const DexFile& dex_file = klass->GetDexFile(); + const DexFile& dex_file = klass.GetDexFile(); Thread* self = Thread::Current(); ObjPtr<mirror::Object> element_object = nullptr; bool set_object = false; @@ -361,9 +436,8 @@ bool ProcessAnnotationValue(Handle<mirror::Class> klass, annotation_value->value_.SetI(index); } else { StackHandleScope<1> hs(self); - Handle<mirror::DexCache> dex_cache(hs.NewHandle(klass->GetDexCache())); element_object = Runtime::Current()->GetClassLinker()->ResolveString( - klass->GetDexFile(), dex::StringIndex(index), dex_cache); + klass.GetDexFile(), dex::StringIndex(index), hs.NewHandle(klass.GetDexCache())); set_object = true; if (element_object == nullptr) { return false; @@ -377,8 +451,12 @@ bool ProcessAnnotationValue(Handle<mirror::Class> klass, annotation_value->value_.SetI(index); } else { dex::TypeIndex type_index(index); + StackHandleScope<2> hs(self); element_object = Runtime::Current()->GetClassLinker()->ResolveType( - klass->GetDexFile(), type_index, klass.Get()); + klass.GetDexFile(), + type_index, + hs.NewHandle(klass.GetDexCache()), + hs.NewHandle(klass.GetClassLoader())); set_object = true; if (element_object == nullptr) { CHECK(self->IsExceptionPending()); @@ -399,12 +477,13 @@ bool ProcessAnnotationValue(Handle<mirror::Class> klass, if (result_style == DexFile::kAllRaw) { annotation_value->value_.SetI(index); } else { - StackHandleScope<2> hs(self); - Handle<mirror::DexCache> dex_cache(hs.NewHandle(klass->GetDexCache())); - Handle<mirror::ClassLoader> class_loader(hs.NewHandle(klass->GetClassLoader())); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + StackHandleScope<2> hs(self); ArtMethod* method = class_linker->ResolveMethodWithoutInvokeType( - klass->GetDexFile(), index, dex_cache, class_loader); + klass.GetDexFile(), + index, + hs.NewHandle(klass.GetDexCache()), + hs.NewHandle(klass.GetClassLoader())); if (method == nullptr) { return false; } @@ -439,10 +518,11 @@ bool ProcessAnnotationValue(Handle<mirror::Class> klass, annotation_value->value_.SetI(index); } else { StackHandleScope<2> hs(self); - Handle<mirror::DexCache> dex_cache(hs.NewHandle(klass->GetDexCache())); - Handle<mirror::ClassLoader> class_loader(hs.NewHandle(klass->GetClassLoader())); ArtField* field = Runtime::Current()->GetClassLinker()->ResolveFieldJLS( - klass->GetDexFile(), index, dex_cache, class_loader); + klass.GetDexFile(), + index, + hs.NewHandle(klass.GetDexCache()), + hs.NewHandle(klass.GetClassLoader())); if (field == nullptr) { return false; } @@ -467,10 +547,12 @@ bool ProcessAnnotationValue(Handle<mirror::Class> klass, annotation_value->value_.SetI(index); } else { StackHandleScope<3> hs(self); - Handle<mirror::DexCache> dex_cache(hs.NewHandle(klass->GetDexCache())); - Handle<mirror::ClassLoader> class_loader(hs.NewHandle(klass->GetClassLoader())); ArtField* enum_field = Runtime::Current()->GetClassLinker()->ResolveField( - klass->GetDexFile(), index, dex_cache, class_loader, true); + klass.GetDexFile(), + index, + hs.NewHandle(klass.GetDexCache()), + hs.NewHandle(klass.GetClassLoader()), + true); if (enum_field == nullptr) { return false; } else { @@ -595,10 +677,10 @@ bool ProcessAnnotationValue(Handle<mirror::Class> klass, return true; } -mirror::Object* CreateAnnotationMember(Handle<mirror::Class> klass, +mirror::Object* CreateAnnotationMember(const ClassData& klass, Handle<mirror::Class> annotation_class, const uint8_t** annotation) { - const DexFile& dex_file = klass->GetDexFile(); + const DexFile& dex_file = klass.GetDexFile(); Thread* self = Thread::Current(); ScopedObjectAccessUnchecked soa(self); StackHandleScope<5> hs(self); @@ -666,12 +748,12 @@ mirror::Object* CreateAnnotationMember(Handle<mirror::Class> klass, } const DexFile::AnnotationItem* GetAnnotationItemFromAnnotationSet( - Handle<mirror::Class> klass, + const ClassData& klass, const DexFile::AnnotationSetItem* annotation_set, uint32_t visibility, Handle<mirror::Class> annotation_class) REQUIRES_SHARED(Locks::mutator_lock_) { - const DexFile& dex_file = klass->GetDexFile(); + const DexFile& dex_file = klass.GetDexFile(); for (uint32_t i = 0; i < annotation_set->size_; ++i) { const DexFile::AnnotationItem* annotation_item = dex_file.GetAnnotationItem(annotation_set, i); if (!IsVisibilityCompatible(annotation_item->visibility_, visibility)) { @@ -679,12 +761,16 @@ const DexFile::AnnotationItem* GetAnnotationItemFromAnnotationSet( } const uint8_t* annotation = annotation_item->annotation_; uint32_t type_index = DecodeUnsignedLeb128(&annotation); + StackHandleScope<2> hs(Thread::Current()); mirror::Class* resolved_class = Runtime::Current()->GetClassLinker()->ResolveType( - klass->GetDexFile(), dex::TypeIndex(type_index), klass.Get()); + klass.GetDexFile(), + dex::TypeIndex(type_index), + hs.NewHandle(klass.GetDexCache()), + hs.NewHandle(klass.GetClassLoader())); if (resolved_class == nullptr) { std::string temp; LOG(WARNING) << StringPrintf("Unable to resolve %s annotation class %d", - klass->GetDescriptor(&temp), type_index); + klass.GetRealClass()->GetDescriptor(&temp), type_index); CHECK(Thread::Current()->IsExceptionPending()); Thread::Current()->ClearException(); continue; @@ -698,7 +784,7 @@ const DexFile::AnnotationItem* GetAnnotationItemFromAnnotationSet( } mirror::Object* GetAnnotationObjectFromAnnotationSet( - Handle<mirror::Class> klass, + const ClassData& klass, const DexFile::AnnotationSetItem* annotation_set, uint32_t visibility, Handle<mirror::Class> annotation_class) @@ -712,13 +798,13 @@ mirror::Object* GetAnnotationObjectFromAnnotationSet( return ProcessEncodedAnnotation(klass, &annotation); } -mirror::Object* GetAnnotationValue(Handle<mirror::Class> klass, +mirror::Object* GetAnnotationValue(const ClassData& klass, const DexFile::AnnotationItem* annotation_item, const char* annotation_name, Handle<mirror::Class> array_class, uint32_t expected_type) REQUIRES_SHARED(Locks::mutator_lock_) { - const DexFile& dex_file = klass->GetDexFile(); + const DexFile& dex_file = klass.GetDexFile(); const uint8_t* annotation = SearchEncodedAnnotation(dex_file, annotation_item->annotation_, annotation_name); if (annotation == nullptr) { @@ -745,10 +831,10 @@ mirror::Object* GetAnnotationValue(Handle<mirror::Class> klass, return annotation_value.value_.GetL(); } -mirror::ObjectArray<mirror::String>* GetSignatureValue(Handle<mirror::Class> klass, +mirror::ObjectArray<mirror::String>* GetSignatureValue(const ClassData& klass, const DexFile::AnnotationSetItem* annotation_set) REQUIRES_SHARED(Locks::mutator_lock_) { - const DexFile& dex_file = klass->GetDexFile(); + const DexFile& dex_file = klass.GetDexFile(); StackHandleScope<1> hs(Thread::Current()); const DexFile::AnnotationItem* annotation_item = SearchAnnotationSet(dex_file, annotation_set, "Ldalvik/annotation/Signature;", @@ -771,10 +857,10 @@ mirror::ObjectArray<mirror::String>* GetSignatureValue(Handle<mirror::Class> kla return obj->AsObjectArray<mirror::String>(); } -mirror::ObjectArray<mirror::Class>* GetThrowsValue(Handle<mirror::Class> klass, +mirror::ObjectArray<mirror::Class>* GetThrowsValue(const ClassData& klass, const DexFile::AnnotationSetItem* annotation_set) REQUIRES_SHARED(Locks::mutator_lock_) { - const DexFile& dex_file = klass->GetDexFile(); + const DexFile& dex_file = klass.GetDexFile(); StackHandleScope<1> hs(Thread::Current()); const DexFile::AnnotationItem* annotation_item = SearchAnnotationSet(dex_file, annotation_set, "Ldalvik/annotation/Throws;", @@ -798,11 +884,11 @@ mirror::ObjectArray<mirror::Class>* GetThrowsValue(Handle<mirror::Class> klass, } mirror::ObjectArray<mirror::Object>* ProcessAnnotationSet( - Handle<mirror::Class> klass, + const ClassData& klass, const DexFile::AnnotationSetItem* annotation_set, uint32_t visibility) REQUIRES_SHARED(Locks::mutator_lock_) { - const DexFile& dex_file = klass->GetDexFile(); + const DexFile& dex_file = klass.GetDexFile(); Thread* self = Thread::Current(); ScopedObjectAccessUnchecked soa(self); StackHandleScope<2> hs(self); @@ -856,11 +942,11 @@ mirror::ObjectArray<mirror::Object>* ProcessAnnotationSet( } mirror::ObjectArray<mirror::Object>* ProcessAnnotationSetRefList( - Handle<mirror::Class> klass, + const ClassData& klass, const DexFile::AnnotationSetRefList* set_ref_list, uint32_t size) REQUIRES_SHARED(Locks::mutator_lock_) { - const DexFile& dex_file = klass->GetDexFile(); + const DexFile& dex_file = klass.GetDexFile(); Thread* self = Thread::Current(); ScopedObjectAccessUnchecked soa(self); StackHandleScope<1> hs(self); @@ -899,15 +985,17 @@ mirror::Object* GetAnnotationForField(ArtField* field, Handle<mirror::Class> ann return nullptr; } StackHandleScope<1> hs(Thread::Current()); - Handle<mirror::Class> field_class(hs.NewHandle(field->GetDeclaringClass())); - return GetAnnotationObjectFromAnnotationSet(field_class, annotation_set, - DexFile::kDexVisibilityRuntime, annotation_class); + const ClassData field_class(hs, field); + return GetAnnotationObjectFromAnnotationSet(field_class, + annotation_set, + DexFile::kDexVisibilityRuntime, + annotation_class); } mirror::ObjectArray<mirror::Object>* GetAnnotationsForField(ArtField* field) { const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForField(field); StackHandleScope<1> hs(Thread::Current()); - Handle<mirror::Class> field_class(hs.NewHandle(field->GetDeclaringClass())); + const ClassData field_class(hs, field); return ProcessAnnotationSet(field_class, annotation_set, DexFile::kDexVisibilityRuntime); } @@ -917,7 +1005,7 @@ mirror::ObjectArray<mirror::String>* GetSignatureAnnotationForField(ArtField* fi return nullptr; } StackHandleScope<1> hs(Thread::Current()); - Handle<mirror::Class> field_class(hs.NewHandle(field->GetDeclaringClass())); + const ClassData field_class(hs, field); return GetSignatureValue(field_class, annotation_set); } @@ -927,17 +1015,17 @@ bool IsFieldAnnotationPresent(ArtField* field, Handle<mirror::Class> annotation_ return false; } StackHandleScope<1> hs(Thread::Current()); - Handle<mirror::Class> field_class(hs.NewHandle(field->GetDeclaringClass())); + const ClassData field_class(hs, field); const DexFile::AnnotationItem* annotation_item = GetAnnotationItemFromAnnotationSet( field_class, annotation_set, DexFile::kDexVisibilityRuntime, annotation_class); return annotation_item != nullptr; } mirror::Object* GetAnnotationDefaultValue(ArtMethod* method) { - const DexFile* dex_file = method->GetDexFile(); - mirror::Class* klass = method->GetDeclaringClass(); + const ClassData klass(method); + const DexFile* dex_file = &klass.GetDexFile(); const DexFile::AnnotationsDirectoryItem* annotations_dir = - dex_file->GetAnnotationsDirectory(*klass->GetClassDef()); + dex_file->GetAnnotationsDirectory(*klass.GetClassDef()); if (annotations_dir == nullptr) { return nullptr; } @@ -965,10 +1053,9 @@ mirror::Object* GetAnnotationDefaultValue(ArtMethod* method) { return nullptr; } DexFile::AnnotationValue annotation_value; - StackHandleScope<2> hs(Thread::Current()); - Handle<mirror::Class> h_klass(hs.NewHandle(klass)); + StackHandleScope<1> hs(Thread::Current()); Handle<mirror::Class> return_type(hs.NewHandle(method->GetReturnType(true /* resolve */))); - if (!ProcessAnnotationValue<false>(h_klass, + if (!ProcessAnnotationValue<false>(klass, &annotation, &annotation_value, return_type, @@ -983,17 +1070,15 @@ mirror::Object* GetAnnotationForMethod(ArtMethod* method, Handle<mirror::Class> if (annotation_set == nullptr) { return nullptr; } - StackHandleScope<1> hs(Thread::Current()); - Handle<mirror::Class> method_class(hs.NewHandle(method->GetDeclaringClass())); - return GetAnnotationObjectFromAnnotationSet(method_class, annotation_set, + return GetAnnotationObjectFromAnnotationSet(ClassData(method), annotation_set, DexFile::kDexVisibilityRuntime, annotation_class); } mirror::ObjectArray<mirror::Object>* GetAnnotationsForMethod(ArtMethod* method) { const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForMethod(method); - StackHandleScope<1> hs(Thread::Current()); - Handle<mirror::Class> method_class(hs.NewHandle(method->GetDeclaringClass())); - return ProcessAnnotationSet(method_class, annotation_set, DexFile::kDexVisibilityRuntime); + return ProcessAnnotationSet(ClassData(method), + annotation_set, + DexFile::kDexVisibilityRuntime); } mirror::ObjectArray<mirror::Class>* GetExceptionTypesForMethod(ArtMethod* method) { @@ -1001,9 +1086,7 @@ mirror::ObjectArray<mirror::Class>* GetExceptionTypesForMethod(ArtMethod* method if (annotation_set == nullptr) { return nullptr; } - StackHandleScope<1> hs(Thread::Current()); - Handle<mirror::Class> method_class(hs.NewHandle(method->GetDeclaringClass())); - return GetThrowsValue(method_class, annotation_set); + return GetThrowsValue(ClassData(method), annotation_set); } mirror::ObjectArray<mirror::Object>* GetParameterAnnotations(ArtMethod* method) { @@ -1019,9 +1102,7 @@ mirror::ObjectArray<mirror::Object>* GetParameterAnnotations(ArtMethod* method) return nullptr; } uint32_t size = set_ref_list->size_; - StackHandleScope<1> hs(Thread::Current()); - Handle<mirror::Class> method_class(hs.NewHandle(method->GetDeclaringClass())); - return ProcessAnnotationSetRefList(method_class, set_ref_list, size); + return ProcessAnnotationSetRefList(ClassData(method), set_ref_list, size); } mirror::Object* GetAnnotationForMethodParameter(ArtMethod* method, @@ -1045,9 +1126,7 @@ mirror::Object* GetAnnotationForMethodParameter(ArtMethod* method, const DexFile::AnnotationSetItem* annotation_set = dex_file->GetSetRefItemItem(annotation_set_ref); - StackHandleScope<1> hs(Thread::Current()); - Handle<mirror::Class> method_class(hs.NewHandle(method->GetDeclaringClass())); - return GetAnnotationObjectFromAnnotationSet(method_class, + return GetAnnotationObjectFromAnnotationSet(ClassData(method), annotation_set, DexFile::kDexVisibilityRuntime, annotation_class); @@ -1072,7 +1151,7 @@ bool GetParametersMetadataForMethod(ArtMethod* method, return false; } - StackHandleScope<5> hs(Thread::Current()); + StackHandleScope<4> hs(Thread::Current()); // Extract the parameters' names String[]. ObjPtr<mirror::Class> string_class = mirror::String::GetJavaLangString(); @@ -1082,9 +1161,9 @@ bool GetParametersMetadataForMethod(ArtMethod* method, return false; } - Handle<mirror::Class> klass = hs.NewHandle(method->GetDeclaringClass()); + ClassData data(method); Handle<mirror::Object> names_obj = - hs.NewHandle(GetAnnotationValue(klass, + hs.NewHandle(GetAnnotationValue(data, annotation_item, "names", string_array_class, @@ -1099,7 +1178,7 @@ bool GetParametersMetadataForMethod(ArtMethod* method, return false; } Handle<mirror::Object> access_flags_obj = - hs.NewHandle(GetAnnotationValue(klass, + hs.NewHandle(GetAnnotationValue(data, annotation_item, "accessFlags", int_array_class, @@ -1118,9 +1197,7 @@ mirror::ObjectArray<mirror::String>* GetSignatureAnnotationForMethod(ArtMethod* if (annotation_set == nullptr) { return nullptr; } - StackHandleScope<1> hs(Thread::Current()); - Handle<mirror::Class> method_class(hs.NewHandle(method->GetDeclaringClass())); - return GetSignatureValue(method_class, annotation_set); + return GetSignatureValue(ClassData(method), annotation_set); } bool IsMethodAnnotationPresent(ArtMethod* method, Handle<mirror::Class> annotation_class, @@ -1129,37 +1206,39 @@ bool IsMethodAnnotationPresent(ArtMethod* method, Handle<mirror::Class> annotati if (annotation_set == nullptr) { return false; } - StackHandleScope<1> hs(Thread::Current()); - Handle<mirror::Class> method_class(hs.NewHandle(method->GetDeclaringClass())); const DexFile::AnnotationItem* annotation_item = - GetAnnotationItemFromAnnotationSet(method_class, annotation_set, visibility, - annotation_class); + GetAnnotationItemFromAnnotationSet(ClassData(method), + annotation_set, visibility, annotation_class); return annotation_item != nullptr; } mirror::Object* GetAnnotationForClass(Handle<mirror::Class> klass, Handle<mirror::Class> annotation_class) { - const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(klass); + ClassData data(klass); + const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data); if (annotation_set == nullptr) { return nullptr; } - return GetAnnotationObjectFromAnnotationSet(klass, annotation_set, DexFile::kDexVisibilityRuntime, + return GetAnnotationObjectFromAnnotationSet(data, + annotation_set, + DexFile::kDexVisibilityRuntime, annotation_class); } mirror::ObjectArray<mirror::Object>* GetAnnotationsForClass(Handle<mirror::Class> klass) { - const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(klass); - return ProcessAnnotationSet(klass, annotation_set, DexFile::kDexVisibilityRuntime); + ClassData data(klass); + const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data); + return ProcessAnnotationSet(data, annotation_set, DexFile::kDexVisibilityRuntime); } mirror::ObjectArray<mirror::Class>* GetDeclaredClasses(Handle<mirror::Class> klass) { - const DexFile& dex_file = klass->GetDexFile(); - const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(klass); + ClassData data(klass); + const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data); if (annotation_set == nullptr) { return nullptr; } const DexFile::AnnotationItem* annotation_item = - SearchAnnotationSet(dex_file, annotation_set, "Ldalvik/annotation/MemberClasses;", + SearchAnnotationSet(data.GetDexFile(), annotation_set, "Ldalvik/annotation/MemberClasses;", DexFile::kDexVisibilitySystem); if (annotation_item == nullptr) { return nullptr; @@ -1172,7 +1251,7 @@ mirror::ObjectArray<mirror::Class>* GetDeclaredClasses(Handle<mirror::Class> kla return nullptr; } mirror::Object* obj = - GetAnnotationValue(klass, annotation_item, "value", class_array_class, + GetAnnotationValue(data, annotation_item, "value", class_array_class, DexFile::kDexAnnotationArray); if (obj == nullptr) { return nullptr; @@ -1181,18 +1260,18 @@ mirror::ObjectArray<mirror::Class>* GetDeclaredClasses(Handle<mirror::Class> kla } mirror::Class* GetDeclaringClass(Handle<mirror::Class> klass) { - const DexFile& dex_file = klass->GetDexFile(); - const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(klass); + ClassData data(klass); + const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data); if (annotation_set == nullptr) { return nullptr; } const DexFile::AnnotationItem* annotation_item = - SearchAnnotationSet(dex_file, annotation_set, "Ldalvik/annotation/EnclosingClass;", + SearchAnnotationSet(data.GetDexFile(), annotation_set, "Ldalvik/annotation/EnclosingClass;", DexFile::kDexVisibilitySystem); if (annotation_item == nullptr) { return nullptr; } - mirror::Object* obj = GetAnnotationValue(klass, annotation_item, "value", + mirror::Object* obj = GetAnnotationValue(data, annotation_item, "value", ScopedNullHandle<mirror::Class>(), DexFile::kDexAnnotationType); if (obj == nullptr) { @@ -1202,28 +1281,30 @@ mirror::Class* GetDeclaringClass(Handle<mirror::Class> klass) { } mirror::Class* GetEnclosingClass(Handle<mirror::Class> klass) { - const DexFile& dex_file = klass->GetDexFile(); mirror::Class* declaring_class = GetDeclaringClass(klass); if (declaring_class != nullptr) { return declaring_class; } - const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(klass); + ClassData data(klass); + const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data); if (annotation_set == nullptr) { return nullptr; } const DexFile::AnnotationItem* annotation_item = - SearchAnnotationSet(dex_file, annotation_set, "Ldalvik/annotation/EnclosingMethod;", + SearchAnnotationSet(data.GetDexFile(), + annotation_set, + "Ldalvik/annotation/EnclosingMethod;", DexFile::kDexVisibilitySystem); if (annotation_item == nullptr) { return nullptr; } const uint8_t* annotation = - SearchEncodedAnnotation(dex_file, annotation_item->annotation_, "value"); + SearchEncodedAnnotation(data.GetDexFile(), annotation_item->annotation_, "value"); if (annotation == nullptr) { return nullptr; } DexFile::AnnotationValue annotation_value; - if (!ProcessAnnotationValue<false>(klass, + if (!ProcessAnnotationValue<false>(data, &annotation, &annotation_value, ScopedNullHandle<mirror::Class>(), @@ -1234,10 +1315,11 @@ mirror::Class* GetEnclosingClass(Handle<mirror::Class> klass) { return nullptr; } StackHandleScope<2> hs(Thread::Current()); - Handle<mirror::DexCache> dex_cache(hs.NewHandle(klass->GetDexCache())); - Handle<mirror::ClassLoader> class_loader(hs.NewHandle(klass->GetClassLoader())); ArtMethod* method = Runtime::Current()->GetClassLinker()->ResolveMethodWithoutInvokeType( - klass->GetDexFile(), annotation_value.value_.GetI(), dex_cache, class_loader); + data.GetDexFile(), + annotation_value.value_.GetI(), + hs.NewHandle(data.GetDexCache()), + hs.NewHandle(data.GetClassLoader())); if (method == nullptr) { return nullptr; } @@ -1245,39 +1327,44 @@ mirror::Class* GetEnclosingClass(Handle<mirror::Class> klass) { } mirror::Object* GetEnclosingMethod(Handle<mirror::Class> klass) { - const DexFile& dex_file = klass->GetDexFile(); - const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(klass); + ClassData data(klass); + const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data); if (annotation_set == nullptr) { return nullptr; } const DexFile::AnnotationItem* annotation_item = - SearchAnnotationSet(dex_file, annotation_set, "Ldalvik/annotation/EnclosingMethod;", + SearchAnnotationSet(data.GetDexFile(), + annotation_set, + "Ldalvik/annotation/EnclosingMethod;", DexFile::kDexVisibilitySystem); if (annotation_item == nullptr) { return nullptr; } - return GetAnnotationValue(klass, annotation_item, "value", ScopedNullHandle<mirror::Class>(), + return GetAnnotationValue(data, annotation_item, "value", ScopedNullHandle<mirror::Class>(), DexFile::kDexAnnotationMethod); } bool GetInnerClass(Handle<mirror::Class> klass, mirror::String** name) { - const DexFile& dex_file = klass->GetDexFile(); - const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(klass); + ClassData data(klass); + const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data); if (annotation_set == nullptr) { return false; } const DexFile::AnnotationItem* annotation_item = SearchAnnotationSet( - dex_file, annotation_set, "Ldalvik/annotation/InnerClass;", DexFile::kDexVisibilitySystem); + data.GetDexFile(), + annotation_set, + "Ldalvik/annotation/InnerClass;", + DexFile::kDexVisibilitySystem); if (annotation_item == nullptr) { return false; } const uint8_t* annotation = - SearchEncodedAnnotation(dex_file, annotation_item->annotation_, "name"); + SearchEncodedAnnotation(data.GetDexFile(), annotation_item->annotation_, "name"); if (annotation == nullptr) { return false; } DexFile::AnnotationValue annotation_value; - if (!ProcessAnnotationValue<false>(klass, + if (!ProcessAnnotationValue<false>(data, &annotation, &annotation_value, ScopedNullHandle<mirror::Class>(), @@ -1293,24 +1380,24 @@ bool GetInnerClass(Handle<mirror::Class> klass, mirror::String** name) { } bool GetInnerClassFlags(Handle<mirror::Class> klass, uint32_t* flags) { - const DexFile& dex_file = klass->GetDexFile(); - const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(klass); + ClassData data(klass); + const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data); if (annotation_set == nullptr) { return false; } const DexFile::AnnotationItem* annotation_item = - SearchAnnotationSet(dex_file, annotation_set, "Ldalvik/annotation/InnerClass;", + SearchAnnotationSet(data.GetDexFile(), annotation_set, "Ldalvik/annotation/InnerClass;", DexFile::kDexVisibilitySystem); if (annotation_item == nullptr) { return false; } const uint8_t* annotation = - SearchEncodedAnnotation(dex_file, annotation_item->annotation_, "accessFlags"); + SearchEncodedAnnotation(data.GetDexFile(), annotation_item->annotation_, "accessFlags"); if (annotation == nullptr) { return false; } DexFile::AnnotationValue annotation_value; - if (!ProcessAnnotationValue<false>(klass, + if (!ProcessAnnotationValue<false>(data, &annotation, &annotation_value, ScopedNullHandle<mirror::Class>(), @@ -1325,20 +1412,22 @@ bool GetInnerClassFlags(Handle<mirror::Class> klass, uint32_t* flags) { } mirror::ObjectArray<mirror::String>* GetSignatureAnnotationForClass(Handle<mirror::Class> klass) { - const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(klass); + ClassData data(klass); + const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data); if (annotation_set == nullptr) { return nullptr; } - return GetSignatureValue(klass, annotation_set); + return GetSignatureValue(data, annotation_set); } bool IsClassAnnotationPresent(Handle<mirror::Class> klass, Handle<mirror::Class> annotation_class) { - const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(klass); + ClassData data(klass); + const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data); if (annotation_set == nullptr) { return false; } const DexFile::AnnotationItem* annotation_item = GetAnnotationItemFromAnnotationSet( - klass, annotation_set, DexFile::kDexVisibilityRuntime, annotation_class); + data, annotation_set, DexFile::kDexVisibilityRuntime, annotation_class); return annotation_item != nullptr; } diff --git a/test/980-redefine-object/check b/test/980-redefine-object/check new file mode 100755 index 0000000000..987066fe15 --- /dev/null +++ b/test/980-redefine-object/check @@ -0,0 +1,20 @@ +#!/bin/bash +# +# Copyright (C) 2014 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. + +# The number of paused background threads (and therefore InterruptedExceptions) +# can change so we will just delete their lines from the log. + +sed "/Object allocated of type 'Ljava\/lang\/InterruptedException;'/d" "$2" | diff --strip-trailing-cr -q "$1" - >/dev/null diff --git a/test/980-redefine-object/expected.txt b/test/980-redefine-object/expected.txt new file mode 100644 index 0000000000..6e9bce027a --- /dev/null +++ b/test/980-redefine-object/expected.txt @@ -0,0 +1,52 @@ + Initializing and loading the TestWatcher class that will (eventually) be notified of object allocations + Allocating an j.l.Object before redefining Object class + Allocating a Transform before redefining Object class + Redefining the Object class to add a hook into the <init> method +Object allocated of type 'Ljava/lang/StringBuilder;' +Object allocated of type 'Ljava/nio/HeapCharBuffer;' + Allocating an j.l.Object after redefining Object class +Object allocated of type 'Ljava/lang/Object;' +Object allocated of type 'Ljava/lang/StringBuilder;' +Object allocated of type 'Ljava/nio/HeapCharBuffer;' + Allocating a Transform after redefining Object class +Object allocated of type 'LTransform;' +Object allocated of type 'Ljava/lang/StringBuilder;' +Object allocated of type 'Ljava/nio/HeapCharBuffer;' + Allocating an int[] after redefining Object class +Object allocated of type 'Ljava/lang/StringBuilder;' +Object allocated of type 'Ljava/nio/HeapCharBuffer;' + Allocating an array list +Object allocated of type 'Ljava/util/ArrayList;' +Object allocated of type 'Ljava/lang/StringBuilder;' +Object allocated of type 'Ljava/nio/HeapCharBuffer;' + Adding a bunch of stuff to the array list +Object allocated of type 'Ljava/lang/Object;' +Object allocated of type 'Ljava/lang/Object;' +Object allocated of type 'LTransform;' +Object allocated of type 'Ljava/lang/StringBuilder;' +Object allocated of type 'Ljava/nio/HeapCharBuffer;' + Allocating a linked list +Object allocated of type 'Ljava/util/LinkedList;' +Object allocated of type 'Ljava/lang/StringBuilder;' +Object allocated of type 'Ljava/nio/HeapCharBuffer;' + Adding a bunch of stuff to the linked list +Object allocated of type 'Ljava/lang/Object;' +Object allocated of type 'Ljava/util/LinkedList$Node;' +Object allocated of type 'Ljava/lang/Object;' +Object allocated of type 'Ljava/util/LinkedList$Node;' +Object allocated of type 'Ljava/util/LinkedList$Node;' +Object allocated of type 'Ljava/util/LinkedList$Node;' +Object allocated of type 'Ljava/util/LinkedList$Node;' +Object allocated of type 'Ljava/util/LinkedList$Node;' +Object allocated of type 'LTransform;' +Object allocated of type 'Ljava/util/LinkedList$Node;' +Object allocated of type 'Ljava/lang/StringBuilder;' +Object allocated of type 'Ljava/nio/HeapCharBuffer;' + Throwing from down 4 stack frames +Object allocated of type 'Ljava/lang/Exception;' +Object allocated of type 'Ljava/lang/StringBuilder;' +Object allocated of type 'Ljava/nio/HeapCharBuffer;' + Exception caught. +Object allocated of type 'Ljava/lang/StringBuilder;' +Object allocated of type 'Ljava/nio/HeapCharBuffer;' + Finishing test! diff --git a/test/980-redefine-object/info.txt b/test/980-redefine-object/info.txt new file mode 100644 index 0000000000..f3e01b596d --- /dev/null +++ b/test/980-redefine-object/info.txt @@ -0,0 +1,23 @@ +Tests basic functions in the jvmti plugin. + +This tests that we are able to redefine methods/constructors on the +java.lang.Object class at runtime. + +This also (indirectly) tests that we correctly handle reading annotations on +obsolete methods. This is something that is not normally done since there is no +way to get a reference to an obsolete method outside of the runtime but some +annotations on the Object class are read by the runtime directly. + +NB This test cannot be run on the RI at the moment. + +If this test starts failing during the doCommonClassRedefinition call it is +possible that the definition of Object contained in the base64 DEX_BYTES array +has become stale and will need to be recreated. The only difference from the +normal Object dex bytes is that (a) it contains only the bytes of the Object +class itself, and (b) it adds an +'invoke-static {p0}, Ljava/lang/Object;->NotifyConstructed(Ljava/lang/Object;)V' +to the <init> function. + +It is also possible it could fail due to the pattern of allocations caused by +doing string concatenation or printing changing. In this case you should simply +update the expected.txt file. diff --git a/test/980-redefine-object/redefine_object.cc b/test/980-redefine-object/redefine_object.cc new file mode 100644 index 0000000000..daae08792a --- /dev/null +++ b/test/980-redefine-object/redefine_object.cc @@ -0,0 +1,54 @@ +/* + * 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. + */ + +#include <inttypes.h> +#include <iostream> + +#include "android-base/stringprintf.h" +#include "base/logging.h" +#include "base/macros.h" +#include "jni.h" +#include "jvmti.h" +#include "ScopedUtfChars.h" + +#include "ti-agent/common_helper.h" +#include "ti-agent/common_load.h" + +namespace art { +namespace Test980RedefineObjects { + +extern "C" JNIEXPORT void JNICALL Java_Main_bindFunctionsForClass( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass target) { + BindFunctionsOnClass(jvmti_env, env, target); +} + +extern "C" JNIEXPORT void JNICALL Java_art_test_TestWatcher_NotifyConstructed( + JNIEnv* env, jclass TestWatcherClass ATTRIBUTE_UNUSED, jobject constructed) { + char* sig = nullptr; + char* generic_sig = nullptr; + if (JvmtiErrorToException(env, jvmti_env->GetClassSignature(env->GetObjectClass(constructed), + &sig, + &generic_sig))) { + // Exception. + return; + } + std::cout << "Object allocated of type '" << sig << "'" << std::endl; + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(sig)); + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(generic_sig)); +} + +} // namespace Test980RedefineObjects +} // namespace art diff --git a/test/980-redefine-object/run b/test/980-redefine-object/run new file mode 100755 index 0000000000..c6e62ae6cd --- /dev/null +++ b/test/980-redefine-object/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/980-redefine-object/src-ex/TestWatcher.java b/test/980-redefine-object/src-ex/TestWatcher.java new file mode 100644 index 0000000000..d15e68871c --- /dev/null +++ b/test/980-redefine-object/src-ex/TestWatcher.java @@ -0,0 +1,25 @@ +/* + * 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. + */ + +package art.test; + +public class TestWatcher { + // NB This function is native since it is called in the Object.<init> method and so cannot cause + // any java allocations at all. The normal System.out.print* functions will cause allocations to + // occur so we cannot use them. This means the easiest way to report the object as being created + // is to go into native code and do it there. + public static native void NotifyConstructed(Object o); +} diff --git a/test/980-redefine-object/src/Main.java b/test/980-redefine-object/src/Main.java new file mode 100644 index 0000000000..348951c4ba --- /dev/null +++ b/test/980-redefine-object/src/Main.java @@ -0,0 +1,390 @@ +/* + * 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. + */ + +import java.util.ArrayList; +import java.util.Base64; +import java.util.LinkedList; + +public class Main { + + // TODO We should make this run on the RI. + /** + * This test cannot be run on the RI. + */ + private static final byte[] CLASS_BYTES = new byte[0]; + + // TODO It might be a good idea to replace this hard-coded Object definition with a + // retransformation based test. + /** + * Base64 encoding of the following smali file. + * + * .class public Ljava/lang/Object; + * .source "Object.java" + * # instance fields + * .field private transient shadow$_klass_:Ljava/lang/Class; + * .annotation system Ldalvik/annotation/Signature; + * value = { + * "Ljava/lang/Class", + * "<*>;" + * } + * .end annotation + * .end field + * + * .field private transient shadow$_monitor_:I + * # direct methods + * .method public constructor <init>()V + * .registers 1 + * .prologue + * invoke-static {p0}, Lart/test/TestWatcher;->NotifyConstructed(Ljava/lang/Object;)V + * return-void + * .end method + * + * .method static identityHashCode(Ljava/lang/Object;)I + * .registers 7 + * .prologue + * iget v0, p0, Ljava/lang/Object;->shadow$_monitor_:I + * const/high16 v3, -0x40000000 # -2.0f + * const/high16 v2, -0x80000000 + * const v1, 0xfffffff + * const/high16 v4, -0x40000000 # -2.0f + * and-int/2addr v4, v0 + * const/high16 v5, -0x80000000 + * if-ne v4, v5, :cond_15 + * const v4, 0xfffffff + * and-int/2addr v4, v0 + * return v4 + * :cond_15 + * invoke-static {p0}, Ljava/lang/Object;->identityHashCodeNative(Ljava/lang/Object;)I + * move-result v4 + * return v4 + * .end method + * + * .method private static native identityHashCodeNative(Ljava/lang/Object;)I + * .annotation build Ldalvik/annotation/optimization/FastNative; + * .end annotation + * .end method + * + * .method private native internalClone()Ljava/lang/Object; + * .annotation build Ldalvik/annotation/optimization/FastNative; + * .end annotation + * .end method + * + * + * # virtual methods + * .method protected clone()Ljava/lang/Object; + * .registers 4 + * .annotation system Ldalvik/annotation/Throws; + * value = { + * Ljava/lang/CloneNotSupportedException; + * } + * .end annotation + * + * .prologue + * instance-of v0, p0, Ljava/lang/Cloneable; + * if-nez v0, :cond_2d + * new-instance v0, Ljava/lang/CloneNotSupportedException; + * new-instance v1, Ljava/lang/StringBuilder; + * invoke-direct {v1}, Ljava/lang/StringBuilder;-><init>()V + * const-string/jumbo v2, "Class " + * invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + * move-result-object v1 + * invoke-virtual {p0}, Ljava/lang/Object;->getClass()Ljava/lang/Class; + * move-result-object v2 + * invoke-virtual {v2}, Ljava/lang/Class;->getName()Ljava/lang/String; + * move-result-object v2 + * invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + * move-result-object v1 + * const-string/jumbo v2, " doesn\'t implement Cloneable" + * invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + * move-result-object v1 + * invoke-virtual {v1}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String; + * move-result-object v1 + * invoke-direct {v0, v1}, Ljava/lang/CloneNotSupportedException;-><init>(Ljava/lang/String;)V + * throw v0 + * :cond_2d + * invoke-direct {p0}, Ljava/lang/Object;->internalClone()Ljava/lang/Object; + * move-result-object v0 + * return-object v0 + * .end method + * + * .method public equals(Ljava/lang/Object;)Z + * .registers 3 + * .prologue + * if-ne p0, p1, :cond_4 + * const/4 v0, 0x1 + * :goto_3 + * return v0 + * :cond_4 + * const/4 v0, 0x0 + * goto :goto_3 + * .end method + * + * .method protected finalize()V + * .registers 1 + * .annotation system Ldalvik/annotation/Throws; + * value = { + * Ljava/lang/Throwable; + * } + * .end annotation + * .prologue + * return-void + * .end method + * + * .method public final getClass()Ljava/lang/Class; + * .registers 2 + * .annotation system Ldalvik/annotation/Signature; + * value = { + * "()", + * "Ljava/lang/Class", + * "<*>;" + * } + * .end annotation + * .prologue + * iget-object v0, p0, Ljava/lang/Object;->shadow$_klass_:Ljava/lang/Class; + * return-object v0 + * .end method + * + * .method public hashCode()I + * .registers 2 + * .prologue + * invoke-static {p0}, Ljava/lang/Object;->identityHashCode(Ljava/lang/Object;)I + * move-result v0 + * return v0 + * .end method + * + * .method public final native notify()V + * .annotation build Ldalvik/annotation/optimization/FastNative; + * .end annotation + * .end method + * + * .method public final native notifyAll()V + * .annotation build Ldalvik/annotation/optimization/FastNative; + * .end annotation + * .end method + * + * .method public toString()Ljava/lang/String; + * .registers 3 + * .prologue + * new-instance v0, Ljava/lang/StringBuilder; + * invoke-direct {v0}, Ljava/lang/StringBuilder;-><init>()V + * invoke-virtual {p0}, Ljava/lang/Object;->getClass()Ljava/lang/Class; + * move-result-object v1 + * invoke-virtual {v1}, Ljava/lang/Class;->getName()Ljava/lang/String; + * move-result-object v1 + * invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + * move-result-object v0 + * const-string/jumbo v1, "@" + * invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + * move-result-object v0 + * invoke-virtual {p0}, Ljava/lang/Object;->hashCode()I + * move-result v1 + * invoke-static {v1}, Ljava/lang/Integer;->toHexString(I)Ljava/lang/String; + * move-result-object v1 + * invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + * move-result-object v0 + * invoke-virtual {v0}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String; + * move-result-object v0 + * return-object v0 + * .end method + * + * .method public final native wait()V + * .annotation system Ldalvik/annotation/Throws; + * value = { + * Ljava/lang/InterruptedException; + * } + * .end annotation + * + * .annotation build Ldalvik/annotation/optimization/FastNative; + * .end annotation + * .end method + * + * .method public final wait(J)V + * .registers 4 + * .annotation system Ldalvik/annotation/Throws; + * value = { + * Ljava/lang/InterruptedException; + * } + * .end annotation + * .prologue + * const/4 v0, 0x0 + * invoke-virtual {p0, p1, p2, v0}, Ljava/lang/Object;->wait(JI)V + * return-void + * .end method + * + * .method public final native wait(JI)V + * .annotation system Ldalvik/annotation/Throws; + * value = { + * Ljava/lang/InterruptedException; + * } + * .end annotation + * + * .annotation build Ldalvik/annotation/optimization/FastNative; + * .end annotation + * .end method + */ + private static final byte[] DEX_BYTES = Base64.getDecoder().decode( + "ZGV4CjAzNQDUlMR9j03MYuOKekKs2p7zJzu2IfDb7RlMCgAAcAAAAHhWNBIAAAAAAAAAAIgJAAA6" + + "AAAAcAAAABEAAABYAQAADQAAAJwBAAACAAAAOAIAABYAAABIAgAAAQAAAPgCAAA0BwAAGAMAABgD" + + "AAA2AwAAOgMAAEADAABIAwAASwMAAFMDAABWAwAAWgMAAF0DAABgAwAAZAMAAGgDAACAAwAAnwMA" + + "ALsDAADoAwAA+gMAAA0EAAA1BAAATAQAAGEEAACDBAAAlwQAAKsEAADGBAAA3QQAAPAEAAD9BAAA" + + "AAUAAAQFAAAJBQAADQUAABAFAAAUBQAAHAUAACMFAAArBQAANQUAAD8FAABIBQAAUgUAAGQFAAB8" + + "BQAAiwUAAJUFAACnBQAAugUAAM0FAADVBQAA3QUAAOgFAADtBQAA/QUAAA8GAAAcBgAAJgYAAC0G" + + "AAAGAAAACAAAAAwAAAANAAAADgAAAA8AAAARAAAAEgAAABMAAAAUAAAAFQAAABYAAAAXAAAAGAAA" + + "ABkAAAAcAAAAIAAAAAYAAAAAAAAAAAAAAAcAAAAAAAAAPAYAAAkAAAAGAAAAAAAAAAkAAAALAAAA" + + "AAAAAAkAAAAMAAAAAAAAAAoAAAAMAAAARAYAAAsAAAANAAAAVAYAABwAAAAPAAAAAAAAAB0AAAAP" + + "AAAATAYAAB4AAAAPAAAANAYAAB8AAAAPAAAAPAYAAB8AAAAPAAAAVAYAACEAAAAQAAAAPAYAAAsA" + + "BgA0AAAACwAAADUAAAACAAoAGgAAAAYABAAnAAAABwALAAMAAAAJAAUANgAAAAsABwADAAAACwAD" + + "ACMAAAALAAwAJAAAAAsABwAlAAAACwACACYAAAALAAAAKAAAAAsAAQApAAAACwABACoAAAALAAMA" + + "KwAAAAsABwAxAAAACwAHADIAAAALAAQANwAAAAsABwA5AAAACwAIADkAAAALAAkAOQAAAA0ABwAD" + + "AAAADQAGACIAAAANAAQANwAAAAsAAAABAAAA/////wAAAAAbAAAA0AYAAD4JAAAAAAAAHCBkb2Vz" + + "bid0IGltcGxlbWVudCBDbG9uZWFibGUAAigpAAQ8Kj47AAY8aW5pdD4AAUAABkNsYXNzIAABSQAC" + + "SUwAAUoAAUwAAkxJAAJMTAAWTGFydC90ZXN0L1Rlc3RXYXRjaGVyOwAdTGRhbHZpay9hbm5vdGF0" + + "aW9uL1NpZ25hdHVyZTsAGkxkYWx2aWsvYW5ub3RhdGlvbi9UaHJvd3M7ACtMZGFsdmlrL2Fubm90" + + "YXRpb24vb3B0aW1pemF0aW9uL0Zhc3ROYXRpdmU7ABBMamF2YS9sYW5nL0NsYXNzABFMamF2YS9s" + + "YW5nL0NsYXNzOwAmTGphdmEvbGFuZy9DbG9uZU5vdFN1cHBvcnRlZEV4Y2VwdGlvbjsAFUxqYXZh" + + "L2xhbmcvQ2xvbmVhYmxlOwATTGphdmEvbGFuZy9JbnRlZ2VyOwAgTGphdmEvbGFuZy9JbnRlcnJ1" + + "cHRlZEV4Y2VwdGlvbjsAEkxqYXZhL2xhbmcvT2JqZWN0OwASTGphdmEvbGFuZy9TdHJpbmc7ABlM" + + "amF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7ABVMamF2YS9sYW5nL1Rocm93YWJsZTsAEU5vdGlmeUNv" + + "bnN0cnVjdGVkAAtPYmplY3QuamF2YQABVgACVkoAA1ZKSQACVkwAAVoAAlpMAAZhcHBlbmQABWNs" + + "b25lAAZlcXVhbHMACGZpbmFsaXplAAhnZXRDbGFzcwAHZ2V0TmFtZQAIaGFzaENvZGUAEGlkZW50" + + "aXR5SGFzaENvZGUAFmlkZW50aXR5SGFzaENvZGVOYXRpdmUADWludGVybmFsQ2xvbmUACGxvY2tX" + + "b3JkABBsb2NrV29yZEhhc2hNYXNrABFsb2NrV29yZFN0YXRlSGFzaAARbG9ja1dvcmRTdGF0ZU1h" + + "c2sABm1pbGxpcwAGbm90aWZ5AAlub3RpZnlBbGwAA29iagAOc2hhZG93JF9rbGFzc18AEHNoYWRv" + + "dyRfbW9uaXRvcl8AC3RvSGV4U3RyaW5nAAh0b1N0cmluZwAFdmFsdWUABHdhaXQAAAIAAAABAAAA" + + "AQAAAAsAAAABAAAAAAAAAAEAAAABAAAAAQAAAAwAAgQBOBwBGAcCBAE4HAEYCgIDATgcAhcQFwIC" + + "BAE4HAEYDgAFAAIDATgcAxcBFxAXAgAAAAAAAAAAAAEAAABaBgAAAgAAAGIGAAB8BgAAAQAAAGIG" + + "AAABAAAAagYAAAEAAAB0BgAAAQAAAHwGAAABAAAAfwYAAAAAAAABAAAACgAAAAAAAAAAAAAAsAYA" + + "AAUAAACUBgAABwAAALgGAAAIAAAAyAYAAAsAAADABgAADAAAAMAGAAANAAAAwAYAAA4AAADABgAA" + + "EAAAAJwGAAARAAAAqAYAABIAAACcBgAAKAAHDgBwATQHDi0DAC0BLQMDMAEtAwIvATwDAS4BeFsA" + + "7AEABw5LARoPOsYArAEBNAcOAMUEAAcOAEEABw4AaAAHDgCRAgAHDgCmAwExBw5LAAAAAQABAAEA" + + "AAA4BwAABAAAAHEQAAAAAA4ABwABAAEAAAA9BwAAGgAAAFJgAQAVAwDAFQIAgBQB////DxUEAMC1" + + "BBUFAIAzVAcAFAT///8PtQQPBHEQCwAGAAoEDwQEAAEAAgAAAFkHAAAyAAAAIDAIADkAKwAiAAcA" + + "IgENAHAQEwABABsCBQAAAG4gFAAhAAwBbhAIAAMADAJuEAEAAgAMAm4gFAAhAAwBGwIAAAAAbiAU" + + "ACEADAFuEBUAAQAMAXAgAgAQACcAcBAMAAMADAARAAMAAgAAAAAAZQcAAAYAAAAzIQQAEhAPABIA" + + "KP4BAAEAAAAAAGwHAAABAAAADgAAAAIAAQAAAAAAcgcAAAMAAABUEAAAEQAAAAIAAQABAAAAdwcA" + + "AAUAAABxEAoAAQAKAA8AAAADAAEAAgAAAHwHAAApAAAAIgANAHAQEwAAAG4QCAACAAwBbhABAAEA" + + "DAFuIBQAEAAMABsBBAAAAG4gFAAQAAwAbhAJAAIACgFxEAMAAQAMAW4gFAAQAAwAbhAVAAAADAAR" + + "AAAABAADAAQAAACCBwAABQAAABIAbkASACEDDgAAAgQLAIIBAYIBBIGABIwPBgikDwGKAgABggIA" + + "BQToDwEB3BABBPgQARGMEQEBpBEEkQIAAZECAAEBwBEBkQIAARGkEgGRAgAAABAAAAAAAAAAAQAA" + + "AAAAAAABAAAAOgAAAHAAAAACAAAAEQAAAFgBAAADAAAADQAAAJwBAAAEAAAAAgAAADgCAAAFAAAA" + + "FgAAAEgCAAAGAAAAAQAAAPgCAAACIAAAOgAAABgDAAABEAAABQAAADQGAAAEIAAABgAAAFoGAAAD" + + "EAAACQAAAIwGAAAGIAAAAQAAANAGAAADIAAACQAAADgHAAABIAAACQAAAIwHAAAAIAAAAQAAAD4J" + + "AAAAEAAAAQAAAIgJAAA="); + + private static final String LISTENER_LOCATION = + System.getenv("DEX_LOCATION") + "/980-redefine-object-ex.jar"; + + public static void main(String[] args) { + doTest(); + } + + private static void ensureTestWatcherInitialized() { + try { + // Make sure the TestWatcher class can be found from the Object <init> function. + addToBootClassLoader(LISTENER_LOCATION); + // Load TestWatcher from the bootclassloader and make sure it is initialized. + Class<?> testwatcher_class = Class.forName("art.test.TestWatcher", true, null); + // Bind the native functions of testwatcher_class. + bindFunctionsForClass(testwatcher_class); + } catch (Exception e) { + throw new Error("Exception while making testwatcher", e); + } + } + + // NB This function will cause 2 objects of type "Ljava/nio/HeapCharBuffer;" and + // "Ljava/nio/HeapCharBuffer;" to be allocated each time it is called. + private static void safePrintln(Object o) { + System.out.flush(); + System.out.print("\t" + o + "\n"); + System.out.flush(); + } + + private static void throwFrom(int depth) throws Exception { + if (depth <= 0) { + throw new Exception("Throwing the exception"); + } else { + throwFrom(depth - 1); + } + } + + public static void doTest() { + safePrintln("Initializing and loading the TestWatcher class that will (eventually) be " + + "notified of object allocations"); + // Make sure the TestWatcher class is initialized before we do anything else. + ensureTestWatcherInitialized(); + safePrintln("Allocating an j.l.Object before redefining Object class"); + // Make sure these aren't shown. + Object o = new Object(); + safePrintln("Allocating a Transform before redefining Object class"); + Transform t = new Transform(); + + // Redefine the Object Class. + safePrintln("Redefining the Object class to add a hook into the <init> method"); + doCommonClassRedefinition(Object.class, CLASS_BYTES, DEX_BYTES); + + safePrintln("Allocating an j.l.Object after redefining Object class"); + Object o2 = new Object(); + safePrintln("Allocating a Transform after redefining Object class"); + Transform t2 = new Transform(); + + // This shouldn't cause the Object constructor to be run. + safePrintln("Allocating an int[] after redefining Object class"); + int[] abc = new int[12]; + + // Try adding stuff to an array list. + safePrintln("Allocating an array list"); + ArrayList<Object> al = new ArrayList<>(); + safePrintln("Adding a bunch of stuff to the array list"); + al.add(new Object()); + al.add(new Object()); + al.add(o2); + al.add(o); + al.add(t); + al.add(t2); + al.add(new Transform()); + + // Try adding stuff to a LinkedList + safePrintln("Allocating a linked list"); + LinkedList<Object> ll = new LinkedList<>(); + safePrintln("Adding a bunch of stuff to the linked list"); + ll.add(new Object()); + ll.add(new Object()); + ll.add(o2); + ll.add(o); + ll.add(t); + ll.add(t2); + ll.add(new Transform()); + + // Try making an exception. + safePrintln("Throwing from down 4 stack frames"); + try { + throwFrom(4); + } catch (Exception e) { + safePrintln("Exception caught."); + } + + safePrintln("Finishing test!"); + } + + private static native void addToBootClassLoader(String s); + + private static native void bindFunctionsForClass(Class<?> target); + + // Transforms the class + private static native void doCommonClassRedefinition(Class<?> target, + byte[] class_file, + byte[] dex_file); +} diff --git a/test/980-redefine-object/src/Transform.java b/test/980-redefine-object/src/Transform.java new file mode 100644 index 0000000000..23f67d96c7 --- /dev/null +++ b/test/980-redefine-object/src/Transform.java @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class Transform { } diff --git a/test/Android.bp b/test/Android.bp index 594cce2a6e..3bb3ef8de7 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -275,6 +275,7 @@ art_cc_defaults { "936-search-onload/search_onload.cc", "944-transform-classloaders/classloader.cc", "945-obsolete-native/obsolete_native.cc", + "980-redefine-object/redefine_object.cc", ], shared_libs: [ "libbase", diff --git a/test/ti-agent/common_helper.cc b/test/ti-agent/common_helper.cc index 4ddd0aafb0..6316a9c368 100644 --- a/test/ti-agent/common_helper.cc +++ b/test/ti-agent/common_helper.cc @@ -520,11 +520,14 @@ void BindFunctions(jvmtiEnv* jenv, JNIEnv* env, const char* class_name) { LOG(FATAL) << "Could not load " << class_name; } } + BindFunctionsOnClass(jenv, env, klass.get()); +} +void BindFunctionsOnClass(jvmtiEnv* jenv, JNIEnv* env, jclass klass) { // Use JVMTI to get the methods. jint method_count; jmethodID* methods; - jvmtiError methods_result = jenv->GetClassMethods(klass.get(), &method_count, &methods); + jvmtiError methods_result = jenv->GetClassMethods(klass, &method_count, &methods); if (methods_result != JVMTI_ERROR_NONE) { LOG(FATAL) << "Could not get methods"; } @@ -538,7 +541,7 @@ void BindFunctions(jvmtiEnv* jenv, JNIEnv* env, const char* class_name) { } constexpr jint kNative = static_cast<jint>(kAccNative); if ((modifiers & kNative) != 0) { - BindMethod(jenv, env, klass.get(), methods[i]); + BindMethod(jenv, env, klass, methods[i]); } } diff --git a/test/ti-agent/common_helper.h b/test/ti-agent/common_helper.h index 0a316edc7b..f10356dcbb 100644 --- a/test/ti-agent/common_helper.h +++ b/test/ti-agent/common_helper.h @@ -81,6 +81,7 @@ bool JvmtiErrorToException(JNIEnv* env, jvmtiError error); // // This will abort on failure. void BindFunctions(jvmtiEnv* jvmti_env, JNIEnv* env, const char* class_name); +void BindFunctionsOnClass(jvmtiEnv* jvmti_env, JNIEnv* env, jclass klass); } // namespace art |