From 3f9d18a4058fbe6958a6b7ec827af221e84f9792 Mon Sep 17 00:00:00 2001 From: Ivan Dubrov Date: Thu, 9 Jul 2015 23:19:55 -0700 Subject: [PATCH] Support for Java7u79 --- build.gradle | 1 + .../dcevm/test/methods/MethodHandleTest.java | 34 +- gradle.properties | 2 +- .../full-jdk7u79-b15-method-handles.patch | 328 + ...k7u79-b02.patch => full-jdk7u79-b15.patch} | 7800 ++++++++--------- hotspot/.hg/patches/series | 5 +- 6 files changed, 4263 insertions(+), 3907 deletions(-) create mode 100644 hotspot/.hg/patches/full-jdk7u79-b15-method-handles.patch rename hotspot/.hg/patches/{full-jdk7u79-b02.patch => full-jdk7u79-b15.patch} (95%) diff --git a/build.gradle b/build.gradle index de9ebce7..456ec0db 100644 --- a/build.gradle +++ b/build.gradle @@ -78,6 +78,7 @@ project('hotspot') { exec { executable 'hg' args 'qpop', '-a' + ignoreExitValue = true } exec { executable 'hg' diff --git a/dcevm/src/test/java7/com/github/dcevm/test/methods/MethodHandleTest.java b/dcevm/src/test/java7/com/github/dcevm/test/methods/MethodHandleTest.java index 1efbf0d3..53f4a256 100644 --- a/dcevm/src/test/java7/com/github/dcevm/test/methods/MethodHandleTest.java +++ b/dcevm/src/test/java7/com/github/dcevm/test/methods/MethodHandleTest.java @@ -46,6 +46,11 @@ public class MethodHandleTest { // Version 0 public static class A { + public int field; + public A(int value) { + field = value; + } + public int method() { return 1; } @@ -65,6 +70,11 @@ public class MethodHandleTest { // Version 1 public static class A___1 { + public int field; + public A___1(int value) { + field = value * 10; + } + public int method() { return 2; } @@ -95,7 +105,7 @@ public class MethodHandleTest { MethodHandles.Lookup lookup = MethodHandles.lookup(); MethodHandle handle = lookup.findVirtual(A.class, "method", MethodType.methodType(int.class)); - A a = new A(); + A a = new A(3); assertEquals(1, (int) handle.invokeExact(a)); __toVersion__(1); @@ -114,7 +124,7 @@ public class MethodHandleTest { MethodHandles.Lookup lookup = MethodHandles.lookup(); MethodHandle handle = lookup.findVirtual(A.class, "method", MethodType.methodType(int.class)); - A a = new A(); + A a = new A(3); MethodHandle boundHandle = handle.bindTo(a); assertEquals(1, (int) boundHandle.invokeExact()); @@ -144,6 +154,24 @@ public class MethodHandleTest { assert __version__() == 0; } + @Test + public void testConstructorMethodHandleUpdated() throws Throwable { + + assert __version__() == 0; + + MethodHandles.Lookup lookup = MethodHandles.lookup(); + MethodHandle handle = lookup.findConstructor(A.class, MethodType.methodType(void.class, int.class)); + + assertEquals(12, ((A) handle.invoke(12)).field); + + __toVersion__(1); + + assertEquals(120, ((A) handle.invoke(12)).field); + + __toVersion__(0); + assert __version__() == 0; + } + @Test public void testComplexMethodHandleUpdated() throws Throwable { @@ -154,7 +182,7 @@ public class MethodHandleTest { MethodHandle filter = lookup.findVirtual(A.class, "filter", MethodType.methodType(int.class, int.class)); MethodHandle staticFilter = lookup.findStatic(A.class, "staticFilter", MethodType.methodType(int.class, int.class)); - A a = new A(); + A a = new A(3); MethodHandle boundFilter = filter.bindTo(a); handle = MethodHandles.filterReturnValue(handle, staticFilter); handle = MethodHandles.filterReturnValue(handle, boundFilter); diff --git a/gradle.properties b/gradle.properties index 6d203725..d20cf3de 100644 --- a/gradle.properties +++ b/gradle.properties @@ -31,7 +31,7 @@ targetJre=build/jre #hotspotTag=jdk7u55-b13 #hotspotTag=jdk7u60-b09 #hotspotTag=jdk7u71-b01 -#hotspotTag=jdk7u79-b02 +#hotspotTag=jdk7u79-b15 #hotspotTag=jdk8u31-b13 #hotspotTag=jdk8u20-b22 diff --git a/hotspot/.hg/patches/full-jdk7u79-b15-method-handles.patch b/hotspot/.hg/patches/full-jdk7u79-b15-method-handles.patch new file mode 100644 index 00000000..41439d75 --- /dev/null +++ b/hotspot/.hg/patches/full-jdk7u79-b15-method-handles.patch @@ -0,0 +1,328 @@ +# HG changeset patch +# Parent 2ffb90b422e20ff948d25b96bfb9be923c130734 + +diff -r 2ffb90b422e2 src/share/vm/classfile/javaClasses.cpp +--- a/src/share/vm/classfile/javaClasses.cpp Fri Jul 10 10:19:24 2015 -0700 ++++ b/src/share/vm/classfile/javaClasses.cpp Fri Jul 10 10:19:37 2015 -0700 +@@ -2409,6 +2409,52 @@ + } + } + ++// Support for java_lang_invoke_DirectMethodHandle$StaticAccessor ++ ++int java_lang_invoke_DirectMethodHandle_StaticAccessor::_static_offset_offset; ++ ++long java_lang_invoke_DirectMethodHandle_StaticAccessor::static_offset(oop dmh) { ++ assert(_static_offset_offset != 0, ""); ++ return dmh->long_field(_static_offset_offset); ++} ++ ++void java_lang_invoke_DirectMethodHandle_StaticAccessor::set_static_offset(oop dmh, long static_offset) { ++ assert(_static_offset_offset != 0, ""); ++ dmh->long_field_put(_static_offset_offset, static_offset); ++} ++ ++ ++void java_lang_invoke_DirectMethodHandle_StaticAccessor::compute_offsets() { ++ klassOop klass_oop = SystemDictionary::DirectMethodHandle_StaticAccessor_klass(); ++ if (klass_oop != NULL && EnableInvokeDynamic) { ++ compute_offset(_static_offset_offset, klass_oop, vmSymbols::static_offset_name(), vmSymbols::long_signature()); ++ } ++} ++ ++// Support for java_lang_invoke_DirectMethodHandle$Accessor ++ ++int java_lang_invoke_DirectMethodHandle_Accessor::_field_offset_offset; ++ ++int java_lang_invoke_DirectMethodHandle_Accessor::field_offset(oop dmh) { ++ assert(_field_offset_offset != 0, ""); ++ return dmh->int_field(_field_offset_offset); ++} ++ ++void java_lang_invoke_DirectMethodHandle_Accessor::set_field_offset(oop dmh, int field_offset) { ++ assert(_field_offset_offset != 0, ""); ++ dmh->int_field_put(_field_offset_offset, field_offset); ++} ++ ++ ++void java_lang_invoke_DirectMethodHandle_Accessor::compute_offsets() { ++ klassOop klass_oop = SystemDictionary::DirectMethodHandle_Accessor_klass(); ++ if (klass_oop != NULL && EnableInvokeDynamic) { ++ compute_offset(_field_offset_offset, klass_oop, vmSymbols::field_offset_name(), vmSymbols::int_signature()); ++ } ++} ++ ++ ++ + // Support for java_lang_invoke_MethodHandle + + int java_lang_invoke_MethodHandle::_type_offset; +@@ -3028,6 +3074,8 @@ + if (EnableInvokeDynamic) { + java_lang_invoke_MethodHandle::compute_offsets(); + java_lang_invoke_DirectMethodHandle::compute_offsets(); ++ java_lang_invoke_DirectMethodHandle_StaticAccessor::compute_offsets(); ++ java_lang_invoke_DirectMethodHandle_Accessor::compute_offsets(); + java_lang_invoke_MemberName::compute_offsets(); + java_lang_invoke_LambdaForm::compute_offsets(); + java_lang_invoke_MethodType::compute_offsets(); +diff -r 2ffb90b422e2 src/share/vm/classfile/javaClasses.hpp +--- a/src/share/vm/classfile/javaClasses.hpp Fri Jul 10 10:19:24 2015 -0700 ++++ b/src/share/vm/classfile/javaClasses.hpp Fri Jul 10 10:19:37 2015 -0700 +@@ -940,6 +940,54 @@ + static int member_offset_in_bytes() { return _member_offset; } + }; + ++// Interface to java.lang.invoke.DirectMethodHandle$StaticAccessor objects ++ ++class java_lang_invoke_DirectMethodHandle_StaticAccessor: AllStatic { ++ friend class JavaClasses; ++ ++ private: ++ static int _static_offset_offset; // offset to static field ++ ++ static void compute_offsets(); ++ ++ public: ++ // Accessors ++ static long static_offset(oop dmh); ++ static void set_static_offset(oop dmh, long value); ++ ++ // Testers ++ static bool is_subclass(klassOop klass) { ++ return Klass::cast(klass)->is_subclass_of(SystemDictionary::DirectMethodHandle_StaticAccessor_klass()); ++ } ++ static bool is_instance(oop obj) { ++ return obj != NULL && is_subclass(obj->klass()); ++ } ++}; ++ ++// Interface to java.lang.invoke.DirectMethodHandle$Accessor objects ++ ++class java_lang_invoke_DirectMethodHandle_Accessor: AllStatic { ++ friend class JavaClasses; ++ ++ private: ++ static int _field_offset_offset; // offset to field ++ ++ static void compute_offsets(); ++ ++ public: ++ // Accessors ++ static int field_offset(oop dmh); ++ static void set_field_offset(oop dmh, int value); ++ ++ // Testers ++ static bool is_subclass(klassOop klass) { ++ return Klass::cast(klass)->is_subclass_of(SystemDictionary::DirectMethodHandle_Accessor_klass()); ++ } ++ static bool is_instance(oop obj) { ++ return obj != NULL && is_subclass(obj->klass()); ++ } ++}; ++ + // Interface to java.lang.invoke.LambdaForm objects + // (These are a private interface for managing adapter code generation.) + +diff -r 2ffb90b422e2 src/share/vm/classfile/systemDictionary.hpp +--- a/src/share/vm/classfile/systemDictionary.hpp Fri Jul 10 10:19:24 2015 -0700 ++++ b/src/share/vm/classfile/systemDictionary.hpp Fri Jul 10 10:19:37 2015 -0700 +@@ -148,6 +148,8 @@ + do_klass(reflect_UnsafeStaticFieldAccessorImpl_klass, sun_reflect_UnsafeStaticFieldAccessorImpl, Opt_Only_JDK15 ) \ + \ + /* support for dynamic typing; it's OK if these are NULL in earlier JDKs */ \ ++ do_klass(DirectMethodHandle_StaticAccessor_klass, java_lang_invoke_DirectMethodHandle_StaticAccessor, Opt ) \ ++ do_klass(DirectMethodHandle_Accessor_klass, java_lang_invoke_DirectMethodHandle_Accessor, Opt ) \ + do_klass(MethodHandle_klass, java_lang_invoke_MethodHandle, Pre_JSR292 ) \ + do_klass(MemberName_klass, java_lang_invoke_MemberName, Pre_JSR292 ) \ + do_klass(MethodHandleNatives_klass, java_lang_invoke_MethodHandleNatives, Pre_JSR292 ) \ +diff -r 2ffb90b422e2 src/share/vm/classfile/vmSymbols.hpp +--- a/src/share/vm/classfile/vmSymbols.hpp Fri Jul 10 10:19:24 2015 -0700 ++++ b/src/share/vm/classfile/vmSymbols.hpp Fri Jul 10 10:19:37 2015 -0700 +@@ -248,6 +248,8 @@ + /* Support for JSR 292 & invokedynamic (JDK 1.7 and above) */ \ + template(java_lang_invoke_CallSite, "java/lang/invoke/CallSite") \ + template(java_lang_invoke_ConstantCallSite, "java/lang/invoke/ConstantCallSite") \ ++ template(java_lang_invoke_DirectMethodHandle_StaticAccessor, "java/lang/invoke/DirectMethodHandle$StaticAccessor") \ ++ template(java_lang_invoke_DirectMethodHandle_Accessor, "java/lang/invoke/DirectMethodHandle$Accessor") \ + template(java_lang_invoke_DirectMethodHandle, "java/lang/invoke/DirectMethodHandle") \ + template(java_lang_invoke_MutableCallSite, "java/lang/invoke/MutableCallSite") \ + template(java_lang_invoke_VolatileCallSite, "java/lang/invoke/VolatileCallSite") \ +@@ -485,6 +487,10 @@ + template(int_StringBuffer_signature, "(I)Ljava/lang/StringBuffer;") \ + template(char_StringBuffer_signature, "(C)Ljava/lang/StringBuffer;") \ + template(int_String_signature, "(I)Ljava/lang/String;") \ ++ template(static_offset_name, "staticOffset") \ ++ template(static_base_name, "staticBase") \ ++ template(field_offset_name, "fieldOffset") \ ++ template(field_type_name, "fieldType") \ + /* signature symbols needed by intrinsics */ \ + VM_INTRINSICS_DO(VM_INTRINSIC_IGNORE, VM_SYMBOL_IGNORE, VM_SYMBOL_IGNORE, template, VM_ALIAS_IGNORE) \ + \ +diff -r 2ffb90b422e2 src/share/vm/prims/jvmtiRedefineClasses.cpp +--- a/src/share/vm/prims/jvmtiRedefineClasses.cpp Fri Jul 10 10:19:24 2015 -0700 ++++ b/src/share/vm/prims/jvmtiRedefineClasses.cpp Fri Jul 10 10:19:37 2015 -0700 +@@ -2110,6 +2110,120 @@ + + } + ++// import java_lang_invoke_MemberName.* ++enum { ++ REFERENCE_KIND_SHIFT = java_lang_invoke_MemberName::MN_REFERENCE_KIND_SHIFT, ++ REFERENCE_KIND_MASK = java_lang_invoke_MemberName::MN_REFERENCE_KIND_MASK, ++}; ++ ++static oop field_name_or_null(Symbol* s) { ++ if (s == NULL) return NULL; ++ return StringTable::lookup(s); ++} ++ ++static oop object_java_mirror() { ++ return Klass::cast(SystemDictionary::Object_klass())->java_mirror(); ++} ++ ++static oop field_signature_type_or_null(Symbol* s) { ++ if (s == NULL) return NULL; ++ BasicType bt = FieldType::basic_type(s); ++ if (is_java_primitive(bt)) { ++ assert(s->utf8_length() == 1, ""); ++ return java_lang_Class::primitive_mirror(bt); ++ } ++ // Here are some more short cuts for common types. ++ // They are optional, since reference types can be resolved lazily. ++ if (bt == T_OBJECT) { ++ if (s == vmSymbols::object_signature()) { ++ return object_java_mirror(); ++ } else if (s == vmSymbols::class_signature()) { ++ return Klass::cast(SystemDictionary::Class_klass())->java_mirror(); ++ } else if (s == vmSymbols::string_signature()) { ++ return Klass::cast(SystemDictionary::String_klass())->java_mirror(); ++ } ++ } ++ return NULL; ++} ++ ++bool update_member_name(oop obj) { ++ int flags = java_lang_invoke_MemberName::flags(obj); ++ int ref_kind = (flags >> REFERENCE_KIND_SHIFT) & REFERENCE_KIND_MASK; ++ if (MethodHandles::ref_kind_is_method(ref_kind)) { ++ methodOop m = (methodOop) java_lang_invoke_MemberName::vmtarget(obj); ++ if (m != NULL && !instanceKlass::cast(m->method_holder())->is_newest_version()) { ++ // Let's try to re-resolve method ++ KlassHandle newest = instanceKlass::cast(m->method_holder())->newest_version(); ++ methodOop new_method = instanceKlass::cast(newest())->find_method(m->name(), m->signature()); ++ ++ // Note: we might set NULL at this point, which should force AbstractMethodError at runtime ++ bool do_dispatch = (ref_kind != JVM_REF_invokeSpecial); ++ MethodHandles::init_method_MemberName(obj, new_method, do_dispatch, newest); ++ } ++ } else if (MethodHandles::ref_kind_is_field(ref_kind)) { ++ klassOop k = (klassOop) java_lang_invoke_MemberName::vmtarget(obj); ++ if (k == NULL) { ++ return false; // Was cleared before, this MemberName is invalid. ++ } ++ ++ if (k != NULL && !Klass::cast(k)->is_newest_version()) { ++ // Let's try to re-resolve field ++ fieldDescriptor fd; ++ int offset = java_lang_invoke_MemberName::vmindex(obj); ++ bool is_static = MethodHandles::ref_kind_is_static(ref_kind); ++ instanceKlass *ik = instanceKlass::cast(k); ++ if (ik->find_local_field_from_offset(offset, is_static, &fd)) { ++ KlassHandle newest = Klass::cast(k)->newest_version(); ++ fieldDescriptor fd_new; ++ if (instanceKlass::cast(newest())->find_local_field(fd.name(), fd.signature(), &fd_new)) { ++ bool is_setter = MethodHandles::ref_kind_is_setter(ref_kind); ++ oop type = field_signature_type_or_null(fd_new.signature()); ++ oop name = field_name_or_null(fd_new.name()); ++ MethodHandles::init_field_MemberName(obj, newest, fd_new.access_flags(), type, name, fd_new.offset(), is_setter); ++ } else { ++ // Matching field is not found in new version, not much we can do here. ++ // JVM will crash once faulty MH is invoked. ++ // However, to avoid that all DMH's using this faulty MH are cleared (set to NULL) ++ // Eventually, we probably want to replace them with something more meaningful, ++ // like instance throwing NoSuchFieldError or DMH that will resort to dynamic ++ // field resolution (with possibility of type conversion) ++ java_lang_invoke_MemberName::set_vmtarget(obj, NULL); ++ java_lang_invoke_MemberName::set_vmindex(obj, 0); ++ return false; ++ } ++ } ++ } ++ } ++ return true; ++} ++ ++bool update_direct_method_handle(oop obj) { ++ // Always update member name first. ++ oop mem_name = java_lang_invoke_DirectMethodHandle::member(obj); ++ if (!update_member_name(mem_name)) { ++ return false; ++ } ++ ++ // Here we rely on DirectMethodHandle implementation. ++ // The current implementation caches field offset in $StaticAccessor/$Accessor ++ int flags = java_lang_invoke_MemberName::flags(mem_name); ++ int ref_kind = (flags >> REFERENCE_KIND_SHIFT) & REFERENCE_KIND_MASK; ++ if (MethodHandles::ref_kind_is_field(ref_kind)) { ++ // Note: we don't care about staticBase field (which is java.lang.Class) ++ // It should be processed during normal object update. ++ // Update offset in StaticAccessor ++ int offset = java_lang_invoke_MemberName::vmindex(mem_name); ++ if (offset != 0) { // index of 0 means that field no longer exist ++ if (java_lang_invoke_DirectMethodHandle_StaticAccessor::is_instance(obj)) { ++ java_lang_invoke_DirectMethodHandle_StaticAccessor::set_static_offset(obj, offset); ++ } else if (java_lang_invoke_DirectMethodHandle_Accessor::is_instance(obj)) { ++ java_lang_invoke_DirectMethodHandle_Accessor::set_field_offset(obj, offset); ++ } ++ } ++ } ++ return true; ++} ++ + template void VM_RedefineClasses::do_oop_work(T* p) { + T heap_oop = oopDesc::load_heap_oop(p); + if (!oopDesc::is_null(heap_oop)) { +@@ -2148,6 +2262,18 @@ + } + } + } ++ } else { ++ // JSR 292 support, uptade java.lang.invoke.MemberName instances ++ if (java_lang_invoke_MemberName::is_instance(obj)) { ++ update_member_name(obj); ++ } else if (java_lang_invoke_DirectMethodHandle::is_instance(obj)) { ++ if (!update_direct_method_handle(obj)) { ++ // DMH is no longer valid, replace it with null reference. ++ // See note above. We probably want to replace this with something more meaningful. ++ oopDesc::encode_store_heap_oop_not_null(p, NULL); ++ //S::oop_store(p, NULL); ++ } ++ } + } + } + } +@@ -2822,6 +2948,11 @@ + // TODO: + transfer_old_native_function_registrations(the_old_class); + ++ // Swap method handles ++ MemberNameTable* mnt = the_old_class->member_names(); ++ assert(the_new_class->member_names() == NULL, ""); ++ the_new_class->set_member_names(mnt); ++ the_old_class->set_member_names(NULL); + + #ifdef ASSERT + +diff -r 2ffb90b422e2 src/share/vm/runtime/mutexLocker.cpp +--- a/src/share/vm/runtime/mutexLocker.cpp Fri Jul 10 10:19:24 2015 -0700 ++++ b/src/share/vm/runtime/mutexLocker.cpp Fri Jul 10 10:19:37 2015 -0700 +@@ -267,7 +267,7 @@ + def(Heap_lock , Monitor, nonleaf+1, false); + def(JfieldIdCreation_lock , Mutex , nonleaf+1, true ); // jfieldID, Used in VM_Operation + def(JNICachedItableIndex_lock , Mutex , nonleaf+1, false); // Used to cache an itable index during JNI invoke +- def(MemberNameTable_lock , Mutex , nonleaf+1, false); // Used to protect MemberNameTable ++ def(MemberNameTable_lock , Mutex , nonleaf+1, true); // Used to protect MemberNameTable + + def(CompiledIC_lock , Mutex , nonleaf+2, false); // locks VtableStubs_lock, InlineCacheBuffer_lock + def(CompileTaskAlloc_lock , Mutex , nonleaf+2, true ); diff --git a/hotspot/.hg/patches/full-jdk7u79-b02.patch b/hotspot/.hg/patches/full-jdk7u79-b15.patch similarity index 95% rename from hotspot/.hg/patches/full-jdk7u79-b02.patch rename to hotspot/.hg/patches/full-jdk7u79-b15.patch index 8b82eea3..19ff13ed 100644 --- a/hotspot/.hg/patches/full-jdk7u79-b02.patch +++ b/hotspot/.hg/patches/full-jdk7u79-b15.patch @@ -1,7 +1,9 @@ -diff --git a/make/bsd/makefiles/gcc.make b/make/bsd/makefiles/gcc.make -index 3de1dea..8bf7f94 100644 ---- a/make/bsd/makefiles/gcc.make -+++ b/make/bsd/makefiles/gcc.make +# HG changeset patch +# Parent 882f6c762ac5352a0d94efcea46c8f9a917ceb08 + +diff -r 882f6c762ac5 make/bsd/makefiles/gcc.make +--- a/make/bsd/makefiles/gcc.make Thu Jul 09 23:10:04 2015 -0700 ++++ b/make/bsd/makefiles/gcc.make Thu Jul 09 23:18:17 2015 -0700 @@ -117,7 +117,10 @@ CFLAGS += -fno-rtti CFLAGS += -fno-exceptions @@ -14,10 +16,9 @@ index 3de1dea..8bf7f94 100644 # version 4 and above support fvisibility=hidden (matches jni_x86.h file) # except 4.1.2 gives pointless warnings that can't be disabled (afaik) ifneq "$(shell expr \( $(CC_VER_MAJOR) \> 4 \) \| \( \( $(CC_VER_MAJOR) = 4 \) \& \( $(CC_VER_MINOR) \>= 3 \) \))" "0" -diff --git a/src/cpu/x86/vm/templateTable_x86_32.cpp b/src/cpu/x86/vm/templateTable_x86_32.cpp -index fc19edc..d2cddd3 100644 ---- a/src/cpu/x86/vm/templateTable_x86_32.cpp -+++ b/src/cpu/x86/vm/templateTable_x86_32.cpp +diff -r 882f6c762ac5 src/cpu/x86/vm/templateTable_x86_32.cpp +--- a/src/cpu/x86/vm/templateTable_x86_32.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/cpu/x86/vm/templateTable_x86_32.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -2109,6 +2109,22 @@ // resolve first time through address entry; @@ -173,10 +174,9 @@ index fc19edc..d2cddd3 100644 // Get receiver klass into rdx - also a null check __ restore_locals(); // restore rdi __ null_check(rcx, oopDesc::klass_offset_in_bytes()); -diff --git a/src/cpu/x86/vm/templateTable_x86_64.cpp b/src/cpu/x86/vm/templateTable_x86_64.cpp -index 932ee97..67bb710 100644 ---- a/src/cpu/x86/vm/templateTable_x86_64.cpp -+++ b/src/cpu/x86/vm/templateTable_x86_64.cpp +diff -r 882f6c762ac5 src/cpu/x86/vm/templateTable_x86_64.cpp +--- a/src/cpu/x86/vm/templateTable_x86_64.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/cpu/x86/vm/templateTable_x86_64.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -2151,6 +2151,22 @@ // resolve first time through address entry; @@ -337,10 +337,9 @@ index 932ee97..67bb710 100644 // Get receiver klass into rdx - also a null check __ restore_locals(); // restore r14 __ null_check(rcx, oopDesc::klass_offset_in_bytes()); -diff --git a/src/share/vm/c1/c1_Compilation.hpp b/src/share/vm/c1/c1_Compilation.hpp -index 9a8ca61..196ab25 100644 ---- a/src/share/vm/c1/c1_Compilation.hpp -+++ b/src/share/vm/c1/c1_Compilation.hpp +diff -r 882f6c762ac5 src/share/vm/c1/c1_Compilation.hpp +--- a/src/share/vm/c1/c1_Compilation.hpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/c1/c1_Compilation.hpp Thu Jul 09 23:18:17 2015 -0700 @@ -242,8 +242,8 @@ #define BAILOUT(msg) { bailout(msg); return; } #define BAILOUT_(msg, res) { bailout(msg); return res; } @@ -352,10 +351,9 @@ index 9a8ca61..196ab25 100644 class InstructionMark: public StackObj { -diff --git a/src/share/vm/ci/ciEnv.cpp b/src/share/vm/ci/ciEnv.cpp -index e20db5d..57f37db 100644 ---- a/src/share/vm/ci/ciEnv.cpp -+++ b/src/share/vm/ci/ciEnv.cpp +diff -r 882f6c762ac5 src/share/vm/ci/ciEnv.cpp +--- a/src/share/vm/ci/ciEnv.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/ci/ciEnv.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -1172,3 +1172,11 @@ // If memory is low, we stop compiling methods. record_method_not_compilable("out of memory"); @@ -368,10 +366,9 @@ index e20db5d..57f37db 100644 + _factory->cleanup_after_redefinition(); + } +} -diff --git a/src/share/vm/ci/ciEnv.hpp b/src/share/vm/ci/ciEnv.hpp -index 103e532..abe2e37 100644 ---- a/src/share/vm/ci/ciEnv.hpp -+++ b/src/share/vm/ci/ciEnv.hpp +diff -r 882f6c762ac5 src/share/vm/ci/ciEnv.hpp +--- a/src/share/vm/ci/ciEnv.hpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/ci/ciEnv.hpp Thu Jul 09 23:18:17 2015 -0700 @@ -417,6 +417,8 @@ void record_failure(const char* reason); void record_method_not_compilable(const char* reason, bool all_tiers = true); @@ -381,10 +378,9 @@ index 103e532..abe2e37 100644 }; #endif // SHARE_VM_CI_CIENV_HPP -diff --git a/src/share/vm/ci/ciObjectFactory.cpp b/src/share/vm/ci/ciObjectFactory.cpp -index e0ab96b..36efef4 100644 ---- a/src/share/vm/ci/ciObjectFactory.cpp -+++ b/src/share/vm/ci/ciObjectFactory.cpp +diff -r 882f6c762ac5 src/share/vm/ci/ciObjectFactory.cpp +--- a/src/share/vm/ci/ciObjectFactory.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/ci/ciObjectFactory.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -296,6 +296,11 @@ // into the table. We need to recompute our index. index = find(keyHandle(), _ci_objects); @@ -448,10 +444,9 @@ index e0ab96b..36efef4 100644 +void ciObjectFactory::cleanup_after_redefinition() { + sort_ci_objects(_ci_objects); +} -diff --git a/src/share/vm/ci/ciObjectFactory.hpp b/src/share/vm/ci/ciObjectFactory.hpp -index 26cc2c3..855a4ac 100644 ---- a/src/share/vm/ci/ciObjectFactory.hpp -+++ b/src/share/vm/ci/ciObjectFactory.hpp +diff -r 882f6c762ac5 src/share/vm/ci/ciObjectFactory.hpp +--- a/src/share/vm/ci/ciObjectFactory.hpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/ci/ciObjectFactory.hpp Thu Jul 09 23:18:17 2015 -0700 @@ -38,6 +38,7 @@ class ciObjectFactory : public ResourceObj { friend class VMStructs; @@ -472,10 +467,9 @@ index 26cc2c3..855a4ac 100644 }; #endif // SHARE_VM_CI_CIOBJECTFACTORY_HPP -diff --git a/src/share/vm/classfile/classFileParser.cpp b/src/share/vm/classfile/classFileParser.cpp -index 505bad1..2912a66 100644 ---- a/src/share/vm/classfile/classFileParser.cpp -+++ b/src/share/vm/classfile/classFileParser.cpp +diff -r 882f6c762ac5 src/share/vm/classfile/classFileParser.cpp +--- a/src/share/vm/classfile/classFileParser.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/classfile/classFileParser.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -795,6 +795,7 @@ Handle class_loader, Handle protection_domain, @@ -557,15 +551,15 @@ index 505bad1..2912a66 100644 // Fill in code attribute information m->set_max_stack(max_stack); m->set_max_locals(max_locals); -@@ -2228,6 +2267,8 @@ - * sure that the oops can pass verification when this field is set. +@@ -2229,6 +2268,8 @@ */ m->constMethod()->set_stackmap_data(stackmap_data()); -+ -+ m->constMethod()->set_code_section_table(code_section_table()); ++ m->constMethod()->set_code_section_table(code_section_table()); ++ // Copy byte codes m->set_code(code_start); + @@ -2825,6 +2866,15 @@ "Invalid Deprecated classfile attribute length %u in class file %s", attribute_length, CHECK); @@ -803,17 +797,17 @@ index 505bad1..2912a66 100644 // reinitialize modifiers, using the InnerClasses attribute int computed_modifiers = this_klass->compute_modifier_flags(CHECK_(nullHandle)); -@@ -3748,6 +3948,10 @@ - +@@ -3749,6 +3949,10 @@ // Allocate mirror and initialize static fields java_lang_Class::create_mirror(this_klass, CHECK_(nullHandle)); -+ + + if (rt == REF_OTHER) { + instanceRefKlass::update_nonstatic_oop_maps(ik); + } - ++ ClassLoadingService::notify_class_loaded(instanceKlass::cast(this_klass()), false /* not shared class */); + @@ -3891,7 +4095,7 @@ } @@ -843,10 +837,9 @@ index 505bad1..2912a66 100644 k->set_is_cloneable(); } } -diff --git a/src/share/vm/classfile/classFileParser.hpp b/src/share/vm/classfile/classFileParser.hpp -index a95a784..ac217fc 100644 ---- a/src/share/vm/classfile/classFileParser.hpp -+++ b/src/share/vm/classfile/classFileParser.hpp +diff -r 882f6c762ac5 src/share/vm/classfile/classFileParser.hpp +--- a/src/share/vm/classfile/classFileParser.hpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/classfile/classFileParser.hpp Thu Jul 09 23:18:17 2015 -0700 @@ -64,6 +64,9 @@ int _sde_length; typeArrayHandle _inner_classes; @@ -923,10 +916,9 @@ index a95a784..ac217fc 100644 // Verifier checks static void check_super_class_access(instanceKlassHandle this_klass, TRAPS); static void check_super_interface_access(instanceKlassHandle this_klass, TRAPS); -diff --git a/src/share/vm/classfile/classLoader.cpp b/src/share/vm/classfile/classLoader.cpp -index a2e61a4..450e19f 100644 ---- a/src/share/vm/classfile/classLoader.cpp -+++ b/src/share/vm/classfile/classLoader.cpp +diff -r 882f6c762ac5 src/share/vm/classfile/classLoader.cpp +--- a/src/share/vm/classfile/classLoader.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/classfile/classLoader.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -915,6 +915,7 @@ instanceKlassHandle result = parser.parseClassFile(h_name, class_loader, @@ -935,10 +927,9 @@ index a2e61a4..450e19f 100644 parsed_name, false, CHECK_(h)); -diff --git a/src/share/vm/classfile/dictionary.cpp b/src/share/vm/classfile/dictionary.cpp -index 78e76cc..ee21f3a 100644 ---- a/src/share/vm/classfile/dictionary.cpp -+++ b/src/share/vm/classfile/dictionary.cpp +diff -r 882f6c762ac5 src/share/vm/classfile/dictionary.cpp +--- a/src/share/vm/classfile/dictionary.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/classfile/dictionary.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -326,6 +326,21 @@ } } @@ -1037,10 +1028,9 @@ index 78e76cc..ee21f3a 100644 } -diff --git a/src/share/vm/classfile/dictionary.hpp b/src/share/vm/classfile/dictionary.hpp -index bd33760..186d0eb 100644 ---- a/src/share/vm/classfile/dictionary.hpp -+++ b/src/share/vm/classfile/dictionary.hpp +diff -r 882f6c762ac5 src/share/vm/classfile/dictionary.hpp +--- a/src/share/vm/classfile/dictionary.hpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/classfile/dictionary.hpp Thu Jul 09 23:18:17 2015 -0700 @@ -73,6 +73,10 @@ void add_klass(Symbol* class_name, Handle class_loader,KlassHandle obj); @@ -1068,10 +1058,9 @@ index bd33760..186d0eb 100644 klassOop find(int index, unsigned int hash, Symbol* name, Handle loader, Handle protection_domain, TRAPS); bool is_valid_protection_domain(int index, unsigned int hash, -diff --git a/src/share/vm/classfile/javaClasses.cpp b/src/share/vm/classfile/javaClasses.cpp -index 7dd5f1b..9c7d8eb 100644 ---- a/src/share/vm/classfile/javaClasses.cpp -+++ b/src/share/vm/classfile/javaClasses.cpp +diff -r 882f6c762ac5 src/share/vm/classfile/javaClasses.cpp +--- a/src/share/vm/classfile/javaClasses.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/classfile/javaClasses.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -1798,7 +1798,7 @@ klassOop klass = SystemDictionary::reflect_Method_klass(); // This class is eagerly initialized during VM initialization, since we keep a refence @@ -1081,10 +1070,9 @@ index 7dd5f1b..9c7d8eb 100644 return instanceKlass::cast(klass)->allocate_instance_handle(CHECK_NH); } -diff --git a/src/share/vm/classfile/javaClasses.hpp b/src/share/vm/classfile/javaClasses.hpp -index 36d1cec..a6de98e 100644 ---- a/src/share/vm/classfile/javaClasses.hpp -+++ b/src/share/vm/classfile/javaClasses.hpp +diff -r 882f6c762ac5 src/share/vm/classfile/javaClasses.hpp +--- a/src/share/vm/classfile/javaClasses.hpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/classfile/javaClasses.hpp Thu Jul 09 23:18:17 2015 -0700 @@ -213,7 +213,6 @@ class java_lang_Class : AllStatic { @@ -1093,10 +1081,9 @@ index 36d1cec..a6de98e 100644 private: // The fake offsets are added by the class loader when java.lang.Class is loaded -diff --git a/src/share/vm/classfile/loaderConstraints.cpp b/src/share/vm/classfile/loaderConstraints.cpp -index 8650cd9..965cce2 100644 ---- a/src/share/vm/classfile/loaderConstraints.cpp -+++ b/src/share/vm/classfile/loaderConstraints.cpp +diff -r 882f6c762ac5 src/share/vm/classfile/loaderConstraints.cpp +--- a/src/share/vm/classfile/loaderConstraints.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/classfile/loaderConstraints.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -449,7 +449,7 @@ if (k != NULL) { // We found the class in the system dictionary, so we should @@ -1106,10 +1093,9 @@ index 8650cd9..965cce2 100644 } else { // If we don't find the class in the system dictionary, it // has to be in the placeholders table. -diff --git a/src/share/vm/classfile/loaderConstraints.hpp b/src/share/vm/classfile/loaderConstraints.hpp -index d01b2c4..1ad80f7 100644 ---- a/src/share/vm/classfile/loaderConstraints.hpp -+++ b/src/share/vm/classfile/loaderConstraints.hpp +diff -r 882f6c762ac5 src/share/vm/classfile/loaderConstraints.hpp +--- a/src/share/vm/classfile/loaderConstraints.hpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/classfile/loaderConstraints.hpp Thu Jul 09 23:18:17 2015 -0700 @@ -106,7 +106,7 @@ klassOop klass() { return literal(); } @@ -1119,10 +1105,9 @@ index d01b2c4..1ad80f7 100644 LoaderConstraintEntry* next() { return (LoaderConstraintEntry*)HashtableEntry::next(); -diff --git a/src/share/vm/classfile/systemDictionary.cpp b/src/share/vm/classfile/systemDictionary.cpp -index 2fa221b..8e45161 100644 ---- a/src/share/vm/classfile/systemDictionary.cpp -+++ b/src/share/vm/classfile/systemDictionary.cpp +diff -r 882f6c762ac5 src/share/vm/classfile/systemDictionary.cpp +--- a/src/share/vm/classfile/systemDictionary.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/classfile/systemDictionary.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -157,6 +157,7 @@ // can return a null klass klass = handle_resolution_exception(class_name, class_loader, protection_domain, throw_error, k_h, THREAD); @@ -1179,7 +1164,7 @@ index 2fa221b..8e45161 100644 const char* pkg = "java/"; if (!HAS_PENDING_EXCEPTION && -@@ -1087,11 +1102,16 @@ +@@ -1087,13 +1102,18 @@ // Add class just loaded // If a class loader supports parallel classloading handle parallel define requests // find_or_define_instance_class may return a different instanceKlass @@ -1191,13 +1176,15 @@ index 2fa221b..8e45161 100644 - define_instance_class(k, THREAD); + define_instance_class(k, old_class, THREAD); } -+ } -+ -+ if (redefine_classes_locked) { -+ Thread::current()->redefine_classes_mutex()->unlock(); } ++ if (redefine_classes_locked) { ++ Thread::current()->redefine_classes_mutex()->unlock(); ++ } ++ // If parsing the class file or define_instance_class failed, we + // need to remove the placeholder added on our behalf. But we + // must make sure parsed_name is valid first (it won't be if we had @@ -1122,7 +1142,7 @@ MutexLocker mu(SystemDictionary_lock, THREAD); @@ -1281,12 +1268,24 @@ index 2fa221b..8e45161 100644 // ---------------------------------------------------------------------------- // GC support -@@ -1702,6 +1745,24 @@ +@@ -1702,7 +1745,8 @@ } +-void SystemDictionary::preloaded_oops_do(OopClosure* f) { +// (tw) Iterate over all pre-loaded classes in the dictionary. +void SystemDictionary::preloaded_classes_do(OopClosure *f) { + for (int k = (int)FIRST_WKID; k < (int)WKID_LIMIT; k++) { + f->do_oop((oop*) &_well_known_klasses[k]); + } +@@ -1716,6 +1760,23 @@ + } + } + ++ // TODO: Check if we need to call FilterFieldsMap ++} ++ ++void SystemDictionary::preloaded_oops_do(OopClosure* f) { + for (int k = (int)FIRST_WKID; k < (int)WKID_LIMIT; k++) { + f->do_oop((oop*) &_well_known_klasses[k]); + } @@ -1300,24 +1299,21 @@ index 2fa221b..8e45161 100644 + } + } + -+ // TODO: Check if we need to call FilterFieldsMap -+} -+ - void SystemDictionary::preloaded_oops_do(OopClosure* f) { - for (int k = (int)FIRST_WKID; k < (int)WKID_LIMIT; k++) { - f->do_oop((oop*) &_well_known_klasses[k]); -@@ -1732,6 +1793,11 @@ - // Don't iterate over placeholders - void SystemDictionary::classes_do(void f(klassOop)) { + // The basic type mirrors would have already been processed in + // Universe::oops_do(), via a call to shared_oops_do(), so should + // not be processed again. +@@ -1734,6 +1795,11 @@ dictionary()->classes_do(f); -+} -+ + } + +// (tw) Iterate over all classes in the dictionary. +void SystemDictionary::classes_do(ObjectClosure *closure) { + dictionary()->classes_do(closure); - } - ++} ++ // Added for initialize_itable_for_klass + // Just the classes from defining class loaders + // Don't iterate over placeholders @@ -1870,7 +1936,9 @@ // Preload ref klasses and set reference types @@ -1342,10 +1338,9 @@ index 2fa221b..8e45161 100644 linkage_error = "loader (instance of %s): attempted duplicate class " "definition for name: \"%s\""; } else { -diff --git a/src/share/vm/classfile/systemDictionary.hpp b/src/share/vm/classfile/systemDictionary.hpp -index 85474c5..2dabd95 100644 ---- a/src/share/vm/classfile/systemDictionary.hpp -+++ b/src/share/vm/classfile/systemDictionary.hpp +diff -r 882f6c762ac5 src/share/vm/classfile/systemDictionary.hpp +--- a/src/share/vm/classfile/systemDictionary.hpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/classfile/systemDictionary.hpp Thu Jul 09 23:18:17 2015 -0700 @@ -270,7 +270,7 @@ // Resolve from stream (called by jni_DefineClass and JVM_DefineClass) static klassOop resolve_from_stream(Symbol* class_name, Handle class_loader, @@ -1364,15 +1359,15 @@ index 85474c5..2dabd95 100644 // Added for initialize_itable_for_klass to handle exceptions static void classes_do(void f(klassOop, TRAPS), TRAPS); // All classes, and their class loaders -@@ -416,6 +418,8 @@ - int limit = (int)end_id + 1; +@@ -417,6 +419,8 @@ initialize_wk_klasses_until((WKID) limit, start_id, THREAD); } -+ -+ static void rollback_redefinition(); ++ static void rollback_redefinition(); ++ public: #define WK_KLASS_DECLARE(name, symbol, option) \ + static klassOop name() { return check_klass_##option(_well_known_klasses[WK_KLASS_ENUM_NAME(name)]); } @@ -598,11 +602,11 @@ // after waiting, but before reentering SystemDictionary_lock // to preserve lock order semantics. @@ -1405,10 +1400,9 @@ index 85474c5..2dabd95 100644 // We pass in the hashtable index so we can calculate it outside of // the SystemDictionary_lock. -diff --git a/src/share/vm/classfile/verifier.cpp b/src/share/vm/classfile/verifier.cpp -index 49c4b2c..74d7614 100644 ---- a/src/share/vm/classfile/verifier.cpp -+++ b/src/share/vm/classfile/verifier.cpp +diff -r 882f6c762ac5 src/share/vm/classfile/verifier.cpp +--- a/src/share/vm/classfile/verifier.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/classfile/verifier.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -106,7 +106,7 @@ return !need_verify; } @@ -1493,10 +1487,9 @@ index 49c4b2c..74d7614 100644 Symbol* ref_class_name = cp->klass_name_at(cp->klass_ref_index_at(index)); // See the comments in verify_field_instructions() for -diff --git a/src/share/vm/classfile/verifier.hpp b/src/share/vm/classfile/verifier.hpp -index 92c4fd2..5e8e0da 100644 ---- a/src/share/vm/classfile/verifier.hpp -+++ b/src/share/vm/classfile/verifier.hpp +diff -r 882f6c762ac5 src/share/vm/classfile/verifier.hpp +--- a/src/share/vm/classfile/verifier.hpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/classfile/verifier.hpp Thu Jul 09 23:18:17 2015 -0700 @@ -48,7 +48,7 @@ * Otherwise, no exception is thrown and the return indicates the * error. @@ -1525,10 +1518,9 @@ index 92c4fd2..5e8e0da 100644 instanceKlassHandle _klass; // the class being verified methodHandle _method; // current method being verified VerificationType _this_type; // the verification type of the current class -diff --git a/src/share/vm/classfile/vmSymbols.hpp b/src/share/vm/classfile/vmSymbols.hpp -index 51ceb15..7ef1e1f 100644 ---- a/src/share/vm/classfile/vmSymbols.hpp -+++ b/src/share/vm/classfile/vmSymbols.hpp +diff -r 882f6c762ac5 src/share/vm/classfile/vmSymbols.hpp +--- a/src/share/vm/classfile/vmSymbols.hpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/classfile/vmSymbols.hpp Thu Jul 09 23:18:17 2015 -0700 @@ -139,6 +139,10 @@ template(tag_annotation_default, "AnnotationDefault") \ template(tag_enclosing_method, "EnclosingMethod") \ @@ -1551,10 +1543,9 @@ index 51ceb15..7ef1e1f 100644 /* non-intrinsic name/signature pairs: */ \ template(register_method_name, "register") \ do_alias(register_method_signature, object_void_signature) \ -diff --git a/src/share/vm/compiler/compileBroker.cpp b/src/share/vm/compiler/compileBroker.cpp -index 3e8a65a..fe96a94 100644 ---- a/src/share/vm/compiler/compileBroker.cpp -+++ b/src/share/vm/compiler/compileBroker.cpp +diff -r 882f6c762ac5 src/share/vm/compiler/compileBroker.cpp +--- a/src/share/vm/compiler/compileBroker.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/compiler/compileBroker.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -1181,6 +1181,14 @@ int comp_level, methodHandle hot_method, int hot_count, @@ -1611,10 +1602,9 @@ index 3e8a65a..fe96a94 100644 + } + } +} -diff --git a/src/share/vm/compiler/compileBroker.hpp b/src/share/vm/compiler/compileBroker.hpp -index 29f2b22..37989d1 100644 ---- a/src/share/vm/compiler/compileBroker.hpp -+++ b/src/share/vm/compiler/compileBroker.hpp +diff -r 882f6c762ac5 src/share/vm/compiler/compileBroker.hpp +--- a/src/share/vm/compiler/compileBroker.hpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/compiler/compileBroker.hpp Thu Jul 09 23:18:17 2015 -0700 @@ -408,6 +408,7 @@ static void print_compiler_threads_on(outputStream* st); @@ -1623,10 +1613,9 @@ index 29f2b22..37989d1 100644 static int get_total_compile_count() { return _total_compile_count; } static int get_total_bailout_count() { return _total_bailout_count; } static int get_total_invalidated_count() { return _total_invalidated_count; } -diff --git a/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp b/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp -index a3abd66..b45f9e1 100644 ---- a/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp -+++ b/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp +diff -r 882f6c762ac5 src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp +--- a/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -162,6 +162,13 @@ } } @@ -1641,10 +1630,9 @@ index a3abd66..b45f9e1 100644 // Like CompactibleSpace forward() but always calls cross_threshold() to // update the block offset table. Removed initialize_threshold call because // CFLS does not use a block offset array for contiguous spaces. -diff --git a/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.hpp b/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.hpp -index 24509b6..6670b73 100644 ---- a/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.hpp -+++ b/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.hpp +diff -r 882f6c762ac5 src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.hpp +--- a/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.hpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.hpp Thu Jul 09 23:18:17 2015 -0700 @@ -149,6 +149,7 @@ // Support for compacting cms @@ -1653,10 +1641,9 @@ index 24509b6..6670b73 100644 HeapWord* forward(oop q, size_t size, CompactPoint* cp, HeapWord* compact_top); // Initialization helpers. -diff --git a/src/share/vm/gc_implementation/shared/markSweep.cpp b/src/share/vm/gc_implementation/shared/markSweep.cpp -index 29841d8..a13a35d 100644 ---- a/src/share/vm/gc_implementation/shared/markSweep.cpp -+++ b/src/share/vm/gc_implementation/shared/markSweep.cpp +diff -r 882f6c762ac5 src/share/vm/gc_implementation/shared/markSweep.cpp +--- a/src/share/vm/gc_implementation/shared/markSweep.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/gc_implementation/shared/markSweep.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -32,6 +32,8 @@ #include "oops/objArrayKlass.inline.hpp" #include "oops/oop.inline.hpp" @@ -1753,22 +1740,22 @@ index 29841d8..a13a35d 100644 + FREE_RESOURCE_ARRAY(HeapWord, tmp_obj, size); + } +} -diff --git a/src/share/vm/gc_implementation/shared/markSweep.hpp b/src/share/vm/gc_implementation/shared/markSweep.hpp -index eb8252c..b96a677 100644 ---- a/src/share/vm/gc_implementation/shared/markSweep.hpp -+++ b/src/share/vm/gc_implementation/shared/markSweep.hpp -@@ -117,7 +117,11 @@ +diff -r 882f6c762ac5 src/share/vm/gc_implementation/shared/markSweep.hpp +--- a/src/share/vm/gc_implementation/shared/markSweep.hpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/gc_implementation/shared/markSweep.hpp Thu Jul 09 23:18:17 2015 -0700 +@@ -117,8 +117,12 @@ friend class AdjustPointerClosure; friend class KeepAliveClosure; friend class VM_MarkSweep; + friend class GenMarkSweep; friend void marksweep_init(); -+ + +public: + static GrowableArray* _rescued_oops; - ++ // // Vars + // @@ -208,6 +212,8 @@ template static inline void mark_and_push(T* p); static inline void push_objarray(oop obj, size_t index); @@ -1778,10 +1765,9 @@ index eb8252c..b96a677 100644 static void follow_stack(); // Empty marking stack. static void preserve_mark(oop p, markOop mark); -diff --git a/src/share/vm/interpreter/interpreterRuntime.cpp b/src/share/vm/interpreter/interpreterRuntime.cpp -index 1a1af1b..94972f0 100644 ---- a/src/share/vm/interpreter/interpreterRuntime.cpp -+++ b/src/share/vm/interpreter/interpreterRuntime.cpp +diff -r 882f6c762ac5 src/share/vm/interpreter/interpreterRuntime.cpp +--- a/src/share/vm/interpreter/interpreterRuntime.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/interpreter/interpreterRuntime.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -402,7 +402,7 @@ assert(h_exception.not_null(), "NULL exceptions should be handled by athrow"); assert(h_exception->is_oop(), "just checking"); @@ -1885,10 +1871,9 @@ index 1a1af1b..94972f0 100644 // It is very unlikely that method is redefined more than 100 times // in the middle of resolve. If it is looping here more than 100 times // means then there could be a bug here. -diff --git a/src/share/vm/interpreter/interpreterRuntime.hpp b/src/share/vm/interpreter/interpreterRuntime.hpp -index 6d5f13a..50fa219 100644 ---- a/src/share/vm/interpreter/interpreterRuntime.hpp -+++ b/src/share/vm/interpreter/interpreterRuntime.hpp +diff -r 882f6c762ac5 src/share/vm/interpreter/interpreterRuntime.hpp +--- a/src/share/vm/interpreter/interpreterRuntime.hpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/interpreter/interpreterRuntime.hpp Thu Jul 09 23:18:17 2015 -0700 @@ -141,6 +141,9 @@ static void post_method_entry(JavaThread *thread); static void post_method_exit (JavaThread *thread); @@ -1899,10 +1884,9 @@ index 6d5f13a..50fa219 100644 // Native signature handlers static void prepare_native_call(JavaThread* thread, methodOopDesc* method); -diff --git a/src/share/vm/interpreter/linkResolver.cpp b/src/share/vm/interpreter/linkResolver.cpp -index 1676add..ccd5241 100644 ---- a/src/share/vm/interpreter/linkResolver.cpp -+++ b/src/share/vm/interpreter/linkResolver.cpp +diff -r 882f6c762ac5 src/share/vm/interpreter/linkResolver.cpp +--- a/src/share/vm/interpreter/linkResolver.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/interpreter/linkResolver.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -153,8 +153,8 @@ // Klass resolution @@ -2224,10 +2208,9 @@ index 1676add..ccd5241 100644 // check if method exists if (selected_method.is_null()) { ResourceMark rm(THREAD); -diff --git a/src/share/vm/interpreter/linkResolver.hpp b/src/share/vm/interpreter/linkResolver.hpp -index dfd74f9..cf6e44a 100644 ---- a/src/share/vm/interpreter/linkResolver.hpp -+++ b/src/share/vm/interpreter/linkResolver.hpp +diff -r 882f6c762ac5 src/share/vm/interpreter/linkResolver.hpp +--- a/src/share/vm/interpreter/linkResolver.hpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/interpreter/linkResolver.hpp Thu Jul 09 23:18:17 2015 -0700 @@ -110,7 +110,11 @@ // It does all necessary link-time checks & throws exceptions if necessary. @@ -2250,10 +2233,9 @@ index dfd74f9..cf6e44a 100644 static void runtime_resolve_interface_method (CallInfo& result, methodHandle resolved_method, KlassHandle resolved_klass, Handle recv, KlassHandle recv_klass, bool check_null_and_abstract, TRAPS); static void check_field_accessability (KlassHandle ref_klass, KlassHandle resolved_klass, KlassHandle sel_klass, fieldDescriptor& fd, TRAPS); -diff --git a/src/share/vm/interpreter/templateTable.hpp b/src/share/vm/interpreter/templateTable.hpp -index 17e9f26..e77500f 100644 ---- a/src/share/vm/interpreter/templateTable.hpp -+++ b/src/share/vm/interpreter/templateTable.hpp +diff -r 882f6c762ac5 src/share/vm/interpreter/templateTable.hpp +--- a/src/share/vm/interpreter/templateTable.hpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/interpreter/templateTable.hpp Thu Jul 09 23:18:17 2015 -0700 @@ -329,8 +329,8 @@ static void shouldnotreachhere(); @@ -2265,10 +2247,9 @@ index 17e9f26..e77500f 100644 static void jvmti_post_fast_field_mod(); // debugging of TemplateGenerator -diff --git a/src/share/vm/memory/genMarkSweep.cpp b/src/share/vm/memory/genMarkSweep.cpp -index 76e18d8..6af7c14 100644 ---- a/src/share/vm/memory/genMarkSweep.cpp -+++ b/src/share/vm/memory/genMarkSweep.cpp +diff -r 882f6c762ac5 src/share/vm/memory/genMarkSweep.cpp +--- a/src/share/vm/memory/genMarkSweep.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/memory/genMarkSweep.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -421,6 +421,7 @@ // in the same order in phase2, phase3 and phase4. We don't quite do that // here (perm_gen first rather than last), so we tell the validate code @@ -2292,10 +2273,9 @@ index 76e18d8..6af7c14 100644 + pg->post_compact(); // Shared spaces verification. } -diff --git a/src/share/vm/memory/permGen.cpp b/src/share/vm/memory/permGen.cpp -index 350f583..59faad1 100644 ---- a/src/share/vm/memory/permGen.cpp -+++ b/src/share/vm/memory/permGen.cpp +diff -r 882f6c762ac5 src/share/vm/memory/permGen.cpp +--- a/src/share/vm/memory/permGen.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/memory/permGen.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -57,7 +57,12 @@ for (;;) { @@ -2310,10 +2290,9 @@ index 350f583..59faad1 100644 if ((obj = gen->allocate(size, false)) != NULL) { return obj; } -diff --git a/src/share/vm/memory/space.cpp b/src/share/vm/memory/space.cpp -index f97bc34..9b20d08 100644 ---- a/src/share/vm/memory/space.cpp -+++ b/src/share/vm/memory/space.cpp +diff -r 882f6c762ac5 src/share/vm/memory/space.cpp +--- a/src/share/vm/memory/space.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/memory/space.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -378,6 +378,31 @@ _compaction_top = bottom(); } @@ -2881,10 +2860,9 @@ index f97bc34..9b20d08 100644 } void Space::print_short() const { print_short_on(tty); } -diff --git a/src/share/vm/memory/space.hpp b/src/share/vm/memory/space.hpp -index ef2f2c6..b54d470 100644 ---- a/src/share/vm/memory/space.hpp -+++ b/src/share/vm/memory/space.hpp +diff -r 882f6c762ac5 src/share/vm/memory/space.hpp +--- a/src/share/vm/memory/space.hpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/memory/space.hpp Thu Jul 09 23:18:17 2015 -0700 @@ -445,6 +445,9 @@ // indicates when the next such action should be taken. virtual void prepare_for_compaction(CompactPoint* cp); @@ -2906,10 +2884,9 @@ index ef2f2c6..b54d470 100644 // Return a size with adjusments as required of the space. virtual size_t adjust_object_size_v(size_t size) const { return size; } -diff --git a/src/share/vm/memory/universe.cpp b/src/share/vm/memory/universe.cpp -index 4030d9d..da9a186 100644 ---- a/src/share/vm/memory/universe.cpp -+++ b/src/share/vm/memory/universe.cpp +diff -r 882f6c762ac5 src/share/vm/memory/universe.cpp +--- a/src/share/vm/memory/universe.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/memory/universe.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -100,6 +100,8 @@ #include "gc_implementation/parallelScavenge/parallelScavengeHeap.hpp" #endif @@ -2958,10 +2935,9 @@ index 4030d9d..da9a186 100644 void Universe::oops_do(OopClosure* f, bool do_all) { f->do_oop((oop*) &_int_mirror); -diff --git a/src/share/vm/memory/universe.hpp b/src/share/vm/memory/universe.hpp -index 50fcb62..afb6b33 100644 ---- a/src/share/vm/memory/universe.hpp -+++ b/src/share/vm/memory/universe.hpp +diff -r 882f6c762ac5 src/share/vm/memory/universe.hpp +--- a/src/share/vm/memory/universe.hpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/memory/universe.hpp Thu Jul 09 23:18:17 2015 -0700 @@ -127,6 +127,8 @@ friend class SystemDictionary; friend class VMStructs; @@ -3007,10 +2983,9 @@ index 50fcb62..afb6b33 100644 static void verify(VerifyOption option, const char* prefix, bool silent = VerifySilently); static void verify(const char* prefix, bool silent = VerifySilently) { verify(VerifyOption_Default, prefix, silent); -diff --git a/src/share/vm/oops/arrayKlass.cpp b/src/share/vm/oops/arrayKlass.cpp -index 16142b8..921e546 100644 ---- a/src/share/vm/oops/arrayKlass.cpp -+++ b/src/share/vm/oops/arrayKlass.cpp +diff -r 882f6c762ac5 src/share/vm/oops/arrayKlass.cpp +--- a/src/share/vm/oops/arrayKlass.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/oops/arrayKlass.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -136,9 +136,9 @@ bool arrayKlass::compute_is_subtype_of(klassOop k) { @@ -3024,10 +2999,9 @@ index 16142b8..921e546 100644 } -diff --git a/src/share/vm/oops/constMethodKlass.cpp b/src/share/vm/oops/constMethodKlass.cpp -index e74811f..be3fe7d 100644 ---- a/src/share/vm/oops/constMethodKlass.cpp -+++ b/src/share/vm/oops/constMethodKlass.cpp +diff -r 882f6c762ac5 src/share/vm/oops/constMethodKlass.cpp +--- a/src/share/vm/oops/constMethodKlass.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/oops/constMethodKlass.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -102,6 +102,7 @@ constMethodOop cm = constMethodOop(obj); MarkSweep::mark_and_push(cm->adr_constants()); @@ -3069,10 +3043,9 @@ index e74811f..be3fe7d 100644 // Get size before changing pointers. // Don't call size() or oop_size() since that is a virtual call. int size = cm->object_size(); -diff --git a/src/share/vm/oops/constMethodOop.hpp b/src/share/vm/oops/constMethodOop.hpp -index 549192b..5cea5c4 100644 ---- a/src/share/vm/oops/constMethodOop.hpp -+++ b/src/share/vm/oops/constMethodOop.hpp +diff -r 882f6c762ac5 src/share/vm/oops/constMethodOop.hpp +--- a/src/share/vm/oops/constMethodOop.hpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/oops/constMethodOop.hpp Thu Jul 09 23:18:17 2015 -0700 @@ -129,7 +129,7 @@ public: @@ -3082,21 +3055,20 @@ index 549192b..5cea5c4 100644 private: // -@@ -140,6 +140,9 @@ - +@@ -141,6 +141,9 @@ // Raw stackmap data for the method typeArrayOop _stackmap_data; -+ + + // (tw) Table mapping code sections for method forward points. + typeArrayOop _code_section_table; - ++ // // End of the oop block. -@@ -194,6 +197,28 @@ - oop_store_without_check((oop*)&_stackmap_data, (oop)sd); + // +@@ -195,6 +198,28 @@ } bool has_stackmap_table() const { return _stackmap_data != NULL; } -+ + + // code section table + typeArrayOop code_section_table() const { return _code_section_table; } + void set_code_section_table(typeArrayOop e) { oop_store_without_check((oop*) &_code_section_table, (oop) e); } @@ -3118,9 +3090,10 @@ index 549192b..5cea5c4 100644 + int code_section_length_at(int index) const { + return _code_section_table->short_at(index * ValuesPerCodeSectionEntry + 2); + } - ++ void init_fingerprint() { const uint64_t initval = CONST64(0x8000000000000000); + _fingerprint = initval; @@ -301,6 +326,7 @@ // Garbage collection support oop* adr_constants() const { return (oop*)&_constants; } @@ -3129,10 +3102,9 @@ index 549192b..5cea5c4 100644 bool is_conc_safe() { return _is_conc_safe; } void set_is_conc_safe(bool v) { _is_conc_safe = v; } -diff --git a/src/share/vm/oops/cpCacheOop.cpp b/src/share/vm/oops/cpCacheOop.cpp -index ad62921..f39f202 100644 ---- a/src/share/vm/oops/cpCacheOop.cpp -+++ b/src/share/vm/oops/cpCacheOop.cpp +diff -r 882f6c762ac5 src/share/vm/oops/cpCacheOop.cpp +--- a/src/share/vm/oops/cpCacheOop.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/oops/cpCacheOop.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -37,9 +37,15 @@ // Implememtation of ConstantPoolCacheEntry @@ -3232,7 +3204,7 @@ index ad62921..f39f202 100644 return true; } -@@ -548,82 +546,25 @@ +@@ -548,84 +546,27 @@ return false; } @@ -3271,8 +3243,8 @@ index ad62921..f39f202 100644 } return false; --} -- + } + -// a constant pool cache entry should never contain old or obsolete methods -bool ConstantPoolCacheEntry::check_no_old_or_obsolete_entries() { - if (is_vfinal()) { @@ -3322,9 +3294,11 @@ index ad62921..f39f202 100644 - - // the method is in the interesting class so the entry is interesting - return true; - } - +-} +- void ConstantPoolCacheEntry::print(outputStream* st, int index) const { + // print separator + if (index == 0) st->print_cr(" -------------"); @@ -663,60 +604,18 @@ } } @@ -3370,7 +3344,7 @@ index ad62921..f39f202 100644 + // (tw) TODO: Update only field offsets and modify only constant pool entries that + // point to changed fields + entry_at(i)->initialize_entry(entry_at(i)->constant_pool_index()); -+ } else if(entry_at(i)->is_method_entry()) { ++ } else if(entry_at(i)->is_method_entry() && !entry_at(i)->is_secondary_entry()) { + entry_at(i)->adjust_method_entry(NULL, NULL); } } @@ -3394,10 +3368,9 @@ index ad62921..f39f202 100644 - } - } -} -diff --git a/src/share/vm/oops/cpCacheOop.hpp b/src/share/vm/oops/cpCacheOop.hpp -index b9107cd..653ab13 100644 ---- a/src/share/vm/oops/cpCacheOop.hpp -+++ b/src/share/vm/oops/cpCacheOop.hpp +diff -r 882f6c762ac5 src/share/vm/oops/cpCacheOop.hpp +--- a/src/share/vm/oops/cpCacheOop.hpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/oops/cpCacheOop.hpp Thu Jul 09 23:18:17 2015 -0700 @@ -136,7 +136,8 @@ void set_bytecode_2(Bytecodes::Code code); void set_f1(oop f1) { @@ -3416,15 +3389,15 @@ index b9107cd..653ab13 100644 is_vfinal_shift = 20, is_volatile_shift = 21, is_final_shift = 22, -@@ -206,6 +208,8 @@ - // Initialization +@@ -207,6 +209,8 @@ void initialize_entry(int original_index); // initialize primary entry void initialize_secondary_entry(int main_index); // initialize secondary entry -+ -+ void copy_from(ConstantPoolCacheEntry *other); ++ void copy_from(ConstantPoolCacheEntry *other); ++ void set_field( // sets entry to resolved field state Bytecodes::Code get_code, // the bytecode used for reading the field + Bytecodes::Code put_code, // the bytecode used for writing the field @@ -368,10 +372,7 @@ // trace_name_printed is set to true if the current call has // printed the klass name so that other routines in the adjust_* @@ -3457,10 +3430,9 @@ index b9107cd..653ab13 100644 }; #endif // SHARE_VM_OOPS_CPCACHEOOP_HPP -diff --git a/src/share/vm/oops/instanceKlass.cpp b/src/share/vm/oops/instanceKlass.cpp -index a775b02..749458f 100644 ---- a/src/share/vm/oops/instanceKlass.cpp -+++ b/src/share/vm/oops/instanceKlass.cpp +diff -r 882f6c762ac5 src/share/vm/oops/instanceKlass.cpp +--- a/src/share/vm/oops/instanceKlass.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/oops/instanceKlass.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -250,12 +250,118 @@ } @@ -3834,10 +3806,9 @@ index a775b02..749458f 100644 st->cr(); st->print(BULLET"fake entry resolved_constructor: "); methodOop ctor = java_lang_Class::resolved_constructor(obj); -diff --git a/src/share/vm/oops/instanceKlass.hpp b/src/share/vm/oops/instanceKlass.hpp -index bb613b0..71f0083 100644 ---- a/src/share/vm/oops/instanceKlass.hpp -+++ b/src/share/vm/oops/instanceKlass.hpp +diff -r 882f6c762ac5 src/share/vm/oops/instanceKlass.hpp +--- a/src/share/vm/oops/instanceKlass.hpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/oops/instanceKlass.hpp Thu Jul 09 23:18:17 2015 -0700 @@ -102,6 +102,22 @@ virtual void do_field(fieldDescriptor* fd) = 0; }; @@ -3910,10 +3881,9 @@ index bb613b0..71f0083 100644 void methods_do(void f(methodOop method)); void array_klasses_do(void f(klassOop k)); -diff --git a/src/share/vm/oops/instanceKlassKlass.cpp b/src/share/vm/oops/instanceKlassKlass.cpp -index 8e7dc12..63d6dc4 100644 ---- a/src/share/vm/oops/instanceKlassKlass.cpp -+++ b/src/share/vm/oops/instanceKlassKlass.cpp +diff -r 882f6c762ac5 src/share/vm/oops/instanceKlassKlass.cpp +--- a/src/share/vm/oops/instanceKlassKlass.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/oops/instanceKlassKlass.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -480,6 +480,28 @@ instanceKlass* ik = instanceKlass::cast(klassOop(obj)); klassKlass::oop_print_on(obj, st); @@ -3952,10 +3922,9 @@ index 8e7dc12..63d6dc4 100644 sib = sib->next_sibling(); } -diff --git a/src/share/vm/oops/instanceRefKlass.cpp b/src/share/vm/oops/instanceRefKlass.cpp -index 7db4f03..1171487 100644 ---- a/src/share/vm/oops/instanceRefKlass.cpp -+++ b/src/share/vm/oops/instanceRefKlass.cpp +diff -r 882f6c762ac5 src/share/vm/oops/instanceRefKlass.cpp +--- a/src/share/vm/oops/instanceRefKlass.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/oops/instanceRefKlass.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -455,10 +455,13 @@ instanceKlass* ik = instanceKlass::cast(k); @@ -3974,10 +3943,9 @@ index 7db4f03..1171487 100644 assert(ik->nonstatic_oop_map_count() == 1, "just checking"); OopMapBlock* map = ik->start_of_nonstatic_oop_maps(); -diff --git a/src/share/vm/oops/klass.cpp b/src/share/vm/oops/klass.cpp -index ff33181..28537e2 100644 ---- a/src/share/vm/oops/klass.cpp -+++ b/src/share/vm/oops/klass.cpp +diff -r 882f6c762ac5 src/share/vm/oops/klass.cpp +--- a/src/share/vm/oops/klass.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/oops/klass.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -55,6 +55,26 @@ return false; } @@ -4031,10 +3999,9 @@ index ff33181..28537e2 100644 assert(super() == NULL || super() == SystemDictionary::Object_klass(), "initialize this only once to a non-trivial value"); set_super(k); -diff --git a/src/share/vm/oops/klass.hpp b/src/share/vm/oops/klass.hpp -index a449e87..52364ba 100644 ---- a/src/share/vm/oops/klass.hpp -+++ b/src/share/vm/oops/klass.hpp +diff -r 882f6c762ac5 src/share/vm/oops/klass.hpp +--- a/src/share/vm/oops/klass.hpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/oops/klass.hpp Thu Jul 09 23:18:17 2015 -0700 @@ -171,6 +171,7 @@ void* operator new(size_t ignored, KlassHandle& klass, int size, TRAPS); }; @@ -4094,11 +4061,10 @@ index a449e87..52364ba 100644 // First subclass (NULL if none); _subklass->next_sibling() is next one klassOop _subklass; // Sibling link (or NULL); links all subklasses of a klass -@@ -253,6 +291,19 @@ - +@@ -254,6 +292,19 @@ jint _modifier_flags; // Processed access flags, for use by Class.getModifiers. AccessFlags _access_flags; // Access flags. The class/interface distinction is stored here. -+ + + // (tw) Non-oop fields for enhanced class redefinition + jint _revision_number; // The revision number for redefined classes + jint _redefinition_index; // Index of this class when performing the redefinition @@ -4111,14 +4077,14 @@ index a449e87..52364ba 100644 + char _field_redefinition_policy; + char _static_field_redefinition_policy; + bool _is_redefining; - ++ #ifndef PRODUCT int _verify_count; // to avoid redundant verifies -@@ -301,6 +352,99 @@ - + #endif +@@ -302,6 +353,99 @@ klassOop secondary_super_cache() const { return _secondary_super_cache; } void set_secondary_super_cache(klassOop k) { oop_store_without_check((oop*) &_secondary_super_cache, (oop) k); } -+ + + // BEGIN class redefinition utilities + + // double links between new and old version of a class @@ -4211,9 +4177,10 @@ index a449e87..52364ba 100644 + } + + // END class redefinition utilities - ++ objArrayOop secondary_supers() const { return _secondary_supers; } void set_secondary_supers(objArrayOop k) { oop_store_without_check((oop*) &_secondary_supers, (oop) k); } + @@ -362,6 +506,8 @@ void set_next_sibling(klassOop s); @@ -4240,10 +4207,9 @@ index a449e87..52364ba 100644 #endif // SHARE_VM_OOPS_KLASS_HPP -diff --git a/src/share/vm/oops/klassKlass.cpp b/src/share/vm/oops/klassKlass.cpp -index 06809d5..9c08f32 100644 ---- a/src/share/vm/oops/klassKlass.cpp -+++ b/src/share/vm/oops/klassKlass.cpp +diff -r 882f6c762ac5 src/share/vm/oops/klassKlass.cpp +--- a/src/share/vm/oops/klassKlass.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/oops/klassKlass.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -68,6 +68,8 @@ Klass* k = Klass::cast(klassOop(obj)); // If we are alive it is valid to keep our superclass and subtype caches alive @@ -4300,10 +4266,9 @@ index 06809d5..9c08f32 100644 for (juint i = 0; i < Klass::primary_super_limit(); i++) MarkSweep::adjust_pointer(k->adr_primary_supers()+i); MarkSweep::adjust_pointer(k->adr_secondary_super_cache()); -diff --git a/src/share/vm/oops/klassOop.hpp b/src/share/vm/oops/klassOop.hpp -index f212fc5..9731a9c 100644 ---- a/src/share/vm/oops/klassOop.hpp -+++ b/src/share/vm/oops/klassOop.hpp +diff -r 882f6c762ac5 src/share/vm/oops/klassOop.hpp +--- a/src/share/vm/oops/klassOop.hpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/oops/klassOop.hpp Thu Jul 09 23:18:17 2015 -0700 @@ -41,8 +41,10 @@ // returns the Klass part containing dispatching behavior Klass* klass_part() const { return (Klass*)((address)this + sizeof(klassOopDesc)); } @@ -4316,10 +4281,9 @@ index f212fc5..9731a9c 100644 private: // These have no implementation since klassOop should never be accessed in this fashion -diff --git a/src/share/vm/oops/klassVtable.cpp b/src/share/vm/oops/klassVtable.cpp -index ff22444..8d39611 100644 ---- a/src/share/vm/oops/klassVtable.cpp -+++ b/src/share/vm/oops/klassVtable.cpp +diff -r 882f6c762ac5 src/share/vm/oops/klassVtable.cpp +--- a/src/share/vm/oops/klassVtable.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/oops/klassVtable.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -97,7 +97,8 @@ vtable_length = Universe::base_vtable_size(); } @@ -4342,12 +4306,10 @@ index ff22444..8d39611 100644 } int klassVtable::index_of(methodOop m, int len) const { -@@ -676,20 +677,6 @@ - } - } +@@ -678,20 +679,6 @@ return true; --} -- + } + -void klassVtable::dump_vtable() { - tty->print_cr("vtable dump --"); - for (int i = 0; i < length(); i++) { @@ -4360,9 +4322,11 @@ index ff22444..8d39611 100644 - tty->cr(); - } - } - } - +-} +- // CDS/RedefineClasses support - clear vtables so they can be reinitialized + void klassVtable::clear_vtable() { + for (int i = 0; i < _length; i++) table()[i].clear(); @@ -1262,6 +1249,7 @@ void klassVtable::verify_against(outputStream* st, klassVtable* vt, int index) { @@ -4438,10 +4402,9 @@ index ff22444..8d39611 100644 int klassItable::_total_classes; // Total no. of classes with itables long klassItable::_total_size; // Total no. of bytes used for itables -diff --git a/src/share/vm/oops/klassVtable.hpp b/src/share/vm/oops/klassVtable.hpp -index 405b0c7..0c8d2f7 100644 ---- a/src/share/vm/oops/klassVtable.hpp -+++ b/src/share/vm/oops/klassVtable.hpp +diff -r 882f6c762ac5 src/share/vm/oops/klassVtable.hpp +--- a/src/share/vm/oops/klassVtable.hpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/oops/klassVtable.hpp Thu Jul 09 23:18:17 2015 -0700 @@ -100,6 +100,7 @@ int methods_length, bool * trace_name_printed); bool check_no_old_or_obsolete_entries(); @@ -4450,10 +4413,9 @@ index 405b0c7..0c8d2f7 100644 // Garbage collection void oop_follow_contents(); -diff --git a/src/share/vm/oops/methodKlass.cpp b/src/share/vm/oops/methodKlass.cpp -index 75d0b09..f1b7d2f 100644 ---- a/src/share/vm/oops/methodKlass.cpp -+++ b/src/share/vm/oops/methodKlass.cpp +diff -r 882f6c762ac5 src/share/vm/oops/methodKlass.cpp +--- a/src/share/vm/oops/methodKlass.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/oops/methodKlass.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -93,6 +93,10 @@ m->set_adapter_entry(NULL); m->clear_code(); // from_c/from_i get set to c2i/i2i @@ -4548,10 +4510,9 @@ index 75d0b09..f1b7d2f 100644 st->print (" - constants: "INTPTR_FORMAT" ", (address)m->constants()); m->constants()->print_value_on(st); st->cr(); st->print (" - access: 0x%x ", m->access_flags().as_int()); m->access_flags().print_on(st); st->cr(); -diff --git a/src/share/vm/oops/methodOop.cpp b/src/share/vm/oops/methodOop.cpp -index 4f59d3a..5cdf147 100644 ---- a/src/share/vm/oops/methodOop.cpp -+++ b/src/share/vm/oops/methodOop.cpp +diff -r 882f6c762ac5 src/share/vm/oops/methodOop.cpp +--- a/src/share/vm/oops/methodOop.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/oops/methodOop.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -328,6 +328,70 @@ } @@ -4633,10 +4594,9 @@ index 4f59d3a..5cdf147 100644 newm->constMethod()->set_code_size(new_code_length); newm->constMethod()->set_constMethod_size(new_const_method_size); newm->set_method_size(new_method_size); -diff --git a/src/share/vm/oops/methodOop.hpp b/src/share/vm/oops/methodOop.hpp -index 486e106..11e52bb 100644 ---- a/src/share/vm/oops/methodOop.hpp -+++ b/src/share/vm/oops/methodOop.hpp +diff -r 882f6c762ac5 src/share/vm/oops/methodOop.hpp +--- a/src/share/vm/oops/methodOop.hpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/oops/methodOop.hpp Thu Jul 09 23:18:17 2015 -0700 @@ -114,6 +114,11 @@ AccessFlags _access_flags; // Access flags int _vtable_index; // vtable index of this method (see VtableIndexFlag) @@ -4649,11 +4609,10 @@ index 486e106..11e52bb 100644 #ifdef CC_INTERP int _result_index; // C++ interpreter needs for converting results to/from stack #endif -@@ -174,6 +179,32 @@ - Symbol* name() const { return constants()->symbol_at(name_index()); } +@@ -175,6 +180,32 @@ int name_index() const { return constMethod()->name_index(); } void set_name_index(int index) { constMethod()->set_name_index(index); } -+ + + methodOop forward_method() const {return _forward_method; } + void set_forward_method(methodOop m) { _forward_method = m; } + bool has_forward_method() const { return forward_method() != NULL; } @@ -4679,9 +4638,10 @@ index 486e106..11e52bb 100644 + return old_version()->oldest_version(); + } + } - ++ // signature Symbol* signature() const { return constants()->symbol_at(signature_index()); } + int signature_index() const { return constMethod()->signature_index(); } @@ -670,6 +701,10 @@ // Inline cache support void cleanup_inline_caches(); @@ -4703,10 +4663,9 @@ index 486e106..11e52bb 100644 oop* adr_method_data() const { return (oop*)&_method_data; } }; -diff --git a/src/share/vm/oops/oop.hpp b/src/share/vm/oops/oop.hpp -index 5982c88..4873fca 100644 ---- a/src/share/vm/oops/oop.hpp -+++ b/src/share/vm/oops/oop.hpp +diff -r 882f6c762ac5 src/share/vm/oops/oop.hpp +--- a/src/share/vm/oops/oop.hpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/oops/oop.hpp Thu Jul 09 23:18:17 2015 -0700 @@ -95,6 +95,7 @@ narrowOop* compressed_klass_addr(); @@ -4723,10 +4682,9 @@ index 5982c88..4873fca 100644 bool is_thread() const; bool is_method() const; bool is_constMethod() const; -diff --git a/src/share/vm/oops/oop.inline.hpp b/src/share/vm/oops/oop.inline.hpp -index f4eb2f7..0acb346 100644 ---- a/src/share/vm/oops/oop.inline.hpp -+++ b/src/share/vm/oops/oop.inline.hpp +diff -r 882f6c762ac5 src/share/vm/oops/oop.inline.hpp +--- a/src/share/vm/oops/oop.inline.hpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/oops/oop.inline.hpp Thu Jul 09 23:18:17 2015 -0700 @@ -123,6 +123,14 @@ } } @@ -4750,10 +4708,9 @@ index f4eb2f7..0acb346 100644 inline bool oopDesc::is_thread() const { return blueprint()->oop_is_thread(); } inline bool oopDesc::is_method() const { return blueprint()->oop_is_method(); } inline bool oopDesc::is_constMethod() const { return blueprint()->oop_is_constMethod(); } -diff --git a/src/share/vm/prims/jni.cpp b/src/share/vm/prims/jni.cpp -index 50e1263..701ed74 100644 ---- a/src/share/vm/prims/jni.cpp -+++ b/src/share/vm/prims/jni.cpp +diff -r 882f6c762ac5 src/share/vm/prims/jni.cpp +--- a/src/share/vm/prims/jni.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/prims/jni.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -406,7 +406,7 @@ } } @@ -4763,10 +4720,9 @@ index 50e1263..701ed74 100644 CHECK_NULL); if (TraceClassResolution && k != NULL) { -diff --git a/src/share/vm/prims/jvm.cpp b/src/share/vm/prims/jvm.cpp -index ccd09f6..ef3f024 100644 ---- a/src/share/vm/prims/jvm.cpp -+++ b/src/share/vm/prims/jvm.cpp +diff -r 882f6c762ac5 src/share/vm/prims/jvm.cpp +--- a/src/share/vm/prims/jvm.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/prims/jvm.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -932,7 +932,7 @@ Handle protection_domain (THREAD, JNIHandles::resolve(pd)); klassOop k = SystemDictionary::resolve_from_stream(class_name, class_loader, @@ -4776,10 +4732,9 @@ index ccd09f6..ef3f024 100644 CHECK_NULL); if (TraceClassResolution && k != NULL) { -diff --git a/src/share/vm/prims/jvmtiEnv.cpp b/src/share/vm/prims/jvmtiEnv.cpp -index 4ac6b82..30b8e84 100644 ---- a/src/share/vm/prims/jvmtiEnv.cpp -+++ b/src/share/vm/prims/jvmtiEnv.cpp +diff -r 882f6c762ac5 src/share/vm/prims/jvmtiEnv.cpp +--- a/src/share/vm/prims/jvmtiEnv.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/prims/jvmtiEnv.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -290,7 +290,10 @@ class_definitions[index].klass = jcls; } @@ -4807,10 +4762,9 @@ index 4ac6b82..30b8e84 100644 return (op.check_error()); } /* end RedefineClasses */ -diff --git a/src/share/vm/prims/jvmtiExport.cpp b/src/share/vm/prims/jvmtiExport.cpp -index ec8ede3..2bd5983 100644 ---- a/src/share/vm/prims/jvmtiExport.cpp -+++ b/src/share/vm/prims/jvmtiExport.cpp +diff -r 882f6c762ac5 src/share/vm/prims/jvmtiExport.cpp +--- a/src/share/vm/prims/jvmtiExport.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/prims/jvmtiExport.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -2296,7 +2296,7 @@ // iterate over any code blob descriptors collected and post a // DYNAMIC_CODE_GENERATED event to the profiler. @@ -4820,10 +4774,9 @@ index ec8ede3..2bd5983 100644 // iterate over any code blob descriptors that we collected if (_code_blobs != NULL) { for (int i=0; i<_code_blobs->length(); i++) { -diff --git a/src/share/vm/prims/jvmtiImpl.cpp b/src/share/vm/prims/jvmtiImpl.cpp -index d3fa140..f4f8b57 100644 ---- a/src/share/vm/prims/jvmtiImpl.cpp -+++ b/src/share/vm/prims/jvmtiImpl.cpp +diff -r 882f6c762ac5 src/share/vm/prims/jvmtiImpl.cpp +--- a/src/share/vm/prims/jvmtiImpl.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/prims/jvmtiImpl.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -286,6 +286,8 @@ void JvmtiBreakpoint::each_method_version_do(method_action meth_act) { ((methodOopDesc*)_method->*meth_act)(_bci); @@ -4833,10 +4786,9 @@ index d3fa140..f4f8b57 100644 // add/remove breakpoint to/from versions of the method that // are EMCP. Directly or transitively obsolete methods are // not saved in the PreviousVersionInfo. -diff --git a/src/share/vm/prims/jvmtiRedefineClasses.cpp b/src/share/vm/prims/jvmtiRedefineClasses.cpp -index 606be1c..ef4f380 100644 ---- a/src/share/vm/prims/jvmtiRedefineClasses.cpp -+++ b/src/share/vm/prims/jvmtiRedefineClasses.cpp +diff -r 882f6c762ac5 src/share/vm/prims/jvmtiRedefineClasses.cpp +--- a/src/share/vm/prims/jvmtiRedefineClasses.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/prims/jvmtiRedefineClasses.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. @@ -4848,10 +4800,10 @@ index 606be1c..ef4f380 100644 #include "interpreter/rewriter.hpp" #include "memory/gcLocker.hpp" #include "memory/universe.inline.hpp" --#include "oops/fieldStreams.hpp" +#include "memory/cardTableRS.hpp" - #include "oops/klassVtable.hpp" -+#include "oops/fieldStreams.hpp" ++#include "oops/klassVtable.hpp" + #include "oops/fieldStreams.hpp" +-#include "oops/klassVtable.hpp" #include "prims/jvmtiImpl.hpp" #include "prims/jvmtiRedefineClasses.hpp" +#include "prims/jvmtiClassFileReconstituter.hpp" @@ -4878,12 +4830,13 @@ index 606be1c..ef4f380 100644 int VM_RedefineClasses::_added_methods_length = 0; klassOop VM_RedefineClasses::_the_class_oop = NULL; -+// Holds the revision number of the current class redefinition -+int VM_RedefineClasses::_revision_number = -1; - +- -VM_RedefineClasses::VM_RedefineClasses(jint class_count, - const jvmtiClassDefinition *class_defs, - JvmtiClassLoadKind class_load_kind) { ++// Holds the revision number of the current class redefinition ++int VM_RedefineClasses::_revision_number = -1; ++ +VM_RedefineClasses::VM_RedefineClasses(jint class_count, const jvmtiClassDefinition *class_defs, JvmtiClassLoadKind class_load_kind) + : VM_GC_Operation(Universe::heap()->total_full_collections(), GCCause::_jvmti_force_gc) { + RC_TIMER_START(_timer_total); @@ -4895,19 +4848,12 @@ index 606be1c..ef4f380 100644 + _result = JVMTI_ERROR_NONE; } --bool VM_RedefineClasses::doit_prologue() { -- if (_class_count == 0) { -- _res = JVMTI_ERROR_NONE; -- return false; +VM_RedefineClasses::~VM_RedefineClasses() { + { + MonitorLockerEx ml(RedefinitionSync_lock); + Threads::set_wait_at_instrumentation_entry(false); + ml.notify_all(); - } -- if (_class_defs == NULL) { -- _res = JVMTI_ERROR_NULL_POINTER; -- return false; ++ } + + unlock_threads(); + RC_TIMER_STOP(_timer_total); @@ -4923,19 +4869,7 @@ index 606be1c..ef4f380 100644 + tty->print_cr("Timing Epilogue: %d", _timer_vm_op_epilogue.milliseconds()); + tty->print_cr("------------------------------------------------------------------"); + tty->print_cr("Total Time: %d", _timer_total.milliseconds()); - } -- for (int i = 0; i < _class_count; i++) { -- if (_class_defs[i].klass == NULL) { -- _res = JVMTI_ERROR_INVALID_CLASS; -- return false; -- } -- if (_class_defs[i].class_byte_count == 0) { -- _res = JVMTI_ERROR_INVALID_CLASS_FORMAT; -- return false; -- } -- if (_class_defs[i].class_bytes == NULL) { -- _res = JVMTI_ERROR_NULL_POINTER; -- return false; ++ } +} + +// Searches for all affected classes and performs a sorting such that a supertype is always before a subtype. @@ -4961,12 +4895,9 @@ index 606be1c..ef4f380 100644 + for (int i=0; iname()->as_C_string())); - } - } - -- // Start timer after all the sanity checks; not quite accurate, but -- // better than adding a bunch of stop() calls. -- RC_TIMER_START(_timer_vm_op_prologue); ++ } ++ } ++ + // Add the array of affected classes and the array of redefined classes to get a list of all classes that need a redefinition + all_affected_klasses->appendAll(&klasses_to_redefine); + all_affected_klasses->appendAll(&affected_classes); @@ -5037,7 +4968,9 @@ index 606be1c..ef4f380 100644 +} + +// Prologue of the VM operation, called on the Java thread in parallel to normal program execution -+bool VM_RedefineClasses::doit_prologue() { + bool VM_RedefineClasses::doit_prologue() { +- if (_class_count == 0) { +- _res = JVMTI_ERROR_NONE; + + _revision_number++; + RC_TRACE(0x00000001, ("Redefinition with revision number %d started!", _revision_number)); @@ -5047,16 +4980,13 @@ index 606be1c..ef4f380 100644 + + if (!check_arguments()) { + RC_TIMER_STOP(_timer_prologue); -+ return false; -+ } - - // We first load new class versions in the prologue, because somewhere down the - // call chain it is required that the current thread is a Java thread. -- _res = load_new_class_versions(Thread::current()); -- if (_res != JVMTI_ERROR_NONE) { -- // Free os::malloc allocated memory in load_new_class_version. -- os::free(_scratch_classes); -- RC_TIMER_STOP(_timer_vm_op_prologue); + return false; + } +- if (_class_defs == NULL) { +- _res = JVMTI_ERROR_NULL_POINTER; ++ ++ // We first load new class versions in the prologue, because somewhere down the ++ // call chain it is required that the current thread is a Java thread. + _new_classes = new (ResourceObj::C_HEAP, mtInternal) GrowableArray(5, true); + _result = load_new_class_versions(Thread::current()); + @@ -5068,20 +4998,108 @@ index 606be1c..ef4f380 100644 + RC_TIMER_STOP(_timer_prologue); return false; } - -- RC_TIMER_STOP(_timer_vm_op_prologue); ++ + RC_TRACE(0x00000001, ("nearly finished")); + VM_GC_Operation::doit_prologue(); + RC_TIMER_STOP(_timer_prologue); + RC_TRACE(0x00000001, ("doit_prologue finished!")); - return true; - } - --void VM_RedefineClasses::doit() { -- Thread *thread = Thread::current(); ++ return true; ++} ++ +// Checks basic properties of the arguments of the redefinition command. +bool VM_RedefineClasses::check_arguments() { ++ ++ if (_class_count == 0) RC_ABORT(JVMTI_ERROR_NONE); ++ if (_class_defs == NULL) RC_ABORT(JVMTI_ERROR_NULL_POINTER); + for (int i = 0; i < _class_count; i++) { +- if (_class_defs[i].klass == NULL) { +- _res = JVMTI_ERROR_INVALID_CLASS; +- return false; ++ if (_class_defs[i].klass == NULL) RC_ABORT(JVMTI_ERROR_INVALID_CLASS); ++ if (_class_defs[i].class_byte_count == 0) RC_ABORT(JVMTI_ERROR_INVALID_CLASS_FORMAT); ++ if (_class_defs[i].class_bytes == NULL) RC_ABORT(JVMTI_ERROR_NULL_POINTER); ++ } ++ ++ return true; ++} ++ ++jvmtiError VM_RedefineClasses::check_exception() const { ++ Thread* THREAD = Thread::current(); ++ if (HAS_PENDING_EXCEPTION) { ++ ++ Symbol* ex_name = PENDING_EXCEPTION->klass()->klass_part()->name(); ++ RC_TRACE(0x00000001, ("parse_stream exception: '%s'", ++ ex_name->as_C_string())); ++ if (TraceRedefineClasses >= 1) { ++ java_lang_Throwable::print(PENDING_EXCEPTION, tty); ++ tty->print_cr(""); + } +- if (_class_defs[i].class_byte_count == 0) { +- _res = JVMTI_ERROR_INVALID_CLASS_FORMAT; +- return false; +- } +- if (_class_defs[i].class_bytes == NULL) { +- _res = JVMTI_ERROR_NULL_POINTER; +- return false; ++ CLEAR_PENDING_EXCEPTION; ++ ++ if (ex_name == vmSymbols::java_lang_UnsupportedClassVersionError()) { ++ return JVMTI_ERROR_UNSUPPORTED_VERSION; ++ } else if (ex_name == vmSymbols::java_lang_ClassFormatError()) { ++ return JVMTI_ERROR_INVALID_CLASS_FORMAT; ++ } else if (ex_name == vmSymbols::java_lang_ClassCircularityError()) { ++ return JVMTI_ERROR_CIRCULAR_CLASS_DEFINITION; ++ } else if (ex_name == vmSymbols::java_lang_NoClassDefFoundError()) { ++ // The message will be "XXX (wrong name: YYY)" ++ return JVMTI_ERROR_NAMES_DONT_MATCH; ++ } else if (ex_name == vmSymbols::java_lang_OutOfMemoryError()) { ++ return JVMTI_ERROR_OUT_OF_MEMORY; ++ } else { ++ // Just in case more exceptions can be thrown.. ++ return JVMTI_ERROR_FAILS_VERIFICATION; + } + } + +- // Start timer after all the sanity checks; not quite accurate, but +- // better than adding a bunch of stop() calls. +- RC_TIMER_START(_timer_vm_op_prologue); +- +- // We first load new class versions in the prologue, because somewhere down the +- // call chain it is required that the current thread is a Java thread. +- _res = load_new_class_versions(Thread::current()); +- if (_res != JVMTI_ERROR_NONE) { +- // Free os::malloc allocated memory in load_new_class_version. +- os::free(_scratch_classes); +- RC_TIMER_STOP(_timer_vm_op_prologue); +- return false; ++ return JVMTI_ERROR_NONE; ++} ++ ++// Loads all new class versions and stores the instanceKlass handles in an array. ++jvmtiError VM_RedefineClasses::load_new_class_versions(TRAPS) { ++ ++ ResourceMark rm(THREAD); ++ ++ RC_TRACE(0x00000001, ("===================================================================")); ++ RC_TRACE(0x00000001, ("load new class versions (%d)", ++ _class_count)); ++ ++ // Retrieve an array of all classes that need to be redefined ++ GrowableArray all_affected_klasses; ++ jvmtiError err = find_sorted_affected_classes(&all_affected_klasses); ++ if (err != JVMTI_ERROR_NONE) { ++ RC_TRACE(0x00000001, ("Error finding sorted affected classes: %d", ++ (int)err)); ++ return err; + } +- RC_TIMER_STOP(_timer_vm_op_prologue); +- return true; +-} +- +-void VM_RedefineClasses::doit() { +- Thread *thread = Thread::current(); +- - if (UseSharedSpaces) { - // Sharing is enabled so we remap the shared readonly space to - // shared readwrite, private just in case we need to redefine @@ -5092,20 +5110,43 @@ index 606be1c..ef4f380 100644 - ("failed to remap shared readonly space to readwrite, private")); - _res = JVMTI_ERROR_INTERNAL; - return; -- } -- } ++ ++ JvmtiThreadState *state = JvmtiThreadState::state_for(JavaThread::current()); ++ ++ _max_redefinition_flags = Klass::NoRedefinition; ++ jvmtiError result = JVMTI_ERROR_NONE; ++ ++ for (int i=0; iname()->as_C_string())); ++ ++ the_class->link_class(THREAD); ++ result = check_exception(); ++ if (result != JVMTI_ERROR_NONE) break; ++ ++ // Find new class bytes ++ const unsigned char* class_bytes; ++ jint class_byte_count; ++ jvmtiError error; ++ jboolean not_changed; ++ if ((error = find_class_bytes(the_class, &class_bytes, &class_byte_count, ¬_changed)) != JVMTI_ERROR_NONE) { ++ RC_TRACE(0x00000001, ("Error finding class bytes: %d", ++ (int)error)); ++ result = error; ++ break; + } +- } - -+ if (_class_count == 0) RC_ABORT(JVMTI_ERROR_NONE); -+ if (_class_defs == NULL) RC_ABORT(JVMTI_ERROR_NULL_POINTER); - for (int i = 0; i < _class_count; i++) { +- for (int i = 0; i < _class_count; i++) { - redefine_single_class(_class_defs[i].klass, _scratch_classes[i], thread); -+ if (_class_defs[i].klass == NULL) RC_ABORT(JVMTI_ERROR_INVALID_CLASS); -+ if (_class_defs[i].class_byte_count == 0) RC_ABORT(JVMTI_ERROR_INVALID_CLASS_FORMAT); -+ if (_class_defs[i].class_bytes == NULL) RC_ABORT(JVMTI_ERROR_NULL_POINTER); - } +- } - // Disable any dependent concurrent compilations - SystemDictionary::notice_modification(); - +- - // Set flag indicating that some invariants are no longer true. - // See jvmtiExport.hpp for detailed explanation. - JvmtiExport::set_has_redefined_a_class(); @@ -5114,7 +5155,82 @@ index 606be1c..ef4f380 100644 -// always called for non-product bits. -#ifdef PRODUCT - if (RC_TRACE_ENABLED(0x00004000)) { --#endif ++ assert(class_bytes != NULL && class_byte_count != 0, "Class bytes defined at this point!"); ++ ++ ++ // Set redefined class handle in JvmtiThreadState class. ++ // This redefined class is sent to agent event handler for class file ++ // load hook event. ++ state->set_class_being_redefined(&the_class, _class_load_kind); ++ ++ RC_TRACE(0x00000002, ("Before resolving from stream")); ++ ++ RC_TIMER_STOP(_timer_prologue); ++ RC_TIMER_START(_timer_class_loading); ++ ++ ++ // Parse the stream. ++ Handle the_class_loader(THREAD, the_class->class_loader()); ++ Handle protection_domain(THREAD, the_class->protection_domain()); ++ Symbol* the_class_sym = the_class->name(); ++ ClassFileStream st((u1*) class_bytes, class_byte_count, (char *)"__VM_RedefineClasses__"); ++ instanceKlassHandle new_class(THREAD, SystemDictionary::resolve_from_stream(the_class_sym, ++ the_class_loader, ++ protection_domain, ++ &st, ++ true, ++ the_class, ++ THREAD)); ++ ++ not_changed = false; ++ ++ RC_TIMER_STOP(_timer_class_loading); ++ RC_TIMER_START(_timer_prologue); ++ ++ RC_TRACE(0x00000002, ("After resolving class from stream!")); ++ // Clear class_being_redefined just to be sure. ++ state->clear_class_being_redefined(); ++ ++ result = check_exception(); ++ if (result != JVMTI_ERROR_NONE) break; ++ ++#ifdef ASSERT ++ ++ assert(new_class() != NULL, "Class could not be loaded!"); ++ assert(new_class() != the_class(), "must be different"); ++ assert(new_class->new_version() == NULL && new_class->old_version() != NULL, ""); ++ ++ ++ objArrayOop k_interfaces = new_class->local_interfaces(); ++ for (int j=0; jlength(); j++) { ++ assert(((klassOop)k_interfaces->obj_at(j))->klass_part()->is_newest_version(), "just checking"); ++ } ++ ++ if (!THREAD->is_Compiler_thread()) { ++ ++ RC_TRACE(0x00000002, ("name=%s loader="INTPTR_FORMAT" protection_domain="INTPTR_FORMAT" ", ++ the_class->name()->as_C_string(), ++ (address)(the_class->class_loader()), ++ (address)(the_class->protection_domain()))); ++ // If we are on the compiler thread, we must not try to resolve a class. ++ klassOop systemLookup = SystemDictionary::resolve_or_null(the_class->name(), the_class->class_loader(), the_class->protection_domain(), THREAD); ++ ++ if (systemLookup != NULL) { ++ assert(systemLookup == new_class->old_version(), "Old class must be in system dictionary!"); ++ ++ ++ Klass *subklass = new_class()->klass_part()->subklass(); ++ while (subklass != NULL) { ++ assert(subklass->new_version() == NULL, "Most recent version of class!"); ++ subklass = subklass->next_sibling(); ++ } ++ } else { ++ // This can happen for reflection generated classes.. ? ++ CLEAR_PENDING_EXCEPTION; ++ } ++ } ++ + #endif - RC_TRACE_WITH_THREAD(0x00004000, thread, ("calling check_class")); - SystemDictionary::classes_do(check_class, thread); -#ifdef PRODUCT @@ -5153,9 +5269,9 @@ index 606be1c..ef4f380 100644 - if (the_class_oop == NULL || !Klass::cast(the_class_oop)->oop_is_instance()) { - return false; - } - return true; - } - +- return true; +-} +- -// Append the current entry at scratch_i in scratch_cp to *merge_cp_p -// where the end of *merge_cp_p is specified by *merge_cp_length_p. For -// direct CP entries, there is just the current entry to append. For @@ -5170,21 +5286,10 @@ index 606be1c..ef4f380 100644 -void VM_RedefineClasses::append_entry(constantPoolHandle scratch_cp, - int scratch_i, constantPoolHandle *merge_cp_p, int *merge_cp_length_p, - TRAPS) { -+jvmtiError VM_RedefineClasses::check_exception() const { -+ Thread* THREAD = Thread::current(); -+ if (HAS_PENDING_EXCEPTION) { - +- - // append is different depending on entry tag type - switch (scratch_cp->tag_at(scratch_i).value()) { -+ Symbol* ex_name = PENDING_EXCEPTION->klass()->klass_part()->name(); -+ RC_TRACE(0x00000001, ("parse_stream exception: '%s'", -+ ex_name->as_C_string())); -+ if (TraceRedefineClasses >= 1) { -+ java_lang_Throwable::print(PENDING_EXCEPTION, tty); -+ tty->print_cr(""); -+ } -+ CLEAR_PENDING_EXCEPTION; - +- - // The old verifier is implemented outside the VM. It loads classes, - // but does not resolve constant pool entries directly so we never - // see Class entries here with the old verifier. Similarly the old @@ -5205,7 +5310,14 @@ index 606be1c..ef4f380 100644 - // The new entry in *merge_cp_p is at a different index than - // the new entry in scratch_cp so we need to map the index values. - map_index(scratch_cp, scratch_i, *merge_cp_length_p); -- } ++ ++ if (RC_TRACE_ENABLED(0x00000001)) { ++ if (new_class->layout_helper() != the_class->layout_helper()) { ++ RC_TRACE(0x00000001, ("Instance size change for class %s: new=%d old=%d", ++ new_class->name()->as_C_string(), ++ new_class->layout_helper(), ++ the_class->layout_helper())); + } - (*merge_cp_length_p)++; - } break; - @@ -5317,69 +5429,168 @@ index 606be1c..ef4f380 100644 - break; - default: - guarantee(false, "bad switch"); -- break; -- } ++ } ++ ++ // Set the new version of the class ++ new_class->set_revision_number(_revision_number); ++ new_class->set_redefinition_index(i); ++ the_class->set_new_version(new_class()); ++ _new_classes->append(new_class); ++ ++ assert(new_class->new_version() == NULL, ""); ++ ++ int redefinition_flags = Klass::NoRedefinition; ++ ++ if (not_changed) { ++ redefinition_flags = Klass::NoRedefinition; ++ } else if (AllowAdvancedClassRedefinition) { ++ redefinition_flags = calculate_redefinition_flags(new_class); ++ } else { ++ jvmtiError allowed = check_redefinition_allowed(new_class); ++ if (allowed != JVMTI_ERROR_NONE) { ++ RC_TRACE(0x00000001, ("Error redefinition not allowed!")); ++ result = allowed; + break; + } - - if (klass_ref_i != new_klass_ref_i) { - RC_TRACE(0x00080000, ("%s entry@%d class_index changed: %d to %d", - entry_name, *merge_cp_length_p, klass_ref_i, new_klass_ref_i)); -- } ++ redefinition_flags = Klass::ModifyClass; ++ } ++ ++ if (new_class->super() != NULL) { ++ redefinition_flags = redefinition_flags | new_class->super()->klass_part()->redefinition_flags(); ++ } ++ ++ for (int j=0; jlocal_interfaces()->length(); j++) { ++ redefinition_flags = redefinition_flags | ((klassOop)new_class->local_interfaces()->obj_at(j))->klass_part()->redefinition_flags(); ++ } ++ ++ new_class->set_redefinition_flags(redefinition_flags); ++ ++ _max_redefinition_flags = _max_redefinition_flags | redefinition_flags; ++ ++ if ((redefinition_flags & Klass::ModifyInstances) != 0) { ++ // TODO: Check if watch access flags of static fields are updated correctly. ++ calculate_instance_update_information(_new_classes->at(i)()); ++ } else { ++ assert(new_class->layout_helper() >> 1 == new_class->old_version()->klass_part()->layout_helper() >> 1, "must be equal"); ++ assert(new_class->fields()->length() == ((instanceKlass*)new_class->old_version()->klass_part())->fields()->length(), "must be equal"); ++ ++ fieldDescriptor fd_new; ++ fieldDescriptor fd_old; ++ for (JavaFieldStream fs(new_class); !fs.done(); fs.next()) { ++ fd_new.initialize(new_class(), fs.index()); ++ fd_old.initialize(new_class->old_version(), fs.index()); ++ transfer_special_access_flags(&fd_old, &fd_new); + } - if (name_and_type_ref_i != new_name_and_type_ref_i) { - RC_TRACE(0x00080000, - ("%s entry@%d name_and_type_index changed: %d to %d", - entry_name, *merge_cp_length_p, name_and_type_ref_i, - new_name_and_type_ref_i)); -- } -- -- if (scratch_i != *merge_cp_length_p) { -- // The new entry in *merge_cp_p is at a different index than -- // the new entry in scratch_cp so we need to map the index values. -- map_index(scratch_cp, scratch_i, *merge_cp_length_p); -- } -- (*merge_cp_length_p)++; -- } break; -- -- // this is an indirect CP entry so it needs special handling -- case JVM_CONSTANT_MethodType: -- { -- int ref_i = scratch_cp->method_type_index_at(scratch_i); -- int new_ref_i = find_or_append_indirect_entry(scratch_cp, ref_i, merge_cp_p, -- merge_cp_length_p, THREAD); -- if (new_ref_i != ref_i) { -- RC_TRACE(0x00080000, -- ("MethodType entry@%d ref_index change: %d to %d", -- *merge_cp_length_p, ref_i, new_ref_i)); -- } -- (*merge_cp_p)->method_type_index_at_put(*merge_cp_length_p, new_ref_i); -- if (scratch_i != *merge_cp_length_p) { -- // The new entry in *merge_cp_p is at a different index than -- // the new entry in scratch_cp so we need to map the index values. -- map_index(scratch_cp, scratch_i, *merge_cp_length_p); -- } -- (*merge_cp_length_p)++; -- } break; ++ } ++ ++ if (RC_TRACE_ENABLED(0x00000008)) { ++ if (new_class->super() != NULL) { ++ RC_TRACE(0x00000008, ("Super class is %s", ++ new_class->super()->klass_part()->name()->as_C_string())); + } - -- // this is an indirect CP entry so it needs special handling -- case JVM_CONSTANT_MethodHandle: -- { -- int ref_kind = scratch_cp->method_handle_ref_kind_at(scratch_i); -- int ref_i = scratch_cp->method_handle_index_at(scratch_i); -- int new_ref_i = find_or_append_indirect_entry(scratch_cp, ref_i, merge_cp_p, -- merge_cp_length_p, THREAD); -- if (new_ref_i != ref_i) { -- RC_TRACE(0x00080000, -- ("MethodHandle entry@%d ref_index change: %d to %d", -- *merge_cp_length_p, ref_i, new_ref_i)); -- } -- (*merge_cp_p)->method_handle_index_at_put(*merge_cp_length_p, ref_kind, new_ref_i); - if (scratch_i != *merge_cp_length_p) { - // The new entry in *merge_cp_p is at a different index than - // the new entry in scratch_cp so we need to map the index values. - map_index(scratch_cp, scratch_i, *merge_cp_length_p); -- } -- (*merge_cp_length_p)++; -- } break; -- ++ } ++ ++#ifdef ASSERT ++ assert(new_class->super() == NULL || new_class->super()->klass_part()->new_version() == NULL, "Super klass must be newest version!"); ++ ++ the_class->vtable()->verify(tty); ++ new_class->vtable()->verify(tty); ++#endif ++ ++ RC_TRACE(0x00000002, ("Verification done!")); ++ ++ if (i == all_affected_klasses.length() - 1) { ++ ++ // This was the last class processed => check if additional classes have been loaded in the meantime ++ ++ RC_TIMER_STOP(_timer_prologue); ++ lock_threads(); ++ RC_TIMER_START(_timer_prologue); ++ ++ for (int j=0; jklass_part()->subklass(); ++ Klass *cur_klass = initial_subklass; ++ while(cur_klass != NULL) { ++ ++ if(cur_klass->oop_is_instance() && cur_klass->is_newest_version()) { ++ instanceKlassHandle handle(THREAD, cur_klass->as_klassOop()); ++ if (!all_affected_klasses.contains(handle)) { ++ ++ int k = i + 1; ++ for (; kis_subtype_of(cur_klass->as_klassOop())) { ++ break; ++ } ++ } ++ all_affected_klasses.insert_before(k, handle); ++ RC_TRACE(0x00000002, ("Adding newly loaded class to affected classes: %s", ++ cur_klass->name()->as_C_string())); ++ } ++ } ++ ++ cur_klass = cur_klass->next_sibling(); ++ } + } +- (*merge_cp_length_p)++; +- } break; +- +- // this is an indirect CP entry so it needs special handling +- case JVM_CONSTANT_MethodType: +- { +- int ref_i = scratch_cp->method_type_index_at(scratch_i); +- int new_ref_i = find_or_append_indirect_entry(scratch_cp, ref_i, merge_cp_p, +- merge_cp_length_p, THREAD); +- if (new_ref_i != ref_i) { +- RC_TRACE(0x00080000, +- ("MethodType entry@%d ref_index change: %d to %d", +- *merge_cp_length_p, ref_i, new_ref_i)); +- } +- (*merge_cp_p)->method_type_index_at_put(*merge_cp_length_p, new_ref_i); +- if (scratch_i != *merge_cp_length_p) { +- // The new entry in *merge_cp_p is at a different index than +- // the new entry in scratch_cp so we need to map the index values. +- map_index(scratch_cp, scratch_i, *merge_cp_length_p); +- } +- (*merge_cp_length_p)++; +- } break; +- +- // this is an indirect CP entry so it needs special handling +- case JVM_CONSTANT_MethodHandle: +- { +- int ref_kind = scratch_cp->method_handle_ref_kind_at(scratch_i); +- int ref_i = scratch_cp->method_handle_index_at(scratch_i); +- int new_ref_i = find_or_append_indirect_entry(scratch_cp, ref_i, merge_cp_p, +- merge_cp_length_p, THREAD); +- if (new_ref_i != ref_i) { +- RC_TRACE(0x00080000, +- ("MethodHandle entry@%d ref_index change: %d to %d", +- *merge_cp_length_p, ref_i, new_ref_i)); +- } +- (*merge_cp_p)->method_handle_index_at_put(*merge_cp_length_p, ref_kind, new_ref_i); +- if (scratch_i != *merge_cp_length_p) { +- // The new entry in *merge_cp_p is at a different index than +- // the new entry in scratch_cp so we need to map the index values. +- map_index(scratch_cp, scratch_i, *merge_cp_length_p); +- } +- (*merge_cp_length_p)++; +- } break; +- - // this is an indirect CP entry so it needs special handling - case JVM_CONSTANT_InvokeDynamic: - { @@ -5453,29 +5664,16 @@ index 606be1c..ef4f380 100644 - // Found a matching entry somewhere else in *merge_cp_p so just need a mapping entry. - new_ref_i = found_i; - map_index(scratch_cp, ref_i, found_i); -+ if (ex_name == vmSymbols::java_lang_UnsupportedClassVersionError()) { -+ return JVMTI_ERROR_UNSUPPORTED_VERSION; -+ } else if (ex_name == vmSymbols::java_lang_ClassFormatError()) { -+ return JVMTI_ERROR_INVALID_CLASS_FORMAT; -+ } else if (ex_name == vmSymbols::java_lang_ClassCircularityError()) { -+ return JVMTI_ERROR_CIRCULAR_CLASS_DEFINITION; -+ } else if (ex_name == vmSymbols::java_lang_NoClassDefFoundError()) { -+ // The message will be "XXX (wrong name: YYY)" -+ return JVMTI_ERROR_NAMES_DONT_MATCH; -+ } else if (ex_name == vmSymbols::java_lang_OutOfMemoryError()) { -+ return JVMTI_ERROR_OUT_OF_MEMORY; - } else { +- } else { - // no match found so we have to append this entry to *merge_cp_p - append_entry(scratch_cp, ref_i, merge_cp_p, merge_cp_length_p, THREAD); - // The above call to append_entry() can only append one entry - // so the post call query of *merge_cp_length_p is only for - // the sake of consistency. - new_ref_i = *merge_cp_length_p - 1; -+ // Just in case more exceptions can be thrown.. -+ return JVMTI_ERROR_FAILS_VERIFICATION; - } - } - +- } +- } +- - return new_ref_i; -} // end find_or_append_indirect_entry() - @@ -5569,9 +5767,16 @@ index 606be1c..ef4f380 100644 - RC_TRACE_WITH_THREAD(0x00040000, THREAD, - ("operands_index_map[%d]: old=%d new=%d", count, i, value)); - count++; -- } -- } -- } ++ ++ int new_count = all_affected_klasses.length() - 1 - i; ++ if (new_count != 0) { ++ ++ unlock_threads(); ++ RC_TRACE(0x00000001, ("Found new number of affected classes: %d", ++ new_count)); + } + } + } - // Clean-up - _operands_index_map_p = NULL; - _operands_cur_length = 0; @@ -5593,335 +5798,88 @@ index 606be1c..ef4f380 100644 - save = scratch_class->get_method_default_annotations_of(i); - scratch_class->set_method_default_annotations_of(i, scratch_class->get_method_default_annotations_of(j)); - scratch_class->set_method_default_annotations_of(j, save); -+ return JVMTI_ERROR_NONE; - } - -+// Loads all new class versions and stores the instanceKlass handles in an array. -+jvmtiError VM_RedefineClasses::load_new_class_versions(TRAPS) { - --jvmtiError VM_RedefineClasses::compare_and_normalize_class_versions( -- instanceKlassHandle the_class, -- instanceKlassHandle scratch_class) { -+ ResourceMark rm(THREAD); -+ -+ RC_TRACE(0x00000001, ("===================================================================")); -+ RC_TRACE(0x00000001, ("load new class versions (%d)", -+ _class_count)); + -+ // Retrieve an array of all classes that need to be redefined -+ GrowableArray all_affected_klasses; -+ jvmtiError err = find_sorted_affected_classes(&all_affected_klasses); -+ if (err != JVMTI_ERROR_NONE) { -+ RC_TRACE(0x00000001, ("Error finding sorted affected classes: %d", -+ (int)err)); -+ return err; ++ if (result != JVMTI_ERROR_NONE) { ++ rollback(); ++ return result; + } + -+ -+ JvmtiThreadState *state = JvmtiThreadState::state_for(JavaThread::current()); -+ -+ _max_redefinition_flags = Klass::NoRedefinition; -+ jvmtiError result = JVMTI_ERROR_NONE; -+ ++ RC_TIMER_STOP(_timer_prologue); ++ RC_TIMER_START(_timer_class_linking); ++ // Link and verify new classes _after_ all classes have been updated in the system dictionary! + for (int i=0; inew_version()); ++ ++ RC_TRACE(0x00000002, ("Linking class %d/%d %s", ++ i, ++ all_affected_klasses.length(), + the_class->name()->as_C_string())); ++ new_class->link_class(THREAD); + -+ the_class->link_class(THREAD); + result = check_exception(); + if (result != JVMTI_ERROR_NONE) break; ++ } ++ RC_TIMER_STOP(_timer_class_linking); ++ RC_TIMER_START(_timer_prologue); + -+ // Find new class bytes -+ const unsigned char* class_bytes; -+ jint class_byte_count; -+ jvmtiError error; -+ jboolean not_changed; -+ if ((error = find_class_bytes(the_class, &class_bytes, &class_byte_count, ¬_changed)) != JVMTI_ERROR_NONE) { -+ RC_TRACE(0x00000001, ("Error finding class bytes: %d", -+ (int)error)); -+ result = error; -+ break; -+ } -+ assert(class_bytes != NULL && class_byte_count != 0, "Class bytes defined at this point!"); ++ if (result != JVMTI_ERROR_NONE) { ++ rollback(); ++ return result; ++ } + ++ RC_TRACE(0x00000002, ("All classes loaded!")); + -+ // Set redefined class handle in JvmtiThreadState class. -+ // This redefined class is sent to agent event handler for class file -+ // load hook event. -+ state->set_class_being_redefined(&the_class, _class_load_kind); ++#ifdef ASSERT ++ for (int i=0; inew_version() != NULL, "Must have been redefined"); ++ instanceKlassHandle new_version = instanceKlassHandle(THREAD, the_class->new_version()); ++ assert(new_version->new_version() == NULL, "Must be newest version"); + -+ RC_TRACE(0x00000002, ("Before resolving from stream")); ++ if (!(new_version->super() == NULL || new_version->super()->klass_part()->new_version() == NULL)) { ++ new_version()->print(); ++ new_version->super()->print(); ++ } ++ assert(new_version->super() == NULL || new_version->super()->klass_part()->new_version() == NULL, "Super class must be newest version"); ++ } + -+ RC_TIMER_STOP(_timer_prologue); -+ RC_TIMER_START(_timer_class_loading); ++ SystemDictionary::classes_do(check_class, THREAD); + ++#endif + -+ // Parse the stream. -+ Handle the_class_loader(THREAD, the_class->class_loader()); -+ Handle protection_domain(THREAD, the_class->protection_domain()); -+ Symbol* the_class_sym = the_class->name(); -+ ClassFileStream st((u1*) class_bytes, class_byte_count, (char *)"__VM_RedefineClasses__"); -+ instanceKlassHandle new_class(THREAD, SystemDictionary::resolve_from_stream(the_class_sym, -+ the_class_loader, -+ protection_domain, -+ &st, -+ true, -+ the_class, -+ THREAD)); ++ RC_TRACE(0x00000001, ("Finished verification!")); ++ return JVMTI_ERROR_NONE; + } + +- +-jvmtiError VM_RedefineClasses::compare_and_normalize_class_versions( +- instanceKlassHandle the_class, +- instanceKlassHandle scratch_class) { ++void VM_RedefineClasses::lock_threads() { + -+ not_changed = false; ++ RC_TIMER_START(_timer_wait_for_locks); + -+ RC_TIMER_STOP(_timer_class_loading); -+ RC_TIMER_START(_timer_prologue); + -+ RC_TRACE(0x00000002, ("After resolving class from stream!")); -+ // Clear class_being_redefined just to be sure. -+ state->clear_class_being_redefined(); ++ JavaThread *javaThread = Threads::first(); ++ while (javaThread != NULL) { ++ if (javaThread->is_Compiler_thread() && javaThread != Thread::current()) { ++ CompilerThread *compilerThread = (CompilerThread *)javaThread; ++ compilerThread->set_should_bailout(true); ++ } ++ javaThread = javaThread->next(); ++ } + -+ result = check_exception(); -+ if (result != JVMTI_ERROR_NONE) break; -+ -+#ifdef ASSERT -+ -+ assert(new_class() != NULL, "Class could not be loaded!"); -+ assert(new_class() != the_class(), "must be different"); -+ assert(new_class->new_version() == NULL && new_class->old_version() != NULL, ""); -+ -+ -+ objArrayOop k_interfaces = new_class->local_interfaces(); -+ for (int j=0; jlength(); j++) { -+ assert(((klassOop)k_interfaces->obj_at(j))->klass_part()->is_newest_version(), "just checking"); -+ } -+ -+ if (!THREAD->is_Compiler_thread()) { -+ -+ RC_TRACE(0x00000002, ("name=%s loader="INTPTR_FORMAT" protection_domain="INTPTR_FORMAT" ", -+ the_class->name()->as_C_string(), -+ (address)(the_class->class_loader()), -+ (address)(the_class->protection_domain()))); -+ // If we are on the compiler thread, we must not try to resolve a class. -+ klassOop systemLookup = SystemDictionary::resolve_or_null(the_class->name(), the_class->class_loader(), the_class->protection_domain(), THREAD); -+ -+ if (systemLookup != NULL) { -+ assert(systemLookup == new_class->old_version(), "Old class must be in system dictionary!"); -+ -+ -+ Klass *subklass = new_class()->klass_part()->subklass(); -+ while (subklass != NULL) { -+ assert(subklass->new_version() == NULL, "Most recent version of class!"); -+ subklass = subklass->next_sibling(); -+ } -+ } else { -+ // This can happen for reflection generated classes.. ? -+ CLEAR_PENDING_EXCEPTION; -+ } -+ } -+ -+#endif -+ -+ if (RC_TRACE_ENABLED(0x00000001)) { -+ if (new_class->layout_helper() != the_class->layout_helper()) { -+ RC_TRACE(0x00000001, ("Instance size change for class %s: new=%d old=%d", -+ new_class->name()->as_C_string(), -+ new_class->layout_helper(), -+ the_class->layout_helper())); -+ } -+ } -+ -+ // Set the new version of the class -+ new_class->set_revision_number(_revision_number); -+ new_class->set_redefinition_index(i); -+ the_class->set_new_version(new_class()); -+ _new_classes->append(new_class); -+ -+ assert(new_class->new_version() == NULL, ""); -+ -+ int redefinition_flags = Klass::NoRedefinition; -+ -+ if (not_changed) { -+ redefinition_flags = Klass::NoRedefinition; -+ } else if (AllowAdvancedClassRedefinition) { -+ redefinition_flags = calculate_redefinition_flags(new_class); -+ } else { -+ jvmtiError allowed = check_redefinition_allowed(new_class); -+ if (allowed != JVMTI_ERROR_NONE) { -+ RC_TRACE(0x00000001, ("Error redefinition not allowed!")); -+ result = allowed; -+ break; -+ } -+ redefinition_flags = Klass::ModifyClass; -+ } -+ -+ if (new_class->super() != NULL) { -+ redefinition_flags = redefinition_flags | new_class->super()->klass_part()->redefinition_flags(); -+ } -+ -+ for (int j=0; jlocal_interfaces()->length(); j++) { -+ redefinition_flags = redefinition_flags | ((klassOop)new_class->local_interfaces()->obj_at(j))->klass_part()->redefinition_flags(); -+ } -+ -+ new_class->set_redefinition_flags(redefinition_flags); -+ -+ _max_redefinition_flags = _max_redefinition_flags | redefinition_flags; -+ -+ if ((redefinition_flags & Klass::ModifyInstances) != 0) { -+ // TODO: Check if watch access flags of static fields are updated correctly. -+ calculate_instance_update_information(_new_classes->at(i)()); -+ } else { -+ assert(new_class->layout_helper() >> 1 == new_class->old_version()->klass_part()->layout_helper() >> 1, "must be equal"); -+ assert(new_class->fields()->length() == ((instanceKlass*)new_class->old_version()->klass_part())->fields()->length(), "must be equal"); -+ -+ fieldDescriptor fd_new; -+ fieldDescriptor fd_old; -+ for (JavaFieldStream fs(new_class); !fs.done(); fs.next()) { -+ fd_new.initialize(new_class(), fs.index()); -+ fd_old.initialize(new_class->old_version(), fs.index()); -+ transfer_special_access_flags(&fd_old, &fd_new); -+ } -+ } -+ -+ if (RC_TRACE_ENABLED(0x00000008)) { -+ if (new_class->super() != NULL) { -+ RC_TRACE(0x00000008, ("Super class is %s", -+ new_class->super()->klass_part()->name()->as_C_string())); -+ } -+ } -+ -+#ifdef ASSERT -+ assert(new_class->super() == NULL || new_class->super()->klass_part()->new_version() == NULL, "Super klass must be newest version!"); -+ -+ the_class->vtable()->verify(tty); -+ new_class->vtable()->verify(tty); -+#endif -+ -+ RC_TRACE(0x00000002, ("Verification done!")); -+ -+ if (i == all_affected_klasses.length() - 1) { -+ -+ // This was the last class processed => check if additional classes have been loaded in the meantime -+ -+ RC_TIMER_STOP(_timer_prologue); -+ lock_threads(); -+ RC_TIMER_START(_timer_prologue); -+ -+ for (int j=0; jklass_part()->subklass(); -+ Klass *cur_klass = initial_subklass; -+ while(cur_klass != NULL) { -+ -+ if(cur_klass->oop_is_instance() && cur_klass->is_newest_version()) { -+ instanceKlassHandle handle(THREAD, cur_klass->as_klassOop()); -+ if (!all_affected_klasses.contains(handle)) { -+ -+ int k = i + 1; -+ for (; kis_subtype_of(cur_klass->as_klassOop())) { -+ break; -+ } -+ } -+ all_affected_klasses.insert_before(k, handle); -+ RC_TRACE(0x00000002, ("Adding newly loaded class to affected classes: %s", -+ cur_klass->name()->as_C_string())); -+ } -+ } -+ -+ cur_klass = cur_klass->next_sibling(); -+ } -+ } -+ -+ int new_count = all_affected_klasses.length() - 1 - i; -+ if (new_count != 0) { -+ -+ unlock_threads(); -+ RC_TRACE(0x00000001, ("Found new number of affected classes: %d", -+ new_count)); -+ } -+ } -+ } -+ -+ if (result != JVMTI_ERROR_NONE) { -+ rollback(); -+ return result; -+ } -+ -+ RC_TIMER_STOP(_timer_prologue); -+ RC_TIMER_START(_timer_class_linking); -+ // Link and verify new classes _after_ all classes have been updated in the system dictionary! -+ for (int i=0; inew_version()); -+ -+ RC_TRACE(0x00000002, ("Linking class %d/%d %s", -+ i, -+ all_affected_klasses.length(), -+ the_class->name()->as_C_string())); -+ new_class->link_class(THREAD); -+ -+ result = check_exception(); -+ if (result != JVMTI_ERROR_NONE) break; -+ } -+ RC_TIMER_STOP(_timer_class_linking); -+ RC_TIMER_START(_timer_prologue); -+ -+ if (result != JVMTI_ERROR_NONE) { -+ rollback(); -+ return result; -+ } -+ -+ RC_TRACE(0x00000002, ("All classes loaded!")); -+ -+#ifdef ASSERT -+ for (int i=0; inew_version() != NULL, "Must have been redefined"); -+ instanceKlassHandle new_version = instanceKlassHandle(THREAD, the_class->new_version()); -+ assert(new_version->new_version() == NULL, "Must be newest version"); -+ -+ if (!(new_version->super() == NULL || new_version->super()->klass_part()->new_version() == NULL)) { -+ new_version()->print(); -+ new_version->super()->print(); -+ } -+ assert(new_version->super() == NULL || new_version->super()->klass_part()->new_version() == NULL, "Super class must be newest version"); -+ } -+ -+ SystemDictionary::classes_do(check_class, THREAD); -+ -+#endif -+ -+ RC_TRACE(0x00000001, ("Finished verification!")); -+ return JVMTI_ERROR_NONE; -+} -+ -+void VM_RedefineClasses::lock_threads() { -+ -+ RC_TIMER_START(_timer_wait_for_locks); -+ -+ -+ JavaThread *javaThread = Threads::first(); -+ while (javaThread != NULL) { -+ if (javaThread->is_Compiler_thread() && javaThread != Thread::current()) { -+ CompilerThread *compilerThread = (CompilerThread *)javaThread; -+ compilerThread->set_should_bailout(true); -+ } -+ javaThread = javaThread->next(); -+ } -+ -+ int cnt = 0; -+ javaThread = Threads::first(); -+ while (javaThread != NULL) { -+ if (javaThread->is_Compiler_thread() && javaThread != Thread::current()) { -+ CompilerThread *compilerThread = (CompilerThread *)javaThread; -+ compilerThread->compilation_mutex()->lock(); -+ cnt++; -+ } -+ javaThread = javaThread->next(); -+ } ++ int cnt = 0; ++ javaThread = Threads::first(); ++ while (javaThread != NULL) { ++ if (javaThread->is_Compiler_thread() && javaThread != Thread::current()) { ++ CompilerThread *compilerThread = (CompilerThread *)javaThread; ++ compilerThread->compilation_mutex()->lock(); ++ cnt++; ++ } ++ javaThread = javaThread->next(); ++ } + + RC_TRACE(0x00000002, ("Locked %d compiler threads", cnt)); + @@ -6070,12 +6028,11 @@ index 606be1c..ef4f380 100644 ++oi; // advance to next old method break; default: -@@ -833,2120 +880,2265 @@ +@@ -833,2121 +880,2266 @@ return JVMTI_ERROR_NONE; } -+int VM_RedefineClasses::calculate_redefinition_flags(instanceKlassHandle new_class) { - +- -// Find new constant pool index value for old constant pool index value -// by seaching the index map. Returns zero (0) if there is no mapped -// value for the old constant pool index. @@ -6083,6 +6040,8 @@ index 606be1c..ef4f380 100644 - if (_index_map_count == 0) { - // map is empty so nothing can be found - return 0; ++int VM_RedefineClasses::calculate_redefinition_flags(instanceKlassHandle new_class) { ++ + int result = Klass::NoRedefinition; + + @@ -6104,29 +6063,51 @@ index 606be1c..ef4f380 100644 - // not happen in regular constant pool merging use, but it can - // happen if a corrupt annotation is processed. - return 0; -- } + int i; - -- int value = _index_map_p->at(old_index); -- if (value == -1) { -- // the old_index is not mapped -- return 0; -- } ++ + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Check superclasses + assert(new_class->super() == NULL || new_class->super()->klass_part()->is_newest_version(), ""); + if (the_class->super() != new_class->super()) { + // Super class changed - -- return value; --} // end find_new_index() ++ + klassOop cur_klass = the_class->super(); + while (cur_klass != NULL) { + if (!new_class->is_subclass_of(cur_klass->klass_part()->newest_version())) { + RC_TRACE(0x00000002, ("Removed super class %s", + cur_klass->klass_part()->name()->as_C_string())); + result = result | Klass::RemoveSuperType | Klass::ModifyInstances | Klass::ModifyClass; - ++ ++ if (!cur_klass->klass_part()->has_subtype_changed()) { ++ RC_TRACE(0x00000002, ("Subtype changed of class %s", ++ cur_klass->klass_part()->name()->as_C_string())); ++ cur_klass->klass_part()->set_subtype_changed(true); ++ } ++ } ++ ++ cur_klass = cur_klass->klass_part()->super(); ++ } ++ ++ cur_klass = new_class->super(); ++ while (cur_klass != NULL) { ++ if (!the_class->is_subclass_of(cur_klass->klass_part()->old_version())) { ++ RC_TRACE(0x00000002, ("Added super class %s", ++ cur_klass->klass_part()->name()->as_C_string())); ++ result = result | Klass::ModifyClass | Klass::ModifyInstances; ++ } ++ cur_klass = cur_klass->klass_part()->super(); ++ } + } + +- int value = _index_map_p->at(old_index); +- if (value == -1) { +- // the old_index is not mapped +- return 0; +- } +- +- return value; +-} // end find_new_index() +- - -// Find new bootstrap specifier index value for old bootstrap specifier index -// value by seaching the index map. Returns zero (-1) if there is no mapped @@ -6280,8 +6261,24 @@ index 606be1c..ef4f380 100644 - return JVMTI_ERROR_OUT_OF_MEMORY; - } else { // Just in case more exceptions can be thrown.. - return JVMTI_ERROR_FAILS_VERIFICATION; -- } -- } ++ ////////////////////////////////////////////////////////////////////////////////////////////////////////// ++ // Check interfaces ++ ++ // Interfaces removed? ++ objArrayOop old_interfaces = the_class->transitive_interfaces(); ++ for (i = 0; ilength(); i++) { ++ instanceKlassHandle old_interface((klassOop)old_interfaces->obj_at(i)); ++ if (!new_class->implements_interface_any_version(old_interface())) { ++ result = result | Klass::RemoveSuperType | Klass::ModifyClass; ++ RC_TRACE(0x00000002, ("Removed interface %s", ++ old_interface->name()->as_C_string())); ++ ++ if (!old_interface->has_subtype_changed()) { ++ RC_TRACE(0x00000002, ("Subtype changed of interface %s", ++ old_interface->name()->as_C_string())); ++ old_interface->set_subtype_changed(true); + } + } - - // Ensure class is linked before redefine - if (!the_class->is_linked()) { @@ -6294,16 +6291,137 @@ index 606be1c..ef4f380 100644 - CLEAR_PENDING_EXCEPTION; - if (ex_name == vmSymbols::java_lang_OutOfMemoryError()) { - return JVMTI_ERROR_OUT_OF_MEMORY; -- } else { ++ } ++ ++ // Interfaces added? ++ objArrayOop new_interfaces = new_class->transitive_interfaces(); ++ for (i = 0; ilength(); i++) { ++ if (!the_class->implements_interface_any_version((klassOop)new_interfaces->obj_at(i))) { ++ result = result | Klass::ModifyClass; ++ RC_TRACE(0x00000002, ("Added interface %s", ++ ((klassOop)new_interfaces->obj_at(i))->klass_part()->name()->as_C_string())); ++ } ++ } ++ ++ ++ // Check whether class modifiers are the same. ++ jushort old_flags = (jushort) the_class->access_flags().get_flags(); ++ jushort new_flags = (jushort) new_class->access_flags().get_flags(); ++ if (old_flags != new_flags) { ++ // TODO (tw): Can this have any effects? ++ } ++ ++ // Check if the number, names, types and order of fields declared in these classes ++ // are the same. ++ JavaFieldStream old_fs(the_class); ++ JavaFieldStream new_fs(new_class); ++ for (; !old_fs.done() && !new_fs.done(); old_fs.next(), new_fs.next()) { ++ // access ++ old_flags = old_fs.access_flags().as_short(); ++ new_flags = new_fs.access_flags().as_short(); ++ if ((old_flags ^ new_flags) & JVM_RECOGNIZED_FIELD_MODIFIERS) { ++ // (tw) Can this have any effects? ++ } ++ // offset ++ if (old_fs.offset() != new_fs.offset()) { ++ result = result | Klass::ModifyInstances; ++ } ++ // name and signature ++ Symbol* name_sym1 = the_class->constants()->symbol_at(old_fs.name_index()); ++ Symbol* sig_sym1 = the_class->constants()->symbol_at(old_fs.signature_index()); ++ Symbol* name_sym2 = new_class->constants()->symbol_at(new_fs.name_index()); ++ Symbol* sig_sym2 = new_class->constants()->symbol_at(new_fs.signature_index()); ++ if (name_sym1 != name_sym2 || sig_sym1 != sig_sym2) { ++ result = result | Klass::ModifyInstances; ++ } ++ } ++ ++ if (!old_fs.done() || !new_fs.done()) { ++ result = result | Klass::ModifyInstances; ++ } ++ ++ // Do a parallel walk through the old and new methods. Detect ++ // cases where they match (exist in both), have been added in ++ // the new methods, or have been deleted (exist only in the ++ // old methods). The class file parser places methods in order ++ // by method name, but does not order overloaded methods by ++ // signature. In order to determine what fate befell the methods, ++ // this code places the overloaded new methods that have matching ++ // old methods in the same order as the old methods and places ++ // new overloaded methods at the end of overloaded methods of ++ // that name. The code for this order normalization is adapted ++ // from the algorithm used in instanceKlass::find_method(). ++ // Since we are swapping out of order entries as we find them, ++ // we only have to search forward through the overloaded methods. ++ // Methods which are added and have the same name as an existing ++ // method (but different signature) will be put at the end of ++ // the methods with that name, and the name mismatch code will ++ // handle them. ++ objArrayHandle k_old_methods(the_class->methods()); ++ objArrayHandle k_new_methods(new_class->methods()); ++ int n_old_methods = k_old_methods->length(); ++ int n_new_methods = k_new_methods->length(); ++ ++ int ni = 0; ++ int oi = 0; ++ while (true) { ++ methodOop k_old_method; ++ methodOop k_new_method; ++ enum { matched, added, deleted, undetermined } method_was = undetermined; ++ ++ if (oi >= n_old_methods) { ++ if (ni >= n_new_methods) { ++ break; // we've looked at everything, done ++ } ++ // New method at the end ++ k_new_method = (methodOop) k_new_methods->obj_at(ni); ++ method_was = added; ++ } else if (ni >= n_new_methods) { ++ // Old method, at the end, is deleted ++ k_old_method = (methodOop) k_old_methods->obj_at(oi); ++ method_was = deleted; ++ } else { ++ // There are more methods in both the old and new lists ++ k_old_method = (methodOop) k_old_methods->obj_at(oi); ++ k_new_method = (methodOop) k_new_methods->obj_at(ni); ++ if (k_old_method->name() != k_new_method->name()) { ++ // Methods are sorted by method name, so a mismatch means added ++ // or deleted ++ if (k_old_method->name()->fast_compare(k_new_method->name()) > 0) { ++ method_was = added; + } else { - return JVMTI_ERROR_INTERNAL; -+ if (!cur_klass->klass_part()->has_subtype_changed()) { -+ RC_TRACE(0x00000002, ("Subtype changed of class %s", -+ cur_klass->klass_part()->name()->as_C_string())); -+ cur_klass->klass_part()->set_subtype_changed(true); ++ method_was = deleted; ++ } ++ } else if (k_old_method->signature() == k_new_method->signature()) { ++ // Both the name and signature match ++ method_was = matched; ++ } else { ++ // The name matches, but the signature doesn't, which means we have to ++ // search forward through the new overloaded methods. ++ int nj; // outside the loop for post-loop check ++ for (nj = ni + 1; nj < n_new_methods; nj++) { ++ methodOop m = (methodOop)k_new_methods->obj_at(nj); ++ if (k_old_method->name() != m->name()) { ++ // reached another method name so no more overloaded methods ++ method_was = deleted; ++ break; ++ } ++ if (k_old_method->signature() == m->signature()) { ++ // found a match so swap the methods ++ k_new_methods->obj_at_put(ni, m); ++ k_new_methods->obj_at_put(nj, k_new_method); ++ k_new_method = m; ++ method_was = matched; ++ break; ++ } ++ } ++ ++ if (nj >= n_new_methods) { ++ // reached the end without a match; so method was deleted ++ method_was = deleted; } } -+ -+ cur_klass = cur_klass->klass_part()->super(); } - // Do the validity checks in compare_and_normalize_class_versions() @@ -6315,10 +6433,18 @@ index 606be1c..ef4f380 100644 - scratch_class); - if (res != JVMTI_ERROR_NONE) { - return res; -- } ++ switch (method_was) { ++ case matched: ++ // methods match, be sure modifiers do too ++ old_flags = (jushort) k_old_method->access_flags().get_flags(); ++ new_flags = (jushort) k_new_method->access_flags().get_flags(); ++ if ((old_flags ^ new_flags) & ~(JVM_ACC_NATIVE)) { ++ // (tw) Can this have any effects? Probably yes on vtables? ++ result = result | Klass::ModifyClass; + } - - // verify what the caller passed us -- { + { - // The bug 6214132 caused the verification to fail. - // Information about the_class and scratch_class is temporarily - // recorded into jvmtiThreadState. This data is used to redirect @@ -6341,26 +6467,54 @@ index 606be1c..ef4f380 100644 - } else { - // tell the caller the bytecodes are bad - return JVMTI_ERROR_FAILS_VERIFICATION; -+ cur_klass = new_class->super(); -+ while (cur_klass != NULL) { -+ if (!the_class->is_subclass_of(cur_klass->klass_part()->old_version())) { -+ RC_TRACE(0x00000002, ("Added super class %s", -+ cur_klass->klass_part()->name()->as_C_string())); -+ result = result | Klass::ModifyClass | Klass::ModifyInstances; ++ u2 new_num = k_new_method->method_idnum(); ++ u2 old_num = k_old_method->method_idnum(); ++ if (new_num != old_num) { ++ methodOop idnum_owner = new_class->method_with_idnum(old_num); ++ if (idnum_owner != NULL) { ++ // There is already a method assigned this idnum -- switch them ++ idnum_owner->set_method_idnum(new_num); ++ } ++ k_new_method->set_method_idnum(old_num); ++ RC_TRACE(0x00000002, ("swapping idnum of new and old method %d / %d!", ++ new_num, ++ old_num)); ++ // swap_all_method_annotations(old_num, new_num, new_class); } -- } + } - - res = merge_cp_and_rewrite(the_class, scratch_class, THREAD); - if (res != JVMTI_ERROR_NONE) { - return res; -- } ++ RC_TRACE(0x00008000, ("Method matched: new: %s [%d] == old: %s [%d]", ++ k_new_method->name_and_sig_as_C_string(), ni, ++ k_old_method->name_and_sig_as_C_string(), oi)); ++ // advance to next pair of methods ++ ++oi; ++ ++ni; ++ break; ++ case added: ++ // method added, see if it is OK ++ new_flags = (jushort) k_new_method->access_flags().get_flags(); ++ if ((new_flags & JVM_ACC_PRIVATE) == 0 ++ // hack: private should be treated as final, but alas ++ || (new_flags & (JVM_ACC_FINAL|JVM_ACC_STATIC)) == 0 ++ ) { ++ // new methods must be private ++ result = result | Klass::ModifyClass; + } - - if (VerifyMergedCPBytecodes) { - // verify what we have done during constant pool merging - { - RedefineVerifyMark rvm(&the_class, &scratch_class, state); - Verifier::verify(scratch_class, Verifier::ThrowException, true, THREAD); -- } ++ { ++ u2 num = the_class->next_method_idnum(); ++ if (num == constMethodOopDesc::UNSET_IDNUM) { ++ // cannot add any more methods ++ result = result | Klass::ModifyClass; + } - - if (HAS_PENDING_EXCEPTION) { - Symbol* ex_name = PENDING_EXCEPTION->klass()->klass_part()->name(); @@ -6374,2815 +6528,1472 @@ index 606be1c..ef4f380 100644 - } else { - // tell the caller that constant pool merging screwed up - return JVMTI_ERROR_INTERNAL; -- } -- } -- } -- -- Rewriter::rewrite(scratch_class, THREAD); -- if (!HAS_PENDING_EXCEPTION) { -- Rewriter::relocate_and_link(scratch_class, THREAD); -- } -- if (HAS_PENDING_EXCEPTION) { -- Symbol* ex_name = PENDING_EXCEPTION->klass()->klass_part()->name(); -- CLEAR_PENDING_EXCEPTION; -- if (ex_name == vmSymbols::java_lang_OutOfMemoryError()) { -- return JVMTI_ERROR_OUT_OF_MEMORY; -- } else { -- return JVMTI_ERROR_INTERNAL; -- } -- } -- -- _scratch_classes[i] = scratch_class; -- -- // RC_TRACE_WITH_THREAD macro has an embedded ResourceMark -- RC_TRACE_WITH_THREAD(0x00000001, THREAD, -- ("loaded name=%s (avail_mem=" UINT64_FORMAT "K)", -- the_class->external_name(), os::available_memory() >> 10)); -- } -- -- return JVMTI_ERROR_NONE; --} -- -- --// Map old_index to new_index as needed. scratch_cp is only needed --// for RC_TRACE() calls. --void VM_RedefineClasses::map_index(constantPoolHandle scratch_cp, -- int old_index, int new_index) { -- if (find_new_index(old_index) != 0) { -- // old_index is already mapped -- return; -- } -- -- if (old_index == new_index) { -- // no mapping is needed -- return; -- } -- -- _index_map_p->at_put(old_index, new_index); -- _index_map_count++; -- -- RC_TRACE(0x00040000, ("mapped tag %d at index %d to %d", -- scratch_cp->tag_at(old_index).value(), old_index, new_index)); --} // end map_index() -- -- --// Map old_index to new_index as needed. --void VM_RedefineClasses::map_operand_index(int old_index, int new_index) { -- if (find_new_operand_index(old_index) != -1) { -- // old_index is already mapped -- return; -- } -- -- if (old_index == new_index) { -- // no mapping is needed -- return; -- } -- -- _operands_index_map_p->at_put(old_index, new_index); -- _operands_index_map_count++; -- -- RC_TRACE(0x00040000, ("mapped bootstrap specifier at index %d to %d", old_index, new_index)); --} // end map_index() -- -- --// Merge old_cp and scratch_cp and return the results of the merge via --// merge_cp_p. The number of entries in *merge_cp_p is returned via --// merge_cp_length_p. The entries in old_cp occupy the same locations --// in *merge_cp_p. Also creates a map of indices from entries in --// scratch_cp to the corresponding entry in *merge_cp_p. Index map --// entries are only created for entries in scratch_cp that occupy a --// different location in *merged_cp_p. --bool VM_RedefineClasses::merge_constant_pools(constantPoolHandle old_cp, -- constantPoolHandle scratch_cp, constantPoolHandle *merge_cp_p, -- int *merge_cp_length_p, TRAPS) { -- -- if (merge_cp_p == NULL) { -- assert(false, "caller must provide scatch constantPool"); -- return false; // robustness -- } -- if (merge_cp_length_p == NULL) { -- assert(false, "caller must provide scatch CP length"); -- return false; // robustness -- } -- // Worst case we need old_cp->length() + scratch_cp()->length(), -- // but the caller might be smart so make sure we have at least -- // the minimum. -- if ((*merge_cp_p)->length() < old_cp->length()) { -- assert(false, "merge area too small"); -- return false; // robustness -- } -- -- RC_TRACE_WITH_THREAD(0x00010000, THREAD, -- ("old_cp_len=%d, scratch_cp_len=%d", old_cp->length(), -- scratch_cp->length())); -- -- { -- // Pass 0: -- // The old_cp is copied to *merge_cp_p; this means that any code -- // using old_cp does not have to change. This work looks like a -- // perfect fit for constantPoolOop::copy_cp_to(), but we need to -- // handle one special case: -- // - revert JVM_CONSTANT_Class to JVM_CONSTANT_UnresolvedClass -- // This will make verification happy. -- -- int old_i; // index into old_cp -- -- // index zero (0) is not used in constantPools -- for (old_i = 1; old_i < old_cp->length(); old_i++) { -- // leave debugging crumb -- jbyte old_tag = old_cp->tag_at(old_i).value(); -- switch (old_tag) { -- case JVM_CONSTANT_Class: -- case JVM_CONSTANT_UnresolvedClass: -- // revert the copy to JVM_CONSTANT_UnresolvedClass -- // May be resolving while calling this so do the same for -- // JVM_CONSTANT_UnresolvedClass (klass_name_at() deals with transition) -- (*merge_cp_p)->unresolved_klass_at_put(old_i, -- old_cp->klass_name_at(old_i)); -- break; -- -- case JVM_CONSTANT_Double: -- case JVM_CONSTANT_Long: -- // just copy the entry to *merge_cp_p, but double and long take -- // two constant pool entries -- constantPoolOopDesc::copy_entry_to(old_cp, old_i, *merge_cp_p, old_i, CHECK_0); -- old_i++; -- break; -- -- default: -- // just copy the entry to *merge_cp_p -- constantPoolOopDesc::copy_entry_to(old_cp, old_i, *merge_cp_p, old_i, CHECK_0); -- break; -- } -- } // end for each old_cp entry -- -- constantPoolOopDesc::copy_operands(old_cp, *merge_cp_p, CHECK_0); -- (*merge_cp_p)->extend_operands(scratch_cp, CHECK_0); -- -- // We don't need to sanity check that *merge_cp_length_p is within -- // *merge_cp_p bounds since we have the minimum on-entry check above. -- (*merge_cp_length_p) = old_i; -- } -- -- // merge_cp_len should be the same as old_cp->length() at this point -- // so this trace message is really a "warm-and-breathing" message. -- RC_TRACE_WITH_THREAD(0x00020000, THREAD, -- ("after pass 0: merge_cp_len=%d", *merge_cp_length_p)); -- -- int scratch_i; // index into scratch_cp -- { -- // Pass 1a: -- // Compare scratch_cp entries to the old_cp entries that we have -- // already copied to *merge_cp_p. In this pass, we are eliminating -- // exact duplicates (matching entry at same index) so we only -- // compare entries in the common indice range. -- int increment = 1; -- int pass1a_length = MIN2(old_cp->length(), scratch_cp->length()); -- for (scratch_i = 1; scratch_i < pass1a_length; scratch_i += increment) { -- switch (scratch_cp->tag_at(scratch_i).value()) { -- case JVM_CONSTANT_Double: -- case JVM_CONSTANT_Long: -- // double and long take two constant pool entries -- increment = 2; -- break; -- -- default: -- increment = 1; -- break; -- } -- -- bool match = scratch_cp->compare_entry_to(scratch_i, *merge_cp_p, -- scratch_i, CHECK_0); -- if (match) { -- // found a match at the same index so nothing more to do -- continue; -- } else if (is_unresolved_class_mismatch(scratch_cp, scratch_i, -- *merge_cp_p, scratch_i)) { -- // The mismatch in compare_entry_to() above is because of a -- // resolved versus unresolved class entry at the same index -- // with the same string value. Since Pass 0 reverted any -- // class entries to unresolved class entries in *merge_cp_p, -- // we go with the unresolved class entry. -- continue; -- } else if (is_unresolved_string_mismatch(scratch_cp, scratch_i, -- *merge_cp_p, scratch_i)) { -- // The mismatch in compare_entry_to() above is because of a -- // resolved versus unresolved string entry at the same index -- // with the same string value. We can live with whichever -- // happens to be at scratch_i in *merge_cp_p. -- continue; -- } -- -- int found_i = scratch_cp->find_matching_entry(scratch_i, *merge_cp_p, -- CHECK_0); -- if (found_i != 0) { -- guarantee(found_i != scratch_i, -- "compare_entry_to() and find_matching_entry() do not agree"); -- -- // Found a matching entry somewhere else in *merge_cp_p so -- // just need a mapping entry. -- map_index(scratch_cp, scratch_i, found_i); -- continue; -- } -- -- // The find_matching_entry() call above could fail to find a match -- // due to a resolved versus unresolved class or string entry situation -- // like we solved above with the is_unresolved_*_mismatch() calls. -- // However, we would have to call is_unresolved_*_mismatch() over -- // all of *merge_cp_p (potentially) and that doesn't seem to be -- // worth the time. -- -- // No match found so we have to append this entry and any unique -- // referenced entries to *merge_cp_p. -- append_entry(scratch_cp, scratch_i, merge_cp_p, merge_cp_length_p, -- CHECK_0); -+ cur_klass = cur_klass->klass_part()->super(); - } - } - -- RC_TRACE_WITH_THREAD(0x00020000, THREAD, -- ("after pass 1a: merge_cp_len=%d, scratch_i=%d, index_map_len=%d", -- *merge_cp_length_p, scratch_i, _index_map_count)); -+ ////////////////////////////////////////////////////////////////////////////////////////////////////////// -+ // Check interfaces - -- if (scratch_i < scratch_cp->length()) { -- // Pass 1b: -- // old_cp is smaller than scratch_cp so there are entries in -- // scratch_cp that we have not yet processed. We take care of -- // those now. -- int increment = 1; -- for (; scratch_i < scratch_cp->length(); scratch_i += increment) { -- switch (scratch_cp->tag_at(scratch_i).value()) { -- case JVM_CONSTANT_Double: -- case JVM_CONSTANT_Long: -- // double and long take two constant pool entries -- increment = 2; -- break; -- -- default: -- increment = 1; -- break; -+ // Interfaces removed? -+ objArrayOop old_interfaces = the_class->transitive_interfaces(); -+ for (i = 0; ilength(); i++) { -+ instanceKlassHandle old_interface((klassOop)old_interfaces->obj_at(i)); -+ if (!new_class->implements_interface_any_version(old_interface())) { -+ result = result | Klass::RemoveSuperType | Klass::ModifyClass; -+ RC_TRACE(0x00000002, ("Removed interface %s", -+ old_interface->name()->as_C_string())); -+ -+ if (!old_interface->has_subtype_changed()) { -+ RC_TRACE(0x00000002, ("Subtype changed of interface %s", -+ old_interface->name()->as_C_string())); -+ old_interface->set_subtype_changed(true); - } -- -- int found_i = -- scratch_cp->find_matching_entry(scratch_i, *merge_cp_p, CHECK_0); -- if (found_i != 0) { -- // Found a matching entry somewhere else in *merge_cp_p so -- // just need a mapping entry. -- map_index(scratch_cp, scratch_i, found_i); -- continue; -- } -- -- // No match found so we have to append this entry and any unique -- // referenced entries to *merge_cp_p. -- append_entry(scratch_cp, scratch_i, merge_cp_p, merge_cp_length_p, -- CHECK_0); - } -- -- RC_TRACE_WITH_THREAD(0x00020000, THREAD, -- ("after pass 1b: merge_cp_len=%d, scratch_i=%d, index_map_len=%d", -- *merge_cp_length_p, scratch_i, _index_map_count)); -- } -- finalize_operands_merge(*merge_cp_p, THREAD); -- -- return true; --} // end merge_constant_pools() -- -- --// Merge constant pools between the_class and scratch_class and --// potentially rewrite bytecodes in scratch_class to use the merged --// constant pool. --jvmtiError VM_RedefineClasses::merge_cp_and_rewrite( -- instanceKlassHandle the_class, instanceKlassHandle scratch_class, -- TRAPS) { -- // worst case merged constant pool length is old and new combined -- int merge_cp_length = the_class->constants()->length() -- + scratch_class->constants()->length(); -- -- constantPoolHandle old_cp(THREAD, the_class->constants()); -- constantPoolHandle scratch_cp(THREAD, scratch_class->constants()); -- -- // Constant pools are not easily reused so we allocate a new one -- // each time. -- // merge_cp is created unsafe for concurrent GC processing. It -- // should be marked safe before discarding it. Even though -- // garbage, if it crosses a card boundary, it may be scanned -- // in order to find the start of the first complete object on the card. -- constantPoolHandle merge_cp(THREAD, -- oopFactory::new_constantPool(merge_cp_length, -- oopDesc::IsUnsafeConc, -- THREAD)); -- int orig_length = old_cp->orig_length(); -- if (orig_length == 0) { -- // This old_cp is an actual original constant pool. We save -- // the original length in the merged constant pool so that -- // merge_constant_pools() can be more efficient. If a constant -- // pool has a non-zero orig_length() value, then that constant -- // pool was created by a merge operation in RedefineClasses. -- merge_cp->set_orig_length(old_cp->length()); -- } else { -- // This old_cp is a merged constant pool from a previous -- // RedefineClasses() calls so just copy the orig_length() -- // value. -- merge_cp->set_orig_length(old_cp->orig_length()); - } - -- ResourceMark rm(THREAD); -- _index_map_count = 0; -- _index_map_p = new intArray(scratch_cp->length(), -1); -- -- _operands_cur_length = constantPoolOopDesc::operand_array_length(old_cp->operands()); -- _operands_index_map_count = 0; -- _operands_index_map_p = new intArray( -- constantPoolOopDesc::operand_array_length(scratch_cp->operands()), -1); -- -- // reference to the cp holder is needed for copy_operands() -- merge_cp->set_pool_holder(scratch_class()); -- bool result = merge_constant_pools(old_cp, scratch_cp, &merge_cp, -- &merge_cp_length, THREAD); -- merge_cp->set_pool_holder(NULL); -- -- if (!result) { -- // The merge can fail due to memory allocation failure or due -- // to robustness checks. -- return JVMTI_ERROR_INTERNAL; -+ // Interfaces added? -+ objArrayOop new_interfaces = new_class->transitive_interfaces(); -+ for (i = 0; ilength(); i++) { -+ if (!the_class->implements_interface_any_version((klassOop)new_interfaces->obj_at(i))) { -+ result = result | Klass::ModifyClass; -+ RC_TRACE(0x00000002, ("Added interface %s", -+ ((klassOop)new_interfaces->obj_at(i))->klass_part()->name()->as_C_string())); -+ } - } - -- RC_TRACE_WITH_THREAD(0x00010000, THREAD, -- ("merge_cp_len=%d, index_map_len=%d", merge_cp_length, _index_map_count)); - -- if (_index_map_count == 0) { -- // there is nothing to map between the new and merged constant pools -+ // Check whether class modifiers are the same. -+ jushort old_flags = (jushort) the_class->access_flags().get_flags(); -+ jushort new_flags = (jushort) new_class->access_flags().get_flags(); -+ if (old_flags != new_flags) { -+ // TODO (tw): Can this have any effects? -+ } - -- if (old_cp->length() == scratch_cp->length()) { -- // The old and new constant pools are the same length and the -- // index map is empty. This means that the three constant pools -- // are equivalent (but not the same). Unfortunately, the new -- // constant pool has not gone through link resolution nor have -- // the new class bytecodes gone through constant pool cache -- // rewriting so we can't use the old constant pool with the new -- // class. -+ // Check if the number, names, types and order of fields declared in these classes -+ // are the same. -+ JavaFieldStream old_fs(the_class); -+ JavaFieldStream new_fs(new_class); -+ for (; !old_fs.done() && !new_fs.done(); old_fs.next(), new_fs.next()) { -+ // access -+ old_flags = old_fs.access_flags().as_short(); -+ new_flags = new_fs.access_flags().as_short(); -+ if ((old_flags ^ new_flags) & JVM_RECOGNIZED_FIELD_MODIFIERS) { -+ // (tw) Can this have any effects? -+ } -+ // offset -+ if (old_fs.offset() != new_fs.offset()) { -+ result = result | Klass::ModifyInstances; -+ } -+ // name and signature -+ Symbol* name_sym1 = the_class->constants()->symbol_at(old_fs.name_index()); -+ Symbol* sig_sym1 = the_class->constants()->symbol_at(old_fs.signature_index()); -+ Symbol* name_sym2 = new_class->constants()->symbol_at(new_fs.name_index()); -+ Symbol* sig_sym2 = new_class->constants()->symbol_at(new_fs.signature_index()); -+ if (name_sym1 != name_sym2 || sig_sym1 != sig_sym2) { -+ result = result | Klass::ModifyInstances; -+ } -+ } - -- merge_cp()->set_is_conc_safe(true); -- merge_cp = constantPoolHandle(); // toss the merged constant pool -- } else if (old_cp->length() < scratch_cp->length()) { -- // The old constant pool has fewer entries than the new constant -- // pool and the index map is empty. This means the new constant -- // pool is a superset of the old constant pool. However, the old -- // class bytecodes have already gone through constant pool cache -- // rewriting so we can't use the new constant pool with the old -- // class. -+ if (!old_fs.done() || !new_fs.done()) { -+ result = result | Klass::ModifyInstances; -+ } - -- merge_cp()->set_is_conc_safe(true); -- merge_cp = constantPoolHandle(); // toss the merged constant pool -+ // Do a parallel walk through the old and new methods. Detect -+ // cases where they match (exist in both), have been added in -+ // the new methods, or have been deleted (exist only in the -+ // old methods). The class file parser places methods in order -+ // by method name, but does not order overloaded methods by -+ // signature. In order to determine what fate befell the methods, -+ // this code places the overloaded new methods that have matching -+ // old methods in the same order as the old methods and places -+ // new overloaded methods at the end of overloaded methods of -+ // that name. The code for this order normalization is adapted -+ // from the algorithm used in instanceKlass::find_method(). -+ // Since we are swapping out of order entries as we find them, -+ // we only have to search forward through the overloaded methods. -+ // Methods which are added and have the same name as an existing -+ // method (but different signature) will be put at the end of -+ // the methods with that name, and the name mismatch code will -+ // handle them. -+ objArrayHandle k_old_methods(the_class->methods()); -+ objArrayHandle k_new_methods(new_class->methods()); -+ int n_old_methods = k_old_methods->length(); -+ int n_new_methods = k_new_methods->length(); -+ -+ int ni = 0; -+ int oi = 0; -+ while (true) { -+ methodOop k_old_method; -+ methodOop k_new_method; -+ enum { matched, added, deleted, undetermined } method_was = undetermined; -+ -+ if (oi >= n_old_methods) { -+ if (ni >= n_new_methods) { -+ break; // we've looked at everything, done -+ } -+ // New method at the end -+ k_new_method = (methodOop) k_new_methods->obj_at(ni); -+ method_was = added; -+ } else if (ni >= n_new_methods) { -+ // Old method, at the end, is deleted -+ k_old_method = (methodOop) k_old_methods->obj_at(oi); -+ method_was = deleted; - } else { -- // The old constant pool has more entries than the new constant -- // pool and the index map is empty. This means that both the old -- // and merged constant pools are supersets of the new constant -- // pool. -+ // There are more methods in both the old and new lists -+ k_old_method = (methodOop) k_old_methods->obj_at(oi); -+ k_new_method = (methodOop) k_new_methods->obj_at(ni); -+ if (k_old_method->name() != k_new_method->name()) { -+ // Methods are sorted by method name, so a mismatch means added -+ // or deleted -+ if (k_old_method->name()->fast_compare(k_new_method->name()) > 0) { -+ method_was = added; -+ } else { -+ method_was = deleted; -+ } -+ } else if (k_old_method->signature() == k_new_method->signature()) { -+ // Both the name and signature match -+ method_was = matched; -+ } else { -+ // The name matches, but the signature doesn't, which means we have to -+ // search forward through the new overloaded methods. -+ int nj; // outside the loop for post-loop check -+ for (nj = ni + 1; nj < n_new_methods; nj++) { -+ methodOop m = (methodOop)k_new_methods->obj_at(nj); -+ if (k_old_method->name() != m->name()) { -+ // reached another method name so no more overloaded methods -+ method_was = deleted; -+ break; -+ } -+ if (k_old_method->signature() == m->signature()) { -+ // found a match so swap the methods -+ k_new_methods->obj_at_put(ni, m); -+ k_new_methods->obj_at_put(nj, k_new_method); -+ k_new_method = m; -+ method_was = matched; -+ break; -+ } -+ } - -- // Replace the new constant pool with a shrunken copy of the -- // merged constant pool; the previous new constant pool will -- // get GCed. -- set_new_constant_pool(scratch_class, merge_cp, merge_cp_length, -- THREAD); -- // drop local ref to the merged constant pool -- merge_cp()->set_is_conc_safe(true); -- merge_cp = constantPoolHandle(); -- } -- } else { -- if (RC_TRACE_ENABLED(0x00040000)) { -- // don't want to loop unless we are tracing -- int count = 0; -- for (int i = 1; i < _index_map_p->length(); i++) { -- int value = _index_map_p->at(i); -- -- if (value != -1) { -- RC_TRACE_WITH_THREAD(0x00040000, THREAD, -- ("index_map[%d]: old=%d new=%d", count, i, value)); -- count++; -+ if (nj >= n_new_methods) { -+ // reached the end without a match; so method was deleted -+ method_was = deleted; - } - } - } - -- // We have entries mapped between the new and merged constant pools -- // so we have to rewrite some constant pool references. -- if (!rewrite_cp_refs(scratch_class, THREAD)) { -- return JVMTI_ERROR_INTERNAL; -+ switch (method_was) { -+ case matched: -+ // methods match, be sure modifiers do too -+ old_flags = (jushort) k_old_method->access_flags().get_flags(); -+ new_flags = (jushort) k_new_method->access_flags().get_flags(); -+ if ((old_flags ^ new_flags) & ~(JVM_ACC_NATIVE)) { -+ // (tw) Can this have any effects? Probably yes on vtables? -+ result = result | Klass::ModifyClass; - } -- -- // Replace the new constant pool with a shrunken copy of the -- // merged constant pool so now the rewritten bytecodes have -- // valid references; the previous new constant pool will get -- // GCed. -- set_new_constant_pool(scratch_class, merge_cp, merge_cp_length, -- THREAD); -- merge_cp()->set_is_conc_safe(true); -- } -- assert(old_cp()->is_conc_safe(), "Just checking"); -- assert(scratch_cp()->is_conc_safe(), "Just checking"); -- -- return JVMTI_ERROR_NONE; --} // end merge_cp_and_rewrite() -- -- --// Rewrite constant pool references in klass scratch_class. --bool VM_RedefineClasses::rewrite_cp_refs(instanceKlassHandle scratch_class, -- TRAPS) { -- -- // rewrite constant pool references in the methods: -- if (!rewrite_cp_refs_in_methods(scratch_class, THREAD)) { -- // propagate failure back to caller -- return false; -- } -- -- // rewrite constant pool references in the class_annotations: -- if (!rewrite_cp_refs_in_class_annotations(scratch_class, THREAD)) { -- // propagate failure back to caller -- return false; -- } -- -- // rewrite constant pool references in the fields_annotations: -- if (!rewrite_cp_refs_in_fields_annotations(scratch_class, THREAD)) { -- // propagate failure back to caller -- return false; -- } -- -- // rewrite constant pool references in the methods_annotations: -- if (!rewrite_cp_refs_in_methods_annotations(scratch_class, THREAD)) { -- // propagate failure back to caller -- return false; -- } -- -- // rewrite constant pool references in the methods_parameter_annotations: -- if (!rewrite_cp_refs_in_methods_parameter_annotations(scratch_class, -- THREAD)) { -- // propagate failure back to caller -- return false; -- } -- -- // rewrite constant pool references in the methods_default_annotations: -- if (!rewrite_cp_refs_in_methods_default_annotations(scratch_class, -- THREAD)) { -- // propagate failure back to caller -- return false; -- } -- -- return true; --} // end rewrite_cp_refs() -- -- --// Rewrite constant pool references in the methods. --bool VM_RedefineClasses::rewrite_cp_refs_in_methods( -- instanceKlassHandle scratch_class, TRAPS) { -- -- objArrayHandle methods(THREAD, scratch_class->methods()); -- -- if (methods.is_null() || methods->length() == 0) { -- // no methods so nothing to do -- return true; -- } -- -- // rewrite constant pool references in the methods: -- for (int i = methods->length() - 1; i >= 0; i--) { -- methodHandle method(THREAD, (methodOop)methods->obj_at(i)); -- methodHandle new_method; -- rewrite_cp_refs_in_method(method, &new_method, CHECK_false); -- if (!new_method.is_null()) { -- // the method has been replaced so save the new method version -- methods->obj_at_put(i, new_method()); -+ { -+ u2 new_num = k_new_method->method_idnum(); -+ u2 old_num = k_old_method->method_idnum(); -+ if (new_num != old_num) { -+ methodOop idnum_owner = new_class->method_with_idnum(old_num); -+ if (idnum_owner != NULL) { -+ // There is already a method assigned this idnum -- switch them -+ idnum_owner->set_method_idnum(new_num); -+ } -+ k_new_method->set_method_idnum(old_num); -+ RC_TRACE(0x00000002, ("swapping idnum of new and old method %d / %d!", -+ new_num, -+ old_num)); -+ // swap_all_method_annotations(old_num, new_num, new_class); -+ } -+ } -+ RC_TRACE(0x00008000, ("Method matched: new: %s [%d] == old: %s [%d]", -+ k_new_method->name_and_sig_as_C_string(), ni, -+ k_old_method->name_and_sig_as_C_string(), oi)); -+ // advance to next pair of methods -+ ++oi; -+ ++ni; -+ break; -+ case added: -+ // method added, see if it is OK -+ new_flags = (jushort) k_new_method->access_flags().get_flags(); -+ if ((new_flags & JVM_ACC_PRIVATE) == 0 -+ // hack: private should be treated as final, but alas -+ || (new_flags & (JVM_ACC_FINAL|JVM_ACC_STATIC)) == 0 -+ ) { -+ // new methods must be private -+ result = result | Klass::ModifyClass; -+ } -+ { -+ u2 num = the_class->next_method_idnum(); -+ if (num == constMethodOopDesc::UNSET_IDNUM) { -+ // cannot add any more methods -+ result = result | Klass::ModifyClass; -+ } -+ u2 new_num = k_new_method->method_idnum(); -+ methodOop idnum_owner = new_class->method_with_idnum(num); -+ if (idnum_owner != NULL) { -+ // There is already a method assigned this idnum -- switch them -+ idnum_owner->set_method_idnum(new_num); -+ } -+ k_new_method->set_method_idnum(num); -+ //swap_all_method_annotations(new_num, num, new_class); -+ } -+ RC_TRACE(0x00000001, ("Method added: new: %s [%d]", -+ k_new_method->name_and_sig_as_C_string(), ni)); -+ ++ni; // advance to next new method -+ break; -+ case deleted: -+ // method deleted, see if it is OK -+ old_flags = (jushort) k_old_method->access_flags().get_flags(); -+ if ((old_flags & JVM_ACC_PRIVATE) == 0 -+ // hack: private should be treated as final, but alas -+ || (old_flags & (JVM_ACC_FINAL|JVM_ACC_STATIC)) == 0 -+ ) { -+ // deleted methods must be private -+ result = result | Klass::ModifyClass; -+ } -+ RC_TRACE(0x00000001, ("Method deleted: old: %s [%d]", -+ k_old_method->name_and_sig_as_C_string(), oi)); -+ ++oi; // advance to next old method -+ break; -+ default: -+ ShouldNotReachHere(); - } - } - -- return true; -+ if (new_class()->size() != new_class->old_version()->size()) { -+ result |= Klass::ModifyClassSize; -+ } -+ -+ if (new_class->size_helper() != ((instanceKlass*)(new_class->old_version()->klass_part()))->size_helper()) { -+ result |= Klass::ModifyInstanceSize; -+ } -+ -+ methodHandle instanceTransformerMethod(new_class->find_method(vmSymbols::transformer_name(), vmSymbols::void_method_signature())); -+ if (!instanceTransformerMethod.is_null() && !instanceTransformerMethod->is_static()) { -+ result |= Klass::HasInstanceTransformer; -+ } -+ -+ // (tw) Check method bodies to be able to return NoChange? -+ return result; - } - -+void VM_RedefineClasses::calculate_instance_update_information(klassOop new_version) { - --// Rewrite constant pool references in the specific method. This code --// was adapted from Rewriter::rewrite_method(). --void VM_RedefineClasses::rewrite_cp_refs_in_method(methodHandle method, -- methodHandle *new_method_p, TRAPS) { -+ class UpdateFieldsEvolutionClosure : public FieldEvolutionClosure { - -- *new_method_p = methodHandle(); // default is no new method -+ private: - -- // We cache a pointer to the bytecodes here in code_base. If GC -- // moves the methodOop, then the bytecodes will also move which -- // will likely cause a crash. We create a No_Safepoint_Verifier -- // object to detect whether we pass a possible safepoint in this -- // code block. -- No_Safepoint_Verifier nsv; -+ GrowableArray info; -+ int curPosition; -+ bool copy_backwards; - -- // Bytecodes and their length -- address code_base = method->code_base(); -- int code_length = method->code_size(); -+ public: - -- int bc_length; -- for (int bci = 0; bci < code_length; bci += bc_length) { -- address bcp = code_base + bci; -- Bytecodes::Code c = (Bytecodes::Code)(*bcp); -- -- bc_length = Bytecodes::length_for(c); -- if (bc_length == 0) { -- // More complicated bytecodes report a length of zero so -- // we have to try again a slightly different way. -- bc_length = Bytecodes::length_at(method(), bcp); -+ bool does_copy_backwards() { -+ return copy_backwards; - } - -- assert(bc_length != 0, "impossible bytecode length"); -+ UpdateFieldsEvolutionClosure(klassOop klass) { - -- switch (c) { -- case Bytecodes::_ldc: -- { -- int cp_index = *(bcp + 1); -- int new_index = find_new_index(cp_index); -+ int base_offset = instanceOopDesc::base_offset_in_bytes(); - -- if (StressLdcRewrite && new_index == 0) { -- // If we are stressing ldc -> ldc_w rewriting, then we -- // always need a new_index value. -- new_index = cp_index; -- } -- if (new_index != 0) { -- // the original index is mapped so we have more work to do -- if (!StressLdcRewrite && new_index <= max_jubyte) { -- // The new value can still use ldc instead of ldc_w -- // unless we are trying to stress ldc -> ldc_w rewriting -- RC_TRACE_WITH_THREAD(0x00080000, THREAD, -- ("%s@" INTPTR_FORMAT " old=%d, new=%d", Bytecodes::name(c), -- bcp, cp_index, new_index)); -- *(bcp + 1) = new_index; -+ if (klass->klass_part()->newest_version() == SystemDictionary::Reference_klass()->klass_part()->newest_version()) { -+ base_offset += java_lang_ref_Reference::number_of_fake_oop_fields*size_of_type(T_OBJECT); -+ } -+ -+ info.append(base_offset); -+ info.append(0); -+ curPosition = base_offset; -+ copy_backwards = false; -+ } -+ -+ GrowableArray &finish() { -+ info.append(0); -+ return info; -+ } -+ -+ virtual void do_new_field(fieldDescriptor* fd){ -+ int alignment = fd->offset() - curPosition; -+ if (alignment > 0) { -+ // This field was aligned, so we need to make sure that we fill the gap -+ fill(alignment); -+ } -+ -+ int size = size_of_type(fd->field_type()); -+ fill(size); -+ } -+ -+ private: -+ -+ void fill(int size) { -+ if (info.length() > 0 && info.at(info.length() - 1) < 0) { -+ (*info.adr_at(info.length() - 1)) -= size; -+ } else { -+ info.append(-size); -+ } -+ -+ curPosition += size; -+ } -+ -+ int size_of_type(BasicType type) { -+ int size = 0; -+ switch(type) { -+ case T_BOOLEAN: -+ size = sizeof(jboolean); -+ break; -+ -+ case T_CHAR: -+ size = (sizeof(jchar)); -+ break; -+ -+ case T_FLOAT: -+ size = (sizeof(jfloat)); -+ break; -+ -+ case T_DOUBLE: -+ size = (sizeof(jdouble)); -+ break; -+ -+ case T_BYTE: -+ size = (sizeof(jbyte)); -+ break; -+ -+ case T_SHORT: -+ size = (sizeof(jshort)); -+ break; -+ -+ case T_INT: -+ size = (sizeof(jint)); -+ break; -+ -+ case T_LONG: -+ size = (sizeof(jlong)); -+ break; -+ -+ case T_OBJECT: -+ case T_ARRAY: -+ if (UseCompressedOops) { -+ size = sizeof(narrowOop); - } else { -- RC_TRACE_WITH_THREAD(0x00080000, THREAD, -- ("%s->ldc_w@" INTPTR_FORMAT " old=%d, new=%d", -- Bytecodes::name(c), bcp, cp_index, new_index)); -- // the new value needs ldc_w instead of ldc -- u_char inst_buffer[4]; // max instruction size is 4 bytes -- bcp = (address)inst_buffer; -- // construct new instruction sequence -- *bcp = Bytecodes::_ldc_w; -- bcp++; -- // Rewriter::rewrite_method() does not rewrite ldc -> ldc_w. -- // See comment below for difference between put_Java_u2() -- // and put_native_u2(). -- Bytes::put_Java_u2(bcp, new_index); -+ size = (sizeof(oop)); -+ } -+ break; - -- Relocator rc(method, NULL /* no RelocatorListener needed */); -- methodHandle m; -- { -- Pause_No_Safepoint_Verifier pnsv(&nsv); -+ default: -+ ShouldNotReachHere(); -+ } - -- // ldc is 2 bytes and ldc_w is 3 bytes -- m = rc.insert_space_at(bci, 3, inst_buffer, THREAD); -- if (m.is_null() || HAS_PENDING_EXCEPTION) { -- guarantee(false, "insert_space_at() failed"); -+ assert(size > 0, ""); -+ return size; -+ -+ } -+ -+ public: -+ -+ virtual void do_old_field(fieldDescriptor* fd){} -+ -+ virtual void do_changed_field(fieldDescriptor* old_fd, fieldDescriptor *new_fd){ -+ -+ int alignment = new_fd->offset() - curPosition; -+ if (alignment > 0) { -+ // This field was aligned, so we need to make sure that we fill the gap -+ fill(alignment); -+ } -+ -+ assert(old_fd->field_type() == new_fd->field_type(), ""); -+ assert(curPosition == new_fd->offset(), "must be correct offset!"); -+ -+ int offset = old_fd->offset(); -+ int size = size_of_type(old_fd->field_type()); -+ -+ int prevEnd = -1; -+ if (info.length() > 0 && info.at(info.length() - 1) > 0) { -+ prevEnd = info.at(info.length() - 2) + info.at(info.length() - 1); -+ } -+ -+ if (prevEnd == offset) { -+ info.at_put(info.length() - 2, info.at(info.length() - 2) + size); -+ } else { -+ info.append(size); -+ info.append(offset); -+ } -+ -+ if (old_fd->offset() < new_fd->offset()) { -+ copy_backwards = true; -+ } -+ -+ transfer_special_access_flags(old_fd, new_fd); -+ -+ curPosition += size; -+ } -+ }; -+ -+ UpdateFieldsEvolutionClosure cl(new_version); -+ ((instanceKlass*)new_version->klass_part())->do_fields_evolution(&cl); -+ -+ GrowableArray result = cl.finish(); -+ ((instanceKlass*)new_version->klass_part())->store_update_information(result); -+ ((instanceKlass*)new_version->klass_part())->set_copying_backwards(cl.does_copy_backwards()); -+ -+ if (RC_TRACE_ENABLED(0x00000002)) { -+ RC_TRACE(0x00000002, ("Instance update information for %s:", -+ new_version->klass_part()->name()->as_C_string())); -+ if (cl.does_copy_backwards()) { -+ RC_TRACE(0x00000002, ("\tDoes copy backwards!")); -+ } -+ for (int i=0; i 0) { -+ RC_TRACE(0x00000002, ("\t%d COPY from %d", curNum, result.at(i + 1))); -+ i++; -+ } else { -+ RC_TRACE(0x00000002, ("\tEND")); -+ } -+ } -+ } -+} -+ -+Symbol* VM_RedefineClasses::signature_to_class_name(Symbol* signature) { -+ assert(FieldType::is_obj(signature), ""); -+ return SymbolTable::new_symbol(signature->as_C_string() + 1, signature->utf8_length() - 2, Thread::current()); -+} -+ -+void VM_RedefineClasses::calculate_type_check_information(klassOop klass) { -+ if (klass->klass_part()->is_redefining()) { -+ klass = klass->klass_part()->old_version(); -+ } -+ -+ // We found an instance klass! -+ instanceKlass *cur_instance_klass = instanceKlass::cast(klass); -+ GrowableArray< Pair > type_check_information; -+ -+ class MyFieldClosure : public FieldClosure { -+ -+ public: -+ -+ GrowableArray< Pair > *_arr; -+ -+ MyFieldClosure(GrowableArray< Pair > *arr) { -+ _arr = arr; -+ } -+ -+ virtual void do_field(fieldDescriptor* fd) { -+ if (fd->field_type() == T_OBJECT) { -+ Symbol* signature = fd->signature(); -+ if (FieldType::is_obj(signature)) { -+ Symbol* name = signature_to_class_name(signature); -+ klassOop field_klass; -+ if (is_field_dangerous(name, fd, field_klass)) { -+ RC_TRACE(0x00000002, ("Found dangerous field %s in klass %s of type %s", -+ fd->name()->as_C_string(), -+ fd->field_holder()->klass_part()->name()->as_C_string(), -+ name->as_C_string())); -+ _arr->append(Pair(fd->offset(), field_klass->klass_part()->newest_version())); -+ } -+ } -+ -+ // Array fields can never be a problem! -+ } -+ } -+ -+ bool is_field_dangerous(Symbol* klass_name, fieldDescriptor *fd, klassOop &field_klass) { -+ field_klass = SystemDictionary::find(klass_name, fd->field_holder()->klass_part()->class_loader(), -+ fd->field_holder()->klass_part()->protection_domain(), Thread::current()); -+ if(field_klass != NULL) { -+ if (field_klass->klass_part()->is_redefining()) { -+ field_klass = field_klass->klass_part()->old_version(); -+ } -+ if (field_klass->klass_part()->has_subtype_changed()) { -+ return true; -+ } -+ } -+ return false; -+ } -+ }; -+ -+ MyFieldClosure fieldClosure(&type_check_information); -+ cur_instance_klass->do_nonstatic_fields(&fieldClosure); -+ -+ if (type_check_information.length() > 0) { -+ type_check_information.append(Pair(-1, NULL)); -+ cur_instance_klass->store_type_check_information(type_check_information); -+ } -+} -+ -+bool VM_RedefineClasses::check_field_value_types() { -+ -+ Thread *THREAD = Thread::current(); -+ class CheckFieldTypesClosure : public ObjectClosure { -+ -+ private: -+ -+ bool _result; -+ -+ public: -+ -+ CheckFieldTypesClosure() { -+ _result = true; -+ } -+ -+ bool result() { return _result; } -+ -+ virtual void do_object(oop obj) { -+ -+ if (!_result) { -+ return; -+ } -+ -+ if (obj->is_objArray()) { -+ -+ objArrayOop array = objArrayOop(obj); -+ -+ klassOop element_klass = objArrayKlass::cast(array->klass())->element_klass(); -+ -+ if (element_klass->klass_part()->has_subtype_changed()) { -+ int length = array->length(); -+ for (int i=0; iobj_at(i); -+ if (element != NULL && element->blueprint()->newest_version()->klass_part()->is_redefining()) { -+ // Check subtype relationship to static type of array -+ if (!element->blueprint()->newest_version()->klass_part()->is_subtype_of(element_klass->klass_part()->newest_version())) { -+ RC_TRACE(0x00000001, ("Array value is INVALID - abort redefinition (static_type=%s, index=%d, dynamic_type=%s)", -+ element_klass->klass_part()->name()->as_C_string(), -+ i, -+ element->blueprint()->name()->as_C_string())); -+ _result = false; -+ break; - } - } -- -- // return the new method so that the caller can update -- // the containing class -- *new_method_p = method = m; -- // switch our bytecode processing loop from the old method -- // to the new method -- code_base = method->code_base(); -- code_length = method->code_size(); -- bcp = code_base + bci; -- c = (Bytecodes::Code)(*bcp); -- bc_length = Bytecodes::length_for(c); -- assert(bc_length != 0, "sanity check"); -- } // end we need ldc_w instead of ldc -- } // end if there is a mapped index -- } break; -- -- // these bytecodes have a two-byte constant pool index -- case Bytecodes::_anewarray : // fall through -- case Bytecodes::_checkcast : // fall through -- case Bytecodes::_getfield : // fall through -- case Bytecodes::_getstatic : // fall through -- case Bytecodes::_instanceof : // fall through -- case Bytecodes::_invokedynamic : // fall through -- case Bytecodes::_invokeinterface: // fall through -- case Bytecodes::_invokespecial : // fall through -- case Bytecodes::_invokestatic : // fall through -- case Bytecodes::_invokevirtual : // fall through -- case Bytecodes::_ldc_w : // fall through -- case Bytecodes::_ldc2_w : // fall through -- case Bytecodes::_multianewarray : // fall through -- case Bytecodes::_new : // fall through -- case Bytecodes::_putfield : // fall through -- case Bytecodes::_putstatic : -- { -- address p = bcp + 1; -- int cp_index = Bytes::get_Java_u2(p); -- int new_index = find_new_index(cp_index); -- if (new_index != 0) { -- // the original index is mapped so update w/ new value -- RC_TRACE_WITH_THREAD(0x00080000, THREAD, -- ("%s@" INTPTR_FORMAT " old=%d, new=%d", Bytecodes::name(c), -- bcp, cp_index, new_index)); -- // Rewriter::rewrite_method() uses put_native_u2() in this -- // situation because it is reusing the constant pool index -- // location for a native index into the constantPoolCache. -- // Since we are updating the constant pool index prior to -- // verification and constantPoolCache initialization, we -- // need to keep the new index in Java byte order. -- Bytes::put_Java_u2(p, new_index); -+ } - } -- } break; -+ -+ } else { -+ Pair *cur = obj->klass()->klass_part()->type_check_information(); -+ if (cur != NULL) { -+ // Type check information exists for this oop -+ while ((*cur).left() != -1) { -+ check_field(obj, (*cur).left(), (*cur).right()); -+ cur++; -+ } -+ } -+ } - } -- } // end for each bytecode --} // end rewrite_cp_refs_in_method() - -+ void check_field(oop obj, int offset, klassOop static_type) { -+ oop field_value = obj->obj_field(offset); -+ if (field_value != NULL) { -+ // Field is not null -+ if (field_value->klass()->klass_part()->newest_version()->klass_part()->is_subtype_of(static_type)) { -+ // We are OK -+ RC_TRACE(0x00008000, ("Field value is OK (klass=%s, static_type=%s, offset=%d, dynamic_type=%s)", -+ obj->klass()->klass_part()->name()->as_C_string(), -+ static_type->klass_part()->name()->as_C_string(), -+ offset, -+ field_value->klass()->klass_part()->name()->as_C_string())); -+ } else { -+ // Failure! -+ RC_TRACE(0x00000001, ("Field value is INVALID - abort redefinition (klass=%s, static_type=%s, offset=%d, dynamic_type=%s)", -+ obj->klass()->klass_part()->name()->as_C_string(), -+ static_type->klass_part()->name()->as_C_string(), -+ offset, -+ field_value->klass()->klass_part()->name()->as_C_string())); -+ _result = false; -+ } -+ } -+ } -+ }; - --// Rewrite constant pool references in the class_annotations field. --bool VM_RedefineClasses::rewrite_cp_refs_in_class_annotations( -- instanceKlassHandle scratch_class, TRAPS) { -+ CheckFieldTypesClosure myObjectClosure; - -- typeArrayHandle class_annotations(THREAD, -- scratch_class->class_annotations()); -- if (class_annotations.is_null() || class_annotations->length() == 0) { -- // no class_annotations so nothing to do -- return true; -+ // make sure that heap is parsable (fills TLABs with filler objects) -+ Universe::heap()->ensure_parsability(false); // no need to retire TLABs -+ -+ // do the iteration -+ // If this operation encounters a bad object when using CMS, -+ // consider using safe_object_iterate() which avoids perm gen -+ // objects that may contain bad references. -+ Universe::heap()->object_iterate(&myObjectClosure); -+ -+ // when sharing is enabled we must iterate over the shared spaces -+ if (UseSharedSpaces) { -+ GenCollectedHeap* gch = GenCollectedHeap::heap(); -+ CompactingPermGenGen* gen = (CompactingPermGenGen*)gch->perm_gen(); -+ gen->ro_space()->object_iterate(&myObjectClosure); -+ gen->rw_space()->object_iterate(&myObjectClosure); - } - -- RC_TRACE_WITH_THREAD(0x02000000, THREAD, -- ("class_annotations length=%d", class_annotations->length())); -- -- int byte_i = 0; // byte index into class_annotations -- return rewrite_cp_refs_in_annotations_typeArray(class_annotations, byte_i, -- THREAD); -+ return myObjectClosure.result(); - } - -- --// Rewrite constant pool references in an annotations typeArray. This --// "structure" is adapted from the RuntimeVisibleAnnotations_attribute --// that is described in section 4.8.15 of the 2nd-edition of the VM spec: --// --// annotations_typeArray { --// u2 num_annotations; --// annotation annotations[num_annotations]; --// } --// --bool VM_RedefineClasses::rewrite_cp_refs_in_annotations_typeArray( -- typeArrayHandle annotations_typeArray, int &byte_i_ref, TRAPS) { -- -- if ((byte_i_ref + 2) > annotations_typeArray->length()) { -- // not enough room for num_annotations field -- RC_TRACE_WITH_THREAD(0x02000000, THREAD, -- ("length() is too small for num_annotations field")); -- return false; -+void VM_RedefineClasses::clear_type_check_information(klassOop k) { -+ if (k->klass_part()->is_redefining()) { -+ k = k->klass_part()->old_version(); - } - -- u2 num_annotations = Bytes::get_Java_u2((address) -- annotations_typeArray->byte_at_addr(byte_i_ref)); -- byte_i_ref += 2; -- -- RC_TRACE_WITH_THREAD(0x02000000, THREAD, -- ("num_annotations=%d", num_annotations)); -- -- int calc_num_annotations = 0; -- for (; calc_num_annotations < num_annotations; calc_num_annotations++) { -- if (!rewrite_cp_refs_in_annotation_struct(annotations_typeArray, -- byte_i_ref, THREAD)) { -- RC_TRACE_WITH_THREAD(0x02000000, THREAD, -- ("bad annotation_struct at %d", calc_num_annotations)); -- // propagate failure back to caller -- return false; -- } -- } -- assert(num_annotations == calc_num_annotations, "sanity check"); -- -- return true; --} // end rewrite_cp_refs_in_annotations_typeArray() -- -- --// Rewrite constant pool references in the annotation struct portion of --// an annotations_typeArray. This "structure" is from section 4.8.15 of --// the 2nd-edition of the VM spec: --// --// struct annotation { --// u2 type_index; --// u2 num_element_value_pairs; --// { --// u2 element_name_index; --// element_value value; --// } element_value_pairs[num_element_value_pairs]; --// } --// --bool VM_RedefineClasses::rewrite_cp_refs_in_annotation_struct( -- typeArrayHandle annotations_typeArray, int &byte_i_ref, TRAPS) { -- if ((byte_i_ref + 2 + 2) > annotations_typeArray->length()) { -- // not enough room for smallest annotation_struct -- RC_TRACE_WITH_THREAD(0x02000000, THREAD, -- ("length() is too small for annotation_struct")); -- return false; -- } -- -- u2 type_index = rewrite_cp_ref_in_annotation_data(annotations_typeArray, -- byte_i_ref, "mapped old type_index=%d", THREAD); -- -- u2 num_element_value_pairs = Bytes::get_Java_u2((address) -- annotations_typeArray->byte_at_addr( -- byte_i_ref)); -- byte_i_ref += 2; -- -- RC_TRACE_WITH_THREAD(0x02000000, THREAD, -- ("type_index=%d num_element_value_pairs=%d", type_index, -- num_element_value_pairs)); -- -- int calc_num_element_value_pairs = 0; -- for (; calc_num_element_value_pairs < num_element_value_pairs; -- calc_num_element_value_pairs++) { -- if ((byte_i_ref + 2) > annotations_typeArray->length()) { -- // not enough room for another element_name_index, let alone -- // the rest of another component -- RC_TRACE_WITH_THREAD(0x02000000, THREAD, -- ("length() is too small for element_name_index")); -- return false; -- } -- -- u2 element_name_index = rewrite_cp_ref_in_annotation_data( -- annotations_typeArray, byte_i_ref, -- "mapped old element_name_index=%d", THREAD); -- -- RC_TRACE_WITH_THREAD(0x02000000, THREAD, -- ("element_name_index=%d", element_name_index)); -- -- if (!rewrite_cp_refs_in_element_value(annotations_typeArray, -- byte_i_ref, THREAD)) { -- RC_TRACE_WITH_THREAD(0x02000000, THREAD, -- ("bad element_value at %d", calc_num_element_value_pairs)); -- // propagate failure back to caller -- return false; -- } -- } // end for each component -- assert(num_element_value_pairs == calc_num_element_value_pairs, -- "sanity check"); -- -- return true; --} // end rewrite_cp_refs_in_annotation_struct() -- -- --// Rewrite a constant pool reference at the current position in --// annotations_typeArray if needed. Returns the original constant --// pool reference if a rewrite was not needed or the new constant --// pool reference if a rewrite was needed. --u2 VM_RedefineClasses::rewrite_cp_ref_in_annotation_data( -- typeArrayHandle annotations_typeArray, int &byte_i_ref, -- const char * trace_mesg, TRAPS) { -- -- address cp_index_addr = (address) -- annotations_typeArray->byte_at_addr(byte_i_ref); -- u2 old_cp_index = Bytes::get_Java_u2(cp_index_addr); -- u2 new_cp_index = find_new_index(old_cp_index); -- if (new_cp_index != 0) { -- RC_TRACE_WITH_THREAD(0x02000000, THREAD, (trace_mesg, old_cp_index)); -- Bytes::put_Java_u2(cp_index_addr, new_cp_index); -- old_cp_index = new_cp_index; -- } -- byte_i_ref += 2; -- return old_cp_index; -+ // We found an instance klass! -+ instanceKlass *cur_instance_klass = instanceKlass::cast(k); -+ cur_instance_klass->clear_type_check_information(); - } - -+void VM_RedefineClasses::update_active_methods() { - --// Rewrite constant pool references in the element_value portion of an --// annotations_typeArray. This "structure" is from section 4.8.15.1 of --// the 2nd-edition of the VM spec: --// --// struct element_value { --// u1 tag; --// union { --// u2 const_value_index; --// { --// u2 type_name_index; --// u2 const_name_index; --// } enum_const_value; --// u2 class_info_index; --// annotation annotation_value; --// struct { --// u2 num_values; --// element_value values[num_values]; --// } array_value; --// } value; --// } --// --bool VM_RedefineClasses::rewrite_cp_refs_in_element_value( -- typeArrayHandle annotations_typeArray, int &byte_i_ref, TRAPS) { -+ RC_TRACE(0x00000002, ("Updating active methods")); -+ JavaThread *java_thread = Threads::first(); -+ while (java_thread != NULL) { - -- if ((byte_i_ref + 1) > annotations_typeArray->length()) { -- // not enough room for a tag let alone the rest of an element_value -- RC_TRACE_WITH_THREAD(0x02000000, THREAD, -- ("length() is too small for a tag")); -+ int stack_depth = 0; -+ if (java_thread->has_last_Java_frame()) { -+ -+ RC_TRACE(0x0000000400, ("checking stack of Java thread %s", java_thread->name())); -+ -+ // vframes are resource allocated -+ Thread* current_thread = Thread::current(); -+ ResourceMark rm(current_thread); -+ HandleMark hm(current_thread); -+ -+ RegisterMap reg_map(java_thread); -+ frame f = java_thread->last_frame(); -+ vframe* vf = vframe::new_vframe(&f, ®_map, java_thread); -+ frame* last_entry_frame = NULL; -+ -+ while (vf != NULL) { -+ if (vf->is_java_frame()) { -+ // java frame (interpreted, compiled, ...) -+ javaVFrame *jvf = javaVFrame::cast(vf); -+ -+ if (!(jvf->method()->is_native())) { -+ int bci = jvf->bci(); -+ RC_TRACE(0x00000400, ("found method: %s / bci=%d", jvf->method()->name()->as_C_string(), bci)); -+ ResourceMark rm(Thread::current()); -+ HandleMark hm; -+ instanceKlassHandle klass(jvf->method()->method_holder()); -+ -+ if (jvf->method()->new_version() != NULL && jvf->is_interpreted_frame()) { -+ -+ -+ RC_TRACE(0x00000002, ("Found method that should just be updated to the newest version %s", -+ jvf->method()->name_and_sig_as_C_string())); -+ -+ if (RC_TRACE_ENABLED(0x01000000)) { -+ int code_size = jvf->method()->code_size(); -+ char *code_base_old = (char*)jvf->method()->code_base(); -+ char *code_base_new = (char*)jvf->method()->new_version()->code_base(); -+ for (int i=0; iprint_cr("old=%d new=%d", *code_base_old++, *code_base_new++); -+ } -+ jvf->method()->print_codes_on(tty); -+ jvf->method()->new_version()->print_codes_on(tty); -+ } -+ -+ assert(jvf->is_interpreted_frame(), "Every frame must be interpreted!"); -+ interpretedVFrame *iframe = (interpretedVFrame *)jvf; -+ -+ -+ if (RC_TRACE_ENABLED(0x01000000)) { -+ constantPoolCacheOop cp_old = jvf->method()->constants()->cache(); -+ tty->print_cr("old cp"); -+ for (int i=0; ilength(); i++) { -+ cp_old->entry_at(i)->print(tty, i); -+ } -+ constantPoolCacheOop cp_new = jvf->method()->new_version()->constants()->cache(); -+ tty->print_cr("new cp"); -+ for (int i=0; ilength(); i++) { -+ cp_new->entry_at(i)->print(tty, i); -+ } -+ } -+ -+ iframe->set_method(jvf->method()->new_version(), bci); -+ RC_TRACE(0x00000002, ("Updated method to newer version")); -+ assert(jvf->method()->new_version() == NULL, "must be latest version"); -+ -+ } -+ } -+ } -+ vf = vf->sender(); -+ } -+ } -+ -+ // Advance to next thread -+ java_thread = java_thread->next(); -+ } -+} -+ -+void VM_RedefineClasses::method_forwarding() { -+ -+ int forwarding_count = 0; -+ JavaThread *java_thread = Threads::first(); -+ while (java_thread != NULL) { -+ -+ int stack_depth = 0; -+ if (java_thread->has_last_Java_frame()) { -+ -+ RC_TRACE(0x00000400, ("checking stack of Java thread %s", java_thread->name())); -+ -+ // vframes are resource allocated -+ Thread* current_thread = Thread::current(); -+ ResourceMark rm(current_thread); -+ HandleMark hm(current_thread); -+ -+ RegisterMap reg_map(java_thread); -+ frame f = java_thread->last_frame(); -+ vframe* vf = vframe::new_vframe(&f, ®_map, java_thread); -+ frame* last_entry_frame = NULL; -+ -+ while (vf != NULL) { -+ if (vf->is_java_frame()) { -+ // java frame (interpreted, compiled, ...) -+ javaVFrame *jvf = javaVFrame::cast(vf); -+ -+ if (!(jvf->method()->is_native())) { -+ RC_TRACE(0x00008000, ("found method: %s", -+ jvf->method()->name()->as_C_string())); -+ ResourceMark rm(Thread::current()); -+ HandleMark hm; -+ instanceKlassHandle klass(jvf->method()->method_holder()); -+ methodOop m = jvf->method(); -+ int bci = jvf->bci(); -+ RC_TRACE(0x00008000, ("klass redef %d", -+ klass->is_redefining())); -+ -+ if (klass->new_version() != NULL && m->new_version() == NULL) { -+ RC_TRACE(0x00008000, ("found potential forwarding method: %s", -+ m->name()->as_C_string())); -+ -+ klassOop new_klass = klass->newest_version(); -+ methodOop new_method = new_klass->klass_part()->lookup_method(m->name(), m->signature()); -+ RC_TRACE(0x00000002, ("%d %d", -+ new_method, -+ new_method->constMethod()->has_code_section_table())); -+ -+ if (new_method != NULL && new_method->constMethod()->has_code_section_table()) { -+ RC_TRACE(0x00008000, ("found code section table for method: %s", -+ new_method->name()->as_C_string())); -+ m->set_forward_method(new_method); -+ if (new_method->max_locals() != m->max_locals()) { -+ tty->print_cr("new_m max locals: %d old_m max locals: %d", new_method->max_locals(), m->max_locals()); -+ } -+ assert(new_method->max_locals() == m->max_locals(), "number of locals must match"); -+ assert(new_method->max_stack() == m->max_stack(), "number of stack values must match"); -+ if (jvf->is_interpreted_frame()) { -+ if (m->is_in_code_section(bci)) { -+ // We must transfer now and cannot delay until next NOP. -+ int new_bci = m->calculate_forward_bci(bci, new_method); -+ interpretedVFrame* iframe = interpretedVFrame::cast(jvf); -+ RC_TRACE(0x00000002, ("Transferring execution of %s to new method old_bci=%d new_bci=%d", -+ new_method->name()->as_C_string(), -+ bci, -+ new_bci)); -+ iframe->set_method(new_method, new_bci); -+ } else { -+ RC_TRACE(0x00000002, ("Delaying method forwarding of %s because %d is not in a code section", -+ new_method->name()->as_C_string(), -+ bci)); -+ } -+ } else { -+ RC_TRACE(0x00000002, ("Delaying method forwarding of %s because method is compiled", -+ new_method->name()->as_C_string())); -+ } -+ } -+ } -+ } -+ } -+ vf = vf->sender(); ++ u2 new_num = k_new_method->method_idnum(); ++ methodOop idnum_owner = new_class->method_with_idnum(num); ++ if (idnum_owner != NULL) { ++ // There is already a method assigned this idnum -- switch them ++ idnum_owner->set_method_idnum(new_num); + } ++ k_new_method->set_method_idnum(num); ++ //swap_all_method_annotations(new_num, num, new_class); + } -+ -+ // Advance to next thread -+ java_thread = java_thread->next(); -+ } -+ -+ RC_TRACE(0x00000001, ("Method forwarding applied to %d methods", -+ forwarding_count)); -+} -+ -+bool VM_RedefineClasses::check_method_stacks() { -+ -+ JavaThread *java_thread = Threads::first(); -+ while (java_thread != NULL) { -+ -+ int stack_depth = 0; -+ if (java_thread->has_last_Java_frame()) { -+ -+ RC_TRACE(0x00000400, ("checking stack of Java thread %s", java_thread->name())); -+ -+ // vframes are resource allocated -+ Thread* current_thread = Thread::current(); -+ ResourceMark rm(current_thread); -+ HandleMark hm(current_thread); -+ -+ RegisterMap reg_map(java_thread); -+ frame f = java_thread->last_frame(); -+ vframe* vf = vframe::new_vframe(&f, ®_map, java_thread); -+ frame* last_entry_frame = NULL; -+ -+ while (vf != NULL) { -+ if (vf->is_java_frame()) { -+ // java frame (interpreted, compiled, ...) -+ javaVFrame *jvf = javaVFrame::cast(vf); -+ -+ if (!(jvf->method()->is_native())) { -+ RC_TRACE(0x00000400, ("found method: %s", jvf->method()->name()->as_C_string())); -+ ResourceMark rm(Thread::current()); -+ HandleMark hm; -+ instanceKlassHandle klass(jvf->method()->method_holder()); -+ -+ StackValueCollection *locals = jvf->locals(); -+ const size_t message_buffer_len = klass->name()->utf8_length() + 1024; -+ char* message_buffer = NEW_RESOURCE_ARRAY(char, message_buffer_len); -+ -+ for (int i=0; isize(); i++) { -+ StackValue *stack_value = locals->at(i); -+ if (stack_value->type() == T_OBJECT) { -+ Handle obj = stack_value->get_obj(); -+ if (!obj.is_null() && obj->klass()->klass_part()->newest_version()->klass_part()->check_redefinition_flag(Klass::RemoveSuperType)) { -+ -+ // OK, so this is a possible failure => check local variable table, if it could be OK. -+ bool result = false; -+ methodOop method = jvf->method(); -+ if (method->has_localvariable_table()) { -+ LocalVariableTableElement *elem = jvf->method()->localvariable_table_start(); -+ for (int j=0; jlocalvariable_table_length(); j++) { -+ -+ if (elem->slot == i) { -+ -+ // Matching index found -+ -+ if (elem->start_bci <= jvf->bci() && elem->start_bci + elem->length > jvf->bci()) { -+ -+ // Also in range!! -+ Symbol* signature = jvf->method()->constants()->symbol_at(elem->descriptor_cp_index); -+ Symbol* klass_name = signature_to_class_name(signature); -+ -+ klassOop local_klass = SystemDictionary::find(klass_name, jvf->method()->method_holder()->klass_part()->class_loader(), jvf->method()->method_holder()->klass_part()->protection_domain(), Thread::current())->klass_part()->newest_version(); -+ klassOop cur = obj->klass()->klass_part()->newest_version(); -+ -+ // Field is not null -+ if (cur->klass_part()->newest_version()->klass_part()->is_subtype_of(local_klass)) { -+ // We are OK -+ RC_TRACE(0x00008000, ("Local variable value is OK (local_klass=%s, cur_klass=%s)", -+ local_klass->klass_part()->name()->as_C_string(), cur->klass_part()->name()->as_C_string())); -+ result = true; -+ } else { -+ // Failure! -+ RC_TRACE(0x00000001, ("Local variable value is INVALID - abort redefinition (local_klass=%s, cur_klass=%s)", -+ local_klass->klass_part()->name()->as_C_string(), -+ cur->klass_part()->name()->as_C_string())); -+ return false; -+ } -+ } -+ } -+ -+ elem++; -+ } -+ } else { -+ RC_TRACE(0x00000002, ("Method %s does not have a local variable table => abort", -+ method->name_and_sig_as_C_string())); -+ } -+ -+ if (!result) { -+ return false; -+ } -+ -+ RC_TRACE(0x00008000, ("Verifying class %s", -+ jvf->method()->method_holder()->klass_part()->name()->as_C_string())); -+ -+ Symbol* exception_name; -+ const size_t message_buffer_len = klass->name()->utf8_length() + 1024; -+ char* message_buffer = NEW_RESOURCE_ARRAY(char, message_buffer_len); -+ -+ Thread::current()->set_pretend_new_universe(true); -+ ClassVerifier split_verifier(klass, Thread::current()); -+ split_verifier.verify_method(jvf->method(), Thread::current()); -+ exception_name = split_verifier.result(); -+ Thread::current()->set_pretend_new_universe(false); -+ -+ if (exception_name != NULL) { -+ -+ RC_TRACE(0x00000001, ("Verification of class %s failed", -+ jvf->method()->method_holder()->klass_part()->name()->as_C_string())); -+ RC_TRACE(0x00000001, ("Exception: %s", -+ exception_name->as_C_string())); -+ RC_TRACE(0x00000001, ("Message: %s", -+ message_buffer)); -+ Thread::current()->clear_pending_exception(); -+ return false; -+ } -+ -+ } -+ } -+ } -+ } -+ } -+ vf = vf->sender(); -+ } ++ RC_TRACE(0x00000001, ("Method added: new: %s [%d]", ++ k_new_method->name_and_sig_as_C_string(), ni)); ++ ++ni; // advance to next new method ++ break; ++ case deleted: ++ // method deleted, see if it is OK ++ old_flags = (jushort) k_old_method->access_flags().get_flags(); ++ if ((old_flags & JVM_ACC_PRIVATE) == 0 ++ // hack: private should be treated as final, but alas ++ || (old_flags & (JVM_ACC_FINAL|JVM_ACC_STATIC)) == 0 ++ ) { ++ // deleted methods must be private ++ result = result | Klass::ModifyClass; ++ } ++ RC_TRACE(0x00000001, ("Method deleted: old: %s [%d]", ++ k_old_method->name_and_sig_as_C_string(), oi)); ++ ++oi; // advance to next old method ++ break; ++ default: ++ ShouldNotReachHere(); + } -+ -+ // Advance to next thread -+ java_thread = java_thread->next(); + } + -+ return true; -+} ++ if (new_class()->size() != new_class->old_version()->size()) { ++ result |= Klass::ModifyClassSize; ++ } + -+bool VM_RedefineClasses::check_method(methodOop method) { ++ if (new_class->size_helper() != ((instanceKlass*)(new_class->old_version()->klass_part()))->size_helper()) { ++ result |= Klass::ModifyInstanceSize; ++ } + ++ methodHandle instanceTransformerMethod(new_class->find_method(vmSymbols::transformer_name(), vmSymbols::void_method_signature())); ++ if (!instanceTransformerMethod.is_null() && !instanceTransformerMethod->is_static()) { ++ result |= Klass::HasInstanceTransformer; ++ } + -+ return true; ++ // (tw) Check method bodies to be able to return NoChange? ++ return result; +} + -+// Warning: destroys redefinition level values of klasses. -+bool VM_RedefineClasses::check_loaded_methods() { ++void VM_RedefineClasses::calculate_instance_update_information(klassOop new_version) { + -+ class CheckLoadedMethodsClosure : public ObjectClosure { ++ class UpdateFieldsEvolutionClosure : public FieldEvolutionClosure { + + private: -+ -+ bool _result; -+ GrowableArray *_dangerous_klasses; + -+ public: -+ CheckLoadedMethodsClosure(GrowableArray *dangerous_klasses) { -+ _result = true; -+ _dangerous_klasses = dangerous_klasses; -+ } ++ GrowableArray info; ++ int curPosition; ++ bool copy_backwards; + -+ bool result() { -+ return _result; -+ } ++ public: + -+ bool is_class_dangerous(klassOop k) { -+ return k->klass_part()->newest_version()->klass_part()->check_redefinition_flag(Klass::RemoveSuperType); ++ bool does_copy_backwards() { ++ return copy_backwards; + } + -+ bool can_be_affected(instanceKlass *klass) { -+ -+ constantPoolOop cp = klass->constants(); -+ -+ Thread *THREAD = Thread::current(); -+ klassOop k; -+ Symbol* symbol; -+ -+ for (int i=1; ilength(); i++) { -+ jbyte tag = cp->tag_at(i).value(); -+ switch(tag) { -+ case JVM_CONSTANT_Long: -+ case JVM_CONSTANT_Double: -+ i++; -+ break; -+ -+ case JVM_CONSTANT_Utf8: -+ case JVM_CONSTANT_Unicode: -+ case JVM_CONSTANT_Integer: -+ case JVM_CONSTANT_Float: -+ case JVM_CONSTANT_String: -+ case JVM_CONSTANT_Fieldref: -+ case JVM_CONSTANT_Methodref: -+ case JVM_CONSTANT_InterfaceMethodref: -+ case JVM_CONSTANT_ClassIndex: -+ case JVM_CONSTANT_UnresolvedString: -+ case JVM_CONSTANT_StringIndex: -+ case JVM_CONSTANT_UnresolvedClassInError: -+ case JVM_CONSTANT_Object: -+ // do nothing -+ break; -+ -+ case JVM_CONSTANT_Class: -+ k = cp->klass_at(i, CHECK_(true)); -+ if (is_class_dangerous(k)) { -+ RC_TRACE(0x00000002, ("Class %s is potentially affected, because at cp[%d] references class %s", -+ klass->name()->as_C_string(), -+ i, -+ k->klass_part()->name()->as_C_string())); -+ return true; -+ } -+ break; -+ -+ case JVM_CONSTANT_NameAndType: -+ symbol = cp->symbol_at(cp->signature_ref_index_at(i)); -+ if (symbol->byte_at(0) == '(') { -+ // This must be a method -+ SignatureStream signatureStream(symbol); -+ while (true) { -+ -+ if (signatureStream.is_array()) { -+ Symbol* cur_signature = signatureStream.as_symbol(Thread::current()); -+ if (is_type_signature_dangerous(cur_signature)) { -+ return true; -+ } -+ } else if (signatureStream.is_object()) { -+ if (is_symbol_dangerous(signatureStream.as_symbol(Thread::current()))) { -+ return true; -+ } -+ } -+ -+ if (signatureStream.at_return_type()) { -+ break; -+ } -+ -+ signatureStream.next(); -+ } -+ -+ } else if (is_type_signature_dangerous(symbol)) { -+ return true; -+ } -+ break; ++ UpdateFieldsEvolutionClosure(klassOop klass) { + -+ case JVM_CONSTANT_UnresolvedClass: -+ symbol = cp->unresolved_klass_at(i); -+ if (is_symbol_dangerous(symbol)) { -+ return true; -+ } -+ break; ++ int base_offset = instanceOopDesc::base_offset_in_bytes(); + -+ default: -+ ShouldNotReachHere(); -+ } ++ if (klass->klass_part()->newest_version() == SystemDictionary::Reference_klass()->klass_part()->newest_version()) { ++ base_offset += java_lang_ref_Reference::number_of_fake_oop_fields*size_of_type(T_OBJECT); + } + -+ return false; ++ info.append(base_offset); ++ info.append(0); ++ curPosition = base_offset; ++ copy_backwards = false; + } + -+ bool is_type_signature_dangerous(Symbol* signature) { -+ // This must be a field type -+ if (FieldType::is_obj(signature)) { -+ Symbol* name = signature_to_class_name(signature); -+ if (is_symbol_dangerous(name)) { -+ return true; -+ } -+ } else if (FieldType::is_array(signature)) { -+ //jint dimension; -+ //Symbol* object_key; -+ FieldArrayInfo fd; -+ FieldType::get_array_info(signature, fd, Thread::current()); -+ if (is_symbol_dangerous(fd.object_key())) { -+ return true; -+ } -+ } -+ return false; ++ GrowableArray &finish() { ++ info.append(0); ++ return info; + } + -+ bool is_symbol_dangerous(Symbol* symbol) { -+ for (int i=0; i<_dangerous_klasses->length(); i++) { -+ if(_dangerous_klasses->at(i)->klass_part()->name() == symbol) { -+ RC_TRACE(0x00000002, ("Found constant pool index %d references class %s", -+ i, -+ symbol->as_C_string())); -+ return true; -+ } ++ virtual void do_new_field(fieldDescriptor* fd){ ++ int alignment = fd->offset() - curPosition; ++ if (alignment > 0) { ++ // This field was aligned, so we need to make sure that we fill the gap ++ fill(alignment); + } -+ return false; -+ } -+ -+ virtual void do_object(oop obj) { -+ -+ if (!_result) return; -+ -+ klassOop klassObj = (klassOop)obj; -+ Thread *THREAD = Thread::current(); -+ -+ // We found an instance klass! -+ instanceKlass *klass = instanceKlass::cast(klassObj); -+ instanceKlassHandle handle(klassObj); + -+ RC_TRACE(0x00000400, ("Check if verification is necessary for class %s major_version=%d", handle->name()->as_C_string(), handle->major_version())); ++ int size = size_of_type(fd->field_type()); ++ fill(size); ++ } + -+ if (!can_be_affected(klass)) { -+ RC_TRACE(0x00000400, ("Skipping verification of class %s major_version=%d", handle->name()->as_C_string(), handle->major_version())); -+ return; -+ } ++ private: + -+ if (handle->major_version() < Verifier::STACKMAP_ATTRIBUTE_MAJOR_VERSION) { -+ RC_TRACE(0x00000001, ("Failing because cannot verify class %s major_version=%d", handle->name()->as_C_string(), handle->major_version())); -+ _result = false; -+ return; ++ void fill(int size) { ++ if (info.length() > 0 && info.at(info.length() - 1) < 0) { ++ (*info.adr_at(info.length() - 1)) -= size; ++ } else { ++ info.append(-size); + } + -+ RC_TRACE(0x00000001, ("Verifying class %s", handle->name()->as_C_string())); -+ -+ if (!Verifier::verify(handle, Verifier::NoException, true, false, Thread::current())) { -+ -+ RC_TRACE(0x00000001, ("Verification of class %s failed", handle->name()->as_C_string())); -+ //Symbol* ex_name = PENDING_EXCEPTION->klass()->klass_part()->name(); -+ //RC_TRACE(0x00000002, ("exception when verifying class: '%s'", ex_name->as_C_string()); -+ //PENDING_EXCEPTION->print(); -+ CLEAR_PENDING_EXCEPTION; -+ _result = false; -+ } -+ -+ /*int method_count = klass->methods()->length(); -+ for (int i=0; imethods()->obj_at(i); -+ if (!check_method(cur_method)) { -+ RC_TRACE(0x00000001, ("Failed to verify consistency of method %s of klass %s", cur_method->name()->as_C_string(), klass->name()->as_C_string()); -+ } -+ }*/ -+ } -+ }; -+ -+ // TODO: Check bytecodes in case of interface => class or class => interface etc.. -+ -+ GrowableArray dangerous_klasses; -+ for (int i=0; i<_new_classes->length(); i++) { -+ instanceKlassHandle handle = _new_classes->at(i); -+ if (handle->check_redefinition_flag(Klass::RemoveSuperType)) { -+ dangerous_klasses.append(handle()); ++ curPosition += size; + } -+ } -+ -+ CheckLoadedMethodsClosure checkLoadedMethodsClosure(&dangerous_klasses); -+ Thread::current()->set_pretend_new_universe(true); -+ SystemDictionary::classes_do(&checkLoadedMethodsClosure); -+ Thread::current()->set_pretend_new_universe(false); + ++ int size_of_type(BasicType type) { ++ int size = 0; ++ switch(type) { ++ case T_BOOLEAN: ++ size = sizeof(jboolean); ++ break; + -+ return checkLoadedMethodsClosure.result(); -+} ++ case T_CHAR: ++ size = (sizeof(jchar)); ++ break; + -+bool VM_RedefineClasses::check_type_consistency() { ++ case T_FLOAT: ++ size = (sizeof(jfloat)); ++ break; + -+ Universe::set_verify_in_progress(true); ++ case T_DOUBLE: ++ size = (sizeof(jdouble)); ++ break; + -+ SystemDictionary::classes_do(calculate_type_check_information); -+ bool result = check_field_value_types(); -+ SystemDictionary::classes_do(clear_type_check_information); -+ if (!result) { -+ RC_TRACE(0x00000001, ("Aborting redefinition because of wrong field or array element value!")); -+ Universe::set_verify_in_progress(false); - return false; - } - -- u1 tag = annotations_typeArray->byte_at(byte_i_ref); -- byte_i_ref++; -- RC_TRACE_WITH_THREAD(0x02000000, THREAD, ("tag='%c'", tag)); -+ result = check_method_stacks(); -+ if (!result) { -+ RC_TRACE(0x00000001, ("Aborting redefinition because of wrong value on the stack")); -+ Universe::set_verify_in_progress(false); -+ return false; -+ } - -- switch (tag) { -- // These BaseType tag values are from Table 4.2 in VM spec: -- case 'B': // byte -- case 'C': // char -- case 'D': // double -- case 'F': // float -- case 'I': // int -- case 'J': // long -- case 'S': // short -- case 'Z': // boolean -+ result = check_loaded_methods(); -+ if (!result) { -+ RC_TRACE(0x00000001, ("Aborting redefinition because of wrong loaded method")); -+ Universe::set_verify_in_progress(false); -+ return false; -+ } - -- // The remaining tag values are from Table 4.8 in the 2nd-edition of -- // the VM spec: -- case 's': -- { -- // For the above tag values (including the BaseType values), -- // value.const_value_index is right union field. -+ RC_TRACE(0x00000001, ("Verification passed => hierarchy change is valid!")); -+ Universe::set_verify_in_progress(false); -+ return true; -+} - -- if ((byte_i_ref + 2) > annotations_typeArray->length()) { -- // not enough room for a const_value_index -- RC_TRACE_WITH_THREAD(0x02000000, THREAD, -- ("length() is too small for a const_value_index")); -- return false; -+void VM_RedefineClasses::rollback() { -+ RC_TRACE(0x00000001, ("Rolling back redefinition!")); -+ SystemDictionary::rollback_redefinition(); ++ case T_BYTE: ++ size = (sizeof(jbyte)); ++ break; + -+ RC_TRACE(0x00000001, ("After rolling back system dictionary!")); -+ for (int i=0; i<_new_classes->length(); i++) { -+ SystemDictionary::remove_from_hierarchy(_new_classes->at(i)); -+ } ++ case T_SHORT: ++ size = (sizeof(jshort)); ++ break; + -+ for (int i=0; i<_new_classes->length(); i++) { -+ instanceKlassHandle new_class = _new_classes->at(i); -+ new_class->set_redefining(false); -+ new_class->old_version()->klass_part()->set_new_version(NULL); -+ new_class->set_old_version(NULL); -+ } ++ case T_INT: ++ size = (sizeof(jint)); ++ break; + -+} ++ case T_LONG: ++ size = (sizeof(jlong)); ++ break; + -+template void VM_RedefineClasses::do_oop_work(T* p) { -+ T heap_oop = oopDesc::load_heap_oop(p); -+ if (!oopDesc::is_null(heap_oop)) { -+ oop obj = oopDesc::decode_heap_oop_not_null(heap_oop); -+ if (obj->is_instanceKlass()) { -+ klassOop klass = (klassOop)obj; -+ // DCEVM: note: can overwrite owner of old_klass constants pool with new_klass, so we need to fix it back later -+ if (klass->new_version() != NULL && klass->new_version()->klass_part()->is_redefining()) { -+ obj = klass->klass_part()->new_version(); -+ oopDesc::encode_store_heap_oop_not_null(p, obj); - } -+ } else if (obj->blueprint()->newest_version() == SystemDictionary::Class_klass()->klass_part()->newest_version()) { -+ // update references to java.lang.Class to point to newest version. Only update references to non-primitive -+ // java.lang.Class instances. -+ klassOop klass_oop = java_lang_Class::as_klassOop(obj); -+ if (klass_oop != NULL) { -+ if (klass_oop->new_version() != NULL && klass_oop->new_version()->klass_part()->is_redefining()) { -+ obj = klass_oop->new_version()->java_mirror(); -+ } else if (klass_oop->klass_part()->is_redefining()) { -+ obj = klass_oop->java_mirror(); -+ } -+ oopDesc::encode_store_heap_oop_not_null(p, obj); - -- u2 const_value_index = rewrite_cp_ref_in_annotation_data( -- annotations_typeArray, byte_i_ref, -- "mapped old const_value_index=%d", THREAD); - -- RC_TRACE_WITH_THREAD(0x02000000, THREAD, -- ("const_value_index=%d", const_value_index)); -- } break; -- -- case 'e': -- { -- // for the above tag value, value.enum_const_value is right union field -- -- if ((byte_i_ref + 4) > annotations_typeArray->length()) { -- // not enough room for a enum_const_value -- RC_TRACE_WITH_THREAD(0x02000000, THREAD, -- ("length() is too small for a enum_const_value")); -- return false; -- } -- -- u2 type_name_index = rewrite_cp_ref_in_annotation_data( -- annotations_typeArray, byte_i_ref, -- "mapped old type_name_index=%d", THREAD); -- -- u2 const_name_index = rewrite_cp_ref_in_annotation_data( -- annotations_typeArray, byte_i_ref, -- "mapped old const_name_index=%d", THREAD); -- -- RC_TRACE_WITH_THREAD(0x02000000, THREAD, -- ("type_name_index=%d const_name_index=%d", type_name_index, -- const_name_index)); -- } break; -- -- case 'c': -- { -- // for the above tag value, value.class_info_index is right union field -- -- if ((byte_i_ref + 2) > annotations_typeArray->length()) { -- // not enough room for a class_info_index -- RC_TRACE_WITH_THREAD(0x02000000, THREAD, -- ("length() is too small for a class_info_index")); -- return false; -- } -- -- u2 class_info_index = rewrite_cp_ref_in_annotation_data( -- annotations_typeArray, byte_i_ref, -- "mapped old class_info_index=%d", THREAD); -- -- RC_TRACE_WITH_THREAD(0x02000000, THREAD, -- ("class_info_index=%d", class_info_index)); -- } break; -- -- case '@': -- // For the above tag value, value.attr_value is the right union -- // field. This is a nested annotation. -- if (!rewrite_cp_refs_in_annotation_struct(annotations_typeArray, -- byte_i_ref, THREAD)) { -- // propagate failure back to caller -- return false; -- } -- break; -- -- case '[': -- { -- if ((byte_i_ref + 2) > annotations_typeArray->length()) { -- // not enough room for a num_values field -- RC_TRACE_WITH_THREAD(0x02000000, THREAD, -- ("length() is too small for a num_values field")); -- return false; -- } -- -- // For the above tag value, value.array_value is the right union -- // field. This is an array of nested element_value. -- u2 num_values = Bytes::get_Java_u2((address) -- annotations_typeArray->byte_at_addr(byte_i_ref)); -- byte_i_ref += 2; -- RC_TRACE_WITH_THREAD(0x02000000, THREAD, ("num_values=%d", num_values)); -- -- int calc_num_values = 0; -- for (; calc_num_values < num_values; calc_num_values++) { -- if (!rewrite_cp_refs_in_element_value( -- annotations_typeArray, byte_i_ref, THREAD)) { -- RC_TRACE_WITH_THREAD(0x02000000, THREAD, -- ("bad nested element_value at %d", calc_num_values)); -- // propagate failure back to caller -- return false; -+ // FIXME: DCEVM: better implementation? -+ // Starting from JDK 7 java_mirror can be kept in the regular heap. Therefore, it is possible -+ // that new java_mirror is in the young generation whereas p is in tenured generation. In that -+ // case we need to run write barrier to make sure card table is properly updated. This will -+ // allow JVM to detect reference in tenured generation properly during young generation GC. -+ if (Universe::heap()->is_in_reserved(p)) { -+ if (GenCollectedHeap::heap()->is_in_young(obj)) { -+ GenRemSet* rs = GenCollectedHeap::heap()->rem_set(); -+ assert(rs->rs_kind() == GenRemSet::CardTable, "Wrong rem set kind."); -+ CardTableRS* _rs = (CardTableRS*)rs; -+ _rs->inline_write_ref_field_gc(p, obj); -+ } - } - } -- assert(num_values == calc_num_values, "sanity check"); -- } break; -- -- default: -- RC_TRACE_WITH_THREAD(0x02000000, THREAD, ("bad tag=0x%x", tag)); -- return false; -- } // end decode tag field -- -- return true; --} // end rewrite_cp_refs_in_element_value() -- -- --// Rewrite constant pool references in a fields_annotations field. --bool VM_RedefineClasses::rewrite_cp_refs_in_fields_annotations( -- instanceKlassHandle scratch_class, TRAPS) { -- -- objArrayHandle fields_annotations(THREAD, -- scratch_class->fields_annotations()); -- -- if (fields_annotations.is_null() || fields_annotations->length() == 0) { -- // no fields_annotations so nothing to do -- return true; -- } -- -- RC_TRACE_WITH_THREAD(0x02000000, THREAD, -- ("fields_annotations length=%d", fields_annotations->length())); -- -- for (int i = 0; i < fields_annotations->length(); i++) { -- typeArrayHandle field_annotations(THREAD, -- (typeArrayOop)fields_annotations->obj_at(i)); -- if (field_annotations.is_null() || field_annotations->length() == 0) { -- // this field does not have any annotations so skip it -- continue; -- } -- -- int byte_i = 0; // byte index into field_annotations -- if (!rewrite_cp_refs_in_annotations_typeArray(field_annotations, byte_i, -- THREAD)) { -- RC_TRACE_WITH_THREAD(0x02000000, THREAD, -- ("bad field_annotations at %d", i)); -- // propagate failure back to caller -- return false; - } - } ++ case T_OBJECT: ++ case T_ARRAY: ++ if (UseCompressedOops) { ++ size = sizeof(narrowOop); ++ } else { ++ size = (sizeof(oop)); ++ } ++ break; ++ ++ default: ++ ShouldNotReachHere(); ++ } ++ ++ assert(size > 0, ""); ++ return size; ++ ++ } ++ ++ public: ++ ++ virtual void do_old_field(fieldDescriptor* fd){} ++ ++ virtual void do_changed_field(fieldDescriptor* old_fd, fieldDescriptor *new_fd){ ++ ++ int alignment = new_fd->offset() - curPosition; ++ if (alignment > 0) { ++ // This field was aligned, so we need to make sure that we fill the gap ++ fill(alignment); ++ } ++ ++ assert(old_fd->field_type() == new_fd->field_type(), ""); ++ assert(curPosition == new_fd->offset(), "must be correct offset!"); ++ ++ int offset = old_fd->offset(); ++ int size = size_of_type(old_fd->field_type()); ++ ++ int prevEnd = -1; ++ if (info.length() > 0 && info.at(info.length() - 1) > 0) { ++ prevEnd = info.at(info.length() - 2) + info.at(info.length() - 1); ++ } ++ ++ if (prevEnd == offset) { ++ info.at_put(info.length() - 2, info.at(info.length() - 2) + size); ++ } else { ++ info.append(size); ++ info.append(offset); ++ } ++ ++ if (old_fd->offset() < new_fd->offset()) { ++ copy_backwards = true; ++ } ++ ++ transfer_special_access_flags(old_fd, new_fd); ++ ++ curPosition += size; ++ } ++ }; ++ ++ UpdateFieldsEvolutionClosure cl(new_version); ++ ((instanceKlass*)new_version->klass_part())->do_fields_evolution(&cl); ++ ++ GrowableArray result = cl.finish(); ++ ((instanceKlass*)new_version->klass_part())->store_update_information(result); ++ ((instanceKlass*)new_version->klass_part())->set_copying_backwards(cl.does_copy_backwards()); ++ ++ if (RC_TRACE_ENABLED(0x00000002)) { ++ RC_TRACE(0x00000002, ("Instance update information for %s:", ++ new_version->klass_part()->name()->as_C_string())); ++ if (cl.does_copy_backwards()) { ++ RC_TRACE(0x00000002, ("\tDoes copy backwards!")); ++ } ++ for (int i=0; i 0) { ++ RC_TRACE(0x00000002, ("\t%d COPY from %d", curNum, result.at(i + 1))); ++ i++; ++ } else { ++ RC_TRACE(0x00000002, ("\tEND")); ++ } ++ } ++ } +} - -- return true; --} // end rewrite_cp_refs_in_fields_annotations() -+void VM_RedefineClasses::swap_marks(oop first, oop second) { -+ markOop first_mark = first->mark(); -+ markOop second_mark = second->mark(); -+ first->set_mark(second_mark); -+ second->set_mark(first_mark); ++ ++Symbol* VM_RedefineClasses::signature_to_class_name(Symbol* signature) { ++ assert(FieldType::is_obj(signature), ""); ++ return SymbolTable::new_symbol(signature->as_C_string() + 1, signature->utf8_length() - 2, Thread::current()); +} - -+void VM_RedefineClasses::doit() { -+ Thread *thread = Thread::current(); - --// Rewrite constant pool references in a methods_annotations field. --bool VM_RedefineClasses::rewrite_cp_refs_in_methods_annotations( -- instanceKlassHandle scratch_class, TRAPS) { -+ RC_TRACE(0x00000001, ("Entering doit!")); - -- objArrayHandle methods_annotations(THREAD, -- scratch_class->methods_annotations()); - -- if (methods_annotations.is_null() || methods_annotations->length() == 0) { -- // no methods_annotations so nothing to do -- return true; -- } -+ if ((_max_redefinition_flags & Klass::RemoveSuperType) != 0) { - -- RC_TRACE_WITH_THREAD(0x02000000, THREAD, -- ("methods_annotations length=%d", methods_annotations->length())); -+ RC_TIMER_START(_timer_check_type); - -- for (int i = 0; i < methods_annotations->length(); i++) { -- typeArrayHandle method_annotations(THREAD, -- (typeArrayOop)methods_annotations->obj_at(i)); -- if (method_annotations.is_null() || method_annotations->length() == 0) { -- // this method does not have any annotations so skip it -- continue; -- } -- -- int byte_i = 0; // byte index into method_annotations -- if (!rewrite_cp_refs_in_annotations_typeArray(method_annotations, byte_i, -- THREAD)) { -- RC_TRACE_WITH_THREAD(0x02000000, THREAD, -- ("bad method_annotations at %d", i)); -- // propagate failure back to caller -- return false; -- } -- } -- -- return true; --} // end rewrite_cp_refs_in_methods_annotations() -- -- --// Rewrite constant pool references in a methods_parameter_annotations --// field. This "structure" is adapted from the --// RuntimeVisibleParameterAnnotations_attribute described in section --// 4.8.17 of the 2nd-edition of the VM spec: --// --// methods_parameter_annotations_typeArray { --// u1 num_parameters; --// { --// u2 num_annotations; --// annotation annotations[num_annotations]; --// } parameter_annotations[num_parameters]; --// } --// --bool VM_RedefineClasses::rewrite_cp_refs_in_methods_parameter_annotations( -- instanceKlassHandle scratch_class, TRAPS) { -- -- objArrayHandle methods_parameter_annotations(THREAD, -- scratch_class->methods_parameter_annotations()); -- -- if (methods_parameter_annotations.is_null() -- || methods_parameter_annotations->length() == 0) { -- // no methods_parameter_annotations so nothing to do -- return true; -- } -- -- RC_TRACE_WITH_THREAD(0x02000000, THREAD, -- ("methods_parameter_annotations length=%d", -- methods_parameter_annotations->length())); -- -- for (int i = 0; i < methods_parameter_annotations->length(); i++) { -- typeArrayHandle method_parameter_annotations(THREAD, -- (typeArrayOop)methods_parameter_annotations->obj_at(i)); -- if (method_parameter_annotations.is_null() -- || method_parameter_annotations->length() == 0) { -- // this method does not have any parameter annotations so skip it -- continue; -- } -- -- if (method_parameter_annotations->length() < 1) { -- // not enough room for a num_parameters field -- RC_TRACE_WITH_THREAD(0x02000000, THREAD, -- ("length() is too small for a num_parameters field at %d", i)); -- return false; ++ ++void VM_RedefineClasses::calculate_type_check_information(klassOop klass) { ++ if (klass->klass_part()->is_redefining()) { ++ klass = klass->klass_part()->old_version(); ++ } ++ ++ // We found an instance klass! ++ instanceKlass *cur_instance_klass = instanceKlass::cast(klass); ++ GrowableArray< Pair > type_check_information; ++ ++ class MyFieldClosure : public FieldClosure { ++ ++ public: ++ ++ GrowableArray< Pair > *_arr; ++ ++ MyFieldClosure(GrowableArray< Pair > *arr) { ++ _arr = arr; ++ } ++ ++ virtual void do_field(fieldDescriptor* fd) { ++ if (fd->field_type() == T_OBJECT) { ++ Symbol* signature = fd->signature(); ++ if (FieldType::is_obj(signature)) { ++ Symbol* name = signature_to_class_name(signature); ++ klassOop field_klass; ++ if (is_field_dangerous(name, fd, field_klass)) { ++ RC_TRACE(0x00000002, ("Found dangerous field %s in klass %s of type %s", ++ fd->name()->as_C_string(), ++ fd->field_holder()->klass_part()->name()->as_C_string(), ++ name->as_C_string())); ++ _arr->append(Pair(fd->offset(), field_klass->klass_part()->newest_version())); ++ } ++ } ++ ++ // Array fields can never be a problem! ++ } ++ } ++ ++ bool is_field_dangerous(Symbol* klass_name, fieldDescriptor *fd, klassOop &field_klass) { ++ field_klass = SystemDictionary::find(klass_name, fd->field_holder()->klass_part()->class_loader(), ++ fd->field_holder()->klass_part()->protection_domain(), Thread::current()); ++ if(field_klass != NULL) { ++ if (field_klass->klass_part()->is_redefining()) { ++ field_klass = field_klass->klass_part()->old_version(); ++ } ++ if (field_klass->klass_part()->has_subtype_changed()) { ++ return true; ++ } ++ } ++ return false; ++ } ++ }; ++ ++ MyFieldClosure fieldClosure(&type_check_information); ++ cur_instance_klass->do_nonstatic_fields(&fieldClosure); ++ ++ if (type_check_information.length() > 0) { ++ type_check_information.append(Pair(-1, NULL)); ++ cur_instance_klass->store_type_check_information(type_check_information); ++ } ++} ++ ++bool VM_RedefineClasses::check_field_value_types() { ++ ++ Thread *THREAD = Thread::current(); ++ class CheckFieldTypesClosure : public ObjectClosure { ++ ++ private: ++ ++ bool _result; ++ ++ public: ++ ++ CheckFieldTypesClosure() { ++ _result = true; ++ } ++ ++ bool result() { return _result; } ++ ++ virtual void do_object(oop obj) { ++ ++ if (!_result) { ++ return; ++ } ++ ++ if (obj->is_objArray()) { ++ ++ objArrayOop array = objArrayOop(obj); ++ ++ klassOop element_klass = objArrayKlass::cast(array->klass())->element_klass(); ++ ++ if (element_klass->klass_part()->has_subtype_changed()) { ++ int length = array->length(); ++ for (int i=0; iobj_at(i); ++ if (element != NULL && element->blueprint()->newest_version()->klass_part()->is_redefining()) { ++ // Check subtype relationship to static type of array ++ if (!element->blueprint()->newest_version()->klass_part()->is_subtype_of(element_klass->klass_part()->newest_version())) { ++ RC_TRACE(0x00000001, ("Array value is INVALID - abort redefinition (static_type=%s, index=%d, dynamic_type=%s)", ++ element_klass->klass_part()->name()->as_C_string(), ++ i, ++ element->blueprint()->name()->as_C_string())); ++ _result = false; ++ break; ++ } ++ } ++ } ++ } ++ ++ } else { ++ Pair *cur = obj->klass()->klass_part()->type_check_information(); ++ if (cur != NULL) { ++ // Type check information exists for this oop ++ while ((*cur).left() != -1) { ++ check_field(obj, (*cur).left(), (*cur).right()); ++ cur++; ++ } + } + } + } + +- Rewriter::rewrite(scratch_class, THREAD); +- if (!HAS_PENDING_EXCEPTION) { +- Rewriter::relocate_and_link(scratch_class, THREAD); - } -- -- int byte_i = 0; // byte index into method_parameter_annotations -- -- u1 num_parameters = method_parameter_annotations->byte_at(byte_i); -- byte_i++; -- -- RC_TRACE_WITH_THREAD(0x02000000, THREAD, -- ("num_parameters=%d", num_parameters)); -- -- int calc_num_parameters = 0; -- for (; calc_num_parameters < num_parameters; calc_num_parameters++) { -- if (!rewrite_cp_refs_in_annotations_typeArray( -- method_parameter_annotations, byte_i, THREAD)) { -- RC_TRACE_WITH_THREAD(0x02000000, THREAD, -- ("bad method_parameter_annotations at %d", calc_num_parameters)); -- // propagate failure back to caller -- return false; +- if (HAS_PENDING_EXCEPTION) { +- Symbol* ex_name = PENDING_EXCEPTION->klass()->klass_part()->name(); +- CLEAR_PENDING_EXCEPTION; +- if (ex_name == vmSymbols::java_lang_OutOfMemoryError()) { +- return JVMTI_ERROR_OUT_OF_MEMORY; +- } else { +- return JVMTI_ERROR_INTERNAL; - } - } -- assert(num_parameters == calc_num_parameters, "sanity check"); -- } -- -- return true; --} // end rewrite_cp_refs_in_methods_parameter_annotations() -- -- --// Rewrite constant pool references in a methods_default_annotations --// field. This "structure" is adapted from the AnnotationDefault_attribute --// that is described in section 4.8.19 of the 2nd-edition of the VM spec: --// --// methods_default_annotations_typeArray { --// element_value default_value; --// } --// --bool VM_RedefineClasses::rewrite_cp_refs_in_methods_default_annotations( -- instanceKlassHandle scratch_class, TRAPS) { - -- objArrayHandle methods_default_annotations(THREAD, -- scratch_class->methods_default_annotations()); +- _scratch_classes[i] = scratch_class; - -- if (methods_default_annotations.is_null() -- || methods_default_annotations->length() == 0) { -- // no methods_default_annotations so nothing to do -- return true; +- // RC_TRACE_WITH_THREAD macro has an embedded ResourceMark +- RC_TRACE_WITH_THREAD(0x00000001, THREAD, +- ("loaded name=%s (avail_mem=" UINT64_FORMAT "K)", +- the_class->external_name(), os::available_memory() >> 10)); - } - -- RC_TRACE_WITH_THREAD(0x02000000, THREAD, -- ("methods_default_annotations length=%d", -- methods_default_annotations->length())); -- -- for (int i = 0; i < methods_default_annotations->length(); i++) { -- typeArrayHandle method_default_annotations(THREAD, -- (typeArrayOop)methods_default_annotations->obj_at(i)); -- if (method_default_annotations.is_null() -- || method_default_annotations->length() == 0) { -- // this method does not have any default annotations so skip it -- continue; -- } +- return JVMTI_ERROR_NONE; +-} - -- int byte_i = 0; // byte index into method_default_annotations - -- if (!rewrite_cp_refs_in_element_value( -- method_default_annotations, byte_i, THREAD)) { -- RC_TRACE_WITH_THREAD(0x02000000, THREAD, -- ("bad default element_value at %d", i)); -- // propagate failure back to caller -- return false; -- } +-// Map old_index to new_index as needed. scratch_cp is only needed +-// for RC_TRACE() calls. +-void VM_RedefineClasses::map_index(constantPoolHandle scratch_cp, +- int old_index, int new_index) { +- if (find_new_index(old_index) != 0) { +- // old_index is already mapped +- return; - } - -- return true; --} // end rewrite_cp_refs_in_methods_default_annotations() -- -- --// Rewrite constant pool references in the method's stackmap table. --// These "structures" are adapted from the StackMapTable_attribute that --// is described in section 4.8.4 of the 6.0 version of the VM spec --// (dated 2005.10.26): --// file:///net/quincunx.sfbay/export/gbracha/ClassFile-Java6.pdf --// --// stack_map { --// u2 number_of_entries; --// stack_map_frame entries[number_of_entries]; --// } --// --void VM_RedefineClasses::rewrite_cp_refs_in_stack_map_table( -- methodHandle method, TRAPS) { -- -- if (!method->has_stackmap_table()) { +- if (old_index == new_index) { +- // no mapping is needed - return; - } - -- typeArrayOop stackmap_data = method->stackmap_data(); -- address stackmap_p = (address)stackmap_data->byte_at_addr(0); -- address stackmap_end = stackmap_p + stackmap_data->length(); -- -- assert(stackmap_p + 2 <= stackmap_end, "no room for number_of_entries"); -- u2 number_of_entries = Bytes::get_Java_u2(stackmap_p); -- stackmap_p += 2; -- -- RC_TRACE_WITH_THREAD(0x04000000, THREAD, -- ("number_of_entries=%u", number_of_entries)); -- -- // walk through each stack_map_frame -- u2 calc_number_of_entries = 0; -- for (; calc_number_of_entries < number_of_entries; calc_number_of_entries++) { -- // The stack_map_frame structure is a u1 frame_type followed by -- // 0 or more bytes of data: -- // -- // union stack_map_frame { -- // same_frame; -- // same_locals_1_stack_item_frame; -- // same_locals_1_stack_item_frame_extended; -- // chop_frame; -- // same_frame_extended; -- // append_frame; -- // full_frame; -- // } -- -- assert(stackmap_p + 1 <= stackmap_end, "no room for frame_type"); -- // The Linux compiler does not like frame_type to be u1 or u2. It -- // issues the following warning for the first if-statement below: -- // -- // "warning: comparison is always true due to limited range of data type" -- // -- u4 frame_type = *stackmap_p; -- stackmap_p++; -- -- // same_frame { -- // u1 frame_type = SAME; /* 0-63 */ -- // } -- if (frame_type >= 0 && frame_type <= 63) { -- // nothing more to do for same_frame -- } -- -- // same_locals_1_stack_item_frame { -- // u1 frame_type = SAME_LOCALS_1_STACK_ITEM; /* 64-127 */ -- // verification_type_info stack[1]; -- // } -- else if (frame_type >= 64 && frame_type <= 127) { -- rewrite_cp_refs_in_verification_type_info(stackmap_p, stackmap_end, -- calc_number_of_entries, frame_type, THREAD); -- } -- -- // reserved for future use -- else if (frame_type >= 128 && frame_type <= 246) { -- // nothing more to do for reserved frame_types -- } -- -- // same_locals_1_stack_item_frame_extended { -- // u1 frame_type = SAME_LOCALS_1_STACK_ITEM_EXTENDED; /* 247 */ -- // u2 offset_delta; -- // verification_type_info stack[1]; -- // } -- else if (frame_type == 247) { -- stackmap_p += 2; -- rewrite_cp_refs_in_verification_type_info(stackmap_p, stackmap_end, -- calc_number_of_entries, frame_type, THREAD); -- } +- _index_map_p->at_put(old_index, new_index); +- _index_map_count++; - -- // chop_frame { -- // u1 frame_type = CHOP; /* 248-250 */ -- // u2 offset_delta; -- // } -- else if (frame_type >= 248 && frame_type <= 250) { -- stackmap_p += 2; -- } +- RC_TRACE(0x00040000, ("mapped tag %d at index %d to %d", +- scratch_cp->tag_at(old_index).value(), old_index, new_index)); +-} // end map_index() - -- // same_frame_extended { -- // u1 frame_type = SAME_FRAME_EXTENDED; /* 251*/ -- // u2 offset_delta; -- // } -- else if (frame_type == 251) { -- stackmap_p += 2; -- } - -- // append_frame { -- // u1 frame_type = APPEND; /* 252-254 */ -- // u2 offset_delta; -- // verification_type_info locals[frame_type - 251]; -- // } -- else if (frame_type >= 252 && frame_type <= 254) { -- assert(stackmap_p + 2 <= stackmap_end, -- "no room for offset_delta"); -- stackmap_p += 2; -- u1 len = frame_type - 251; -- for (u1 i = 0; i < len; i++) { -- rewrite_cp_refs_in_verification_type_info(stackmap_p, stackmap_end, -- calc_number_of_entries, frame_type, THREAD); -- } -- } +-// Map old_index to new_index as needed. +-void VM_RedefineClasses::map_operand_index(int old_index, int new_index) { +- if (find_new_operand_index(old_index) != -1) { +- // old_index is already mapped +- return; +- } - -- // full_frame { -- // u1 frame_type = FULL_FRAME; /* 255 */ -- // u2 offset_delta; -- // u2 number_of_locals; -- // verification_type_info locals[number_of_locals]; -- // u2 number_of_stack_items; -- // verification_type_info stack[number_of_stack_items]; -- // } -- else if (frame_type == 255) { -- assert(stackmap_p + 2 + 2 <= stackmap_end, -- "no room for smallest full_frame"); -- stackmap_p += 2; +- if (old_index == new_index) { +- // no mapping is needed +- return; +- } - -- u2 number_of_locals = Bytes::get_Java_u2(stackmap_p); -- stackmap_p += 2; +- _operands_index_map_p->at_put(old_index, new_index); +- _operands_index_map_count++; - -- for (u2 locals_i = 0; locals_i < number_of_locals; locals_i++) { -- rewrite_cp_refs_in_verification_type_info(stackmap_p, stackmap_end, -- calc_number_of_entries, frame_type, THREAD); -- } +- RC_TRACE(0x00040000, ("mapped bootstrap specifier at index %d to %d", old_index, new_index)); +-} // end map_index() - -- // Use the largest size for the number_of_stack_items, but only get -- // the right number of bytes. -- u2 number_of_stack_items = Bytes::get_Java_u2(stackmap_p); -- stackmap_p += 2; - -- for (u2 stack_i = 0; stack_i < number_of_stack_items; stack_i++) { -- rewrite_cp_refs_in_verification_type_info(stackmap_p, stackmap_end, -- calc_number_of_entries, frame_type, THREAD); -- } -- } -- } // end while there is a stack_map_frame -- assert(number_of_entries == calc_number_of_entries, "sanity check"); --} // end rewrite_cp_refs_in_stack_map_table() +-// Merge old_cp and scratch_cp and return the results of the merge via +-// merge_cp_p. The number of entries in *merge_cp_p is returned via +-// merge_cp_length_p. The entries in old_cp occupy the same locations +-// in *merge_cp_p. Also creates a map of indices from entries in +-// scratch_cp to the corresponding entry in *merge_cp_p. Index map +-// entries are only created for entries in scratch_cp that occupy a +-// different location in *merged_cp_p. +-bool VM_RedefineClasses::merge_constant_pools(constantPoolHandle old_cp, +- constantPoolHandle scratch_cp, constantPoolHandle *merge_cp_p, +- int *merge_cp_length_p, TRAPS) { - +- if (merge_cp_p == NULL) { +- assert(false, "caller must provide scatch constantPool"); +- return false; // robustness +- } +- if (merge_cp_length_p == NULL) { +- assert(false, "caller must provide scatch CP length"); +- return false; // robustness +- } +- // Worst case we need old_cp->length() + scratch_cp()->length(), +- // but the caller might be smart so make sure we have at least +- // the minimum. +- if ((*merge_cp_p)->length() < old_cp->length()) { +- assert(false, "merge area too small"); +- return false; // robustness +- } - --// Rewrite constant pool references in the verification type info --// portion of the method's stackmap table. These "structures" are --// adapted from the StackMapTable_attribute that is described in --// section 4.8.4 of the 6.0 version of the VM spec (dated 2005.10.26): --// file:///net/quincunx.sfbay/export/gbracha/ClassFile-Java6.pdf --// --// The verification_type_info structure is a u1 tag followed by 0 or --// more bytes of data: --// --// union verification_type_info { --// Top_variable_info; --// Integer_variable_info; --// Float_variable_info; --// Long_variable_info; --// Double_variable_info; --// Null_variable_info; --// UninitializedThis_variable_info; --// Object_variable_info; --// Uninitialized_variable_info; --// } --// --void VM_RedefineClasses::rewrite_cp_refs_in_verification_type_info( -- address& stackmap_p_ref, address stackmap_end, u2 frame_i, -- u1 frame_type, TRAPS) { +- RC_TRACE_WITH_THREAD(0x00010000, THREAD, +- ("old_cp_len=%d, scratch_cp_len=%d", old_cp->length(), +- scratch_cp->length())); - -- assert(stackmap_p_ref + 1 <= stackmap_end, "no room for tag"); -- u1 tag = *stackmap_p_ref; -- stackmap_p_ref++; +- { +- // Pass 0: +- // The old_cp is copied to *merge_cp_p; this means that any code +- // using old_cp does not have to change. This work looks like a +- // perfect fit for constantPoolOop::copy_cp_to(), but we need to +- // handle one special case: +- // - revert JVM_CONSTANT_Class to JVM_CONSTANT_UnresolvedClass +- // This will make verification happy. - -- switch (tag) { -- // Top_variable_info { -- // u1 tag = ITEM_Top; /* 0 */ -- // } -- // verificationType.hpp has zero as ITEM_Bogus instead of ITEM_Top -- case 0: // fall through +- int old_i; // index into old_cp - -- // Integer_variable_info { -- // u1 tag = ITEM_Integer; /* 1 */ -- // } -- case ITEM_Integer: // fall through +- // index zero (0) is not used in constantPools +- for (old_i = 1; old_i < old_cp->length(); old_i++) { +- // leave debugging crumb +- jbyte old_tag = old_cp->tag_at(old_i).value(); +- switch (old_tag) { +- case JVM_CONSTANT_Class: +- case JVM_CONSTANT_UnresolvedClass: +- // revert the copy to JVM_CONSTANT_UnresolvedClass +- // May be resolving while calling this so do the same for +- // JVM_CONSTANT_UnresolvedClass (klass_name_at() deals with transition) +- (*merge_cp_p)->unresolved_klass_at_put(old_i, +- old_cp->klass_name_at(old_i)); +- break; - -- // Float_variable_info { -- // u1 tag = ITEM_Float; /* 2 */ -- // } -- case ITEM_Float: // fall through +- case JVM_CONSTANT_Double: +- case JVM_CONSTANT_Long: +- // just copy the entry to *merge_cp_p, but double and long take +- // two constant pool entries +- constantPoolOopDesc::copy_entry_to(old_cp, old_i, *merge_cp_p, old_i, CHECK_0); +- old_i++; +- break; - -- // Double_variable_info { -- // u1 tag = ITEM_Double; /* 3 */ -- // } -- case ITEM_Double: // fall through +- default: +- // just copy the entry to *merge_cp_p +- constantPoolOopDesc::copy_entry_to(old_cp, old_i, *merge_cp_p, old_i, CHECK_0); +- break; +- } +- } // end for each old_cp entry - -- // Long_variable_info { -- // u1 tag = ITEM_Long; /* 4 */ -- // } -- case ITEM_Long: // fall through +- constantPoolOopDesc::copy_operands(old_cp, *merge_cp_p, CHECK_0); +- (*merge_cp_p)->extend_operands(scratch_cp, CHECK_0); - -- // Null_variable_info { -- // u1 tag = ITEM_Null; /* 5 */ -- // } -- case ITEM_Null: // fall through +- // We don't need to sanity check that *merge_cp_length_p is within +- // *merge_cp_p bounds since we have the minimum on-entry check above. +- (*merge_cp_length_p) = old_i; +- } - -- // UninitializedThis_variable_info { -- // u1 tag = ITEM_UninitializedThis; /* 6 */ -- // } -- case ITEM_UninitializedThis: -- // nothing more to do for the above tag types -- break; +- // merge_cp_len should be the same as old_cp->length() at this point +- // so this trace message is really a "warm-and-breathing" message. +- RC_TRACE_WITH_THREAD(0x00020000, THREAD, +- ("after pass 0: merge_cp_len=%d", *merge_cp_length_p)); - -- // Object_variable_info { -- // u1 tag = ITEM_Object; /* 7 */ -- // u2 cpool_index; -- // } -- case ITEM_Object: +- int scratch_i; // index into scratch_cp - { -- assert(stackmap_p_ref + 2 <= stackmap_end, "no room for cpool_index"); -- u2 cpool_index = Bytes::get_Java_u2(stackmap_p_ref); -- u2 new_cp_index = find_new_index(cpool_index); -- if (new_cp_index != 0) { -- RC_TRACE_WITH_THREAD(0x04000000, THREAD, -- ("mapped old cpool_index=%d", cpool_index)); -- Bytes::put_Java_u2(stackmap_p_ref, new_cp_index); -- cpool_index = new_cp_index; -- } -- stackmap_p_ref += 2; +- // Pass 1a: +- // Compare scratch_cp entries to the old_cp entries that we have +- // already copied to *merge_cp_p. In this pass, we are eliminating +- // exact duplicates (matching entry at same index) so we only +- // compare entries in the common indice range. +- int increment = 1; +- int pass1a_length = MIN2(old_cp->length(), scratch_cp->length()); +- for (scratch_i = 1; scratch_i < pass1a_length; scratch_i += increment) { +- switch (scratch_cp->tag_at(scratch_i).value()) { +- case JVM_CONSTANT_Double: +- case JVM_CONSTANT_Long: +- // double and long take two constant pool entries +- increment = 2; +- break; - -- RC_TRACE_WITH_THREAD(0x04000000, THREAD, -- ("frame_i=%u, frame_type=%u, cpool_index=%d", frame_i, -- frame_type, cpool_index)); -- } break; +- default: +- increment = 1; +- break; +- } - -- // Uninitialized_variable_info { -- // u1 tag = ITEM_Uninitialized; /* 8 */ -- // u2 offset; -- // } -- case ITEM_Uninitialized: -- assert(stackmap_p_ref + 2 <= stackmap_end, "no room for offset"); -- stackmap_p_ref += 2; -- break; +- bool match = scratch_cp->compare_entry_to(scratch_i, *merge_cp_p, +- scratch_i, CHECK_0); +- if (match) { +- // found a match at the same index so nothing more to do +- continue; +- } else if (is_unresolved_class_mismatch(scratch_cp, scratch_i, +- *merge_cp_p, scratch_i)) { +- // The mismatch in compare_entry_to() above is because of a +- // resolved versus unresolved class entry at the same index +- // with the same string value. Since Pass 0 reverted any +- // class entries to unresolved class entries in *merge_cp_p, +- // we go with the unresolved class entry. +- continue; +- } else if (is_unresolved_string_mismatch(scratch_cp, scratch_i, +- *merge_cp_p, scratch_i)) { +- // The mismatch in compare_entry_to() above is because of a +- // resolved versus unresolved string entry at the same index +- // with the same string value. We can live with whichever +- // happens to be at scratch_i in *merge_cp_p. +- continue; +- } +- +- int found_i = scratch_cp->find_matching_entry(scratch_i, *merge_cp_p, +- CHECK_0); +- if (found_i != 0) { +- guarantee(found_i != scratch_i, +- "compare_entry_to() and find_matching_entry() do not agree"); +- +- // Found a matching entry somewhere else in *merge_cp_p so +- // just need a mapping entry. +- map_index(scratch_cp, scratch_i, found_i); +- continue; +- } +- +- // The find_matching_entry() call above could fail to find a match +- // due to a resolved versus unresolved class or string entry situation +- // like we solved above with the is_unresolved_*_mismatch() calls. +- // However, we would have to call is_unresolved_*_mismatch() over +- // all of *merge_cp_p (potentially) and that doesn't seem to be +- // worth the time. - -- default: -- RC_TRACE_WITH_THREAD(0x04000000, THREAD, -- ("frame_i=%u, frame_type=%u, bad tag=0x%x", frame_i, frame_type, tag)); -- ShouldNotReachHere(); -- break; -- } // end switch (tag) --} // end rewrite_cp_refs_in_verification_type_info() +- // No match found so we have to append this entry and any unique +- // referenced entries to *merge_cp_p. +- append_entry(scratch_cp, scratch_i, merge_cp_p, merge_cp_length_p, +- CHECK_0); +- } +- } - +- RC_TRACE_WITH_THREAD(0x00020000, THREAD, +- ("after pass 1a: merge_cp_len=%d, scratch_i=%d, index_map_len=%d", +- *merge_cp_length_p, scratch_i, _index_map_count)); - --// Change the constant pool associated with klass scratch_class to --// scratch_cp. If shrink is true, then scratch_cp_length elements --// are copied from scratch_cp to a smaller constant pool and the --// smaller constant pool is associated with scratch_class. --void VM_RedefineClasses::set_new_constant_pool( -- instanceKlassHandle scratch_class, constantPoolHandle scratch_cp, -- int scratch_cp_length, TRAPS) { -- assert(scratch_cp->length() >= scratch_cp_length, "sanity check"); +- if (scratch_i < scratch_cp->length()) { +- // Pass 1b: +- // old_cp is smaller than scratch_cp so there are entries in +- // scratch_cp that we have not yet processed. We take care of +- // those now. +- int increment = 1; +- for (; scratch_i < scratch_cp->length(); scratch_i += increment) { +- switch (scratch_cp->tag_at(scratch_i).value()) { +- case JVM_CONSTANT_Double: +- case JVM_CONSTANT_Long: +- // double and long take two constant pool entries +- increment = 2; +- break; - -- // scratch_cp is a merged constant pool and has enough space for a -- // worst case merge situation. We want to associate the minimum -- // sized constant pool with the klass to save space. -- constantPoolHandle smaller_cp(THREAD, -- oopFactory::new_constantPool(scratch_cp_length, -- oopDesc::IsUnsafeConc, -- THREAD)); -- // preserve orig_length() value in the smaller copy -- int orig_length = scratch_cp->orig_length(); -- assert(orig_length != 0, "sanity check"); -- smaller_cp->set_orig_length(orig_length); +- default: +- increment = 1; +- break; +- } - -- // attach klass to new constant pool -- // reference to the cp holder is needed for copy_operands() -- smaller_cp->set_pool_holder(scratch_class()); +- int found_i = +- scratch_cp->find_matching_entry(scratch_i, *merge_cp_p, CHECK_0); +- if (found_i != 0) { +- // Found a matching entry somewhere else in *merge_cp_p so +- // just need a mapping entry. +- map_index(scratch_cp, scratch_i, found_i); +- continue; +- } - -- scratch_cp->copy_cp_to(1, scratch_cp_length - 1, smaller_cp, 1, THREAD); -- scratch_cp = smaller_cp; -- smaller_cp()->set_is_conc_safe(true); +- // No match found so we have to append this entry and any unique +- // referenced entries to *merge_cp_p. +- append_entry(scratch_cp, scratch_i, merge_cp_p, merge_cp_length_p, +- CHECK_0); +- } - -- // attach new constant pool to klass -- scratch_class->set_constants(scratch_cp()); +- RC_TRACE_WITH_THREAD(0x00020000, THREAD, +- ("after pass 1b: merge_cp_len=%d, scratch_i=%d, index_map_len=%d", +- *merge_cp_length_p, scratch_i, _index_map_count)); +- } +- finalize_operands_merge(*merge_cp_p, THREAD); - -- int i; // for portability +- return true; +-} // end merge_constant_pools() - -- // update each field in klass to use new constant pool indices as needed -- for (JavaFieldStream fs(scratch_class); !fs.done(); fs.next()) { -- jshort cur_index = fs.name_index(); -- jshort new_index = find_new_index(cur_index); -- if (new_index != 0) { -- RC_TRACE_WITH_THREAD(0x00080000, THREAD, -- ("field-name_index change: %d to %d", cur_index, new_index)); -- fs.set_name_index(new_index); -- } -- cur_index = fs.signature_index(); -- new_index = find_new_index(cur_index); -- if (new_index != 0) { -- RC_TRACE_WITH_THREAD(0x00080000, THREAD, -- ("field-signature_index change: %d to %d", cur_index, new_index)); -- fs.set_signature_index(new_index); -- } -- cur_index = fs.initval_index(); -- new_index = find_new_index(cur_index); -- if (new_index != 0) { -- RC_TRACE_WITH_THREAD(0x00080000, THREAD, -- ("field-initval_index change: %d to %d", cur_index, new_index)); -- fs.set_initval_index(new_index); -- } -- cur_index = fs.generic_signature_index(); -- new_index = find_new_index(cur_index); -- if (new_index != 0) { -- RC_TRACE_WITH_THREAD(0x00080000, THREAD, -- ("field-generic_signature change: %d to %d", cur_index, new_index)); -- fs.set_generic_signature_index(new_index); -- } -- } // end for each field - -- // Update constant pool indices in the inner classes info to use -- // new constant indices as needed. The inner classes info is a -- // quadruple: -- // (inner_class_info, outer_class_info, inner_name, inner_access_flags) -- InnerClassesIterator iter(scratch_class); -- for (; !iter.done(); iter.next()) { -- int cur_index = iter.inner_class_info_index(); -- if (cur_index == 0) { -- continue; // JVM spec. allows null inner class refs so skip it -- } -- int new_index = find_new_index(cur_index); -- if (new_index != 0) { -- RC_TRACE_WITH_THREAD(0x00080000, THREAD, -- ("inner_class_info change: %d to %d", cur_index, new_index)); -- iter.set_inner_class_info_index(new_index); -- } -- cur_index = iter.outer_class_info_index(); -- new_index = find_new_index(cur_index); -- if (new_index != 0) { -- RC_TRACE_WITH_THREAD(0x00080000, THREAD, -- ("outer_class_info change: %d to %d", cur_index, new_index)); -- iter.set_outer_class_info_index(new_index); -- } -- cur_index = iter.inner_name_index(); -- new_index = find_new_index(cur_index); -- if (new_index != 0) { -- RC_TRACE_WITH_THREAD(0x00080000, THREAD, -- ("inner_name change: %d to %d", cur_index, new_index)); -- iter.set_inner_name_index(new_index); -- } -- } // end for each inner class +-// Merge constant pools between the_class and scratch_class and +-// potentially rewrite bytecodes in scratch_class to use the merged +-// constant pool. +-jvmtiError VM_RedefineClasses::merge_cp_and_rewrite( +- instanceKlassHandle the_class, instanceKlassHandle scratch_class, +- TRAPS) { +- // worst case merged constant pool length is old and new combined +- int merge_cp_length = the_class->constants()->length() +- + scratch_class->constants()->length(); - -- // Attach each method in klass to the new constant pool and update -- // to use new constant pool indices as needed: -- objArrayHandle methods(THREAD, scratch_class->methods()); -- for (i = methods->length() - 1; i >= 0; i--) { -- methodHandle method(THREAD, (methodOop)methods->obj_at(i)); -- method->set_constants(scratch_cp()); +- constantPoolHandle old_cp(THREAD, the_class->constants()); +- constantPoolHandle scratch_cp(THREAD, scratch_class->constants()); - -- int new_index = find_new_index(method->name_index()); -- if (new_index != 0) { -- RC_TRACE_WITH_THREAD(0x00080000, THREAD, -- ("method-name_index change: %d to %d", method->name_index(), -- new_index)); -- method->set_name_index(new_index); -- } -- new_index = find_new_index(method->signature_index()); -- if (new_index != 0) { -- RC_TRACE_WITH_THREAD(0x00080000, THREAD, -- ("method-signature_index change: %d to %d", -- method->signature_index(), new_index)); -- method->set_signature_index(new_index); -- } -- new_index = find_new_index(method->generic_signature_index()); -- if (new_index != 0) { -- RC_TRACE_WITH_THREAD(0x00080000, THREAD, -- ("method-generic_signature_index change: %d to %d", -- method->generic_signature_index(), new_index)); -- method->set_generic_signature_index(new_index); -- } +- // Constant pools are not easily reused so we allocate a new one +- // each time. +- // merge_cp is created unsafe for concurrent GC processing. It +- // should be marked safe before discarding it. Even though +- // garbage, if it crosses a card boundary, it may be scanned +- // in order to find the start of the first complete object on the card. +- constantPoolHandle merge_cp(THREAD, +- oopFactory::new_constantPool(merge_cp_length, +- oopDesc::IsUnsafeConc, +- THREAD)); +- int orig_length = old_cp->orig_length(); +- if (orig_length == 0) { +- // This old_cp is an actual original constant pool. We save +- // the original length in the merged constant pool so that +- // merge_constant_pools() can be more efficient. If a constant +- // pool has a non-zero orig_length() value, then that constant +- // pool was created by a merge operation in RedefineClasses. +- merge_cp->set_orig_length(old_cp->length()); +- } else { +- // This old_cp is a merged constant pool from a previous +- // RedefineClasses() calls so just copy the orig_length() +- // value. +- merge_cp->set_orig_length(old_cp->orig_length()); +- } - -- // Update constant pool indices in the method's checked exception -- // table to use new constant indices as needed. -- int cext_length = method->checked_exceptions_length(); -- if (cext_length > 0) { -- CheckedExceptionElement * cext_table = -- method->checked_exceptions_start(); -- for (int j = 0; j < cext_length; j++) { -- int cur_index = cext_table[j].class_cp_index; -- int new_index = find_new_index(cur_index); -- if (new_index != 0) { -- RC_TRACE_WITH_THREAD(0x00080000, THREAD, -- ("cext-class_cp_index change: %d to %d", cur_index, new_index)); -- cext_table[j].class_cp_index = (u2)new_index; -- } -- } // end for each checked exception table entry -- } // end if there are checked exception table entries +- ResourceMark rm(THREAD); +- _index_map_count = 0; +- _index_map_p = new intArray(scratch_cp->length(), -1); - -- // Update each catch type index in the method's exception table -- // to use new constant pool indices as needed. The exception table -- // holds quadruple entries of the form: -- // (beg_bci, end_bci, handler_bci, klass_index) +- _operands_cur_length = constantPoolOopDesc::operand_array_length(old_cp->operands()); +- _operands_index_map_count = 0; +- _operands_index_map_p = new intArray( +- constantPoolOopDesc::operand_array_length(scratch_cp->operands()), -1); - -- ExceptionTable ex_table(method()); -- int ext_length = ex_table.length(); +- // reference to the cp holder is needed for copy_operands() +- merge_cp->set_pool_holder(scratch_class()); +- bool result = merge_constant_pools(old_cp, scratch_cp, &merge_cp, +- &merge_cp_length, THREAD); +- merge_cp->set_pool_holder(NULL); - -- for (int j = 0; j < ext_length; j ++) { -- int cur_index = ex_table.catch_type_index(j); -- int new_index = find_new_index(cur_index); -- if (new_index != 0) { -- RC_TRACE_WITH_THREAD(0x00080000, THREAD, -- ("ext-klass_index change: %d to %d", cur_index, new_index)); -- ex_table.set_catch_type_index(j, new_index); -- } -- } // end for each exception table entry +- if (!result) { +- // The merge can fail due to memory allocation failure or due +- // to robustness checks. +- return JVMTI_ERROR_INTERNAL; +- } +- +- RC_TRACE_WITH_THREAD(0x00010000, THREAD, +- ("merge_cp_len=%d, index_map_len=%d", merge_cp_length, _index_map_count)); - -- // Update constant pool indices in the method's local variable -- // table to use new constant indices as needed. The local variable -- // table hold sextuple entries of the form: -- // (start_pc, length, name_index, descriptor_index, signature_index, slot) -- int lvt_length = method->localvariable_table_length(); -- if (lvt_length > 0) { -- LocalVariableTableElement * lv_table = -- method->localvariable_table_start(); -- for (int j = 0; j < lvt_length; j++) { -- int cur_index = lv_table[j].name_cp_index; -- int new_index = find_new_index(cur_index); -- if (new_index != 0) { -- RC_TRACE_WITH_THREAD(0x00080000, THREAD, -- ("lvt-name_cp_index change: %d to %d", cur_index, new_index)); -- lv_table[j].name_cp_index = (u2)new_index; -- } -- cur_index = lv_table[j].descriptor_cp_index; -- new_index = find_new_index(cur_index); -- if (new_index != 0) { -- RC_TRACE_WITH_THREAD(0x00080000, THREAD, -- ("lvt-descriptor_cp_index change: %d to %d", cur_index, -- new_index)); -- lv_table[j].descriptor_cp_index = (u2)new_index; -- } -- cur_index = lv_table[j].signature_cp_index; -- new_index = find_new_index(cur_index); -- if (new_index != 0) { -- RC_TRACE_WITH_THREAD(0x00080000, THREAD, -- ("lvt-signature_cp_index change: %d to %d", cur_index, new_index)); -- lv_table[j].signature_cp_index = (u2)new_index; -- } -- } // end for each local variable table entry -- } // end if there are local variable table entries +- if (_index_map_count == 0) { +- // there is nothing to map between the new and merged constant pools - -- rewrite_cp_refs_in_stack_map_table(method, THREAD); -- } // end for each method -- assert(scratch_cp()->is_conc_safe(), "Just checking"); --} // end set_new_constant_pool() +- if (old_cp->length() == scratch_cp->length()) { +- // The old and new constant pools are the same length and the +- // index map is empty. This means that the three constant pools +- // are equivalent (but not the same). Unfortunately, the new +- // constant pool has not gone through link resolution nor have +- // the new class bytecodes gone through constant pool cache +- // rewriting so we can't use the old constant pool with the new +- // class. - +- merge_cp()->set_is_conc_safe(true); +- merge_cp = constantPoolHandle(); // toss the merged constant pool +- } else if (old_cp->length() < scratch_cp->length()) { +- // The old constant pool has fewer entries than the new constant +- // pool and the index map is empty. This means the new constant +- // pool is a superset of the old constant pool. However, the old +- // class bytecodes have already gone through constant pool cache +- // rewriting so we can't use the new constant pool with the old +- // class. - --// Unevolving classes may point to methods of the_class directly --// from their constant pool caches, itables, and/or vtables. We --// use the SystemDictionary::classes_do() facility and this helper --// to fix up these pointers. --// --// Note: We currently don't support updating the vtable in --// arrayKlassOops. See Open Issues in jvmtiRedefineClasses.hpp. --void VM_RedefineClasses::adjust_cpool_cache_and_vtable(klassOop k_oop, -- oop initiating_loader, TRAPS) { -- Klass *k = k_oop->klass_part(); -- if (k->oop_is_instance()) { -- HandleMark hm(THREAD); -- instanceKlass *ik = (instanceKlass *) k; +- merge_cp()->set_is_conc_safe(true); +- merge_cp = constantPoolHandle(); // toss the merged constant pool +- } else { +- // The old constant pool has more entries than the new constant +- // pool and the index map is empty. This means that both the old +- // and merged constant pools are supersets of the new constant +- // pool. - -- // HotSpot specific optimization! HotSpot does not currently -- // support delegation from the bootstrap class loader to a -- // user-defined class loader. This means that if the bootstrap -- // class loader is the initiating class loader, then it will also -- // be the defining class loader. This also means that classes -- // loaded by the bootstrap class loader cannot refer to classes -- // loaded by a user-defined class loader. Note: a user-defined -- // class loader can delegate to the bootstrap class loader. -- // -- // If the current class being redefined has a user-defined class -- // loader as its defining class loader, then we can skip all -- // classes loaded by the bootstrap class loader. -- bool is_user_defined = -- instanceKlass::cast(_the_class_oop)->class_loader() != NULL; -- if (is_user_defined && ik->class_loader() == NULL) { +- // Replace the new constant pool with a shrunken copy of the +- // merged constant pool; the previous new constant pool will +- // get GCed. +- set_new_constant_pool(scratch_class, merge_cp, merge_cp_length, +- THREAD); +- // drop local ref to the merged constant pool +- merge_cp()->set_is_conc_safe(true); +- merge_cp = constantPoolHandle(); +- } +- } else { +- if (RC_TRACE_ENABLED(0x00040000)) { +- // don't want to loop unless we are tracing +- int count = 0; +- for (int i = 1; i < _index_map_p->length(); i++) { +- int value = _index_map_p->at(i); +- +- if (value != -1) { +- RC_TRACE_WITH_THREAD(0x00040000, THREAD, +- ("index_map[%d]: old=%d new=%d", count, i, value)); +- count++; ++ void check_field(oop obj, int offset, klassOop static_type) { ++ oop field_value = obj->obj_field(offset); ++ if (field_value != NULL) { ++ // Field is not null ++ if (field_value->klass()->klass_part()->newest_version()->klass_part()->is_subtype_of(static_type)) { ++ // We are OK ++ RC_TRACE(0x00008000, ("Field value is OK (klass=%s, static_type=%s, offset=%d, dynamic_type=%s)", ++ obj->klass()->klass_part()->name()->as_C_string(), ++ static_type->klass_part()->name()->as_C_string(), ++ offset, ++ field_value->klass()->klass_part()->name()->as_C_string())); ++ } else { ++ // Failure! ++ RC_TRACE(0x00000001, ("Field value is INVALID - abort redefinition (klass=%s, static_type=%s, offset=%d, dynamic_type=%s)", ++ obj->klass()->klass_part()->name()->as_C_string(), ++ static_type->klass_part()->name()->as_C_string(), ++ offset, ++ field_value->klass()->klass_part()->name()->as_C_string())); ++ _result = false; + } + } + } +- +- // We have entries mapped between the new and merged constant pools +- // so we have to rewrite some constant pool references. +- if (!rewrite_cp_refs(scratch_class, THREAD)) { +- return JVMTI_ERROR_INTERNAL; ++ }; ++ ++ CheckFieldTypesClosure myObjectClosure; ++ ++ // make sure that heap is parsable (fills TLABs with filler objects) ++ Universe::heap()->ensure_parsability(false); // no need to retire TLABs ++ ++ // do the iteration ++ // If this operation encounters a bad object when using CMS, ++ // consider using safe_object_iterate() which avoids perm gen ++ // objects that may contain bad references. ++ Universe::heap()->object_iterate(&myObjectClosure); ++ ++ // when sharing is enabled we must iterate over the shared spaces ++ if (UseSharedSpaces) { ++ GenCollectedHeap* gch = GenCollectedHeap::heap(); ++ CompactingPermGenGen* gen = (CompactingPermGenGen*)gch->perm_gen(); ++ gen->ro_space()->object_iterate(&myObjectClosure); ++ gen->rw_space()->object_iterate(&myObjectClosure); ++ } ++ ++ return myObjectClosure.result(); ++} ++ ++void VM_RedefineClasses::clear_type_check_information(klassOop k) { ++ if (k->klass_part()->is_redefining()) { ++ k = k->klass_part()->old_version(); ++ } ++ ++ // We found an instance klass! ++ instanceKlass *cur_instance_klass = instanceKlass::cast(k); ++ cur_instance_klass->clear_type_check_information(); ++} ++ ++void VM_RedefineClasses::update_active_methods() { ++ ++ RC_TRACE(0x00000002, ("Updating active methods")); ++ JavaThread *java_thread = Threads::first(); ++ while (java_thread != NULL) { ++ ++ int stack_depth = 0; ++ if (java_thread->has_last_Java_frame()) { ++ ++ RC_TRACE(0x0000000400, ("checking stack of Java thread %s", java_thread->name())); ++ ++ // vframes are resource allocated ++ Thread* current_thread = Thread::current(); ++ ResourceMark rm(current_thread); ++ HandleMark hm(current_thread); ++ ++ RegisterMap reg_map(java_thread); ++ frame f = java_thread->last_frame(); ++ vframe* vf = vframe::new_vframe(&f, ®_map, java_thread); ++ frame* last_entry_frame = NULL; ++ ++ while (vf != NULL) { ++ if (vf->is_java_frame()) { ++ // java frame (interpreted, compiled, ...) ++ javaVFrame *jvf = javaVFrame::cast(vf); ++ ++ if (!(jvf->method()->is_native())) { ++ int bci = jvf->bci(); ++ RC_TRACE(0x00000400, ("found method: %s / bci=%d", jvf->method()->name()->as_C_string(), bci)); ++ ResourceMark rm(Thread::current()); ++ HandleMark hm; ++ instanceKlassHandle klass(jvf->method()->method_holder()); ++ ++ if (jvf->method()->new_version() != NULL && jvf->is_interpreted_frame()) { ++ ++ ++ RC_TRACE(0x00000002, ("Found method that should just be updated to the newest version %s", ++ jvf->method()->name_and_sig_as_C_string())); ++ ++ if (RC_TRACE_ENABLED(0x01000000)) { ++ int code_size = jvf->method()->code_size(); ++ char *code_base_old = (char*)jvf->method()->code_base(); ++ char *code_base_new = (char*)jvf->method()->new_version()->code_base(); ++ for (int i=0; iprint_cr("old=%d new=%d", *code_base_old++, *code_base_new++); ++ } ++ jvf->method()->print_codes_on(tty); ++ jvf->method()->new_version()->print_codes_on(tty); ++ } ++ ++ assert(jvf->is_interpreted_frame(), "Every frame must be interpreted!"); ++ interpretedVFrame *iframe = (interpretedVFrame *)jvf; ++ ++ ++ if (RC_TRACE_ENABLED(0x01000000)) { ++ constantPoolCacheOop cp_old = jvf->method()->constants()->cache(); ++ tty->print_cr("old cp"); ++ for (int i=0; ilength(); i++) { ++ cp_old->entry_at(i)->print(tty, i); ++ } ++ constantPoolCacheOop cp_new = jvf->method()->new_version()->constants()->cache(); ++ tty->print_cr("new cp"); ++ for (int i=0; ilength(); i++) { ++ cp_new->entry_at(i)->print(tty, i); ++ } ++ } ++ ++ iframe->set_method(jvf->method()->new_version(), bci); ++ RC_TRACE(0x00000002, ("Updated method to newer version")); ++ assert(jvf->method()->new_version() == NULL, "must be latest version"); ++ ++ } ++ } ++ } ++ vf = vf->sender(); ++ } + } + +- // Replace the new constant pool with a shrunken copy of the +- // merged constant pool so now the rewritten bytecodes have +- // valid references; the previous new constant pool will get +- // GCed. +- set_new_constant_pool(scratch_class, merge_cp, merge_cp_length, +- THREAD); +- merge_cp()->set_is_conc_safe(true); ++ // Advance to next thread ++ java_thread = java_thread->next(); + } +- assert(old_cp()->is_conc_safe(), "Just checking"); +- assert(scratch_cp()->is_conc_safe(), "Just checking"); +- +- return JVMTI_ERROR_NONE; +-} // end merge_cp_and_rewrite() +- +- +-// Rewrite constant pool references in klass scratch_class. +-bool VM_RedefineClasses::rewrite_cp_refs(instanceKlassHandle scratch_class, +- TRAPS) { +- +- // rewrite constant pool references in the methods: +- if (!rewrite_cp_refs_in_methods(scratch_class, THREAD)) { +- // propagate failure back to caller ++} ++ ++void VM_RedefineClasses::method_forwarding() { ++ ++ int forwarding_count = 0; ++ JavaThread *java_thread = Threads::first(); ++ while (java_thread != NULL) { ++ ++ int stack_depth = 0; ++ if (java_thread->has_last_Java_frame()) { ++ ++ RC_TRACE(0x00000400, ("checking stack of Java thread %s", java_thread->name())); ++ ++ // vframes are resource allocated ++ Thread* current_thread = Thread::current(); ++ ResourceMark rm(current_thread); ++ HandleMark hm(current_thread); ++ ++ RegisterMap reg_map(java_thread); ++ frame f = java_thread->last_frame(); ++ vframe* vf = vframe::new_vframe(&f, ®_map, java_thread); ++ frame* last_entry_frame = NULL; ++ ++ while (vf != NULL) { ++ if (vf->is_java_frame()) { ++ // java frame (interpreted, compiled, ...) ++ javaVFrame *jvf = javaVFrame::cast(vf); ++ ++ if (!(jvf->method()->is_native())) { ++ RC_TRACE(0x00008000, ("found method: %s", ++ jvf->method()->name()->as_C_string())); ++ ResourceMark rm(Thread::current()); ++ HandleMark hm; ++ instanceKlassHandle klass(jvf->method()->method_holder()); ++ methodOop m = jvf->method(); ++ int bci = jvf->bci(); ++ RC_TRACE(0x00008000, ("klass redef %d", ++ klass->is_redefining())); ++ ++ if (klass->new_version() != NULL && m->new_version() == NULL) { ++ RC_TRACE(0x00008000, ("found potential forwarding method: %s", ++ m->name()->as_C_string())); ++ ++ klassOop new_klass = klass->newest_version(); ++ methodOop new_method = new_klass->klass_part()->lookup_method(m->name(), m->signature()); ++ RC_TRACE(0x00000002, ("%d %d", ++ new_method, ++ new_method->constMethod()->has_code_section_table())); ++ ++ if (new_method != NULL && new_method->constMethod()->has_code_section_table()) { ++ RC_TRACE(0x00008000, ("found code section table for method: %s", ++ new_method->name()->as_C_string())); ++ m->set_forward_method(new_method); ++ if (new_method->max_locals() != m->max_locals()) { ++ tty->print_cr("new_m max locals: %d old_m max locals: %d", new_method->max_locals(), m->max_locals()); ++ } ++ assert(new_method->max_locals() == m->max_locals(), "number of locals must match"); ++ assert(new_method->max_stack() == m->max_stack(), "number of stack values must match"); ++ if (jvf->is_interpreted_frame()) { ++ if (m->is_in_code_section(bci)) { ++ // We must transfer now and cannot delay until next NOP. ++ int new_bci = m->calculate_forward_bci(bci, new_method); ++ interpretedVFrame* iframe = interpretedVFrame::cast(jvf); ++ RC_TRACE(0x00000002, ("Transferring execution of %s to new method old_bci=%d new_bci=%d", ++ new_method->name()->as_C_string(), ++ bci, ++ new_bci)); ++ iframe->set_method(new_method, new_bci); ++ } else { ++ RC_TRACE(0x00000002, ("Delaying method forwarding of %s because %d is not in a code section", ++ new_method->name()->as_C_string(), ++ bci)); ++ } ++ } else { ++ RC_TRACE(0x00000002, ("Delaying method forwarding of %s because method is compiled", ++ new_method->name()->as_C_string())); ++ } ++ } ++ } ++ } ++ } ++ vf = vf->sender(); ++ } ++ } ++ ++ // Advance to next thread ++ java_thread = java_thread->next(); ++ } ++ ++ RC_TRACE(0x00000001, ("Method forwarding applied to %d methods", ++ forwarding_count)); ++} ++ ++bool VM_RedefineClasses::check_method_stacks() { ++ ++ JavaThread *java_thread = Threads::first(); ++ while (java_thread != NULL) { ++ ++ int stack_depth = 0; ++ if (java_thread->has_last_Java_frame()) { ++ ++ RC_TRACE(0x00000400, ("checking stack of Java thread %s", java_thread->name())); ++ ++ // vframes are resource allocated ++ Thread* current_thread = Thread::current(); ++ ResourceMark rm(current_thread); ++ HandleMark hm(current_thread); ++ ++ RegisterMap reg_map(java_thread); ++ frame f = java_thread->last_frame(); ++ vframe* vf = vframe::new_vframe(&f, ®_map, java_thread); ++ frame* last_entry_frame = NULL; ++ ++ while (vf != NULL) { ++ if (vf->is_java_frame()) { ++ // java frame (interpreted, compiled, ...) ++ javaVFrame *jvf = javaVFrame::cast(vf); ++ ++ if (!(jvf->method()->is_native())) { ++ RC_TRACE(0x00000400, ("found method: %s", jvf->method()->name()->as_C_string())); ++ ResourceMark rm(Thread::current()); ++ HandleMark hm; ++ instanceKlassHandle klass(jvf->method()->method_holder()); ++ ++ StackValueCollection *locals = jvf->locals(); ++ const size_t message_buffer_len = klass->name()->utf8_length() + 1024; ++ char* message_buffer = NEW_RESOURCE_ARRAY(char, message_buffer_len); ++ ++ for (int i=0; isize(); i++) { ++ StackValue *stack_value = locals->at(i); ++ if (stack_value->type() == T_OBJECT) { ++ Handle obj = stack_value->get_obj(); ++ if (!obj.is_null() && obj->klass()->klass_part()->newest_version()->klass_part()->check_redefinition_flag(Klass::RemoveSuperType)) { ++ ++ // OK, so this is a possible failure => check local variable table, if it could be OK. ++ bool result = false; ++ methodOop method = jvf->method(); ++ if (method->has_localvariable_table()) { ++ LocalVariableTableElement *elem = jvf->method()->localvariable_table_start(); ++ for (int j=0; jlocalvariable_table_length(); j++) { ++ ++ if (elem->slot == i) { ++ ++ // Matching index found ++ ++ if (elem->start_bci <= jvf->bci() && elem->start_bci + elem->length > jvf->bci()) { ++ ++ // Also in range!! ++ Symbol* signature = jvf->method()->constants()->symbol_at(elem->descriptor_cp_index); ++ Symbol* klass_name = signature_to_class_name(signature); ++ ++ klassOop local_klass = SystemDictionary::find(klass_name, jvf->method()->method_holder()->klass_part()->class_loader(), jvf->method()->method_holder()->klass_part()->protection_domain(), Thread::current())->klass_part()->newest_version(); ++ klassOop cur = obj->klass()->klass_part()->newest_version(); ++ ++ // Field is not null ++ if (cur->klass_part()->newest_version()->klass_part()->is_subtype_of(local_klass)) { ++ // We are OK ++ RC_TRACE(0x00008000, ("Local variable value is OK (local_klass=%s, cur_klass=%s)", ++ local_klass->klass_part()->name()->as_C_string(), cur->klass_part()->name()->as_C_string())); ++ result = true; ++ } else { ++ // Failure! ++ RC_TRACE(0x00000001, ("Local variable value is INVALID - abort redefinition (local_klass=%s, cur_klass=%s)", ++ local_klass->klass_part()->name()->as_C_string(), ++ cur->klass_part()->name()->as_C_string())); ++ return false; ++ } ++ } ++ } ++ ++ elem++; ++ } ++ } else { ++ RC_TRACE(0x00000002, ("Method %s does not have a local variable table => abort", ++ method->name_and_sig_as_C_string())); ++ } ++ ++ if (!result) { ++ return false; ++ } ++ ++ RC_TRACE(0x00008000, ("Verifying class %s", ++ jvf->method()->method_holder()->klass_part()->name()->as_C_string())); ++ ++ Symbol* exception_name; ++ const size_t message_buffer_len = klass->name()->utf8_length() + 1024; ++ char* message_buffer = NEW_RESOURCE_ARRAY(char, message_buffer_len); ++ ++ Thread::current()->set_pretend_new_universe(true); ++ ClassVerifier split_verifier(klass, Thread::current()); ++ split_verifier.verify_method(jvf->method(), Thread::current()); ++ exception_name = split_verifier.result(); ++ Thread::current()->set_pretend_new_universe(false); ++ ++ if (exception_name != NULL) { ++ ++ RC_TRACE(0x00000001, ("Verification of class %s failed", ++ jvf->method()->method_holder()->klass_part()->name()->as_C_string())); ++ RC_TRACE(0x00000001, ("Exception: %s", ++ exception_name->as_C_string())); ++ RC_TRACE(0x00000001, ("Message: %s", ++ message_buffer)); ++ Thread::current()->clear_pending_exception(); ++ return false; ++ } ++ ++ } ++ } ++ } ++ } ++ } ++ vf = vf->sender(); ++ } ++ } ++ ++ // Advance to next thread ++ java_thread = java_thread->next(); ++ } ++ ++ return true; ++} ++ ++bool VM_RedefineClasses::check_method(methodOop method) { ++ ++ ++ return true; ++} ++ ++// Warning: destroys redefinition level values of klasses. ++bool VM_RedefineClasses::check_loaded_methods() { ++ ++ class CheckLoadedMethodsClosure : public ObjectClosure { ++ ++ private: ++ ++ bool _result; ++ GrowableArray *_dangerous_klasses; ++ ++ public: ++ CheckLoadedMethodsClosure(GrowableArray *dangerous_klasses) { ++ _result = true; ++ _dangerous_klasses = dangerous_klasses; ++ } ++ ++ bool result() { ++ return _result; ++ } ++ ++ bool is_class_dangerous(klassOop k) { ++ return k->klass_part()->newest_version()->klass_part()->check_redefinition_flag(Klass::RemoveSuperType); ++ } ++ ++ bool can_be_affected(instanceKlass *klass) { ++ ++ constantPoolOop cp = klass->constants(); ++ ++ Thread *THREAD = Thread::current(); ++ klassOop k; ++ Symbol* symbol; ++ ++ for (int i=1; ilength(); i++) { ++ jbyte tag = cp->tag_at(i).value(); ++ switch(tag) { ++ case JVM_CONSTANT_Long: ++ case JVM_CONSTANT_Double: ++ i++; ++ break; ++ ++ case JVM_CONSTANT_Utf8: ++ case JVM_CONSTANT_Unicode: ++ case JVM_CONSTANT_Integer: ++ case JVM_CONSTANT_Float: ++ case JVM_CONSTANT_String: ++ case JVM_CONSTANT_Fieldref: ++ case JVM_CONSTANT_Methodref: ++ case JVM_CONSTANT_InterfaceMethodref: ++ case JVM_CONSTANT_ClassIndex: ++ case JVM_CONSTANT_UnresolvedString: ++ case JVM_CONSTANT_StringIndex: ++ case JVM_CONSTANT_UnresolvedClassInError: ++ case JVM_CONSTANT_Object: ++ // do nothing ++ break; ++ ++ case JVM_CONSTANT_Class: ++ k = cp->klass_at(i, CHECK_(true)); ++ if (is_class_dangerous(k)) { ++ RC_TRACE(0x00000002, ("Class %s is potentially affected, because at cp[%d] references class %s", ++ klass->name()->as_C_string(), ++ i, ++ k->klass_part()->name()->as_C_string())); ++ return true; ++ } ++ break; ++ ++ case JVM_CONSTANT_NameAndType: ++ symbol = cp->symbol_at(cp->signature_ref_index_at(i)); ++ if (symbol->byte_at(0) == '(') { ++ // This must be a method ++ SignatureStream signatureStream(symbol); ++ while (true) { ++ ++ if (signatureStream.is_array()) { ++ Symbol* cur_signature = signatureStream.as_symbol(Thread::current()); ++ if (is_type_signature_dangerous(cur_signature)) { ++ return true; ++ } ++ } else if (signatureStream.is_object()) { ++ if (is_symbol_dangerous(signatureStream.as_symbol(Thread::current()))) { ++ return true; ++ } ++ } ++ ++ if (signatureStream.at_return_type()) { ++ break; ++ } ++ ++ signatureStream.next(); ++ } ++ ++ } else if (is_type_signature_dangerous(symbol)) { ++ return true; ++ } ++ break; ++ ++ case JVM_CONSTANT_UnresolvedClass: ++ symbol = cp->unresolved_klass_at(i); ++ if (is_symbol_dangerous(symbol)) { ++ return true; ++ } ++ break; ++ ++ default: ++ ShouldNotReachHere(); ++ } ++ } ++ ++ return false; ++ } ++ ++ bool is_type_signature_dangerous(Symbol* signature) { ++ // This must be a field type ++ if (FieldType::is_obj(signature)) { ++ Symbol* name = signature_to_class_name(signature); ++ if (is_symbol_dangerous(name)) { ++ return true; ++ } ++ } else if (FieldType::is_array(signature)) { ++ //jint dimension; ++ //Symbol* object_key; ++ FieldArrayInfo fd; ++ FieldType::get_array_info(signature, fd, Thread::current()); ++ if (is_symbol_dangerous(fd.object_key())) { ++ return true; ++ } ++ } ++ return false; ++ } ++ ++ bool is_symbol_dangerous(Symbol* symbol) { ++ for (int i=0; i<_dangerous_klasses->length(); i++) { ++ if(_dangerous_klasses->at(i)->klass_part()->name() == symbol) { ++ RC_TRACE(0x00000002, ("Found constant pool index %d references class %s", ++ i, ++ symbol->as_C_string())); ++ return true; ++ } ++ } ++ return false; ++ } ++ ++ virtual void do_object(oop obj) { ++ ++ if (!_result) return; ++ ++ klassOop klassObj = (klassOop)obj; ++ Thread *THREAD = Thread::current(); ++ ++ // We found an instance klass! ++ instanceKlass *klass = instanceKlass::cast(klassObj); ++ instanceKlassHandle handle(klassObj); ++ ++ RC_TRACE(0x00000400, ("Check if verification is necessary for class %s major_version=%d", handle->name()->as_C_string(), handle->major_version())); ++ ++ if (!can_be_affected(klass)) { ++ RC_TRACE(0x00000400, ("Skipping verification of class %s major_version=%d", handle->name()->as_C_string(), handle->major_version())); ++ return; ++ } ++ ++ if (handle->major_version() < Verifier::STACKMAP_ATTRIBUTE_MAJOR_VERSION) { ++ RC_TRACE(0x00000001, ("Failing because cannot verify class %s major_version=%d", handle->name()->as_C_string(), handle->major_version())); ++ _result = false; ++ return; ++ } ++ ++ RC_TRACE(0x00000001, ("Verifying class %s", handle->name()->as_C_string())); ++ ++ if (!Verifier::verify(handle, Verifier::NoException, true, false, Thread::current())) { ++ ++ RC_TRACE(0x00000001, ("Verification of class %s failed", handle->name()->as_C_string())); ++ //Symbol* ex_name = PENDING_EXCEPTION->klass()->klass_part()->name(); ++ //RC_TRACE(0x00000002, ("exception when verifying class: '%s'", ex_name->as_C_string()); ++ //PENDING_EXCEPTION->print(); ++ CLEAR_PENDING_EXCEPTION; ++ _result = false; ++ } ++ ++ /*int method_count = klass->methods()->length(); ++ for (int i=0; imethods()->obj_at(i); ++ if (!check_method(cur_method)) { ++ RC_TRACE(0x00000001, ("Failed to verify consistency of method %s of klass %s", cur_method->name()->as_C_string(), klass->name()->as_C_string()); ++ } ++ }*/ ++ } ++ }; ++ ++ // TODO: Check bytecodes in case of interface => class or class => interface etc.. ++ ++ GrowableArray dangerous_klasses; ++ for (int i=0; i<_new_classes->length(); i++) { ++ instanceKlassHandle handle = _new_classes->at(i); ++ if (handle->check_redefinition_flag(Klass::RemoveSuperType)) { ++ dangerous_klasses.append(handle()); ++ } ++ } ++ ++ CheckLoadedMethodsClosure checkLoadedMethodsClosure(&dangerous_klasses); ++ Thread::current()->set_pretend_new_universe(true); ++ SystemDictionary::classes_do(&checkLoadedMethodsClosure); ++ Thread::current()->set_pretend_new_universe(false); ++ ++ ++ return checkLoadedMethodsClosure.result(); ++} ++ ++bool VM_RedefineClasses::check_type_consistency() { ++ ++ Universe::set_verify_in_progress(true); ++ ++ SystemDictionary::classes_do(calculate_type_check_information); ++ bool result = check_field_value_types(); ++ SystemDictionary::classes_do(clear_type_check_information); ++ if (!result) { ++ RC_TRACE(0x00000001, ("Aborting redefinition because of wrong field or array element value!")); ++ Universe::set_verify_in_progress(false); + return false; + } + +- // rewrite constant pool references in the class_annotations: +- if (!rewrite_cp_refs_in_class_annotations(scratch_class, THREAD)) { +- // propagate failure back to caller ++ result = check_method_stacks(); ++ if (!result) { ++ RC_TRACE(0x00000001, ("Aborting redefinition because of wrong value on the stack")); ++ Universe::set_verify_in_progress(false); + return false; + } + +- // rewrite constant pool references in the fields_annotations: +- if (!rewrite_cp_refs_in_fields_annotations(scratch_class, THREAD)) { +- // propagate failure back to caller ++ result = check_loaded_methods(); ++ if (!result) { ++ RC_TRACE(0x00000001, ("Aborting redefinition because of wrong loaded method")); ++ Universe::set_verify_in_progress(false); + return false; + } + +- // rewrite constant pool references in the methods_annotations: +- if (!rewrite_cp_refs_in_methods_annotations(scratch_class, THREAD)) { +- // propagate failure back to caller ++ RC_TRACE(0x00000001, ("Verification passed => hierarchy change is valid!")); ++ Universe::set_verify_in_progress(false); ++ return true; ++} ++ ++void VM_RedefineClasses::rollback() { ++ RC_TRACE(0x00000001, ("Rolling back redefinition!")); ++ SystemDictionary::rollback_redefinition(); ++ ++ RC_TRACE(0x00000001, ("After rolling back system dictionary!")); ++ for (int i=0; i<_new_classes->length(); i++) { ++ SystemDictionary::remove_from_hierarchy(_new_classes->at(i)); ++ } ++ ++ for (int i=0; i<_new_classes->length(); i++) { ++ instanceKlassHandle new_class = _new_classes->at(i); ++ new_class->set_redefining(false); ++ new_class->old_version()->klass_part()->set_new_version(NULL); ++ new_class->set_old_version(NULL); ++ } ++ ++} ++ ++template void VM_RedefineClasses::do_oop_work(T* p) { ++ T heap_oop = oopDesc::load_heap_oop(p); ++ if (!oopDesc::is_null(heap_oop)) { ++ oop obj = oopDesc::decode_heap_oop_not_null(heap_oop); ++ if (obj->is_instanceKlass()) { ++ klassOop klass = (klassOop)obj; ++ // DCEVM: note: can overwrite owner of old_klass constants pool with new_klass, so we need to fix it back later ++ if (klass->new_version() != NULL && klass->new_version()->klass_part()->is_redefining()) { ++ obj = klass->klass_part()->new_version(); ++ oopDesc::encode_store_heap_oop_not_null(p, obj); ++ } ++ } else if (obj->blueprint()->newest_version() == SystemDictionary::Class_klass()->klass_part()->newest_version()) { ++ // update references to java.lang.Class to point to newest version. Only update references to non-primitive ++ // java.lang.Class instances. ++ klassOop klass_oop = java_lang_Class::as_klassOop(obj); ++ if (klass_oop != NULL) { ++ if (klass_oop->new_version() != NULL && klass_oop->new_version()->klass_part()->is_redefining()) { ++ obj = klass_oop->new_version()->java_mirror(); ++ } else if (klass_oop->klass_part()->is_redefining()) { ++ obj = klass_oop->java_mirror(); ++ } ++ oopDesc::encode_store_heap_oop_not_null(p, obj); ++ ++ ++ // FIXME: DCEVM: better implementation? ++ // Starting from JDK 7 java_mirror can be kept in the regular heap. Therefore, it is possible ++ // that new java_mirror is in the young generation whereas p is in tenured generation. In that ++ // case we need to run write barrier to make sure card table is properly updated. This will ++ // allow JVM to detect reference in tenured generation properly during young generation GC. ++ if (Universe::heap()->is_in_reserved(p)) { ++ if (GenCollectedHeap::heap()->is_in_young(obj)) { ++ GenRemSet* rs = GenCollectedHeap::heap()->rem_set(); ++ assert(rs->rs_kind() == GenRemSet::CardTable, "Wrong rem set kind."); ++ CardTableRS* _rs = (CardTableRS*)rs; ++ _rs->inline_write_ref_field_gc(p, obj); ++ } ++ } ++ } ++ } ++ } ++} ++ ++void VM_RedefineClasses::swap_marks(oop first, oop second) { ++ markOop first_mark = first->mark(); ++ markOop second_mark = second->mark(); ++ first->set_mark(second_mark); ++ second->set_mark(first_mark); ++} ++ ++void VM_RedefineClasses::doit() { ++ Thread *thread = Thread::current(); ++ ++ RC_TRACE(0x00000001, ("Entering doit!")); ++ ++ ++ if ((_max_redefinition_flags & Klass::RemoveSuperType) != 0) { ++ ++ RC_TIMER_START(_timer_check_type); ++ + if (!check_type_consistency()) { + // (tw) TODO: Rollback the class redefinition + rollback(); + RC_TRACE(0x00000001, ("Detected type inconsistency!")); + _result = JVMTI_ERROR_UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED; + RC_TIMER_STOP(_timer_check_type); - return; - } - -- // This is a very busy routine. We don't want too much tracing -- // printed out. -- bool trace_name_printed = false; ++ return; ++ } ++ + RC_TIMER_STOP(_timer_check_type); - -- // Very noisy: only enable this call if you are trying to determine -- // that a specific class gets found by this routine. -- // RC_TRACE macro has an embedded ResourceMark -- // RC_TRACE_WITH_THREAD(0x00100000, THREAD, -- // ("adjust check: name=%s", ik->external_name())); -- // trace_name_printed = true; ++ + } else { + RC_TRACE(0x00000001, ("No type narrowing => skipping check for type inconsistency")); + } - -- // Fix the vtable embedded in the_class and subclasses of the_class, -- // if one exists. We discard scratch_class and we don't keep an -- // instanceKlass around to hold obsolete methods so we don't have -- // any other instanceKlass embedded vtables to update. The vtable -- // holds the methodOops for virtual (but not final) methods. -- if (ik->vtable_length() > 0 && ik->is_subtype_of(_the_class_oop)) { -- // ik->vtable() creates a wrapper object; rm cleans it up -- ResourceMark rm(THREAD); -- ik->vtable()->adjust_method_entries(_matching_old_methods, -- _matching_new_methods, -- _matching_methods_length, -- &trace_name_printed); ++ + if (UseMethodForwardPoints) { + RC_TRACE(0x00000001, ("Check stack for forwarding methods to new version")); + method_forwarding(); @@ -9299,54 +8110,16 @@ index 606be1c..ef4f380 100644 + Universe::root_oops_do(&oopClosure); + Universe::heap()->object_iterate(&objectClosure); + SharedHeap::heap()->gc_epilogue(false); - } - -- // If the current class has an itable and we are either redefining an -- // interface or if the current class is a subclass of the_class, then -- // we potentially have to fix the itable. If we are redefining an -- // interface, then we have to call adjust_method_entries() for -- // every instanceKlass that has an itable since there isn't a -- // subclass relationship between an interface and an instanceKlass. -- if (ik->itable_length() > 0 && (Klass::cast(_the_class_oop)->is_interface() -- || ik->is_subclass_of(_the_class_oop))) { -- // ik->itable() creates a wrapper object; rm cleans it up -- ResourceMark rm(THREAD); -- ik->itable()->adjust_method_entries(_matching_old_methods, -- _matching_new_methods, -- _matching_methods_length, -- &trace_name_printed); ++ } ++ + // Swap marks to have same hashcodes + for (int i=0; i<_new_classes->length(); i++) { + swap_marks(_new_classes->at(i)(), _new_classes->at(i)->old_version()); + swap_marks(_new_classes->at(i)->java_mirror(), _new_classes->at(i)->old_version()->java_mirror()); - } - -- // The constant pools in other classes (other_cp) can refer to -- // methods in the_class. We have to update method information in -- // other_cp's cache. If other_cp has a previous version, then we -- // have to repeat the process for each previous version. The -- // constant pool cache holds the methodOops for non-virtual -- // methods and for virtual, final methods. -- // -- // Special case: if the current class is the_class, then new_cp -- // has already been attached to the_class and old_cp has already -- // been added as a previous version. The new_cp doesn't have any -- // cached references to old methods so it doesn't need to be -- // updated. We can simply start with the previous version(s) in -- // that case. -- constantPoolHandle other_cp; -- constantPoolCacheOop cp_cache; ++ } ++ + _updated_oops = objectClosure.updated_oops(); - -- if (k_oop != _the_class_oop) { -- // this klass' constant pool cache may need adjustment -- other_cp = constantPoolHandle(ik->constants()); -- cp_cache = other_cp->cache(); -- if (cp_cache != NULL) { -- cp_cache->adjust_method_entries(_matching_old_methods, -- _matching_new_methods, -- _matching_methods_length, -- &trace_name_printed); ++ + if (objectClosure.needs_instance_update()){ + + // Do a full garbage collection to update the instance sizes accordingly @@ -9457,27 +8230,8 @@ index 606be1c..ef4f380 100644 + instanceKlass::ClassState state = instanceKlass::cast(klass->old_version())->init_state(); + if (state > instanceKlass::linked) { + klass->initialize(Thread::current()); - } - } -- { -- ResourceMark rm(THREAD); -- // PreviousVersionInfo objects returned via PreviousVersionWalker -- // contain a GrowableArray of handles. We have to clean up the -- // GrowableArray _after_ the PreviousVersionWalker destructor -- // has destroyed the handles. -- { -- // the previous versions' constant pool caches may need adjustment -- PreviousVersionWalker pvw(ik); -- for (PreviousVersionInfo * pv_info = pvw.next_previous_version(); -- pv_info != NULL; pv_info = pvw.next_previous_version()) { -- other_cp = pv_info->prev_constant_pool_handle(); -- cp_cache = other_cp->cache(); -- if (cp_cache != NULL) { -- cp_cache->adjust_method_entries(_matching_old_methods, -- _matching_new_methods, -- _matching_methods_length, -- &trace_name_printed); -- } ++ } ++ } + + // Find instance transformer method + @@ -9493,9 +8247,7 @@ index 606be1c..ef4f380 100644 + break; + } else { + cur_klass = cur_klass->klass_part()->super(); - } -- } // pvw is cleaned up -- } // rm is cleaned up ++ } + } + assert(cur_klass != NULL, "must have instance transformer method"); + } else { @@ -9551,13 +8303,24 @@ index 606be1c..ef4f380 100644 +bool VM_RedefineClasses::is_modifiable_class(oop klass_mirror) { + // classes for primitives cannot be redefined + if (java_lang_Class::is_primitive(klass_mirror)) { -+ return false; -+ } + return false; + } +- +- // rewrite constant pool references in the methods_parameter_annotations: +- if (!rewrite_cp_refs_in_methods_parameter_annotations(scratch_class, +- THREAD)) { +- // propagate failure back to caller + klassOop the_class_oop = java_lang_Class::as_klassOop(klass_mirror); + // classes for arrays cannot be redefined + if (the_class_oop == NULL || !Klass::cast(the_class_oop)->oop_is_instance()) { -+ return false; -+ } + return false; + } +- +- // rewrite constant pool references in the methods_default_annotations: +- if (!rewrite_cp_refs_in_methods_default_annotations(scratch_class, +- THREAD)) { +- // propagate failure back to caller +- return false; + return true; +} + @@ -9570,7 +8333,31 @@ index 606be1c..ef4f380 100644 + instanceKlassHandle k_handle(THREAD, k_oop); + Verifier::verify(k_handle, Verifier::ThrowException, true, true, THREAD); + k_oop = k_oop->klass_part()->old_version(); -+ } + } +- +- return true; +-} // end rewrite_cp_refs() +- +- +-// Rewrite constant pool references in the methods. +-bool VM_RedefineClasses::rewrite_cp_refs_in_methods( +- instanceKlassHandle scratch_class, TRAPS) { +- +- objArrayHandle methods(THREAD, scratch_class->methods()); +- +- if (methods.is_null() || methods->length() == 0) { +- // no methods so nothing to do +- return true; +- } +- +- // rewrite constant pool references in the methods: +- for (int i = methods->length() - 1; i >= 0; i--) { +- methodHandle method(THREAD, (methodOop)methods->obj_at(i)); +- methodHandle new_method; +- rewrite_cp_refs_in_method(method, &new_method, CHECK_false); +- if (!new_method.is_null()) { +- // the method has been replaced so save the new method version +- methods->obj_at_put(i, new_method()); +} + +#endif @@ -9614,18 +8401,1125 @@ index 606be1c..ef4f380 100644 + code2 == Bytecodes::_fast_agetfield || + code2 == Bytecodes::_fast_fgetfield, ""); + *(bcp + 1) = Bytecodes::java_code(code2); -+ } -+ } -+} -+ + } + } +- +- return true; + } + +- +-// Rewrite constant pool references in the specific method. This code +-// was adapted from Rewriter::rewrite_method(). +-void VM_RedefineClasses::rewrite_cp_refs_in_method(methodHandle method, +- methodHandle *new_method_p, TRAPS) { +- +- *new_method_p = methodHandle(); // default is no new method +- +- // We cache a pointer to the bytecodes here in code_base. If GC +- // moves the methodOop, then the bytecodes will also move which +- // will likely cause a crash. We create a No_Safepoint_Verifier +- // object to detect whether we pass a possible safepoint in this +- // code block. +- No_Safepoint_Verifier nsv; +- +- // Bytecodes and their length +- address code_base = method->code_base(); +- int code_length = method->code_size(); +- +- int bc_length; +- for (int bci = 0; bci < code_length; bci += bc_length) { +- address bcp = code_base + bci; +- Bytecodes::Code c = (Bytecodes::Code)(*bcp); +- +- bc_length = Bytecodes::length_for(c); +- if (bc_length == 0) { +- // More complicated bytecodes report a length of zero so +- // we have to try again a slightly different way. +- bc_length = Bytecodes::length_at(method(), bcp); +- } +- +- assert(bc_length != 0, "impossible bytecode length"); +- +- switch (c) { +- case Bytecodes::_ldc: +- { +- int cp_index = *(bcp + 1); +- int new_index = find_new_index(cp_index); +- +- if (StressLdcRewrite && new_index == 0) { +- // If we are stressing ldc -> ldc_w rewriting, then we +- // always need a new_index value. +- new_index = cp_index; +- } +- if (new_index != 0) { +- // the original index is mapped so we have more work to do +- if (!StressLdcRewrite && new_index <= max_jubyte) { +- // The new value can still use ldc instead of ldc_w +- // unless we are trying to stress ldc -> ldc_w rewriting +- RC_TRACE_WITH_THREAD(0x00080000, THREAD, +- ("%s@" INTPTR_FORMAT " old=%d, new=%d", Bytecodes::name(c), +- bcp, cp_index, new_index)); +- *(bcp + 1) = new_index; +- } else { +- RC_TRACE_WITH_THREAD(0x00080000, THREAD, +- ("%s->ldc_w@" INTPTR_FORMAT " old=%d, new=%d", +- Bytecodes::name(c), bcp, cp_index, new_index)); +- // the new value needs ldc_w instead of ldc +- u_char inst_buffer[4]; // max instruction size is 4 bytes +- bcp = (address)inst_buffer; +- // construct new instruction sequence +- *bcp = Bytecodes::_ldc_w; +- bcp++; +- // Rewriter::rewrite_method() does not rewrite ldc -> ldc_w. +- // See comment below for difference between put_Java_u2() +- // and put_native_u2(). +- Bytes::put_Java_u2(bcp, new_index); +- +- Relocator rc(method, NULL /* no RelocatorListener needed */); +- methodHandle m; +- { +- Pause_No_Safepoint_Verifier pnsv(&nsv); +- +- // ldc is 2 bytes and ldc_w is 3 bytes +- m = rc.insert_space_at(bci, 3, inst_buffer, THREAD); +- if (m.is_null() || HAS_PENDING_EXCEPTION) { +- guarantee(false, "insert_space_at() failed"); +- } +- } +- +- // return the new method so that the caller can update +- // the containing class +- *new_method_p = method = m; +- // switch our bytecode processing loop from the old method +- // to the new method +- code_base = method->code_base(); +- code_length = method->code_size(); +- bcp = code_base + bci; +- c = (Bytecodes::Code)(*bcp); +- bc_length = Bytecodes::length_for(c); +- assert(bc_length != 0, "sanity check"); +- } // end we need ldc_w instead of ldc +- } // end if there is a mapped index +- } break; +- +- // these bytecodes have a two-byte constant pool index +- case Bytecodes::_anewarray : // fall through +- case Bytecodes::_checkcast : // fall through +- case Bytecodes::_getfield : // fall through +- case Bytecodes::_getstatic : // fall through +- case Bytecodes::_instanceof : // fall through +- case Bytecodes::_invokedynamic : // fall through +- case Bytecodes::_invokeinterface: // fall through +- case Bytecodes::_invokespecial : // fall through +- case Bytecodes::_invokestatic : // fall through +- case Bytecodes::_invokevirtual : // fall through +- case Bytecodes::_ldc_w : // fall through +- case Bytecodes::_ldc2_w : // fall through +- case Bytecodes::_multianewarray : // fall through +- case Bytecodes::_new : // fall through +- case Bytecodes::_putfield : // fall through +- case Bytecodes::_putstatic : +- { +- address p = bcp + 1; +- int cp_index = Bytes::get_Java_u2(p); +- int new_index = find_new_index(cp_index); +- if (new_index != 0) { +- // the original index is mapped so update w/ new value +- RC_TRACE_WITH_THREAD(0x00080000, THREAD, +- ("%s@" INTPTR_FORMAT " old=%d, new=%d", Bytecodes::name(c), +- bcp, cp_index, new_index)); +- // Rewriter::rewrite_method() uses put_native_u2() in this +- // situation because it is reusing the constant pool index +- // location for a native index into the constantPoolCache. +- // Since we are updating the constant pool index prior to +- // verification and constantPoolCache initialization, we +- // need to keep the new index in Java byte order. +- Bytes::put_Java_u2(p, new_index); +- } +- } break; +- } +- } // end for each bytecode +-} // end rewrite_cp_refs_in_method() +- +- +-// Rewrite constant pool references in the class_annotations field. +-bool VM_RedefineClasses::rewrite_cp_refs_in_class_annotations( +- instanceKlassHandle scratch_class, TRAPS) { +- +- typeArrayHandle class_annotations(THREAD, +- scratch_class->class_annotations()); +- if (class_annotations.is_null() || class_annotations->length() == 0) { +- // no class_annotations so nothing to do +- return true; +- } +- +- RC_TRACE_WITH_THREAD(0x02000000, THREAD, +- ("class_annotations length=%d", class_annotations->length())); +- +- int byte_i = 0; // byte index into class_annotations +- return rewrite_cp_refs_in_annotations_typeArray(class_annotations, byte_i, +- THREAD); +-} +- +- +-// Rewrite constant pool references in an annotations typeArray. This +-// "structure" is adapted from the RuntimeVisibleAnnotations_attribute +-// that is described in section 4.8.15 of the 2nd-edition of the VM spec: +-// +-// annotations_typeArray { +-// u2 num_annotations; +-// annotation annotations[num_annotations]; +-// } +-// +-bool VM_RedefineClasses::rewrite_cp_refs_in_annotations_typeArray( +- typeArrayHandle annotations_typeArray, int &byte_i_ref, TRAPS) { +- +- if ((byte_i_ref + 2) > annotations_typeArray->length()) { +- // not enough room for num_annotations field +- RC_TRACE_WITH_THREAD(0x02000000, THREAD, +- ("length() is too small for num_annotations field")); +- return false; +- } +- +- u2 num_annotations = Bytes::get_Java_u2((address) +- annotations_typeArray->byte_at_addr(byte_i_ref)); +- byte_i_ref += 2; +- +- RC_TRACE_WITH_THREAD(0x02000000, THREAD, +- ("num_annotations=%d", num_annotations)); +- +- int calc_num_annotations = 0; +- for (; calc_num_annotations < num_annotations; calc_num_annotations++) { +- if (!rewrite_cp_refs_in_annotation_struct(annotations_typeArray, +- byte_i_ref, THREAD)) { +- RC_TRACE_WITH_THREAD(0x02000000, THREAD, +- ("bad annotation_struct at %d", calc_num_annotations)); +- // propagate failure back to caller +- return false; +- } +- } +- assert(num_annotations == calc_num_annotations, "sanity check"); +- +- return true; +-} // end rewrite_cp_refs_in_annotations_typeArray() +- +- +-// Rewrite constant pool references in the annotation struct portion of +-// an annotations_typeArray. This "structure" is from section 4.8.15 of +-// the 2nd-edition of the VM spec: +-// +-// struct annotation { +-// u2 type_index; +-// u2 num_element_value_pairs; +-// { +-// u2 element_name_index; +-// element_value value; +-// } element_value_pairs[num_element_value_pairs]; +-// } +-// +-bool VM_RedefineClasses::rewrite_cp_refs_in_annotation_struct( +- typeArrayHandle annotations_typeArray, int &byte_i_ref, TRAPS) { +- if ((byte_i_ref + 2 + 2) > annotations_typeArray->length()) { +- // not enough room for smallest annotation_struct +- RC_TRACE_WITH_THREAD(0x02000000, THREAD, +- ("length() is too small for annotation_struct")); +- return false; +- } +- +- u2 type_index = rewrite_cp_ref_in_annotation_data(annotations_typeArray, +- byte_i_ref, "mapped old type_index=%d", THREAD); +- +- u2 num_element_value_pairs = Bytes::get_Java_u2((address) +- annotations_typeArray->byte_at_addr( +- byte_i_ref)); +- byte_i_ref += 2; +- +- RC_TRACE_WITH_THREAD(0x02000000, THREAD, +- ("type_index=%d num_element_value_pairs=%d", type_index, +- num_element_value_pairs)); +- +- int calc_num_element_value_pairs = 0; +- for (; calc_num_element_value_pairs < num_element_value_pairs; +- calc_num_element_value_pairs++) { +- if ((byte_i_ref + 2) > annotations_typeArray->length()) { +- // not enough room for another element_name_index, let alone +- // the rest of another component +- RC_TRACE_WITH_THREAD(0x02000000, THREAD, +- ("length() is too small for element_name_index")); +- return false; +- } +- +- u2 element_name_index = rewrite_cp_ref_in_annotation_data( +- annotations_typeArray, byte_i_ref, +- "mapped old element_name_index=%d", THREAD); +- +- RC_TRACE_WITH_THREAD(0x02000000, THREAD, +- ("element_name_index=%d", element_name_index)); +- +- if (!rewrite_cp_refs_in_element_value(annotations_typeArray, +- byte_i_ref, THREAD)) { +- RC_TRACE_WITH_THREAD(0x02000000, THREAD, +- ("bad element_value at %d", calc_num_element_value_pairs)); +- // propagate failure back to caller +- return false; +- } +- } // end for each component +- assert(num_element_value_pairs == calc_num_element_value_pairs, +- "sanity check"); +- +- return true; +-} // end rewrite_cp_refs_in_annotation_struct() +- +- +-// Rewrite a constant pool reference at the current position in +-// annotations_typeArray if needed. Returns the original constant +-// pool reference if a rewrite was not needed or the new constant +-// pool reference if a rewrite was needed. +-u2 VM_RedefineClasses::rewrite_cp_ref_in_annotation_data( +- typeArrayHandle annotations_typeArray, int &byte_i_ref, +- const char * trace_mesg, TRAPS) { +- +- address cp_index_addr = (address) +- annotations_typeArray->byte_at_addr(byte_i_ref); +- u2 old_cp_index = Bytes::get_Java_u2(cp_index_addr); +- u2 new_cp_index = find_new_index(old_cp_index); +- if (new_cp_index != 0) { +- RC_TRACE_WITH_THREAD(0x02000000, THREAD, (trace_mesg, old_cp_index)); +- Bytes::put_Java_u2(cp_index_addr, new_cp_index); +- old_cp_index = new_cp_index; +- } +- byte_i_ref += 2; +- return old_cp_index; +-} +- +- +-// Rewrite constant pool references in the element_value portion of an +-// annotations_typeArray. This "structure" is from section 4.8.15.1 of +-// the 2nd-edition of the VM spec: +-// +-// struct element_value { +-// u1 tag; +-// union { +-// u2 const_value_index; +-// { +-// u2 type_name_index; +-// u2 const_name_index; +-// } enum_const_value; +-// u2 class_info_index; +-// annotation annotation_value; +-// struct { +-// u2 num_values; +-// element_value values[num_values]; +-// } array_value; +-// } value; +-// } +-// +-bool VM_RedefineClasses::rewrite_cp_refs_in_element_value( +- typeArrayHandle annotations_typeArray, int &byte_i_ref, TRAPS) { +- +- if ((byte_i_ref + 1) > annotations_typeArray->length()) { +- // not enough room for a tag let alone the rest of an element_value +- RC_TRACE_WITH_THREAD(0x02000000, THREAD, +- ("length() is too small for a tag")); +- return false; +- } +- +- u1 tag = annotations_typeArray->byte_at(byte_i_ref); +- byte_i_ref++; +- RC_TRACE_WITH_THREAD(0x02000000, THREAD, ("tag='%c'", tag)); +- +- switch (tag) { +- // These BaseType tag values are from Table 4.2 in VM spec: +- case 'B': // byte +- case 'C': // char +- case 'D': // double +- case 'F': // float +- case 'I': // int +- case 'J': // long +- case 'S': // short +- case 'Z': // boolean +- +- // The remaining tag values are from Table 4.8 in the 2nd-edition of +- // the VM spec: +- case 's': +- { +- // For the above tag values (including the BaseType values), +- // value.const_value_index is right union field. +- +- if ((byte_i_ref + 2) > annotations_typeArray->length()) { +- // not enough room for a const_value_index +- RC_TRACE_WITH_THREAD(0x02000000, THREAD, +- ("length() is too small for a const_value_index")); +- return false; +- } +- +- u2 const_value_index = rewrite_cp_ref_in_annotation_data( +- annotations_typeArray, byte_i_ref, +- "mapped old const_value_index=%d", THREAD); +- +- RC_TRACE_WITH_THREAD(0x02000000, THREAD, +- ("const_value_index=%d", const_value_index)); +- } break; +- +- case 'e': +- { +- // for the above tag value, value.enum_const_value is right union field +- +- if ((byte_i_ref + 4) > annotations_typeArray->length()) { +- // not enough room for a enum_const_value +- RC_TRACE_WITH_THREAD(0x02000000, THREAD, +- ("length() is too small for a enum_const_value")); +- return false; +- } +- +- u2 type_name_index = rewrite_cp_ref_in_annotation_data( +- annotations_typeArray, byte_i_ref, +- "mapped old type_name_index=%d", THREAD); +- +- u2 const_name_index = rewrite_cp_ref_in_annotation_data( +- annotations_typeArray, byte_i_ref, +- "mapped old const_name_index=%d", THREAD); +- +- RC_TRACE_WITH_THREAD(0x02000000, THREAD, +- ("type_name_index=%d const_name_index=%d", type_name_index, +- const_name_index)); +- } break; +- +- case 'c': +- { +- // for the above tag value, value.class_info_index is right union field +- +- if ((byte_i_ref + 2) > annotations_typeArray->length()) { +- // not enough room for a class_info_index +- RC_TRACE_WITH_THREAD(0x02000000, THREAD, +- ("length() is too small for a class_info_index")); +- return false; +- } +- +- u2 class_info_index = rewrite_cp_ref_in_annotation_data( +- annotations_typeArray, byte_i_ref, +- "mapped old class_info_index=%d", THREAD); +- +- RC_TRACE_WITH_THREAD(0x02000000, THREAD, +- ("class_info_index=%d", class_info_index)); +- } break; +- +- case '@': +- // For the above tag value, value.attr_value is the right union +- // field. This is a nested annotation. +- if (!rewrite_cp_refs_in_annotation_struct(annotations_typeArray, +- byte_i_ref, THREAD)) { +- // propagate failure back to caller +- return false; +- } +- break; +- +- case '[': +- { +- if ((byte_i_ref + 2) > annotations_typeArray->length()) { +- // not enough room for a num_values field +- RC_TRACE_WITH_THREAD(0x02000000, THREAD, +- ("length() is too small for a num_values field")); +- return false; +- } +- +- // For the above tag value, value.array_value is the right union +- // field. This is an array of nested element_value. +- u2 num_values = Bytes::get_Java_u2((address) +- annotations_typeArray->byte_at_addr(byte_i_ref)); +- byte_i_ref += 2; +- RC_TRACE_WITH_THREAD(0x02000000, THREAD, ("num_values=%d", num_values)); +- +- int calc_num_values = 0; +- for (; calc_num_values < num_values; calc_num_values++) { +- if (!rewrite_cp_refs_in_element_value( +- annotations_typeArray, byte_i_ref, THREAD)) { +- RC_TRACE_WITH_THREAD(0x02000000, THREAD, +- ("bad nested element_value at %d", calc_num_values)); +- // propagate failure back to caller +- return false; +- } +- } +- assert(num_values == calc_num_values, "sanity check"); +- } break; +- +- default: +- RC_TRACE_WITH_THREAD(0x02000000, THREAD, ("bad tag=0x%x", tag)); +- return false; +- } // end decode tag field +- +- return true; +-} // end rewrite_cp_refs_in_element_value() +- +- +-// Rewrite constant pool references in a fields_annotations field. +-bool VM_RedefineClasses::rewrite_cp_refs_in_fields_annotations( +- instanceKlassHandle scratch_class, TRAPS) { +- +- objArrayHandle fields_annotations(THREAD, +- scratch_class->fields_annotations()); +- +- if (fields_annotations.is_null() || fields_annotations->length() == 0) { +- // no fields_annotations so nothing to do +- return true; +- } +- +- RC_TRACE_WITH_THREAD(0x02000000, THREAD, +- ("fields_annotations length=%d", fields_annotations->length())); +- +- for (int i = 0; i < fields_annotations->length(); i++) { +- typeArrayHandle field_annotations(THREAD, +- (typeArrayOop)fields_annotations->obj_at(i)); +- if (field_annotations.is_null() || field_annotations->length() == 0) { +- // this field does not have any annotations so skip it +- continue; +- } +- +- int byte_i = 0; // byte index into field_annotations +- if (!rewrite_cp_refs_in_annotations_typeArray(field_annotations, byte_i, +- THREAD)) { +- RC_TRACE_WITH_THREAD(0x02000000, THREAD, +- ("bad field_annotations at %d", i)); +- // propagate failure back to caller +- return false; +- } +- } +- +- return true; +-} // end rewrite_cp_refs_in_fields_annotations() +- +- +-// Rewrite constant pool references in a methods_annotations field. +-bool VM_RedefineClasses::rewrite_cp_refs_in_methods_annotations( +- instanceKlassHandle scratch_class, TRAPS) { +- +- objArrayHandle methods_annotations(THREAD, +- scratch_class->methods_annotations()); +- +- if (methods_annotations.is_null() || methods_annotations->length() == 0) { +- // no methods_annotations so nothing to do +- return true; +- } +- +- RC_TRACE_WITH_THREAD(0x02000000, THREAD, +- ("methods_annotations length=%d", methods_annotations->length())); +- +- for (int i = 0; i < methods_annotations->length(); i++) { +- typeArrayHandle method_annotations(THREAD, +- (typeArrayOop)methods_annotations->obj_at(i)); +- if (method_annotations.is_null() || method_annotations->length() == 0) { +- // this method does not have any annotations so skip it +- continue; +- } +- +- int byte_i = 0; // byte index into method_annotations +- if (!rewrite_cp_refs_in_annotations_typeArray(method_annotations, byte_i, +- THREAD)) { +- RC_TRACE_WITH_THREAD(0x02000000, THREAD, +- ("bad method_annotations at %d", i)); +- // propagate failure back to caller +- return false; +- } +- } +- +- return true; +-} // end rewrite_cp_refs_in_methods_annotations() +- +- +-// Rewrite constant pool references in a methods_parameter_annotations +-// field. This "structure" is adapted from the +-// RuntimeVisibleParameterAnnotations_attribute described in section +-// 4.8.17 of the 2nd-edition of the VM spec: +-// +-// methods_parameter_annotations_typeArray { +-// u1 num_parameters; +-// { +-// u2 num_annotations; +-// annotation annotations[num_annotations]; +-// } parameter_annotations[num_parameters]; +-// } +-// +-bool VM_RedefineClasses::rewrite_cp_refs_in_methods_parameter_annotations( +- instanceKlassHandle scratch_class, TRAPS) { +- +- objArrayHandle methods_parameter_annotations(THREAD, +- scratch_class->methods_parameter_annotations()); +- +- if (methods_parameter_annotations.is_null() +- || methods_parameter_annotations->length() == 0) { +- // no methods_parameter_annotations so nothing to do +- return true; +- } +- +- RC_TRACE_WITH_THREAD(0x02000000, THREAD, +- ("methods_parameter_annotations length=%d", +- methods_parameter_annotations->length())); +- +- for (int i = 0; i < methods_parameter_annotations->length(); i++) { +- typeArrayHandle method_parameter_annotations(THREAD, +- (typeArrayOop)methods_parameter_annotations->obj_at(i)); +- if (method_parameter_annotations.is_null() +- || method_parameter_annotations->length() == 0) { +- // this method does not have any parameter annotations so skip it +- continue; +- } +- +- if (method_parameter_annotations->length() < 1) { +- // not enough room for a num_parameters field +- RC_TRACE_WITH_THREAD(0x02000000, THREAD, +- ("length() is too small for a num_parameters field at %d", i)); +- return false; +- } +- +- int byte_i = 0; // byte index into method_parameter_annotations +- +- u1 num_parameters = method_parameter_annotations->byte_at(byte_i); +- byte_i++; +- +- RC_TRACE_WITH_THREAD(0x02000000, THREAD, +- ("num_parameters=%d", num_parameters)); +- +- int calc_num_parameters = 0; +- for (; calc_num_parameters < num_parameters; calc_num_parameters++) { +- if (!rewrite_cp_refs_in_annotations_typeArray( +- method_parameter_annotations, byte_i, THREAD)) { +- RC_TRACE_WITH_THREAD(0x02000000, THREAD, +- ("bad method_parameter_annotations at %d", calc_num_parameters)); +- // propagate failure back to caller +- return false; +- } +- } +- assert(num_parameters == calc_num_parameters, "sanity check"); +- } +- +- return true; +-} // end rewrite_cp_refs_in_methods_parameter_annotations() +- +- +-// Rewrite constant pool references in a methods_default_annotations +-// field. This "structure" is adapted from the AnnotationDefault_attribute +-// that is described in section 4.8.19 of the 2nd-edition of the VM spec: +-// +-// methods_default_annotations_typeArray { +-// element_value default_value; +-// } +-// +-bool VM_RedefineClasses::rewrite_cp_refs_in_methods_default_annotations( +- instanceKlassHandle scratch_class, TRAPS) { +- +- objArrayHandle methods_default_annotations(THREAD, +- scratch_class->methods_default_annotations()); +- +- if (methods_default_annotations.is_null() +- || methods_default_annotations->length() == 0) { +- // no methods_default_annotations so nothing to do +- return true; +- } +- +- RC_TRACE_WITH_THREAD(0x02000000, THREAD, +- ("methods_default_annotations length=%d", +- methods_default_annotations->length())); +- +- for (int i = 0; i < methods_default_annotations->length(); i++) { +- typeArrayHandle method_default_annotations(THREAD, +- (typeArrayOop)methods_default_annotations->obj_at(i)); +- if (method_default_annotations.is_null() +- || method_default_annotations->length() == 0) { +- // this method does not have any default annotations so skip it +- continue; +- } +- +- int byte_i = 0; // byte index into method_default_annotations +- +- if (!rewrite_cp_refs_in_element_value( +- method_default_annotations, byte_i, THREAD)) { +- RC_TRACE_WITH_THREAD(0x02000000, THREAD, +- ("bad default element_value at %d", i)); +- // propagate failure back to caller +- return false; +- } +- } +- +- return true; +-} // end rewrite_cp_refs_in_methods_default_annotations() +- +- +-// Rewrite constant pool references in the method's stackmap table. +-// These "structures" are adapted from the StackMapTable_attribute that +-// is described in section 4.8.4 of the 6.0 version of the VM spec +-// (dated 2005.10.26): +-// file:///net/quincunx.sfbay/export/gbracha/ClassFile-Java6.pdf +-// +-// stack_map { +-// u2 number_of_entries; +-// stack_map_frame entries[number_of_entries]; +-// } +-// +-void VM_RedefineClasses::rewrite_cp_refs_in_stack_map_table( +- methodHandle method, TRAPS) { +- +- if (!method->has_stackmap_table()) { +- return; +- } +- +- typeArrayOop stackmap_data = method->stackmap_data(); +- address stackmap_p = (address)stackmap_data->byte_at_addr(0); +- address stackmap_end = stackmap_p + stackmap_data->length(); +- +- assert(stackmap_p + 2 <= stackmap_end, "no room for number_of_entries"); +- u2 number_of_entries = Bytes::get_Java_u2(stackmap_p); +- stackmap_p += 2; +- +- RC_TRACE_WITH_THREAD(0x04000000, THREAD, +- ("number_of_entries=%u", number_of_entries)); +- +- // walk through each stack_map_frame +- u2 calc_number_of_entries = 0; +- for (; calc_number_of_entries < number_of_entries; calc_number_of_entries++) { +- // The stack_map_frame structure is a u1 frame_type followed by +- // 0 or more bytes of data: +- // +- // union stack_map_frame { +- // same_frame; +- // same_locals_1_stack_item_frame; +- // same_locals_1_stack_item_frame_extended; +- // chop_frame; +- // same_frame_extended; +- // append_frame; +- // full_frame; +- // } +- +- assert(stackmap_p + 1 <= stackmap_end, "no room for frame_type"); +- // The Linux compiler does not like frame_type to be u1 or u2. It +- // issues the following warning for the first if-statement below: +- // +- // "warning: comparison is always true due to limited range of data type" +- // +- u4 frame_type = *stackmap_p; +- stackmap_p++; +- +- // same_frame { +- // u1 frame_type = SAME; /* 0-63 */ +- // } +- if (frame_type >= 0 && frame_type <= 63) { +- // nothing more to do for same_frame +- } +- +- // same_locals_1_stack_item_frame { +- // u1 frame_type = SAME_LOCALS_1_STACK_ITEM; /* 64-127 */ +- // verification_type_info stack[1]; +- // } +- else if (frame_type >= 64 && frame_type <= 127) { +- rewrite_cp_refs_in_verification_type_info(stackmap_p, stackmap_end, +- calc_number_of_entries, frame_type, THREAD); +- } +- +- // reserved for future use +- else if (frame_type >= 128 && frame_type <= 246) { +- // nothing more to do for reserved frame_types +- } +- +- // same_locals_1_stack_item_frame_extended { +- // u1 frame_type = SAME_LOCALS_1_STACK_ITEM_EXTENDED; /* 247 */ +- // u2 offset_delta; +- // verification_type_info stack[1]; +- // } +- else if (frame_type == 247) { +- stackmap_p += 2; +- rewrite_cp_refs_in_verification_type_info(stackmap_p, stackmap_end, +- calc_number_of_entries, frame_type, THREAD); +- } +- +- // chop_frame { +- // u1 frame_type = CHOP; /* 248-250 */ +- // u2 offset_delta; +- // } +- else if (frame_type >= 248 && frame_type <= 250) { +- stackmap_p += 2; +- } +- +- // same_frame_extended { +- // u1 frame_type = SAME_FRAME_EXTENDED; /* 251*/ +- // u2 offset_delta; +- // } +- else if (frame_type == 251) { +- stackmap_p += 2; +- } +- +- // append_frame { +- // u1 frame_type = APPEND; /* 252-254 */ +- // u2 offset_delta; +- // verification_type_info locals[frame_type - 251]; +- // } +- else if (frame_type >= 252 && frame_type <= 254) { +- assert(stackmap_p + 2 <= stackmap_end, +- "no room for offset_delta"); +- stackmap_p += 2; +- u1 len = frame_type - 251; +- for (u1 i = 0; i < len; i++) { +- rewrite_cp_refs_in_verification_type_info(stackmap_p, stackmap_end, +- calc_number_of_entries, frame_type, THREAD); +- } +- } +- +- // full_frame { +- // u1 frame_type = FULL_FRAME; /* 255 */ +- // u2 offset_delta; +- // u2 number_of_locals; +- // verification_type_info locals[number_of_locals]; +- // u2 number_of_stack_items; +- // verification_type_info stack[number_of_stack_items]; +- // } +- else if (frame_type == 255) { +- assert(stackmap_p + 2 + 2 <= stackmap_end, +- "no room for smallest full_frame"); +- stackmap_p += 2; +- +- u2 number_of_locals = Bytes::get_Java_u2(stackmap_p); +- stackmap_p += 2; +- +- for (u2 locals_i = 0; locals_i < number_of_locals; locals_i++) { +- rewrite_cp_refs_in_verification_type_info(stackmap_p, stackmap_end, +- calc_number_of_entries, frame_type, THREAD); +- } +- +- // Use the largest size for the number_of_stack_items, but only get +- // the right number of bytes. +- u2 number_of_stack_items = Bytes::get_Java_u2(stackmap_p); +- stackmap_p += 2; +- +- for (u2 stack_i = 0; stack_i < number_of_stack_items; stack_i++) { +- rewrite_cp_refs_in_verification_type_info(stackmap_p, stackmap_end, +- calc_number_of_entries, frame_type, THREAD); +- } +- } +- } // end while there is a stack_map_frame +- assert(number_of_entries == calc_number_of_entries, "sanity check"); +-} // end rewrite_cp_refs_in_stack_map_table() +- +- +-// Rewrite constant pool references in the verification type info +-// portion of the method's stackmap table. These "structures" are +-// adapted from the StackMapTable_attribute that is described in +-// section 4.8.4 of the 6.0 version of the VM spec (dated 2005.10.26): +-// file:///net/quincunx.sfbay/export/gbracha/ClassFile-Java6.pdf +-// +-// The verification_type_info structure is a u1 tag followed by 0 or +-// more bytes of data: +-// +-// union verification_type_info { +-// Top_variable_info; +-// Integer_variable_info; +-// Float_variable_info; +-// Long_variable_info; +-// Double_variable_info; +-// Null_variable_info; +-// UninitializedThis_variable_info; +-// Object_variable_info; +-// Uninitialized_variable_info; +-// } +-// +-void VM_RedefineClasses::rewrite_cp_refs_in_verification_type_info( +- address& stackmap_p_ref, address stackmap_end, u2 frame_i, +- u1 frame_type, TRAPS) { +- +- assert(stackmap_p_ref + 1 <= stackmap_end, "no room for tag"); +- u1 tag = *stackmap_p_ref; +- stackmap_p_ref++; +- +- switch (tag) { +- // Top_variable_info { +- // u1 tag = ITEM_Top; /* 0 */ +- // } +- // verificationType.hpp has zero as ITEM_Bogus instead of ITEM_Top +- case 0: // fall through +- +- // Integer_variable_info { +- // u1 tag = ITEM_Integer; /* 1 */ +- // } +- case ITEM_Integer: // fall through +- +- // Float_variable_info { +- // u1 tag = ITEM_Float; /* 2 */ +- // } +- case ITEM_Float: // fall through +- +- // Double_variable_info { +- // u1 tag = ITEM_Double; /* 3 */ +- // } +- case ITEM_Double: // fall through +- +- // Long_variable_info { +- // u1 tag = ITEM_Long; /* 4 */ +- // } +- case ITEM_Long: // fall through +- +- // Null_variable_info { +- // u1 tag = ITEM_Null; /* 5 */ +- // } +- case ITEM_Null: // fall through +- +- // UninitializedThis_variable_info { +- // u1 tag = ITEM_UninitializedThis; /* 6 */ +- // } +- case ITEM_UninitializedThis: +- // nothing more to do for the above tag types +- break; +- +- // Object_variable_info { +- // u1 tag = ITEM_Object; /* 7 */ +- // u2 cpool_index; +- // } +- case ITEM_Object: +- { +- assert(stackmap_p_ref + 2 <= stackmap_end, "no room for cpool_index"); +- u2 cpool_index = Bytes::get_Java_u2(stackmap_p_ref); +- u2 new_cp_index = find_new_index(cpool_index); +- if (new_cp_index != 0) { +- RC_TRACE_WITH_THREAD(0x04000000, THREAD, +- ("mapped old cpool_index=%d", cpool_index)); +- Bytes::put_Java_u2(stackmap_p_ref, new_cp_index); +- cpool_index = new_cp_index; +- } +- stackmap_p_ref += 2; +- +- RC_TRACE_WITH_THREAD(0x04000000, THREAD, +- ("frame_i=%u, frame_type=%u, cpool_index=%d", frame_i, +- frame_type, cpool_index)); +- } break; +- +- // Uninitialized_variable_info { +- // u1 tag = ITEM_Uninitialized; /* 8 */ +- // u2 offset; +- // } +- case ITEM_Uninitialized: +- assert(stackmap_p_ref + 2 <= stackmap_end, "no room for offset"); +- stackmap_p_ref += 2; +- break; +- +- default: +- RC_TRACE_WITH_THREAD(0x04000000, THREAD, +- ("frame_i=%u, frame_type=%u, bad tag=0x%x", frame_i, frame_type, tag)); +- ShouldNotReachHere(); +- break; +- } // end switch (tag) +-} // end rewrite_cp_refs_in_verification_type_info() +- +- +-// Change the constant pool associated with klass scratch_class to +-// scratch_cp. If shrink is true, then scratch_cp_length elements +-// are copied from scratch_cp to a smaller constant pool and the +-// smaller constant pool is associated with scratch_class. +-void VM_RedefineClasses::set_new_constant_pool( +- instanceKlassHandle scratch_class, constantPoolHandle scratch_cp, +- int scratch_cp_length, TRAPS) { +- assert(scratch_cp->length() >= scratch_cp_length, "sanity check"); +- +- // scratch_cp is a merged constant pool and has enough space for a +- // worst case merge situation. We want to associate the minimum +- // sized constant pool with the klass to save space. +- constantPoolHandle smaller_cp(THREAD, +- oopFactory::new_constantPool(scratch_cp_length, +- oopDesc::IsUnsafeConc, +- THREAD)); +- // preserve orig_length() value in the smaller copy +- int orig_length = scratch_cp->orig_length(); +- assert(orig_length != 0, "sanity check"); +- smaller_cp->set_orig_length(orig_length); +- +- // attach klass to new constant pool +- // reference to the cp holder is needed for copy_operands() +- smaller_cp->set_pool_holder(scratch_class()); +- +- scratch_cp->copy_cp_to(1, scratch_cp_length - 1, smaller_cp, 1, THREAD); +- scratch_cp = smaller_cp; +- smaller_cp()->set_is_conc_safe(true); +- +- // attach new constant pool to klass +- scratch_class->set_constants(scratch_cp()); +- +- int i; // for portability +- +- // update each field in klass to use new constant pool indices as needed +- for (JavaFieldStream fs(scratch_class); !fs.done(); fs.next()) { +- jshort cur_index = fs.name_index(); +- jshort new_index = find_new_index(cur_index); +- if (new_index != 0) { +- RC_TRACE_WITH_THREAD(0x00080000, THREAD, +- ("field-name_index change: %d to %d", cur_index, new_index)); +- fs.set_name_index(new_index); +- } +- cur_index = fs.signature_index(); +- new_index = find_new_index(cur_index); +- if (new_index != 0) { +- RC_TRACE_WITH_THREAD(0x00080000, THREAD, +- ("field-signature_index change: %d to %d", cur_index, new_index)); +- fs.set_signature_index(new_index); +- } +- cur_index = fs.initval_index(); +- new_index = find_new_index(cur_index); +- if (new_index != 0) { +- RC_TRACE_WITH_THREAD(0x00080000, THREAD, +- ("field-initval_index change: %d to %d", cur_index, new_index)); +- fs.set_initval_index(new_index); +- } +- cur_index = fs.generic_signature_index(); +- new_index = find_new_index(cur_index); +- if (new_index != 0) { +- RC_TRACE_WITH_THREAD(0x00080000, THREAD, +- ("field-generic_signature change: %d to %d", cur_index, new_index)); +- fs.set_generic_signature_index(new_index); +- } +- } // end for each field +- +- // Update constant pool indices in the inner classes info to use +- // new constant indices as needed. The inner classes info is a +- // quadruple: +- // (inner_class_info, outer_class_info, inner_name, inner_access_flags) +- InnerClassesIterator iter(scratch_class); +- for (; !iter.done(); iter.next()) { +- int cur_index = iter.inner_class_info_index(); +- if (cur_index == 0) { +- continue; // JVM spec. allows null inner class refs so skip it +- } +- int new_index = find_new_index(cur_index); +- if (new_index != 0) { +- RC_TRACE_WITH_THREAD(0x00080000, THREAD, +- ("inner_class_info change: %d to %d", cur_index, new_index)); +- iter.set_inner_class_info_index(new_index); +- } +- cur_index = iter.outer_class_info_index(); +- new_index = find_new_index(cur_index); +- if (new_index != 0) { +- RC_TRACE_WITH_THREAD(0x00080000, THREAD, +- ("outer_class_info change: %d to %d", cur_index, new_index)); +- iter.set_outer_class_info_index(new_index); +- } +- cur_index = iter.inner_name_index(); +- new_index = find_new_index(cur_index); +- if (new_index != 0) { +- RC_TRACE_WITH_THREAD(0x00080000, THREAD, +- ("inner_name change: %d to %d", cur_index, new_index)); +- iter.set_inner_name_index(new_index); +- } +- } // end for each inner class +- +- // Attach each method in klass to the new constant pool and update +- // to use new constant pool indices as needed: +- objArrayHandle methods(THREAD, scratch_class->methods()); +- for (i = methods->length() - 1; i >= 0; i--) { +- methodHandle method(THREAD, (methodOop)methods->obj_at(i)); +- method->set_constants(scratch_cp()); +- +- int new_index = find_new_index(method->name_index()); +- if (new_index != 0) { +- RC_TRACE_WITH_THREAD(0x00080000, THREAD, +- ("method-name_index change: %d to %d", method->name_index(), +- new_index)); +- method->set_name_index(new_index); +- } +- new_index = find_new_index(method->signature_index()); +- if (new_index != 0) { +- RC_TRACE_WITH_THREAD(0x00080000, THREAD, +- ("method-signature_index change: %d to %d", +- method->signature_index(), new_index)); +- method->set_signature_index(new_index); +- } +- new_index = find_new_index(method->generic_signature_index()); +- if (new_index != 0) { +- RC_TRACE_WITH_THREAD(0x00080000, THREAD, +- ("method-generic_signature_index change: %d to %d", +- method->generic_signature_index(), new_index)); +- method->set_generic_signature_index(new_index); +- } +- +- // Update constant pool indices in the method's checked exception +- // table to use new constant indices as needed. +- int cext_length = method->checked_exceptions_length(); +- if (cext_length > 0) { +- CheckedExceptionElement * cext_table = +- method->checked_exceptions_start(); +- for (int j = 0; j < cext_length; j++) { +- int cur_index = cext_table[j].class_cp_index; +- int new_index = find_new_index(cur_index); +- if (new_index != 0) { +- RC_TRACE_WITH_THREAD(0x00080000, THREAD, +- ("cext-class_cp_index change: %d to %d", cur_index, new_index)); +- cext_table[j].class_cp_index = (u2)new_index; +- } +- } // end for each checked exception table entry +- } // end if there are checked exception table entries +- +- // Update each catch type index in the method's exception table +- // to use new constant pool indices as needed. The exception table +- // holds quadruple entries of the form: +- // (beg_bci, end_bci, handler_bci, klass_index) +- +- ExceptionTable ex_table(method()); +- int ext_length = ex_table.length(); +- +- for (int j = 0; j < ext_length; j ++) { +- int cur_index = ex_table.catch_type_index(j); +- int new_index = find_new_index(cur_index); +- if (new_index != 0) { +- RC_TRACE_WITH_THREAD(0x00080000, THREAD, +- ("ext-klass_index change: %d to %d", cur_index, new_index)); +- ex_table.set_catch_type_index(j, new_index); +- } +- } // end for each exception table entry +- +- // Update constant pool indices in the method's local variable +- // table to use new constant indices as needed. The local variable +- // table hold sextuple entries of the form: +- // (start_pc, length, name_index, descriptor_index, signature_index, slot) +- int lvt_length = method->localvariable_table_length(); +- if (lvt_length > 0) { +- LocalVariableTableElement * lv_table = +- method->localvariable_table_start(); +- for (int j = 0; j < lvt_length; j++) { +- int cur_index = lv_table[j].name_cp_index; +- int new_index = find_new_index(cur_index); +- if (new_index != 0) { +- RC_TRACE_WITH_THREAD(0x00080000, THREAD, +- ("lvt-name_cp_index change: %d to %d", cur_index, new_index)); +- lv_table[j].name_cp_index = (u2)new_index; +- } +- cur_index = lv_table[j].descriptor_cp_index; +- new_index = find_new_index(cur_index); +- if (new_index != 0) { +- RC_TRACE_WITH_THREAD(0x00080000, THREAD, +- ("lvt-descriptor_cp_index change: %d to %d", cur_index, +- new_index)); +- lv_table[j].descriptor_cp_index = (u2)new_index; +- } +- cur_index = lv_table[j].signature_cp_index; +- new_index = find_new_index(cur_index); +- if (new_index != 0) { +- RC_TRACE_WITH_THREAD(0x00080000, THREAD, +- ("lvt-signature_cp_index change: %d to %d", cur_index, new_index)); +- lv_table[j].signature_cp_index = (u2)new_index; +- } +- } // end for each local variable table entry +- } // end if there are local variable table entries +- +- rewrite_cp_refs_in_stack_map_table(method, THREAD); +- } // end for each method +- assert(scratch_cp()->is_conc_safe(), "Just checking"); +-} // end set_new_constant_pool() +- +- +-// Unevolving classes may point to methods of the_class directly +// Unevolving classes may point to old methods directly -+// from their constant pool caches, itables, and/or vtables. We -+// use the SystemDictionary::classes_do() facility and this helper + // from their constant pool caches, itables, and/or vtables. We + // use the SystemDictionary::classes_do() facility and this helper +-// to fix up these pointers. +// to fix up these pointers. Additional field offsets and vtable indices +// in the constant pool cache entries are fixed. -+// -+// Note: We currently don't support updating the vtable in -+// arrayKlassOops. See Open Issues in jvmtiRedefineClasses.hpp. + // + // Note: We currently don't support updating the vtable in + // arrayKlassOops. See Open Issues in jvmtiRedefineClasses.hpp. +-void VM_RedefineClasses::adjust_cpool_cache_and_vtable(klassOop k_oop, +- oop initiating_loader, TRAPS) { +void VM_RedefineClasses::adjust_cpool_cache(klassOop k_oop_latest, oop initiating_loader, TRAPS) { + klassOop k_oop = k_oop_latest; + while (k_oop != NULL) { @@ -9676,24 +9570,22 @@ index 606be1c..ef4f380 100644 + } + } + k_oop = k_oop->klass_part()->old_version(); - } - } - - void VM_RedefineClasses::update_jmethod_ids() { - for (int j = 0; j < _matching_methods_length; ++j) { -- methodOop old_method = _matching_old_methods[j]; ++ } ++} ++ ++void VM_RedefineClasses::update_jmethod_ids() { ++ for (int j = 0; j < _matching_methods_length; ++j) { + methodOop old_method = (methodOop)_old_methods->obj_at(_matching_old_methods[j]); + RC_TRACE(0x00008000, ("matching method %s", old_method->name_and_sig_as_C_string())); + - jmethodID jmid = old_method->find_jmethod_id_or_null(); ++ jmethodID jmid = old_method->find_jmethod_id_or_null(); + if (old_method->new_version() != NULL && jmid == NULL) { + // (tw) Have to create jmethodID in this case + jmid = old_method->jmethod_id(); + } + - if (jmid != NULL) { - // There is a jmethodID, change it to point to the new method -- methodHandle new_method_h(_matching_new_methods[j]); ++ if (jmid != NULL) { ++ // There is a jmethodID, change it to point to the new method + methodHandle new_method_h((methodOop)_new_methods->obj_at(_matching_new_methods[j])); + if (old_method->new_version() == NULL) { + methodHandle old_method_h((methodOop)_old_methods->obj_at(_matching_old_methods[j])); @@ -9707,35 +9599,18 @@ index 606be1c..ef4f380 100644 + //RC_TRACE(0x00008000, ("Changed jmethodID for new method assigned to %d / result=%d", jmid, result); + + } - JNIHandles::change_method_associated_with_jmethod_id(jmid, new_method_h); -- assert(JNIHandles::resolve_jmethod_id(jmid) == _matching_new_methods[j], -- "should be replaced"); ++ JNIHandles::change_method_associated_with_jmethod_id(jmid, new_method_h); + //RC_TRACE(0x00008000, ("changing method associated with jmethod id %d to %s", (int)jmid, new_method_h->name()->as_C_string()); + assert(JNIHandles::resolve_jmethod_id(jmid) == (methodOop)_new_methods->obj_at(_matching_new_methods[j]), "should be replaced"); + jmethodID mid = ((methodOop)_new_methods->obj_at(_matching_new_methods[j]))->jmethod_id(); + assert(JNIHandles::resolve_non_null((jobject)mid) == new_method_h(), "must match!"); + + //RC_TRACE(0x00008000, ("jmethodID new method: %d jmethodID old method: %d", new_method_h->jmethod_id(), old_method->jmethod_id()); - } - } - } - --void VM_RedefineClasses::check_methods_and_mark_as_obsolete( -- BitMap *emcp_methods, int * emcp_method_count_p) { -- *emcp_method_count_p = 0; -- int obsolete_count = 0; -- int old_index = 0; -- for (int j = 0; j < _matching_methods_length; ++j, ++old_index) { -- methodOop old_method = _matching_old_methods[j]; -- methodOop new_method = _matching_new_methods[j]; -- methodOop old_array_method; - -- // Maintain an old_index into the _old_methods array by skipping -- // deleted methods -- while ((old_array_method = (methodOop) _old_methods->obj_at(old_index)) -- != old_method) { -- ++old_index; -- } ++ } ++ } ++} ++ ++ +// Deoptimize all compiled code that depends on this class. +// +// If the can_redefine_classes capability is obtained in the onload @@ -9753,100 +9628,20 @@ index 606be1c..ef4f380 100644 +// +void VM_RedefineClasses::flush_dependent_code(instanceKlassHandle k_h, TRAPS) { + assert_locked_or_safepoint(Compile_lock); - -- if (MethodComparator::methods_EMCP(old_method, new_method)) { -- // The EMCP definition from JSR-163 requires the bytecodes to be -- // the same with the exception of constant pool indices which may -- // differ. However, the constants referred to by those indices -- // must be the same. -- // -- // We use methods_EMCP() for comparison since constant pool -- // merging can remove duplicate constant pool entries that were -- // present in the old method and removed from the rewritten new -- // method. A faster binary comparison function would consider the -- // old and new methods to be different when they are actually -- // EMCP. -- // -- // The old and new methods are EMCP and you would think that we -- // could get rid of one of them here and now and save some space. -- // However, the concept of EMCP only considers the bytecodes and -- // the constant pool entries in the comparison. Other things, -- // e.g., the line number table (LNT) or the local variable table -- // (LVT) don't count in the comparison. So the new (and EMCP) -- // method can have a new LNT that we need so we can't just -- // overwrite the new method with the old method. -- // -- // When this routine is called, we have already attached the new -- // methods to the_class so the old methods are effectively -- // overwritten. However, if an old method is still executing, -- // then the old method cannot be collected until sometime after -- // the old method call has returned. So the overwriting of old -- // methods by new methods will save us space except for those -- // (hopefully few) old methods that are still executing. -- // -- // A method refers to a constMethodOop and this presents another -- // possible avenue to space savings. The constMethodOop in the -- // new method contains possibly new attributes (LNT, LVT, etc). -- // At first glance, it seems possible to save space by replacing -- // the constMethodOop in the old method with the constMethodOop -- // from the new method. The old and new methods would share the -- // same constMethodOop and we would save the space occupied by -- // the old constMethodOop. However, the constMethodOop contains -- // a back reference to the containing method. Sharing the -- // constMethodOop between two methods could lead to confusion in -- // the code that uses the back reference. This would lead to -- // brittle code that could be broken in non-obvious ways now or -- // in the future. -- // -- // Another possibility is to copy the constMethodOop from the new -- // method to the old method and then overwrite the new method with -- // the old method. Since the constMethodOop contains the bytecodes -- // for the method embedded in the oop, this option would change -- // the bytecodes out from under any threads executing the old -- // method and make the thread's bcp invalid. Since EMCP requires -- // that the bytecodes be the same modulo constant pool indices, it -- // is straight forward to compute the correct new bcp in the new -- // constMethodOop from the old bcp in the old constMethodOop. The -- // time consuming part would be searching all the frames in all -- // of the threads to find all of the calls to the old method. -- // -- // It looks like we will have to live with the limited savings -- // that we get from effectively overwriting the old methods -- // when the new methods are attached to the_class. ++ + // All dependencies have been recorded from startup or this is a second or + // subsequent use of RedefineClasses - -- // track which methods are EMCP for add_previous_version() call -- emcp_methods->set_bit(old_index); -- (*emcp_method_count_p)++; ++ + // For now deopt all + // (tw) TODO: Improve the dependency system such that we can safely deopt only a subset of the methods + if (0 && JvmtiExport::all_dependencies_are_recorded()) { + Universe::flush_evol_dependents_on(k_h); + } else { + CodeCache::mark_all_nmethods_for_deoptimization(); - -- // An EMCP method is _not_ obsolete. An obsolete method has a -- // different jmethodID than the current method. An EMCP method -- // has the same jmethodID as the current method. Having the -- // same jmethodID for all EMCP versions of a method allows for -- // a consistent view of the EMCP methods regardless of which -- // EMCP method you happen to have in hand. For example, a -- // breakpoint set in one EMCP method will work for all EMCP -- // versions of the method including the current one. -- } else { -- // mark obsolete methods as such -- old_method->set_is_obsolete(); -- obsolete_count++; ++ + ResourceMark rm(THREAD); + DeoptimizationMarker dm; - -- // obsolete methods need a unique idnum -- u2 num = instanceKlass::cast(_the_class_oop)->next_method_idnum(); -- if (num != constMethodOopDesc::UNSET_IDNUM) { --// u2 old_num = old_method->method_idnum(); -- old_method->set_method_idnum(num); --// TO DO: attach obsolete annotations to obsolete method's new idnum ++ + // Deoptimize all activations depending on marked nmethods + Deoptimization::deoptimize_dependents(); + @@ -9877,7 +9672,7 @@ index 606be1c..ef4f380 100644 + if (oj >= _old_methods->length()) { + if (nj >= _new_methods->length()) { + break; // we've looked at everything, done - } ++ } + // New method at the end + new_method = (methodOop) _new_methods->obj_at(nj); + _added_methods[_added_methods_length++] = nj; @@ -10094,40 +9889,16 @@ index 606be1c..ef4f380 100644 + old_method->set_is_old(); + old_method->set_is_obsolete(); + ++obsolete_count; - // With tracing we try not to "yack" too much. The position of - // this trace assumes there are fewer obsolete methods than - // EMCP methods. -- RC_TRACE(0x00000100, ("mark %s(%s) as obsolete", ++ // With tracing we try not to "yack" too much. The position of ++ // this trace assumes there are fewer obsolete methods than ++ // EMCP methods. + RC_TRACE(0x00008000, ("mark deleted %s(%s) as obsolete", - old_method->name()->as_C_string(), - old_method->signature()->as_C_string())); - } -- old_method->set_is_old(); -- } -- for (int i = 0; i < _deleted_methods_length; ++i) { -- methodOop old_method = _deleted_methods[i]; -- -- assert(old_method->vtable_index() < 0, -- "cannot delete methods with vtable entries");; -- -- // Mark all deleted methods as old and obsolete -- old_method->set_is_old(); -- old_method->set_is_obsolete(); -- ++obsolete_count; -- // With tracing we try not to "yack" too much. The position of -- // this trace assumes there are fewer obsolete methods than -- // EMCP methods. -- RC_TRACE(0x00000100, ("mark deleted %s(%s) as obsolete", -- old_method->name()->as_C_string(), -- old_method->signature()->as_C_string())); -- } -- assert((*emcp_method_count_p + obsolete_count) == _old_methods->length(), -- "sanity check"); -- RC_TRACE(0x00000100, ("EMCP_cnt=%d, obsolete_cnt=%d", *emcp_method_count_p, -- obsolete_count)); ++ old_method->name()->as_C_string(), ++ old_method->signature()->as_C_string())); ++ } + //assert((*emcp_method_count_p + obsolete_count) == _old_methods->length(), "sanity check"); + RC_TRACE(0x00008000, ("EMCP_cnt=%d, obsolete_cnt=%d !", *emcp_method_count_p, obsolete_count)); - } ++} + +// Increment the classRedefinedCount field in the specific instanceKlass +// and in all direct and indirect subclasses. @@ -10141,27 +9912,162 @@ index 606be1c..ef4f380 100644 + +#ifndef PRODUCT +void VM_RedefineClasses::check_class(klassOop k_oop, TRAPS) { -+ Klass *k = k_oop->klass_part(); -+ if (k->oop_is_instance()) { -+ HandleMark hm(THREAD); -+ instanceKlass *ik = (instanceKlass *) k; + Klass *k = k_oop->klass_part(); + if (k->oop_is_instance()) { + HandleMark hm(THREAD); + instanceKlass *ik = (instanceKlass *) k; +- +- // HotSpot specific optimization! HotSpot does not currently +- // support delegation from the bootstrap class loader to a +- // user-defined class loader. This means that if the bootstrap +- // class loader is the initiating class loader, then it will also +- // be the defining class loader. This also means that classes +- // loaded by the bootstrap class loader cannot refer to classes +- // loaded by a user-defined class loader. Note: a user-defined +- // class loader can delegate to the bootstrap class loader. +- // +- // If the current class being redefined has a user-defined class +- // loader as its defining class loader, then we can skip all +- // classes loaded by the bootstrap class loader. +- bool is_user_defined = +- instanceKlass::cast(_the_class_oop)->class_loader() != NULL; +- if (is_user_defined && ik->class_loader() == NULL) { +- return; +- } +- +- // This is a very busy routine. We don't want too much tracing +- // printed out. +- bool trace_name_printed = false; +- +- // Very noisy: only enable this call if you are trying to determine +- // that a specific class gets found by this routine. +- // RC_TRACE macro has an embedded ResourceMark +- // RC_TRACE_WITH_THREAD(0x00100000, THREAD, +- // ("adjust check: name=%s", ik->external_name())); +- // trace_name_printed = true; +- +- // Fix the vtable embedded in the_class and subclasses of the_class, +- // if one exists. We discard scratch_class and we don't keep an +- // instanceKlass around to hold obsolete methods so we don't have +- // any other instanceKlass embedded vtables to update. The vtable +- // holds the methodOops for virtual (but not final) methods. +- if (ik->vtable_length() > 0 && ik->is_subtype_of(_the_class_oop)) { +- // ik->vtable() creates a wrapper object; rm cleans it up + assert(ik->is_newest_version(), "must be latest version in system dictionary"); + + if (ik->vtable_length() > 0) { -+ ResourceMark rm(THREAD); + ResourceMark rm(THREAD); +- ik->vtable()->adjust_method_entries(_matching_old_methods, +- _matching_new_methods, +- _matching_methods_length, +- &trace_name_printed); +- } +- +- // If the current class has an itable and we are either redefining an +- // interface or if the current class is a subclass of the_class, then +- // we potentially have to fix the itable. If we are redefining an +- // interface, then we have to call adjust_method_entries() for +- // every instanceKlass that has an itable since there isn't a +- // subclass relationship between an interface and an instanceKlass. +- if (ik->itable_length() > 0 && (Klass::cast(_the_class_oop)->is_interface() +- || ik->is_subclass_of(_the_class_oop))) { +- // ik->itable() creates a wrapper object; rm cleans it up +- ResourceMark rm(THREAD); +- ik->itable()->adjust_method_entries(_matching_old_methods, +- _matching_new_methods, +- _matching_methods_length, +- &trace_name_printed); +- } +- +- // The constant pools in other classes (other_cp) can refer to +- // methods in the_class. We have to update method information in +- // other_cp's cache. If other_cp has a previous version, then we +- // have to repeat the process for each previous version. The +- // constant pool cache holds the methodOops for non-virtual +- // methods and for virtual, final methods. +- // +- // Special case: if the current class is the_class, then new_cp +- // has already been attached to the_class and old_cp has already +- // been added as a previous version. The new_cp doesn't have any +- // cached references to old methods so it doesn't need to be +- // updated. We can simply start with the previous version(s) in +- // that case. +- constantPoolHandle other_cp; +- constantPoolCacheOop cp_cache; +- +- if (k_oop != _the_class_oop) { +- // this klass' constant pool cache may need adjustment +- other_cp = constantPoolHandle(ik->constants()); +- cp_cache = other_cp->cache(); +- if (cp_cache != NULL) { +- cp_cache->adjust_method_entries(_matching_old_methods, +- _matching_new_methods, +- _matching_methods_length, +- &trace_name_printed); + if (!ik->vtable()->check_no_old_entries()) { + RC_TRACE(0x00000001, ("size of class: %d\n", + k_oop->size())); + RC_TRACE(0x00000001, ("klassVtable::check_no_old_entries failure -- OLD method found -- class: %s", + ik->signature_name())); + assert(false, "OLD method found"); -+ } + } +- } +- { +- ResourceMark rm(THREAD); +- // PreviousVersionInfo objects returned via PreviousVersionWalker +- // contain a GrowableArray of handles. We have to clean up the +- // GrowableArray _after_ the PreviousVersionWalker destructor +- // has destroyed the handles. +- { +- // the previous versions' constant pool caches may need adjustment +- PreviousVersionWalker pvw(ik); +- for (PreviousVersionInfo * pv_info = pvw.next_previous_version(); +- pv_info != NULL; pv_info = pvw.next_previous_version()) { +- other_cp = pv_info->prev_constant_pool_handle(); +- cp_cache = other_cp->cache(); +- if (cp_cache != NULL) { +- cp_cache->adjust_method_entries(_matching_old_methods, +- _matching_new_methods, +- _matching_methods_length, +- &trace_name_printed); +- } +- } +- } // pvw is cleaned up +- } // rm is cleaned up +- } +-} +- +-void VM_RedefineClasses::update_jmethod_ids() { +- for (int j = 0; j < _matching_methods_length; ++j) { +- methodOop old_method = _matching_old_methods[j]; +- jmethodID jmid = old_method->find_jmethod_id_or_null(); +- if (jmid != NULL) { +- // There is a jmethodID, change it to point to the new method +- methodHandle new_method_h(_matching_new_methods[j]); +- JNIHandles::change_method_associated_with_jmethod_id(jmid, new_method_h); +- assert(JNIHandles::resolve_jmethod_id(jmid) == _matching_new_methods[j], +- "should be replaced"); + + ik->vtable()->verify(tty, true); -+ } -+ } -+} -+ + } + } + } + +-void VM_RedefineClasses::check_methods_and_mark_as_obsolete( +- BitMap *emcp_methods, int * emcp_method_count_p) { +- *emcp_method_count_p = 0; +- int obsolete_count = 0; +- int old_index = 0; +- for (int j = 0; j < _matching_methods_length; ++j, ++old_index) { +- methodOop old_method = _matching_old_methods[j]; +- methodOop new_method = _matching_new_methods[j]; +- methodOop old_array_method; +- +- // Maintain an old_index into the _old_methods array by skipping +- // deleted methods +- while ((old_array_method = (methodOop) _old_methods->obj_at(old_index)) +- != old_method) { +- ++old_index; +#endif + +VM_RedefineClasses::FindAffectedKlassesClosure::FindAffectedKlassesClosure( GrowableArray *original_klasses, GrowableArray *result ) @@ -10184,7 +10090,91 @@ index 606be1c..ef4f380 100644 + RC_TRACE(0x00008000, ("Found affected class: %s", klass->klass_part()->name()->as_C_string())); + _result->append(klass); + break; -+ } + } +- +- if (MethodComparator::methods_EMCP(old_method, new_method)) { +- // The EMCP definition from JSR-163 requires the bytecodes to be +- // the same with the exception of constant pool indices which may +- // differ. However, the constants referred to by those indices +- // must be the same. +- // +- // We use methods_EMCP() for comparison since constant pool +- // merging can remove duplicate constant pool entries that were +- // present in the old method and removed from the rewritten new +- // method. A faster binary comparison function would consider the +- // old and new methods to be different when they are actually +- // EMCP. +- // +- // The old and new methods are EMCP and you would think that we +- // could get rid of one of them here and now and save some space. +- // However, the concept of EMCP only considers the bytecodes and +- // the constant pool entries in the comparison. Other things, +- // e.g., the line number table (LNT) or the local variable table +- // (LVT) don't count in the comparison. So the new (and EMCP) +- // method can have a new LNT that we need so we can't just +- // overwrite the new method with the old method. +- // +- // When this routine is called, we have already attached the new +- // methods to the_class so the old methods are effectively +- // overwritten. However, if an old method is still executing, +- // then the old method cannot be collected until sometime after +- // the old method call has returned. So the overwriting of old +- // methods by new methods will save us space except for those +- // (hopefully few) old methods that are still executing. +- // +- // A method refers to a constMethodOop and this presents another +- // possible avenue to space savings. The constMethodOop in the +- // new method contains possibly new attributes (LNT, LVT, etc). +- // At first glance, it seems possible to save space by replacing +- // the constMethodOop in the old method with the constMethodOop +- // from the new method. The old and new methods would share the +- // same constMethodOop and we would save the space occupied by +- // the old constMethodOop. However, the constMethodOop contains +- // a back reference to the containing method. Sharing the +- // constMethodOop between two methods could lead to confusion in +- // the code that uses the back reference. This would lead to +- // brittle code that could be broken in non-obvious ways now or +- // in the future. +- // +- // Another possibility is to copy the constMethodOop from the new +- // method to the old method and then overwrite the new method with +- // the old method. Since the constMethodOop contains the bytecodes +- // for the method embedded in the oop, this option would change +- // the bytecodes out from under any threads executing the old +- // method and make the thread's bcp invalid. Since EMCP requires +- // that the bytecodes be the same modulo constant pool indices, it +- // is straight forward to compute the correct new bcp in the new +- // constMethodOop from the old bcp in the old constMethodOop. The +- // time consuming part would be searching all the frames in all +- // of the threads to find all of the calls to the old method. +- // +- // It looks like we will have to live with the limited savings +- // that we get from effectively overwriting the old methods +- // when the new methods are attached to the_class. +- +- // track which methods are EMCP for add_previous_version() call +- emcp_methods->set_bit(old_index); +- (*emcp_method_count_p)++; +- +- // An EMCP method is _not_ obsolete. An obsolete method has a +- // different jmethodID than the current method. An EMCP method +- // has the same jmethodID as the current method. Having the +- // same jmethodID for all EMCP versions of a method allows for +- // a consistent view of the EMCP methods regardless of which +- // EMCP method you happen to have in hand. For example, a +- // breakpoint set in one EMCP method will work for all EMCP +- // versions of the method including the current one. +- } else { +- // mark obsolete methods as such +- old_method->set_is_obsolete(); +- obsolete_count++; +- +- // obsolete methods need a unique idnum +- u2 num = instanceKlass::cast(_the_class_oop)->next_method_idnum(); +- if (num != constMethodOopDesc::UNSET_IDNUM) { +-// u2 old_num = old_method->method_idnum(); +- old_method->set_method_idnum(num); +-// TO DO: attach obsolete annotations to obsolete method's new idnum + } +} + @@ -10222,9 +10212,32 @@ index 606be1c..ef4f380 100644 + links->append(Pair(curOop, the_class())); + break; + } -+ } -+ } -+ } + } +- // With tracing we try not to "yack" too much. The position of +- // this trace assumes there are fewer obsolete methods than +- // EMCP methods. +- RC_TRACE(0x00000100, ("mark %s(%s) as obsolete", +- old_method->name()->as_C_string(), +- old_method->signature()->as_C_string())); + } +- old_method->set_is_old(); + } +- for (int i = 0; i < _deleted_methods_length; ++i) { +- methodOop old_method = _deleted_methods[i]; +- +- assert(old_method->vtable_index() < 0, +- "cannot delete methods with vtable entries");; +- +- // Mark all deleted methods as old and obsolete +- old_method->set_is_old(); +- old_method->set_is_obsolete(); +- ++obsolete_count; +- // With tracing we try not to "yack" too much. The position of +- // this trace assumes there are fewer obsolete methods than +- // EMCP methods. +- RC_TRACE(0x00000100, ("mark deleted %s(%s) as obsolete", +- old_method->name()->as_C_string(), +- old_method->signature()->as_C_string())); + + + RC_TRACE(0x00000001, ("Identified links between classes! ")); @@ -10245,7 +10258,11 @@ index 606be1c..ef4f380 100644 + links->append(Pair(interfaceKlass, klass())); + } + } -+ } + } +- assert((*emcp_method_count_p + obsolete_count) == _old_methods->length(), +- "sanity check"); +- RC_TRACE(0x00000100, ("EMCP_cnt=%d, obsolete_cnt=%d", *emcp_method_count_p, +- obsolete_count)); + + if (RC_TRACE_ENABLED(0x00000002)) { + RC_TRACE(0x00000002, ("Identified links: ")); @@ -10295,8 +10312,8 @@ index 606be1c..ef4f380 100644 + } + + return JVMTI_ERROR_NONE; -+} -+ + } + +void VM_RedefineClasses::oops_do(OopClosure *closure) { + + if (_updated_oops != NULL) { @@ -10322,9 +10339,10 @@ index 606be1c..ef4f380 100644 + fi->set_access_flags(fd->access_flags().as_short()); +} + - ++ // This internal class transfers the native function registration from old methods // to new methods. It is designed to handle both the simple case of unchanged + // native methods and the complex cases of native method prefixes being added and/or @@ -2969,7 +3161,7 @@ // Same, caused by prefix removal only 3_2_1_m -> 3_2_m // @@ -10363,13 +10381,7 @@ index 606be1c..ef4f380 100644 + // Wahoo, we found a (possibly prefixed) version of the method, return it. + return method; } -+ if (depth < prefix_count) { -+ // Try applying further prefixes (other than this one). -+ method = search_prefix_name_space(depth+1, name_str, name_len, signature); -+ if (method != NULL) { -+ return method; // found -+ } - +- - // Try adding this prefix to the method name and see if it matches - // another method name. - char* prefix = prefixes[depth]; @@ -10384,6 +10396,13 @@ index 606be1c..ef4f380 100644 - // If found along this branch, it was prefixed, mark as such - method->set_is_prefixed_native(); - return method; // found ++ if (depth < prefix_count) { ++ // Try applying further prefixes (other than this one). ++ method = search_prefix_name_space(depth+1, name_str, name_len, signature); ++ if (method != NULL) { ++ return method; // found ++ } ++ + // Try adding this prefix to the method name and see if it matches + // another method name. + char* prefix = prefixes[depth]; @@ -10452,7 +10471,11 @@ index 606be1c..ef4f380 100644 - TransferNativeFunctionRegistration transfer(the_class); - transfer.transfer_registrations(_deleted_methods, _deleted_methods_length); - transfer.transfer_registrations(_matching_old_methods, _matching_methods_length); --} ++void VM_RedefineClasses::transfer_old_native_function_registrations(instanceKlassHandle old_klass) { ++ TransferNativeFunctionRegistration transfer(old_klass); ++ transfer.transfer_registrations(old_klass, _deleted_methods, _deleted_methods_length); ++ transfer.transfer_registrations(old_klass, _matching_old_methods, _matching_methods_length); + } - -// Deoptimize all compiled code that depends on this class. -// @@ -10988,15 +11011,10 @@ index 606be1c..ef4f380 100644 - m->print_name(tty); - tty->cr(); - } -+void VM_RedefineClasses::transfer_old_native_function_registrations(instanceKlassHandle old_klass) { -+ TransferNativeFunctionRegistration transfer(old_klass); -+ transfer.transfer_registrations(old_klass, _deleted_methods, _deleted_methods_length); -+ transfer.transfer_registrations(old_klass, _matching_old_methods, _matching_methods_length); - } -diff --git a/src/share/vm/prims/jvmtiRedefineClasses.hpp b/src/share/vm/prims/jvmtiRedefineClasses.hpp -index bfac62c..d7ba79f 100644 ---- a/src/share/vm/prims/jvmtiRedefineClasses.hpp -+++ b/src/share/vm/prims/jvmtiRedefineClasses.hpp +-} +diff -r 882f6c762ac5 src/share/vm/prims/jvmtiRedefineClasses.hpp +--- a/src/share/vm/prims/jvmtiRedefineClasses.hpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/prims/jvmtiRedefineClasses.hpp Thu Jul 09 23:18:17 2015 -0700 @@ -1,26 +1,29 @@ /* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. @@ -11049,7 +11067,7 @@ index bfac62c..d7ba79f 100644 #ifndef SHARE_VM_PRIMS_JVMTIREDEFINECLASSES_HPP #define SHARE_VM_PRIMS_JVMTIREDEFINECLASSES_HPP -@@ -32,330 +35,27 @@ +@@ -32,331 +35,28 @@ #include "oops/objArrayOop.hpp" #include "prims/jvmtiRedefineClassesTrace.hpp" #include "runtime/vm_operations.hpp" @@ -11385,12 +11403,13 @@ index bfac62c..d7ba79f 100644 static int _deleted_methods_length; static int _added_methods_length; static klassOop _the_class_oop; -+ -+ static int _revision_number; ++ static int _revision_number; ++ // The instance fields are used to pass information from // doit_prologue() to doit() and doit_epilogue(). -@@ -370,42 +70,28 @@ + jint _class_count; +@@ -370,43 +70,29 @@ // _index_map_p contains any entries. int _index_map_count; intArray * _index_map_p; @@ -11435,7 +11454,7 @@ index bfac62c..d7ba79f 100644 + jvmtiError find_sorted_affected_classes(GrowableArray *all_affected_klasses); + jvmtiError find_class_bytes(instanceKlassHandle the_class, const unsigned char **class_bytes, jint *class_byte_count, jboolean *not_changed); jvmtiError load_new_class_versions(TRAPS); -- + - // Verify that the caller provided class definition(s) that meet - // the restrictions of RedefineClasses. Normalize the order of - // overloaded methods as needed. @@ -11446,9 +11465,10 @@ index bfac62c..d7ba79f 100644 - // Used by compare_and_normalize_class_versions() when normalizing - // overloaded methods or changing idnum as when adding or deleting methods. - void swap_all_method_annotations(int i, int j, instanceKlassHandle scratch_class); - +- // Figure out which new methods match old methods in name and signature, // which methods have been added, and which are no longer present + void compute_added_deleted_matching_methods(); @@ -414,103 +100,99 @@ // Change jmethodIDs to point to the new methods void update_jmethod_ids(); @@ -11625,10 +11645,9 @@ index bfac62c..d7ba79f 100644 }; #endif // SHARE_VM_PRIMS_JVMTIREDEFINECLASSES_HPP -diff --git a/src/share/vm/prims/methodComparator.cpp b/src/share/vm/prims/methodComparator.cpp -index 60eaf97..07bb6e3 100644 ---- a/src/share/vm/prims/methodComparator.cpp -+++ b/src/share/vm/prims/methodComparator.cpp +diff -r 882f6c762ac5 src/share/vm/prims/methodComparator.cpp +--- a/src/share/vm/prims/methodComparator.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/prims/methodComparator.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -65,6 +65,7 @@ if (! args_same(c_old, c_new)) return false; @@ -11637,10 +11656,9 @@ index 60eaf97..07bb6e3 100644 return true; } -diff --git a/src/share/vm/prims/nativeLookup.cpp b/src/share/vm/prims/nativeLookup.cpp -index 41fc42d..53b3e0c 100644 ---- a/src/share/vm/prims/nativeLookup.cpp -+++ b/src/share/vm/prims/nativeLookup.cpp +diff -r 882f6c762ac5 src/share/vm/prims/nativeLookup.cpp +--- a/src/share/vm/prims/nativeLookup.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/prims/nativeLookup.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -35,6 +35,7 @@ #include "oops/symbol.hpp" #include "prims/jvm_misc.hpp" @@ -11649,14 +11667,14 @@ index 41fc42d..53b3e0c 100644 #include "runtime/arguments.hpp" #include "runtime/handles.inline.hpp" #include "runtime/javaCalls.hpp" -@@ -52,7 +53,6 @@ - #ifdef TARGET_OS_FAMILY_bsd +@@ -53,7 +54,6 @@ # include "os_bsd.inline.hpp" #endif -- +- static void mangle_name_on(outputStream* st, Symbol* name, int begin, int end) { char* bytes = (char*)name->bytes() + begin; + char* end_bytes = (char*)name->bytes() + end; @@ -138,6 +138,40 @@ { CC"Java_sun_hotspot_WhiteBox_registerNatives", NULL, FN_PTR(JVM_RegisterWhiteBoxMethods) }, }; @@ -11708,10 +11726,9 @@ index 41fc42d..53b3e0c 100644 // Otherwise call static method findNative in ClassLoader KlassHandle klass (THREAD, SystemDictionary::ClassLoader_klass()); -diff --git a/src/share/vm/runtime/arguments.cpp b/src/share/vm/runtime/arguments.cpp -index 0496367..62e9d55 100644 ---- a/src/share/vm/runtime/arguments.cpp -+++ b/src/share/vm/runtime/arguments.cpp +diff -r 882f6c762ac5 src/share/vm/runtime/arguments.cpp +--- a/src/share/vm/runtime/arguments.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/runtime/arguments.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -1874,6 +1874,15 @@ status = false; } @@ -11728,10 +11745,9 @@ index 0496367..62e9d55 100644 return status; } -diff --git a/src/share/vm/runtime/deoptimization.cpp b/src/share/vm/runtime/deoptimization.cpp -index 2b767d4..e9b6f47 100644 ---- a/src/share/vm/runtime/deoptimization.cpp -+++ b/src/share/vm/runtime/deoptimization.cpp +diff -r 882f6c762ac5 src/share/vm/runtime/deoptimization.cpp +--- a/src/share/vm/runtime/deoptimization.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/runtime/deoptimization.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -599,6 +599,38 @@ // Cleanup thread deopt data cleanup_deopt_info(thread, array); @@ -11771,10 +11787,9 @@ index 2b767d4..e9b6f47 100644 #ifndef PRODUCT if (VerifyStack) { ResourceMark res_mark; -diff --git a/src/share/vm/runtime/frame.cpp b/src/share/vm/runtime/frame.cpp -index aacd835..3772b3f 100644 ---- a/src/share/vm/runtime/frame.cpp -+++ b/src/share/vm/runtime/frame.cpp +diff -r 882f6c762ac5 src/share/vm/runtime/frame.cpp +--- a/src/share/vm/runtime/frame.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/runtime/frame.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -408,6 +408,12 @@ *interpreter_frame_method_addr() = method; } @@ -11823,10 +11838,9 @@ index aacd835..3772b3f 100644 } } } -diff --git a/src/share/vm/runtime/frame.hpp b/src/share/vm/runtime/frame.hpp -index 9c7bb72..04a6595 100644 ---- a/src/share/vm/runtime/frame.hpp -+++ b/src/share/vm/runtime/frame.hpp +diff -r 882f6c762ac5 src/share/vm/runtime/frame.hpp +--- a/src/share/vm/runtime/frame.hpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/runtime/frame.hpp Thu Jul 09 23:18:17 2015 -0700 @@ -346,6 +346,7 @@ // Method & constant pool cache methodOop interpreter_frame_method() const; @@ -11835,10 +11849,9 @@ index 9c7bb72..04a6595 100644 methodOop* interpreter_frame_method_addr() const; constantPoolCacheOop* interpreter_frame_cache_addr() const; #ifdef PPC -diff --git a/src/share/vm/runtime/globals.hpp b/src/share/vm/runtime/globals.hpp -index 0731ca0..cbd6eca 100644 ---- a/src/share/vm/runtime/globals.hpp -+++ b/src/share/vm/runtime/globals.hpp +diff -r 882f6c762ac5 src/share/vm/runtime/globals.hpp +--- a/src/share/vm/runtime/globals.hpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/runtime/globals.hpp Thu Jul 09 23:18:17 2015 -0700 @@ -1229,9 +1229,23 @@ product(bool, StressLdcRewrite, false, \ "Force ldc -> ldc_w rewrite during RedefineClasses") \ @@ -11863,10 +11876,9 @@ index 0731ca0..cbd6eca 100644 develop(bool, StressMethodComparator, false, \ "run the MethodComparator on all loaded methods") \ \ -diff --git a/src/share/vm/runtime/interfaceSupport.hpp b/src/share/vm/runtime/interfaceSupport.hpp -index 2875ee0..61fd8fe 100644 ---- a/src/share/vm/runtime/interfaceSupport.hpp -+++ b/src/share/vm/runtime/interfaceSupport.hpp +diff -r 882f6c762ac5 src/share/vm/runtime/interfaceSupport.hpp +--- a/src/share/vm/runtime/interfaceSupport.hpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/runtime/interfaceSupport.hpp Thu Jul 09 23:18:17 2015 -0700 @@ -296,7 +296,7 @@ ThreadToNativeFromVM(JavaThread *thread) : ThreadStateTransition(thread) { // We are leaving the VM at this point and going directly to native code. @@ -11876,10 +11888,9 @@ index 2875ee0..61fd8fe 100644 thread->frame_anchor()->make_walkable(thread); trans_and_fence(_thread_in_vm, _thread_in_native); // Check for pending. async. exceptions or suspends. -diff --git a/src/share/vm/runtime/javaCalls.cpp b/src/share/vm/runtime/javaCalls.cpp -index edbba98..4a27925 100644 ---- a/src/share/vm/runtime/javaCalls.cpp -+++ b/src/share/vm/runtime/javaCalls.cpp +diff -r 882f6c762ac5 src/share/vm/runtime/javaCalls.cpp +--- a/src/share/vm/runtime/javaCalls.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/runtime/javaCalls.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -60,7 +60,7 @@ bool clear_pending_exception = true; @@ -11889,10 +11900,9 @@ index edbba98..4a27925 100644 guarantee(!thread->is_Compiler_thread(), "cannot make java calls from the compiler"); _result = result; -diff --git a/src/share/vm/runtime/jniHandles.cpp b/src/share/vm/runtime/jniHandles.cpp -index 3cbcaca..30839d7 100644 ---- a/src/share/vm/runtime/jniHandles.cpp -+++ b/src/share/vm/runtime/jniHandles.cpp +diff -r 882f6c762ac5 src/share/vm/runtime/jniHandles.cpp +--- a/src/share/vm/runtime/jniHandles.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/runtime/jniHandles.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -112,6 +112,10 @@ } @@ -11904,10 +11914,9 @@ index 3cbcaca..30839d7 100644 return (jmethodID) make_weak_global(mh); } -diff --git a/src/share/vm/runtime/mutex.cpp b/src/share/vm/runtime/mutex.cpp -index 2095237..c541434 100644 ---- a/src/share/vm/runtime/mutex.cpp -+++ b/src/share/vm/runtime/mutex.cpp +diff -r 882f6c762ac5 src/share/vm/runtime/mutex.cpp +--- a/src/share/vm/runtime/mutex.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/runtime/mutex.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -1227,7 +1227,7 @@ // in increasing rank order (modulo any native ranks) for (tmp = locks; tmp != NULL; tmp = tmp->next()) { @@ -11934,10 +11943,9 @@ index 2095237..c541434 100644 locks != NULL && locks->rank() <= this->rank() && !SafepointSynchronize::is_at_safepoint() && this != Interrupt_lock && -diff --git a/src/share/vm/runtime/mutex.hpp b/src/share/vm/runtime/mutex.hpp -index 7d2cd82..11eb32e 100644 ---- a/src/share/vm/runtime/mutex.hpp -+++ b/src/share/vm/runtime/mutex.hpp +diff -r 882f6c762ac5 src/share/vm/runtime/mutex.hpp +--- a/src/share/vm/runtime/mutex.hpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/runtime/mutex.hpp Thu Jul 09 23:18:17 2015 -0700 @@ -109,7 +109,8 @@ barrier = safepoint + 1, nonleaf = barrier + 1, @@ -11948,10 +11956,9 @@ index 7d2cd82..11eb32e 100644 }; // The WaitSet and EntryList linked lists are composed of ParkEvents. -diff --git a/src/share/vm/runtime/mutexLocker.cpp b/src/share/vm/runtime/mutexLocker.cpp -index 77fddef..9d453d4 100644 ---- a/src/share/vm/runtime/mutexLocker.cpp -+++ b/src/share/vm/runtime/mutexLocker.cpp +diff -r 882f6c762ac5 src/share/vm/runtime/mutexLocker.cpp +--- a/src/share/vm/runtime/mutexLocker.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/runtime/mutexLocker.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -49,6 +49,7 @@ // Consider using GCC's __read_mostly. @@ -11984,10 +11991,9 @@ index 77fddef..9d453d4 100644 #ifdef INCLUDE_TRACE def(JfrMsg_lock , Monitor, leaf, true); -diff --git a/src/share/vm/runtime/mutexLocker.hpp b/src/share/vm/runtime/mutexLocker.hpp -index a6549b1..089ed11 100644 ---- a/src/share/vm/runtime/mutexLocker.hpp -+++ b/src/share/vm/runtime/mutexLocker.hpp +diff -r 882f6c762ac5 src/share/vm/runtime/mutexLocker.hpp +--- a/src/share/vm/runtime/mutexLocker.hpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/runtime/mutexLocker.hpp Thu Jul 09 23:18:17 2015 -0700 @@ -43,6 +43,8 @@ // Mutexes used in the VM. @@ -11997,10 +12003,9 @@ index a6549b1..089ed11 100644 extern Monitor* SystemDictionary_lock; // a lock on the system dictonary extern Mutex* PackageTable_lock; // a lock on the class loader package table extern Mutex* CompiledIC_lock; // a lock used to guard compiled IC patching and access -diff --git a/src/share/vm/runtime/reflection.cpp b/src/share/vm/runtime/reflection.cpp -index 1665d93..6baabba 100644 ---- a/src/share/vm/runtime/reflection.cpp -+++ b/src/share/vm/runtime/reflection.cpp +diff -r 882f6c762ac5 src/share/vm/runtime/reflection.cpp +--- a/src/share/vm/runtime/reflection.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/runtime/reflection.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -468,7 +468,8 @@ // sun/reflect/MagicAccessorImpl subclasses to succeed trivially. if ( JDK_Version::is_gte_jdk14x_version() @@ -12034,10 +12039,9 @@ index 1665d93..6baabba 100644 return true; } -diff --git a/src/share/vm/runtime/sharedRuntime.cpp b/src/share/vm/runtime/sharedRuntime.cpp -index 709d783..e0e19b1 100644 ---- a/src/share/vm/runtime/sharedRuntime.cpp -+++ b/src/share/vm/runtime/sharedRuntime.cpp +diff -r 882f6c762ac5 src/share/vm/runtime/sharedRuntime.cpp +--- a/src/share/vm/runtime/sharedRuntime.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/runtime/sharedRuntime.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -1137,7 +1137,20 @@ if (JvmtiExport::can_hotswap_or_post_breakpoint()) { int retry_count = 0; @@ -12060,10 +12064,9 @@ index 709d783..e0e19b1 100644 // If has a pending exception then there is no need to re-try to // resolve this method. // If the method has been redefined, we need to try again. -diff --git a/src/share/vm/runtime/thread.cpp b/src/share/vm/runtime/thread.cpp -index f3acf4d..ea61cae 100644 ---- a/src/share/vm/runtime/thread.cpp -+++ b/src/share/vm/runtime/thread.cpp +diff -r 882f6c762ac5 src/share/vm/runtime/thread.cpp +--- a/src/share/vm/runtime/thread.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/runtime/thread.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -216,6 +216,8 @@ set_self_raw_id(0); set_lgrp_id(-1); @@ -12081,22 +12084,22 @@ index f3acf4d..ea61cae 100644 #ifdef ASSERT _visited_for_critical_count = false; -@@ -880,6 +883,15 @@ - bool Thread::owns_locks_but_compiled_lock() const { - for(Monitor *cur = _owned_locks; cur; cur = cur->next()) { - if (cur != Compile_lock) return true; -+ } -+ return false; -+} -+ +@@ -884,6 +887,15 @@ + return false; + } + +bool Thread::owns_locks_but_redefine_classes_lock() const { + for(Monitor *cur = _owned_locks; cur; cur = cur->next()) { + if (cur != RedefineClasses_lock && cur->rank() != Mutex::redefine_classes) { + return true; + } - } - return false; - } ++ } ++ return false; ++} ++ + + #endif + @@ -1637,7 +1649,7 @@ ThreadStateTransition::transition_and_fence(this, _thread_new, _thread_in_vm); @@ -12131,11 +12134,10 @@ index f3acf4d..ea61cae 100644 // All JavaThreads #define ALL_JAVA_THREADS(X) for (JavaThread* X = _thread_list; X; X = X->next()) -diff --git a/src/share/vm/runtime/thread.hpp b/src/share/vm/runtime/thread.hpp -index 774bd27..4ca4502 100644 ---- a/src/share/vm/runtime/thread.hpp -+++ b/src/share/vm/runtime/thread.hpp -@@ -203,10 +203,13 @@ +diff -r 882f6c762ac5 src/share/vm/runtime/thread.hpp +--- a/src/share/vm/runtime/thread.hpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/runtime/thread.hpp Thu Jul 09 23:18:17 2015 -0700 +@@ -203,11 +203,14 @@ void enter_signal_handler() { _num_nested_signal++; } void leave_signal_handler() { _num_nested_signal--; } bool is_inside_signal_handler() const { return _num_nested_signal > 0; } @@ -12144,11 +12146,12 @@ index 774bd27..4ca4502 100644 private: // Debug tracing static void trace(const char* msg, const Thread* const thread) PRODUCT_RETURN; -+ -+ Mutex* _redefine_classes_mutex; ++ Mutex* _redefine_classes_mutex; ++ // Active_handles points to a block of handles JNIHandleBlock* _active_handles; + @@ -530,10 +533,15 @@ uintptr_t _self_raw_id; // used by get_thread (mutable) int _lgrp_id; @@ -12217,10 +12220,9 @@ index 774bd27..4ca4502 100644 // Initializes the vm and creates the vm thread static jint create_vm(JavaVMInitArgs* args, bool* canTryAgain); static void convert_vm_init_libraries_to_agents(); -diff --git a/src/share/vm/runtime/vframe.cpp b/src/share/vm/runtime/vframe.cpp -index 09e324f..d47ffef 100644 ---- a/src/share/vm/runtime/vframe.cpp -+++ b/src/share/vm/runtime/vframe.cpp +diff -r 882f6c762ac5 src/share/vm/runtime/vframe.cpp +--- a/src/share/vm/runtime/vframe.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/runtime/vframe.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -253,6 +253,46 @@ return fr().interpreter_frame_method(); } @@ -12268,10 +12270,9 @@ index 09e324f..d47ffef 100644 StackValueCollection* interpretedVFrame::locals() const { int length = method()->max_locals(); -diff --git a/src/share/vm/runtime/vframe.hpp b/src/share/vm/runtime/vframe.hpp -index badfea5..edbc5c7 100644 ---- a/src/share/vm/runtime/vframe.hpp -+++ b/src/share/vm/runtime/vframe.hpp +diff -r 882f6c762ac5 src/share/vm/runtime/vframe.hpp +--- a/src/share/vm/runtime/vframe.hpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/runtime/vframe.hpp Thu Jul 09 23:18:17 2015 -0700 @@ -163,6 +163,7 @@ StackValueCollection* locals() const; StackValueCollection* expressions() const; @@ -12280,10 +12281,9 @@ index badfea5..edbc5c7 100644 void set_locals(StackValueCollection* values) const; -diff --git a/src/share/vm/runtime/vmThread.cpp b/src/share/vm/runtime/vmThread.cpp -index 0a3e0da..3d294e1 100644 ---- a/src/share/vm/runtime/vmThread.cpp -+++ b/src/share/vm/runtime/vmThread.cpp +diff -r 882f6c762ac5 src/share/vm/runtime/vmThread.cpp +--- a/src/share/vm/runtime/vmThread.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/runtime/vmThread.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -694,6 +694,10 @@ void VMThread::oops_do(OopClosure* f, CodeBlobClosure* cf) { Thread::oops_do(f, cf); @@ -12295,10 +12295,9 @@ index 0a3e0da..3d294e1 100644 } //------------------------------------------------------------------------------------------------------------------ -diff --git a/src/share/vm/utilities/exceptions.cpp b/src/share/vm/utilities/exceptions.cpp -index 03f254d..c9e0efc 100644 ---- a/src/share/vm/utilities/exceptions.cpp -+++ b/src/share/vm/utilities/exceptions.cpp +diff -r 882f6c762ac5 src/share/vm/utilities/exceptions.cpp +--- a/src/share/vm/utilities/exceptions.cpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/utilities/exceptions.cpp Thu Jul 09 23:18:17 2015 -0700 @@ -254,6 +254,8 @@ assert(thread->is_Java_thread(), "can only be called by a Java thread"); assert(!thread->has_pending_exception(), "already has exception"); @@ -12334,10 +12333,9 @@ index 03f254d..c9e0efc 100644 return h_exception; } -diff --git a/src/share/vm/utilities/growableArray.hpp b/src/share/vm/utilities/growableArray.hpp -index ea92a80..05e940e 100644 ---- a/src/share/vm/utilities/growableArray.hpp -+++ b/src/share/vm/utilities/growableArray.hpp +diff -r 882f6c762ac5 src/share/vm/utilities/growableArray.hpp +--- a/src/share/vm/utilities/growableArray.hpp Thu Jul 09 23:10:04 2015 -0700 ++++ b/src/share/vm/utilities/growableArray.hpp Thu Jul 09 23:18:17 2015 -0700 @@ -145,6 +145,33 @@ assert(on_stack(), "fast ResourceObj path only"); return (void*)resource_allocate_bytes(thread, elementSize * _max); @@ -12371,4 +12369,4 @@ index ea92a80..05e940e 100644 + } }; - template class GrowableArray : public GenericGrowableArray { \ No newline at end of file + template class GrowableArray : public GenericGrowableArray { diff --git a/hotspot/.hg/patches/series b/hotspot/.hg/patches/series index c15467c0..10f843cb 100644 --- a/hotspot/.hg/patches/series +++ b/hotspot/.hg/patches/series @@ -35,8 +35,9 @@ light-jdk7u79-b02.patch #+light-jdk7u79-b02 light-jdk7u60-deopt-cp.patch #+light-jdk7u60-b09 #+light-jdk7u71-b01 #+light-jdk7u79-b02 full-jdk7u60-b09.patch #+full-jdk7u60-b09 full-jdk7u71-b01.patch #+full-jdk7u71-b01 -full-jdk7u79-b02.patch #+full-jdk7u79-b02 -full-jdk7u60-deopt-cp.patch #+full-jdk7u60-b09 #+full-jdk7u71-b01 #+full-jdk7u79-b02 +full-jdk7u79-b15.patch #+full-jdk7u79-b15 +full-jdk7u60-deopt-cp.patch #+full-jdk7u60-b09 #+full-jdk7u71-b01 #+full-jdk7u79-b15 +full-jdk7u79-b15-method-handles.patch #+full-jdk7u79-b15 light-jdk8u5-b13.patch #+light-jdk8u5-b13 -- 2.39.5