diff options
47 files changed, 1419 insertions, 142 deletions
diff --git a/compiler/utils/assembler_thumb_test_expected.cc.inc b/compiler/utils/assembler_thumb_test_expected.cc.inc index 842716fac3..49ac2f571d 100644 --- a/compiler/utils/assembler_thumb_test_expected.cc.inc +++ b/compiler/utils/assembler_thumb_test_expected.cc.inc @@ -76,7 +76,7 @@ const char* const VixlJniHelpersResults[] = { " f0: f1bc 0f00 cmp.w ip, #0\n", " f4: bf18 it ne\n", " f6: f20d 4c01 addwne ip, sp, #1025 ; 0x401\n", - " fa: f8d9 c09c ldr.w ip, [r9, #156] ; 0x9c\n", + " fa: f8d9 c0a4 ldr.w ip, [r9, #164] ; 0xa4\n", " fe: f1bc 0f00 cmp.w ip, #0\n", " 102: d171 bne.n 1e8 <VixlJniHelpers+0x1e8>\n", " 104: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", @@ -153,7 +153,7 @@ const char* const VixlJniHelpersResults[] = { " 21c: f8d9 8034 ldr.w r8, [r9, #52] ; 0x34\n", " 220: 4770 bx lr\n", " 222: 4660 mov r0, ip\n", - " 224: f8d9 c2e4 ldr.w ip, [r9, #740] ; 0x2e4\n", + " 224: f8d9 c2ec ldr.w ip, [r9, #748] ; 0x2ec\n", " 228: 47e0 blx ip\n", nullptr }; diff --git a/libartbase/base/iteration_range.h b/libartbase/base/iteration_range.h index cd87d85f68..eaed8b06a8 100644 --- a/libartbase/base/iteration_range.h +++ b/libartbase/base/iteration_range.h @@ -49,6 +49,11 @@ inline IterationRange<Iter> MakeIterationRange(const Iter& begin_it, const Iter& return IterationRange<Iter>(begin_it, end_it); } +template<typename List> +inline IterationRange<typename List::iterator> MakeIterationRange(List& list) { + return IterationRange<typename List::iterator>(list.begin(), list.end()); +} + template <typename Iter> inline IterationRange<Iter> MakeEmptyIterationRange(const Iter& it) { return IterationRange<Iter>(it, it); diff --git a/openjdkjvmti/ti_extension.cc b/openjdkjvmti/ti_extension.cc index 058a188630..130169711f 100644 --- a/openjdkjvmti/ti_extension.cc +++ b/openjdkjvmti/ti_extension.cc @@ -423,29 +423,31 @@ jvmtiError ExtensionUtil::GetExtensionFunctions(jvmtiEnv* env, reinterpret_cast<jvmtiExtensionFunction>(Redefiner::StructurallyRedefineClasses), "com.android.art.class.structurally_redefine_classes", "Entrypoint for structural class redefinition. Has the same signature as RedefineClasses." - " Currently this only supports adding new static fields to a class without any instance" - " fields or methods. After calling this com.android.art.structural_dex_file_load_hook" - " events will be triggered, followed by re-transformable ClassFileLoadHook events. After" - " this method completes subsequent RetransformClasses calls will use the input to this" - " function as the initial class definition.", + " Currently does not support redefining a class and any of its supertypes at the same time." + " Only supports additive changes, methods and fields may not be removed. Supertypes and" + " implemented interfaces may not be changed. After calling this" + " com.android.art.structural_dex_file_load_hook events will be triggered, followed by" + " re-transformable ClassFileLoadHook events. After this method completes subsequent" + " RetransformClasses calls will use the input to this function as the initial class" + " definition.", { - { "num_classes", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false }, - { "class_definitions", JVMTI_KIND_IN_BUF, JVMTI_TYPE_CVOID, false }, + { "num_classes", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false }, + { "class_definitions", JVMTI_KIND_IN_BUF, JVMTI_TYPE_CVOID, false }, }, { - ERR(CLASS_LOADER_UNSUPPORTED), - ERR(FAILS_VERIFICATION), - ERR(ILLEGAL_ARGUMENT), - ERR(INVALID_CLASS), - ERR(MUST_POSSESS_CAPABILITY), - ERR(MUST_POSSESS_CAPABILITY), - ERR(NULL_POINTER), - ERR(OUT_OF_MEMORY), - ERR(UNMODIFIABLE_CLASS), - ERR(UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED), - ERR(UNSUPPORTED_REDEFINITION_METHOD_ADDED), - ERR(UNSUPPORTED_REDEFINITION_METHOD_DELETED), - ERR(UNSUPPORTED_REDEFINITION_SCHEMA_CHANGED), + ERR(CLASS_LOADER_UNSUPPORTED), + ERR(FAILS_VERIFICATION), + ERR(ILLEGAL_ARGUMENT), + ERR(INVALID_CLASS), + ERR(MUST_POSSESS_CAPABILITY), + ERR(MUST_POSSESS_CAPABILITY), + ERR(NULL_POINTER), + ERR(OUT_OF_MEMORY), + ERR(UNMODIFIABLE_CLASS), + ERR(UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED), + ERR(UNSUPPORTED_REDEFINITION_METHOD_ADDED), + ERR(UNSUPPORTED_REDEFINITION_METHOD_DELETED), + ERR(UNSUPPORTED_REDEFINITION_SCHEMA_CHANGED), }); if (error != ERR(NONE)) { return error; diff --git a/openjdkjvmti/ti_heap.cc b/openjdkjvmti/ti_heap.cc index b25b4d11e2..e1f349a5ac 100644 --- a/openjdkjvmti/ti_heap.cc +++ b/openjdkjvmti/ti_heap.cc @@ -1653,6 +1653,10 @@ static void ReplaceObjectReferences(const ObjectMap& map) // We don't want to update the declaring class of any objects. They will be replaced // in the heap and we need the declaring class to know its size. return; + } else if (UNLIKELY(!is_static && off == art::mirror::Class::SuperClassOffset() && + obj->IsClass())) { + // We don't want to be messing with the class hierarcy either. + return; } VLOG(plugin) << "Updating field at offset " << off.Uint32Value() << " of type " << obj->GetClass()->PrettyClass(); diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc index ebbe6acdbd..d794fd2287 100644 --- a/openjdkjvmti/ti_redefine.cc +++ b/openjdkjvmti/ti_redefine.cc @@ -54,7 +54,10 @@ #include "base/casts.h" #include "base/enums.h" #include "base/globals.h" +#include "base/iteration_range.h" #include "base/length_prefixed_array.h" +#include "base/locks.h" +#include "base/stl_util.h" #include "base/utils.h" #include "class_linker-inl.h" #include "class_linker.h" @@ -461,36 +464,6 @@ jvmtiError Redefiner::GetClassRedefinitionError(art::Handle<art::mirror::Class> "safe to structurally redefine it."; return ERR(UNMODIFIABLE_CLASS); } - // Check for already existing non-static fields/methods. - // TODO Remove this once we support generic method/field addition. - if (!klass->IsFinal()) { - bool non_static_method = false; - klass->VisitMethods([&](art::ArtMethod* m) REQUIRES_SHARED(art::Locks::mutator_lock_) { - // Since direct-methods (ie privates + <init> are not in any vtable/iftable we can update - // them). - if (!m->IsDirect()) { - non_static_method = true; - *error_msg = StringPrintf("%s has a non-direct function %s", - klass->PrettyClass().c_str(), - m->PrettyMethod().c_str()); - } - }, art::kRuntimePointerSize); - if (non_static_method) { - return ERR(UNMODIFIABLE_CLASS); - } - bool non_static_field = false; - klass->VisitFields([&](art::ArtField* f) REQUIRES_SHARED(art::Locks::mutator_lock_) { - if (!f->IsStatic()) { - non_static_field = true; - *error_msg = StringPrintf("%s has a non-static field %s", - klass->PrettyClass().c_str(), - f->PrettyField().c_str()); - } - }); - if (non_static_field) { - return ERR(UNMODIFIABLE_CLASS); - } - } // Check for fields/methods which were returned before moving to index jni id type. // TODO We might want to rework how this is done. Once full redefinition is implemented we will // need to check any subtypes too. @@ -995,8 +968,7 @@ bool Redefiner::ClassRedefinition::CheckMethods() { RecordHasVirtualMembers(); } if (old_iter == old_methods.cend()) { - // TODO Support adding non-static methods. - if (is_structural && (new_method.IsStaticOrDirect() || h_klass->IsFinal())) { + if (is_structural) { RecordNewMethodAdded(); } else { RecordFailure( @@ -1059,8 +1031,7 @@ bool Redefiner::ClassRedefinition::CheckFields() { RecordHasVirtualMembers(); } if (old_iter == old_fields.cend()) { - // TODO Support adding non-static fields. - if (driver_->IsStructuralRedefinition() && (new_field.IsStatic() || h_klass->IsFinal())) { + if (driver_->IsStructuralRedefinition()) { RecordNewFieldAdded(); } else { RecordFailure(ERR(UNSUPPORTED_REDEFINITION_SCHEMA_CHANGED), @@ -1219,9 +1190,11 @@ class RedefinitionDataHolder { kSlotNewClassObject = 8, kSlotOldInstanceObjects = 9, kSlotNewInstanceObjects = 10, + kSlotOldClasses = 11, + kSlotNewClasses = 12, // Must be last one. - kNumSlots = 11, + kNumSlots = 13, }; // This needs to have a HandleScope passed in that is capable of creating a new Handle without @@ -1298,6 +1271,16 @@ class RedefinitionDataHolder { return art::ObjPtr<art::mirror::ObjectArray<art::mirror::Object>>::DownCast( GetSlot(klass_index, kSlotNewInstanceObjects)); } + art::ObjPtr<art::mirror::ObjectArray<art::mirror::Class>> GetOldClasses(jint klass_index) const + REQUIRES_SHARED(art::Locks::mutator_lock_) { + return art::ObjPtr<art::mirror::ObjectArray<art::mirror::Class>>::DownCast( + GetSlot(klass_index, kSlotOldClasses)); + } + art::ObjPtr<art::mirror::ObjectArray<art::mirror::Class>> GetNewClasses(jint klass_index) const + REQUIRES_SHARED(art::Locks::mutator_lock_) { + return art::ObjPtr<art::mirror::ObjectArray<art::mirror::Class>>::DownCast( + GetSlot(klass_index, kSlotNewClasses)); + } void SetSourceClassLoader(jint klass_index, art::ObjPtr<art::mirror::ClassLoader> loader) REQUIRES_SHARED(art::Locks::mutator_lock_) { @@ -1348,6 +1331,16 @@ class RedefinitionDataHolder { REQUIRES_SHARED(art::Locks::mutator_lock_) { SetSlot(klass_index, kSlotNewInstanceObjects, objs); } + void SetOldClasses(jint klass_index, + art::ObjPtr<art::mirror::ObjectArray<art::mirror::Class>> klasses) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + SetSlot(klass_index, kSlotOldClasses, klasses); + } + void SetNewClasses(jint klass_index, + art::ObjPtr<art::mirror::ObjectArray<art::mirror::Class>> klasses) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + SetSlot(klass_index, kSlotNewClasses, klasses); + } int32_t Length() const REQUIRES_SHARED(art::Locks::mutator_lock_) { return arr_->GetLength() / kNumSlots; } @@ -1491,6 +1484,14 @@ class RedefinitionDataIter { REQUIRES_SHARED(art::Locks::mutator_lock_) { return holder_.GetNewInstanceObjects(idx_); } + art::ObjPtr<art::mirror::ObjectArray<art::mirror::Class>> GetOldClasses() const + REQUIRES_SHARED(art::Locks::mutator_lock_) { + return holder_.GetOldClasses(idx_); + } + art::ObjPtr<art::mirror::ObjectArray<art::mirror::Class>> GetNewClasses() const + REQUIRES_SHARED(art::Locks::mutator_lock_) { + return holder_.GetNewClasses(idx_); + } int32_t GetIndex() const { return idx_; } @@ -1539,6 +1540,14 @@ class RedefinitionDataIter { REQUIRES_SHARED(art::Locks::mutator_lock_) { holder_.SetNewInstanceObjects(idx_, objs); } + void SetOldClasses(art::ObjPtr<art::mirror::ObjectArray<art::mirror::Class>> klasses) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + holder_.SetOldClasses(idx_, klasses); + } + void SetNewClasses(art::ObjPtr<art::mirror::ObjectArray<art::mirror::Class>> klasses) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + holder_.SetNewClasses(idx_, klasses); + } private: int32_t idx_; @@ -1649,23 +1658,104 @@ bool Redefiner::ClassRedefinition::CollectAndCreateNewInstances( art::VariableSizedHandleScope hs(driver_->self_); art::Handle<art::mirror::Class> old_klass(hs.NewHandle(cur_data->GetMirrorClass())); std::vector<art::Handle<art::mirror::Object>> old_instances; + std::vector<art::Handle<art::mirror::Class>> old_types; art::gc::Heap* heap = driver_->runtime_->GetHeap(); + auto is_subtype = [&](art::mirror::Object* obj) REQUIRES_SHARED(art::Locks::mutator_lock_) { + // We've already waited for class defines to be finished and paused them. All classes should be + // either resolved or error. We don't need to do anything with error classes, since they cannot + // be accessed in any observable way. + return obj->IsClass() && obj->AsClass()->IsResolved() && + old_klass->IsAssignableFrom(obj->AsClass()); + }; auto is_instance = [&](art::mirror::Object* obj) REQUIRES_SHARED(art::Locks::mutator_lock_) { - if (HasVirtualMembers()) { - return old_klass->IsAssignableFrom(obj->GetClass()); - } else { - // We don't need to deal with objects of subtypes when we don't modify virtuals since the - // vtable + field layout will remain the same. - return old_klass.Get() == obj->GetClass(); - } + return obj->InstanceOf(old_klass.Get()); }; heap->VisitObjects([&](art::mirror::Object* obj) REQUIRES_SHARED(art::Locks::mutator_lock_) { if (is_instance(obj)) { - CHECK(old_klass.Get() == obj->GetClass()) << "No support for subtypes yet!"; old_instances.push_back(hs.NewHandle(obj)); + } else if (is_subtype(obj)) { + old_types.push_back(hs.NewHandle(obj->AsClass())); } }); VLOG(plugin) << "Collected " << old_instances.size() << " instances to recreate!"; + VLOG(plugin) << "Found " << old_types.size() << " types that are/are subtypes of " + << old_klass->PrettyClass(); + + art::Handle<art::mirror::Class> cls_array_class( + hs.NewHandle(art::GetClassRoot<art::mirror::ObjectArray<art::mirror::Class>>( + driver_->runtime_->GetClassLinker()))); + art::Handle<art::mirror::ObjectArray<art::mirror::Class>> old_classes_arr( + hs.NewHandle(art::mirror::ObjectArray<art::mirror::Class>::Alloc( + driver_->self_, cls_array_class.Get(), old_types.size()))); + if (old_classes_arr.IsNull()) { + driver_->self_->AssertPendingOOMException(); + driver_->self_->ClearException(); + RecordFailure(ERR(OUT_OF_MEMORY), "Could not allocate old_classes arrays!"); + return false; + } + // Sort the old_types topologically. + { + art::ScopedAssertNoThreadSuspension sants("Sort classes"); + auto count_distance = + [&](art::ObjPtr<art::mirror::Class> c) REQUIRES_SHARED(art::Locks::mutator_lock_) { + uint32_t res = 0; + while (c != old_klass.Get()) { + DCHECK_NE(c, art::GetClassRoot<art::mirror::Object>()); + res++; + c = c->GetSuperClass(); + } + return res; + }; + auto compare_handles = [&](auto l, auto r) REQUIRES_SHARED(art::Locks::mutator_lock_) { + return count_distance(l.Get()) < count_distance(r.Get()); + }; + // Sort them by the distance to the base-class. This ensures that any class occurs before any of + // its subtypes. + std::sort(old_types.begin(), old_types.end(), compare_handles); + } + for (uint32_t i = 0; i < old_types.size(); ++i) { + old_classes_arr->Set(i, old_types[i].Get()); + } + cur_data->SetOldClasses(old_classes_arr.Get()); + art::Handle<art::mirror::ObjectArray<art::mirror::Class>> new_classes_arr( + hs.NewHandle(art::mirror::ObjectArray<art::mirror::Class>::Alloc( + driver_->self_, cls_array_class.Get(), old_types.size()))); + if (new_classes_arr.IsNull()) { + driver_->self_->AssertPendingOOMException(); + driver_->self_->ClearException(); + RecordFailure(ERR(OUT_OF_MEMORY), "Could not allocate new_classes arrays!"); + return false; + } + art::MutableHandle<art::mirror::DexCache> dch(hs.NewHandle<art::mirror::DexCache>(nullptr)); + art::MutableHandle<art::mirror::Class> superclass(hs.NewHandle<art::mirror::Class>(nullptr)); + for (size_t i = 0; i < old_types.size(); i++) { + art::Handle<art::mirror::Class>& old_class = old_types[i]; + if (old_class.Get() == cur_data->GetMirrorClass()) { + CHECK_EQ(i, 0u) << "original class not at index 0. Bad sort!"; + new_classes_arr->Set(i, cur_data->GetNewClassObject()); + continue; + } else { + auto old_super = std::find_if(old_types.begin(), + old_types.begin() + i, + [&](art::Handle<art::mirror::Class>& v) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + return v.Get() == old_class->GetSuperClass(); + }); + // Only the GetMirrorClass should not be in this list. + CHECK(old_super != old_types.begin() + i) + << "from first " << i << " could not find super of " << old_class->PrettyClass() + << " expected to find " << old_class->GetSuperClass()->PrettyClass(); + superclass.Assign(new_classes_arr->Get(std::distance(old_types.begin(), old_super))); + dch.Assign(old_class->GetDexCache()); + art::ObjPtr<art::mirror::Class> new_class( + AllocateNewClassObject(old_class, superclass, dch, old_class->GetDexClassDefIndex())); + if (new_class == nullptr) { + return false; + } + new_classes_arr->Set(i, new_class); + } + } + cur_data->SetNewClasses(new_classes_arr.Get()); art::Handle<art::mirror::Class> obj_array_class( hs.NewHandle(art::GetClassRoot<art::mirror::ObjectArray<art::mirror::Object>>( @@ -1693,9 +1783,18 @@ bool Redefiner::ClassRedefinition::CollectAndCreateNewInstances( RecordFailure(ERR(OUT_OF_MEMORY), "Could not allocate new_instance arrays!"); return false; } - art::Handle<art::mirror::Class> new_klass(hs.NewHandle(cur_data->GetNewClassObject())); - for (uint32_t i = 0; i < old_instances.size(); ++i) { - art::ObjPtr<art::mirror::Object> new_instance(new_klass->AllocObject(driver_->self_)); + for (auto pair : art::ZipCount(art::IterationRange(old_instances.begin(), old_instances.end()))) { + art::Handle<art::mirror::Object> hinstance(pair.first); + int32_t i = pair.second; + auto iterator = art::ZipLeft(old_classes_arr.Iterate<art::mirror::Class>(), + new_classes_arr.Iterate<art::mirror::Class>()); + auto [_, new_type] = + *(std::find_if(iterator.begin(), + iterator.end(), + [&](auto class_pair) REQUIRES_SHARED(art::Locks::mutator_lock_) { + return class_pair.first == hinstance->GetClass(); + })); + art::ObjPtr<art::mirror::Object> new_instance(new_type->AllocObject(driver_->self_)); if (new_instance.IsNull()) { driver_->self_->AssertPendingOOMException(); driver_->self_->ClearException(); @@ -1755,8 +1854,6 @@ bool Redefiner::ClassRedefinition::FinishRemainingAllocations( art::Handle<art::mirror::Class> nc(hs.NewHandle( AllocateNewClassObject(hs.NewHandle(cur_data->GetNewDexCache())))); if (nc.IsNull()) { - driver_->self_->ClearException(); - RecordFailure(ERR(OUT_OF_MEMORY), "Unable to allocate new class object"); return false; } @@ -1770,18 +1867,15 @@ bool Redefiner::ClassRedefinition::FinishRemainingAllocations( return true; } -uint32_t Redefiner::ClassRedefinition::GetNewClassSize(bool with_embedded_tables, - art::Handle<art::mirror::Class> old_klass) { - // TODO Once we can add methods this won't work any more. - uint32_t num_vtable_entries = old_klass->GetVTableLength(); +uint32_t Redefiner::ClassRedefinition::GetNewClassSize(art::ClassAccessor& accessor) { uint32_t num_8bit_static_fields = 0; uint32_t num_16bit_static_fields = 0; uint32_t num_32bit_static_fields = 0; uint32_t num_64bit_static_fields = 0; uint32_t num_ref_static_fields = 0; - art::ClassAccessor accessor(*dex_file_, dex_file_->GetClassDef(0)); for (const art::ClassAccessor::Field& f : accessor.GetStaticFields()) { - std::string_view desc(dex_file_->GetFieldTypeDescriptor(dex_file_->GetFieldId(f.GetIndex()))); + std::string_view desc(accessor.GetDexFile().GetFieldTypeDescriptor( + accessor.GetDexFile().GetFieldId(f.GetIndex()))); if (desc[0] == 'L' || desc[0] == '[') { num_ref_static_fields++; } else if (desc == "Z" || desc == "B") { @@ -1797,8 +1891,8 @@ uint32_t Redefiner::ClassRedefinition::GetNewClassSize(bool with_embedded_tables } } - return art::mirror::Class::ComputeClassSize(with_embedded_tables, - with_embedded_tables ? num_vtable_entries : 0, + return art::mirror::Class::ComputeClassSize(/*has_embedded_vtable=*/ false, + /*num_vtable_entries=*/ 0, num_8bit_static_fields, num_16bit_static_fields, num_32bit_static_fields, @@ -1809,23 +1903,41 @@ uint32_t Redefiner::ClassRedefinition::GetNewClassSize(bool with_embedded_tables art::ObjPtr<art::mirror::Class> Redefiner::ClassRedefinition::AllocateNewClassObject(art::Handle<art::mirror::DexCache> cache) { + art::StackHandleScope<2> hs(driver_->self_); + art::Handle<art::mirror::Class> old_class(hs.NewHandle(GetMirrorClass())); + art::Handle<art::mirror::Class> super_class(hs.NewHandle(old_class->GetSuperClass())); + return AllocateNewClassObject(old_class, super_class, cache, /*dex_class_def_index*/0); +} + +art::ObjPtr<art::mirror::Class> Redefiner::ClassRedefinition::AllocateNewClassObject( + art::Handle<art::mirror::Class> old_class, + art::Handle<art::mirror::Class> super_class, + art::Handle<art::mirror::DexCache> cache, + uint16_t dex_class_def_index) { // This is a stripped down DefineClass. We don't want to use DefineClass directly because it needs // to perform a lot of extra steps to tell the ClassTable and the jit and everything about a new // class. For now we will need to rely on our tests catching any issues caused by changes in how // class_linker sets up classes. // TODO Unify/move this into ClassLinker maybe. - art::StackHandleScope<5> hs(driver_->self_); + art::StackHandleScope<3> hs(driver_->self_); art::ClassLinker* linker = driver_->runtime_->GetClassLinker(); - art::Handle<art::mirror::Class> old_class(hs.NewHandle(GetMirrorClass())); + const art::DexFile* dex_file = cache->GetDexFile(); + art::ClassAccessor accessor(*dex_file, dex_class_def_index); art::Handle<art::mirror::Class> new_class(hs.NewHandle(linker->AllocClass( - driver_->self_, GetNewClassSize(/*with_embedded_tables=*/false, old_class)))); + driver_->self_, GetNewClassSize(accessor)))); if (new_class.IsNull()) { driver_->self_->AssertPendingOOMException(); - JVMTI_LOG(ERROR, driver_->env_) << "Unable to allocate new class object!"; + RecordFailure( + ERR(OUT_OF_MEMORY), + "Unable to allocate class object for redefinition of " + old_class->PrettyClass()); + driver_->self_->ClearException(); return nullptr; } new_class->SetDexCache(cache.Get()); - linker->SetupClass(*dex_file_, dex_file_->GetClassDef(0), new_class, old_class->GetClassLoader()); + linker->SetupClass(*dex_file, + dex_file->GetClassDef(dex_class_def_index), + new_class, + old_class->GetClassLoader()); // Make sure we are ready for linking. The lock isn't really needed since this isn't visible to // other threads but the linker expects it. @@ -1833,31 +1945,46 @@ Redefiner::ClassRedefinition::AllocateNewClassObject(art::Handle<art::mirror::De new_class->SetClinitThreadId(driver_->self_->GetTid()); // Make sure we have a valid empty iftable even if there are errors. new_class->SetIfTable(art::GetClassRoot<art::mirror::Object>(linker)->GetIfTable()); - linker->LoadClass(driver_->self_, *dex_file_, dex_file_->GetClassDef(0), new_class); + linker->LoadClass( + driver_->self_, *dex_file, dex_file->GetClassDef(dex_class_def_index), new_class); // NB. We know the interfaces and supers didn't change! :) art::MutableHandle<art::mirror::Class> linked_class(hs.NewHandle<art::mirror::Class>(nullptr)); art::Handle<art::mirror::ObjectArray<art::mirror::Class>> proxy_ifaces( hs.NewHandle<art::mirror::ObjectArray<art::mirror::Class>>(nullptr)); // No changing hierarchy so everything is loaded. - new_class->SetSuperClass(old_class->GetSuperClass()); + new_class->SetSuperClass(super_class.Get()); art::mirror::Class::SetStatus(new_class, art::ClassStatus::kLoaded, nullptr); if (!linker->LinkClass(driver_->self_, nullptr, new_class, proxy_ifaces, &linked_class)) { - JVMTI_LOG(ERROR, driver_->env_) - << "failed to link class due to " + std::ostringstream oss; + oss << "failed to link class due to " << (driver_->self_->IsExceptionPending() ? driver_->self_->GetException()->Dump() : " unknown"); + RecordFailure(ERR(INTERNAL), oss.str()); driver_->self_->ClearException(); return nullptr; } - // We will initialize it manually. + // Everything is already resolved. art::ObjectLock<art::mirror::Class> objlock(driver_->self_, linked_class); - // We already verified the class earlier. No need to do it again. - linked_class->SetVerificationAttempted(); // Mark the class as initialized. - CHECK(old_class->IsInitialized()) - << "Attempting to redefine an uninitalized class " << old_class->PrettyClass() + CHECK(old_class->IsResolved()) + << "Attempting to redefine an unresolved class " << old_class->PrettyClass() << " status=" << old_class->GetStatus(); - linker->ForceClassInitialized(driver_->self_, linked_class); + CHECK(linked_class->IsResolved()); + if (old_class->WasVerificationAttempted()) { + // Match verification-attempted flag + linked_class->SetVerificationAttempted(); + } + if (old_class->ShouldSkipHiddenApiChecks()) { + // Match skip hiddenapi flag + linked_class->SetSkipHiddenApiChecks(); + } + if (old_class->IsInitialized()) { + // We already verified the class earlier. No need to do it again. + linker->ForceClassInitialized(driver_->self_, linked_class); + } else if (old_class->GetStatus() > linked_class->GetStatus()) { + // We want to match the old status. + art::mirror::Class::SetStatus(linked_class, old_class->GetStatus(), driver_->self_); + } // Make sure we have ext-data space for method & field ids. We won't know if we need them until // it's too late to create them. // TODO We might want to remove these arrays if they're not needed. @@ -1866,7 +1993,9 @@ Redefiner::ClassRedefinition::AllocateNewClassObject(art::Handle<art::mirror::De art::mirror::Class::GetOrCreateMethodIds(linked_class).IsNull()) { driver_->self_->AssertPendingOOMException(); driver_->self_->ClearException(); - JVMTI_LOG(ERROR, driver_->env_) << "Unable to allocate jni-id arrays!"; + RecordFailure( + ERR(OUT_OF_MEMORY), + "Unable to allocate jni-id arrays for redefinition of " + old_class->PrettyClass()); return nullptr; } // Finish setting up methods. @@ -1883,6 +2012,9 @@ Redefiner::ClassRedefinition::AllocateNewClassObject(art::Handle<art::mirror::De DCHECK_EQ(f->GetDeclaringClass(), linked_class.Get()); }); } + // Reset ClinitThreadId back to the thread that loaded the old class. This is needed if we are in + // the middle of initializing a class. + linked_class->SetClinitThreadId(old_class->GetClinitThreadId()); return linked_class.Get(); } @@ -1913,9 +2045,39 @@ bool Redefiner::CheckAllRedefinitionAreValid() { return false; } } + if (std::any_of( + redefinitions_.begin(), + redefinitions_.end(), + std::function<bool(ClassRedefinition&)>(&ClassRedefinition::IsStructuralRedefinition))) { + return CheckClassHierarchy(); + } return true; } +bool Redefiner::CheckClassHierarchy() { + return std::all_of( + redefinitions_.begin(), + redefinitions_.end(), + [&](ClassRedefinition& r) REQUIRES_SHARED(art::Locks::mutator_lock_) { + return std::none_of( + redefinitions_.begin(), + redefinitions_.end(), + [&](ClassRedefinition& r2) REQUIRES_SHARED(art::Locks::mutator_lock_) { + bool related = &r2 != &r && + r2.GetMirrorClass()->IsAssignableFrom(r.GetMirrorClass()) && + r2.IsStructuralRedefinition(); + if (related) { + std::ostringstream oss; + oss << "Structurally redefining " << r2.GetMirrorClass()->PrettyClass() + << " which is a superclass of " << r.GetMirrorClass()->PrettyClass() + << ". This is not currently supported"; + r2.RecordFailure(ERR(INTERNAL), oss.str()); + } + return related; + }); + }); +} + void Redefiner::RestoreObsoleteMethodMapsIfUnneeded(RedefinitionDataHolder& holder) { for (RedefinitionDataIter data = holder.begin(); data != holder.end(); ++data) { data.GetRedefinition().RestoreObsoleteMethodMapsIfUnneeded(&data); @@ -1989,6 +2151,142 @@ class ScopedDisableConcurrentAndMovingGc { art::Thread* self_; }; +class ClassDefinitionPauser : public art::ClassLoadCallback { + public: + explicit ClassDefinitionPauser(art::Thread* self) REQUIRES_SHARED(art::Locks::mutator_lock_) + : self_(self), + is_running_(false), + barrier_(0), + release_mu_("SuspendClassDefinition lock", art::kGenericBottomLock), + release_barrier_(0), + release_cond_("SuspendClassDefinition condvar", release_mu_), + count_(0), + release_(false) { + art::Locks::mutator_lock_->AssertSharedHeld(self_); + } + ~ClassDefinitionPauser() REQUIRES_SHARED(art::Locks::mutator_lock_) { + art::Locks::mutator_lock_->AssertSharedHeld(self_); + if (is_running_) { + uint32_t count; + // Wake up everything. + { + art::MutexLock mu(self_, release_mu_); + release_ = true; + count = count_; + release_cond_.Broadcast(self_); + } + // Wait for all threads to leave this structs code. + art::ScopedThreadSuspension sts(self_, art::ThreadState::kWaiting); + VLOG(plugin) << "Resuming " << count << " threads paused before class-allocation!"; + release_barrier_.Increment(self_, count); + } + } + void BeginDefineClass() override REQUIRES_SHARED(art::Locks::mutator_lock_) { + art::Thread* this_thread = art::Thread::Current(); + if (this_thread == self_) { + // Allow the redefining thread to do whatever. + return; + } + if (this_thread->GetDefineClassCount() != 0) { + // We are in the middle of a recursive define-class. Don't suspend now allow it to finish. + VLOG(plugin) << "Recursive DefineClass in " << *this_thread + << " allowed to proceed despite class-def pause initiated by " << *self_; + return; + } + art::ScopedThreadSuspension sts(this_thread, art::ThreadState::kSuspended); + { + art::MutexLock mu(this_thread, release_mu_); + if (release_) { + // Count already retrieved, no need to pass. + return; + } + VLOG(plugin) << "Suspending " << *this_thread << " due to class definition. class-def pause " + << "initiated by " << *self_; + count_++; + while (!release_) { + release_cond_.Wait(this_thread); + } + } + release_barrier_.Pass(this_thread); + } + void EndDefineClass() override REQUIRES_SHARED(art::Locks::mutator_lock_) { + art::Thread* this_thread = art::Thread::Current(); + if (this_thread == self_) { + // Allow the redefining thread to do whatever. + return; + } + if (this_thread->GetDefineClassCount() == 0) { + // We are done with defining classes. + barrier_.Pass(this_thread); + } + } + + void ClassLoad(art::Handle<art::mirror::Class> klass ATTRIBUTE_UNUSED) override {} + void ClassPrepare(art::Handle<art::mirror::Class> klass1 ATTRIBUTE_UNUSED, + art::Handle<art::mirror::Class> klass2 ATTRIBUTE_UNUSED) override {} + + void SetRunning() { + is_running_ = true; + } + void WaitFor(uint32_t t) REQUIRES(!art::Locks::mutator_lock_) { + barrier_.Increment(self_, t); + } + + private: + art::Thread* self_; + bool is_running_; + art::Barrier barrier_; + art::Mutex release_mu_; + art::Barrier release_barrier_; + art::ConditionVariable release_cond_; + uint32_t count_ GUARDED_BY(release_mu_); + bool release_; +}; + +class ScopedSuspendClassLoading { + public: + ScopedSuspendClassLoading(art::Thread* self, art::Runtime* runtime, RedefinitionDataHolder& h) + REQUIRES_SHARED(art::Locks::mutator_lock_) + : self_(self), runtime_(runtime), paused_(false), pauser_(self_) { + if (std::any_of(h.begin(), h.end(), [](auto r) REQUIRES_SHARED(art::Locks::mutator_lock_) { + return r.GetRedefinition().IsStructuralRedefinition(); + })) { + VLOG(plugin) << "Pausing Class loading for structural redefinition."; + paused_ = true; + { + art::ScopedThreadSuspension sts(self_, art::ThreadState::kNative); + uint32_t in_progress_defines = 0; + { + art::ScopedSuspendAll ssa(__FUNCTION__); + pauser_.SetRunning(); + runtime_->GetRuntimeCallbacks()->AddClassLoadCallback(&pauser_); + art::MutexLock mu(self_, *art::Locks::thread_list_lock_); + runtime_->GetThreadList()->ForEach([&](art::Thread* t) { + if (t != self_ && t->GetDefineClassCount() != 0) { + in_progress_defines++; + } + }); + VLOG(plugin) << "Waiting for " << in_progress_defines << " in progress class-loads to finish"; + } + pauser_.WaitFor(in_progress_defines); + } + } + } + ~ScopedSuspendClassLoading() { + if (paused_) { + art::ScopedThreadSuspension sts(self_, art::ThreadState::kNative); + art::ScopedSuspendAll ssa(__FUNCTION__); + runtime_->GetRuntimeCallbacks()->RemoveClassLoadCallback(&pauser_); + } + } + + private: + art::Thread* self_; + art::Runtime* runtime_; + bool paused_; + ClassDefinitionPauser pauser_; +}; + class ScopedSuspendAllocations { public: ScopedSuspendAllocations(art::Runtime* runtime, RedefinitionDataHolder& h) @@ -2043,6 +2341,7 @@ jvmtiError Redefiner::Run() { return result_; } + ScopedSuspendClassLoading suspend_class_load(self_, runtime_, holder); ScopedSuspendAllocations suspend_alloc(runtime_, holder); if (!CollectAndCreateNewInstances(holder)) { return result_; @@ -2174,27 +2473,28 @@ void Redefiner::ClassRedefinition::CollectNewFieldAndMethodMappings( const RedefinitionDataIter& data, std::map<art::ArtMethod*, art::ArtMethod*>* method_map, std::map<art::ArtField*, art::ArtField*>* field_map) { - art::ObjPtr<art::mirror::Class> old_cls(data.GetMirrorClass()); - art::ObjPtr<art::mirror::Class> new_cls(data.GetNewClassObject()); - for (art::ArtField& f : old_cls->GetSFields()) { - (*field_map)[&f] = new_cls->FindDeclaredStaticField(f.GetName(), f.GetTypeDescriptor()); - } - for (art::ArtField& f : old_cls->GetIFields()) { - (*field_map)[&f] = new_cls->FindDeclaredInstanceField(f.GetName(), f.GetTypeDescriptor()); - } - auto new_methods = new_cls->GetMethods(art::kRuntimePointerSize); - for (art::ArtMethod& m : old_cls->GetMethods(art::kRuntimePointerSize)) { - // No support for finding methods in this way since it's generally not needed. Just do it the - // easy way. - auto nm_iter = std::find_if( - new_methods.begin(), - new_methods.end(), - [&](art::ArtMethod& cand) REQUIRES_SHARED(art::Locks::mutator_lock_) { - return cand.GetNameView() == m.GetNameView() && cand.GetSignature() == m.GetSignature(); - }); - CHECK(nm_iter != new_methods.end()) - << "Could not find redefined version of " << m.PrettyMethod(); - (*method_map)[&m] = &(*nm_iter); + for (auto [new_cls, old_cls] : + art::ZipLeft(data.GetNewClasses()->Iterate(), data.GetOldClasses()->Iterate())) { + for (art::ArtField& f : old_cls->GetSFields()) { + (*field_map)[&f] = new_cls->FindDeclaredStaticField(f.GetName(), f.GetTypeDescriptor()); + } + for (art::ArtField& f : old_cls->GetIFields()) { + (*field_map)[&f] = new_cls->FindDeclaredInstanceField(f.GetName(), f.GetTypeDescriptor()); + } + auto new_methods = new_cls->GetMethods(art::kRuntimePointerSize); + for (art::ArtMethod& m : old_cls->GetMethods(art::kRuntimePointerSize)) { + // No support for finding methods in this way since it's generally not needed. Just do it the + // easy way. + auto nm_iter = std::find_if( + new_methods.begin(), + new_methods.end(), + [&](art::ArtMethod& cand) REQUIRES_SHARED(art::Locks::mutator_lock_) { + return cand.GetNameView() == m.GetNameView() && cand.GetSignature() == m.GetSignature(); + }); + CHECK(nm_iter != new_methods.end()) + << "Could not find redefined version of " << m.PrettyMethod(); + (*method_map)[&m] = &(*nm_iter); + } } } @@ -2321,6 +2621,8 @@ void Redefiner::ClassRedefinition::UpdateClassStructurally(const RedefinitionDat art::ScopedAssertNoThreadSuspension sants(__FUNCTION__); art::ObjPtr<art::mirror::Class> orig(holder.GetMirrorClass()); art::ObjPtr<art::mirror::Class> replacement(holder.GetNewClassObject()); + art::ObjPtr<art::mirror::ObjectArray<art::mirror::Class>> new_classes(holder.GetNewClasses()); + art::ObjPtr<art::mirror::ObjectArray<art::mirror::Class>> old_classes(holder.GetOldClasses()); // Collect mappings from old to new fields/methods std::map<art::ArtMethod*, art::ArtMethod*> method_map; std::map<art::ArtField*, art::ArtField*> field_map; @@ -2331,13 +2633,20 @@ void Redefiner::ClassRedefinition::UpdateClassStructurally(const RedefinitionDat holder.GetOldInstanceObjects()); CHECK(!orig.IsNull()); CHECK(!replacement.IsNull()); + // Once we do the ReplaceReferences old_classes will have the new_classes in it. We want to keep + // ahold of the old classes so copy them now. + std::vector<art::ObjPtr<art::mirror::Class>> old_classes_vec(old_classes->Iterate().begin(), + old_classes->Iterate().end()); // Copy over the static fields of the class and all the instance fields. - CopyAndClearFields(/*is_static=*/true, replacement, replacement, orig, orig); + for (auto [new_class, old_class] : art::ZipLeft(new_classes->Iterate(), old_classes->Iterate())) { + CHECK(!new_class.IsNull()); + CHECK(!old_class.IsNull()); + CopyAndClearFields(true, new_class, new_class, old_class, old_class); + } // Copy and clear the fields of the old-instances. - for (int32_t i = 0; i < old_instances->GetLength(); i++) { - art::ObjPtr<art::mirror::Object> old_instance(old_instances->Get(i)); - art::ObjPtr<art::mirror::Object> new_instance(new_instances->Get(i)); + for (auto [new_instance, old_instance] : + art::ZipLeft(new_instances->Iterate(), old_instances->Iterate())) { CopyAndClearFields(/*is_static=*/false, new_instance, new_instance->GetClass(), @@ -2345,12 +2654,15 @@ void Redefiner::ClassRedefinition::UpdateClassStructurally(const RedefinitionDat old_instance->GetClass()); } // Mark old class obsolete. - orig->SetObsoleteObject(); - // Mark methods obsolete. We need to wait until later to actually clear the jit data. - for (art::ArtMethod& m : orig->GetMethods(art::kRuntimePointerSize)) { - m.SetIsObsolete(); - m.SetDontCompile(); - DCHECK_EQ(orig, m.GetDeclaringClass()); + for (auto old_class : old_classes->Iterate()) { + old_class->SetObsoleteObject(); + // Mark methods obsolete. We need to wait until later to actually clear the jit data. + for (art::ArtMethod& m : old_class->GetMethods(art::kRuntimePointerSize)) { + m.SetIsObsolete(); + if (m.IsInvokable()) { + m.SetDontCompile(); + } + } } // Update live pointers in ART code. auto could_change_resolution_of = [&](auto* field_or_method, @@ -2439,9 +2751,16 @@ void Redefiner::ClassRedefinition::UpdateClassStructurally(const RedefinitionDat std::unordered_map<art::ObjPtr<art::mirror::Object>, art::ObjPtr<art::mirror::Object>, art::HashObjPtr> map; - map.emplace(orig, replacement); - for (int32_t i = 0; i < old_instances->GetLength(); i++) { - map.emplace(old_instances->Get(i), new_instances->Get(i)); + for (auto [new_class, old_class] : art::ZipLeft(new_classes->Iterate(), old_classes->Iterate())) { + map.emplace(old_class, new_class); + } + for (auto [new_instance, old_instance] : + art::ZipLeft(new_instances->Iterate(), old_instances->Iterate())) { + map.emplace(old_instance, new_instance); + // Bare-bones check that the mapping is correct. + CHECK(new_instance->GetClass() == map[old_instance->GetClass()]->AsClass()) + << new_instance->GetClass()->PrettyClass() << " vs " + << map[old_instance->GetClass()]->AsClass()->PrettyClass(); } // Actually perform the general replacement. This doesn't affect ArtMethod/ArtFields. It does @@ -2451,8 +2770,10 @@ void Redefiner::ClassRedefinition::UpdateClassStructurally(const RedefinitionDat // Save the old class so that the JIT gc doesn't get confused by it being collected before the // jit code. This is also needed to keep the dex-caches of any obsolete methods live. - replacement->GetExtData()->SetObsoleteClass(orig); - + for (auto [new_class, old_class] : + art::ZipLeft(new_classes->Iterate(), art::MakeIterationRange(old_classes_vec))) { + new_class->GetExtData()->SetObsoleteClass(old_class); + } art::jit::Jit* jit = driver_->runtime_->GetJit(); if (jit != nullptr) { @@ -2475,6 +2796,10 @@ void Redefiner::ClassRedefinition::UpdateClassStructurally(const RedefinitionDat // Just make sure we didn't screw up any of the now obsolete methods or fields. We need their // declaring-class to still be the obolete class orig->VisitMethods([&](art::ArtMethod* method) REQUIRES_SHARED(art::Locks::mutator_lock_) { + if (method->IsCopied()) { + // Copied methods have interfaces as their declaring class. + return; + } DCHECK_EQ(method->GetDeclaringClass(), orig) << method->GetDeclaringClass()->PrettyClass() << " vs " << orig->PrettyClass(); }, art::kRuntimePointerSize); diff --git a/openjdkjvmti/ti_redefine.h b/openjdkjvmti/ti_redefine.h index cedce92806..36bc9d3dd4 100644 --- a/openjdkjvmti/ti_redefine.h +++ b/openjdkjvmti/ti_redefine.h @@ -52,6 +52,7 @@ #include "obj_ptr.h" namespace art { +class ClassAccessor; namespace dex { struct ClassDef; } // namespace dex @@ -168,10 +169,15 @@ class Redefiner { void FindAndAllocateObsoleteMethods(art::ObjPtr<art::mirror::Class> art_klass) REQUIRES(art::Locks::mutator_lock_); + art::ObjPtr<art::mirror::Class> AllocateNewClassObject( + art::Handle<art::mirror::Class> old_class, + art::Handle<art::mirror::Class> super_class, + art::Handle<art::mirror::DexCache> cache, + uint16_t dex_class_def_index) REQUIRES_SHARED(art::Locks::mutator_lock_); art::ObjPtr<art::mirror::Class> AllocateNewClassObject(art::Handle<art::mirror::DexCache> cache) REQUIRES_SHARED(art::Locks::mutator_lock_); - uint32_t GetNewClassSize(bool with_embedded_tables, art::Handle<art::mirror::Class> old_class) + uint32_t GetNewClassSize(art::ClassAccessor& accessor) REQUIRES_SHARED(art::Locks::mutator_lock_); // Checks that the dex file contains only the single expected class and that the top-level class @@ -317,6 +323,7 @@ class Redefiner { jvmtiError Run() REQUIRES_SHARED(art::Locks::mutator_lock_); bool CheckAllRedefinitionAreValid() REQUIRES_SHARED(art::Locks::mutator_lock_); + bool CheckClassHierarchy() REQUIRES_SHARED(art::Locks::mutator_lock_); bool CheckAllClassesAreVerified(RedefinitionDataHolder& holder) REQUIRES_SHARED(art::Locks::mutator_lock_); bool EnsureAllClassAllocationsFinished(RedefinitionDataHolder& holder) diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 438d5cb709..d4f98afe84 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -3359,12 +3359,31 @@ static bool IsReservedBootClassPathDescriptor(const char* descriptor) { StartsWith(descriptor_sv, "Landroid/media/"); } +// Helper for maintaining DefineClass counting. We need to notify callbacks when we start/end a +// define-class and how many recursive DefineClasses we are at in order to allow for doing things +// like pausing class definition. +struct ScopedDefiningClass { + public: + explicit ScopedDefiningClass(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) : self_(self) { + Runtime::Current()->GetRuntimeCallbacks()->BeginDefineClass(); + self_->IncrDefineClassCount(); + } + ~ScopedDefiningClass() REQUIRES_SHARED(Locks::mutator_lock_) { + self_->DecrDefineClassCount(); + Runtime::Current()->GetRuntimeCallbacks()->EndDefineClass(); + } + + private: + Thread* self_; +}; + ObjPtr<mirror::Class> ClassLinker::DefineClass(Thread* self, const char* descriptor, size_t hash, Handle<mirror::ClassLoader> class_loader, const DexFile& dex_file, const dex::ClassDef& dex_class_def) { + ScopedDefiningClass sdc(self); StackHandleScope<3> hs(self); auto klass = hs.NewHandle<mirror::Class>(nullptr); diff --git a/runtime/class_linker.h b/runtime/class_linker.h index a1ba461270..dd3ef7a164 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -1440,6 +1440,10 @@ class ClassLoadCallback { public: virtual ~ClassLoadCallback() {} + // Called immediately before beginning class-definition and immediately before returning from it. + virtual void BeginDefineClass() REQUIRES_SHARED(Locks::mutator_lock_) {} + virtual void EndDefineClass() REQUIRES_SHARED(Locks::mutator_lock_) {} + // If set we will replace initial_class_def & initial_dex_file with the final versions. The // callback author is responsible for ensuring these are allocated in such a way they can be // cleaned up if another transformation occurs. Note that both must be set or null/unchanged on diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h index cc1a7f88e5..c67b1b0791 100644 --- a/runtime/entrypoints/entrypoint_utils-inl.h +++ b/runtime/entrypoints/entrypoint_utils-inl.h @@ -358,7 +358,8 @@ inline ArtField* FindFieldFromCode(uint32_t field_idx, DCHECK(self->IsExceptionPending()); // Throw exception and unwind. return nullptr; // Failure. } - if (UNLIKELY(is_set && resolved_field->IsFinal() && (fields_class != referring_class))) { + if (UNLIKELY(is_set && resolved_field->IsFinal() && (fields_class != referring_class) && + !referring_class->IsObsoleteVersionOf(fields_class))) { ThrowIllegalAccessErrorFinalField(referrer, resolved_field); return nullptr; // Failure. } else { diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index db5cbce647..79d767d9f1 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -550,6 +550,22 @@ inline bool Class::CheckResolvedMethodAccess(ObjPtr<Class> access_to, access_to, method, dex_cache, method_idx, throw_invoke_type); } +inline bool Class::IsObsoleteVersionOf(ObjPtr<Class> klass) { + DCHECK(!klass->IsObsoleteObject()) << klass->PrettyClass() << " is obsolete!"; + if (LIKELY(!IsObsoleteObject())) { + return false; + } + ObjPtr<Class> current(klass); + do { + if (UNLIKELY(current == this)) { + return true; + } else { + current = current->GetObsoleteClass(); + } + } while (!current.IsNull()); + return false; +} + inline bool Class::IsSubClass(ObjPtr<Class> klass) { // Since the SubtypeCheck::IsSubtypeOf needs to lookup the Depth, // it is always O(Depth) in terms of speed to do the check. diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index 834b8574fc..caa646bbff 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -336,6 +336,15 @@ void Class::SetClassSize(uint32_t new_class_size) { SetField32Transaction(OFFSET_OF_OBJECT_MEMBER(Class, class_size_), new_class_size); } +ObjPtr<Class> Class::GetObsoleteClass() { + ObjPtr<ClassExt> ext(GetExtData()); + if (ext.IsNull()) { + return nullptr; + } else { + return ext->GetObsoleteClass(); + } +} + // Return the class' name. The exact format is bizarre, but it's the specified behavior for // Class.getName: keywords for primitive types, regular "[I" form for primitive arrays (so "int" // but "[I"), and arrays of reference types written between "L" and ";" but with dots rather than diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index b1186d9b12..f88438bd6d 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -617,6 +617,11 @@ class MANAGED Class final : public Object { // to themselves. Classes for primitive types may not assign to each other. ALWAYS_INLINE bool IsAssignableFrom(ObjPtr<Class> src) REQUIRES_SHARED(Locks::mutator_lock_); + // Checks if 'klass' is a redefined version of this. + bool IsObsoleteVersionOf(ObjPtr<Class> klass) REQUIRES_SHARED(Locks::mutator_lock_); + + ObjPtr<Class> GetObsoleteClass() REQUIRES_SHARED(Locks::mutator_lock_); + template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, ReadBarrierOption kReadBarrierOption = kWithReadBarrier> ALWAYS_INLINE ObjPtr<Class> GetSuperClass() REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/runtime_callbacks.cc b/runtime/runtime_callbacks.cc index ac73364ff0..e0f57c013d 100644 --- a/runtime/runtime_callbacks.cc +++ b/runtime/runtime_callbacks.cc @@ -228,6 +228,19 @@ void RuntimeCallbacks::ClassLoad(Handle<mirror::Class> klass) { } } +void RuntimeCallbacks::EndDefineClass() { + for (ClassLoadCallback* cb : COPY(class_callbacks_)) { + cb->EndDefineClass(); + } +} + +void RuntimeCallbacks::BeginDefineClass() { + for (ClassLoadCallback* cb : COPY(class_callbacks_)) { + cb->BeginDefineClass(); + } +} + + void RuntimeCallbacks::ClassPreDefine(const char* descriptor, Handle<mirror::Class> temp_class, Handle<mirror::ClassLoader> loader, diff --git a/runtime/runtime_callbacks.h b/runtime/runtime_callbacks.h index 7111ba0085..3cadd974e1 100644 --- a/runtime/runtime_callbacks.h +++ b/runtime/runtime_callbacks.h @@ -181,6 +181,8 @@ class RuntimeCallbacks { void AddClassLoadCallback(ClassLoadCallback* cb) REQUIRES(Locks::mutator_lock_); void RemoveClassLoadCallback(ClassLoadCallback* cb) REQUIRES(Locks::mutator_lock_); + void BeginDefineClass() REQUIRES_SHARED(Locks::mutator_lock_); + void EndDefineClass() REQUIRES_SHARED(Locks::mutator_lock_); void ClassLoad(Handle<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_); void ClassPrepare(Handle<mirror::Class> temp_klass, Handle<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/thread.h b/runtime/thread.h index 29375e51f4..09e88107bd 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -263,6 +263,17 @@ class Thread { (state_and_flags.as_struct.flags & kSuspendRequest) != 0; } + void DecrDefineClassCount() { + tls32_.define_class_counter--; + } + + void IncrDefineClassCount() { + tls32_.define_class_counter++; + } + uint32_t GetDefineClassCount() const { + return tls32_.define_class_counter; + } + // If delta > 0 and (this != self or suspend_barrier is not null), this function may temporarily // release thread_suspend_count_lock_ internally. ALWAYS_INLINE @@ -1550,7 +1561,8 @@ class Thread { user_code_suspend_count(0), force_interpreter_count(0), use_mterp(0), - make_visibly_initialized_counter(0) {} + make_visibly_initialized_counter(0), + define_class_counter(0) {} union StateAndFlags state_and_flags; static_assert(sizeof(union StateAndFlags) == sizeof(int32_t), @@ -1648,6 +1660,10 @@ class Thread { // initialized but not visibly initialized for a long time even if no more classes are // being initialized anymore. uint32_t make_visibly_initialized_counter; + + // Counter for how many nested define-classes are ongoing in this thread. Used to allow waiting + // for threads to be done with class-definition work. + uint32_t define_class_counter; } tls32_; struct PACKED(8) tls_64bit_sized_values { diff --git a/test/1983-structural-redefinition-failures/expected.txt b/test/1983-structural-redefinition-failures/expected.txt index 40a0914278..222aac45d5 100644 --- a/test/1983-structural-redefinition-failures/expected.txt +++ b/test/1983-structural-redefinition-failures/expected.txt @@ -24,13 +24,13 @@ Is Structurally modifiable class java.lang.Throwable false Is Structurally modifiable class java.lang.invoke.VarHandle false Is Structurally modifiable class java.lang.invoke.FieldVarHandle false Checking non-mirror'd classes -Is Structurally modifiable class java.util.ArrayList false +Is Structurally modifiable class java.util.ArrayList true Is Structurally modifiable class java.util.Objects true Is Structurally modifiable class java.util.Arrays true Is Structurally modifiable class [Ljava.lang.Object; false Is Structurally modifiable class java.lang.Integer true -Is Structurally modifiable class java.lang.Number false +Is Structurally modifiable class java.lang.Number true Is Structurally modifiable class art.Test1983$NoVirtuals true -Is Structurally modifiable class art.Test1983$WithVirtuals false -Is Structurally modifiable class art.Test1983$SubWithVirtuals false +Is Structurally modifiable class art.Test1983$WithVirtuals true +Is Structurally modifiable class art.Test1983$SubWithVirtuals true Is Structurally modifiable class java.lang.invoke.MethodHandles true diff --git a/test/1993-fallback-non-structural/expected.txt b/test/1993-fallback-non-structural/expected.txt index 7e2cdf4ee5..f523e706b2 100644 --- a/test/1993-fallback-non-structural/expected.txt +++ b/test/1993-fallback-non-structural/expected.txt @@ -1,3 +1,3 @@ -Can structurally Redefine: false +Can structurally Redefine: true hello Goodbye diff --git a/test/1993-fallback-non-structural/src/art/Test1993.java b/test/1993-fallback-non-structural/src/art/Test1993.java index f4420993f7..e2a8f6e8e6 100644 --- a/test/1993-fallback-non-structural/src/art/Test1993.java +++ b/test/1993-fallback-non-structural/src/art/Test1993.java @@ -17,6 +17,7 @@ package art; import java.util.Base64; +import java.lang.reflect.*; public class Test1993 { static class Transform { @@ -61,17 +62,25 @@ public class Test1993 { "AAAASAEAAAMgAAACAAAAgAEAAAEQAAABAAAAjAEAAAIgAAAVAAAAkgEAAAQgAAACAAAALAMAAAAg" + "AAABAAAAOwMAAAMQAAACAAAATAMAAAYgAAABAAAAXAMAAAAQAAABAAAAbAMAAA=="); - public static void run() { + public static void run() throws Exception { Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE); doTest(new Transform()); } - public static void doTest(Transform t) { - // TODO Remove this once the class is structurally modifiable. + public static void doTest(Transform t) throws Exception { System.out.println("Can structurally Redefine: " + Redefinition.isStructurallyModifiable(Transform.class)); t.sayHi(); Redefinition.doCommonStructuralClassRedefinition(Transform.class, DEX_BYTES); t.sayHi(); + // Check and make sure we didn't structurally redefine by looking for ClassExt.obsoleteClass + Field ext_data_field = Class.class.getDeclaredField("extData"); + ext_data_field.setAccessible(true); + Object ext_data = ext_data_field.get(Transform.class); + Field obsolete_class_field = ext_data.getClass().getDeclaredField("obsoleteClass"); + obsolete_class_field.setAccessible(true); + if (obsolete_class_field.get(ext_data) != null) { + System.out.println("Expected no ClassExt.obsoleteClass but got " + obsolete_class_field.get(ext_data)); + } } } diff --git a/test/1999-virtual-structural/expected.txt b/test/1999-virtual-structural/expected.txt new file mode 100644 index 0000000000..335b5d73d9 --- /dev/null +++ b/test/1999-virtual-structural/expected.txt @@ -0,0 +1,4 @@ +Hi(SubTransform called 1 times) +Hi(SubTransform called 2 times) +Hi(SubTransform called 3 times) +Hello (Transform called 1 times)(SubTransform called 4 times) diff --git a/test/1999-virtual-structural/info.txt b/test/1999-virtual-structural/info.txt new file mode 100644 index 0000000000..606c984f7a --- /dev/null +++ b/test/1999-virtual-structural/info.txt @@ -0,0 +1,3 @@ +Tests basic functions in the jvmti plugin. + +Tests that using the structural redefinition can add new virtual methods and fields. diff --git a/test/1999-virtual-structural/run b/test/1999-virtual-structural/run new file mode 100755 index 0000000000..0d416328d4 --- /dev/null +++ b/test/1999-virtual-structural/run @@ -0,0 +1,21 @@ +#!/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. + +# TODO(b/144168550) This test uses access patterns that can be replaced by +# invoke-virtual-quick during dex2dex compilation. This breaks the test since the +# -quick opcode encodes the exact byte offset of vtable methods. Since this test +# changes the offset this causes problems. +./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true -Xcompiler-option --debuggable diff --git a/test/1999-virtual-structural/src/Main.java b/test/1999-virtual-structural/src/Main.java new file mode 100644 index 0000000000..86a492bcb6 --- /dev/null +++ b/test/1999-virtual-structural/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.Test1999.run(); + } +} diff --git a/test/1999-virtual-structural/src/art/Redefinition.java b/test/1999-virtual-structural/src/art/Redefinition.java new file mode 120000 index 0000000000..81eaf31bbb --- /dev/null +++ b/test/1999-virtual-structural/src/art/Redefinition.java @@ -0,0 +1 @@ +../../../jvmti-common/Redefinition.java
\ No newline at end of file diff --git a/test/1999-virtual-structural/src/art/Test1999.java b/test/1999-virtual-structural/src/art/Test1999.java new file mode 100644 index 0000000000..f6811a90cd --- /dev/null +++ b/test/1999-virtual-structural/src/art/Test1999.java @@ -0,0 +1,85 @@ +/* + * 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.util.Base64; +public class Test1999 { + + public static class Transform { + public String getGreeting() { + return "Hi"; + } + } + + public static class SubTransform extends Transform { + private int count = 0; + public void sayHi() { + System.out.println(getGreeting() + "(SubTransform called " + (++count) + " times)"); + } + } + + /** + * base64 encoded class/dex file for + * public static class Transform { + * private int count; + * public String getGreeting() { + * return "Hello (Transform called " + incrCount() + " times)"; + * } + * protected int incrCount() { + * return ++count; + * } + * } + */ + private static final byte[] DEX_BYTES = Base64.getDecoder().decode( +"ZGV4CjAzNQAwwbMpPdPdWkU+6UJnvqa7v4VBdcuq2vkMBQAAcAAAAHhWNBIAAAAAAAAAAEgEAAAa" + +"AAAAcAAAAAkAAADYAAAABQAAAPwAAAABAAAAOAEAAAgAAABAAQAAAQAAAIABAABsAwAAoAEAADoC" + +"AABDAgAASwIAAGUCAABoAgAAawIAAG8CAABzAgAAjQIAAJ0CAADBAgAA4QIAAPUCAAAJAwAAJAMA" + +"ADMDAAA+AwAAQQMAAE4DAABWAwAAXQMAAGoDAAB1AwAAewMAAIUDAACMAwAAAwAAAAcAAAAIAAAA" + +"CQAAAAoAAAALAAAADAAAAA0AAAAQAAAAAwAAAAAAAAAAAAAABAAAAAYAAAAAAAAABQAAAAcAAAAs" + +"AgAABgAAAAcAAAA0AgAAEAAAAAgAAAAAAAAAAQAAABMAAAABAAQAAQAAAAEAAQAUAAAAAQAAABUA" + +"AAAFAAQAAQAAAAcABAABAAAABwACABIAAAAHAAMAEgAAAAcAAQAXAAAAAQAAAAEAAAAFAAAAAAAA" + +"AA4AAAA4BAAAEwQAAAAAAAACAAEAAAAAACgCAAAHAAAAUhAAANgAAAFZEAAADwAAAAQAAQACAAAA" + +"JAIAABsAAABuEAIAAwAKACIBBwBwEAQAAQAaAgIAbiAGACEAbiAFAAEAGgAAAG4gBgABAG4QBwAB" + +"AAwAEQAAAAEAAQABAAAAIAIAAAQAAABwEAMAAAAOAAMADgAGAA4ACQAOAAEAAAAAAAAAAQAAAAYA" + +"ByB0aW1lcykABjxpbml0PgAYSGVsbG8gKFRyYW5zZm9ybSBjYWxsZWQgAAFJAAFMAAJMSQACTEwA" + +"GExhcnQvVGVzdDE5OTkkVHJhbnNmb3JtOwAOTGFydC9UZXN0MTk5OTsAIkxkYWx2aWsvYW5ub3Rh" + +"dGlvbi9FbmNsb3NpbmdDbGFzczsAHkxkYWx2aWsvYW5ub3RhdGlvbi9Jbm5lckNsYXNzOwASTGph" + +"dmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAGUxqYXZhL2xhbmcvU3RyaW5nQnVp" + +"bGRlcjsADVRlc3QxOTk5LmphdmEACVRyYW5zZm9ybQABVgALYWNjZXNzRmxhZ3MABmFwcGVuZAAF" + +"Y291bnQAC2dldEdyZWV0aW5nAAlpbmNyQ291bnQABG5hbWUACHRvU3RyaW5nAAV2YWx1ZQB2fn5E" + +"OHsiY29tcGlsYXRpb24tbW9kZSI6ImRlYnVnIiwibWluLWFwaSI6MSwic2hhLTEiOiI2MGRhNGQ2" + +"N2IzODFjNDI0Njc3NTdjNDlmYjZlNTU3NTZkODhhMmYzIiwidmVyc2lvbiI6IjEuNy4xMi1kZXYi" + +"fQACAwEYGAICBAIRBAkWFw8AAQECAAIAgYAEiAQBAcADAQSgAwAAAAAAAgAAAAQEAAAKBAAALAQA" + +"AAAAAAAAAAAAAAAAABAAAAAAAAAAAQAAAAAAAAABAAAAGgAAAHAAAAACAAAACQAAANgAAAADAAAA" + +"BQAAAPwAAAAEAAAAAQAAADgBAAAFAAAACAAAAEABAAAGAAAAAQAAAIABAAABIAAAAwAAAKABAAAD" + +"IAAAAwAAACACAAABEAAAAgAAACwCAAACIAAAGgAAADoCAAAEIAAAAgAAAAQEAAAAIAAAAQAAABME" + +"AAADEAAAAgAAACgEAAAGIAAAAQAAADgEAAAAEAAAAQAAAEgEAAA="); + + + public static void run() { + Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE); + doTest(new SubTransform()); + } + + public static void doTest(SubTransform t) { + t.sayHi(); + t.sayHi(); + t.sayHi(); + Redefinition.doCommonStructuralClassRedefinition(Transform.class, DEX_BYTES); + t.sayHi(); + } +} diff --git a/test/2000-virtual-list-structural/AbstractCollection.patch b/test/2000-virtual-list-structural/AbstractCollection.patch new file mode 100644 index 0000000000..7507c7d862 --- /dev/null +++ b/test/2000-virtual-list-structural/AbstractCollection.patch @@ -0,0 +1,16 @@ +--- ../../../libcore/ojluni/src/main/java/java/util/AbstractCollection.java 2019-05-31 10:36:26.634361294 -0700 ++++ src-ex/java/util/AbstractCollection.java 2019-11-18 13:04:48.253575013 -0800 +@@ -63,7 +63,13 @@ + * Sole constructor. (For invocation by subclass constructors, typically + * implicit.) + */ ++ public static volatile int TOTAL_COUNT; ++ public int cnt; ++ + protected AbstractCollection() { ++ synchronized (Collection.class) { ++ cnt = ++TOTAL_COUNT; ++ } + } + + // Query Operations diff --git a/test/2000-virtual-list-structural/build b/test/2000-virtual-list-structural/build new file mode 100755 index 0000000000..87d6acc2f5 --- /dev/null +++ b/test/2000-virtual-list-structural/build @@ -0,0 +1,31 @@ +#!/bin/bash +# +# Copyright 2019 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. + +# Stop on failure. +set -e + +# Deref the symlink. +mv src-ex/java/util/AbstractCollection.java src-ex/java/util/AbstractCollection.bak +cp src-ex/java/util/AbstractCollection.bak src-ex/java/util/AbstractCollection.java + +# Patch the copied version. +patch src-ex/java/util/AbstractCollection.java AbstractCollection.patch + +DESUGAR=false ./default-build "$@" + +# restore the symlink +rm src-ex/java/util/AbstractCollection.java +mv src-ex/java/util/AbstractCollection.bak src-ex/java/util/AbstractCollection.java diff --git a/test/2000-virtual-list-structural/expected.txt b/test/2000-virtual-list-structural/expected.txt new file mode 100644 index 0000000000..9d3e1b6a4a --- /dev/null +++ b/test/2000-virtual-list-structural/expected.txt @@ -0,0 +1,5 @@ +List is: [a, b, c, d] +List is: [1, 2, 3, 4] +List is: [1, 2, 3, 4, xyz: 0, xyz: 1, xyz: 2, xyz: 3, xyz: 4, xyz: 5, xyz: 6, xyz: 7, xyz: 8, xyz: 9, xyz: 10, xyz: 11, xyz: 12, xyz: 13, xyz: 14, xyz: 15, xyz: 16, xyz: 17, xyz: 18, xyz: 19, xyz: 20, xyz: 21, xyz: 22, xyz: 23, xyz: 24, xyz: 25, xyz: 26, xyz: 27, xyz: 28, xyz: 29, xyz: 30, xyz: 31, xyz: 32, xyz: 33, xyz: 34, xyz: 35, xyz: 36, xyz: 37, xyz: 38, xyz: 39, xyz: 40, xyz: 41, xyz: 42, xyz: 43, xyz: 44, xyz: 45, xyz: 46, xyz: 47, xyz: 48, xyz: 49, xyz: 50, xyz: 51, xyz: 52, xyz: 53, xyz: 54, xyz: 55, xyz: 56, xyz: 57, xyz: 58, xyz: 59, xyz: 60, xyz: 61, xyz: 62, xyz: 63, xyz: 64, xyz: 65, xyz: 66, xyz: 67, xyz: 68, xyz: 69, xyz: 70, xyz: 71, xyz: 72, xyz: 73, xyz: 74, xyz: 75, xyz: 76, xyz: 77, xyz: 78, xyz: 79, xyz: 80, xyz: 81, xyz: 82, xyz: 83, xyz: 84, xyz: 85, xyz: 86, xyz: 87, xyz: 88, xyz: 89, xyz: 90, xyz: 91, xyz: 92, xyz: 93, xyz: 94, xyz: 95, xyz: 96, xyz: 97, xyz: 98, xyz: 99, xyz: 100, xyz: 101, xyz: 102, xyz: 103, xyz: 104, xyz: 105, xyz: 106, xyz: 107, xyz: 108, xyz: 109, xyz: 110, xyz: 111, xyz: 112, xyz: 113, xyz: 114, xyz: 115, xyz: 116, xyz: 117, xyz: 118, xyz: 119, xyz: 120, xyz: 121, xyz: 122, xyz: 123, xyz: 124, xyz: 125, xyz: 126, xyz: 127, xyz: 128, xyz: 129, xyz: 130, xyz: 131, xyz: 132, xyz: 133, xyz: 134, xyz: 135, xyz: 136, xyz: 137, xyz: 138, xyz: 139, xyz: 140, xyz: 141, xyz: 142, xyz: 143, xyz: 144, xyz: 145, xyz: 146, xyz: 147, xyz: 148, xyz: 149, xyz: 150, xyz: 151, xyz: 152, xyz: 153, xyz: 154, xyz: 155, xyz: 156, xyz: 157, xyz: 158, xyz: 159, xyz: 160, xyz: 161, xyz: 162, xyz: 163, xyz: 164, xyz: 165, xyz: 166, xyz: 167, xyz: 168, xyz: 169, xyz: 170, xyz: 171, xyz: 172, xyz: 173, xyz: 174, xyz: 175, xyz: 176, xyz: 177, xyz: 178, xyz: 179, xyz: 180, xyz: 181, xyz: 182, xyz: 183, xyz: 184, xyz: 185, xyz: 186, xyz: 187, xyz: 188, xyz: 189, xyz: 190, xyz: 191, xyz: 192, xyz: 193, xyz: 194, xyz: 195, xyz: 196, xyz: 197, xyz: 198, xyz: 199, xyz: 200, xyz: 201, xyz: 202, xyz: 203, xyz: 204, xyz: 205, xyz: 206, xyz: 207, xyz: 208, xyz: 209, xyz: 210, xyz: 211, xyz: 212, xyz: 213, xyz: 214, xyz: 215, xyz: 216, xyz: 217, xyz: 218, xyz: 219, xyz: 220, xyz: 221, xyz: 222, xyz: 223, xyz: 224, xyz: 225, xyz: 226, xyz: 227, xyz: 228, xyz: 229, xyz: 230, xyz: 231, xyz: 232, xyz: 233, xyz: 234, xyz: 235, xyz: 236, xyz: 237, xyz: 238, xyz: 239, xyz: 240, xyz: 241, xyz: 242, xyz: 243, xyz: 244, xyz: 245, xyz: 246, xyz: 247, xyz: 248, xyz: 249, xyz: 250, xyz: 251, xyz: 252, xyz: 253, xyz: 254, xyz: 255, xyz: 256, xyz: 257, xyz: 258, xyz: 259, xyz: 260, xyz: 261, xyz: 262, xyz: 263, xyz: 264, xyz: 265, xyz: 266, xyz: 267, xyz: 268, xyz: 269, xyz: 270, xyz: 271, xyz: 272, xyz: 273, xyz: 274, xyz: 275, xyz: 276, xyz: 277, xyz: 278, xyz: 279, xyz: 280, xyz: 281, xyz: 282, xyz: 283, xyz: 284, xyz: 285, xyz: 286, xyz: 287, xyz: 288, xyz: 289, xyz: 290, xyz: 291, xyz: 292, xyz: 293, xyz: 294, xyz: 295, xyz: 296, xyz: 297, xyz: 298, xyz: 299, xyz: 300, xyz: 301, xyz: 302, xyz: 303, xyz: 304, xyz: 305, xyz: 306, xyz: 307, xyz: 308, xyz: 309, xyz: 310, xyz: 311, xyz: 312, xyz: 313, xyz: 314, xyz: 315, xyz: 316, xyz: 317, xyz: 318, xyz: 319, xyz: 320, xyz: 321, xyz: 322, xyz: 323, xyz: 324, xyz: 325, xyz: 326, xyz: 327, xyz: 328, xyz: 329, xyz: 330, xyz: 331, xyz: 332, xyz: 333, xyz: 334, xyz: 335, xyz: 336, xyz: 337, xyz: 338, xyz: 339, xyz: 340, xyz: 341, xyz: 342, xyz: 343, xyz: 344, xyz: 345, xyz: 346, xyz: 347, xyz: 348, xyz: 349, xyz: 350, xyz: 351, xyz: 352, xyz: 353, xyz: 354, xyz: 355, xyz: 356, xyz: 357, xyz: 358, xyz: 359, xyz: 360, xyz: 361, xyz: 362, xyz: 363, xyz: 364, xyz: 365, xyz: 366, xyz: 367, xyz: 368, xyz: 369, xyz: 370, xyz: 371, xyz: 372, xyz: 373, xyz: 374, xyz: 375, xyz: 376, xyz: 377, xyz: 378, xyz: 379, xyz: 380, xyz: 381, xyz: 382, xyz: 383, xyz: 384, xyz: 385, xyz: 386, xyz: 387, xyz: 388, xyz: 389, xyz: 390, xyz: 391, xyz: 392, xyz: 393, xyz: 394, xyz: 395, xyz: 396, xyz: 397, xyz: 398, xyz: 399, xyz: 400, xyz: 401, xyz: 402, xyz: 403, xyz: 404, xyz: 405, xyz: 406, xyz: 407, xyz: 408, xyz: 409, xyz: 410, xyz: 411, xyz: 412, xyz: 413, xyz: 414, xyz: 415, xyz: 416, xyz: 417, xyz: 418, xyz: 419, xyz: 420, xyz: 421, xyz: 422, xyz: 423, xyz: 424, xyz: 425, xyz: 426, xyz: 427, xyz: 428, xyz: 429, xyz: 430, xyz: 431, xyz: 432, xyz: 433, xyz: 434, xyz: 435, xyz: 436, xyz: 437, xyz: 438, xyz: 439, xyz: 440, xyz: 441, xyz: 442, xyz: 443, xyz: 444, xyz: 445, xyz: 446, xyz: 447, xyz: 448, xyz: 449, xyz: 450, xyz: 451, xyz: 452, xyz: 453, xyz: 454, xyz: 455, xyz: 456, xyz: 457, xyz: 458, xyz: 459, xyz: 460, xyz: 461, xyz: 462, xyz: 463, xyz: 464, xyz: 465, xyz: 466, xyz: 467, xyz: 468, xyz: 469, xyz: 470, xyz: 471, xyz: 472, xyz: 473, xyz: 474, xyz: 475, xyz: 476, xyz: 477, xyz: 478, xyz: 479, xyz: 480, xyz: 481, xyz: 482, xyz: 483, xyz: 484, xyz: 485, xyz: 486, xyz: 487, xyz: 488, xyz: 489, xyz: 490, xyz: 491, xyz: 492, xyz: 493, xyz: 494, xyz: 495, xyz: 496, xyz: 497, xyz: 498, xyz: 499, xyz: 500, xyz: 501, xyz: 502, xyz: 503, xyz: 504, xyz: 505, xyz: 506, xyz: 507, xyz: 508, xyz: 509, xyz: 510, xyz: 511, xyz: 512, xyz: 513, xyz: 514, xyz: 515, xyz: 516, xyz: 517, xyz: 518, xyz: 519, xyz: 520, xyz: 521, xyz: 522, xyz: 523, xyz: 524, xyz: 525, xyz: 526, xyz: 527, xyz: 528, xyz: 529, xyz: 530, xyz: 531, xyz: 532, xyz: 533, xyz: 534, xyz: 535, xyz: 536, xyz: 537, xyz: 538, xyz: 539, xyz: 540, xyz: 541, xyz: 542, xyz: 543, xyz: 544, xyz: 545, xyz: 546, xyz: 547, xyz: 548, xyz: 549, xyz: 550, xyz: 551, xyz: 552, xyz: 553, xyz: 554, xyz: 555, xyz: 556, xyz: 557, xyz: 558, xyz: 559, xyz: 560, xyz: 561, xyz: 562, xyz: 563, xyz: 564, xyz: 565, xyz: 566, xyz: 567, xyz: 568, xyz: 569, xyz: 570, xyz: 571, xyz: 572, xyz: 573, xyz: 574, xyz: 575, xyz: 576, xyz: 577, xyz: 578, xyz: 579, xyz: 580, xyz: 581, xyz: 582, xyz: 583, xyz: 584, xyz: 585, xyz: 586, xyz: 587, xyz: 588, xyz: 589, xyz: 590, xyz: 591, xyz: 592, xyz: 593, xyz: 594, xyz: 595, xyz: 596, xyz: 597, xyz: 598, xyz: 599, xyz: 600, xyz: 601, xyz: 602, xyz: 603, xyz: 604, xyz: 605, xyz: 606, xyz: 607, xyz: 608, xyz: 609, xyz: 610, xyz: 611, xyz: 612, xyz: 613, xyz: 614, xyz: 615, xyz: 616, xyz: 617, xyz: 618, xyz: 619, xyz: 620, xyz: 621, xyz: 622, xyz: 623, xyz: 624, xyz: 625, xyz: 626, xyz: 627, xyz: 628, xyz: 629, xyz: 630, xyz: 631, xyz: 632, xyz: 633, xyz: 634, xyz: 635, xyz: 636, xyz: 637, xyz: 638, xyz: 639, xyz: 640, xyz: 641, xyz: 642, xyz: 643, xyz: 644, xyz: 645, xyz: 646, xyz: 647, xyz: 648, xyz: 649, xyz: 650, xyz: 651, xyz: 652, xyz: 653, xyz: 654, xyz: 655, xyz: 656, xyz: 657, xyz: 658, xyz: 659, xyz: 660, xyz: 661, xyz: 662, xyz: 663, xyz: 664, xyz: 665, xyz: 666, xyz: 667, xyz: 668, xyz: 669, xyz: 670, xyz: 671, xyz: 672, xyz: 673, xyz: 674, xyz: 675, xyz: 676, xyz: 677, xyz: 678, xyz: 679, xyz: 680, xyz: 681, xyz: 682, xyz: 683, xyz: 684, xyz: 685, xyz: 686, xyz: 687, xyz: 688, xyz: 689, xyz: 690, xyz: 691, xyz: 692, xyz: 693, xyz: 694, xyz: 695, xyz: 696, xyz: 697, xyz: 698, xyz: 699, xyz: 700, xyz: 701, xyz: 702, xyz: 703, xyz: 704, xyz: 705, xyz: 706, xyz: 707, xyz: 708, xyz: 709, xyz: 710, xyz: 711, xyz: 712, xyz: 713, xyz: 714, xyz: 715, xyz: 716, xyz: 717, xyz: 718, xyz: 719, xyz: 720, xyz: 721, xyz: 722, xyz: 723, xyz: 724, xyz: 725, xyz: 726, xyz: 727, xyz: 728, xyz: 729, xyz: 730, xyz: 731, xyz: 732, xyz: 733, xyz: 734, xyz: 735, xyz: 736, xyz: 737, xyz: 738, xyz: 739, xyz: 740, xyz: 741, xyz: 742, xyz: 743, xyz: 744, xyz: 745, xyz: 746, xyz: 747, xyz: 748, xyz: 749, xyz: 750, xyz: 751, xyz: 752, xyz: 753, xyz: 754, xyz: 755, xyz: 756, xyz: 757, xyz: 758, xyz: 759, xyz: 760, xyz: 761, xyz: 762, xyz: 763, xyz: 764, xyz: 765, xyz: 766, xyz: 767, xyz: 768, xyz: 769, xyz: 770, xyz: 771, xyz: 772, xyz: 773, xyz: 774, xyz: 775, xyz: 776, xyz: 777, xyz: 778, xyz: 779, xyz: 780, xyz: 781, xyz: 782, xyz: 783, xyz: 784, xyz: 785, xyz: 786, xyz: 787, xyz: 788, xyz: 789, xyz: 790, xyz: 791, xyz: 792, xyz: 793, xyz: 794, xyz: 795, xyz: 796, xyz: 797, xyz: 798, xyz: 799, xyz: 800, xyz: 801, xyz: 802, xyz: 803, xyz: 804, xyz: 805, xyz: 806, xyz: 807, xyz: 808, xyz: 809, xyz: 810, xyz: 811, xyz: 812, xyz: 813, xyz: 814, xyz: 815, xyz: 816, xyz: 817, xyz: 818, xyz: 819, xyz: 820, xyz: 821, xyz: 822, xyz: 823, xyz: 824, xyz: 825, xyz: 826, xyz: 827, xyz: 828, xyz: 829, xyz: 830, xyz: 831, xyz: 832, xyz: 833, xyz: 834, xyz: 835, xyz: 836, xyz: 837, xyz: 838, xyz: 839, xyz: 840, xyz: 841, xyz: 842, xyz: 843, xyz: 844, xyz: 845, xyz: 846, xyz: 847, xyz: 848, xyz: 849, xyz: 850, xyz: 851, xyz: 852, xyz: 853, xyz: 854, xyz: 855, xyz: 856, xyz: 857, xyz: 858, xyz: 859, xyz: 860, xyz: 861, xyz: 862, xyz: 863, xyz: 864, xyz: 865, xyz: 866, xyz: 867, xyz: 868, xyz: 869, xyz: 870, xyz: 871, xyz: 872, xyz: 873, xyz: 874, xyz: 875, xyz: 876, xyz: 877, xyz: 878, xyz: 879, xyz: 880, xyz: 881, xyz: 882, xyz: 883, xyz: 884, xyz: 885, xyz: 886, xyz: 887, xyz: 888, xyz: 889, xyz: 890, xyz: 891, xyz: 892, xyz: 893, xyz: 894, xyz: 895, xyz: 896, xyz: 897, xyz: 898, xyz: 899, xyz: 900, xyz: 901, xyz: 902, xyz: 903, xyz: 904, xyz: 905, xyz: 906, xyz: 907, xyz: 908, xyz: 909, xyz: 910, xyz: 911, xyz: 912, xyz: 913, xyz: 914, xyz: 915, xyz: 916, xyz: 917, xyz: 918, xyz: 919, xyz: 920, xyz: 921, xyz: 922, xyz: 923, xyz: 924, xyz: 925, xyz: 926, xyz: 927, xyz: 928, xyz: 929, xyz: 930, xyz: 931, xyz: 932, xyz: 933, xyz: 934, xyz: 935, xyz: 936, xyz: 937, xyz: 938, xyz: 939, xyz: 940, xyz: 941, xyz: 942, xyz: 943, xyz: 944, xyz: 945, xyz: 946, xyz: 947, xyz: 948, xyz: 949, xyz: 950, xyz: 951, xyz: 952, xyz: 953, xyz: 954, xyz: 955, xyz: 956, xyz: 957, xyz: 958, xyz: 959, xyz: 960, xyz: 961, xyz: 962, xyz: 963, xyz: 964, xyz: 965, xyz: 966, xyz: 967, xyz: 968, xyz: 969, xyz: 970, xyz: 971, xyz: 972, xyz: 973, xyz: 974, xyz: 975, xyz: 976, xyz: 977, xyz: 978, xyz: 979, xyz: 980, xyz: 981, xyz: 982, xyz: 983, xyz: 984, xyz: 985, xyz: 986, xyz: 987, xyz: 988, xyz: 989, xyz: 990, xyz: 991, xyz: 992, xyz: 993, xyz: 994, xyz: 995, xyz: 996, xyz: 997, xyz: 998, xyz: 999] +List is: [1, 2, 3, 4] +List is: [a, b, c, d] diff --git a/test/2000-virtual-list-structural/info.txt b/test/2000-virtual-list-structural/info.txt new file mode 100644 index 0000000000..606c984f7a --- /dev/null +++ b/test/2000-virtual-list-structural/info.txt @@ -0,0 +1,3 @@ +Tests basic functions in the jvmti plugin. + +Tests that using the structural redefinition can add new virtual methods and fields. diff --git a/test/2000-virtual-list-structural/run b/test/2000-virtual-list-structural/run new file mode 100755 index 0000000000..0d416328d4 --- /dev/null +++ b/test/2000-virtual-list-structural/run @@ -0,0 +1,21 @@ +#!/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. + +# TODO(b/144168550) This test uses access patterns that can be replaced by +# invoke-virtual-quick during dex2dex compilation. This breaks the test since the +# -quick opcode encodes the exact byte offset of vtable methods. Since this test +# changes the offset this causes problems. +./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true -Xcompiler-option --debuggable diff --git a/test/2000-virtual-list-structural/src-ex/java/util/AbstractCollection.java b/test/2000-virtual-list-structural/src-ex/java/util/AbstractCollection.java new file mode 120000 index 0000000000..a30fbdc6e0 --- /dev/null +++ b/test/2000-virtual-list-structural/src-ex/java/util/AbstractCollection.java @@ -0,0 +1 @@ +../../../../../../libcore/ojluni/src/main/java/java/util/AbstractCollection.java
\ No newline at end of file diff --git a/test/2000-virtual-list-structural/src/Main.java b/test/2000-virtual-list-structural/src/Main.java new file mode 100644 index 0000000000..6a10c23735 --- /dev/null +++ b/test/2000-virtual-list-structural/src/Main.java @@ -0,0 +1,94 @@ +/* + * 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 art.*; +import java.util.*; +import java.lang.invoke.*; +import java.io.*; + +public class Main { + public static final boolean PRINT_COUNT = false; + public static MethodHandles.Lookup lookup = MethodHandles.publicLookup(); + public static MethodHandle getcnt; + public static MethodHandle get_total_cnt; + public static void GetHandles() throws Throwable { + getcnt = lookup.findGetter(AbstractCollection.class, "cnt", Integer.TYPE); + get_total_cnt = lookup.findStaticGetter(AbstractCollection.class, "TOTAL_COUNT", Integer.TYPE); + } + + public static byte[] GetDexBytes() throws Throwable { + try (RandomAccessFile dex = new RandomAccessFile(new File(System.getenv("DEX_LOCATION") + "/" + "classes-ex.dex"), "r")) { + byte[] res = new byte[(int)dex.length()]; + dex.read(res); + return res; + } + } + public static void PrintListAndData(AbstractCollection<String> c) throws Throwable { + if (PRINT_COUNT) { + System.out.println("List is: " + c + " count = " + getcnt.invoke(c) + " TOTAL_COUNT = " + get_total_cnt.invoke()); + } else { + System.out.println("List is: " + c); + } + } + public static void main(String[] args) throws Throwable { + AbstractCollection<String> l1 = (AbstractCollection<String>)Arrays.asList("a", "b", "c", "d"); + AbstractCollection<String> l2 = new ArrayList<>(); + l2.add("1"); + l2.add("2"); + l2.add("3"); + l2.add("4"); + Redefinition.doCommonStructuralClassRedefinition(AbstractCollection.class, GetDexBytes()); + GetHandles(); + AbstractCollection<String> l3 = new HashSet<>(l2); + AbstractCollection<String> l4 = new LinkedList<>(l1); + PrintListAndData(l1); + PrintListAndData(l2); + for (int i = 0; i < 1000; i++) { + l2.add("xyz: " + i); + } + PrintListAndData(l2); + PrintListAndData(l3); + PrintListAndData(l4); + CheckLE(getcnt.invoke(l1), get_total_cnt.invoke()); + CheckLE(getcnt.invoke(l2), get_total_cnt.invoke()); + CheckLE(getcnt.invoke(l3), get_total_cnt.invoke()); + CheckLE(getcnt.invoke(l4), get_total_cnt.invoke()); + CheckEQ(getcnt.invoke(l1), 0); + CheckLE(getcnt.invoke(l2), 0); + CheckLE(getcnt.invoke(l1), getcnt.invoke(l2)); + CheckLE(getcnt.invoke(l1), getcnt.invoke(l3)); + CheckLE(getcnt.invoke(l1), getcnt.invoke(l4)); + CheckLE(getcnt.invoke(l2), getcnt.invoke(l3)); + CheckLE(getcnt.invoke(l2), getcnt.invoke(l4)); + CheckLE(getcnt.invoke(l3), getcnt.invoke(l4)); + } + public static void CheckEQ(Object a, int b) { + CheckEQ(((Integer)a).intValue(), b); + } + public static void CheckLE(Object a, Object b) { + CheckLE(((Integer)a).intValue(), ((Integer)b).intValue()); + } + public static void CheckEQ(int a, int b) { + if (a != b) { + throw new Error(a + " is not equal to " + b); + } + } + public static void CheckLE(int a, int b) { + if (!(a <= b)) { + throw new Error(a + " is not less than or equal to " + b); + } + } +} diff --git a/test/2000-virtual-list-structural/src/art/Redefinition.java b/test/2000-virtual-list-structural/src/art/Redefinition.java new file mode 120000 index 0000000000..81eaf31bbb --- /dev/null +++ b/test/2000-virtual-list-structural/src/art/Redefinition.java @@ -0,0 +1 @@ +../../../jvmti-common/Redefinition.java
\ No newline at end of file diff --git a/test/2001-virtual-structural-multithread/expected.txt b/test/2001-virtual-structural-multithread/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/2001-virtual-structural-multithread/expected.txt diff --git a/test/2001-virtual-structural-multithread/info.txt b/test/2001-virtual-structural-multithread/info.txt new file mode 100644 index 0000000000..3e5291df87 --- /dev/null +++ b/test/2001-virtual-structural-multithread/info.txt @@ -0,0 +1,4 @@ +Tests structural redefinition with multiple threads. + +Tests that using the structural redefinition while concurrently loading and using a subtype of +the class being redefined doesn't cause any unexpected problems. diff --git a/test/2001-virtual-structural-multithread/run b/test/2001-virtual-structural-multithread/run new file mode 100755 index 0000000000..421f7b0bf2 --- /dev/null +++ b/test/2001-virtual-structural-multithread/run @@ -0,0 +1,21 @@ +#!/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. + +# TODO(b/144168550) This test uses access patterns that can be replaced by +# iget-object-quick during dex2dex compilation. This breaks the test since the +# -quick opcode encodes the exact byte offset of fields. Since this test changes +# the offset this causes problems. +./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true -Xcompiler-option --debuggable diff --git a/test/2001-virtual-structural-multithread/src-art/Main.java b/test/2001-virtual-structural-multithread/src-art/Main.java new file mode 100644 index 0000000000..618cdcdf25 --- /dev/null +++ b/test/2001-virtual-structural-multithread/src-art/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.Test2001.run(); + } +} diff --git a/test/2001-virtual-structural-multithread/src-art/art/Redefinition.java b/test/2001-virtual-structural-multithread/src-art/art/Redefinition.java new file mode 120000 index 0000000000..81eaf31bbb --- /dev/null +++ b/test/2001-virtual-structural-multithread/src-art/art/Redefinition.java @@ -0,0 +1 @@ +../../../jvmti-common/Redefinition.java
\ No newline at end of file diff --git a/test/2001-virtual-structural-multithread/src-art/art/Test2001.java b/test/2001-virtual-structural-multithread/src-art/art/Test2001.java new file mode 100644 index 0000000000..e6ee0ceaec --- /dev/null +++ b/test/2001-virtual-structural-multithread/src-art/art/Test2001.java @@ -0,0 +1,231 @@ +/* + * 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 dalvik.system.InMemoryDexClassLoader; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Base64; +import java.util.concurrent.CountDownLatch; +import java.util.function.Supplier; + +public class Test2001 { + private static final int NUM_THREADS = 20; + + public static class Transform { + public String greetingEnglish; + + public Transform() { + this.greetingEnglish = "Hello"; + } + + public String sayHi() { + return greetingEnglish + " from " + Thread.currentThread().getName(); + } + } + + /** + * base64 encoded class/dex file for + * public static class Transform { + * public String greetingEnglish; + * public String greetingFrench; + * public String greetingDanish; + * public String greetingJapanese; + * + * public Transform() { + * this.greetingEnglish = "Hello World"; + * this.greetingFrench = "Bonjour le Monde"; + * this.greetingDanish = "Hej Verden"; + * this.greetingJapanese = "こんにちは世界"; + * } + * public String sayHi() { + * return sayHiEnglish() + ", " + sayHiFrench() + ", " + sayHiDanish() + ", " + sayHiJapanese() + " from " + Thread.currentThread().getName(); + * } + * public String sayHiEnglish() { + * return greetingEnglish; + * } + * public String sayHiDanish() { + * return greetingDanish; + * } + * public String sayHiJapanese() { + * return greetingJapanese; + * } + * public String sayHiFrench() { + * return greetingFrench; + * } + * } + */ + private static final byte[] DEX_BYTES = + Base64.getDecoder() + .decode( + "ZGV4CjAzNQCnKPY06VRa4aM/zFW0MYLmRxT/NtXxD/H4BgAAcAAAAHhWNBIAAAAAAAAAADQGAAAl" + + "AAAAcAAAAAkAAAAEAQAABAAAACgBAAAEAAAAWAEAAAwAAAB4AQAAAQAAANgBAAAABQAA+AEAAEoD" + + "AABSAwAAVgMAAF4DAABwAwAAfAMAAIkDAACMAwAAkAMAAKoDAAC6AwAA3gMAAP4DAAASBAAAJgQA" + + "AEEEAABVBAAAZAQAAG8EAAByBAAAfwQAAIcEAACWBAAAnwQAAK8EAADABAAA0AQAAOIEAADoBAAA" + + "7wQAAPwEAAAKBQAAFwUAACYFAAAwBQAANwUAAMUFAAAIAAAACQAAAAoAAAALAAAADAAAAA0AAAAO" + + "AAAADwAAABIAAAAGAAAABQAAAAAAAAAHAAAABgAAAEQDAAAGAAAABwAAAAAAAAASAAAACAAAAAAA" + + "AAAAAAUAFwAAAAAABQAYAAAAAAAFABkAAAAAAAUAGgAAAAAAAwACAAAAAAAAABwAAAAAAAAAHQAA" + + "AAAAAAAeAAAAAAAAAB8AAAAAAAAAIAAAAAQAAwACAAAABgADAAIAAAAGAAEAFAAAAAYAAAAhAAAA" + + "BwACABUAAAAHAAAAFgAAAAAAAAABAAAABAAAAAAAAAAQAAAAJAYAAOsFAAAAAAAABwABAAIAAAAt" + + "AwAAQQAAAG4QAwAGAAwAbhAEAAYADAFuEAIABgAMAm4QBQAGAAwDcQAKAAAADARuEAsABAAMBCIF" + + "BgBwEAcABQBuIAgABQAaAAEAbiAIAAUAbiAIABUAbiAIAAUAbiAIACUAbiAIAAUAbiAIADUAGgAA" + + "AG4gCAAFAG4gCABFAG4QCQAFAAwAEQAAAAIAAQAAAAAAMQMAAAMAAABUEAAAEQAAAAIAAQAAAAAA" + + "NQMAAAMAAABUEAEAEQAAAAIAAQAAAAAAOQMAAAMAAABUEAIAEQAAAAIAAQAAAAAAPQMAAAMAAABU" + + "EAMAEQAAAAIAAQABAAAAJAMAABQAAABwEAYAAQAaAAUAWxABABoAAwBbEAIAGgAEAFsQAAAaACQA" + + "WxADAA4ACwAOPEtLS0sAEgAOABgADgAVAA4AHgAOABsADgAAAAABAAAABQAGIGZyb20gAAIsIAAG" + + "PGluaXQ+ABBCb25qb3VyIGxlIE1vbmRlAApIZWogVmVyZGVuAAtIZWxsbyBXb3JsZAABTAACTEwA" + + "GExhcnQvVGVzdDIwMDEkVHJhbnNmb3JtOwAOTGFydC9UZXN0MjAwMTsAIkxkYWx2aWsvYW5ub3Rh" + + "dGlvbi9FbmNsb3NpbmdDbGFzczsAHkxkYWx2aWsvYW5ub3RhdGlvbi9Jbm5lckNsYXNzOwASTGph" + + "dmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAGUxqYXZhL2xhbmcvU3RyaW5nQnVp" + + "bGRlcjsAEkxqYXZhL2xhbmcvVGhyZWFkOwANVGVzdDIwMDEuamF2YQAJVHJhbnNmb3JtAAFWAAth" + + "Y2Nlc3NGbGFncwAGYXBwZW5kAA1jdXJyZW50VGhyZWFkAAdnZXROYW1lAA5ncmVldGluZ0Rhbmlz" + + "aAAPZ3JlZXRpbmdFbmdsaXNoAA5ncmVldGluZ0ZyZW5jaAAQZ3JlZXRpbmdKYXBhbmVzZQAEbmFt" + + "ZQAFc2F5SGkAC3NheUhpRGFuaXNoAAxzYXlIaUVuZ2xpc2gAC3NheUhpRnJlbmNoAA1zYXlIaUph" + + "cGFuZXNlAAh0b1N0cmluZwAFdmFsdWUAiwF+fkQ4eyJjb21waWxhdGlvbi1tb2RlIjoiZGVidWci" + + "LCJoYXMtY2hlY2tzdW1zIjpmYWxzZSwibWluLWFwaSI6MSwic2hhLTEiOiJmNjJiOGNlNmEwNTkw" + + "MDU0ZWYzNGExYWVkZTcwYjQ2NjY4ZThiNDlmIiwidmVyc2lvbiI6IjIuMC4xLWRldiJ9AAfjgZPj" + + "gpPjgavjgaHjga/kuJbnlYwAAgIBIhgBAgMCEwQJGxcRAAQBBQABAQEBAQEBAIGABOwFAQH4AwEB" + + "jAUBAaQFAQG8BQEB1AUAAAAAAAAAAgAAANwFAADiBQAAGAYAAAAAAAAAAAAAAAAAABAAAAAAAAAA" + + "AQAAAAAAAAABAAAAJQAAAHAAAAACAAAACQAAAAQBAAADAAAABAAAACgBAAAEAAAABAAAAFgBAAAF" + + "AAAADAAAAHgBAAAGAAAAAQAAANgBAAABIAAABgAAAPgBAAADIAAABgAAACQDAAABEAAAAQAAAEQD" + + "AAACIAAAJQAAAEoDAAAEIAAAAgAAANwFAAAAIAAAAQAAAOsFAAADEAAAAgAAABQGAAAGIAAAAQAA" + + "ACQGAAAAEAAAAQAAADQGAAA="); + + /* + * base64 encoded class/dex file for + package art; + import java.util.function.Supplier; + public class SubTransform extends art.Test2001.Transform implements Supplier<String> { + public SubTransform() { + super(); + } + public String get() { + return "from SUBCLASS: " + super.sayHi(); + } + } + */ + private static final byte[] SUB_DEX_BYTES = + Base64.getDecoder() + .decode( + "ZGV4CjAzNQBawzkIDf9khFw00md41U4vIqRuhqBTjM+0BAAAcAAAAHhWNBIAAAAAAAAAAPwDAAAV" + + "AAAAcAAAAAgAAADEAAAABAAAAOQAAAAAAAAAAAAAAAgAAAAUAQAAAQAAAFQBAABAAwAAdAEAAAIC" + + "AAAKAgAADgIAABECAAAVAgAAKQIAAEMCAABiAgAAdgIAAIoCAAClAgAAxAIAAOMCAAD2AgAA+QIA" + + "AAEDAAASAwAAFwMAAB4DAAAoAwAALwMAAAQAAAAFAAAABgAAAAcAAAAIAAAACQAAAAoAAAANAAAA" + + "AgAAAAMAAAAAAAAAAgAAAAQAAAAAAAAAAwAAAAUAAAD8AQAADQAAAAcAAAAAAAAAAAADAAAAAAAA" + + "AAAAEAAAAAAAAQAQAAAAAQADAAAAAAABAAEAEQAAAAUAAwAAAAAABQACAA4AAAAFAAEAEgAAAAAA" + + "AAABAAAAAQAAAPQBAAAMAAAA7AMAAMsDAAAAAAAAAgABAAEAAADpAQAABQAAAG4QAgABAAwAEQAA" + + "AAQAAQACAAAA7QEAABYAAABvEAQAAwAMACIBBQBwEAUAAQAaAg8AbiAGACEAbiAGAAEAbhAHAAEA" + + "DAARAAEAAQABAAAA5AEAAAQAAABwEAMAAAAOAAYADjwABAAOAAkADgAAAAABAAAABgAAAAEAAAAE" + + "AAY8aW5pdD4AAj47AAFMAAJMTAASTGFydC9TdWJUcmFuc2Zvcm07ABhMYXJ0L1Rlc3QyMDAxJFRy" + + "YW5zZm9ybTsAHUxkYWx2aWsvYW5ub3RhdGlvbi9TaWduYXR1cmU7ABJMamF2YS9sYW5nL09iamVj" + + "dDsAEkxqYXZhL2xhbmcvU3RyaW5nOwAZTGphdmEvbGFuZy9TdHJpbmdCdWlsZGVyOwAdTGphdmEv" + + "dXRpbC9mdW5jdGlvbi9TdXBwbGllcjsAHUxqYXZhL3V0aWwvZnVuY3Rpb24vU3VwcGxpZXI8ABFT" + + "dWJUcmFuc2Zvcm0uamF2YQABVgAGYXBwZW5kAA9mcm9tIFNVQkNMQVNTOiAAA2dldAAFc2F5SGkA" + + "CHRvU3RyaW5nAAV2YWx1ZQCLAX5+RDh7ImNvbXBpbGF0aW9uLW1vZGUiOiJkZWJ1ZyIsImhhcy1j" + + "aGVja3N1bXMiOmZhbHNlLCJtaW4tYXBpIjoxLCJzaGEtMSI6ImY2MmI4Y2U2YTA1OTAwNTRlZjM0" + + "YTFhZWRlNzBiNDY2NjhlOGI0OWYiLCJ2ZXJzaW9uIjoiMi4wLjEtZGV2In0AAgIBExwEFwUXCxcI" + + "FwEAAAECAIGABMwDAcEg9AIBAZADAAAAAAAAAQAAAL0DAADkAwAAAAAAAAAAAAAAAAAADwAAAAAA" + + "AAABAAAAAAAAAAEAAAAVAAAAcAAAAAIAAAAIAAAAxAAAAAMAAAAEAAAA5AAAAAUAAAAIAAAAFAEA" + + "AAYAAAABAAAAVAEAAAEgAAADAAAAdAEAAAMgAAADAAAA5AEAAAEQAAACAAAA9AEAAAIgAAAVAAAA" + + "AgIAAAQgAAABAAAAvQMAAAAgAAABAAAAywMAAAMQAAACAAAA4AMAAAYgAAABAAAA7AMAAAAQAAAB" + + "AAAA/AMAAA=="); + + public static void run() throws Exception { + Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE); + doTest(); + } + + public static Supplier<String> mkTransform() { + try { + return (Supplier<String>) + (new InMemoryDexClassLoader( + ByteBuffer.wrap(SUB_DEX_BYTES), Test2001.class.getClassLoader()) + .loadClass("art.SubTransform") + .newInstance()); + } catch (Exception e) { + return () -> { + return e.toString(); + }; + } + } + + public static final class MyThread extends Thread { + public MyThread(CountDownLatch delay, int id) { + super("Thread: " + id); + this.thr_id = id; + this.results = new ArrayList<>(1000); + this.finish = false; + this.delay = delay; + } + + public void run() { + delay.countDown(); + while (!finish) { + Supplier<String> t = mkTransform(); + results.add(t.get()); + } + } + + public void finish() throws Exception { + finish = true; + this.join(); + } + + public void Check() throws Exception { + for (String s : results) { + if (!s.equals("from SUBCLASS: Hello from " + getName()) + && !s.equals("from SUBCLASS: Hello, null, null, null from " + getName()) + && !s.equals( + "from SUBCLASS: Hello World, Bonjour le Monde, Hej Verden, こんにちは世界 from " + + getName())) { + System.out.println("FAIL " + thr_id + ": Unexpected result: " + s); + } + } + } + + public ArrayList<String> results; + public volatile boolean finish; + public int thr_id; + public CountDownLatch delay; + } + + public static MyThread[] startThreads(int num_threads) throws Exception { + CountDownLatch cdl = new CountDownLatch(num_threads); + MyThread[] res = new MyThread[num_threads]; + for (int i = 0; i < num_threads; i++) { + res[i] = new MyThread(cdl, i); + res[i].start(); + } + cdl.await(); + return res; + } + + public static void finishThreads(MyThread[] thrs) throws Exception { + for (MyThread t : thrs) { + t.finish(); + } + for (MyThread t : thrs) { + t.Check(); + } + } + + public static void doTest() throws Exception { + MyThread[] threads = startThreads(NUM_THREADS); + Redefinition.doCommonStructuralClassRedefinition(Transform.class, DEX_BYTES); + finishThreads(threads); + } +} diff --git a/test/2001-virtual-structural-multithread/src/Main.java b/test/2001-virtual-structural-multithread/src/Main.java new file mode 100644 index 0000000000..89b8557970 --- /dev/null +++ b/test/2001-virtual-structural-multithread/src/Main.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2019 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 { + System.out.println("FAIL: Test is only for art!"); + } +} diff --git a/test/2002-virtual-structural-initializing/expected.txt b/test/2002-virtual-structural-initializing/expected.txt new file mode 100644 index 0000000000..c1c8a70a8b --- /dev/null +++ b/test/2002-virtual-structural-initializing/expected.txt @@ -0,0 +1 @@ +Initialized Static Hello diff --git a/test/2002-virtual-structural-initializing/info.txt b/test/2002-virtual-structural-initializing/info.txt new file mode 100644 index 0000000000..3e5291df87 --- /dev/null +++ b/test/2002-virtual-structural-initializing/info.txt @@ -0,0 +1,4 @@ +Tests structural redefinition with multiple threads. + +Tests that using the structural redefinition while concurrently loading and using a subtype of +the class being redefined doesn't cause any unexpected problems. diff --git a/test/2002-virtual-structural-initializing/run b/test/2002-virtual-structural-initializing/run new file mode 100755 index 0000000000..421f7b0bf2 --- /dev/null +++ b/test/2002-virtual-structural-initializing/run @@ -0,0 +1,21 @@ +#!/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. + +# TODO(b/144168550) This test uses access patterns that can be replaced by +# iget-object-quick during dex2dex compilation. This breaks the test since the +# -quick opcode encodes the exact byte offset of fields. Since this test changes +# the offset this causes problems. +./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true -Xcompiler-option --debuggable diff --git a/test/2002-virtual-structural-initializing/src-art/Main.java b/test/2002-virtual-structural-initializing/src-art/Main.java new file mode 100644 index 0000000000..a0aab42c7f --- /dev/null +++ b/test/2002-virtual-structural-initializing/src-art/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.Test2002.run(); + } +} diff --git a/test/2002-virtual-structural-initializing/src-art/art/Redefinition.java b/test/2002-virtual-structural-initializing/src-art/art/Redefinition.java new file mode 120000 index 0000000000..81eaf31bbb --- /dev/null +++ b/test/2002-virtual-structural-initializing/src-art/art/Redefinition.java @@ -0,0 +1 @@ +../../../jvmti-common/Redefinition.java
\ No newline at end of file diff --git a/test/2002-virtual-structural-initializing/src-art/art/Test2002.java b/test/2002-virtual-structural-initializing/src-art/art/Test2002.java new file mode 100644 index 0000000000..f91e3f7ec5 --- /dev/null +++ b/test/2002-virtual-structural-initializing/src-art/art/Test2002.java @@ -0,0 +1,161 @@ +/* + * 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 dalvik.system.InMemoryDexClassLoader; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Base64; +import java.util.concurrent.CountDownLatch; +import java.util.function.Supplier; + +public class Test2002 { + public static final CountDownLatch start_latch = new CountDownLatch(1); + public static final CountDownLatch finish_latch = new CountDownLatch(1); + public static class Transform { + public Transform() { } + + public String sayHi() { + return "Hi"; + } + } + + /** + * base64 encoded class/dex file for + * public static class Transform { + * public String greeting; + * + * public Transform() { + * greeting = "Hello"; + * } + * public String sayHi() { + * return greeting; + * } + * } + */ + private static final byte[] DEX_BYTES = + Base64.getDecoder() + .decode( +"ZGV4CjAzNQBlpDFxr5PhCBfCyN+GZYuYQvSqtTEESU3oAwAAcAAAAHhWNBIAAAAAAAAAADADAAAS" + +"AAAAcAAAAAcAAAC4AAAAAgAAANQAAAABAAAA7AAAAAMAAAD0AAAAAQAAAAwBAAC8AgAALAEAAHAB" + +"AAB4AQAAfwEAAIIBAACcAQAArAEAANABAADwAQAABAIAABgCAAAnAgAAMgIAADUCAABCAgAATAIA" + +"AFICAABZAgAAYAIAAAMAAAAEAAAABQAAAAYAAAAHAAAACAAAAAsAAAACAAAABQAAAAAAAAALAAAA" + +"BgAAAAAAAAAAAAUADQAAAAAAAQAAAAAAAAAAAA8AAAAEAAEAAAAAAAAAAAABAAAABAAAAAAAAAAJ" + +"AAAAIAMAAP0CAAAAAAAAAgABAAAAAABqAQAAAwAAAFQQAAARAAAAAgABAAEAAABkAQAACAAAAHAQ" + +"AgABABoAAQBbEAAADgAGAA48SwAKAA4AAAAGPGluaXQ+AAVIZWxsbwABTAAYTGFydC9UZXN0MjAw" + +"MiRUcmFuc2Zvcm07AA5MYXJ0L1Rlc3QyMDAyOwAiTGRhbHZpay9hbm5vdGF0aW9uL0VuY2xvc2lu" + +"Z0NsYXNzOwAeTGRhbHZpay9hbm5vdGF0aW9uL0lubmVyQ2xhc3M7ABJMamF2YS9sYW5nL09iamVj" + +"dDsAEkxqYXZhL2xhbmcvU3RyaW5nOwANVGVzdDIwMDIuamF2YQAJVHJhbnNmb3JtAAFWAAthY2Nl" + +"c3NGbGFncwAIZ3JlZXRpbmcABG5hbWUABXNheUhpAAV2YWx1ZQCLAX5+RDh7ImNvbXBpbGF0aW9u" + +"LW1vZGUiOiJkZWJ1ZyIsImhhcy1jaGVja3N1bXMiOmZhbHNlLCJtaW4tYXBpIjoxLCJzaGEtMSI6" + +"ImY2MmI4Y2U2YTA1OTAwNTRlZjM0YTFhZWRlNzBiNDY2NjhlOGI0OWYiLCJ2ZXJzaW9uIjoiMi4w" + +"LjEtZGV2In0AAgIBEBgBAgMCDAQJDhcKAAEBAQABAIGABMQCAQGsAgAAAAAAAAACAAAA7gIAAPQC" + +"AAAUAwAAAAAAAAAAAAAAAAAADwAAAAAAAAABAAAAAAAAAAEAAAASAAAAcAAAAAIAAAAHAAAAuAAA" + +"AAMAAAACAAAA1AAAAAQAAAABAAAA7AAAAAUAAAADAAAA9AAAAAYAAAABAAAADAEAAAEgAAACAAAA" + +"LAEAAAMgAAACAAAAZAEAAAIgAAASAAAAcAEAAAQgAAACAAAA7gIAAAAgAAABAAAA/QIAAAMQAAAC" + +"AAAAEAMAAAYgAAABAAAAIAMAAAAQAAABAAAAMAMAAA=="); + + /* + * base64 encoded class/dex file for + package art; + import java.util.function.Supplier; + import java.util.concurrent.CountDownLatch; + + public class SubTransform extends art.Test2002.Transform implements Supplier<String> { + public static final String staticId; + static { + String res = null; + try { + Test2002.start_latch.countDown(); + Test2002.finish_latch.await(); + res = "Initialized Static"; + } catch (Exception e) { + res = e.toString(); + } + staticId = res; + } + public SubTransform() { + super(); + } + public String get() { + return SubTransform.staticId + " " + sayHi(); + } + } + */ + private static final byte[] SUB_DEX_BYTES = + Base64.getDecoder() + .decode( +"ZGV4CjAzNQB0BhXQtGTKXAGE/UzeevPgeNK7UrQJRJkoBgAAcAAAAHhWNBIAAAAAAAAAAGQFAAAf" + +"AAAAcAAAAAsAAADsAAAABAAAABgBAAADAAAASAEAAAwAAABgAQAAAQAAAMABAABIBAAA4AEAAM4C" + +"AADRAgAA2wIAAOMCAADnAgAA+wIAAP4CAAACAwAAFgMAADADAABAAwAAXwMAAHYDAACKAwAAngMA" + +"ALkDAADgAwAA/wMAAB4EAAAxBAAANAQAADwEAABDBAAATgQAAFwEAABhBAAAaAQAAHUEAAB/BAAA" + +"iQQAAJAEAAAHAAAACAAAAAkAAAAKAAAACwAAAAwAAAANAAAADgAAAA8AAAAQAAAAEwAAAAUAAAAF" + +"AAAAAAAAAAUAAAAGAAAAAAAAAAYAAAAHAAAAyAIAABMAAAAKAAAAAAAAAAAABgAbAAAAAgAIABcA" + +"AAACAAgAGgAAAAAAAwABAAAAAAADAAIAAAAAAAAAGAAAAAAAAQAYAAAAAAABABkAAAABAAMAAgAA" + +"AAQAAQAcAAAABwADAAIAAAAHAAIAFAAAAAcAAQAcAAAACAADABUAAAAIAAMAFgAAAAAAAAABAAAA" + +"AQAAAMACAAASAAAAVAUAACwFAAAAAAAAAgABAAEAAAC1AgAABQAAAG4QAwABAAwAEQAAAAQAAQAC" + +"AAAAuQIAABsAAABiAAAAbhAEAAMADAEiAgcAcBAHAAIAbiAIAAIAGgAAAG4gCAACAG4gCAASAG4Q" + +"CQACAAwAEQAAAAEAAAABAAEApAIAABYAAAAAAGIAAgBuEAsAAABiAAEAbhAKAAAAGgAEACgGDQBu" + +"EAYAAAAMAGkAAAAOAAEAAAAMAAEAAQEEDgEAAQABAAAAsAIAAAQAAABwEAUAAAAOAAgADh9aWi8b" + +"HkwtABMADjwABQAOABYADgAAAAABAAAACQAAAAEAAAAGAAEgAAg8Y2xpbml0PgAGPGluaXQ+AAI+" + +"OwASSW5pdGlhbGl6ZWQgU3RhdGljAAFMAAJMTAASTGFydC9TdWJUcmFuc2Zvcm07ABhMYXJ0L1Rl" + +"c3QyMDAyJFRyYW5zZm9ybTsADkxhcnQvVGVzdDIwMDI7AB1MZGFsdmlrL2Fubm90YXRpb24vU2ln" + +"bmF0dXJlOwAVTGphdmEvbGFuZy9FeGNlcHRpb247ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZh" + +"L2xhbmcvU3RyaW5nOwAZTGphdmEvbGFuZy9TdHJpbmdCdWlsZGVyOwAlTGphdmEvdXRpbC9jb25j" + +"dXJyZW50L0NvdW50RG93bkxhdGNoOwAdTGphdmEvdXRpbC9mdW5jdGlvbi9TdXBwbGllcjsAHUxq" + +"YXZhL3V0aWwvZnVuY3Rpb24vU3VwcGxpZXI8ABFTdWJUcmFuc2Zvcm0uamF2YQABVgAGYXBwZW5k" + +"AAVhd2FpdAAJY291bnREb3duAAxmaW5pc2hfbGF0Y2gAA2dldAAFc2F5SGkAC3N0YXJ0X2xhdGNo" + +"AAhzdGF0aWNJZAAIdG9TdHJpbmcABXZhbHVlAIsBfn5EOHsiY29tcGlsYXRpb24tbW9kZSI6ImRl" + +"YnVnIiwiaGFzLWNoZWNrc3VtcyI6ZmFsc2UsIm1pbi1hcGkiOjEsInNoYS0xIjoiZjYyYjhjZTZh" + +"MDU5MDA1NGVmMzRhMWFlZGU3MGI0NjY2OGU4YjQ5ZiIsInZlcnNpb24iOiIyLjAuMS1kZXYifQAC" + +"AwEdHAQXCBcRFw0XAwEAAgIAGQCIgATEBAGBgASMBQLBIOADAQH8AwAAAAAAAQAAAB4FAABMBQAA" + +"AAAAAAAAAAAAAAAAEAAAAAAAAAABAAAAAAAAAAEAAAAfAAAAcAAAAAIAAAALAAAA7AAAAAMAAAAE" + +"AAAAGAEAAAQAAAADAAAASAEAAAUAAAAMAAAAYAEAAAYAAAABAAAAwAEAAAEgAAAEAAAA4AEAAAMg" + +"AAAEAAAApAIAAAEQAAACAAAAwAIAAAIgAAAfAAAAzgIAAAQgAAABAAAAHgUAAAAgAAABAAAALAUA" + +"AAMQAAACAAAASAUAAAYgAAABAAAAVAUAAAAQAAABAAAAZAUAAA=="); + + public static void run() throws Exception { + Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE); + doTest(); + } + + public static Supplier<String> mkTransform() { + try { + return (Supplier<String>) + (new InMemoryDexClassLoader( + ByteBuffer.wrap(SUB_DEX_BYTES), Test2002.class.getClassLoader()) + .loadClass("art.SubTransform") + .newInstance()); + } catch (Exception e) { + return () -> { + return e.toString(); + }; + } + } + + public static void doTest() throws Exception { + Thread t = new Thread(() -> { + Supplier<String> s = mkTransform(); + System.out.println(s.get()); + }); + t.start(); + start_latch.await(); + Redefinition.doCommonStructuralClassRedefinition(Transform.class, DEX_BYTES); + finish_latch.countDown(); + t.join(); + } +} diff --git a/test/2002-virtual-structural-initializing/src/Main.java b/test/2002-virtual-structural-initializing/src/Main.java new file mode 100644 index 0000000000..89b8557970 --- /dev/null +++ b/test/2002-virtual-structural-initializing/src/Main.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2019 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 { + System.out.println("FAIL: Test is only for art!"); + } +} diff --git a/test/knownfailures.json b/test/knownfailures.json index 069cecb27c..8cfb3a5d90 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -1150,7 +1150,11 @@ "1995-final-virtual-structural-multithread", "1996-final-override-virtual-structural", "1997-structural-shadow-method", - "1998-structural-shadow-field" + "1998-structural-shadow-field", + "1999-virtual-structural", + "2000-virtual-list-structural", + "2001-virtual-structural-multithread", + "2002-virtual-structural-initializing" ], "variant": "jvm", "description": ["Doesn't run on RI."] |