diff -r f5603a6e5042 .hgignore --- a/.hgignore Wed Nov 17 22:42:08 2010 -0800 +++ b/.hgignore Fri Dec 17 13:24:08 2010 +0100 @@ -1,7 +1,27 @@ -^build/ -^dist/ -/nbproject/private/ -^src/share/tools/hsdis/build/ -^src/share/tools/IdealGraphVisualizer/[a-zA-Z0-9]*/build/ -^src/share/tools/IdealGraphVisualizer/build/ -^src/share/tools/IdealGraphVisualizer/dist/ +^build/ +^work/ +^work64/ +^java/ +^java64/ +^dist/ +^make/ +build/ +/nbproject/private/ +^src/share/tools/hsdis/build/ +private/ +dist/ +.toc$ +.ps$ +.dvi$ +.aux$ +.swp$ +.dll$ +.dylib$ +.so$ +.bak$ +.orig$ +~$ +hotspot.log$ +^src/share/tools/IdealGraphVisualizer/[a-zA-Z0-9]*/build/ +^src/share/tools/IdealGraphVisualizer/build/ +^src/share/tools/IdealGraphVisualizer/dist/ diff -r f5603a6e5042 .hgtags --- a/.hgtags Wed Nov 17 22:42:08 2010 -0800 +++ b/.hgtags Fri Dec 17 13:24:08 2010 +0100 @@ -131,3 +131,7 @@ 806d0c037e6bbb88dac0699673f4ba55ee8c02da jdk7-b117 698b7b727e12de44139d8cca6ab9a494ead13253 jdk7-b118 3ef7426b4deac5dcfd4afb35cabe9ab3d666df91 hs20-b02 +946201493cab53f518c55272b6f27517a0ba4e0e build 0.1-b01 +4425fe0d7f0ec7f8583ae687f7eb1b4a3e94ea09 build 0.1-b02 +0c5e4a085baa8aaa9aed55b20810de4bdc2d6548 build 0.2-b01 +34520cf6c532e28f529340f66f71855aadf83d95 build 0.2-b02 diff -r f5603a6e5042 build.cmd --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/build.cmd Fri Dec 17 13:24:08 2010 +0100 @@ -0,0 +1,12 @@ +set HotSpotMksHome=C:\Cygwin\bin +set path=%JAVA_HOME%;C:\Cygwin\bin +call "%VS_VCVARS%\vsvars32.bat" + +set OrigPath=%cd% +cd make\windows + +call build.bat product compiler1 %OrigPath% %JAVA_HOME% +call build.bat fastdebug compiler1 %OrigPath% %JAVA_HOME% + +cd %OrigPath% +pause diff -r f5603a6e5042 create.cmd --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/create.cmd Fri Dec 17 13:24:08 2010 +0100 @@ -0,0 +1,12 @@ +set HotSpotMksHome=C:\Cygwin\bin +set path=%JAVA_HOME%\bin;C:\Cygwin\bin +call "%VS_VCVARS%\vsvars32.bat" + +set OrigPath=%cd% +cd make\windows + +mkdir %OrigPath%\work +call create.bat %OrigPath% %OrigPath%\work %OrigPath%\java + +cd %OrigPath% +pause diff -r f5603a6e5042 create64.cmd --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/create64.cmd Fri Dec 17 13:24:08 2010 +0100 @@ -0,0 +1,14 @@ +set HotSpotMksHome=C:\cygwin\bin +set JAVA_HOME=%cd%\java64 +set ORIG_PATH=%PATH% +set path=%JAVA_HOME%\bin;%path%;C:\cygwin\bin + +set OrigPath=%cd% +cd make\windows + +mkdir %OrigPath%\work64 +call create.bat %OrigPath% %OrigPath%\work64 %OrigPath%\java64 + +set PATH=%ORIG_PATH% +cd %OrigPath% +pause diff -r f5603a6e5042 create_installers.cmd --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/create_installers.cmd Fri Dec 17 13:24:08 2010 +0100 @@ -0,0 +1,11 @@ +copy hotswapinstaller\Installer\dist\Installer.jar dcevm-mac.jar +jar uvf dcevm-mac.jar -C hotswapbinaries\mac\ . +jar uvf dcevm-mac.jar -C hotswapinstaller\Installer\dist data\dcevm.jar + +copy hotswapinstaller\Installer\dist\Installer.jar dcevm-win.jar +jar uvf dcevm-win.jar -C hotswapbinaries\win\ . +jar uvf dcevm-win.jar -C hotswapinstaller\Installer\dist data\dcevm.jar + +copy hotswapinstaller\Installer\dist\Installer.jar dcevm-linux.jar +jar uvf dcevm-linux.jar -C hotswapbinaries\linux\ . +jar uvf dcevm-linux.jar -C hotswapinstaller\Installer\dist data\dcevm.jar \ No newline at end of file diff -r f5603a6e5042 create_installers.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/create_installers.sh Fri Dec 17 13:24:08 2010 +0100 @@ -0,0 +1,11 @@ +cp hotswapinstaller/Installer/dist/Installer.jar dcevm-mac.jar +jar uvf dcevm-mac.jar -C hotswapbinaries/mac/ . +jar uvf dcevm-mac.jar -C hotswapinstaller/Installer/dist data/dcevm.jar + +cp hotswapinstaller/Installer/dist/Installer.jar dcevm-win.jar +jar uvf dcevm-win.jar -C hotswapbinaries/win/ . +jar uvf dcevm-win.jar -C hotswapinstaller/Installer/dist data/dcevm.jar + +cp hotswapinstaller/Installer/dist/Installer.jar dcevm-linux.jar +jar uvf dcevm-linux.jar -C hotswapbinaries/linux/ . +jar uvf dcevm-linux.jar -C hotswapinstaller/Installer/dist data/dcevm.jar diff -r f5603a6e5042 hotswapbinaries/README --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotswapbinaries/README Fri Dec 17 13:24:08 2010 +0100 @@ -0,0 +1,18 @@ +The binary files are not stored in the mercurial repository, because they would bloat the repository size. The expected files in this subdirectory are: + +========================================== +Windows +win/data/64/bin/server/jvm.dll +win/data/bin/client/jvm.dll +win/data/bin/server/jvm.dll + +========================================== +Mac +mac/data/lib/i386/client/libjvm.dylib +mac/data/lib/i386/server/libjvm.dylib + +========================================== +Linux +linux/data/64/lib/amd64/server/libjvm.so +linux/data/lib/i386/client/libjvm.so +linux/data/lib/i386/server/libjvm.so diff -r f5603a6e5042 make/windows/build_vm_def.sh --- a/make/windows/build_vm_def.sh Wed Nov 17 22:42:08 2010 -0800 +++ b/make/windows/build_vm_def.sh Fri Dec 17 13:24:08 2010 +0100 @@ -51,6 +51,7 @@ CAT="$MKS_HOME/cat.exe" RM="$MKS_HOME/rm.exe" DUMPBIN="link.exe /dump" +export VS_UNICODE_OUTPUT= # When called from IDE the first param should contain the link version, otherwise may be nill if [ "x$1" != "x" ]; then diff -r f5603a6e5042 make/windows/create.bat --- a/make/windows/create.bat Wed Nov 17 22:42:08 2010 -0800 +++ b/make/windows/create.bat Fri Dec 17 13:24:08 2010 +0100 @@ -39,6 +39,8 @@ REM Note: Running this batch file from the Windows command shell requires REM that "grep" be accessible on the PATH. An MKS install does this. REM +cl 2>&1 | grep "x64" >NUL +if %errorlevel% == 0 goto amd64 cl 2>&1 | grep "IA-64" >NUL if %errorlevel% == 0 goto isia64 cl 2>&1 | grep "AMD64" >NUL diff -r f5603a6e5042 src/cpu/x86/vm/interp_masm_x86_32.cpp --- a/src/cpu/x86/vm/interp_masm_x86_32.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/cpu/x86/vm/interp_masm_x86_32.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -1352,7 +1352,7 @@ } // RedefineClasses() tracing support for obsolete method entry - if (RC_TRACE_IN_RANGE(0x00001000, 0x00002000)) { + IF_TRACE_RC4 { get_thread(rcx); get_method(rbx); call_VM_leaf( diff -r f5603a6e5042 src/cpu/x86/vm/interp_masm_x86_64.cpp --- a/src/cpu/x86/vm/interp_masm_x86_64.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/cpu/x86/vm/interp_masm_x86_64.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -1438,7 +1438,7 @@ } // RedefineClasses() tracing support for obsolete method entry - if (RC_TRACE_IN_RANGE(0x00001000, 0x00002000)) { + IF_TRACE_RC4 { get_method(c_rarg1); call_VM_leaf( CAST_FROM_FN_PTR(address, SharedRuntime::rc_trace_method_entry), diff -r f5603a6e5042 src/cpu/x86/vm/sharedRuntime_x86_32.cpp --- a/src/cpu/x86/vm/sharedRuntime_x86_32.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/cpu/x86/vm/sharedRuntime_x86_32.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -1494,7 +1494,7 @@ } // RedefineClasses() tracing support for obsolete method entry - if (RC_TRACE_IN_RANGE(0x00001000, 0x00002000)) { + IF_TRACE_RC4 { __ movoop(rax, JNIHandles::make_local(method())); __ call_VM_leaf( CAST_FROM_FN_PTR(address, SharedRuntime::rc_trace_method_entry), diff -r f5603a6e5042 src/cpu/x86/vm/sharedRuntime_x86_64.cpp --- a/src/cpu/x86/vm/sharedRuntime_x86_64.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/cpu/x86/vm/sharedRuntime_x86_64.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -1490,7 +1490,7 @@ } // RedefineClasses() tracing support for obsolete method entry - if (RC_TRACE_IN_RANGE(0x00001000, 0x00002000)) { + IF_TRACE_RC4 { // protect the args we've loaded save_args(masm, total_c_args, c_arg, out_regs); __ movoop(c_rarg1, JNIHandles::make_local(method())); diff -r f5603a6e5042 src/cpu/x86/vm/templateTable_x86_32.cpp --- a/src/cpu/x86/vm/templateTable_x86_32.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/cpu/x86/vm/templateTable_x86_32.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -2110,6 +2110,22 @@ // resolve first time through address entry; switch (bytecode()) { + case Bytecodes::_fast_agetfield : // fall through + case Bytecodes::_fast_bgetfield : // fall through + case Bytecodes::_fast_cgetfield : // fall through + case Bytecodes::_fast_dgetfield : // fall through + case Bytecodes::_fast_fgetfield : // fall through + case Bytecodes::_fast_igetfield : // fall through + case Bytecodes::_fast_lgetfield : // fall through + case Bytecodes::_fast_sgetfield : // fall through + case Bytecodes::_fast_aputfield : // fall through + case Bytecodes::_fast_bputfield : // fall through + case Bytecodes::_fast_cputfield : // fall through + case Bytecodes::_fast_dputfield : // fall through + case Bytecodes::_fast_fputfield : // fall through + case Bytecodes::_fast_iputfield : // fall through + case Bytecodes::_fast_lputfield : // fall through + case Bytecodes::_fast_sputfield : // fall through case Bytecodes::_getstatic : // fall through case Bytecodes::_putstatic : // fall through case Bytecodes::_getfield : // fall through @@ -2204,6 +2220,7 @@ // Correct values of the cache and index registers are preserved. void TemplateTable::jvmti_post_field_access(Register cache, Register index, + int byte_no, bool is_static, bool has_tos) { if (JvmtiExport::can_post_field_access()) { @@ -2230,7 +2247,11 @@ // cache: cache entry pointer __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::post_field_access), rax, cache); - __ get_cache_and_index_at_bcp(cache, index, 1); + + // (tw) Redefinition might have occured => reresolve the cp entry. + __ restore_bcp(); + resolve_cache_and_index(byte_no, noreg, cache, index, sizeof(u2)); + __ bind(L1); } } @@ -2251,7 +2272,7 @@ const Register flags = rax; resolve_cache_and_index(byte_no, noreg, cache, index, sizeof(u2)); - jvmti_post_field_access(cache, index, is_static, false); + jvmti_post_field_access(cache, index, byte_no, is_static, false); load_field_cp_cache_entry(obj, cache, index, off, flags, is_static); if (!is_static) pop_and_check_object(obj); @@ -2386,7 +2407,7 @@ // The registers cache and index expected to be set before call. // The function may destroy various registers, just not the cache and index registers. -void TemplateTable::jvmti_post_field_mod(Register cache, Register index, bool is_static) { +void TemplateTable::jvmti_post_field_mod(Register cache, Register index, int byte_no, bool is_static) { ByteSize cp_base_offset = constantPoolCacheOopDesc::base_offset(); @@ -2444,7 +2465,11 @@ // rcx: jvalue object on the stack __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::post_field_modification), rbx, rax, rcx); - __ get_cache_and_index_at_bcp(cache, index, 1); + + // (tw) Redefinition might have occured => reresolve the cp entry. + __ restore_bcp(); + resolve_cache_and_index(byte_no, noreg, cache, index, sizeof(u2)); + __ bind(L1); } } @@ -2460,7 +2485,7 @@ const Register flags = rax; resolve_cache_and_index(byte_no, noreg, cache, index, sizeof(u2)); - jvmti_post_field_mod(cache, index, is_static); + jvmti_post_field_mod(cache, index, byte_no, is_static); load_field_cp_cache_entry(obj, cache, index, off, flags, is_static); // Doug Lea believes this is not needed with current Sparcs (TSO) and Intel (PSO). @@ -2683,6 +2708,11 @@ // rax,: cache entry pointer // rcx: jvalue object on the stack __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::post_field_modification), rbx, rax, rcx); + + // (tw) Redefinition might have occured => reresolve the cp entry. + __ restore_bcp(); + resolve_cache_and_index(2, noreg, rax, rcx, sizeof(u2)); + if (bytecode() == Bytecodes::_fast_lputfield) __ pop(rdx); // restore high value __ pop(rax); // restore lower value __ addptr(rsp, sizeof(jvalue)); // release jvalue object space @@ -2803,6 +2833,11 @@ // rcx: cache entry pointer __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::post_field_access), rax, rcx); __ pop_ptr(rax); // restore object pointer + + // (tw) Redefinition might have occured => reresolve the cp entry. + __ restore_bcp(); + resolve_cache_and_index(1, noreg, rax, rcx, sizeof(u2)); + __ bind(L1); } diff -r f5603a6e5042 src/cpu/x86/vm/templateTable_x86_64.cpp --- a/src/cpu/x86/vm/templateTable_x86_64.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/cpu/x86/vm/templateTable_x86_64.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -2113,6 +2113,22 @@ // resolve first time through address entry; switch (bytecode()) { + case Bytecodes::_fast_agetfield : // fall through + case Bytecodes::_fast_bgetfield : // fall through + case Bytecodes::_fast_cgetfield : // fall through + case Bytecodes::_fast_dgetfield : // fall through + case Bytecodes::_fast_fgetfield : // fall through + case Bytecodes::_fast_igetfield : // fall through + case Bytecodes::_fast_lgetfield : // fall through + case Bytecodes::_fast_sgetfield : // fall through + case Bytecodes::_fast_aputfield : // fall through + case Bytecodes::_fast_bputfield : // fall through + case Bytecodes::_fast_cputfield : // fall through + case Bytecodes::_fast_dputfield : // fall through + case Bytecodes::_fast_fputfield : // fall through + case Bytecodes::_fast_iputfield : // fall through + case Bytecodes::_fast_lputfield : // fall through + case Bytecodes::_fast_sputfield : // fall through case Bytecodes::_getstatic: case Bytecodes::_putstatic: case Bytecodes::_getfield: @@ -2219,7 +2235,7 @@ // The registers cache and index expected to be set before call. // Correct values of the cache and index registers are preserved. void TemplateTable::jvmti_post_field_access(Register cache, Register index, - bool is_static, bool has_tos) { + int byte_no, bool is_static, bool has_tos) { // do the JVMTI work here to avoid disturbing the register state below // We use c_rarg registers here because we want to use the register used in // the call to the VM @@ -2250,7 +2266,11 @@ __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::post_field_access), c_rarg1, c_rarg2, c_rarg3); - __ get_cache_and_index_at_bcp(cache, index, 1); + + // (tw) Redefinition might have occured => reresolve the cp entry. + __ restore_bcp(); + resolve_cache_and_index(byte_no, noreg, cache, index, sizeof(u2)); + __ bind(L1); } } @@ -2272,7 +2292,7 @@ const Register bc = c_rarg3; // uses same reg as obj, so don't mix them resolve_cache_and_index(byte_no, noreg, cache, index, sizeof(u2)); - jvmti_post_field_access(cache, index, is_static, false); + jvmti_post_field_access(cache, index, byte_no, is_static, false); load_field_cp_cache_entry(obj, cache, index, off, flags, is_static); if (!is_static) { @@ -2406,7 +2426,7 @@ // The registers cache and index expected to be set before call. // The function may destroy various registers, just not the cache and index registers. -void TemplateTable::jvmti_post_field_mod(Register cache, Register index, bool is_static) { +void TemplateTable::jvmti_post_field_mod(Register cache, Register index, int byte_no, bool is_static) { transition(vtos, vtos); ByteSize cp_base_offset = constantPoolCacheOopDesc::base_offset(); @@ -2459,7 +2479,11 @@ CAST_FROM_FN_PTR(address, InterpreterRuntime::post_field_modification), c_rarg1, c_rarg2, c_rarg3); - __ get_cache_and_index_at_bcp(cache, index, 1); + + // (tw) Redefinition might have occured => reresolve the cp entry. + __ restore_bcp(); + resolve_cache_and_index(byte_no, noreg, cache, index, sizeof(u2)); + __ bind(L1); } } @@ -2475,7 +2499,7 @@ const Register bc = c_rarg3; resolve_cache_and_index(byte_no, noreg, cache, index, sizeof(u2)); - jvmti_post_field_mod(cache, index, is_static); + jvmti_post_field_mod(cache, index, byte_no, is_static); load_field_cp_cache_entry(obj, cache, index, off, flags, is_static); // [jk] not needed currently @@ -2661,6 +2685,11 @@ CAST_FROM_FN_PTR(address, InterpreterRuntime::post_field_modification), rbx, c_rarg2, c_rarg3); + + // (tw) Redefinition might have occured => reresolve the cp entry. + __ restore_bcp(); + resolve_cache_and_index(2, noreg, rax, rcx, sizeof(u2)); + __ pop(rax); // restore lower value __ addptr(rsp, sizeof(jvalue)); // release jvalue object space __ bind(L2); @@ -2762,6 +2791,11 @@ c_rarg1, c_rarg2); __ mov(rax, r12); // restore object pointer __ reinit_heapbase(); + + // (tw) Redefinition might have occured => reresolve the cp entry. + __ restore_bcp(); + resolve_cache_and_index(1, noreg, rax, rcx, sizeof(u2)); + __ bind(L1); } diff -r f5603a6e5042 src/share/tools/MakeDeps/BuildConfig.java --- a/src/share/tools/MakeDeps/BuildConfig.java Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/tools/MakeDeps/BuildConfig.java Fri Dec 17 13:24:08 2010 +0100 @@ -242,7 +242,20 @@ void initDefaultDefines(Vector defines) { Vector sysDefines = new Vector(); - sysDefines.add("WIN32"); + + if( Util.os().equals("Win32")) { + sysDefines.add("WIN32"); + sysDefines.add("HOTSPOT_LIB_ARCH=\\\"i386\\\""); + } else { + sysDefines.add("_AMD64_"); + sysDefines.add("AMD64"); + sysDefines.add("_WIN64"); + sysDefines.add("_LP64"); + if (System.getenv("MSC_VER") != null) + sysDefines.add("MSC_VER=" + System.getenv("MSC_VER")); + sysDefines.add("HOTSPOT_LIB_ARCH=\\\"amd64\\\""); + } + sysDefines.add("_WINDOWS"); sysDefines.add("HOTSPOT_BUILD_USER="+System.getProperty("user.name")); sysDefines.add("HOTSPOT_BUILD_TARGET=\\\""+get("Build")+"\\\""); diff -r f5603a6e5042 src/share/tools/MakeDeps/Util.java --- a/src/share/tools/MakeDeps/Util.java Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/tools/MakeDeps/Util.java Fri Dec 17 13:24:08 2010 +0100 @@ -84,5 +84,26 @@ } static String sep = File.separator; - static String os = "Win32"; //System.getProperty("os.name"); + + private static String _os; + + static String os() { + if( _os==null) { + + for(Map.Entry entry: System.getenv().entrySet()) + if("PLATFORM_ARCH_MODEL".equals(entry.getKey().toUpperCase())) { + String archModel = entry.getValue(); + if("x86_32".equals(archModel)) + _os = "Win32"; + else if("x86_64".equals(archModel)) + _os = "x64"; + else + throw new RuntimeException("Unsupported PLATFORM_ARCH_MODEL " + archModel); + return _os; + } + throw new RuntimeException("PLATFORM_ARCH_MODEL not specified"); + } + return _os; + } + } diff -r f5603a6e5042 src/share/tools/MakeDeps/WinGammaPlatformVC6.java --- a/src/share/tools/MakeDeps/WinGammaPlatformVC6.java Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/tools/MakeDeps/WinGammaPlatformVC6.java Fri Dec 17 13:24:08 2010 +0100 @@ -236,7 +236,7 @@ " /nologo /base:\"0x8000000\" /subsystem:windows /dll" + " /export:JNI_GetDefaultJavaVMInitArgs /export:JNI_CreateJavaVM /export:JNI_GetCreatedJavaVMs "+ " /export:jio_snprintf /export:jio_printf /export:jio_fprintf /export:jio_vfprintf "+ - " /export:jio_vsnprintf "); + " /export:jio_vsnprintf /export:JVM_GetVersionInfo /export:JVM_GetThreadStateNames /export:JVM_GetThreadStateValues /export:JVM_InitAgentProperties /export:JVM_FindClassFromBootLoader"); rv.add("SUBTRACT LINK32 /pdb:none /map"); return rv; @@ -286,6 +286,6 @@ } String makeCfgName(String flavourBuild) { - return "vm - "+ Util.os + " " + flavourBuild; + return "vm - "+ "Win32" + " " + flavourBuild; } } diff -r f5603a6e5042 src/share/tools/MakeDeps/WinGammaPlatformVC7.java --- a/src/share/tools/MakeDeps/WinGammaPlatformVC7.java Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/tools/MakeDeps/WinGammaPlatformVC7.java Fri Dec 17 13:24:08 2010 +0100 @@ -51,7 +51,7 @@ ); startTag("Platforms", null); - tag("Platform", new String[] {"Name", Util.os}); + tag("Platform", new String[] {"Name", Util.os()}); endTag("Platforms"); startTag("Configurations", null); @@ -467,7 +467,7 @@ "PreprocessorDefinitions", "NDEBUG", "MkTypLibCompatible", "TRUE", "SuppressStartupBanner", "TRUE", - "TargetEnvironment", "1", + "TargetEnvironment", Util.os().equals("Win32") ? "1" : "3", "TypeLibraryName", cfg.get("OutputDir") + Util.sep + "vm.tlb", "HeaderFileName", "" } @@ -596,7 +596,7 @@ "/export:JNI_GetCreatedJavaVMs "+ "/export:jio_snprintf /export:jio_printf "+ "/export:jio_fprintf /export:jio_vfprintf "+ - "/export:jio_vsnprintf "); + "/export:jio_vsnprintf /export:JVM_GetVersionInfo /export:JVM_GetThreadStateNames /export:JVM_GetThreadStateValues /export:JVM_InitAgentProperties /export:JVM_FindClassFromBootLoader"); addAttr(rv, "AdditionalDependencies", "Wsock32.lib winmm.lib"); addAttr(rv, "OutputFile", outDll); // Set /INCREMENTAL option. 1 is linkIncrementalNo @@ -609,7 +609,7 @@ addAttr(rv, "BaseAddress", "0x8000000"); addAttr(rv, "ImportLibrary", outDir+Util.sep+"jvm.lib"); // Set /MACHINE option. 1 is machineX86 - addAttr(rv, "TargetMachine", "1"); + addAttr(rv, "TargetMachine", Util.os().equals("Win32") ? "1" : "17"); return rv; } @@ -687,6 +687,6 @@ } String makeCfgName(String flavourBuild) { - return flavourBuild + "|" + Util.os; + return flavourBuild + "|" + Util.os(); } } diff -r f5603a6e5042 src/share/tools/MakeDeps/WinGammaPlatformVC8.java --- a/src/share/tools/MakeDeps/WinGammaPlatformVC8.java Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/tools/MakeDeps/WinGammaPlatformVC8.java Fri Dec 17 13:24:08 2010 +0100 @@ -41,6 +41,9 @@ addAttr(rv, "UsePrecompiledHeader", "2"); // Set /EHsc- option. 0 is cppExceptionHandlingNo addAttr(rv, "ExceptionHandling", "0"); + // Parallel compilation + addAttr(rv, "AdditionalOptions", "/MP"); + return rv; } diff -r f5603a6e5042 src/share/vm/c1/c1_Compilation.hpp --- a/src/share/vm/c1/c1_Compilation.hpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/c1/c1_Compilation.hpp Fri Dec 17 13:24:08 2010 +0100 @@ -232,8 +232,8 @@ #define BAILOUT(msg) { bailout(msg); return; } #define BAILOUT_(msg, res) { bailout(msg); return res; } -#define CHECK_BAILOUT() { if (bailed_out()) return; } -#define CHECK_BAILOUT_(res) { if (bailed_out()) return res; } +#define CHECK_BAILOUT() { if (((CompilerThread *)Thread::current())->should_bailout()) bailout("Aborted externally"); if (bailed_out()) return; } +#define CHECK_BAILOUT_(res) { if (((CompilerThread *)Thread::current())->should_bailout()) bailout("Aborted externally"); if (bailed_out()) return res; } class InstructionMark: public StackObj { diff -r f5603a6e5042 src/share/vm/c1/c1_LIRGenerator.cpp --- a/src/share/vm/c1/c1_LIRGenerator.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/c1/c1_LIRGenerator.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -2498,7 +2498,7 @@ // Load CallSite object from constant pool cache. __ oop2reg(cpcache->constant_encoding(), tmp); - __ load(new LIR_Address(tmp, call_site_offset, T_OBJECT), tmp); + __ load(new LIR_Address(tmp, (int)call_site_offset, T_OBJECT), tmp); // Load target MethodHandle from CallSite object. __ load(new LIR_Address(tmp, java_dyn_CallSite::target_offset_in_bytes(), T_OBJECT), receiver); diff -r f5603a6e5042 src/share/vm/ci/ciEnv.cpp --- a/src/share/vm/ci/ciEnv.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/ci/ciEnv.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -1075,3 +1075,11 @@ // If memory is low, we stop compiling methods. record_method_not_compilable("out of memory"); } + +// (tw) Called after class redefinition to clean up possibly invalidated state. +void ciEnv::cleanup_after_redefinition() { + + if (_factory != NULL) { + _factory->cleanup_after_redefinition(); + } +} \ No newline at end of file diff -r f5603a6e5042 src/share/vm/ci/ciEnv.hpp --- a/src/share/vm/ci/ciEnv.hpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/ci/ciEnv.hpp Fri Dec 17 13:24:08 2010 +0100 @@ -383,4 +383,6 @@ void record_failure(const char* reason); void record_method_not_compilable(const char* reason, bool all_tiers = true); void record_out_of_memory_failure(); + + void cleanup_after_redefinition(); }; diff -r f5603a6e5042 src/share/vm/ci/ciObjectFactory.cpp --- a/src/share/vm/ci/ciObjectFactory.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/ci/ciObjectFactory.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -255,6 +255,11 @@ // into the table. We need to recompute our index. index = find(keyHandle(), _ci_objects); } + + if (is_found_at(index, keyHandle(), _ci_objects)) { + // (tw) Check if this is an error? Can occur when redefining classes. + return _ci_objects->at(index); + } assert(!is_found_at(index, keyHandle(), _ci_objects), "no double insert"); insert(index, new_object, _ci_objects); return new_object; @@ -711,3 +716,50 @@ _unloaded_instances->length(), _unloaded_klasses->length()); } + +// (tw) Resoring the ciObject arrays after class redefinition +void ciObjectFactory::sort_ci_objects(GrowableArray* objects) { + + // Resort the _ci_objects array. The order of two class pointers can be changed during class redefinition. + oop last = NULL; + for (int j = 0; j< objects->length(); j++) { + oop o = objects->at(j)->get_oop(); + if (last >= o) { + int cur_last_index = j - 1; + oop cur_last = last; + while (cur_last >= o) { + + // Swap the two objects to guarantee ordering + ciObject *tmp = objects->at(cur_last_index); + objects->at_put(cur_last_index, objects->at(cur_last_index + 1)); + objects->at_put(cur_last_index + 1, tmp); + + // Decrement index to move one step to the left + cur_last_index--; + if (cur_last_index < 0) { + break; + } + cur_last =objects->at(cur_last_index)->get_oop(); + } + } else { + assert(last < o, "out of order"); + last = o; + } + } + +#ifdef ASSERT + if (CIObjectFactoryVerify) { + oop last = NULL; + for (int j = 0; j< objects->length(); j++) { + oop o = objects->at(j)->get_oop(); + assert(last < o, "out of order"); + last = o; + } + } +#endif // ASSERT +} + +// (tw) Called after class redefinition to clean up possibly invalidated state. +void ciObjectFactory::cleanup_after_redefinition() { + sort_ci_objects(_ci_objects); +} \ No newline at end of file diff -r f5603a6e5042 src/share/vm/ci/ciObjectFactory.hpp --- a/src/share/vm/ci/ciObjectFactory.hpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/ci/ciObjectFactory.hpp Fri Dec 17 13:24:08 2010 +0100 @@ -29,6 +29,10 @@ // which ensures that for each oop, at most one ciObject is created. // This invariant allows efficient implementation of ciObject. class ciObjectFactory : public ResourceObj { + + friend class ciEnv; + friend class CompileBroker; + private: static volatile bool _initialized; static GrowableArray* _shared_ci_objects; @@ -122,4 +126,9 @@ void print_contents(); void print(); + +private: + + static void sort_ci_objects(GrowableArray* objects); + void cleanup_after_redefinition(); }; diff -r f5603a6e5042 src/share/vm/classfile/classFileParser.cpp --- a/src/share/vm/classfile/classFileParser.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/classfile/classFileParser.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -833,6 +833,7 @@ Handle class_loader, Handle protection_domain, symbolHandle class_name, + KlassHandle old_klass, TRAPS) { ClassFileStream* cfs = stream(); assert(length > 0, "only called for length>0"); @@ -851,6 +852,9 @@ interface_index, CHECK_(nullHandle)); if (cp->tag_at(interface_index).is_klass()) { interf = KlassHandle(THREAD, cp->resolved_klass_at(interface_index)); + if (!old_klass.is_null() && !interf->is_newest_version()) { + interf = KlassHandle(THREAD, interf->newest_version()); + } } else { symbolHandle unresolved_klass (THREAD, cp->klass_name_at(interface_index)); @@ -863,6 +867,9 @@ klassOop k = SystemDictionary::resolve_super_or_fail(class_name, unresolved_klass, class_loader, protection_domain, false, CHECK_(nullHandle)); + if (!old_klass.is_null()) { + k = k->klass_part()->newest_version(); + } interf = KlassHandle(THREAD, k); if (LinkWellKnownClasses) // my super type is well known to me @@ -1805,7 +1812,6 @@ // Parse and compress line number table parse_linenumber_table(code_attribute_length, code_length, &linenumber_table, CHECK_(nullHandle)); - } else if (LoadLocalVariableTables && cp->symbol_at(code_attribute_name_index) == vmSymbols::tag_local_variable_table()) { // Parse local variable table @@ -2549,7 +2555,7 @@ } -static void initialize_static_field(fieldDescriptor* fd, TRAPS) { +void ClassFileParser::initialize_static_field(fieldDescriptor* fd, TRAPS) { KlassHandle h_k (THREAD, fd->field_holder()); assert(h_k.not_null() && fd->is_static(), "just checking"); if (fd->has_initial_value()) { @@ -2746,6 +2752,123 @@ (*next_nonstatic_oop_offset_ptr) += (extra * heapOopSize); } +// (tw) Finds the super symbols by reading the bytes of the class and returns +// them in a growable array. +void ClassFileParser::findSuperSymbols(symbolHandle name, + Handle class_loader, + Handle protection_domain, + KlassHandle old_klass, + GrowableArray &handles, + TRAPS) { + + _cp_patches = NULL; + // So that JVMTI can cache class file in the state before retransformable agents + // have modified it + unsigned char *cached_class_file_bytes = NULL; + + ClassFileStream* cfs = stream(); + + _has_finalizer = _has_empty_finalizer = _has_vanilla_constructor = false; + + instanceKlassHandle nullHandle; + + // Save the class file name for easier error message printing. + _class_name = name.not_null()? name : vmSymbolHandles::unknown_class_name(); + + cfs->guarantee_more(8, CHECK); // magic, major, minor + // Magic value + u4 magic = cfs->get_u4_fast(); + if (magic != JAVA_CLASSFILE_MAGIC) { + // Invalid class file! + return; + } + + // Version numbers + u2 minor_version = cfs->get_u2_fast(); + u2 major_version = cfs->get_u2_fast(); + + // Check version numbers - we check this even with verifier off + if (!is_supported_version(major_version, minor_version)) { + + // Unsupported version! + return; + } + + _major_version = major_version; + _minor_version = minor_version; + + + // Check if verification needs to be relaxed for this class file + // Do not restrict it to jdk1.0 or jdk1.1 to maintain backward compatibility (4982376) + _relax_verify = Verifier::relax_verify_for(class_loader()); + _need_verify = false; + + // Constant pool + constantPoolHandle cp = parse_constant_pool(CHECK); + int cp_size = cp->length(); + + cfs->guarantee_more(8, CHECK); // flags, this_class, super_class, infs_len + + // Access flags + AccessFlags access_flags; + jint flags = cfs->get_u2_fast() & JVM_RECOGNIZED_CLASS_MODIFIERS; + + if ((flags & JVM_ACC_INTERFACE) && _major_version < JAVA_6_VERSION) { + // Set abstract bit for old class files for backward compatibility + flags |= JVM_ACC_ABSTRACT; + } + access_flags.set_flags(flags); + + // This class and superclass + instanceKlassHandle super_klass; + u2 this_class_index = cfs->get_u2_fast(); + check_property( + valid_cp_range(this_class_index, cp_size) && + cp->tag_at(this_class_index).is_unresolved_klass(), + "Invalid this class index %u in constant pool in class file %s", + this_class_index, CHECK); + + symbolHandle class_name (THREAD, cp->unresolved_klass_at(this_class_index)); + assert(class_name.not_null(), "class_name can't be null"); + + // Update _class_name which could be null previously to be class_name + _class_name = class_name; + + // (tw) DO NOT release all handles when parsing is done + {// HandleMark hm(THREAD); + + // Checks if name in class file matches requested name + if (name.not_null() && class_name() != name()) { + return; + } + + u2 super_class_index = cfs->get_u2_fast(); + + if (super_class_index != 0) { + symbolHandle super_class (THREAD, cp->klass_name_at(super_class_index)); + handles.append(super_class); + } else { + // (tw) This redefinition must be for the Object class. + } + + // Interfaces + u2 itfs_len = cfs->get_u2_fast(); + objArrayHandle local_interfaces; + if (itfs_len == 0) { + local_interfaces = objArrayHandle(THREAD, Universe::the_empty_system_obj_array()); + } else { + local_interfaces = parse_interfaces(cp, itfs_len, class_loader, protection_domain, _class_name, old_klass, CHECK); + } + + for (int i=0; ilength(); i++) { + oop o = local_interfaces->obj_at(i); + symbolHandle interface_handle (THREAD, ((klassOop)o)->klass_part()->name()); + handles.append(interface_handle); + } + } +} + + // Force MethodHandle.vmentry to be an unmanaged pointer. // There is no way for a classfile to express this, so we must help it. @@ -2819,6 +2942,7 @@ instanceKlassHandle ClassFileParser::parseClassFile(symbolHandle name, Handle class_loader, Handle protection_domain, + KlassHandle old_klass, KlassHandle host_klass, GrowableArray* cp_patches, symbolHandle& parsed_name, @@ -2847,10 +2971,13 @@ unsigned char* ptr = cfs->buffer(); unsigned char* end_ptr = cfs->buffer() + cfs->length(); + bool pretend_new_universe = Thread::current()->pretend_new_universe(); + Thread::current()->set_pretend_new_universe(false); JvmtiExport::post_class_file_load_hook(name, class_loader, protection_domain, &ptr, &end_ptr, &cached_class_file_bytes, &cached_class_file_length); + Thread::current()->set_pretend_new_universe(pretend_new_universe); if (ptr != cfs->buffer()) { // JVMTI agent has modified class file data. @@ -3001,7 +3128,11 @@ // However, make sure it is not an array type. bool is_array = false; if (cp->tag_at(super_class_index).is_klass()) { - super_klass = instanceKlassHandle(THREAD, cp->resolved_klass_at(super_class_index)); + klassOop resolved_klass = cp->resolved_klass_at(super_class_index); + if (!old_klass.is_null()) { + resolved_klass = resolved_klass->klass_part()->newest_version(); + } + super_klass = instanceKlassHandle(THREAD, resolved_klass); if (_need_verify) is_array = super_klass->oop_is_array(); } else if (_need_verify) { @@ -3019,7 +3150,7 @@ if (itfs_len == 0) { local_interfaces = objArrayHandle(THREAD, Universe::the_empty_system_obj_array()); } else { - local_interfaces = parse_interfaces(cp, itfs_len, class_loader, protection_domain, _class_name, CHECK_(nullHandle)); + local_interfaces = parse_interfaces(cp, itfs_len, class_loader, protection_domain, _class_name, old_klass, CHECK_(nullHandle)); } // Fields (offsets are filled in later) @@ -3063,7 +3194,9 @@ protection_domain, true, CHECK_(nullHandle)); - + if (!old_klass.is_null()) { + k = k->klass_part()->newest_version(); + } KlassHandle kh (THREAD, k); super_klass = instanceKlassHandle(THREAD, kh()); if (LinkWellKnownClasses) // my super class is well known to me @@ -3490,6 +3623,19 @@ rt = REF_NONE; } else { rt = super_klass->reference_type(); + + // (tw) With class redefinition, it can also happen that special classes are loaded. + if (name() == vmSymbols::java_lang_ref_Reference()) { + rt = REF_OTHER; + } else if (name() == vmSymbols::java_lang_ref_SoftReference()) { + rt = REF_SOFT; + } else if (name() == vmSymbols::java_lang_ref_WeakReference()) { + rt = REF_WEAK; + } else if (name() == vmSymbols::java_lang_ref_FinalReference()) { + rt = REF_FINAL; + } else if (name() == vmSymbols::java_lang_ref_PhantomReference()) { + rt = REF_PHANTOM; + } } // We can now create the basic klassOop for this klass @@ -3591,7 +3737,7 @@ // Do final class setup fill_oop_maps(this_klass, nonstatic_oop_map_count, nonstatic_oop_offsets, nonstatic_oop_counts); - set_precomputed_flags(this_klass); + set_precomputed_flags(this_klass, old_klass); // reinitialize modifiers, using the InnerClasses attribute int computed_modifiers = this_klass->compute_modifier_flags(CHECK_(nullHandle)); @@ -3611,6 +3757,10 @@ check_illegal_static_method(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 */); @@ -3753,7 +3903,7 @@ } -void ClassFileParser::set_precomputed_flags(instanceKlassHandle k) { +void ClassFileParser::set_precomputed_flags(instanceKlassHandle k, KlassHandle old_klass) { klassOop super = k->super(); // Check if this klass has an empty finalize method (i.e. one with return bytecode only), @@ -3761,7 +3911,9 @@ if (!_has_empty_finalizer) { if (_has_finalizer || (super != NULL && super->klass_part()->has_finalizer())) { - k->set_has_finalizer(); + if (old_klass.is_null() || old_klass->has_finalizer()) { + k->set_has_finalizer(); + } } } @@ -3777,7 +3929,7 @@ // Check if this klass supports the java.lang.Cloneable interface if (SystemDictionary::Cloneable_klass_loaded()) { - if (k->is_subtype_of(SystemDictionary::Cloneable_klass())) { + if (k->is_subtype_of(SystemDictionary::Cloneable_klass()) || k->is_subtype_of(SystemDictionary::Cloneable_klass()->klass_part()->newest_version())) { k->set_is_cloneable(); } } diff -r f5603a6e5042 src/share/vm/classfile/classFileParser.hpp --- a/src/share/vm/classfile/classFileParser.hpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/classfile/classFileParser.hpp Fri Dec 17 13:24:08 2010 +0100 @@ -65,6 +65,7 @@ Handle class_loader, Handle protection_domain, symbolHandle class_name, + KlassHandle old_klass, TRAPS); // Field parsing @@ -135,7 +136,7 @@ unsigned int nonstatic_oop_map_count, int* nonstatic_oop_offsets, unsigned int* nonstatic_oop_counts); - void set_precomputed_flags(instanceKlassHandle k); + void set_precomputed_flags(instanceKlassHandle k, KlassHandle old_klass); objArrayHandle compute_transitive_interfaces(instanceKlassHandle super, objArrayHandle local_ifs, TRAPS); @@ -265,21 +266,33 @@ instanceKlassHandle parseClassFile(symbolHandle name, Handle class_loader, Handle protection_domain, + KlassHandle old_klass, symbolHandle& parsed_name, bool verify, TRAPS) { KlassHandle no_host_klass; - return parseClassFile(name, class_loader, protection_domain, no_host_klass, NULL, parsed_name, verify, THREAD); + return parseClassFile(name, class_loader, protection_domain, old_klass, no_host_klass, NULL, parsed_name, verify, THREAD); } instanceKlassHandle parseClassFile(symbolHandle name, Handle class_loader, Handle protection_domain, + KlassHandle old_klass, KlassHandle host_klass, GrowableArray* cp_patches, symbolHandle& parsed_name, bool verify, TRAPS); + static void initialize_static_field(fieldDescriptor* fd, TRAPS); + + // (tw) Creates symbol handles for the super class and the interfaces + void findSuperSymbols(symbolHandle name, + Handle class_loader, + Handle protection_domain, + KlassHandle old_klass, + GrowableArray &handles, + TRAPS); + // Verifier checks static void check_super_class_access(instanceKlassHandle this_klass, TRAPS); static void check_super_interface_access(instanceKlassHandle this_klass, TRAPS); diff -r f5603a6e5042 src/share/vm/classfile/classLoader.cpp --- a/src/share/vm/classfile/classLoader.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/classfile/classLoader.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -164,7 +164,7 @@ if (file_handle != -1) { // read contents into resource array u1* buffer = NEW_RESOURCE_ARRAY(u1, st.st_size); - size_t num_read = os::read(file_handle, (char*) buffer, st.st_size); + size_t num_read = hpi::read(file_handle, (char*) buffer, st.st_size); // close file hpi::close(file_handle); // construct ClassFileStream @@ -872,6 +872,7 @@ instanceKlassHandle result = parser.parseClassFile(h_name, class_loader, protection_domain, + KlassHandle(), parsed_name, false, CHECK_(h)); diff -r f5603a6e5042 src/share/vm/classfile/dictionary.cpp --- a/src/share/vm/classfile/dictionary.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/classfile/dictionary.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -149,8 +149,8 @@ int live_count = 0; // RC_TRACE macro has an embedded ResourceMark - RC_TRACE(0x00000200, ("unload: %s: previous version length=%d", - ik->external_name(), ik->previous_versions()->length())); + TRACE_RC4("unload: %s: previous version length=%d", + ik->external_name(), ik->previous_versions()->length()); for (int i = ik->previous_versions()->length() - 1; i >= 0; i--) { // check the previous versions array for GC'ed weak refs @@ -175,7 +175,7 @@ gc_count++; continue; } else { - RC_TRACE(0x00000200, ("unload: previous version @%d is alive", i)); + TRACE_RC4("unload: previous version @%d is alive", i); if (is_alive->do_object_b(pvcp)) { live_count++; } else { @@ -185,8 +185,7 @@ GrowableArray* method_refs = pv_node->prev_EMCP_methods(); if (method_refs != NULL) { - RC_TRACE(0x00000200, ("unload: previous methods length=%d", - method_refs->length())); + TRACE_RC4("unload: previous methods length=%d", method_refs->length()); for (int j = method_refs->length() - 1; j >= 0; j--) { jweak method_ref = method_refs->at(j); assert(method_ref != NULL, "weak method ref was unexpectedly cleared"); @@ -203,19 +202,14 @@ JNIHandles::destroy_weak_global(method_ref); method_refs->remove_at(j); } else { - // RC_TRACE macro has an embedded ResourceMark - RC_TRACE(0x00000200, - ("unload: %s(%s): prev method @%d in version @%d is alive", - method->name()->as_C_string(), - method->signature()->as_C_string(), j, i)); + TRACE_RC4("unload: %s(%s): prev method @%d in version @%d is alive", method->name()->as_C_string(), + method->signature()->as_C_string(), j, i); } } } } assert(ik->previous_versions()->length() == live_count, "sanity check"); - RC_TRACE(0x00000200, - ("unload: previous version stats: live=%d, GC'ed=%d", live_count, - gc_count)); + TRACE_RC4("unload: previous version stats: live=%d, GC'ed=%d", live_count, gc_count); } // Non-unloadable classes were handled in always_strong_oops_do @@ -321,6 +315,21 @@ } } + +// (tw) Just the classes from defining class loaders +void Dictionary::classes_do(ObjectClosure *closure) { + for (int index = 0; index < table_size(); index++) { + for (DictionaryEntry* probe = bucket(index); + probe != NULL; + probe = probe->next()) { + klassOop k = probe->klass(); + if (probe->loader() == instanceKlass::cast(k)->class_loader()) { + closure->do_object(k); + } + } + } +} + // Added for initialize_itable_for_klass to handle exceptions // Just the classes from defining class loaders void Dictionary::classes_do(void f(klassOop, TRAPS), TRAPS) { @@ -428,6 +437,33 @@ add_entry(index, entry); } +// (tw) Updates the klass entry to point to the new klassOop. Necessary only for class redefinition. +bool Dictionary::update_klass(int index, unsigned int hash, symbolHandle name, Handle loader, KlassHandle k, KlassHandle old_class) { + + // There are several entries for the same class in the dictionary: One extra entry for each parent classloader of the classloader of the class. + bool found = false; + for (int index = 0; index < table_size(); index++) { + for (DictionaryEntry* entry = bucket(index); entry != NULL; entry = entry->next()) { + if (entry->klass() == old_class()) { + entry->set_literal(k()); + found = true; + } + } + } + + return found; +} + +// (tw) Undo previous updates to the system dictionary +void Dictionary::rollback_redefinition() { + for (int index = 0; index < table_size(); index++) { + for (DictionaryEntry* entry = bucket(index); entry != NULL; entry = entry->next()) { + if (entry->klass()->klass_part()->is_redefining()) { + entry->set_literal(entry->klass()->klass_part()->old_version()); + } + } + } +} // This routine does not lock the system dictionary. // @@ -455,12 +491,21 @@ return NULL; } +klassOop Dictionary::intercept_for_version(klassOop k) { + if (k == NULL) return k; + + if (k->klass_part()->is_redefining() && !Thread::current()->pretend_new_universe()) { + return k->klass_part()->old_version(); + } + + return k; +} klassOop Dictionary::find(int index, unsigned int hash, symbolHandle name, Handle loader, Handle protection_domain, TRAPS) { DictionaryEntry* entry = get_entry(index, hash, name, loader); if (entry != NULL && entry->is_valid_protection_domain(protection_domain)) { - return entry->klass(); + return intercept_for_version(entry->klass()); } else { return NULL; } @@ -473,7 +518,7 @@ assert (index == index_for(name, loader), "incorrect index?"); DictionaryEntry* entry = get_entry(index, hash, name, loader); - return (entry != NULL) ? entry->klass() : (klassOop)NULL; + return intercept_for_version((entry != NULL) ? entry->klass() : (klassOop)NULL); } @@ -485,7 +530,7 @@ assert (index == index_for(name, Handle()), "incorrect index?"); DictionaryEntry* entry = get_entry(index, hash, name, Handle()); - return (entry != NULL) ? entry->klass() : (klassOop)NULL; + return intercept_for_version((entry != NULL) ? entry->klass() : (klassOop)NULL); } diff -r f5603a6e5042 src/share/vm/classfile/dictionary.hpp --- a/src/share/vm/classfile/dictionary.hpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/classfile/dictionary.hpp Fri Dec 17 13:24:08 2010 +0100 @@ -65,6 +65,10 @@ void add_klass(symbolHandle class_name, Handle class_loader,KlassHandle obj); + bool update_klass(int index, unsigned int hash, symbolHandle name, Handle loader, KlassHandle k, KlassHandle old_class); + + void rollback_redefinition(); + klassOop find_class(int index, unsigned int hash, symbolHandle name, Handle loader); @@ -81,6 +85,7 @@ void classes_do(void f(klassOop, TRAPS), TRAPS); void classes_do(void f(klassOop, oop)); void classes_do(void f(klassOop, oop, TRAPS), TRAPS); + void classes_do(ObjectClosure *closure); void methods_do(void f(methodOop)); @@ -97,6 +102,7 @@ bool do_unloading(BoolObjectClosure* is_alive); // Protection domains + static klassOop intercept_for_version(klassOop k); klassOop find(int index, unsigned int hash, symbolHandle name, Handle loader, Handle protection_domain, TRAPS); bool is_valid_protection_domain(int index, unsigned int hash, diff -r f5603a6e5042 src/share/vm/classfile/javaClasses.cpp --- a/src/share/vm/classfile/javaClasses.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/classfile/javaClasses.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -1537,7 +1537,7 @@ klassOop klass = SystemDictionary::reflect_Method_klass(); // This class is eagerly initialized during VM initialization, since we keep a refence // to one of the methods - assert(instanceKlass::cast(klass)->is_initialized(), "must be initialized"); + assert(instanceKlass::cast(klass)->is_initialized() || klass->klass_part()->old_version() != NULL, "must be initialized"); return instanceKlass::cast(klass)->allocate_instance_handle(CHECK_NH); } diff -r f5603a6e5042 src/share/vm/classfile/javaClasses.hpp --- a/src/share/vm/classfile/javaClasses.hpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/classfile/javaClasses.hpp Fri Dec 17 13:24:08 2010 +0100 @@ -124,6 +124,7 @@ class java_lang_Class : AllStatic { friend class VMStructs; + friend class VM_RedefineClasses; private: // The fake offsets are added by the class loader when java.lang.Class is loaded diff -r f5603a6e5042 src/share/vm/classfile/loaderConstraints.cpp --- a/src/share/vm/classfile/loaderConstraints.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/classfile/loaderConstraints.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -449,7 +449,7 @@ if (k != NULL) { // We found the class in the system dictionary, so we should // make sure that the klassOop matches what we already have. - guarantee(k == probe->klass(), "klass should be in dictionary"); + guarantee(k == probe->klass()->klass_part()->newest_version(), "klass should be in dictionary"); } else { // If we don't find the class in the system dictionary, it // has to be in the placeholders table. diff -r f5603a6e5042 src/share/vm/classfile/loaderConstraints.hpp --- a/src/share/vm/classfile/loaderConstraints.hpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/classfile/loaderConstraints.hpp Fri Dec 17 13:24:08 2010 +0100 @@ -99,7 +99,7 @@ klassOop klass() { return (klassOop)literal(); } klassOop* klass_addr() { return (klassOop*)literal_addr(); } - void set_klass(klassOop k) { set_literal(k); } + void set_klass(klassOop k) { set_literal(k); assert(k == NULL || !k->klass_part()->is_redefining(), "just checking"); } LoaderConstraintEntry* next() { return (LoaderConstraintEntry*)HashtableEntry::next(); diff -r f5603a6e5042 src/share/vm/classfile/systemDictionary.cpp --- a/src/share/vm/classfile/systemDictionary.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/classfile/systemDictionary.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -119,6 +119,7 @@ // can return a null klass klass = handle_resolution_exception(class_name, class_loader, protection_domain, throw_error, k_h, THREAD); } + assert(klass == NULL || klass->klass_part()->is_newest_version() || klass->klass_part()->newest_version()->klass_part()->is_redefining(), "must be"); return klass; } @@ -161,7 +162,8 @@ // Forwards to resolve_instance_class_or_null klassOop SystemDictionary::resolve_or_null(symbolHandle class_name, Handle class_loader, Handle protection_domain, TRAPS) { - assert(!THREAD->is_Compiler_thread(), "Can not load classes with the Compiler thread"); + // (tw) Check if this relaxing of the condition is correct? Test case hs203t004 failing otherwise. + assert(!THREAD->is_Compiler_thread() || JvmtiThreadState::state_for(JavaThread::current())->get_class_being_redefined() != NULL, "Can not load classes with the Compiler thread"); if (FieldType::is_array(class_name())) { return resolve_array_class_or_null(class_name, class_loader, protection_domain, CHECK_NULL); } else { @@ -976,6 +978,7 @@ instanceKlassHandle k = ClassFileParser(st).parseClassFile(class_name, class_loader, protection_domain, + KlassHandle(), host_klass, cp_patches, parsed_name, @@ -1036,8 +1039,15 @@ Handle protection_domain, ClassFileStream* st, bool verify, + KlassHandle old_class, TRAPS) { + bool redefine_classes_locked = false; + if (!Thread::current()->redefine_classes_mutex()->owned_by_self()) { + Thread::current()->redefine_classes_mutex()->lock(); + redefine_classes_locked = true; + } + // Classloaders that support parallelism, e.g. bootstrap classloader, // or all classloaders with UnsyncloadClass do not acquire lock here bool DoObjectLock = true; @@ -1065,9 +1075,14 @@ instanceKlassHandle k = ClassFileParser(st).parseClassFile(class_name, class_loader, protection_domain, + old_class, parsed_name, verify, THREAD); + if (!old_class.is_null() && !k.is_null()) { + k->set_redefining(true); + k->set_old_version(old_class()); + } const char* pkg = "java/"; if (!HAS_PENDING_EXCEPTION && @@ -1103,13 +1118,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 - if (is_parallelCapable(class_loader)) { + // (tw) TODO: for class redefinition the parallel version does not work, check if this is a problem? + if (is_parallelCapable(class_loader) && old_class.is_null()) { k = find_or_define_instance_class(class_name, class_loader, k, THREAD); } else { - define_instance_class(k, THREAD); + define_instance_class(k, old_class, THREAD); } } + 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 @@ -1138,7 +1158,7 @@ MutexLocker mu(SystemDictionary_lock, THREAD); oop check = find_class_or_placeholder(parsed_name, class_loader); - assert(check == k(), "should be present in the dictionary"); + assert((check == k() && !k->is_redefining()) || (k->is_redefining() && check == k->old_version()), "should be present in the dictionary"); oop check2 = find_class_or_placeholder(h_name, h_loader); assert(check == check2, "name inconsistancy in SystemDictionary"); @@ -1424,7 +1444,11 @@ } } -void SystemDictionary::define_instance_class(instanceKlassHandle k, TRAPS) { +void SystemDictionary::rollback_redefinition() { + dictionary()->rollback_redefinition(); +} + +void SystemDictionary::define_instance_class(instanceKlassHandle k, KlassHandle old_class, TRAPS) { Handle class_loader_h(THREAD, k->class_loader()); @@ -1451,13 +1475,23 @@ symbolHandle name_h(THREAD, k->name()); unsigned int d_hash = dictionary()->compute_hash(name_h, class_loader_h); int d_index = dictionary()->hash_to_index(d_hash); - check_constraints(d_index, d_hash, k, class_loader_h, true, CHECK); + + // (tw) Update version of the klassOop in the system dictionary + // TODO: Check for thread safety! + if (!old_class.is_null()) { + bool ok = dictionary()->update_klass(d_index, d_hash, name_h, class_loader_h, k, old_class); + assert (ok, "must have found old class and updated!"); + } + check_constraints(d_index, d_hash, k, class_loader_h, old_class.is_null(), CHECK); + + if(!old_class.is_null() && TraceRedefineClasses >= 3){ tty->print_cr("Class has been updated!"); } // Register class just loaded with class loader (placed in Vector) // Note we do this before updating the dictionary, as this can // fail with an OutOfMemoryError (if it does, we will *not* put this // class in the dictionary and will not update the class hierarchy). - if (k->class_loader() != NULL) { + // (tw) Only register if not redefining a class. + if (k->class_loader() != NULL && old_class.is_null()) { methodHandle m(THREAD, Universe::loader_addClass_method()); JavaValue result(T_VOID); JavaCallArguments args(class_loader_h); @@ -1483,8 +1517,9 @@ } k->eager_initialize(THREAD); + // (tw) Only notify jvmti if not redefining a class. // notify jvmti - if (JvmtiExport::should_post_class_load()) { + if (JvmtiExport::should_post_class_load() && old_class.is_null()) { assert(THREAD->is_Java_thread(), "thread->is_Java_thread()"); JvmtiExport::post_class_load((JavaThread *) THREAD, k()); @@ -1558,7 +1593,7 @@ } } - define_instance_class(k, THREAD); + define_instance_class(k, KlassHandle(), THREAD); Handle linkage_exception = Handle(); // null handle @@ -1698,6 +1733,14 @@ Universe::flush_dependents_on(k); } +// (tw) Remove from hierarchy - Undo add_to_hierarchy. +void SystemDictionary::remove_from_hierarchy(instanceKlassHandle k) { + assert(k.not_null(), "just checking"); + + k->remove_from_sibling_list(); + + // TODO: Remove from interfaces. +} // ---------------------------------------------------------------------------- // GC support @@ -1778,10 +1821,8 @@ } -void SystemDictionary::preloaded_oops_do(OopClosure* f) { - f->do_oop((oop*) &wk_klass_name_limits[0]); - f->do_oop((oop*) &wk_klass_name_limits[1]); - +// (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]); } @@ -1795,6 +1836,26 @@ } } + // TODO: Check if we need to call FilterFieldsMap +} + +void SystemDictionary::preloaded_oops_do(OopClosure* f) { + f->do_oop((oop*) &wk_klass_name_limits[0]); + f->do_oop((oop*) &wk_klass_name_limits[1]); + + for (int k = (int)FIRST_WKID; k < (int)WKID_LIMIT; k++) { + f->do_oop((oop*) &_well_known_klasses[k]); + } + + { + for (int i = 0; i < T_VOID+1; i++) { + if (_box_klasses[i] != NULL) { + assert(i >= T_BOOLEAN, "checking"); + f->do_oop((oop*) &_box_klasses[i]); + } + } + } + // 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. @@ -1813,6 +1874,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 @@ -1962,7 +2028,9 @@ // Preload ref klasses and set reference types instanceKlass::cast(WK_KLASS(Reference_klass))->set_reference_type(REF_OTHER); - instanceRefKlass::update_nonstatic_oop_maps(WK_KLASS(Reference_klass)); + + // (tw) This is now done in parseClassFile in order to support class redefinition + // instanceRefKlass::update_nonstatic_oop_maps(WK_KLASS(Reference_klass)); initialize_wk_klasses_through(WK_KLASS_ENUM_NAME(PhantomReference_klass), scan, CHECK); instanceKlass::cast(WK_KLASS(SoftReference_klass))->set_reference_type(REF_SOFT); @@ -2064,7 +2132,11 @@ // also holds array classes assert(check->klass_part()->oop_is_instance(), "noninstance in systemdictionary"); - if ((defining == true) || (k() != check)) { + if ((defining == true) && ((k() != check) && k->old_version() != check)) { + ResourceMark rm(Thread::current()); + tty->print_cr("(%d / %d) (%s/%s)", k->revision_number(), check->klass_part()->revision_number(), k->name()->as_C_string(), check->klass_part()->name()->as_C_string()); + k()->print(); + check->print(); linkage_error = "loader (instance of %s): attempted duplicate class " "definition for name: \"%s\""; } else { @@ -2718,8 +2790,9 @@ if (probe == NULL) { probe = SystemDictionary::find_shared_class(class_name); } + // (tw) Relaxed assertion to allow different class versions. Also allow redefining classes lie around (because of rollback). guarantee(probe != NULL && - (!probe->is_klass() || probe == obj()), + (!probe->is_klass() || (!((klassOop)(obj()))->klass_part()->is_redefining()) || ((klassOop)probe)->klass_part()->is_same_or_older_version((klassOop)(obj()))) || ((klassOop)(obj()))->klass_part()->is_redefining(), "Loaded klasses should be in SystemDictionary"); } diff -r f5603a6e5042 src/share/vm/classfile/systemDictionary.hpp --- a/src/share/vm/classfile/systemDictionary.hpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/classfile/systemDictionary.hpp Fri Dec 17 13:24:08 2010 +0100 @@ -260,7 +260,7 @@ // Resolve from stream (called by jni_DefineClass and JVM_DefineClass) static klassOop resolve_from_stream(symbolHandle class_name, Handle class_loader, Handle protection_domain, - ClassFileStream* st, bool verify, TRAPS); + ClassFileStream* st, bool verify, KlassHandle old_class, TRAPS); // Lookup an already loaded class. If not found NULL is returned. static klassOop find(symbolHandle class_name, Handle class_loader, Handle protection_domain, TRAPS); @@ -304,6 +304,8 @@ // Iterate over all klasses in dictionary // Just the classes from defining class loaders static void classes_do(void f(klassOop)); + static void classes_do(ObjectClosure *closure); + static void preloaded_classes_do(OopClosure *closure); // Added for initialize_itable_for_klass to handle exceptions static void classes_do(void f(klassOop, TRAPS), TRAPS); // All classes, and their class loaders @@ -408,6 +410,8 @@ initialize_wk_klasses_until((WKID) limit, start_id, THREAD); } + static void rollback_redefinition(); + public: #define WK_KLASS_DECLARE(name, ignore_symbol, option) \ static klassOop name() { return check_klass_##option(_well_known_klasses[WK_KLASS_ENUM_NAME(name)]); } @@ -576,7 +580,7 @@ // after waiting, but before reentering SystemDictionary_lock // to preserve lock order semantics. static void double_lock_wait(Handle lockObject, TRAPS); - static void define_instance_class(instanceKlassHandle k, TRAPS); + static void define_instance_class(instanceKlassHandle k, KlassHandle old_class, TRAPS); static instanceKlassHandle find_or_define_instance_class(symbolHandle class_name, Handle class_loader, instanceKlassHandle k, TRAPS); @@ -595,6 +599,11 @@ // Setup link to hierarchy static void add_to_hierarchy(instanceKlassHandle k, TRAPS); +public: + + // Remove link to hierarchy + static void remove_from_hierarchy(instanceKlassHandle k); + private: // We pass in the hashtable index so we can calculate it outside of // the SystemDictionary_lock. diff -r f5603a6e5042 src/share/vm/classfile/verifier.cpp --- a/src/share/vm/classfile/verifier.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/classfile/verifier.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -70,7 +70,7 @@ return !need_verify; } -bool Verifier::verify(instanceKlassHandle klass, Verifier::Mode mode, bool should_verify_class, TRAPS) { +bool Verifier::verify(instanceKlassHandle klass, Verifier::Mode mode, bool should_verify_class, bool may_use_old_verifier, TRAPS) { ResourceMark rm(THREAD); HandleMark hm; @@ -94,17 +94,19 @@ split_verifier.verify_class(THREAD); exception_name = split_verifier.result(); if (klass->major_version() < NOFAILOVER_MAJOR_VERSION && - FailOverToOldVerifier && !HAS_PENDING_EXCEPTION && + FailOverToOldVerifier && may_use_old_verifier && !HAS_PENDING_EXCEPTION && (exception_name == vmSymbols::java_lang_VerifyError() || exception_name == vmSymbols::java_lang_ClassFormatError())) { if (TraceClassInitialization) { tty->print_cr( "Fail over class verification to old verifier for: %s", klassName); } + assert(may_use_old_verifier, ""); exception_name = inference_verify( klass, message_buffer, message_buffer_len, THREAD); } } else { + assert(may_use_old_verifier, ""); exception_name = inference_verify( klass, message_buffer, message_buffer_len, THREAD); } @@ -119,6 +121,9 @@ } tty->print_cr("End class verification for: %s", klassName); } + } else if (TraceClassInitialization) { + // (tw) Output not verified classes + tty->print_cr("Class %s was not verified", klassName); } if (HAS_PENDING_EXCEPTION) { @@ -170,7 +175,7 @@ // NOTE: this is called too early in the bootstrapping process to be // guarded by Universe::is_gte_jdk14x_version()/UseNewReflection. (refl_magic_klass == NULL || - !klass->is_subtype_of(refl_magic_klass) || + !(klass->is_subtype_of(refl_magic_klass) || klass->is_subtype_of(refl_magic_klass->klass_part()->newest_version())) || VerifyReflectionBytecodes) ); } @@ -240,7 +245,7 @@ ClassVerifier::ClassVerifier( instanceKlassHandle klass, char* msg, size_t msg_len, TRAPS) : _thread(THREAD), _exception_type(symbolHandle()), _message(msg), - _message_buffer_len(msg_len), _klass(klass) { + _message_buffer_len(msg_len), _klass(klass->newest_version()), _klass_to_verify(klass) { _this_type = VerificationType::reference_type(klass->name()); } @@ -253,7 +258,7 @@ _klass->external_name()); } - objArrayHandle methods(THREAD, _klass->methods()); + objArrayHandle methods(THREAD, _klass_to_verify->methods()); int num_methods = methods->length(); for (int index = 0; index < num_methods; index++) { @@ -2043,7 +2048,10 @@ VerificationType stack_object_type = current_frame->pop_stack(ref_class_type, CHECK_VERIFY(this)); if (current_type() != stack_object_type) { - assert(cp->cache() == NULL, "not rewritten yet"); + + // (tw) TODO: Check if relaxing the following assertion is correct. For class redefinition we might call the verifier twice. + //assert(cp->cache() == NULL, "not rewritten yet"); + symbolHandle ref_class_name = symbolHandle(THREAD, cp->klass_name_at(cp->klass_ref_index_at(index))); // See the comments in verify_field_instructions() for diff -r f5603a6e5042 src/share/vm/classfile/verifier.hpp --- a/src/share/vm/classfile/verifier.hpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/classfile/verifier.hpp Fri Dec 17 13:24:08 2010 +0100 @@ -37,7 +37,7 @@ * Otherwise, no exception is thrown and the return indicates the * error. */ - static bool verify(instanceKlassHandle klass, Mode mode, bool should_verify_class, TRAPS); + static bool verify(instanceKlassHandle klass, Mode mode, bool should_verify_class, bool may_use_old_verifier, TRAPS); // Return false if the class is loaded by the bootstrap loader, // or if defineClass was called requesting skipping verification @@ -88,7 +88,10 @@ char* _message; size_t _message_buffer_len; +public: void verify_method(methodHandle method, TRAPS); + +private: char* generate_code_data(methodHandle m, u4 code_length, TRAPS); void verify_exception_handler_table(u4 code_length, char* code_data, int& min, int& max, TRAPS); void verify_local_variable_table(u4 code_length, char* code_data, TRAPS); @@ -157,6 +160,7 @@ bool name_in_supers(symbolOop ref_name, instanceKlassHandle current); + instanceKlassHandle _klass_to_verify; instanceKlassHandle _klass; // the class being verified methodHandle _method; // current method being verified VerificationType _this_type; // the verification type of the current class diff -r f5603a6e5042 src/share/vm/classfile/vmSymbols.hpp --- a/src/share/vm/classfile/vmSymbols.hpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/classfile/vmSymbols.hpp Fri Dec 17 13:24:08 2010 +0100 @@ -349,6 +349,9 @@ template(erasedType_name, "erasedType") \ template(genericInvoker_name, "genericInvoker") \ template(append_name, "append") \ + /* mutator in case of class redefinition */ \ + template(static_transformer_name, "$staticTransformer") \ + template(transformer_name, "$transformer") \ \ /* non-intrinsic name/signature pairs: */ \ template(register_method_name, "register") \ diff -r f5603a6e5042 src/share/vm/code/nmethod.cpp --- a/src/share/vm/code/nmethod.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/code/nmethod.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -2055,15 +2055,14 @@ methodOop method = deps.method_argument(0); for (int j = 0; j < dependee_methods->length(); j++) { if ((methodOop) dependee_methods->obj_at(j) == method) { - // RC_TRACE macro has an embedded ResourceMark - RC_TRACE(0x01000000, - ("Found evol dependency of nmethod %s.%s(%s) compile_id=%d on method %s.%s(%s)", + ResourceMark rm(Thread::current()); + TRACE_RC3("Found evol dependency of nmethod %s.%s(%s) compile_id=%d on method %s.%s(%s)", _method->method_holder()->klass_part()->external_name(), _method->name()->as_C_string(), _method->signature()->as_C_string(), compile_id(), method->method_holder()->klass_part()->external_name(), method->name()->as_C_string(), - method->signature()->as_C_string())); + method->signature()->as_C_string()); if (TraceDependencies || LogCompilation) deps.log_dependency(dependee); return true; diff -r f5603a6e5042 src/share/vm/compiler/compileBroker.cpp --- a/src/share/vm/compiler/compileBroker.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/compiler/compileBroker.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -1027,6 +1027,7 @@ } // RedefineClasses() has replaced this method; just return + // (tw) This is important for the new version of hotswapping: Old code will only execute properly in the interpreter! if (method->is_old()) { return NULL; } @@ -1352,6 +1353,8 @@ // Never compile a method if breakpoints are present in it if (method()->number_of_breakpoints() == 0) { + thread->compilation_mutex()->lock(); + thread->set_should_bailout(false); // Compile the method. if ((UseCompiler || AlwaysCompileLoopMethods) && CompileBroker::should_compile_new_jobs()) { #ifdef COMPILER1 @@ -1375,6 +1378,7 @@ // After compilation is disabled, remove remaining methods from queue method->clear_queued_for_compilation(); } + thread->compilation_mutex()->unlock(); } } } @@ -1535,7 +1539,11 @@ //assert(false, "compiler should always document failure"); // The compiler elected, without comment, not to register a result. // Do not attempt further compilations of this method. - ci_env.record_method_not_compilable("compile failed"); + if (((CompilerThread *)Thread::current())->should_bailout()) { + ci_env.record_failure("compile externally aborted"); + } else { + ci_env.record_method_not_compilable("compile failed"); + } } if (ci_env.failing()) { @@ -1882,3 +1890,15 @@ st->cr(); #endif } + +// (tw) Clean up compiler interface after a class redefinition step +void CompileBroker::cleanup_after_redefinition() { + int num_threads = _method_threads->length(); + + ciObjectFactory::sort_ci_objects(ciObjectFactory::_shared_ci_objects); + for (int i=0; iat(i)->env() != NULL && _method_threads->at(i)->env() != (ciEnv *)badAddress) { + _method_threads->at(i)->env()->cleanup_after_redefinition(); + } + } +} \ No newline at end of file diff -r f5603a6e5042 src/share/vm/compiler/compileBroker.hpp --- a/src/share/vm/compiler/compileBroker.hpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/compiler/compileBroker.hpp Fri Dec 17 13:24:08 2010 +0100 @@ -379,4 +379,6 @@ static void print_last_compile(); static void print_compiler_threads_on(outputStream* st); + + static void cleanup_after_redefinition(); }; diff -r f5603a6e5042 src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp --- a/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -140,6 +140,13 @@ } } + +HeapWord* CompactibleFreeListSpace::forward_compact_top(size_t size, + CompactPoint* cp, HeapWord* compact_top) { + ShouldNotReachHere(); + return NULL; +} + // 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 -r f5603a6e5042 src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.hpp --- a/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.hpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.hpp Fri Dec 17 13:24:08 2010 +0100 @@ -140,6 +140,7 @@ // Support for compacting cms HeapWord* cross_threshold(HeapWord* start, HeapWord* end); + HeapWord* forward_compact_top(size_t size, CompactPoint* cp, HeapWord* compact_top); HeapWord* forward(oop q, size_t size, CompactPoint* cp, HeapWord* compact_top); // Initialization helpers. diff -r f5603a6e5042 src/share/vm/gc_implementation/includeDB_gc_g1 --- a/src/share/vm/gc_implementation/includeDB_gc_g1 Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/gc_implementation/includeDB_gc_g1 Fri Dec 17 13:24:08 2010 +0100 @@ -330,8 +330,6 @@ ptrQueue.hpp allocation.hpp ptrQueue.hpp sizes.hpp -ptrQueue.inline.hpp ptrQueue.hpp - satbQueue.cpp allocation.inline.hpp satbQueue.cpp mutexLocker.hpp satbQueue.cpp satbQueue.hpp diff -r f5603a6e5042 src/share/vm/gc_implementation/shared/markSweep.cpp --- a/src/share/vm/gc_implementation/shared/markSweep.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/gc_implementation/shared/markSweep.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -25,6 +25,8 @@ #include "incls/_precompiled.incl" #include "incls/_markSweep.cpp.incl" +GrowableArray* MarkSweep::_rescued_oops = NULL; + Stack MarkSweep::_marking_stack; Stack MarkSweep::_revisit_mdo_stack; Stack MarkSweep::_revisit_klass_stack; @@ -345,3 +347,86 @@ } #endif + +// (tw) Copy the rescued objects to their destination address after compaction. +void MarkSweep::copy_rescued_objects_back() { + + if (_rescued_oops != NULL) { + + for (int i=0; i<_rescued_oops->length(); i++) { + oop rescued_obj = _rescued_oops->at(i); + + int size = rescued_obj->size(); + oop new_obj = rescued_obj->forwardee(); + + if (rescued_obj->blueprint()->new_version() != NULL) { + MarkSweep::update_fields(rescued_obj, new_obj); + } else { + Copy::aligned_disjoint_words((HeapWord*)rescued_obj, (HeapWord*)new_obj, size); + } + + FREE_RESOURCE_ARRAY(HeapWord, rescued_obj, size); + + new_obj->init_mark(); + assert(new_obj->is_oop(), "must be a valid oop"); + } + _rescued_oops->clear(); + _rescued_oops = NULL; + } +} + +// (tw) Update instances of a class whose fields changed. +void MarkSweep::update_fields(oop q, oop new_location) { + + assert(q->blueprint()->new_version() != NULL, "class of old object must have new version"); + + klassOop old_klass_oop = q->klass(); + klassOop new_klass_oop = q->blueprint()->new_version(); + + instanceKlass *old_klass = instanceKlass::cast(old_klass_oop); + instanceKlass *new_klass = instanceKlass::cast(new_klass_oop); + + int size = q->size_given_klass(old_klass); + int new_size = q->size_given_klass(new_klass); + + oop tmp_obj = q; + + if (new_klass_oop->klass_part()->is_copying_backwards()) { + if (((HeapWord *)q >= (HeapWord *)new_location && (HeapWord *)q < (HeapWord *)new_location + new_size) || + ((HeapWord *)new_location >= (HeapWord *)q && (HeapWord *)new_location < (HeapWord *)q + size)) { + tmp_obj = (oop)resource_allocate_bytes(size * HeapWordSize); + Copy::aligned_disjoint_words((HeapWord*)q, (HeapWord*)tmp_obj, size); + } + } + + int *cur = new_klass_oop->klass_part()->update_information(); + + tmp_obj->set_klass_no_check(new_klass_oop); + + if (cur == NULL) { + assert(size == new_size, "just checking"); + Copy::conjoint_words(((HeapWord *)tmp_obj), ((HeapWord *)new_location), size); + } else { + int destOffset = 0; + while (*cur != 0) { + if (*cur > 0) { + int size = *cur; + cur++; + int offset = *cur; + Copy::conjoint_jbytes(((char *)tmp_obj) + offset, ((char *)new_location) + destOffset, size); + destOffset += size; + cur++; + } else { + assert(*cur < 0, ""); + int skip = -*cur; + Copy::fill_to_bytes(((char*)new_location) + destOffset, skip, 0); + destOffset += skip; + cur++; + } + } + } + + if (tmp_obj != q) { + FREE_RESOURCE_ARRAY(HeapWord, tmp_obj, size); + } +} diff -r f5603a6e5042 src/share/vm/gc_implementation/shared/markSweep.hpp --- a/src/share/vm/gc_implementation/shared/markSweep.hpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/gc_implementation/shared/markSweep.hpp Fri Dec 17 13:24:08 2010 +0100 @@ -103,8 +103,12 @@ friend class AdjustPointerClosure; friend class KeepAliveClosure; friend class VM_MarkSweep; + friend class GenMarkSweep; friend void marksweep_init(); +public: + static GrowableArray* _rescued_oops; + // // Vars // @@ -190,6 +194,8 @@ template static inline void mark_and_push(T* p); static inline void push_objarray(oop obj, size_t index); + static void copy_rescued_objects_back(); + static void update_fields(oop q, oop new_location); static void follow_stack(); // Empty marking stack. static void preserve_mark(oop p, markOop mark); diff -r f5603a6e5042 src/share/vm/includeDB_core --- a/src/share/vm/includeDB_core Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/includeDB_core Fri Dec 17 13:24:08 2010 +0100 @@ -1782,6 +1782,7 @@ genMarkSweep.cpp thread_.inline.hpp genMarkSweep.cpp vmSymbols.hpp genMarkSweep.cpp vmThread.hpp +genMarkSweep.cpp jvmtiRedefineClassesTrace.hpp genMarkSweep.hpp markSweep.hpp @@ -2625,6 +2626,7 @@ klassVtable.hpp handles.hpp klassVtable.hpp oopsHierarchy.hpp +linkResolver.cpp jvmtiRedefineClassesTrace.hpp linkResolver.cpp bytecode.hpp linkResolver.cpp collectedHeap.inline.hpp linkResolver.cpp compilationPolicy.hpp @@ -3860,6 +3862,7 @@ space.cpp systemDictionary.hpp space.cpp universe.inline.hpp space.cpp vmSymbols.hpp +space.cpp jvmtiRedefineClassesTrace.hpp space.hpp allocation.hpp space.hpp blockOffsetTable.hpp diff -r f5603a6e5042 src/share/vm/includeDB_jvmti --- a/src/share/vm/includeDB_jvmti Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/includeDB_jvmti Fri Dec 17 13:24:08 2010 +0100 @@ -231,7 +231,10 @@ jvmtiRedefineClasses.cpp systemDictionary.hpp jvmtiRedefineClasses.cpp universe.inline.hpp jvmtiRedefineClasses.cpp verifier.hpp +jvmtiRedefineClasses.cpp jvmtiClassFileReconstituter.hpp +jvmtiRedefineClasses.cpp compileBroker.hpp +jvmtiRedefineClasses.hpp vmGCOperations.hpp jvmtiRedefineClasses.hpp jvmtiEnv.hpp jvmtiRedefineClasses.hpp jvmtiRedefineClassesTrace.hpp jvmtiRedefineClasses.hpp objArrayKlass.hpp diff -r f5603a6e5042 src/share/vm/interpreter/interpreterRuntime.cpp --- a/src/share/vm/interpreter/interpreterRuntime.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/interpreter/interpreterRuntime.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -366,7 +366,7 @@ assert(h_exception.not_null(), "NULL exceptions should be handled by athrow"); assert(h_exception->is_oop(), "just checking"); // Check that exception is a subclass of Throwable, otherwise we have a VerifyError - if (!(h_exception->is_a(SystemDictionary::Throwable_klass()))) { + if (!(h_exception->is_a(SystemDictionary::Throwable_klass()->klass_part()->newest_version())) && !(h_exception->is_a(SystemDictionary::Throwable_klass()))) { if (ExitVMOnVerifyError) vm_exit(-1); ShouldNotReachHere(); } diff -r f5603a6e5042 src/share/vm/interpreter/linkResolver.cpp --- a/src/share/vm/interpreter/linkResolver.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/interpreter/linkResolver.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -114,8 +114,8 @@ // Klass resolution void LinkResolver::check_klass_accessability(KlassHandle ref_klass, KlassHandle sel_klass, TRAPS) { - if (!Reflection::verify_class_access(ref_klass->as_klassOop(), - sel_klass->as_klassOop(), + if (!Reflection::verify_class_access(ref_klass->as_klassOop()->klass_part()->newest_version(), + sel_klass->as_klassOop()->klass_part()->newest_version(), true)) { ResourceMark rm(THREAD); Exceptions::fthrow( @@ -227,7 +227,7 @@ // We'll check for the method name first, as that's most likely // to be false (so we'll short-circuit out of these tests). if (sel_method->name() == vmSymbols::clone_name() && - sel_klass() == SystemDictionary::Object_klass() && + sel_klass()->klass_part()->newest_version() == SystemDictionary::Object_klass()->klass_part()->newest_version() && resolved_klass->oop_is_array()) { // We need to change "protected" to "public". assert(flags.is_protected(), "clone not protected?"); @@ -513,7 +513,7 @@ } // Final fields can only be accessed from its own class. - if (is_put && fd.access_flags().is_final() && sel_klass() != pool->pool_holder()) { + if (is_put && fd.access_flags().is_final() && sel_klass() != pool->pool_holder()->klass_part()->active_version() && sel_klass() != pool->pool_holder()) { THROW(vmSymbols::java_lang_IllegalAccessError()); } @@ -715,7 +715,7 @@ bool check_access, bool check_null_and_abstract, TRAPS) { methodHandle resolved_method; linktime_resolve_virtual_method(resolved_method, resolved_klass, method_name, method_signature, current_klass, check_access, CHECK); - runtime_resolve_virtual_method(result, resolved_method, resolved_klass, recv, receiver_klass, check_null_and_abstract, CHECK); + runtime_resolve_virtual_method(result, resolved_method, resolved_klass, recv, receiver_klass, current_klass, check_null_and_abstract, CHECK); } // throws linktime exceptions @@ -744,6 +744,7 @@ KlassHandle resolved_klass, Handle recv, KlassHandle recv_klass, + KlassHandle current_klass, bool check_null_and_abstract, TRAPS) { @@ -792,7 +793,31 @@ // recv_klass might be an arrayKlassOop but all vtables start at // the same place. The cast is to avoid virtual call and assertion. instanceKlass* inst = (instanceKlass*)recv_klass()->klass_part(); + + // (tw) The type of the virtual method call and the type of the receiver do not need to + // have anything in common, as the receiver type could've been hotswapped. + // Does not always work (method could be resolved with correct dynamic type and later + // be called at the same place with a wrong dynamic type). + // (tw) TODO: Need to handle the static type vs dynamic type issue more generally. + + // The vTable must be based on the view of the world of the resolved method + klassOop method_holder = resolved_method->method_holder(); + + if (inst->is_subtype_of(method_holder)) { selected_method = methodHandle(THREAD, inst->method_at_vtable(vtable_index)); + } else { + + tty->print_cr("Failure:"); + inst->as_klassOop()->print(); + inst->super()->print(); + juint off = inst->super_check_offset(); + klassOop sup = *(klassOop*)( (address)inst->as_klassOop() + off ); + sup->print(); + method_holder->print(); + + bool b = inst->is_subtype_of(method_holder); + THROW_MSG(vmSymbols::java_lang_NoSuchMethodError(), "(tw) A virtual method was called, but the type of the receiver is not related with the type of the class of the called method!"); + } } } diff -r f5603a6e5042 src/share/vm/interpreter/linkResolver.hpp --- a/src/share/vm/interpreter/linkResolver.hpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/interpreter/linkResolver.hpp Fri Dec 17 13:24:08 2010 +0100 @@ -100,7 +100,11 @@ // It does all necessary link-time checks & throws exceptions if necessary. class LinkResolver: AllStatic { - private: +private: + static void lookup_method (methodHandle& result, KlassHandle resolved_klass, symbolHandle name, symbolHandle signature, bool is_interface, KlassHandle current_klass, TRAPS); + static void lookup_correct_field (fieldDescriptor &fd, KlassHandle &sel_klass, KlassHandle resolved_klass, KlassHandle current_klass, symbolOop field_name, symbolOop field_sig, bool is_static); + static void lookup_correct_method (methodHandle& result, KlassHandle resolved_klass, KlassHandle current_klass, symbolHandle name, symbolHandle signature, bool is_interface, TRAPS); + static void find_correct_resolved_klass (KlassHandle &resolved_klass, KlassHandle ¤t_klass); static void lookup_method_in_klasses (methodHandle& result, KlassHandle klass, symbolHandle name, symbolHandle signature, TRAPS); static void lookup_instance_method_in_klasses (methodHandle& result, KlassHandle klass, symbolHandle name, symbolHandle signature, TRAPS); static void lookup_method_in_interfaces (methodHandle& result, KlassHandle klass, symbolHandle name, symbolHandle signature, TRAPS); @@ -123,7 +127,7 @@ static void linktime_resolve_interface_method (methodHandle& resolved_method, KlassHandle resolved_klass, symbolHandle method_name, symbolHandle method_signature, KlassHandle current_klass, bool check_access, TRAPS); static void runtime_resolve_special_method (CallInfo& result, methodHandle resolved_method, KlassHandle resolved_klass, KlassHandle current_klass, bool check_access, TRAPS); - static void runtime_resolve_virtual_method (CallInfo& result, methodHandle resolved_method, KlassHandle resolved_klass, Handle recv, KlassHandle recv_klass, bool check_null_and_abstract, TRAPS); + static void runtime_resolve_virtual_method (CallInfo& result, methodHandle resolved_method, KlassHandle resolved_klass, Handle recv, KlassHandle recv_klass, KlassHandle current_klass, bool check_null_and_abstract, TRAPS); 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 -r f5603a6e5042 src/share/vm/interpreter/oopMapCache.cpp --- a/src/share/vm/interpreter/oopMapCache.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/interpreter/oopMapCache.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -530,9 +530,9 @@ if (!_array[i].is_empty() && _array[i].method()->is_old()) { // Cache entry is occupied by an old redefined method and we don't want // to pin it down so flush the entry. - RC_TRACE(0x08000000, ("flush: %s(%s): cached entry @%d", + TRACE_RC3("flush: %s(%s): cached entry @%d", _array[i].method()->name()->as_C_string(), - _array[i].method()->signature()->as_C_string(), i)); + _array[i].method()->signature()->as_C_string(), i); _array[i].flush(); } diff -r f5603a6e5042 src/share/vm/interpreter/templateTable.hpp --- a/src/share/vm/interpreter/templateTable.hpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/interpreter/templateTable.hpp Fri Dec 17 13:24:08 2010 +0100 @@ -303,8 +303,8 @@ static void shouldnotreachhere(); // jvmti support - static void jvmti_post_field_access(Register cache, Register index, bool is_static, bool has_tos); - static void jvmti_post_field_mod(Register cache, Register index, bool is_static); + static void jvmti_post_field_access(Register cache, Register index, int byte_no, bool is_static, bool has_tos); + static void jvmti_post_field_mod(Register cache, Register index, int byte_no, bool is_static); static void jvmti_post_fast_field_mod(); // debugging of TemplateGenerator diff -r f5603a6e5042 src/share/vm/memory/genMarkSweep.cpp --- a/src/share/vm/memory/genMarkSweep.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/memory/genMarkSweep.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -372,6 +372,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 // to use a higher index (saved from phase2) when verifying perm_gen. + assert(_rescued_oops == NULL, "must be empty before processing"); GenCollectedHeap* gch = GenCollectedHeap::heap(); Generation* pg = gch->perm_gen(); @@ -385,10 +386,14 @@ VALIDATE_MARK_SWEEP_ONLY(reset_live_oop_tracking(false)); + MarkSweep::copy_rescued_objects_back(); + GenCompactClosure blk; gch->generation_iterate(&blk, true); VALIDATE_MARK_SWEEP_ONLY(compaction_complete()); + MarkSweep::copy_rescued_objects_back(); + pg->post_compact(); // Shared spaces verification. } diff -r f5603a6e5042 src/share/vm/memory/permGen.cpp --- a/src/share/vm/memory/permGen.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/memory/permGen.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -44,7 +44,12 @@ for (;;) { { - MutexLocker ml(Heap_lock); + // (tw) Only lock when not at a safepoint (necessary to use the split verifier from the VmThread) + Monitor *lock = Heap_lock; + if (SafepointSynchronize::is_at_safepoint()) { + lock = NULL; + } + MutexLockerEx ml(lock); if ((obj = gen->allocate(size, false)) != NULL) { return obj; } diff -r f5603a6e5042 src/share/vm/memory/space.cpp --- a/src/share/vm/memory/space.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/memory/space.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -359,6 +359,31 @@ _compaction_top = bottom(); } +// (tw) Calculates the compact_top that will be used for placing the next object with the giving size on the heap. +HeapWord* CompactibleSpace::forward_compact_top(size_t size, +CompactPoint* cp, HeapWord* compact_top) { + // First check if we should switch compaction space + assert(this == cp->space, "'this' should be current compaction space."); + size_t compaction_max_size = pointer_delta(end(), compact_top); + while (size > compaction_max_size) { + // switch to next compaction space + cp->space->set_compaction_top(compact_top); + cp->space = cp->space->next_compaction_space(); + if (cp->space == NULL) { + cp->gen = GenCollectedHeap::heap()->prev_gen(cp->gen); + assert(cp->gen != NULL, "compaction must succeed"); + cp->space = cp->gen->first_compaction_space(); + assert(cp->space != NULL, "generation must have a first compaction space"); + } + compact_top = cp->space->bottom(); + cp->space->set_compaction_top(compact_top); + cp->threshold = cp->space->initialize_threshold(); + compaction_max_size = pointer_delta(cp->space->end(), compact_top); + } + + return compact_top; +} + HeapWord* CompactibleSpace::forward(oop q, size_t size, CompactPoint* cp, HeapWord* compact_top) { // q is alive @@ -382,7 +407,7 @@ } // store the forwarding pointer into the mark word - if ((HeapWord*)q != compact_top) { + if ((HeapWord*)q != compact_top || (size_t)q->size() != size) { q->forward_to(oop(compact_top)); assert(q->is_gc_marked(), "encoding the pointer should preserve the mark"); } else { @@ -430,7 +455,204 @@ // Faster object search. void ContiguousSpace::prepare_for_compaction(CompactPoint* cp) { - SCAN_AND_FORWARD(cp, top, block_is_always_obj, obj_size); + if (!Universe::is_redefining_gc_run()) { + SCAN_AND_FORWARD(cp, top, block_is_always_obj, obj_size); + return; + } + + /* Compute the new addresses for the live objects and store it in the mark + * Used by universe::mark_sweep_phase2() + */ + HeapWord* compact_top; /* This is where we are currently compacting to. */ + + /* We're sure to be here before any objects are compacted into this + * space, so this is a good time to initialize this: + */ + set_compaction_top(bottom()); + + if (cp->space == NULL) { + assert(cp->gen != NULL, "need a generation"); + assert(cp->threshold == NULL, "just checking"); + assert(cp->gen->first_compaction_space() == this, "just checking"); + cp->space = cp->gen->first_compaction_space(); + compact_top = cp->space->bottom(); + cp->space->set_compaction_top(compact_top); + cp->threshold = cp->space->initialize_threshold(); + } else { + compact_top = cp->space->compaction_top(); + } + + /* We allow some amount of garbage towards the bottom of the space, so + * we don't start compacting before there is a significant gain to be made. + * Occasionally, we want to ensure a full compaction, which is determined + * by the MarkSweepAlwaysCompactCount parameter. + */ + int invocations = SharedHeap::heap()->perm_gen()->stat_record()->invocations; + bool skip_dead = ((invocations % MarkSweepAlwaysCompactCount) != 0); + + size_t allowed_deadspace = 0; + if (skip_dead) { + int ratio = (int)allowed_dead_ratio(); + allowed_deadspace = (capacity() * ratio / 100) / HeapWordSize; + } + + HeapWord* q = bottom(); + HeapWord* t = end(); + + HeapWord* end_of_live= q; /* One byte beyond the last byte of the last + live object. */ + HeapWord* first_dead = end();/* The first dead object. */ + LiveRange* liveRange = NULL; /* The current live range, recorded in the + first header of preceding free area. */ + _first_dead = first_dead; + + const intx interval = PrefetchScanIntervalInBytes; + + while (q < t) { + assert(!block_is_obj(q) || + oop(q)->mark()->is_marked() || oop(q)->mark()->is_unlocked() || + oop(q)->mark()->has_bias_pattern(), + "these are the only valid states during a mark sweep"); + if (block_is_obj(q) && oop(q)->is_gc_marked()) { + /* prefetch beyond q */ + Prefetch::write(q, interval); + /* size_t size = oop(q)->size(); changing this for cms for perm gen */ + size_t size = block_size(q); + + size_t forward_size = size; + + ////////////////////////////////////////////////////////////////////////// + // Compute the forward sizes and leave out objects whose position could + // possibly overlap other objects. + + // (tw) There is a new version of the class of q => different size + if (oop(q)->blueprint()->new_version() != NULL && oop(q)->blueprint()->new_version()->klass_part()->update_information() != NULL) { + + size_t new_size = oop(q)->size_given_klass(oop(q)->blueprint()->new_version()->klass_part()); + assert(size != new_size || oop(q)->is_perm(), "instances without changed size have to be updated prior to GC run"); + forward_size = new_size; + } + + compact_top = cp->space->forward_compact_top(forward_size, cp, compact_top); + + bool rescueing = false; + if (rescueing = must_rescue(oop(q), oop(compact_top))) { + if (MarkSweep::_rescued_oops == NULL) { + MarkSweep::_rescued_oops = new GrowableArray(128); + } + TRACE_RC5("rescue obj %d klass=%s", MarkSweep::_rescued_oops->length(), oop(q)->klass()->klass_part()->name()->as_C_string()); + MarkSweep::_rescued_oops->append(oop(q)); + } else { + compact_top = cp->space->forward(oop(q), forward_size, cp, compact_top); + } + + if ((size != forward_size || rescueing) && q < first_dead) { + // (tw) This object moves => first_dead must be set to here! + first_dead = q; + } + ////////////////////////////////////////////////////////////////////////// + + end_of_live = q + size; //forward_size; + q += size; + } else { + /* run over all the contiguous dead objects */ + HeapWord* end = q; + do { + /* prefetch beyond end */ + Prefetch::write(end, interval); + end += block_size(end); + } while (end < t && (!block_is_obj(end) || !oop(end)->is_gc_marked())); + + /* see if we might want to pretend this object is alive so that + * we don't have to compact quite as often. + */ + if (allowed_deadspace > 0 && q == compact_top) { + size_t sz = pointer_delta(end, q); + if (insert_deadspace(allowed_deadspace, q, sz)) { + compact_top = cp->space->forward(oop(q), sz, cp, compact_top); + q = end; + end_of_live = end; + continue; + } + } + + /* otherwise, it really is a free region. */ + + /* for the previous LiveRange, record the end of the live objects. */ + if (liveRange) { + liveRange->set_end(q); + } + + /* record the current LiveRange object. + * liveRange->start() is overlaid on the mark word. + */ + liveRange = (LiveRange*)q; + liveRange->set_start(end); + liveRange->set_end(end); + + /* see if this is the first dead region. */ + if (q < first_dead) { + first_dead = q; + } + + /* move on to the next object */ + q = end; + } + } + + ////////////////////////////////////////////////////////////////////////// + // Compute the forwarding addresses for the objects that need to be + // rescued. + // TODO: empty the _rescued_oops after ALL spaces are compacted! + if (MarkSweep::_rescued_oops != NULL) { + TRACE_RC2("Calculating new forward sizes for %d objects!", MarkSweep::_rescued_oops->length()); + + for (int i=0; ilength(); i++) { + oop q = MarkSweep::_rescued_oops->at(i); + + /* size_t size = oop(q)->size(); changing this for cms for perm gen */ + size_t size = block_size((HeapWord*)q); + + size_t forward_size = size; + + // (tw) There is a new version of the class of q => different size + if (oop(q)->blueprint()->new_version() != NULL) { + + size_t new_size = oop(q)->size_given_klass(oop(q)->blueprint()->new_version()->klass_part()); + assert(size != new_size || oop(q)->is_perm(), "instances without changed size have to be updated prior to GC run"); + forward_size = new_size; + } + + compact_top = cp->space->forward(oop(q), forward_size, cp, compact_top); + assert(compact_top <= t, "must not write over end of space!"); + } + MarkSweep::_rescued_oops->clear(); + MarkSweep::_rescued_oops = NULL; + } + ////////////////////////////////////////////////////////////////////////// + + assert(q == t, "just checking"); + if (liveRange != NULL) { + liveRange->set_end(q); + } + _end_of_live = end_of_live; + if (end_of_live < first_dead) { + first_dead = end_of_live; + } + _first_dead = first_dead; + + if (_first_dead > top()) { + _first_dead = top(); + } + + if (_end_of_live > top()) { + _end_of_live = top(); + } + assert(_first_dead <= top(), "Must be smaller equal"); + assert(_end_of_live <= top(), "Must be smaller equal"); + + /* save the compaction_top of the compaction space. */ + cp->space->set_compaction_top(compact_top); } void Space::adjust_pointers() { @@ -471,17 +693,322 @@ assert(q == t, "just checking"); } + +#ifdef ASSERT + +int CompactibleSpace::space_index(oop obj) { + GenCollectedHeap* heap = GenCollectedHeap::heap(); + + if (heap->is_in_permanent(obj)) { + return -1; + } + + int index = 0; + for (int i = heap->n_gens() - 1; i >= 0; i--) { + Generation* gen = heap->get_gen(i); + CompactibleSpace* space = gen->first_compaction_space(); + while (space != NULL) { + if (space->is_in_reserved(obj)) { + return index; + } + space = space->next_compaction_space(); + index++; + } + } + + tty->print_cr("could not compute space_index for %08xh", obj); + index = 0; + for (int i = heap->n_gens() - 1; i >= 0; i--) { + Generation* gen = heap->get_gen(i); + tty->print_cr(" generation %s: %08xh - %08xh", gen->name(), gen->reserved().start(), gen->reserved().end()); + + CompactibleSpace* space = gen->first_compaction_space(); + while (space != NULL) { + tty->print_cr(" %2d space %08xh - %08xh", index, space->bottom(), space->end()); + space = space->next_compaction_space(); + index++; + } + } + + ShouldNotReachHere(); + return 0; +} +#endif + +bool CompactibleSpace::must_rescue(oop old_obj, oop new_obj) { + + assert(is_in_reserved(old_obj), "old_obj must be in this space"); + + if (old_obj->is_perm()) { + // This object is in perm gen; check for invariant obj->klass() <= obj + if (oop(old_obj)->blueprint()->new_version() != NULL) { + return true; + } + } + + int size = old_obj->size(); + int original_size = size; + if (oop(old_obj)->blueprint()->is_redefining()) { + assert(oop(old_obj)->blueprint()->old_version() != NULL, "must not be null"); + original_size = oop(old_obj)->size_given_klass(oop(old_obj)->blueprint()->old_version()->klass_part()); + } else if (oop(old_obj)->blueprint()->new_version() != NULL) { + size = oop(old_obj)->size_given_klass(oop(old_obj)->blueprint()->new_version()->klass_part()); + } + + bool normalComparison = (old_obj + original_size < new_obj + size); + + if (is_in_reserved(new_obj)) { + // Old and new address are in same space, so just compare the address. + // Must rescue if object moves towards the top of the space. + assert(space_index(old_obj) == space_index(new_obj), "old_obj and new_obj must be in same space"); + return normalComparison; + + } else { + + assert(space_index(old_obj) != space_index(new_obj), "old_obj and new_obj must be in different spaces"); + + Generation* tenured_gen = GenCollectedHeap::heap()->get_gen(1); + if (tenured_gen->is_in_reserved(new_obj)) { + // Must never rescue when moving from the new into the old generation. + assert(GenCollectedHeap::heap()->get_gen(0)->is_in_reserved(old_obj), "old_obj must be in DefNewGeneration"); + assert(space_index(old_obj) > space_index(new_obj), "must be"); + return false; + + } else if (tenured_gen->is_in_reserved(old_obj)) { + // Must always rescue when moving from the old into the new generation. + assert(GenCollectedHeap::heap()->get_gen(0)->is_in_reserved(new_obj), "new_obj must be in DefNewGeneration"); + assert(space_index(old_obj) < space_index(new_obj), "must be"); + return true; + + } else { + // In the new generation, eden is located before the from space, so a + // simple pointer comparison is sufficient. + assert(GenCollectedHeap::heap()->get_gen(0)->is_in_reserved(old_obj), "old_obj must be in DefNewGeneration"); + assert(GenCollectedHeap::heap()->get_gen(0)->is_in_reserved(new_obj), "new_obj must be in DefNewGeneration"); + assert((normalComparison) == (space_index(old_obj) < space_index(new_obj)), "slow and fast computation must yield same result"); + return normalComparison; + } + } +} + +oop CompactibleSpace::rescue(oop old_obj) { + assert(must_rescue(old_obj, old_obj->forwardee()), "do not call otherwise"); + + int size = old_obj->size(); + oop rescued_obj = (oop)resource_allocate_bytes(size * HeapWordSize); + Copy::aligned_disjoint_words((HeapWord*)old_obj, (HeapWord*)rescued_obj, size); + + if (MarkSweep::_rescued_oops == NULL) { + MarkSweep::_rescued_oops = new GrowableArray(128); + } + + MarkSweep::_rescued_oops->append(rescued_obj); + return rescued_obj; +} + void CompactibleSpace::adjust_pointers() { // Check first is there is any work to do. if (used() == 0) { return; // Nothing to do. } + /* adjust all the interior pointers to point at the new locations of objects + * Used by MarkSweep::mark_sweep_phase3() */ - SCAN_AND_ADJUST_POINTERS(adjust_obj_size); + HeapWord* q = bottom(); + HeapWord* t = _end_of_live; /* Established by "prepare_for_compaction". */ + + assert(_first_dead <= _end_of_live, "Stands to reason, no?"); + + debug_only(HeapWord* prev_q = NULL); + debug_only(HeapWord* prev_prev_q = NULL); + debug_only(HeapWord* prev_prev_prev_q = NULL); + if (q < t && _first_dead > q && + !oop(q)->is_gc_marked()) { + /* we have a chunk of the space which hasn't moved and we've + * reinitialized the mark word during the previous pass, so we can't + * use is_gc_marked for the traversal. */ + HeapWord* end = _first_dead; + + while (q < end) { + /* I originally tried to conjoin "block_start(q) == q" to the + * assertion below, but that doesn't work, because you can't + * accurately traverse previous objects to get to the current one + * after their pointers (including pointers into permGen) have been + * updated, until the actual compaction is done. dld, 4/00 */ + assert(block_is_obj(q), + "should be at block boundaries, and should be looking at objs"); + + VALIDATE_MARK_SWEEP_ONLY(MarkSweep::track_interior_pointers(oop(q))); + + /* point all the oops to the new location */ + size_t size = oop(q)->adjust_pointers(); + size = adjust_obj_size(size); + + VALIDATE_MARK_SWEEP_ONLY(MarkSweep::check_interior_pointers()); + VALIDATE_MARK_SWEEP_ONLY(MarkSweep::validate_live_oop(oop(q), size)); + + debug_only(prev_prev_prev_q = prev_prev_q); + debug_only(prev_prev_q = prev_q); + debug_only(prev_q = q); + q += size; + } + + // (tw) first_dead can be live object! + q = _first_dead; + +// if (_first_dead == t) { +// q = t; +// } else { +// /* $$$ This is funky. Using this to read the previously written +// * LiveRange. See also use below. */ +// q = (HeapWord*)oop(_first_dead)->mark()->decode_pointer(); +// } + } + + const intx interval = PrefetchScanIntervalInBytes; + + debug_only(prev_q = NULL); + debug_only(prev_prev_q = NULL); + debug_only(prev_prev_prev_q = NULL); + while (q < t) { + /* prefetch beyond q */ + Prefetch::write(q, interval); + if (oop(q)->is_gc_marked()) { + /* q is alive */ + VALIDATE_MARK_SWEEP_ONLY(MarkSweep::track_interior_pointers(oop(q))); + /* point all the oops to the new location */ + size_t size = oop(q)->adjust_pointers(); + size = adjust_obj_size(size); + VALIDATE_MARK_SWEEP_ONLY(MarkSweep::check_interior_pointers()); + VALIDATE_MARK_SWEEP_ONLY(MarkSweep::validate_live_oop(oop(q), size)); + debug_only(prev_prev_prev_q = prev_prev_q); + debug_only(prev_prev_q = prev_q); + debug_only(prev_q = q); + q += size; + } else { + /* q is not a live object, so its mark should point at the next + * live object */ + debug_only(prev_prev_prev_q = prev_prev_q); + debug_only(prev_prev_q = prev_q); + debug_only(prev_q = q); + q = (HeapWord*) oop(q)->mark()->decode_pointer(); + assert(q > prev_q, "we should be moving forward through memory"); + } + } + + assert(q == t, "just checking"); } void CompactibleSpace::compact() { - SCAN_AND_COMPACT(obj_size); + + if(!Universe::is_redefining_gc_run()) { + SCAN_AND_COMPACT(obj_size); + return; + } + + /* Copy all live objects to their new location + * Used by MarkSweep::mark_sweep_phase4() */ + + HeapWord* q = bottom(); + HeapWord* const t = _end_of_live; + debug_only(HeapWord* prev_q = NULL); + debug_only(HeapWord* prev_prev_q = NULL); + debug_only(HeapWord* prev_compaction_top = NULL); + debug_only(int old_size = 0); + + if (q < t && _first_dead > q && + !oop(q)->is_gc_marked()) { + /* we have a chunk of the space which hasn't moved and we've reinitialized + * the mark word during the previous pass, so we can't use is_gc_marked for + * the traversal. */ + HeapWord* const end = _first_dead; + +#ifdef ASSERT + while (q < end) { + size_t size = oop(q)->size(); + assert(!oop(q)->is_gc_marked(), + "should be unmarked (special dense prefix handling)"); + VALIDATE_MARK_SWEEP_ONLY(MarkSweep::live_oop_moved_to(q, size, q)); + debug_only(prev_prev_q = prev_q); + debug_only(prev_q = q); + q += size; + } +#endif + // (tw) first_dead can be live object! + q = _first_dead; + + //if (_first_dead == t) { + // q = t; + //} else { + ///* $$$ Funky */ + //q = (HeapWord*) oop(_first_dead)->mark()->decode_pointer(); + //} + } + + const intx scan_interval = PrefetchScanIntervalInBytes; + const intx copy_interval = PrefetchCopyIntervalInBytes; + while (q < t) { + if (!oop(q)->is_gc_marked()) { + /* mark is pointer to next marked oop */ + debug_only(prev_prev_q = prev_q); + debug_only(prev_q = q); + q = (HeapWord*) oop(q)->mark()->decode_pointer(); + assert(q > prev_q, "we should be moving forward through memory"); + } else { + /* prefetch beyond q */ + Prefetch::read(q, scan_interval); + + /* size and destination */ + size_t size = obj_size(q); + HeapWord* compaction_top = (HeapWord*)oop(q)->forwardee(); + + size_t original_size = size; + + if (must_rescue(oop(q), oop(q)->forwardee())) { + oop dest_obj = rescue(oop(q)); + debug_only(Copy::fill_to_words(q, original_size, 0)); + } else { + + /* prefetch beyond compaction_top */ + Prefetch::write(compaction_top, copy_interval); + + /* copy object and reinit its mark */ + VALIDATE_MARK_SWEEP_ONLY(MarkSweep::live_oop_moved_to(q, size, + compaction_top)); + assert(q != compaction_top || oop(q)->blueprint()->new_version() != NULL, "everything in this pass should be moving"); + + if (oop(q)->blueprint()->new_version() != NULL) { + MarkSweep::update_fields(oop(q), oop(compaction_top)); + } else { + Copy::aligned_conjoint_words(q, compaction_top, size); + } + oop(compaction_top)->init_mark(); + assert(oop(compaction_top)->klass() != NULL, "should have a class"); + } + + debug_only(prev_compaction_top = compaction_top); + debug_only(prev_prev_q = prev_q); + debug_only(prev_q = q); + debug_only(old_size = (int)size); + q += original_size; + } + } + + /* Reset space after compaction is complete */ + reset_after_compaction(); + /* We do this clear, below, since it has overloaded meanings for some */ + /* space subtypes. For example, OffsetTableContigSpace's that were */ + /* compacted into will have had their offset table thresholds updated */ + /* continuously, but those that weren't need to have their thresholds */ + /* re-initialized. Also mangles unused area for debugging. */ + if (is_empty()) { + clear(SpaceDecorator::Mangle); + } else { + if (ZapUnusedHeapArea) mangle_unused_area(); + } + + + //SCAN_AND_COMPACT(obj_size); } void Space::print_short() const { print_short_on(tty); } diff -r f5603a6e5042 src/share/vm/memory/space.hpp --- a/src/share/vm/memory/space.hpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/memory/space.hpp Fri Dec 17 13:24:08 2010 +0100 @@ -419,6 +419,9 @@ // indicates when the next such action should be taken. virtual void prepare_for_compaction(CompactPoint* cp); // MarkSweep support phase3 + DEBUG_ONLY(int space_index(oop obj)); + bool must_rescue(oop old_obj, oop new_obj); + oop rescue(oop old_obj); virtual void adjust_pointers(); // MarkSweep support phase4 virtual void compact(); @@ -449,6 +452,10 @@ virtual HeapWord* forward(oop q, size_t size, CompactPoint* cp, HeapWord* compact_top); + // (tw) + virtual HeapWord* forward_compact_top(size_t size, CompactPoint* cp, + HeapWord* compact_top); + // Return a size with adjusments as required of the space. virtual size_t adjust_object_size_v(size_t size) const { return size; } diff -r f5603a6e5042 src/share/vm/memory/universe.cpp --- a/src/share/vm/memory/universe.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/memory/universe.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -25,6 +25,8 @@ # include "incls/_precompiled.incl" # include "incls/_universe.cpp.incl" +bool Universe::_is_redefining_gc_run = false; + // Known objects klassOop Universe::_boolArrayKlassObj = NULL; klassOop Universe::_byteArrayKlassObj = NULL; @@ -131,6 +133,43 @@ f(systemObjArrayKlassObj()); } +// (tw) This method should iterate all pointers that are not within heap objects. +void Universe::root_oops_do(OopClosure *oopClosure) { + + class AlwaysTrueClosure: public BoolObjectClosure { + public: + void do_object(oop p) { ShouldNotReachHere(); } + bool do_object_b(oop p) { return true; } + }; + AlwaysTrueClosure always_true; + + Universe::oops_do(oopClosure); + ReferenceProcessor::oops_do(oopClosure); + JNIHandles::oops_do(oopClosure); // Global (strong) JNI handles + Threads::oops_do(oopClosure, NULL); + ObjectSynchronizer::oops_do(oopClosure); + FlatProfiler::oops_do(oopClosure); + JvmtiExport::oops_do(oopClosure); + // SO_AllClasses + SystemDictionary::oops_do(oopClosure); + vmSymbols::oops_do(oopClosure); + + // Now adjust pointers in remaining weak roots. (All of which should + // have been cleared if they pointed to non-surviving objects.) + // Global (weak) JNI handles + JNIHandles::weak_oops_do(&always_true, oopClosure); + + CodeCache::oops_do(oopClosure); + SymbolTable::oops_do(oopClosure); + StringTable::oops_do(oopClosure); + + // (tw) TODO: Check if this is correct? + //CodeCache::scavenge_root_nmethods_oops_do(oopClosure); + //Management::oops_do(oopClosure); + //ref_processor()->weak_oops_do(&oopClosure); + //PSScavenge::reference_processor()->weak_oops_do(&oopClosure); +} + void Universe::oops_do(OopClosure* f, bool do_all) { f->do_oop((oop*) &_int_mirror); @@ -1424,10 +1463,9 @@ } // RC_TRACE macro has an embedded ResourceMark - RC_TRACE(0x00000100, - ("add: %s(%s): adding prev version ref for cached method @%d", + TRACE_RC2("add: %s(%s): adding prev version ref for cached method @%d", method->name()->as_C_string(), method->signature()->as_C_string(), - _prev_methods->length())); + _prev_methods->length()); methodHandle method_h(method); jweak method_ref = JNIHandles::make_weak_global(method_h); @@ -1454,9 +1492,8 @@ JNIHandles::destroy_weak_global(method_ref); _prev_methods->remove_at(i); } else { - // RC_TRACE macro has an embedded ResourceMark - RC_TRACE(0x00000400, ("add: %s(%s): previous cached method @%d is alive", - m->name()->as_C_string(), m->signature()->as_C_string(), i)); + TRACE_RC2("add: %s(%s): previous cached method @%d is alive", + m->name()->as_C_string(), m->signature()->as_C_string(), i); } } } // end add_previous_version() diff -r f5603a6e5042 src/share/vm/memory/universe.hpp --- a/src/share/vm/memory/universe.hpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/memory/universe.hpp Fri Dec 17 13:24:08 2010 +0100 @@ -113,6 +113,8 @@ friend class SystemDictionary; friend class VMStructs; friend class CompactingPermGenGen; + friend class Space; + friend class ContiguousSpace; friend class VM_PopulateDumpSharedSpace; friend jint universe_init(); @@ -246,7 +248,18 @@ static void compute_verify_oop_data(); + static bool _is_redefining_gc_run; + public: + + static bool is_redefining_gc_run() { + return _is_redefining_gc_run; + } + + static void set_redefining_gc_run(bool b) { + _is_redefining_gc_run = b; + } + // Known classes in the VM static klassOop boolArrayKlassObj() { return _boolArrayKlassObj; } static klassOop byteArrayKlassObj() { return _byteArrayKlassObj; } @@ -388,6 +401,8 @@ // Iteration + static void root_oops_do(OopClosure *f); + // Apply "f" to the addresses of all the direct heap pointers maintained // as static fields of "Universe". static void oops_do(OopClosure* f, bool do_all = false); @@ -404,6 +419,7 @@ // Debugging static bool verify_in_progress() { return _verify_in_progress; } + static void set_verify_in_progress(bool b) { _verify_in_progress = b; } static void verify(bool allow_dirty = true, bool silent = false, bool option = true); static int verify_count() { return _verify_count; } static void print(); diff -r f5603a6e5042 src/share/vm/oops/arrayKlass.cpp --- a/src/share/vm/oops/arrayKlass.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/oops/arrayKlass.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -117,9 +117,9 @@ bool arrayKlass::compute_is_subtype_of(klassOop k) { // An array is a subtype of Serializable, Clonable, and Object - return k == SystemDictionary::Object_klass() - || k == SystemDictionary::Cloneable_klass() - || k == SystemDictionary::Serializable_klass(); + return k->klass_part()->newest_version() == SystemDictionary::Object_klass()->klass_part()->newest_version() + || k->klass_part()->newest_version() == SystemDictionary::Cloneable_klass()->klass_part()->newest_version() + || k->klass_part()->newest_version() == SystemDictionary::Serializable_klass()->klass_part()->newest_version(); } diff -r f5603a6e5042 src/share/vm/oops/cpCacheOop.cpp --- a/src/share/vm/oops/cpCacheOop.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/oops/cpCacheOop.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -28,9 +28,15 @@ // Implememtation of ConstantPoolCacheEntry +void ConstantPoolCacheEntry::copy_from(ConstantPoolCacheEntry *other) { + _flags = other->_flags; // flags +} + void ConstantPoolCacheEntry::initialize_entry(int index) { assert(0 < index && index < 0x10000, "sanity check"); _indices = index; + _f1 = NULL; + _f2 = 0; assert(constant_pool_index() == index, ""); } @@ -149,7 +155,8 @@ int vtable_index) { assert(!is_secondary_entry(), ""); assert(method->interpreter_entry() != NULL, "should have been set at this point"); - assert(!method->is_obsolete(), "attempt to write obsolete method to cpCache"); + // (tw) No longer valid assert + //assert(!method->is_obsolete(), "attempt to write obsolete method to cpCache"); bool change_to_virtual = (invoke_code == Bytecodes::_invokeinterface); int byte_no = -1; @@ -163,6 +170,9 @@ } else { assert(vtable_index >= 0, "valid index"); set_f2(vtable_index); + + // (tw) save method holder in f1 for virtual calls + set_f1(method()); } byte_no = 2; break; @@ -376,26 +386,13 @@ // If this constantPoolCacheEntry refers to old_method then update it // to refer to new_method. bool ConstantPoolCacheEntry::adjust_method_entry(methodOop old_method, - methodOop new_method, bool * trace_name_printed) { + methodOop new_method) { if (is_vfinal()) { + // virtual and final so f2() contains method ptr instead of vtable index - if (f2() == (intptr_t)old_method) { - // match old_method so need an update - _f2 = (intptr_t)new_method; - if (RC_TRACE_IN_RANGE(0x00100000, 0x00400000)) { - if (!(*trace_name_printed)) { - // RC_TRACE_MESG macro has an embedded ResourceMark - RC_TRACE_MESG(("adjust: name=%s", - Klass::cast(old_method->method_holder())->external_name())); - *trace_name_printed = true; - } - // RC_TRACE macro has an embedded ResourceMark - RC_TRACE(0x00400000, ("cpc vf-entry update: %s(%s)", - new_method->name()->as_C_string(), - new_method->signature()->as_C_string())); - } - + if((methodOop)f2() != NULL && ((methodOop)f2())->method_holder()->klass_part()->new_version()) { + initialize_entry(constant_pool_index()); return true; } @@ -403,65 +400,28 @@ return false; } - if ((oop)_f1 == NULL) { - // NULL f1() means this is a virtual entry so bail out - // We are assuming that the vtable index does not need change. + // (tw) check how to update interface methods! + if (bytecode_1() == Bytecodes::_invokevirtual || bytecode_2() == Bytecodes::_invokevirtual) { + + if(((methodOop)f1())->method_holder()->klass_part()->new_version()) { + initialize_entry(constant_pool_index()); + return true; + } + return false; } if ((oop)_f1 == old_method) { _f1 = new_method; - if (RC_TRACE_IN_RANGE(0x00100000, 0x00400000)) { - if (!(*trace_name_printed)) { - // RC_TRACE_MESG macro has an embedded ResourceMark - RC_TRACE_MESG(("adjust: name=%s", - Klass::cast(old_method->method_holder())->external_name())); - *trace_name_printed = true; - } - // RC_TRACE macro has an embedded ResourceMark - RC_TRACE(0x00400000, ("cpc entry update: %s(%s)", - new_method->name()->as_C_string(), - new_method->signature()->as_C_string())); - } - + return true; + } else if(_f1 != NULL && (bytecode_1() != Bytecodes::_invokeinterface && ((methodOop)f1())->method_holder()->klass_part()->new_version())) { + initialize_entry(constant_pool_index()); return true; } return false; } -bool ConstantPoolCacheEntry::is_interesting_method_entry(klassOop k) { - if (!is_method_entry()) { - // not a method entry so not interesting by default - return false; - } - - methodOop m = NULL; - if (is_vfinal()) { - // virtual and final so _f2 contains method ptr instead of vtable index - m = (methodOop)_f2; - } else if ((oop)_f1 == NULL) { - // NULL _f1 means this is a virtual entry so also not interesting - return false; - } else { - if (!((oop)_f1)->is_method()) { - // _f1 can also contain a klassOop for an interface - return false; - } - m = (methodOop)_f1; - } - - assert(m != NULL && m->is_method(), "sanity check"); - if (m == NULL || !m->is_method() || m->method_holder() != k) { - // robustness for above sanity checks or method is not in - // the interesting class - return false; - } - - // 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) tty->print_cr(" -------------"); @@ -502,38 +462,18 @@ // RedefineClasses() API support: // If any entry of this constantPoolCache points to any of // old_methods, replace it with the corresponding new_method. -void constantPoolCacheOopDesc::adjust_method_entries(methodOop* old_methods, methodOop* new_methods, - int methods_length, bool * trace_name_printed) { - - if (methods_length == 0) { - // nothing to do if there are no methods - return; - } - - // get shorthand for the interesting class - klassOop old_holder = old_methods[0]->method_holder(); +void constantPoolCacheOopDesc::adjust_entries(methodOop* old_methods, methodOop* new_methods, + int methods_length) { for (int i = 0; i < length(); i++) { - if (!entry_at(i)->is_interesting_method_entry(old_holder)) { - // skip uninteresting methods - continue; - } + if (entry_at(i)->is_field_entry()) { - // The constantPoolCache contains entries for several different - // things, but we only care about methods. In fact, we only care - // about methods in the same class as the one that contains the - // old_methods. At this point, we have an interesting entry. - - for (int j = 0; j < methods_length; j++) { - methodOop old_method = old_methods[j]; - methodOop new_method = new_methods[j]; - - if (entry_at(i)->adjust_method_entry(old_method, new_method, - trace_name_printed)) { - // current old_method matched this entry and we updated it so - // break out and get to the next interesting entry if there one - break; - } + // (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()) { + entry_at(i)->adjust_method_entry(NULL, NULL); } } } diff -r f5603a6e5042 src/share/vm/oops/cpCacheOop.hpp --- a/src/share/vm/oops/cpCacheOop.hpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/oops/cpCacheOop.hpp Fri Dec 17 13:24:08 2010 +0100 @@ -127,11 +127,15 @@ void set_bytecode_2(Bytecodes::Code code); void set_f1(oop f1) { oop existing_f1 = _f1; // read once - assert(existing_f1 == NULL || existing_f1 == f1, "illegal field change"); + // (tw) need to relax assertion for redefinition + // assert(existing_f1 == NULL || existing_f1 == f1, "illegal field change"); oop_store(&_f1, f1); } void set_f1_if_null_atomic(oop f1); - void set_f2(intx f2) { assert(_f2 == 0 || _f2 == f2, "illegal field change"); _f2 = f2; } + void set_f2(intx f2) { + // (tw) need to relax assertion for redefinition + // assert(_f2 == 0 || _f2 == f2, "illegal field change"); + _f2 = f2; } int as_flags(TosState state, bool is_final, bool is_vfinal, bool is_volatile, bool is_method_interface, bool is_method); void set_flags(intx flags) { _flags = flags; } @@ -159,6 +163,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 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 @@ -287,9 +293,7 @@ // trace_name_printed is set to true if the current call has // printed the klass name so that other routines in the adjust_* // group don't print the klass name. - bool adjust_method_entry(methodOop old_method, methodOop new_method, - bool * trace_name_printed); - bool is_interesting_method_entry(klassOop k); + bool adjust_method_entry(methodOop old_method, methodOop new_method); bool is_field_entry() const { return (_flags & (1 << hotSwapBit)) == 0; } bool is_method_entry() const { return (_flags & (1 << hotSwapBit)) != 0; } @@ -397,12 +401,7 @@ return (base_offset() + ConstantPoolCacheEntry::size_in_bytes() * index); } - // RedefineClasses() API support: - // If any entry of this constantPoolCache points to any of - // old_methods, replace it with the corresponding new_method. - // trace_name_printed is set to true if the current call has - // printed the klass name so that other routines in the adjust_* - // group don't print the klass name. - void adjust_method_entries(methodOop* old_methods, methodOop* new_methods, - int methods_length, bool * trace_name_printed); + // (tw) Update method and field references + void adjust_entries(methodOop* old_methods, methodOop* new_methods, + int methods_length); }; diff -r f5603a6e5042 src/share/vm/oops/instanceKlass.cpp --- a/src/share/vm/oops/instanceKlass.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/oops/instanceKlass.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -157,12 +157,116 @@ } +void instanceKlass::initialize_redefined_class() { + + TRACE_RC3("initializing redefined class %s", name()->as_C_string()); + + assert(!is_initialized(), ""); + assert(this->old_version() != NULL, ""); + assert(is_linked(), "must be linked before"); + + + instanceKlassHandle this_oop(Thread::current(), this->as_klassOop()); + class UpdateStaticFieldClosure : public FieldClosure { + + private: + instanceKlassHandle this_oop; + + public: + UpdateStaticFieldClosure(instanceKlassHandle this_oop) { + this->this_oop = this_oop; + } + + virtual void do_field(fieldDescriptor* fd) { + fieldDescriptor result; + bool found = ((instanceKlass *)(this_oop->old_version()->klass_part()))->find_local_field(fd->name(), fd->signature(), &result); + + if (found && result.is_static()) { + int old_offset = result.offset(); + assert(result.field_type() == fd->field_type(), ""); + + oop new_location = this_oop(); + oop old_location = this_oop->old_version(); + int offset = fd->offset(); + TRACE_RC3("Copying static field value for field %s old_offset=%d new_offset=%d", fd->name()->as_C_string(), old_offset, offset); + + oop cur_oop; + + switch(result.field_type()) { + + // Found static field with same name and type in the old klass => copy value from old to new klass + + case T_BOOLEAN: + new_location->bool_field_put(offset, old_location->bool_field(old_offset)); + DEBUG_ONLY(old_location->byte_field_put(old_offset, 0)); + break; + + case T_CHAR: + new_location->char_field_put(offset, old_location->char_field(old_offset)); + DEBUG_ONLY(old_location->char_field_put(old_offset, 0)); + break; + + case T_FLOAT: + new_location->float_field_put(offset, old_location->float_field(old_offset)); + DEBUG_ONLY(old_location->float_field_put(old_offset, 0)); + break; + + case T_DOUBLE: + new_location->double_field_put(offset, old_location->double_field(old_offset)); + DEBUG_ONLY(old_location->double_field_put(old_offset, 0)); + break; + + case T_BYTE: + new_location->byte_field_put(offset, old_location->byte_field(old_offset)); + DEBUG_ONLY(old_location->byte_field_put(old_offset, 0)); + break; + + case T_SHORT: + new_location->short_field_put(offset, old_location->short_field(old_offset)); + DEBUG_ONLY(old_location->short_field_put(old_offset, 0)); + break; + + case T_INT: + new_location->int_field_put(offset, old_location->int_field(old_offset)); + DEBUG_ONLY(old_location->int_field_put(old_offset, 0)); + break; + + case T_LONG: + new_location->long_field_put(offset, old_location->long_field(old_offset)); + DEBUG_ONLY(old_location->long_field_put(old_offset, 0)); + break; + + case T_OBJECT: + case T_ARRAY: + cur_oop = old_location->obj_field(old_offset); + new_location->obj_field_raw_put(offset, cur_oop); + old_location->obj_field_raw_put(old_offset, NULL); + break; + + default: + ShouldNotReachHere(); + } + } else { + + TRACE_RC2("New static field %s has_initial_value=%d", fd->name()->as_C_string(), (int)(fd->has_initial_value())); + // field not found + // (tw) TODO: Probably this call is not necessary here! + ClassFileParser::initialize_static_field(fd, Thread::current()); + } + } + }; + + UpdateStaticFieldClosure cl(this_oop); + this->do_local_static_fields(&cl); +} + + bool instanceKlass::verify_code( instanceKlassHandle this_oop, bool throw_verifyerror, TRAPS) { // 1) Verify the bytecodes Verifier::Mode mode = throw_verifyerror ? Verifier::ThrowException : Verifier::NoException; - return Verifier::verify(this_oop, mode, this_oop->should_verify_class(), CHECK_false); + return Verifier::verify(this_oop, mode, this_oop->should_verify_class(), true, CHECK_false); } @@ -269,7 +373,13 @@ jt->get_thread_stat()->perf_recursion_counts_addr(), jt->get_thread_stat()->perf_timers_addr(), PerfClassTraceTime::CLASS_VERIFY); - bool verify_ok = verify_code(this_oop, throw_verifyerror, THREAD); + if (this_oop->is_redefining()) { + Thread::current()->set_pretend_new_universe(true); + } + bool verify_ok = verify_code(this_oop, throw_verifyerror, THREAD); + if (this_oop->is_redefining()) { + Thread::current()->set_pretend_new_universe(false); + } if (!verify_ok) { return false; } @@ -304,7 +414,8 @@ } #endif this_oop->set_init_state(linked); - if (JvmtiExport::should_post_class_prepare()) { + // (tw) Must check for old version in order to prevent infinite loops. + if (JvmtiExport::should_post_class_prepare() && this_oop->old_version() == NULL /* JVMTI deadlock otherwise */) { Thread *thread = THREAD; assert(thread->is_Java_thread(), "thread->is_Java_thread()"); JvmtiExport::post_class_prepare((JavaThread *) thread, this_oop()); @@ -571,6 +682,18 @@ return false; } +bool instanceKlass::implements_interface_any_version(klassOop k) const { + k = k->klass_part()->newest_version(); + if (this->newest_version() == k) return true; + assert(Klass::cast(k)->is_interface(), "should be an interface class"); + for (int i = 0; i < transitive_interfaces()->length(); i++) { + if (((klassOop)transitive_interfaces()->obj_at(i))->klass_part()->newest_version() == k) { + return true; + } + } + return false; +} + objArrayOop instanceKlass::allocate_objArray(int n, int length, TRAPS) { if (length < 0) THROW_0(vmSymbols::java_lang_NegativeArraySizeException()); if (length > arrayOopDesc::max_array_length(T_OBJECT)) { @@ -691,7 +814,25 @@ } void instanceKlass::call_class_initializer_impl(instanceKlassHandle this_oop, TRAPS) { + + ResourceMark rm(THREAD); methodHandle h_method(THREAD, this_oop->class_initializer()); + + if (this_oop->revision_number() != -1){ + methodOop m = NULL; + if (AllowAdvancedClassRedefinition) { + m = this_oop->find_method(vmSymbols::static_transformer_name(), vmSymbols::void_method_signature()); + } + methodHandle method(m); + if (method() != NULL && method()->is_static()) { + TRACE_RC2("Calling static transformer instead of static initializer"); + h_method = method; + } else if (!((instanceKlass*)this_oop->old_version()->klass_part())->is_not_initialized()) { + // Only execute the static initializer, if it was not yet executed for the old version of the class. + return; + } + } + assert(!this_oop->is_initialized(), "we cannot initialize twice"); if (TraceClassInitialization) { tty->print("%d Initializing ", call_class_initializer_impl_counter++); @@ -843,6 +984,138 @@ } } +void instanceKlass::store_update_information(GrowableArray &values) { + int *arr = NEW_C_HEAP_ARRAY(int, values.length()); + for (int i=0; ias_klassOop(); + + if (_fields_not_changed) { + + class MyFieldClosure : public FieldClosure { + + FieldEvolutionClosure *_cl; + public: + MyFieldClosure(FieldEvolutionClosure *cl) {_cl = cl; } + virtual void do_field(fieldDescriptor* fd) { + _cl->do_changed_field(fd, fd); + } + }; + + MyFieldClosure mfc(cl); + do_nonstatic_fields(&mfc); + } else { + + _fields_not_changed = true; + + + GrowableArray fds; + + + while (true) { + + for (int i=0; ifields()->length(); i+=instanceKlass::next_offset) { + + fd.initialize(cur_new_klass_oop, i); + + if (fd.is_static()) { + continue; + } + + fds.append(fd); + } + + if (cur_new_klass->super() != NULL) { + cur_new_klass_oop = cur_new_klass->super(); + cur_new_klass = instanceKlass::cast(cur_new_klass_oop); + } else { + break; + } + } + + GrowableArray sortedFds; + + while (fds.length() > 0) { + + int minOffset = 0x7fffffff; + int minIndex = -1; + for (int i=0; ioffset(); + if (curOffset < minOffset) { + minOffset = curOffset; + minIndex = i; + } + } + + sortedFds.append(fds.at(minIndex)); + fds.remove_at(minIndex); + } + + + for (int i=0; ifields()->length(); j+=instanceKlass::next_offset) { + + old_fd.initialize(cur_old_klass_oop, j); + + if (old_fd.is_static()) { + continue; + } + + if (old_fd.name() == fd.name() && old_fd.signature() == fd.signature()) { + found = 1; + break; + } + } + + if (!found && cur_old_klass->super()) { + cur_old_klass_oop = cur_old_klass->super(); + cur_old_klass = instanceKlass::cast(cur_old_klass_oop); + } else { + break; + } + } + + if (found) { + if (old_fd.offset() != fd.offset()) { + _fields_not_changed = false; + } + cl->do_changed_field(&old_fd, &fd); + } else { + _fields_not_changed = false; + cl->do_new_field(&fd); + } + } + } +} + void instanceKlass::do_local_static_fields(FieldClosure* cl) { fieldDescriptor fd; int length = fields()->length(); @@ -1229,6 +1502,20 @@ return id; } +bool instanceKlass::update_jmethod_id(methodOop method, jmethodID newMethodID) { + size_t idnum = (size_t)method->method_idnum(); + jmethodID* jmeths = methods_jmethod_ids_acquire(); + size_t length; // length assigned as debugging crumb + jmethodID id = NULL; + if (jmeths != NULL && // If there is a cache + (length = (size_t)jmeths[0]) > idnum) { // and if it is long enough, + jmeths[idnum+1] = newMethodID; // Set the id (may be NULL) + return true; + } + + return false; +} + // Cache an itable index void instanceKlass::set_cached_itable_index(size_t idnum, int index) { @@ -1419,6 +1706,13 @@ last = b; b = b->next(); } + + // (tw) Hack as dependencies get wrong version of klassOop + if(this->old_version() != NULL) { + ((instanceKlass *)this->old_version()->klass_part())->remove_dependent_nmethod(nm); + return; + } + #ifdef ASSERT tty->print_cr("### %s can't find dependent nmethod:", this->external_name()); nm->print(); @@ -2552,9 +2846,8 @@ GrowableArray(2, true); } - // RC_TRACE macro has an embedded ResourceMark - RC_TRACE(0x00000100, ("adding previous version ref for %s @%d, EMCP_cnt=%d", - ikh->external_name(), _previous_versions->length(), emcp_method_count)); + TRACE_RC3("adding previous version ref for %s @%d, EMCP_cnt=%d", + ikh->external_name(), _previous_versions->length(), emcp_method_count); constantPoolHandle cp_h(ikh->constants()); jobject cp_ref; if (cp_h->is_shared()) { @@ -2570,8 +2863,7 @@ if (emcp_method_count == 0) { // non-shared ConstantPool gets a weak reference pv_node = new PreviousVersionNode(cp_ref, !cp_h->is_shared(), NULL); - RC_TRACE(0x00000400, - ("add: all methods are obsolete; flushing any EMCP weak refs")); + TRACE_RC3("add: all methods are obsolete; flushing any EMCP weak refs"); } else { int local_count = 0; GrowableArray* method_refs = new (ResourceObj::C_HEAP) @@ -2600,8 +2892,7 @@ // caller is the VMThread and we are at a safepoint, this is a good // time to clear out unused weak references. - RC_TRACE(0x00000400, ("add: previous version length=%d", - _previous_versions->length())); + TRACE_RC3("add: previous version length=%d", _previous_versions->length()); // skip the last entry since we just added it for (int i = _previous_versions->length() - 2; i >= 0; i--) { @@ -2626,13 +2917,12 @@ // do anything special with the index. continue; } else { - RC_TRACE(0x00000400, ("add: previous version @%d is alive", i)); + TRACE_RC3("add: previous version @%d is alive", i); } GrowableArray* method_refs = pv_node->prev_EMCP_methods(); if (method_refs != NULL) { - RC_TRACE(0x00000400, ("add: previous methods length=%d", - method_refs->length())); + TRACE_RC3("add: previous methods length=%d", method_refs->length()); for (int j = method_refs->length() - 1; j >= 0; j--) { jweak method_ref = method_refs->at(j); assert(method_ref != NULL, "weak method ref was unexpectedly cleared"); @@ -2651,11 +2941,8 @@ JNIHandles::destroy_weak_global(method_ref); method_refs->remove_at(j); } else { - // RC_TRACE macro has an embedded ResourceMark - RC_TRACE(0x00000400, - ("add: %s(%s): previous method @%d in version @%d is alive", - method->name()->as_C_string(), method->signature()->as_C_string(), - j, i)); + TRACE_RC3("add: %s(%s): previous method @%d in version @%d is alive", + method->name()->as_C_string(), method->signature()->as_C_string(), j, i); } } } @@ -2738,9 +3025,8 @@ // The current RedefineClasses() call has made all EMCP // versions of this method obsolete so mark it as obsolete // and remove the weak ref. - RC_TRACE(0x00000400, - ("add: %s(%s): flush obsolete method @%d in version @%d", - m_name->as_C_string(), m_signature->as_C_string(), k, j)); + TRACE_RC3("add: %s(%s): flush obsolete method @%d in version @%d", + m_name->as_C_string(), m_signature->as_C_string(), k, j); method->set_is_obsolete(); JNIHandles::destroy_weak_global(method_ref); diff -r f5603a6e5042 src/share/vm/oops/instanceKlass.hpp --- a/src/share/vm/oops/instanceKlass.hpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/oops/instanceKlass.hpp Fri Dec 17 13:24:08 2010 +0100 @@ -86,6 +86,14 @@ virtual void do_field(fieldDescriptor* fd) = 0; }; +// (tw) Iterates over the fields of the old and new class +class FieldEvolutionClosure : public StackObj { +public: + virtual void do_new_field(fieldDescriptor* fd) = 0; + virtual void do_old_field(fieldDescriptor* fd) = 0; + virtual void do_changed_field(fieldDescriptor* old_fd, fieldDescriptor *new_fd) = 0; +}; + #ifndef PRODUCT // Print fields. // If "obj" argument to constructor is NULL, prints static fields, otherwise prints non-static fields. @@ -244,6 +252,11 @@ JvmtiCachedClassFieldMap* _jvmti_cached_class_field_map; // JVMTI: used during heap iteration volatile u2 _idnum_allocated_count; // JNI/JVMTI: increments with the addition of methods, old ids don't change + // (tw) Field that allows for a short-path when calculating updated fields for the second time and + // no fields changed. Testing performance impact with this, can be removed later when the update + // information is cached. + bool _fields_not_changed; + // embedded Java vtable follows here // embedded Java itables follows here // embedded static fields follows here @@ -374,6 +387,7 @@ // initialization (virtuals from Klass) bool should_be_initialized() const; // means that initialize should be called void initialize(TRAPS); + void initialize_redefined_class(); void link_class(TRAPS); bool link_class_or_fail(TRAPS); // returns false on failure void unlink_class(); @@ -519,6 +533,7 @@ static void get_jmethod_id_length_value(jmethodID* cache, size_t idnum, size_t *length_p, jmethodID* id_p); jmethodID jmethod_id_or_null(methodOop method); + bool update_jmethod_id(methodOop method, jmethodID newMethodID); // cached itable index support void set_cached_itable_index(size_t idnum, int index); @@ -600,6 +615,7 @@ // subclass/subinterface checks bool implements_interface(klassOop k) const; + bool implements_interface_any_version(klassOop k) const; // Access to implementors of an interface. We only store the count // of implementors, and in case, there are only a few @@ -629,6 +645,10 @@ void do_local_static_fields(FieldClosure* cl); void do_nonstatic_fields(FieldClosure* cl); // including inherited fields void do_local_static_fields(void f(fieldDescriptor*, TRAPS), TRAPS); + void do_fields_evolution(FieldEvolutionClosure *cl); + void store_update_information(GrowableArray &values); + void clear_update_information(); + void methods_do(void f(methodOop method)); void array_klasses_do(void f(klassOop k)); diff -r f5603a6e5042 src/share/vm/oops/instanceKlassKlass.cpp --- a/src/share/vm/oops/instanceKlassKlass.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/oops/instanceKlassKlass.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -487,6 +487,28 @@ instanceKlass* ik = instanceKlass::cast(klassOop(obj)); klassKlass::oop_print_on(obj, st); + // (tw) Output revision number and revision numbers of older / newer and oldest / newest version of this class. + + st->print(BULLET"revision: %d", ik->revision_number()); + + if (ik->new_version() != NULL) { + st->print(" (newer=%d)", ik->new_version()->klass_part()->revision_number()); + } + + if (ik->newest_version() != ik->new_version() && ik->newest_version() != obj) { + st->print(" (newest=%d)", ik->newest_version()->klass_part()->revision_number()); + } + + if (ik->old_version() != NULL) { + st->print(" (old=%d)", ik->old_version()->klass_part()->revision_number()); + } + + if (ik->oldest_version() != ik->old_version() && ik->oldest_version() != obj) { + st->print(" (oldest=%d)", ik->oldest_version()->klass_part()->revision_number()); + } + + st->cr(); + st->print(BULLET"instance size: %d", ik->size_helper()); st->cr(); st->print(BULLET"klass size: %d", ik->object_size()); st->cr(); st->print(BULLET"access: "); ik->access_flags().print_on(st); st->cr(); @@ -685,7 +707,7 @@ } guarantee(sib->as_klassOop()->is_klass(), "should be klass"); guarantee(sib->as_klassOop()->is_perm(), "should be in permspace"); - guarantee(sib->super() == super, "siblings should have same superklass"); + guarantee(sib->super() == super || super->klass_part()->newest_version() == SystemDictionary::Object_klass(), "siblings should have same superklass"); sib = sib->next_sibling(); } diff -r f5603a6e5042 src/share/vm/oops/instanceRefKlass.cpp --- a/src/share/vm/oops/instanceRefKlass.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/oops/instanceRefKlass.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -363,10 +363,13 @@ instanceKlass* ik = instanceKlass::cast(k); // Check that we have the right class - debug_only(static bool first_time = true); - assert(k == SystemDictionary::Reference_klass() && first_time, - "Invalid update of maps"); - debug_only(first_time = false); + + // (tw) Asserts no longer valid for class redefinition + // debug_only(static bool first_time = true); + + //assert(k == SystemDictionary::Reference_klass() && first_time, + // "Invalid update of maps"); + //debug_only(first_time = false); assert(ik->nonstatic_oop_map_count() == 1, "just checking"); OopMapBlock* map = ik->start_of_nonstatic_oop_maps(); diff -r f5603a6e5042 src/share/vm/oops/klass.cpp --- a/src/share/vm/oops/klass.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/oops/klass.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -40,6 +40,26 @@ return false; } +void Klass::update_supers_to_newest_version() { + + if (super() != NULL) set_super(super()->klass_part()->newest_version()); + + for (uint i=0; iklass_part()->newest_version(); + } + } + + // Scan the array-of-objects + int cnt = secondary_supers()->length(); + for (int i = 0; i < cnt; i++) { + klassOop cur = (klassOop)secondary_supers()->obj_at(i); + if (cur != NULL) { + secondary_supers()->obj_at_put(i, cur->klass_part()->newest_version()); + } + } +} bool Klass::search_secondary_supers(klassOop k) const { // Put some extra logic here out-of-line, before the search proper. // This cuts down the size of the inline method. @@ -145,6 +165,13 @@ kl->set_alloc_count(0); kl->set_alloc_size(0); + kl->set_redefinition_flags(Klass::NoRedefinition); + kl->set_redefining(false); + kl->set_new_version(NULL); + kl->set_old_version(NULL); + kl->set_redefinition_index(-1); + kl->set_revision_number(-1); + kl->set_prototype_header(markOopDesc::prototype()); kl->set_biased_lock_revocation_count(0); kl->set_last_biased_lock_bulk_revocation_time(0); @@ -217,7 +244,7 @@ set_super(NULL); oop_store_without_check((oop*) &_primary_supers[0], (oop) this->as_klassOop()); assert(super_depth() == 0, "Object must already be initialized properly"); - } else if (k != super() || k == SystemDictionary::Object_klass()) { + } else if (k != super() || k->klass_part()->super() == NULL) { assert(super() == NULL || super() == SystemDictionary::Object_klass(), "initialize this only once to a non-trivial value"); set_super(k); diff -r f5603a6e5042 src/share/vm/oops/klass.hpp --- a/src/share/vm/oops/klass.hpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/oops/klass.hpp Fri Dec 17 13:24:08 2010 +0100 @@ -150,6 +150,7 @@ void* operator new(size_t ignored, KlassHandle& klass, int size, TRAPS); }; +template class Pair; class Klass : public Klass_vtbl { friend class VMStructs; @@ -198,6 +199,31 @@ oop* oop_block_beg() const { return adr_secondary_super_cache(); } oop* oop_block_end() const { return adr_next_sibling() + 1; } + // (tw) Different class redefinition flags of code evolution. + enum RedefinitionFlags { + + // This class is not redefined at all! + NoRedefinition, + + // There are changes to the class meta data. + ModifyClass = 1, + + // The size of the class meta data changes. + ModifyClassSize = ModifyClass << 1, + + // There are change to the instance format. + ModifyInstances = ModifyClassSize << 1, + + // The size of instances changes. + ModifyInstanceSize = ModifyInstances << 1, + + // A super type of this class is removed. + RemoveSuperType = ModifyInstanceSize << 1, + + // This class (or one of its super classes) has an instance transformer method. + HasInstanceTransformer = RemoveSuperType << 1, + }; + protected: // // The oop block. All oop fields must be declared here and only oop fields @@ -217,6 +243,10 @@ oop _java_mirror; // Superclass klassOop _super; + // Old class + klassOop _old_version; + // New class + klassOop _new_version; // Class name. Instance classes: java/lang/String, etc. Array classes: [I, // [Ljava/lang/String;, etc. Set to zero for all other kinds of classes. symbolOop _name; @@ -232,6 +262,16 @@ 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 + bool _subtype_changed; + int _redefinition_flags; // Level of class redefinition + bool _is_copying_backwards; // Does the class need to copy fields backwards? => possibly overwrite itself? + int * _update_information; // Update information + Pair * _type_check_information; // Offsets of object fields that need a type check + bool _is_redefining; + #ifndef PRODUCT int _verify_count; // to avoid redundant verifies #endif @@ -279,6 +319,75 @@ 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 + klassOop old_version() const { return _old_version; } + void set_old_version(klassOop klass) { assert(_old_version == NULL || klass == NULL, "Can only be set once!"); _old_version = klass; } + klassOop new_version() const { return _new_version; } + void set_new_version(klassOop klass) { assert(_new_version == NULL || klass == NULL, "Can only be set once!"); _new_version = klass; } + + // A subtype of this class is no longer a subtype + bool has_subtype_changed() const { return _subtype_changed; } + void set_subtype_changed(bool b) { assert(is_newest_version() || new_version()->klass_part()->is_newest_version(), "must be newest or second newest version"); + _subtype_changed = b; } + // state of being redefined + int redefinition_index() const { return _redefinition_index; } + void set_redefinition_index(int index) { _redefinition_index = index; } + void set_redefining(bool b) { _is_redefining = b; } + bool is_redefining() const { return _is_redefining; } + int redefinition_flags() const { return _redefinition_flags; } + bool check_redefinition_flag(int flags) const { return (_redefinition_flags & flags) != 0; } + void set_redefinition_flags(int flags) { _redefinition_flags = flags; } + bool is_copying_backwards() const { return _is_copying_backwards; } + void set_copying_backwards(bool b) { _is_copying_backwards = b; } + + // update information + int *update_information() const { return _update_information; } + void set_update_information(int *info) { _update_information = info; } + Pair *type_check_information() const { return _type_check_information; } + void set_type_check_information(Pair *info) { _type_check_information = info; } + + bool is_same_or_older_version(klassOop klass) const { + if (Klass::cast(klass) == this) { return true; } + else if (_old_version == NULL) { return false; } + else { return _old_version->klass_part()->is_same_or_older_version(klass); } + } + + // Revision number for redefined classes, -1 for originally loaded classes + jint revision_number() const { + return _revision_number; + } + + bool was_redefined() const { + return _revision_number != -1; + } + + void set_revision_number(jint number) { + _revision_number = number; + } + + klassOop oldest_version() const { + if (_old_version == NULL) { return this->as_klassOop(); } + else { return _old_version->klass_part()->oldest_version(); }; + } + + klassOop newest_version() const { + if (_new_version == NULL) { return this->as_klassOop(); } + else { return _new_version->klass_part()->newest_version(); }; + } + + klassOop active_version() const { + if (_new_version == NULL || _new_version->klass_part()->is_redefining()) { return this->as_klassOop(); assert(!this->is_redefining(), "just checking"); } + else { return _new_version->klass_part()->active_version(); }; + } + + bool is_newest_version() const { + return _new_version == NULL; + } + + // 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); } @@ -339,6 +448,8 @@ void set_next_sibling(klassOop s); oop* adr_super() const { return (oop*)&_super; } + oop* adr_old_version() const { return (oop*)&_old_version; } + oop* adr_new_version() const { return (oop*)&_new_version; } oop* adr_primary_supers() const { return (oop*)&_primary_supers[0]; } oop* adr_secondary_super_cache() const { return (oop*)&_secondary_super_cache; } oop* adr_secondary_supers()const { return (oop*)&_secondary_supers; } @@ -469,6 +580,7 @@ return search_secondary_supers(k); } } + void update_supers_to_newest_version(); bool search_secondary_supers(klassOop k) const; // Find LCA in class hierarchy diff -r f5603a6e5042 src/share/vm/oops/klassKlass.cpp --- a/src/share/vm/oops/klassKlass.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/oops/klassKlass.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -45,6 +45,8 @@ Klass* k = Klass::cast(klassOop(obj)); // If we are alive it is valid to keep our superclass and subtype caches alive MarkSweep::mark_and_push(k->adr_super()); + MarkSweep::mark_and_push(k->adr_old_version()); + MarkSweep::mark_and_push(k->adr_new_version()); for (juint i = 0; i < Klass::primary_super_limit(); i++) MarkSweep::mark_and_push(k->adr_primary_supers()+i); MarkSweep::mark_and_push(k->adr_secondary_super_cache()); @@ -65,6 +67,8 @@ Klass* k = Klass::cast(klassOop(obj)); // If we are alive it is valid to keep our superclass and subtype caches alive PSParallelCompact::mark_and_push(cm, k->adr_super()); + PSParallelCompact::mark_and_push(cm, k->adr_old_version()); + PSParallelCompact::mark_and_push(cm, k->adr_new_version()); for (juint i = 0; i < Klass::primary_super_limit(); i++) PSParallelCompact::mark_and_push(cm, k->adr_primary_supers()+i); PSParallelCompact::mark_and_push(cm, k->adr_secondary_super_cache()); @@ -85,6 +89,8 @@ int size = oop_size(obj); Klass* k = Klass::cast(klassOop(obj)); blk->do_oop(k->adr_super()); + blk->do_oop(k->adr_old_version()); + blk->do_oop(k->adr_new_version()); for (juint i = 0; i < Klass::primary_super_limit(); i++) blk->do_oop(k->adr_primary_supers()+i); blk->do_oop(k->adr_secondary_super_cache()); @@ -114,6 +120,10 @@ oop* adr; adr = k->adr_super(); if (mr.contains(adr)) blk->do_oop(adr); + adr = k->adr_old_version(); + if (mr.contains(adr)) blk->do_oop(adr); + adr = k->adr_new_version(); + if (mr.contains(adr)) blk->do_oop(adr); for (juint i = 0; i < Klass::primary_super_limit(); i++) { adr = k->adr_primary_supers()+i; if (mr.contains(adr)) blk->do_oop(adr); @@ -149,6 +159,8 @@ Klass* k = Klass::cast(klassOop(obj)); MarkSweep::adjust_pointer(k->adr_super()); + MarkSweep::adjust_pointer(k->adr_new_version()); + MarkSweep::adjust_pointer(k->adr_old_version()); 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 -r f5603a6e5042 src/share/vm/oops/klassVtable.cpp --- a/src/share/vm/oops/klassVtable.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/oops/klassVtable.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -82,7 +82,8 @@ vtable_length = Universe::base_vtable_size(); } - if (super == NULL && !Universe::is_bootstrapping() && + // (tw) TODO: Check if we can relax the condition on a fixed base vtable size + /*if (super == NULL && !Universe::is_bootstrapping() && vtable_length != Universe::base_vtable_size()) { // Someone is attempting to redefine java.lang.Object incorrectly. The // only way this should happen is from @@ -92,9 +93,9 @@ vtable_length = Universe::base_vtable_size(); } assert(super != NULL || vtable_length == Universe::base_vtable_size(), - "bad vtable size for class Object"); + "bad vtable size for class Object");*/ assert(vtable_length % vtableEntry::size() == 0, "bad vtable length"); - assert(vtable_length >= Universe::base_vtable_size(), "vtable too small"); + //assert(vtable_length >= Universe::base_vtable_size(), "vtable too small"); } int klassVtable::index_of(methodOop m, int len) const { @@ -611,17 +612,13 @@ if (unchecked_method_at(index) == old_method) { put_method_at(new_method, index); - if (RC_TRACE_IN_RANGE(0x00100000, 0x00400000)) { + IF_TRACE_RC4 { if (!(*trace_name_printed)) { - // RC_TRACE_MESG macro has an embedded ResourceMark - RC_TRACE_MESG(("adjust: name=%s", - Klass::cast(old_method->method_holder())->external_name())); + TRACE_RC4("adjust: name=%s", Klass::cast(old_method->method_holder())->external_name()); *trace_name_printed = true; } - // RC_TRACE macro has an embedded ResourceMark - RC_TRACE(0x00100000, ("vtable method update: %s(%s)", - new_method->name()->as_C_string(), - new_method->signature()->as_C_string())); + TRACE_RC4("vtable method update: %s(%s)", new_method->name()->as_C_string(), + new_method->signature()->as_C_string()); } } } @@ -994,17 +991,13 @@ if (ime->method() == old_method) { ime->initialize(new_method); - if (RC_TRACE_IN_RANGE(0x00100000, 0x00400000)) { + IF_TRACE_RC4 { if (!(*trace_name_printed)) { - // RC_TRACE_MESG macro has an embedded ResourceMark - RC_TRACE_MESG(("adjust: name=%s", - Klass::cast(old_method->method_holder())->external_name())); + TRACE_RC4("adjust: name=%s", Klass::cast(old_method->method_holder())->external_name()); *trace_name_printed = true; } - // RC_TRACE macro has an embedded ResourceMark - RC_TRACE(0x00200000, ("itable method update: %s(%s)", - new_method->name()->as_C_string(), - new_method->signature()->as_C_string())); + TRACE_RC4("itable method update: %s(%s)", new_method->name()->as_C_string(), + new_method->signature()->as_C_string()); } break; } @@ -1198,6 +1191,7 @@ void klassVtable::verify_against(outputStream* st, klassVtable* vt, int index) { vtableEntry* vte = &vt->table()[index]; + if (vte->method() == NULL || table()[index].method() == NULL) return; if (vte->method()->name() != table()[index].method()->name() || vte->method()->signature() != table()[index].method()->signature()) { fatal("mismatched name/signature of vtable entries"); @@ -1217,6 +1211,8 @@ void vtableEntry::verify(klassVtable* vt, outputStream* st) { NOT_PRODUCT(FlagSetting fs(IgnoreLockingAssertions, true)); + // (tw) TODO: Check: Does not hold? + if (method() != NULL) { assert(method() != NULL, "must have set method"); method()->verify(); // we sub_type, because it could be a miranda method @@ -1224,7 +1220,13 @@ #ifndef PRODUCT print(); #endif - fatal(err_msg("vtableEntry " PTR_FORMAT ": method is from subclass", this)); + klassOop first_klass = vt->klass()(); + klassOop second_klass = method()->method_holder(); + // (tw) the following fatal does not work for old versions of classes + if (first_klass->klass_part()->is_newest_version()) { + //fatal1("vtableEntry %#lx: method is from subclass", this); + } + } } } @@ -1232,7 +1234,7 @@ void vtableEntry::print() { ResourceMark rm; - tty->print("vtableEntry %s: ", method()->name()->as_C_string()); + tty->print("vtableEntry %s: ", (method() == NULL) ? "null" : method()->name()->as_C_string()); if (Verbose) { tty->print("m %#lx ", (address)method()); } @@ -1304,7 +1306,7 @@ for (int i = 0; i < length(); i++) { methodOop m = unchecked_method_at(i); if (m != NULL) { - if (m->is_old()) { + if (m->is_old() || !m->method_holder()->klass_part()->is_newest_version()) { return false; } } diff -r f5603a6e5042 src/share/vm/oops/methodKlass.cpp --- a/src/share/vm/oops/methodKlass.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/oops/methodKlass.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -78,6 +78,10 @@ m->set_adapter_entry(NULL); m->clear_code(); // from_c/from_i get set to c2i/i2i + m->set_forward_method(NULL); + m->set_new_version(NULL); + m->set_old_version(NULL); + if (access_flags.is_native()) { m->clear_native_function(); m->set_signature_handler(NULL); @@ -106,6 +110,9 @@ // Performance tweak: We skip iterating over the klass pointer since we // know that Universe::methodKlassObj never moves. MarkSweep::mark_and_push(m->adr_constMethod()); + MarkSweep::mark_and_push(m->adr_forward_method()); + MarkSweep::mark_and_push(m->adr_new_version()); + MarkSweep::mark_and_push(m->adr_old_version()); MarkSweep::mark_and_push(m->adr_constants()); if (m->method_data() != NULL) { MarkSweep::mark_and_push(m->adr_method_data()); @@ -120,6 +127,9 @@ // Performance tweak: We skip iterating over the klass pointer since we // know that Universe::methodKlassObj never moves. PSParallelCompact::mark_and_push(cm, m->adr_constMethod()); + PSParallelCompact::mark_and_push(cm, m->adr_forward_method()); + PSParallelCompact::mark_and_push(cm, m->adr_new_version()); + PSParallelCompact::mark_and_push(cm, m->adr_old_version()); PSParallelCompact::mark_and_push(cm, m->adr_constants()); #ifdef COMPILER2 if (m->method_data() != NULL) { @@ -138,6 +148,9 @@ // Performance tweak: We skip iterating over the klass pointer since we // know that Universe::methodKlassObj never moves blk->do_oop(m->adr_constMethod()); + blk->do_oop(m->adr_forward_method()); + blk->do_oop(m->adr_new_version()); + blk->do_oop(m->adr_old_version()); blk->do_oop(m->adr_constants()); if (m->method_data() != NULL) { blk->do_oop(m->adr_method_data()); @@ -157,6 +170,12 @@ oop* adr; adr = m->adr_constMethod(); if (mr.contains(adr)) blk->do_oop(adr); + adr = m->adr_new_version(); + if (mr.contains(adr)) blk->do_oop(adr); + adr = m->adr_forward_method(); + if (mr.contains(adr)) blk->do_oop(adr); + adr = m->adr_old_version(); + if (mr.contains(adr)) blk->do_oop(adr); adr = m->adr_constants(); if (mr.contains(adr)) blk->do_oop(adr); if (m->method_data() != NULL) { @@ -176,6 +195,9 @@ // Performance tweak: We skip iterating over the klass pointer since we // know that Universe::methodKlassObj never moves. MarkSweep::adjust_pointer(m->adr_constMethod()); + MarkSweep::adjust_pointer(m->adr_forward_method()); + MarkSweep::adjust_pointer(m->adr_new_version()); + MarkSweep::adjust_pointer(m->adr_old_version()); MarkSweep::adjust_pointer(m->adr_constants()); if (m->method_data() != NULL) { MarkSweep::adjust_pointer(m->adr_method_data()); @@ -192,6 +214,8 @@ assert(obj->is_method(), "should be method"); methodOop m = methodOop(obj); PSParallelCompact::adjust_pointer(m->adr_constMethod()); + PSParallelCompact::adjust_pointer(m->adr_new_version()); + PSParallelCompact::adjust_pointer(m->adr_old_version()); PSParallelCompact::adjust_pointer(m->adr_constants()); #ifdef COMPILER2 if (m->method_data() != NULL) { @@ -210,6 +234,12 @@ p = m->adr_constMethod(); PSParallelCompact::adjust_pointer(p, beg_addr, end_addr); + p = m->adr_forward_method(); + PSParallelCompact::adjust_pointer(p, beg_addr, end_addr); + p = m->adr_new_version(); + PSParallelCompact::adjust_pointer(p, beg_addr, end_addr); + p = m->adr_old_version(); + PSParallelCompact::adjust_pointer(p, beg_addr, end_addr); p = m->adr_constants(); PSParallelCompact::adjust_pointer(p, beg_addr, end_addr); @@ -234,7 +264,18 @@ methodOop m = methodOop(obj); // get the effect of PrintOopAddress, always, for methods: st->print_cr(" - this oop: "INTPTR_FORMAT, (intptr_t)m); - st->print (" - method holder: "); m->method_holder()->print_value_on(st); st->cr(); + st->print (" - method holder: "); m->method_holder()->print_value_on(st); + + if (m->method_holder()->klass_part()->new_version() != NULL) { + st->print(" (old)"); + } + st->cr(); + + st->print_cr(" - is obsolete: %d", (int)(m->is_obsolete())); + st->print_cr(" - is old: %d", (int)(m->is_old())); + st->print_cr(" - new version: %d", (int)(m->new_version())); + st->print_cr(" - old version: %d", (int)(m->old_version())); + st->print_cr(" - holder revision: %d", m->method_holder()->klass_part()->revision_number()); 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 -r f5603a6e5042 src/share/vm/oops/methodOop.cpp --- a/src/share/vm/oops/methodOop.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/oops/methodOop.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -986,6 +986,9 @@ // Reset correct method/const method, method size, and parameter info newcm->set_method(newm()); newm->set_constMethod(newcm); + newm->set_forward_method(newm->forward_method()); + newm->set_new_version(newm->new_version()); + newm->set_old_version(newm->old_version()); assert(newcm->method() == newm(), "check"); newm->constMethod()->set_code_size(new_code_length); newm->constMethod()->set_constMethod_size(new_const_method_size); diff -r f5603a6e5042 src/share/vm/oops/methodOop.hpp --- a/src/share/vm/oops/methodOop.hpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/oops/methodOop.hpp Fri Dec 17 13:24:08 2010 +0100 @@ -96,6 +96,11 @@ AccessFlags _access_flags; // Access flags int _vtable_index; // vtable index of this method (see VtableIndexFlag) // note: can have vtables with >2**16 elements (because of inheritance) + // (tw) Newer version of method available? + methodOop _forward_method; + methodOop _new_version; + methodOop _old_version; + #ifdef CC_INTERP int _result_index; // C++ interpreter needs for converting results to/from stack #endif @@ -150,6 +155,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; } + methodOop new_version() const {return _new_version; } + void set_new_version(methodOop m) { _new_version = m; } + methodOop newest_version() { if(_new_version == NULL) return this; else return new_version()->newest_version(); } + + methodOop old_version() const {return _old_version; }; + void set_old_version(methodOop m) { + if (m == NULL) { + _old_version = NULL; + return; + } + + assert(_old_version == NULL, "may only be set once"); + assert(this->code_size() == m->code_size(), "must have same code length"); + _old_version = m; + } + + methodOop oldest_version() const { + if(_old_version == NULL) return (methodOop)this; + else { + return old_version()->oldest_version(); + } + } + // signature symbolOop signature() const { return _constants->symbol_at(signature_index()); } int signature_index() const { return constMethod()->signature_index(); } @@ -669,6 +700,9 @@ // Garbage collection support oop* adr_constMethod() const { return (oop*)&_constMethod; } + oop* adr_forward_method() const { return (oop*)&_forward_method; } + oop* adr_new_version() const { return (oop*)&_new_version; } + oop* adr_old_version() const { return (oop*)&_old_version; } oop* adr_constants() const { return (oop*)&_constants; } oop* adr_method_data() const { return (oop*)&_method_data; } }; diff -r f5603a6e5042 src/share/vm/oops/oop.hpp --- a/src/share/vm/oops/oop.hpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/oops/oop.hpp Fri Dec 17 13:24:08 2010 +0100 @@ -81,6 +81,7 @@ narrowOop* compressed_klass_addr(); void set_klass(klassOop k); + void set_klass_no_check(klassOop k); // For klass field compression int klass_gap() const; @@ -121,6 +122,7 @@ bool is_objArray() const; bool is_symbol() const; bool is_klass() const; + bool is_instanceKlass() const; bool is_thread() const; bool is_method() const; bool is_constMethod() const; diff -r f5603a6e5042 src/share/vm/oops/oop.inline.hpp --- a/src/share/vm/oops/oop.inline.hpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/oops/oop.inline.hpp Fri Dec 17 13:24:08 2010 +0100 @@ -78,6 +78,14 @@ } } +inline void oopDesc::set_klass_no_check(klassOop k) { + if (UseCompressedOops) { + oop_store_without_check(compressed_klass_addr(), (oop)k); + } else { + oop_store_without_check(klass_addr(), (oop) k); + } +} + inline int oopDesc::klass_gap() const { return *(int*)(((intptr_t)this) + klass_gap_offset_in_bytes()); } @@ -111,6 +119,7 @@ inline bool oopDesc::is_javaArray() const { return blueprint()->oop_is_javaArray(); } inline bool oopDesc::is_symbol() const { return blueprint()->oop_is_symbol(); } inline bool oopDesc::is_klass() const { return blueprint()->oop_is_klass(); } +inline bool oopDesc::is_instanceKlass() const { return blueprint()->oop_is_instanceKlass(); } 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 -r f5603a6e5042 src/share/vm/prims/jni.cpp --- a/src/share/vm/prims/jni.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/prims/jni.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -299,7 +299,7 @@ } } klassOop k = SystemDictionary::resolve_from_stream(class_name, class_loader, - Handle(), &st, true, + Handle(), &st, true, KlassHandle(), CHECK_NULL); if (TraceClassResolution && k != NULL) { diff -r f5603a6e5042 src/share/vm/prims/jvm.cpp --- a/src/share/vm/prims/jvm.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/prims/jvm.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -811,7 +811,7 @@ Handle protection_domain (THREAD, JNIHandles::resolve(pd)); klassOop k = SystemDictionary::resolve_from_stream(class_name, class_loader, protection_domain, &st, - verify != 0, + verify != 0, KlassHandle(), CHECK_NULL); if (TraceClassResolution && k != NULL) { diff -r f5603a6e5042 src/share/vm/prims/jvmtiClassFileReconstituter.cpp --- a/src/share/vm/prims/jvmtiClassFileReconstituter.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/prims/jvmtiClassFileReconstituter.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -120,6 +120,7 @@ constMethodHandle const_method(thread(), method->constMethod()); u2 line_num_cnt = 0; int stackmap_len = 0; + int local_variable_table_length = 0; // compute number and length of attributes -- FIXME: for now no LVT int attr_count = 0; @@ -154,6 +155,25 @@ attr_size += 2 + 4 + stackmap_len; } } + if (method->has_localvariable_table()) { + local_variable_table_length = method->localvariable_table_length(); + ++attr_count; + if (local_variable_table_length != 0) { + // Compute the size of the local variable table attribute (VM stores raw): + // LocalVariableTable_attribute { + // u2 attribute_name_index; + // u4 attribute_length; + // u2 local_variable_table_length; + // { + // u2 start_pc; + // u2 length; + // u2 name_index; + // u2 descriptor_index; + // u2 index; + // } + attr_size += 2 + 4 + 2 + local_variable_table_length * (2 + 2 + 2 + 2 + 2); + } + } typeArrayHandle exception_table(thread(), const_method->exception_table()); int exception_table_length = exception_table->length(); @@ -188,6 +208,10 @@ write_stackmap_table_attribute(method, stackmap_len); } + if (local_variable_table_length != 0) { + write_local_variable_table_attribute(method, local_variable_table_length); + } + // FIXME: write LVT attribute } @@ -355,6 +379,36 @@ } } +// Write LineNumberTable attribute +// JVMSpec| LocalVariableTable_attribute { +// JVMSpec| u2 attribute_name_index; +// JVMSpec| u4 attribute_length; +// JVMSpec| u2 local_variable_table_length; +// JVMSpec| { u2 start_pc; +// JVMSpec| u2 length; +// JVMSpec| u2 name_index; +// JVMSpec| u2 descriptor_index; +// JVMSpec| u2 index; +// JVMSpec| } local_variable_table[local_variable_table_length]; +// JVMSpec| } +void JvmtiClassFileReconstituter::write_local_variable_table_attribute(methodHandle method, u2 num_entries) { + write_attribute_name_index("LocalVariableTable"); + write_u4(2 + num_entries * (2 + 2 + 2 + 2 + 2)); + write_u2(num_entries); + + assert(method->localvariable_table_length() == num_entries, "just checking"); + + LocalVariableTableElement *elem = method->localvariable_table_start(); + for (int j=0; jlocalvariable_table_length(); j++) { + write_u2(elem->start_bci); + write_u2(elem->length); + write_u2(elem->name_cp_index); + write_u2(elem->descriptor_cp_index); + write_u2(elem->slot); + elem++; + } +} + // Write stack map table attribute // JSR-202| StackMapTable_attribute { // JSR-202| u2 attribute_name_index; diff -r f5603a6e5042 src/share/vm/prims/jvmtiClassFileReconstituter.hpp --- a/src/share/vm/prims/jvmtiClassFileReconstituter.hpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/prims/jvmtiClassFileReconstituter.hpp Fri Dec 17 13:24:08 2010 +0100 @@ -114,6 +114,7 @@ void write_source_debug_extension_attribute(); u2 line_number_table_entries(methodHandle method); void write_line_number_table_attribute(methodHandle method, u2 num_entries); + void write_local_variable_table_attribute(methodHandle method, u2 num_entries); void write_stackmap_table_attribute(methodHandle method, int stackmap_table_len); u2 inner_classes_attribute_length(); void write_inner_classes_attribute(int length); diff -r f5603a6e5042 src/share/vm/prims/jvmtiEnv.cpp --- a/src/share/vm/prims/jvmtiEnv.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/prims/jvmtiEnv.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -240,7 +240,10 @@ class_definitions[index].klass = jcls; } VM_RedefineClasses op(class_count, class_definitions, jvmti_class_load_kind_retransform); - VMThread::execute(&op); + { + MutexLocker sd_mutex(RedefineClasses_lock); + VMThread::execute(&op); + } return (op.check_error()); } /* end RetransformClasses */ @@ -249,9 +252,12 @@ // class_definitions - pre-checked for NULL jvmtiError JvmtiEnv::RedefineClasses(jint class_count, const jvmtiClassDefinition* class_definitions) { -//TODO: add locking + VM_RedefineClasses op(class_count, class_definitions, jvmti_class_load_kind_redefine); - VMThread::execute(&op); + { + MutexLocker sd_mutex(RedefineClasses_lock); + VMThread::execute(&op); + } return (op.check_error()); } /* end RedefineClasses */ diff -r f5603a6e5042 src/share/vm/prims/jvmtiExport.cpp --- a/src/share/vm/prims/jvmtiExport.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/prims/jvmtiExport.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -756,7 +756,7 @@ void JvmtiExport::post_pending_compiled_method_unload_events() { JavaThread* self = JavaThread::current(); - assert(!self->owns_locks(), "can't hold locks"); + assert(!self->owns_locks_but_redefine_classes_lock(), "can't hold locks"); // Indicates if this is the first activiation of this function. // In theory the profiler's callback could call back into VM and provoke @@ -2392,7 +2392,7 @@ // iterate over any code blob descriptors collected and post a // DYNAMIC_CODE_GENERATED event to the profiler. JvmtiDynamicCodeEventCollector::~JvmtiDynamicCodeEventCollector() { - assert(!JavaThread::current()->owns_locks(), "all locks must be released to post deferred events"); + assert(!JavaThread::current()->owns_locks_but_redefine_classes_lock(), "all locks must be released to post deferred events"); // iterate over any code blob descriptors that we collected if (_code_blobs != NULL) { for (int i=0; i<_code_blobs->length(); i++) { diff -r f5603a6e5042 src/share/vm/prims/jvmtiImpl.cpp --- a/src/share/vm/prims/jvmtiImpl.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/prims/jvmtiImpl.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -254,6 +254,8 @@ void JvmtiBreakpoint::each_method_version_do(method_action meth_act) { ((methodOopDesc*)_method->*meth_act)(_bci); + // (tw) TODO: Check how we can implement this differently here! + // add/remove breakpoint to/from versions of the method that // are EMCP. Directly or transitively obsolete methods are // not saved in the PreviousVersionInfo. @@ -293,10 +295,10 @@ for (int i = methods->length() - 1; i >= 0; i--) { methodHandle method = methods->at(i); if (method->name() == m_name && method->signature() == m_signature) { - RC_TRACE(0x00000800, ("%sing breakpoint in %s(%s)", + TRACE_RC3("%sing breakpoint in %s(%s)", meth_act == &methodOopDesc::set_breakpoint ? "sett" : "clear", method->name()->as_C_string(), - method->signature()->as_C_string())); + method->signature()->as_C_string()); assert(!method->is_obsolete(), "only EMCP methods here"); ((methodOopDesc*)method()->*meth_act)(_bci); diff -r f5603a6e5042 src/share/vm/prims/jvmtiRedefineClasses.cpp --- a/src/share/vm/prims/jvmtiRedefineClasses.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/prims/jvmtiRedefineClasses.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -1,26 +1,26 @@ /* - * Copyright (c) 2003, 2009, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ +* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. +* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +* +* This code is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License version 2 only, as +* published by the Free Software Foundation. +* +* This code is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +* version 2 for more details (a copy is included in the LICENSE file that +* accompanied this code). +* +* You should have received a copy of the GNU General Public License version +* 2 along with this work; if not, write to the Free Software Foundation, +* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +* +* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +* or visit www.oracle.com if you need additional information or have any +* questions. +* +*/ # include "incls/_precompiled.incl" # include "incls/_jvmtiRedefineClasses.cpp.incl" @@ -28,479 +28,600 @@ objArrayOop VM_RedefineClasses::_old_methods = NULL; objArrayOop VM_RedefineClasses::_new_methods = NULL; -methodOop* VM_RedefineClasses::_matching_old_methods = NULL; -methodOop* VM_RedefineClasses::_matching_new_methods = NULL; -methodOop* VM_RedefineClasses::_deleted_methods = NULL; -methodOop* VM_RedefineClasses::_added_methods = NULL; +int* VM_RedefineClasses::_matching_old_methods = NULL; +int* VM_RedefineClasses::_matching_new_methods = NULL; +int* VM_RedefineClasses::_deleted_methods = NULL; +int* VM_RedefineClasses::_added_methods = NULL; int VM_RedefineClasses::_matching_methods_length = 0; int VM_RedefineClasses::_deleted_methods_length = 0; int VM_RedefineClasses::_added_methods_length = 0; klassOop VM_RedefineClasses::_the_class_oop = NULL; - -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()) { + RC_TIMER_START(_timer_total); _class_count = class_count; _class_defs = class_defs; _class_load_kind = class_load_kind; - _res = JVMTI_ERROR_NONE; + _updated_oops = NULL; + _result = JVMTI_ERROR_NONE; } +VM_RedefineClasses::~VM_RedefineClasses() { + unlock_threads(); + RC_TIMER_STOP(_timer_total); + + if (TimeRedefineClasses) { + tty->print_cr("Timing Prologue: %d", _timer_prologue.milliseconds()); + tty->print_cr("Timing Class Loading: %d", _timer_class_loading.milliseconds()); + tty->print_cr("Timing Waiting for Lock: %d", _timer_wait_for_locks.milliseconds()); + tty->print_cr("Timing Class Linking: %d", _timer_class_linking.milliseconds()); + tty->print_cr("Timing Check Type: %d", _timer_check_type.milliseconds()); + tty->print_cr("Timing Prepare Redefinition: %d", _timer_prepare_redefinition.milliseconds()); + tty->print_cr("Timing Redefinition GC: %d", _timer_redefinition.milliseconds()); + tty->print_cr("Timing Epilogue: %d", _timer_vm_op_epilogue.milliseconds()); + tty->print_cr("------------------------------------------------------------------"); + tty->print_cr("Total Time: %d", _timer_total.milliseconds()); + } +} + +// Searches for all affected classes and performs a sorting such that a supertype is always before a subtype. +jvmtiError VM_RedefineClasses::find_sorted_affected_classes(GrowableArray *all_affected_klasses) { + + // Create array with all classes for which the redefine command was given + GrowableArray klasses_to_redefine; + for (int i=0; i<_class_count; i++) { + oop mirror = JNIHandles::resolve_non_null(_class_defs[i].klass); + instanceKlassHandle klass_handle(Thread::current(), java_lang_Class::as_klassOop(mirror)); + klasses_to_redefine.append(klass_handle); + assert(klass_handle->new_version() == NULL, "Must be new class"); + } + + // Find classes not directly redefined, but affected by a redefinition (because one of its supertypes is redefined) + GrowableArray affected_classes; + FindAffectedKlassesClosure closure(&klasses_to_redefine, &affected_classes); + + // Trace affected classes + TRACE_RC1("Klasses affected: %d", affected_classes.length()); + IF_TRACE_RC2 { + for (int i=0; iname()->as_C_string()); + } + } + + // 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); + + // Sort the affected klasses such that a supertype is always on a smaller array index than its subtype. + jvmtiError result = do_topological_class_sorting(_class_defs, _class_count, &affected_classes, all_affected_klasses, Thread::current()); + IF_TRACE_RC2 { + TRACE_RC2("Redefine order: "); + for (int i=0; ilength(); i++) { + TRACE_RC2("%s", all_affected_klasses->at(i)->name()->as_C_string()); + } + } + + return result; +} + +// Searches for the class bytes of the given class and returns them as a byte array. +jvmtiError VM_RedefineClasses::find_class_bytes(instanceKlassHandle the_class, const unsigned char **class_bytes, jint *class_byte_count, jboolean *not_changed) { + + *not_changed = false; + + // Search for the index in the redefinition array that corresponds to the current class + int j; + for (j=0; j<_class_count; j++) { + oop mirror = JNIHandles::resolve_non_null(_class_defs[j].klass); + klassOop the_class_oop = java_lang_Class::as_klassOop(mirror); + if (the_class_oop == the_class()) { + break; + } + } + + if (j == _class_count) { + + *not_changed = true; + + // Redefine with same bytecodes. This is a class that is only indirectly affected by redefinition, + // so the user did not specify a different bytecode for that class. + + if (the_class->get_cached_class_file_bytes() == NULL) { + // not cached, we need to reconstitute the class file from VM representation + constantPoolHandle constants(Thread::current(), the_class->constants()); + ObjectLocker ol(constants, Thread::current()); // lock constant pool while we query it + + JvmtiClassFileReconstituter reconstituter(the_class); + if (reconstituter.get_error() != JVMTI_ERROR_NONE) { + return reconstituter.get_error(); + } + + *class_byte_count = (jint)reconstituter.class_file_size(); + *class_bytes = (unsigned char*)reconstituter.class_file_bytes(); + + } else { + + // it is cached, get it from the cache + *class_byte_count = the_class->get_cached_class_file_len(); + *class_bytes = the_class->get_cached_class_file_bytes(); + } + + } else { + + // Redefine with bytecodes at index j + *class_bytes = _class_defs[j].class_bytes; + *class_byte_count = _class_defs[j].class_byte_count; + } + + return JVMTI_ERROR_NONE; +} + +// Prologue of the VM operation, called on the Java thread in parallel to normal program execution bool VM_RedefineClasses::doit_prologue() { - if (_class_count == 0) { - _res = JVMTI_ERROR_NONE; + + _revision_number++; + TRACE_RC1("Redefinition with revision number %d started!", _revision_number); + + assert(Thread::current()->is_Java_thread(), "must be Java thread"); + RC_TIMER_START(_timer_prologue); + + if (!check_arguments()) { + RC_TIMER_STOP(_timer_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) GrowableArray(5, true); + _result = load_new_class_versions(Thread::current()); + + TRACE_RC1("Loaded new class versions!"); + if (_result != JVMTI_ERROR_NONE) { + TRACE_RC1("error occured: %d!", _result); + delete _new_classes; + _new_classes = NULL; + RC_TIMER_STOP(_timer_prologue); return false; } + + TRACE_RC2("nearly finished"); + VM_GC_Operation::doit_prologue(); + RC_TIMER_STOP(_timer_prologue); + TRACE_RC2("doit_prologue finished!"); + 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) { + + symbolOop ex_name = PENDING_EXCEPTION->klass()->klass_part()->name(); + TRACE_RC1("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); + + TRACE_RC1("==================================================================="); + TRACE_RC1("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) { + TRACE_RC1("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 - // a shared class. We do the remap during the doit() phase of - // the safepoint to be safer. - if (!CompactingPermGenGen::remap_shared_readonly_as_readwrite()) { - RC_TRACE_WITH_THREAD(0x00000001, thread, - ("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) { + TRACE_RC1("Error finding class bytes: %d", (int)error); + result = error; + break; + } + 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); + + TRACE_RC2("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()); + symbolHandle the_class_sym = symbolHandle(THREAD, 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); + + TRACE_RC2("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()) { + + TRACE_RC2("name=%s loader=%d protection_domain=%d", the_class->name()->as_C_string(), (int)(the_class->class_loader()), (int)(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_TRACE_RC1 { + if (new_class->layout_helper() != the_class->layout_helper()) { + TRACE_RC1("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); + if (redefinition_flags >= Klass::RemoveSuperType) { + TRACE_RC1("Remove super type is not allowed"); + result = JVMTI_ERROR_UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED; + break; + } + } else { + jvmtiError allowed = check_redefinition_allowed(new_class); + if (allowed != JVMTI_ERROR_NONE) { + TRACE_RC1("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 (int i=0; ifields()->length(); i+=instanceKlass::next_offset) { + fd_new.initialize(new_class(), i); + fd_old.initialize(new_class->old_version(), i); + transfer_special_access_flags(&fd_old, &fd_new); + } + } + + IF_TRACE_RC3 { + if (new_class->super() != NULL) { + TRACE_RC3("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 + + TRACE_RC2("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); + TRACE_RC2("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(); + TRACE_RC1("Found new number of affected classes: %d", new_count); + } } } - for (int i = 0; i < _class_count; i++) { - redefine_single_class(_class_defs[i].klass, _scratch_classes[i], thread); + if (result != JVMTI_ERROR_NONE) { + rollback(); + return result; } - // 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(); + + 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()); + + TRACE_RC2("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; + } + + TRACE_RC2("All classes loaded!"); #ifdef ASSERT - SystemDictionary::classes_do(check_class, thread); + 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 + + TRACE_RC1("Finished verification!"); + return JVMTI_ERROR_NONE; } -void VM_RedefineClasses::doit_epilogue() { - // Free os::malloc allocated memory. - // The memory allocated in redefine will be free'ed in next VM operation. - os::free(_scratch_classes); - - if (RC_TRACE_ENABLED(0x00000004)) { - // Used to have separate timers for "doit" and "all", but the timer - // overhead skewed the measurements. - jlong doit_time = _timer_rsc_phase1.milliseconds() + - _timer_rsc_phase2.milliseconds(); - jlong all_time = _timer_vm_op_prologue.milliseconds() + doit_time; - - RC_TRACE(0x00000004, ("vm_op: all=" UINT64_FORMAT - " prologue=" UINT64_FORMAT " doit=" UINT64_FORMAT, all_time, - _timer_vm_op_prologue.milliseconds(), doit_time)); - RC_TRACE(0x00000004, - ("redefine_single_class: phase1=" UINT64_FORMAT " phase2=" UINT64_FORMAT, - _timer_rsc_phase1.milliseconds(), _timer_rsc_phase2.milliseconds())); +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(); + } + + TRACE_RC2("Locked %d compiler threads", cnt); + + cnt = 0; + javaThread = Threads::first(); + while (javaThread != NULL) { + if (javaThread != Thread::current()) { + javaThread->redefine_classes_mutex()->lock(); + } + javaThread = javaThread->next(); + } + + + TRACE_RC2("Locked %d threads", cnt); + + RC_TIMER_STOP(_timer_wait_for_locks); } -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; +void VM_RedefineClasses::unlock_threads() { + + int cnt = 0; + JavaThread *javaThread = Threads::first(); + Thread *thread = Thread::current(); + while (javaThread != NULL) { + if (javaThread->is_Compiler_thread() && javaThread != Thread::current()) { + CompilerThread *compilerThread = (CompilerThread *)javaThread; + if (compilerThread->compilation_mutex()->owned_by_self()) { + compilerThread->compilation_mutex()->unlock(); + cnt++; + } + } + javaThread = javaThread->next(); } - 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; + + TRACE_RC2("Unlocked %d compiler threads", cnt); + + cnt = 0; + javaThread = Threads::first(); + while (javaThread != NULL) { + if (javaThread != Thread::current()) { + if (javaThread->redefine_classes_mutex()->owned_by_self()) { + javaThread->redefine_classes_mutex()->unlock(); + } + } + javaThread = javaThread->next(); } - return true; + + TRACE_RC2("Unlocked %d threads", cnt); } -// 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 -// indirect and double-indirect CP entries, there are zero or more -// referenced CP entries along with the current entry to append. -// Indirect and double-indirect CP entries are handled by recursive -// calls to append_entry() as needed. The referenced CP entries are -// always appended to *merge_cp_p before the referee CP entry. These -// referenced CP entries may already exist in *merge_cp_p in which case -// there is nothing extra to append and only the current entry is -// appended. -void VM_RedefineClasses::append_entry(constantPoolHandle scratch_cp, - int scratch_i, constantPoolHandle *merge_cp_p, int *merge_cp_length_p, - TRAPS) { - - // append is different depending on entry tag type - switch (scratch_cp->tag_at(scratch_i).value()) { - - // 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 - // verifier does not like Class entries in the input constant pool. - // The split-verifier is implemented in the VM so it can optionally - // and directly resolve constant pool entries to load classes. The - // split-verifier can accept either Class entries or UnresolvedClass - // entries in the input constant pool. We revert the appended copy - // back to UnresolvedClass so that either verifier will be happy - // with the constant pool entry. - case JVM_CONSTANT_Class: - { - // revert the copy to JVM_CONSTANT_UnresolvedClass - (*merge_cp_p)->unresolved_klass_at_put(*merge_cp_length_p, - scratch_cp->klass_name_at(scratch_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; - - // these are direct CP entries so they can be directly appended, - // but double and long take two constant pool entries - case JVM_CONSTANT_Double: // fall through - case JVM_CONSTANT_Long: - { - scratch_cp->copy_entry_to(scratch_i, *merge_cp_p, *merge_cp_length_p, - THREAD); - - 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) += 2; - } break; - - // these are direct CP entries so they can be directly appended - case JVM_CONSTANT_Float: // fall through - case JVM_CONSTANT_Integer: // fall through - case JVM_CONSTANT_Utf8: // fall through - - // This was an indirect CP entry, but it has been changed into - // an interned string so this entry can be directly appended. - case JVM_CONSTANT_String: // fall through - - // These were indirect CP entries, but they have been changed into - // symbolOops so these entries can be directly appended. - case JVM_CONSTANT_UnresolvedClass: // fall through - case JVM_CONSTANT_UnresolvedString: - { - scratch_cp->copy_entry_to(scratch_i, *merge_cp_p, *merge_cp_length_p, - THREAD); - - 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_NameAndType: - { - int name_ref_i = scratch_cp->name_ref_index_at(scratch_i); - int new_name_ref_i = 0; - bool match = (name_ref_i < *merge_cp_length_p) && - scratch_cp->compare_entry_to(name_ref_i, *merge_cp_p, name_ref_i, - THREAD); - if (!match) { - // forward reference in *merge_cp_p or not a direct match - - int found_i = scratch_cp->find_matching_entry(name_ref_i, *merge_cp_p, - THREAD); - if (found_i != 0) { - guarantee(found_i != name_ref_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. - new_name_ref_i = found_i; - map_index(scratch_cp, name_ref_i, found_i); - } else { - // no match found so we have to append this entry to *merge_cp_p - append_entry(scratch_cp, name_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_name_ref_i = *merge_cp_length_p - 1; - } - } - - int signature_ref_i = scratch_cp->signature_ref_index_at(scratch_i); - int new_signature_ref_i = 0; - match = (signature_ref_i < *merge_cp_length_p) && - scratch_cp->compare_entry_to(signature_ref_i, *merge_cp_p, - signature_ref_i, THREAD); - if (!match) { - // forward reference in *merge_cp_p or not a direct match - - int found_i = scratch_cp->find_matching_entry(signature_ref_i, - *merge_cp_p, THREAD); - if (found_i != 0) { - guarantee(found_i != signature_ref_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. - new_signature_ref_i = found_i; - map_index(scratch_cp, signature_ref_i, found_i); - } else { - // no match found so we have to append this entry to *merge_cp_p - append_entry(scratch_cp, signature_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_signature_ref_i = *merge_cp_length_p - 1; - } - } - - // If the referenced entries already exist in *merge_cp_p, then - // both new_name_ref_i and new_signature_ref_i will both be 0. - // In that case, all we are appending is the current entry. - if (new_name_ref_i == 0) { - new_name_ref_i = name_ref_i; - } else { - RC_TRACE(0x00080000, - ("NameAndType entry@%d name_ref_index change: %d to %d", - *merge_cp_length_p, name_ref_i, new_name_ref_i)); - } - if (new_signature_ref_i == 0) { - new_signature_ref_i = signature_ref_i; - } else { - RC_TRACE(0x00080000, - ("NameAndType entry@%d signature_ref_index change: %d to %d", - *merge_cp_length_p, signature_ref_i, new_signature_ref_i)); - } - - (*merge_cp_p)->name_and_type_at_put(*merge_cp_length_p, - new_name_ref_i, new_signature_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 a double-indirect CP entry so it needs special handling - case JVM_CONSTANT_Fieldref: // fall through - case JVM_CONSTANT_InterfaceMethodref: // fall through - case JVM_CONSTANT_Methodref: - { - int klass_ref_i = scratch_cp->uncached_klass_ref_index_at(scratch_i); - int new_klass_ref_i = 0; - bool match = (klass_ref_i < *merge_cp_length_p) && - scratch_cp->compare_entry_to(klass_ref_i, *merge_cp_p, klass_ref_i, - THREAD); - if (!match) { - // forward reference in *merge_cp_p or not a direct match - - int found_i = scratch_cp->find_matching_entry(klass_ref_i, *merge_cp_p, - THREAD); - if (found_i != 0) { - guarantee(found_i != klass_ref_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. - new_klass_ref_i = found_i; - map_index(scratch_cp, klass_ref_i, found_i); - } else { - // no match found so we have to append this entry to *merge_cp_p - append_entry(scratch_cp, klass_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. Without the optimization where we - // use JVM_CONSTANT_UnresolvedClass, then up to two entries - // could be appended. - new_klass_ref_i = *merge_cp_length_p - 1; - } - } - - int name_and_type_ref_i = - scratch_cp->uncached_name_and_type_ref_index_at(scratch_i); - int new_name_and_type_ref_i = 0; - match = (name_and_type_ref_i < *merge_cp_length_p) && - scratch_cp->compare_entry_to(name_and_type_ref_i, *merge_cp_p, - name_and_type_ref_i, THREAD); - if (!match) { - // forward reference in *merge_cp_p or not a direct match - - int found_i = scratch_cp->find_matching_entry(name_and_type_ref_i, - *merge_cp_p, THREAD); - if (found_i != 0) { - guarantee(found_i != name_and_type_ref_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. - new_name_and_type_ref_i = found_i; - map_index(scratch_cp, name_and_type_ref_i, found_i); - } else { - // no match found so we have to append this entry to *merge_cp_p - append_entry(scratch_cp, name_and_type_ref_i, merge_cp_p, - merge_cp_length_p, THREAD); - // The above call to append_entry() can append more than - // one entry so the post call query of *merge_cp_length_p - // is required in order to get the right index for the - // JVM_CONSTANT_NameAndType entry. - new_name_and_type_ref_i = *merge_cp_length_p - 1; - } - } - - // If the referenced entries already exist in *merge_cp_p, then - // both new_klass_ref_i and new_name_and_type_ref_i will both be - // 0. In that case, all we are appending is the current entry. - if (new_klass_ref_i == 0) { - new_klass_ref_i = klass_ref_i; - } - if (new_name_and_type_ref_i == 0) { - new_name_and_type_ref_i = name_and_type_ref_i; - } - - const char *entry_name; - switch (scratch_cp->tag_at(scratch_i).value()) { - case JVM_CONSTANT_Fieldref: - entry_name = "Fieldref"; - (*merge_cp_p)->field_at_put(*merge_cp_length_p, new_klass_ref_i, - new_name_and_type_ref_i); - break; - case JVM_CONSTANT_InterfaceMethodref: - entry_name = "IFMethodref"; - (*merge_cp_p)->interface_method_at_put(*merge_cp_length_p, - new_klass_ref_i, new_name_and_type_ref_i); - break; - case JVM_CONSTANT_Methodref: - entry_name = "Methodref"; - (*merge_cp_p)->method_at_put(*merge_cp_length_p, new_klass_ref_i, - new_name_and_type_ref_i); - break; - default: - guarantee(false, "bad switch"); - 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)); - } - 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; - - // At this stage, Class or UnresolvedClass could be here, but not - // ClassIndex - case JVM_CONSTANT_ClassIndex: // fall through - - // Invalid is used as the tag for the second constant pool entry - // occupied by JVM_CONSTANT_Double or JVM_CONSTANT_Long. It should - // not be seen by itself. - case JVM_CONSTANT_Invalid: // fall through - - // At this stage, String or UnresolvedString could be here, but not - // StringIndex - case JVM_CONSTANT_StringIndex: // fall through - - // At this stage JVM_CONSTANT_UnresolvedClassInError should not be - // here - case JVM_CONSTANT_UnresolvedClassInError: // fall through - - default: - { - // leave a breadcrumb - jbyte bad_value = scratch_cp->tag_at(scratch_i).value(); - ShouldNotReachHere(); - } break; - } // end switch tag value -} // end append_entry() - - -void VM_RedefineClasses::swap_all_method_annotations(int i, int j, instanceKlassHandle scratch_class) { - typeArrayOop save; - - save = scratch_class->get_method_annotations_of(i); - scratch_class->set_method_annotations_of(i, scratch_class->get_method_annotations_of(j)); - scratch_class->set_method_annotations_of(j, save); - - save = scratch_class->get_method_parameter_annotations_of(i); - scratch_class->set_method_parameter_annotations_of(i, scratch_class->get_method_parameter_annotations_of(j)); - scratch_class->set_method_parameter_annotations_of(j, save); - - 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); -} - - -jvmtiError VM_RedefineClasses::compare_and_normalize_class_versions( - instanceKlassHandle the_class, - instanceKlassHandle scratch_class) { +jvmtiError VM_RedefineClasses::check_redefinition_allowed(instanceKlassHandle scratch_class) { + + + + // Compatibility mode => check for unsupported modification + + + assert(scratch_class->old_version() != NULL, "must have old version"); + instanceKlassHandle the_class(scratch_class->old_version()); + int i; // Check superclasses, or rather their names, since superclasses themselves can be // requested to replace. // Check for NULL superclass first since this might be java.lang.Object if (the_class->super() != scratch_class->super() && - (the_class->super() == NULL || scratch_class->super() == NULL || - Klass::cast(the_class->super())->name() != - Klass::cast(scratch_class->super())->name())) { - return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED; + (the_class->super() == NULL || scratch_class->super() == NULL || + Klass::cast(the_class->super())->name() != + Klass::cast(scratch_class->super())->name())) { + return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED; } // Check if the number, names and order of directly implemented interfaces are the same. @@ -518,8 +639,8 @@ } for (i = 0; i < n_intfs; i++) { if (Klass::cast((klassOop) k_interfaces->obj_at(i))->name() != - Klass::cast((klassOop) k_new_interfaces->obj_at(i))->name()) { - return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED; + Klass::cast((klassOop) k_new_interfaces->obj_at(i))->name()) { + return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED; } } @@ -554,10 +675,10 @@ } // offset if (k_old_fields->short_at(i + instanceKlass::low_offset) != - k_new_fields->short_at(i + instanceKlass::low_offset) || - k_old_fields->short_at(i + instanceKlass::high_offset) != - k_new_fields->short_at(i + instanceKlass::high_offset)) { - return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_SCHEMA_CHANGED; + k_new_fields->short_at(i + instanceKlass::low_offset) || + k_old_fields->short_at(i + instanceKlass::high_offset) != + k_new_fields->short_at(i + instanceKlass::high_offset)) { + return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_SCHEMA_CHANGED; } // name and signature jshort name_index = k_old_fields->short_at(i + instanceKlass::name_index_offset); @@ -573,6 +694,7 @@ } } + // 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 @@ -674,12 +796,8 @@ idnum_owner->set_method_idnum(new_num); } k_new_method->set_method_idnum(old_num); - swap_all_method_annotations(old_num, new_num, scratch_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; @@ -688,11 +806,11 @@ // 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 - return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_ADDED; + // hack: private should be treated as final, but alas + || (new_flags & (JVM_ACC_FINAL|JVM_ACC_STATIC)) == 0 + ) { + // new methods must be private + return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_ADDED; } { u2 num = the_class->next_method_idnum(); @@ -707,24 +825,19 @@ idnum_owner->set_method_idnum(new_num); } k_new_method->set_method_idnum(num); - swap_all_method_annotations(new_num, num, scratch_class); } - RC_TRACE(0x00008000, ("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 - return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_DELETED; + // hack: private should be treated as final, but alas + || (old_flags & (JVM_ACC_FINAL|JVM_ACC_STATIC)) == 0 + ) { + // deleted methods must be private + return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_DELETED; } - RC_TRACE(0x00008000, ("Method deleted: old: %s [%d]", - k_old_method->name_and_sig_as_C_string(), oi)); ++oi; // advance to next old method break; default: @@ -735,2081 +848,1502 @@ return JVMTI_ERROR_NONE; } - -// 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. -int VM_RedefineClasses::find_new_index(int old_index) { - 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; + + + + TRACE_RC2("Comparing different class versions of class %s", new_class->name()->as_C_string()); + + assert(new_class->old_version() != NULL, "must have old version"); + instanceKlassHandle the_class(new_class->old_version()); + + // Check whether class is in the error init state. + if (the_class->is_in_error_state()) { + // TBD #5057930: special error code is needed in 1.6 + //result = Klass::union_redefinition_level(result, Klass::Invalid); } - if (old_index < 1 || old_index >= _index_map_p->length()) { - // The old_index is out of range so it is not mapped. This should - // not happen in regular constant pool merging use, but it can - // happen if a corrupt annotation is processed. - return 0; + int i; + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + // 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 + + klassOop cur_klass = the_class->super(); + while (cur_klass != NULL) { + if (!new_class->is_subclass_of(cur_klass->klass_part()->newest_version())) { + TRACE_RC2("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()) { + TRACE_RC2("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())) { + TRACE_RC2("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() - - -// Returns true if the current mismatch is due to a resolved/unresolved -// class pair. Otherwise, returns false. -bool VM_RedefineClasses::is_unresolved_class_mismatch(constantPoolHandle cp1, - int index1, constantPoolHandle cp2, int index2) { - - jbyte t1 = cp1->tag_at(index1).value(); - if (t1 != JVM_CONSTANT_Class && t1 != JVM_CONSTANT_UnresolvedClass) { - return false; // wrong entry type; not our special case - } - - jbyte t2 = cp2->tag_at(index2).value(); - if (t2 != JVM_CONSTANT_Class && t2 != JVM_CONSTANT_UnresolvedClass) { - return false; // wrong entry type; not our special case - } - - if (t1 == t2) { - return false; // not a mismatch; not our special case - } - - char *s1 = cp1->klass_name_at(index1)->as_C_string(); - char *s2 = cp2->klass_name_at(index2)->as_C_string(); - if (strcmp(s1, s2) != 0) { - return false; // strings don't match; not our special case - } - - return true; // made it through the gauntlet; this is our special case -} // end is_unresolved_class_mismatch() - - -// Returns true if the current mismatch is due to a resolved/unresolved -// string pair. Otherwise, returns false. -bool VM_RedefineClasses::is_unresolved_string_mismatch(constantPoolHandle cp1, - int index1, constantPoolHandle cp2, int index2) { - - jbyte t1 = cp1->tag_at(index1).value(); - if (t1 != JVM_CONSTANT_String && t1 != JVM_CONSTANT_UnresolvedString) { - return false; // wrong entry type; not our special case - } - - jbyte t2 = cp2->tag_at(index2).value(); - if (t2 != JVM_CONSTANT_String && t2 != JVM_CONSTANT_UnresolvedString) { - return false; // wrong entry type; not our special case - } - - if (t1 == t2) { - return false; // not a mismatch; not our special case - } - - char *s1 = cp1->string_at_noresolve(index1); - char *s2 = cp2->string_at_noresolve(index2); - if (strcmp(s1, s2) != 0) { - return false; // strings don't match; not our special case - } - - return true; // made it through the gauntlet; this is our special case -} // end is_unresolved_string_mismatch() - - -jvmtiError VM_RedefineClasses::load_new_class_versions(TRAPS) { - // For consistency allocate memory using os::malloc wrapper. - _scratch_classes = (instanceKlassHandle *) - os::malloc(sizeof(instanceKlassHandle) * _class_count); - if (_scratch_classes == NULL) { - return JVMTI_ERROR_OUT_OF_MEMORY; - } - - ResourceMark rm(THREAD); - - JvmtiThreadState *state = JvmtiThreadState::state_for(JavaThread::current()); - // state can only be NULL if the current thread is exiting which - // should not happen since we're trying to do a RedefineClasses - guarantee(state != NULL, "exiting thread calling load_new_class_versions"); - for (int i = 0; i < _class_count; i++) { - oop mirror = JNIHandles::resolve_non_null(_class_defs[i].klass); - // classes for primitives cannot be redefined - if (!is_modifiable_class(mirror)) { - return JVMTI_ERROR_UNMODIFIABLE_CLASS; - } - klassOop the_class_oop = java_lang_Class::as_klassOop(mirror); - instanceKlassHandle the_class = instanceKlassHandle(THREAD, the_class_oop); - symbolHandle the_class_sym = symbolHandle(THREAD, the_class->name()); - - // RC_TRACE_WITH_THREAD macro has an embedded ResourceMark - RC_TRACE_WITH_THREAD(0x00000001, THREAD, - ("loading name=%s (avail_mem=" UINT64_FORMAT "K)", - the_class->external_name(), os::available_memory() >> 10)); - - ClassFileStream st((u1*) _class_defs[i].class_bytes, - _class_defs[i].class_byte_count, (char *)"__VM_RedefineClasses__"); - - // Parse the stream. - Handle the_class_loader(THREAD, the_class->class_loader()); - Handle protection_domain(THREAD, the_class->protection_domain()); - // 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); - - klassOop k = SystemDictionary::parse_stream(the_class_sym, - the_class_loader, - protection_domain, - &st, - THREAD); - // Clear class_being_redefined just to be sure. - state->clear_class_being_redefined(); - - // TODO: if this is retransform, and nothing changed we can skip it - - instanceKlassHandle scratch_class (THREAD, k); - - if (HAS_PENDING_EXCEPTION) { - symbolOop ex_name = PENDING_EXCEPTION->klass()->klass_part()->name(); - // RC_TRACE_WITH_THREAD macro has an embedded ResourceMark - RC_TRACE_WITH_THREAD(0x00000002, THREAD, ("parse_stream exception: '%s'", - ex_name->as_C_string())); - 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; + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + // 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; + TRACE_RC2("Removed interface %s", old_interface->name()->as_C_string()); + + if (!old_interface->has_subtype_changed()) { + TRACE_RC2("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()) { - the_class->link_class(THREAD); - if (HAS_PENDING_EXCEPTION) { - symbolOop ex_name = PENDING_EXCEPTION->klass()->klass_part()->name(); - // RC_TRACE_WITH_THREAD macro has an embedded ResourceMark - RC_TRACE_WITH_THREAD(0x00000002, THREAD, ("link_class exception: '%s'", - ex_name->as_C_string())); - CLEAR_PENDING_EXCEPTION; - if (ex_name == vmSymbols::java_lang_OutOfMemoryError()) { - return JVMTI_ERROR_OUT_OF_MEMORY; + } + + // 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; + TRACE_RC2("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. + typeArrayOop k_old_fields = the_class->fields(); + typeArrayOop k_new_fields = new_class->fields(); + int n_fields = k_old_fields->length(); + if (n_fields != k_new_fields->length()) { + result = result | Klass::ModifyInstances; + } else { + for (i = 0; i < n_fields; i += instanceKlass::next_offset) { + // access + old_flags = k_old_fields->ushort_at(i + instanceKlass::access_flags_offset); + new_flags = k_new_fields->ushort_at(i + instanceKlass::access_flags_offset); + if ((old_flags ^ new_flags) & JVM_RECOGNIZED_FIELD_MODIFIERS) { + // (tw) Can this have any effects? + } + // offset + if (k_old_fields->short_at(i + instanceKlass::low_offset) != + k_new_fields->short_at(i + instanceKlass::low_offset) || + k_old_fields->short_at(i + instanceKlass::high_offset) != + k_new_fields->short_at(i + instanceKlass::high_offset)) { + result = result | Klass::ModifyInstances; + } + // name and signature + jshort name_index = k_old_fields->short_at(i + instanceKlass::name_index_offset); + jshort sig_index = k_old_fields->short_at(i +instanceKlass::signature_index_offset); + symbolOop name_sym1 = the_class->constants()->symbol_at(name_index); + symbolOop sig_sym1 = the_class->constants()->symbol_at(sig_index); + name_index = k_new_fields->short_at(i + instanceKlass::name_index_offset); + sig_index = k_new_fields->short_at(i + instanceKlass::signature_index_offset); + symbolOop name_sym2 = new_class->constants()->symbol_at(name_index); + symbolOop sig_sym2 = new_class->constants()->symbol_at(sig_index); + if (name_sym1 != name_sym2 || sig_sym1 != sig_sym2) { + 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; + 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; } } } - // Do the validity checks in compare_and_normalize_class_versions() - // before verifying the byte codes. By doing these checks first, we - // limit the number of functions that require redirection from - // the_class to scratch_class. In particular, we don't have to - // modify JNI GetSuperclass() and thus won't change its performance. - jvmtiError res = compare_and_normalize_class_versions(the_class, - 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 - // the_class to scratch_class in the JVM_* functions called by the - // verifier. Please, refer to jvmtiThreadState.hpp for the detailed - // description. - RedefineVerifyMark rvm(&the_class, &scratch_class, state); - Verifier::verify( - scratch_class, Verifier::ThrowException, true, THREAD); - } - - if (HAS_PENDING_EXCEPTION) { - symbolOop ex_name = PENDING_EXCEPTION->klass()->klass_part()->name(); - // RC_TRACE_WITH_THREAD macro has an embedded ResourceMark - RC_TRACE_WITH_THREAD(0x00000002, THREAD, - ("verify_byte_codes exception: '%s'", ex_name->as_C_string())); - CLEAR_PENDING_EXCEPTION; - if (ex_name == vmSymbols::java_lang_OutOfMemoryError()) { - return JVMTI_ERROR_OUT_OF_MEMORY; - } else { - // tell the caller the bytecodes are bad - return JVMTI_ERROR_FAILS_VERIFICATION; + 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); + TRACE_RC2("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; + TRACE_RC3("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) { - symbolOop ex_name = PENDING_EXCEPTION->klass()->klass_part()->name(); - // RC_TRACE_WITH_THREAD macro has an embedded ResourceMark - RC_TRACE_WITH_THREAD(0x00000002, THREAD, - ("verify_byte_codes post merge-CP exception: '%s'", - ex_name->as_C_string())); - CLEAR_PENDING_EXCEPTION; - if (ex_name == vmSymbols::java_lang_OutOfMemoryError()) { - return JVMTI_ERROR_OUT_OF_MEMORY; - } else { - // tell the caller that constant pool merging screwed up - return JVMTI_ERROR_INTERNAL; + 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); + } + TRACE_RC1("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; + } + TRACE_RC1("Method deleted: old: %s [%d]", + k_old_method->name_and_sig_as_C_string(), oi); + ++oi; // advance to next old method + break; + default: + ShouldNotReachHere(); + } + } + + 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) { + + class UpdateFieldsEvolutionClosure : public FieldEvolutionClosure { + + private: + + GrowableArray info; + int curPosition; + bool copy_backwards; + + public: + + bool does_copy_backwards() { + return copy_backwards; + } + + UpdateFieldsEvolutionClosure(klassOop klass) { + + int base_offset = instanceOopDesc::base_offset_in_bytes(); + + if (klass->klass_part()->newest_version() == SystemDictionary::Class_klass()->klass_part()->newest_version()) { + base_offset += java_lang_Class::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 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 { + 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_TRACE_RC2 { + TRACE_RC2("Instance update information for %s:", new_version->klass_part()->name()->as_C_string()); + if (cl.does_copy_backwards()) { + TRACE_RC2("\tDoes copy backwards!"); + } + for (int i=0; i 0) { + TRACE_RC2("\t%d COPY from %d", curNum, result.at(i + 1)); + i++; + } else { + TRACE_RC2("\tEND"); + } + } + } +} + +void VM_RedefineClasses::update_active_methods() { + + TRACE_RC2("Updating active methods"); + JavaThread *java_thread = Threads::first(); + while (java_thread != NULL) { + + int stack_depth = 0; + if (java_thread->has_last_Java_frame()) { + + TRACE_RC4("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(); + TRACE_RC4("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()) { + + + TRACE_RC2("Found method that should just be updated to the newest version %s", jvf->method()->name_and_sig_as_C_string()); + + IF_TRACE_RC5 { + 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_TRACE_RC5 { + 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); + TRACE_RC2("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::rollback() { + TRACE_RC1("Rolling back redefinition!"); + SystemDictionary::rollback_redefinition(); + + TRACE_RC1("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; + if (klass->klass_part()->new_version() != NULL && klass->klass_part()->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()) { + + klassOop klass_oop = java_lang_Class::as_klassOop(obj); + if (klass_oop != NULL) { + if (klass_oop->klass_part()->new_version() != NULL && klass_oop->klass_part()->new_version()->klass_part()->is_redefining()) { + obj = klass_oop->klass_part()->new_version()->klass_part()->java_mirror(); + oopDesc::encode_store_heap_oop_not_null(p, obj); + } else if (klass_oop->klass_part()->is_redefining()) { + obj = klass_oop->klass_part()->java_mirror(); + oopDesc::encode_store_heap_oop_not_null(p, obj); } } } - - Rewriter::rewrite(scratch_class, THREAD); - if (HAS_PENDING_EXCEPTION) { - symbolOop 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() - - -// 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: - // revert the copy to JVM_CONSTANT_UnresolvedClass - (*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 - old_cp->copy_entry_to(old_i, *merge_cp_p, old_i, CHECK_0); - old_i++; - break; - - default: - // just copy the entry to *merge_cp_p - old_cp->copy_entry_to(old_i, *merge_cp_p, old_i, CHECK_0); - break; - } - } // end for each old_cp entry - - // 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); +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(); + + TRACE_RC1("Entering doit!"); + + assert((_max_redefinition_flags & Klass::RemoveSuperType) == 0, "removing super types not allowed"); + + if (UseSharedSpaces) { + // Sharing is enabled so we remap the shared readonly space to + // shared readwrite, private just in case we need to redefine + // a shared class. We do the remap during the doit() phase of + // the safepoint to be safer. + if (!CompactingPermGenGen::remap_shared_readonly_as_readwrite()) { + TRACE_RC1("failed to remap shared readonly space to readwrite, private"); + _result = JVMTI_ERROR_INTERNAL; + return; } } - 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)); - - 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; + RC_TIMER_START(_timer_prepare_redefinition); + for (int i = 0; i < _new_classes->length(); i++) { + redefine_single_class(_new_classes->at(i), thread); + } + + // Deoptimize all compiled code that depends on this class + flush_dependent_code(instanceKlassHandle(Thread::current(), (klassOop)NULL), Thread::current()); + + // Adjust constantpool caches and vtables for all classes + // that reference methods of the evolved class. + SystemDictionary::classes_do(adjust_cpool_cache, Thread::current()); + + RC_TIMER_STOP(_timer_prepare_redefinition); + RC_TIMER_START(_timer_redefinition); + + class ChangePointersOopClosure : public OopClosure { + virtual void do_oop(oop* o) { + do_oop_work(o); } - 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; + virtual void do_oop(narrowOop* o) { + do_oop_work(o); } - - // 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); + }; + + class ChangePointersObjectClosure : public ObjectClosure { + + private: + + OopClosure *_closure; + bool _needs_instance_update; + GrowableArray *_updated_oops; + + public: + ChangePointersObjectClosure(OopClosure *closure) : _closure(closure), _needs_instance_update(false), _updated_oops(NULL) {} + + bool needs_instance_update() { + return _needs_instance_update; + } + + GrowableArray *updated_oops() { return _updated_oops; } + + virtual void do_object(oop obj) { + if (!obj->is_instanceKlass()) { + obj->oop_iterate(_closure); + + if (obj->blueprint()->is_redefining()) { + + if (obj->blueprint()->check_redefinition_flag(Klass::HasInstanceTransformer)) { + if (_updated_oops == NULL) { + _updated_oops = new (ResourceObj::C_HEAP) GrowableArray(100, true); + } + _updated_oops->append(obj); + } + + if(obj->blueprint()->update_information() != NULL || obj->is_perm()) { + + assert(obj->blueprint()->old_version() != NULL, "must have old version"); + obj->set_klass_no_check(obj->blueprint()->old_version()); + + if (obj->size() != obj->size_given_klass(obj->blueprint()->new_version()->klass_part()) || obj->is_perm()) { + // We need an instance update => set back to old klass + _needs_instance_update = true; + + } else { + MarkSweep::update_fields(obj, obj); + assert(obj->blueprint()->is_redefining(), "update fields resets the klass"); + } + } + } + + } else { + instanceKlass *klass = instanceKlass::cast((klassOop)obj); + if (klass->is_redefining()) { + // Initialize the new class! Special static initialization that does not execute the + // static constructor but copies static field values from the old class if name + // and signature of a static field match. + klass->initialize_redefined_class(); + } + klass->iterate_static_fields(_closure); + } + } + }; + + ChangePointersOopClosure oopClosure; + ChangePointersObjectClosure objectClosure(&oopClosure); + + { + SharedHeap::heap()->gc_prologue(true); + Universe::root_oops_do(&oopClosure); + Universe::heap()->object_iterate(&objectClosure); + SharedHeap::heap()->gc_epilogue(false); } - 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)); + // 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()->klass_part()->java_mirror()); + ((instanceKlass*)_new_classes->at(i)->old_version()->klass_part())->constants()->set_pool_holder(_new_classes->at(i)->old_version()); + } + + _updated_oops = objectClosure.updated_oops(); + + if (objectClosure.needs_instance_update()){ + + // Do a full garbage collection to update the instance sizes accordingly + TRACE_RC1("Before performing full GC!"); + Universe::set_redefining_gc_run(true); + JvmtiGCFullMarker jgcm; + notify_gc_begin(true); + Universe::heap()->collect_as_vm_thread(GCCause::_heap_inspection); + notify_gc_end(); + Universe::set_redefining_gc_run(false); + TRACE_RC1("GC done!"); } - 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 because, even if - // 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, - methodOopDesc::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()); + + IF_TRACE_RC1 { + if (_updated_oops != NULL) { + TRACE_RC1("%d object(s) updated!", _updated_oops->length()); + } else { + TRACE_RC1("No objects updated!"); + } } - ResourceMark rm(THREAD); - _index_map_count = 0; - _index_map_p = new intArray(scratch_cp->length(), -1); - - bool result = merge_constant_pools(old_cp, scratch_cp, &merge_cp, - &merge_cp_length, THREAD); - if (!result) { - // The merge can fail due to memory allocation failure or due - // to robustness checks. - return JVMTI_ERROR_INTERNAL; + // Unmark klassOops as "redefining" + for (int i=0; i<_new_classes->length(); i++) { + klassOop cur = _new_classes->at(i)(); + _new_classes->at(i)->set_redefining(false); + _new_classes->at(i)->clear_update_information(); + _new_classes->at(i)->update_supers_to_newest_version(); + + if (((instanceKlass *)cur->klass_part()->old_version()->klass_part())->array_klasses() != NULL) { + update_array_classes_to_newest_version(((instanceKlass *)cur->klass_part()->old_version()->klass_part())->array_klasses()); + + // Transfer the array classes, otherwise we might get cast exceptions when casting array types. + ((instanceKlass*)cur->klass_part())->set_array_klasses(((instanceKlass*)cur->klass_part()->old_version()->klass_part())->array_klasses()); + + oop new_mirror = _new_classes->at(i)->java_mirror(); + oop old_mirror = _new_classes->at(i)->old_version()->klass_part()->java_mirror(); + java_lang_Class::set_array_klass(new_mirror, java_lang_Class::array_klass(old_mirror)); + } } - 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 - - 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. - - merge_cp()->set_is_conc_safe(true); - merge_cp = constantPoolHandle(); // toss the merged constant pool + for (int i=T_BOOLEAN; i<=T_LONG; i++) { + update_array_classes_to_newest_version(Universe::typeArrayKlassObj((BasicType)i)); + } + + // 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(); + + // Clean up caches in the compiler interface and compiler threads + CompileBroker::cleanup_after_redefinition(); + +#ifdef ASSERT + + // Universe::verify(); + // JNIHandles::verify(); + + SystemDictionary::classes_do(check_class, thread); +#endif + + update_active_methods(); + RC_TIMER_STOP(_timer_redefinition); + +} + +void VM_RedefineClasses::update_array_classes_to_newest_version(klassOop smallest_dimension) { + + arrayKlass *curArrayKlass = arrayKlass::cast(smallest_dimension); + assert(curArrayKlass->lower_dimension() == NULL, "argument must be smallest dimension"); + + + while (curArrayKlass != NULL) { + klassOop higher_dimension = curArrayKlass->higher_dimension(); + klassOop lower_dimension = curArrayKlass->lower_dimension(); + curArrayKlass->update_supers_to_newest_version(); + + curArrayKlass = NULL; + if (higher_dimension != NULL) { + curArrayKlass = arrayKlass::cast(higher_dimension); + } + } + +} + +void VM_RedefineClasses::doit_epilogue() { + + RC_TIMER_START(_timer_vm_op_epilogue); + + unlock_threads(); + + ResourceMark mark; + + VM_GC_Operation::doit_epilogue(); + TRACE_RC1("GC Operation epilogue finished! "); + + GrowableArray instanceTransformerMethods; + + // Call static transformers + for (int i=0; i<_new_classes->length(); i++) { + + instanceKlassHandle klass = _new_classes->at(i); + + // Find instance transformer method + + if (klass->check_redefinition_flag(Klass::HasInstanceTransformer)) { + + TRACE_RC5("Call instance transformer of %s instance", klass->name()->as_C_string()); + klassOop cur_klass = klass(); + while (cur_klass != NULL) { + methodOop method = ((instanceKlass*)cur_klass->klass_part())->find_method(vmSymbols::transformer_name(), vmSymbols::void_method_signature()); + if (method != NULL) { + methodHandle instanceTransformerMethod(method); + instanceTransformerMethods.append(instanceTransformerMethod); + break; + } else { + cur_klass = cur_klass->klass_part()->super(); + } + } + assert(cur_klass != NULL, "must have instance transformer method"); } 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. - - // 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, true, - THREAD); - // drop local ref to the merged constant pool - merge_cp()->set_is_conc_safe(true); - merge_cp = constantPoolHandle(); + instanceTransformerMethods.append(methodHandle(Thread::current(), NULL)); } - } 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++; + } + + + // Call instance transformers + if (_updated_oops != NULL) { + + for (int i=0; i<_updated_oops->length(); i++) { + assert(_updated_oops->at(i) != NULL, "must not be null!"); + Handle cur(_updated_oops->at(i)); + instanceKlassHandle klass(cur->klass()); + + if (klass->check_redefinition_flag(Klass::HasInstanceTransformer)) { + + methodHandle method = instanceTransformerMethods.at(klass->redefinition_index()); + + TRACE_RC5("executing transformer method"); + + Thread *__the_thread__ = Thread::current(); + JavaValue result(T_VOID); + JavaCallArguments args(cur); + JavaCalls::call(&result, + method, + &args, + THREAD); + + // TODO: What to do with an exception here? + if (HAS_PENDING_EXCEPTION) { + symbolOop ex_name = PENDING_EXCEPTION->klass()->klass_part()->name(); + TRACE_RC2("exception when executing transformer: '%s'", ex_name->as_C_string()); + CLEAR_PENDING_EXCEPTION; } } } - // 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; - } - - // 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, true, - THREAD); - merge_cp()->set_is_conc_safe(true); + delete _updated_oops; + _updated_oops = NULL; } - 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 + + // Free the array of scratch classes + delete _new_classes; + _new_classes = NULL; + TRACE_RC1("Redefinition finished!"); + + RC_TIMER_STOP(_timer_vm_op_epilogue); +} + +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; } - - // rewrite constant pool references in the class_annotations: - if (!rewrite_cp_refs_in_class_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; } - - // 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()); - } - } - 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(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::_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; +#ifdef ASSERT + +void VM_RedefineClasses::verify_classes(klassOop k_oop_latest, oop initiating_loader, TRAPS) { + klassOop k_oop = k_oop_latest; + while (k_oop != NULL) { + + instanceKlassHandle k_handle(THREAD, k_oop); + Verifier::verify(k_handle, Verifier::ThrowException, true, true, THREAD); + k_oop = k_oop->klass_part()->old_version(); } - - 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, bool shrink, TRAPS) { - assert(!shrink || scratch_cp->length() >= scratch_cp_length, "sanity check"); - - if (shrink) { - // 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, - methodOopDesc::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); - 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_cp->set_pool_holder(scratch_class()); - - // attach klass to new constant pool - scratch_class->set_constants(scratch_cp()); - - int i; // for portability - - // update each field in klass to use new constant pool indices as needed - typeArrayHandle fields(THREAD, scratch_class->fields()); - int n_fields = fields->length(); - for (i = 0; i < n_fields; i += instanceKlass::next_offset) { - jshort cur_index = fields->short_at(i + instanceKlass::name_index_offset); - 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)); - fields->short_at_put(i + instanceKlass::name_index_offset, new_index); - } - cur_index = fields->short_at(i + instanceKlass::signature_index_offset); - 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)); - fields->short_at_put(i + instanceKlass::signature_index_offset, - new_index); - } - cur_index = fields->short_at(i + instanceKlass::initval_index_offset); - 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)); - fields->short_at_put(i + instanceKlass::initval_index_offset, new_index); - } - cur_index = fields->short_at(i + instanceKlass::generic_signature_offset); - 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)); - fields->short_at_put(i + instanceKlass::generic_signature_offset, - 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) - typeArrayOop inner_class_list = scratch_class->inner_classes(); - int icl_length = (inner_class_list == NULL) ? 0 : inner_class_list->length(); - if (icl_length > 0) { - typeArrayHandle inner_class_list_h(THREAD, inner_class_list); - for (int i = 0; i < icl_length; - i += instanceKlass::inner_class_next_offset) { - int cur_index = inner_class_list_h->ushort_at(i - + instanceKlass::inner_class_inner_class_info_offset); - 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)); - inner_class_list_h->ushort_at_put(i - + instanceKlass::inner_class_inner_class_info_offset, new_index); - } - cur_index = inner_class_list_h->ushort_at(i - + instanceKlass::inner_class_outer_class_info_offset); - 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)); - inner_class_list_h->ushort_at_put(i - + instanceKlass::inner_class_outer_class_info_offset, new_index); - } - cur_index = inner_class_list_h->ushort_at(i - + instanceKlass::inner_class_inner_name_offset); - 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)); - inner_class_list_h->ushort_at_put(i - + instanceKlass::inner_class_inner_name_offset, new_index); - } - } // end for each inner class - } // end if we have inner classes - - // 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) - const int beg_bci_offset = 0; - const int end_bci_offset = 1; - const int handler_bci_offset = 2; - const int klass_index_offset = 3; - const int entry_size = 4; - - typeArrayHandle ex_table (THREAD, method->exception_table()); - int ext_length = ex_table->length(); - assert(ext_length % entry_size == 0, "exception table format has changed"); - - for (int j = 0; j < ext_length; j += entry_size) { - int cur_index = ex_table->int_at(j + klass_index_offset); - 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->int_at_put(j + klass_index_offset, 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 +#endif + + +// 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 -// 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. -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) { + //tty->print_cr("name=%s", k_oop->klass_part()->name()->as_C_string()); +/* + methodOop *matching_old_methods = NEW_RESOURCE_ARRAY(methodOop, _old_methods->length()); + methodOop *matching_new_methods = NEW_RESOURCE_ARRAY(methodOop, _old_methods->length()); + + for (int i=0; i<_matching_methods_length; i++) { + matching_old_methods[i] = (methodOop)_old_methods->obj_at(_matching_old_methods[i]); + matching_new_methods[i] = (methodOop)_new_methods->obj_at(_matching_new_methods[i]); + }*/ + + Klass *k = k_oop->klass_part(); + if (k->oop_is_instance()) { + HandleMark hm(THREAD); + instanceKlass *ik = (instanceKlass *) k; + + constantPoolHandle other_cp; + constantPoolCacheOop cp_cache; + + other_cp = constantPoolHandle(ik->constants()); + + for (int i=0; ilength(); i++) { + if (other_cp->tag_at(i).is_klass()) { + klassOop klass = other_cp->klass_at(i, THREAD); + if (klass->klass_part()->new_version() != NULL) { + + // (tw) TODO: check why/if this is necessary + other_cp->klass_at_put(i, klass->klass_part()->new_version()); + } + klass = other_cp->klass_at(i, THREAD); + assert(klass->klass_part()->new_version() == NULL, "Must be new klass!"); + } + } + + cp_cache = other_cp->cache(); + + if (cp_cache != NULL) { + cp_cache->adjust_entries(NULL, + NULL, + 0); + } + } + 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 = (methodOop)_old_methods->obj_at(_matching_old_methods[j]); + TRACE_RC3("matching method %s", old_method->name_and_sig_as_C_string()); + + 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((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])); + jmethodID new_jmethod_id = JNIHandles::make_jmethod_id(old_method_h); + bool result = instanceKlass::cast(old_method_h->method_holder())->update_jmethod_id(old_method_h(), new_jmethod_id); + //TRACE_RC3("Changed jmethodID for old method assigned to %d / result=%d", new_jmethod_id, result); + //TRACE_RC3("jmethodID new method: %d jmethodID old method: %d", new_method_h->jmethod_id(), old_method->jmethod_id()); + } else { + jmethodID mid = new_method_h->jmethod_id(); + bool result = instanceKlass::cast(new_method_h->method_holder())->update_jmethod_id(new_method_h(), jmid); + //TRACE_RC3("Changed jmethodID for new method assigned to %d / result=%d", jmid, result); + + } + JNIHandles::change_method_associated_with_jmethod_id(jmid, new_method_h); + //TRACE_RC3("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!"); + + //TRACE_RC3("jmethodID new method: %d jmethodID old method: %d", new_method_h->jmethod_id(), old_method->jmethod_id()); + } + } +} + + +// Deoptimize all compiled code that depends on this class. +// +// If the can_redefine_classes capability is obtained in the onload +// phase then the compiler has recorded all dependencies from startup. +// In that case we need only deoptimize and throw away all compiled code +// that depends on the class. +// +// If can_redefine_classes is obtained sometime after the onload +// phase then the dependency information may be incomplete. In that case +// the first call to RedefineClasses causes all compiled code to be +// thrown away. As can_redefine_classes has been obtained then +// all future compilations will record dependencies so second and +// subsequent calls to RedefineClasses need only throw away code +// that depends on the class. +// +void VM_RedefineClasses::flush_dependent_code(instanceKlassHandle k_h, TRAPS) { + assert_locked_or_safepoint(Compile_lock); + + // All dependencies have been recorded from startup or this is a second or + // subsequent use of RedefineClasses + + // 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(); + + ResourceMark rm(THREAD); + DeoptimizationMarker dm; + + // Deoptimize all activations depending on marked nmethods + Deoptimization::deoptimize_dependents(); + + // Make the dependent methods not entrant (in VM_Deoptimize they are made zombies) + CodeCache::make_marked_nmethods_not_entrant(); + + // From now on we know that the dependency information is complete + JvmtiExport::set_all_dependencies_are_recorded(true); + } +} + +void VM_RedefineClasses::compute_added_deleted_matching_methods() { + methodOop old_method; + methodOop new_method; + + _matching_old_methods = NEW_RESOURCE_ARRAY(int, _old_methods->length()); + _matching_new_methods = NEW_RESOURCE_ARRAY(int, _old_methods->length()); + _added_methods = NEW_RESOURCE_ARRAY(int, _new_methods->length()); + _deleted_methods = NEW_RESOURCE_ARRAY(int, _old_methods->length()); + + _matching_methods_length = 0; + _deleted_methods_length = 0; + _added_methods_length = 0; + + int nj = 0; + int oj = 0; + while (true) { + 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; + ++nj; + } else if (nj >= _new_methods->length()) { + // Old method, at the end, is deleted + old_method = (methodOop) _old_methods->obj_at(oj); + _deleted_methods[_deleted_methods_length++] = oj; + ++oj; + } else { + old_method = (methodOop) _old_methods->obj_at(oj); + new_method = (methodOop) _new_methods->obj_at(nj); + if (old_method->name() == new_method->name()) { + if (old_method->signature() == new_method->signature()) { + _matching_old_methods[_matching_methods_length ] = oj;//old_method; + _matching_new_methods[_matching_methods_length++] = nj;//new_method; + ++nj; + ++oj; + } else { + // added overloaded have already been moved to the end, + // so this is a deleted overloaded method + _deleted_methods[_deleted_methods_length++] = oj;//old_method; + ++oj; + } + } else { // names don't match + if (old_method->name()->fast_compare(new_method->name()) > 0) { + // new method + _added_methods[_added_methods_length++] = nj;//new_method; + ++nj; + } else { + // deleted method + _deleted_methods[_deleted_methods_length++] = oj;//old_method; + ++oj; + } + } + } + } + assert(_matching_methods_length + _deleted_methods_length == _old_methods->length(), "sanity"); + assert(_matching_methods_length + _added_methods_length == _new_methods->length(), "sanity"); + TRACE_RC3("Matching methods = %d / deleted methods = %d / added methods = %d", _matching_methods_length, _deleted_methods_length, _added_methods_length); +} + + + +// Install the redefinition of a class: +// - house keeping (flushing breakpoints and caches, deoptimizing +// dependent compiled code) +// - adjusting constant pool caches and vtables in other classes +void VM_RedefineClasses::redefine_single_class(instanceKlassHandle the_new_class, TRAPS) { + + ResourceMark rm(THREAD); + + assert(the_new_class->old_version() != NULL, "Must not be null"); + assert(the_new_class->old_version()->klass_part()->new_version() == the_new_class(), "Must equal"); + + instanceKlassHandle the_old_class = instanceKlassHandle(THREAD, the_new_class->old_version()); + +#ifndef JVMTI_KERNEL + // Remove all breakpoints in methods of this class + JvmtiBreakpoints& jvmti_breakpoints = JvmtiCurrentBreakpoints::get_jvmti_breakpoints(); + jvmti_breakpoints.clearall_in_class_at_safepoint(the_old_class()); +#endif // !JVMTI_KERNEL + + if (the_old_class() == Universe::reflect_invoke_cache()->klass()) { + // We are redefining java.lang.reflect.Method. Method.invoke() is + // cached and users of the cache care about each active version of + // the method so we have to track this previous version. + // Do this before methods get switched + Universe::reflect_invoke_cache()->add_previous_version( + the_old_class->method_with_idnum(Universe::reflect_invoke_cache()->method_idnum())); + } + + _old_methods = the_old_class->methods(); + _new_methods = the_new_class->methods(); + _the_class_oop = the_old_class(); + compute_added_deleted_matching_methods(); + + // track which methods are EMCP for add_previous_version() call below + + // (tw) TODO: Check if we need the concept of EMCP? + BitMap emcp_methods(_old_methods->length()); + int emcp_method_count = 0; + emcp_methods.clear(); // clears 0..(length() - 1) + + // We need to mark methods as old!! + check_methods_and_mark_as_obsolete(&emcp_methods, &emcp_method_count); + update_jmethod_ids(); + + // keep track of previous versions of this class + the_new_class->add_previous_version(the_old_class, &emcp_methods, + emcp_method_count); + + // TODO: + transfer_old_native_function_registrations(the_old_class); + + + +#ifdef ASSERT + +// klassOop systemLookup1 = SystemDictionary::resolve_or_null(the_old_class->name(), the_old_class->class_loader(), the_old_class->protection_domain(), THREAD); +// assert(systemLookup1 == the_new_class(), "New class must be in system dictionary!"); + + //JNIHandles::verify(); + +// klassOop systemLookup = SystemDictionary::resolve_or_null(the_old_class->name(), the_old_class->class_loader(), the_old_class->protection_domain(), THREAD); + +// assert(systemLookup == the_new_class(), "New class must be in system dictionary!"); + assert(the_new_class->old_version() != NULL, "Must not be null"); + assert(the_new_class->old_version()->klass_part()->new_version() == the_new_class(), "Must equal"); + + for (int i=0; imethods()->length(); i++) { + assert(((methodOop)the_new_class->methods()->obj_at(i))->method_holder() == the_new_class(), "method holder must match!"); + } + + _old_methods->verify(); + _new_methods->verify(); + + the_new_class->vtable()->verify(tty); + the_old_class->vtable()->verify(tty); + +#endif + + // increment the classRedefinedCount field in the_class and in any + // direct and indirect subclasses of the_class + increment_class_counter((instanceKlass *)the_old_class()->klass_part(), THREAD); + +} + + +void VM_RedefineClasses::check_methods_and_mark_as_obsolete(BitMap *emcp_methods, int * emcp_method_count_p) { + TRACE_RC3("Checking matching methods for EMCP"); + *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 = (methodOop)_old_methods->obj_at(_matching_old_methods[j]); + methodOop new_method = (methodOop)_new_methods->obj_at(_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; + } + + 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. + + // 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. + + old_method->set_new_version(new_method); + new_method->set_old_version(old_method); + + TRACE_RC3("Found EMCP method %s", old_method->name_and_sig_as_C_string()); + + // Transfer breakpoints + instanceKlass *ik = instanceKlass::cast(old_method->method_holder()); + for (BreakpointInfo* bp = ik->breakpoints(); bp != NULL; bp = bp->next()) { + TRACE_RC2("Checking breakpoint"); + TRACE_RC2("%d / %d", bp->match(old_method), bp->match(new_method)); + if (bp->match(old_method)) { + assert(bp->match(new_method), "if old method is method, then new method must match too"); + TRACE_RC2("Found a breakpoint in an old EMCP method"); + new_method->set_breakpoint(bp->bci()); + } + } + + + + } else { + // mark obsolete methods as such + 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. + TRACE_RC3("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 = (methodOop)_old_methods->obj_at(_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. + TRACE_RC3("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"); + TRACE_RC3("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. +void VM_RedefineClasses::increment_class_counter(instanceKlass *ik, TRAPS) { + oop class_mirror = ik->java_mirror(); + klassOop class_oop = java_lang_Class::as_klassOop(class_mirror); + int new_count = java_lang_Class::classRedefinedCount(class_mirror) + 1; + java_lang_Class::set_classRedefinedCount(class_mirror, new_count); + TRACE_RC3("updated count for class=%s to %d", ik->external_name(), new_count); +} + +#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; - - // 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); - 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()) { + TRACE_RC1("size of class: %d\n", k_oop->size()); + TRACE_RC1("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 ) +{ + assert(original_klasses != NULL && result != NULL, ""); + this->_original_klasses = original_klasses; + this->_result = result; + SystemDictionary::classes_do(this); +} + +void VM_RedefineClasses::FindAffectedKlassesClosure::do_object( oop obj ) +{ + klassOop klass = (klassOop)obj; + assert(!_result->contains(klass), "must not occur more than once!"); + assert(klass->klass_part()->new_version() == NULL, "Only last version is valid entry in system dictionary"); + + for(int i=0; i<_original_klasses->length(); i++) { + instanceKlassHandle cur = _original_klasses->at(i); + if (cur() != klass && klass->klass_part()->is_subtype_of(cur()) && !_original_klasses->contains(klass)) { + TRACE_RC3("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 + } +} + +jvmtiError VM_RedefineClasses::do_topological_class_sorting( const jvmtiClassDefinition *class_defs, int class_count, GrowableArray *affected, GrowableArray *arr, TRAPS) +{ + GrowableArray< Pair > *links = new GrowableArray< Pair >(); + + for (int i=0; iclass_loader()); + Handle protection_domain(THREAD, the_class->protection_domain()); + + ClassFileStream st((u1*) class_defs[i].class_bytes, + class_defs[i].class_byte_count, (char *)"__VM_RedefineClasses__"); + ClassFileParser cfp(&st); + + GrowableArray symbolArr; + TRACE_RC2("Before find super symbols of class %s", the_class->name()->as_C_string()); + cfp.findSuperSymbols(the_class->name(), the_class_loader, protection_domain, the_class, symbolArr, THREAD); + + for (int j=0; jas_C_string()); + + for (int k=0; klength(); k++) { + klassOop curOop = arr->at(k)(); + // (tw) TODO: Check if we get aliasing problems with different class loaders? + if (curOop->klass_part()->name() == sym() /*&& curOop->klass_part()->class_loader() == the_class_loader()*/) { + TRACE_RC2("Found class to link"); + 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())); + + + TRACE_RC1("Identified links between classes! "); + + for (int i=0; ilength(); i++) { + + instanceKlassHandle klass = affected->at(i); + + klassOop superKlass = klass->super(); + if (affected->contains(superKlass)) { + links->append(Pair(superKlass, klass())); + } + + objArrayOop superInterfaces = klass->local_interfaces(); + for (int j=0; jlength(); j++) { + klassOop interfaceKlass = (klassOop)superInterfaces->obj_at(j); + if (arr->contains(interfaceKlass)) { + 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_TRACE_RC2 { + TRACE_RC2("Identified links: "); + for (int i=0; ilength(); i++) { + TRACE_RC2("%s to %s", links->at(i).left()->klass_part()->name()->as_C_string(), + links->at(i).right()->klass_part()->name()->as_C_string()); + } + } + + for (int i=0; ilength(); i++) { + + int j; + for (j=i; jlength(); j++) { + + int k; + for (k=0; klength(); k++) { + + klassOop k1 = links->adr_at(k)->right(); + klassOop k2 = arr->at(j)(); + if (k1 == k2) { + break; + } + } + + if (k == links->length()) { + break; + } + } + + if (j == arr->length()) { + // circle detected + return JVMTI_ERROR_CIRCULAR_CLASS_DEFINITION; + } + + for (int k=0; klength(); k++) { + if (links->adr_at(k)->left() == arr->at(j)()) { + links->at_put(k, links->at(links->length() - 1)); + links->remove_at(links->length() - 1); + k--; + } + } + + instanceKlassHandle tmp = arr->at(j); + arr->at_put(j, arr->at(i)); + arr->at_put(i, tmp); + } + + return JVMTI_ERROR_NONE; } +void VM_RedefineClasses::oops_do(OopClosure *closure) { + + if (_updated_oops != NULL) { + for (int i=0; i<_updated_oops->length(); i++) { + closure->do_oop(_updated_oops->adr_at(i)); + } + } +} + +void VM_RedefineClasses::transfer_special_access_flags(fieldDescriptor *from, fieldDescriptor *to) { + to->set_is_field_modification_watched(from->is_field_modification_watched()); + to->set_is_field_access_watched(from->is_field_access_watched()); + if (from->is_field_modification_watched() || from->is_field_access_watched()) { + TRACE_RC2("Transfered watch for field %s", from->name()->as_C_string()); + } + update_klass_field_access_flag(to); +} + +void VM_RedefineClasses::update_klass_field_access_flag(fieldDescriptor *fd) { + instanceKlass* ik = instanceKlass::cast(fd->field_holder()); + typeArrayOop fields = ik->fields(); + fields->ushort_at_put(fd->index(), (jushort)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 @@ -2831,7 +2365,7 @@ // Same, caused by prefix removal only 3_2_1_m -> 3_2_m // class TransferNativeFunctionRegistration { - private: +private: instanceKlassHandle the_class; int prefix_count; char** prefixes; @@ -2844,42 +2378,42 @@ // (2) with the prefix. // where 'prefix' is the prefix at that 'depth' (first prefix, second prefix,...) methodOop search_prefix_name_space(int depth, char* name_str, size_t name_len, - symbolOop signature) { - symbolOop name_symbol = SymbolTable::probe(name_str, (int)name_len); - if (name_symbol != NULL) { - methodOop method = Klass::cast(the_class())->lookup_method(name_symbol, signature); - if (method != NULL) { - // Even if prefixed, intermediate methods must exist. - if (method->is_native()) { - // 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 + symbolOop signature) { + symbolOop name_symbol = SymbolTable::probe(name_str, (int)name_len); + if (name_symbol != NULL) { + methodOop method = Klass::cast(the_class()->klass_part()->new_version())->lookup_method(name_symbol, signature); + if (method != NULL) { + // Even if prefixed, intermediate methods must exist. + if (method->is_native()) { + // Wahoo, we found a (possibly prefixed) version of the method, return it. + return method; } - - // Try adding this prefix to the method name and see if it matches - // another method name. - char* prefix = prefixes[depth]; - size_t prefix_len = strlen(prefix); - size_t trial_len = name_len + prefix_len; - char* trial_name_str = NEW_RESOURCE_ARRAY(char, trial_len + 1); - strcpy(trial_name_str, prefix); - strcat(trial_name_str, name_str); - method = search_prefix_name_space(depth+1, trial_name_str, trial_len, - signature); - if (method != NULL) { - // 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]; + size_t prefix_len = strlen(prefix); + size_t trial_len = name_len + prefix_len; + char* trial_name_str = NEW_RESOURCE_ARRAY(char, trial_len + 1); + strcpy(trial_name_str, prefix); + strcat(trial_name_str, name_str); + method = search_prefix_name_space(depth+1, trial_name_str, trial_len, + signature); + if (method != NULL) { + // If found along this branch, it was prefixed, mark as such + method->set_is_prefixed_native(); + return method; // found + } } } } - } - return NULL; // This whole branch bore nothing + return NULL; // This whole branch bore nothing } // Return the method name with old prefixes stripped away. @@ -2904,10 +2438,10 @@ ResourceMark rm; char* name_str = method_name_without_prefixes(method); return search_prefix_name_space(0, name_str, strlen(name_str), - method->signature()); + method->signature()); } - public: +public: // Construct a native method transfer processor for this class. TransferNativeFunctionRegistration(instanceKlassHandle _the_class) { @@ -2918,9 +2452,9 @@ } // Attempt to transfer any of the old or deleted methods that are native - void transfer_registrations(methodOop* old_methods, int methods_length) { + void transfer_registrations(instanceKlassHandle old_klass, int* old_methods, int methods_length) { for (int j = 0; j < methods_length; j++) { - methodOop old_method = old_methods[j]; + methodOop old_method = (methodOop)old_klass->methods()->obj_at(old_methods[j]); if (old_method->is_native() && old_method->has_native_function()) { methodOop new_method = strip_and_search_for_new_native(old_method); @@ -2929,7 +2463,9 @@ // Redefine does not send events (except CFLH), certainly not this // behind the scenes re-registration. new_method->set_native_function(old_method->native_function(), - !methodOopDesc::native_bind_event_is_interesting); + !methodOopDesc::native_bind_event_is_interesting); + + TRACE_RC3("Transfering native function for method %s", old_method->name()->as_C_string()); } } } @@ -2937,481 +2473,8 @@ }; // Don't lose the association between a native method and its JNI function. -void VM_RedefineClasses::transfer_old_native_function_registrations(instanceKlassHandle the_class) { - 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. -// -// If the can_redefine_classes capability is obtained in the onload -// phase then the compiler has recorded all dependencies from startup. -// In that case we need only deoptimize and throw away all compiled code -// that depends on the class. -// -// If can_redefine_classes is obtained sometime after the onload -// phase then the dependency information may be incomplete. In that case -// the first call to RedefineClasses causes all compiled code to be -// thrown away. As can_redefine_classes has been obtained then -// all future compilations will record dependencies so second and -// subsequent calls to RedefineClasses need only throw away code -// that depends on the class. -// -void VM_RedefineClasses::flush_dependent_code(instanceKlassHandle k_h, TRAPS) { - assert_locked_or_safepoint(Compile_lock); - - // All dependencies have been recorded from startup or this is a second or - // subsequent use of RedefineClasses - if (JvmtiExport::all_dependencies_are_recorded()) { - Universe::flush_evol_dependents_on(k_h); - } else { - CodeCache::mark_all_nmethods_for_deoptimization(); - - ResourceMark rm(THREAD); - DeoptimizationMarker dm; - - // Deoptimize all activations depending on marked nmethods - Deoptimization::deoptimize_dependents(); - - // Make the dependent methods not entrant (in VM_Deoptimize they are made zombies) - CodeCache::make_marked_nmethods_not_entrant(); - - // From now on we know that the dependency information is complete - JvmtiExport::set_all_dependencies_are_recorded(true); - } -} - -void VM_RedefineClasses::compute_added_deleted_matching_methods() { - methodOop old_method; - methodOop new_method; - - _matching_old_methods = NEW_RESOURCE_ARRAY(methodOop, _old_methods->length()); - _matching_new_methods = NEW_RESOURCE_ARRAY(methodOop, _old_methods->length()); - _added_methods = NEW_RESOURCE_ARRAY(methodOop, _new_methods->length()); - _deleted_methods = NEW_RESOURCE_ARRAY(methodOop, _old_methods->length()); - - _matching_methods_length = 0; - _deleted_methods_length = 0; - _added_methods_length = 0; - - int nj = 0; - int oj = 0; - while (true) { - 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++] = new_method; - ++nj; - } else if (nj >= _new_methods->length()) { - // Old method, at the end, is deleted - old_method = (methodOop) _old_methods->obj_at(oj); - _deleted_methods[_deleted_methods_length++] = old_method; - ++oj; - } else { - old_method = (methodOop) _old_methods->obj_at(oj); - new_method = (methodOop) _new_methods->obj_at(nj); - if (old_method->name() == new_method->name()) { - if (old_method->signature() == new_method->signature()) { - _matching_old_methods[_matching_methods_length ] = old_method; - _matching_new_methods[_matching_methods_length++] = new_method; - ++nj; - ++oj; - } else { - // added overloaded have already been moved to the end, - // so this is a deleted overloaded method - _deleted_methods[_deleted_methods_length++] = old_method; - ++oj; - } - } else { // names don't match - if (old_method->name()->fast_compare(new_method->name()) > 0) { - // new method - _added_methods[_added_methods_length++] = new_method; - ++nj; - } else { - // deleted method - _deleted_methods[_deleted_methods_length++] = old_method; - ++oj; - } - } - } - } - assert(_matching_methods_length + _deleted_methods_length == _old_methods->length(), "sanity"); - assert(_matching_methods_length + _added_methods_length == _new_methods->length(), "sanity"); -} - - - -// Install the redefinition of a class: -// - house keeping (flushing breakpoints and caches, deoptimizing -// dependent compiled code) -// - replacing parts in the_class with parts from scratch_class -// - adding a weak reference to track the obsolete but interesting -// parts of the_class -// - adjusting constant pool caches and vtables in other classes -// that refer to methods in the_class. These adjustments use the -// SystemDictionary::classes_do() facility which only allows -// a helper method to be specified. The interesting parameters -// that we would like to pass to the helper method are saved in -// static global fields in the VM operation. -void VM_RedefineClasses::redefine_single_class(jclass the_jclass, - instanceKlassHandle scratch_class, TRAPS) { - - RC_TIMER_START(_timer_rsc_phase1); - - oop the_class_mirror = JNIHandles::resolve_non_null(the_jclass); - klassOop the_class_oop = java_lang_Class::as_klassOop(the_class_mirror); - instanceKlassHandle the_class = instanceKlassHandle(THREAD, the_class_oop); - -#ifndef JVMTI_KERNEL - // Remove all breakpoints in methods of this class - JvmtiBreakpoints& jvmti_breakpoints = JvmtiCurrentBreakpoints::get_jvmti_breakpoints(); - jvmti_breakpoints.clearall_in_class_at_safepoint(the_class_oop); -#endif // !JVMTI_KERNEL - - if (the_class_oop == Universe::reflect_invoke_cache()->klass()) { - // We are redefining java.lang.reflect.Method. Method.invoke() is - // cached and users of the cache care about each active version of - // the method so we have to track this previous version. - // Do this before methods get switched - Universe::reflect_invoke_cache()->add_previous_version( - the_class->method_with_idnum(Universe::reflect_invoke_cache()->method_idnum())); - } - - // Deoptimize all compiled code that depends on this class - flush_dependent_code(the_class, THREAD); - - _old_methods = the_class->methods(); - _new_methods = scratch_class->methods(); - _the_class_oop = the_class_oop; - compute_added_deleted_matching_methods(); - update_jmethod_ids(); - - // Attach new constant pool to the original klass. The original - // klass still refers to the old constant pool (for now). - scratch_class->constants()->set_pool_holder(the_class()); - -#if 0 - // In theory, with constant pool merging in place we should be able - // to save space by using the new, merged constant pool in place of - // the old constant pool(s). By "pool(s)" I mean the constant pool in - // the klass version we are replacing now and any constant pool(s) in - // previous versions of klass. Nice theory, doesn't work in practice. - // When this code is enabled, even simple programs throw NullPointer - // exceptions. I'm guessing that this is caused by some constant pool - // cache difference between the new, merged constant pool and the - // constant pool that was just being used by the klass. I'm keeping - // this code around to archive the idea, but the code has to remain - // disabled for now. - - // Attach each old method to the new constant pool. This can be - // done here since we are past the bytecode verification and - // constant pool optimization phases. - for (int i = _old_methods->length() - 1; i >= 0; i--) { - methodOop method = (methodOop)_old_methods->obj_at(i); - method->set_constants(scratch_class->constants()); - } - - { - // walk all previous versions of the klass - instanceKlass *ik = (instanceKlass *)the_class()->klass_part(); - PreviousVersionWalker pvw(ik); - instanceKlassHandle ikh; - do { - ikh = pvw.next_previous_version(); - if (!ikh.is_null()) { - ik = ikh(); - - // attach previous version of klass to the new constant pool - ik->set_constants(scratch_class->constants()); - - // Attach each method in the previous version of klass to the - // new constant pool - objArrayOop prev_methods = ik->methods(); - for (int i = prev_methods->length() - 1; i >= 0; i--) { - methodOop method = (methodOop)prev_methods->obj_at(i); - method->set_constants(scratch_class->constants()); - } - } - } while (!ikh.is_null()); - } -#endif - - // Replace methods and constantpool - the_class->set_methods(_new_methods); - scratch_class->set_methods(_old_methods); // To prevent potential GCing of the old methods, - // and to be able to undo operation easily. - - constantPoolOop old_constants = the_class->constants(); - the_class->set_constants(scratch_class->constants()); - scratch_class->set_constants(old_constants); // See the previous comment. -#if 0 - // We are swapping the guts of "the new class" with the guts of "the - // class". Since the old constant pool has just been attached to "the - // new class", it seems logical to set the pool holder in the old - // constant pool also. However, doing this will change the observable - // class hierarchy for any old methods that are still executing. A - // method can query the identity of its "holder" and this query uses - // the method's constant pool link to find the holder. The change in - // holding class from "the class" to "the new class" can confuse - // things. - // - // Setting the old constant pool's holder will also cause - // verification done during vtable initialization below to fail. - // During vtable initialization, the vtable's class is verified to be - // a subtype of the method's holder. The vtable's class is "the - // class" and the method's holder is gotten from the constant pool - // link in the method itself. For "the class"'s directly implemented - // methods, the method holder is "the class" itself (as gotten from - // the new constant pool). The check works fine in this case. The - // check also works fine for methods inherited from super classes. - // - // Miranda methods are a little more complicated. A miranda method is - // provided by an interface when the class implementing the interface - // does not provide its own method. These interfaces are implemented - // internally as an instanceKlass. These special instanceKlasses - // share the constant pool of the class that "implements" the - // interface. By sharing the constant pool, the method holder of a - // miranda method is the class that "implements" the interface. In a - // non-redefine situation, the subtype check works fine. However, if - // the old constant pool's pool holder is modified, then the check - // fails because there is no class hierarchy relationship between the - // vtable's class and "the new class". - - old_constants->set_pool_holder(scratch_class()); -#endif - - // track which methods are EMCP for add_previous_version() call below - BitMap emcp_methods(_old_methods->length()); - int emcp_method_count = 0; - emcp_methods.clear(); // clears 0..(length() - 1) - check_methods_and_mark_as_obsolete(&emcp_methods, &emcp_method_count); - transfer_old_native_function_registrations(the_class); - - // The class file bytes from before any retransformable agents mucked - // with them was cached on the scratch class, move to the_class. - // Note: we still want to do this if nothing needed caching since it - // should get cleared in the_class too. - the_class->set_cached_class_file(scratch_class->get_cached_class_file_bytes(), - scratch_class->get_cached_class_file_len()); - - // Replace inner_classes - typeArrayOop old_inner_classes = the_class->inner_classes(); - the_class->set_inner_classes(scratch_class->inner_classes()); - scratch_class->set_inner_classes(old_inner_classes); - - // Initialize the vtable and interface table after - // methods have been rewritten - { - ResourceMark rm(THREAD); - // no exception should happen here since we explicitly - // do not check loader constraints. - // compare_and_normalize_class_versions has already checked: - // - classloaders unchanged, signatures unchanged - // - all instanceKlasses for redefined classes reused & contents updated - the_class->vtable()->initialize_vtable(false, THREAD); - the_class->itable()->initialize_itable(false, THREAD); - assert(!HAS_PENDING_EXCEPTION || (THREAD->pending_exception()->is_a(SystemDictionary::ThreadDeath_klass())), "redefine exception"); - } - - // Leave arrays of jmethodIDs and itable index cache unchanged - - // Copy the "source file name" attribute from new class version - the_class->set_source_file_name(scratch_class->source_file_name()); - - // Copy the "source debug extension" attribute from new class version - the_class->set_source_debug_extension( - scratch_class->source_debug_extension()); - - // Use of javac -g could be different in the old and the new - if (scratch_class->access_flags().has_localvariable_table() != - the_class->access_flags().has_localvariable_table()) { - - AccessFlags flags = the_class->access_flags(); - if (scratch_class->access_flags().has_localvariable_table()) { - flags.set_has_localvariable_table(); - } else { - flags.clear_has_localvariable_table(); - } - the_class->set_access_flags(flags); - } - - // Replace class annotation fields values - typeArrayOop old_class_annotations = the_class->class_annotations(); - the_class->set_class_annotations(scratch_class->class_annotations()); - scratch_class->set_class_annotations(old_class_annotations); - - // Replace fields annotation fields values - objArrayOop old_fields_annotations = the_class->fields_annotations(); - the_class->set_fields_annotations(scratch_class->fields_annotations()); - scratch_class->set_fields_annotations(old_fields_annotations); - - // Replace methods annotation fields values - objArrayOop old_methods_annotations = the_class->methods_annotations(); - the_class->set_methods_annotations(scratch_class->methods_annotations()); - scratch_class->set_methods_annotations(old_methods_annotations); - - // Replace methods parameter annotation fields values - objArrayOop old_methods_parameter_annotations = - the_class->methods_parameter_annotations(); - the_class->set_methods_parameter_annotations( - scratch_class->methods_parameter_annotations()); - scratch_class->set_methods_parameter_annotations(old_methods_parameter_annotations); - - // Replace methods default annotation fields values - objArrayOop old_methods_default_annotations = - the_class->methods_default_annotations(); - the_class->set_methods_default_annotations( - scratch_class->methods_default_annotations()); - scratch_class->set_methods_default_annotations(old_methods_default_annotations); - - // Replace minor version number of class file - u2 old_minor_version = the_class->minor_version(); - the_class->set_minor_version(scratch_class->minor_version()); - scratch_class->set_minor_version(old_minor_version); - - // Replace major version number of class file - u2 old_major_version = the_class->major_version(); - the_class->set_major_version(scratch_class->major_version()); - scratch_class->set_major_version(old_major_version); - - // Replace CP indexes for class and name+type of enclosing method - u2 old_class_idx = the_class->enclosing_method_class_index(); - u2 old_method_idx = the_class->enclosing_method_method_index(); - the_class->set_enclosing_method_indices( - scratch_class->enclosing_method_class_index(), - scratch_class->enclosing_method_method_index()); - scratch_class->set_enclosing_method_indices(old_class_idx, old_method_idx); - - // keep track of previous versions of this class - the_class->add_previous_version(scratch_class, &emcp_methods, - emcp_method_count); - - RC_TIMER_STOP(_timer_rsc_phase1); - RC_TIMER_START(_timer_rsc_phase2); - - // Adjust constantpool caches and vtables for all classes - // that reference methods of the evolved class. - SystemDictionary::classes_do(adjust_cpool_cache_and_vtable, THREAD); - - if (the_class->oop_map_cache() != NULL) { - // Flush references to any obsolete methods from the oop map cache - // so that obsolete methods are not pinned. - the_class->oop_map_cache()->flush_obsolete_entries(); - } - - // increment the classRedefinedCount field in the_class and in any - // direct and indirect subclasses of the_class - increment_class_counter((instanceKlass *)the_class()->klass_part(), THREAD); - - // RC_TRACE macro has an embedded ResourceMark - RC_TRACE_WITH_THREAD(0x00000001, THREAD, - ("redefined name=%s, count=%d (avail_mem=" UINT64_FORMAT "K)", - the_class->external_name(), - java_lang_Class::classRedefinedCount(the_class_mirror), - os::available_memory() >> 10)); - - RC_TIMER_STOP(_timer_rsc_phase2); -} // end redefine_single_class() - - -// Increment the classRedefinedCount field in the specific instanceKlass -// and in all direct and indirect subclasses. -void VM_RedefineClasses::increment_class_counter(instanceKlass *ik, TRAPS) { - oop class_mirror = ik->java_mirror(); - klassOop class_oop = java_lang_Class::as_klassOop(class_mirror); - int new_count = java_lang_Class::classRedefinedCount(class_mirror) + 1; - java_lang_Class::set_classRedefinedCount(class_mirror, new_count); - - if (class_oop != _the_class_oop) { - // _the_class_oop count is printed at end of redefine_single_class() - RC_TRACE_WITH_THREAD(0x00000008, THREAD, - ("updated count in subclass=%s to %d", ik->external_name(), new_count)); - } - - for (Klass *subk = ik->subklass(); subk != NULL; - subk = subk->next_sibling()) { - klassOop sub = subk->as_klassOop(); - instanceKlass *subik = (instanceKlass *)sub->klass_part(); - - // recursively do subclasses of the current subclass - increment_class_counter(subik, THREAD); - } -} - -#ifndef PRODUCT -void VM_RedefineClasses::check_class(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; - - if (ik->vtable_length() > 0) { - ResourceMark rm(THREAD); - if (!ik->vtable()->check_no_old_entries()) { - tty->print_cr("klassVtable::check_no_old_entries failure -- OLD method found -- class: %s", ik->signature_name()); - ik->vtable()->dump_vtable(); - dump_methods(); - assert(false, "OLD method found"); - } - } - } -} - -void VM_RedefineClasses::dump_methods() { - int j; - tty->print_cr("_old_methods --"); - for (j = 0; j < _old_methods->length(); ++j) { - methodOop m = (methodOop) _old_methods->obj_at(j); - tty->print("%4d (%5d) ", j, m->vtable_index()); - m->access_flags().print_on(tty); - tty->print(" -- "); - m->print_name(tty); - tty->cr(); - } - tty->print_cr("_new_methods --"); - for (j = 0; j < _new_methods->length(); ++j) { - methodOop m = (methodOop) _new_methods->obj_at(j); - tty->print("%4d (%5d) ", j, m->vtable_index()); - m->access_flags().print_on(tty); - tty->print(" -- "); - m->print_name(tty); - tty->cr(); - } - tty->print_cr("_matching_(old/new)_methods --"); - for (j = 0; j < _matching_methods_length; ++j) { - methodOop m = _matching_old_methods[j]; - tty->print("%4d (%5d) ", j, m->vtable_index()); - m->access_flags().print_on(tty); - tty->print(" -- "); - m->print_name(tty); - tty->cr(); - m = _matching_new_methods[j]; - tty->print(" (%5d) ", m->vtable_index()); - m->access_flags().print_on(tty); - tty->cr(); - } - tty->print_cr("_deleted_methods --"); - for (j = 0; j < _deleted_methods_length; ++j) { - methodOop m = _deleted_methods[j]; - tty->print("%4d (%5d) ", j, m->vtable_index()); - m->access_flags().print_on(tty); - tty->print(" -- "); - m->print_name(tty); - tty->cr(); - } - tty->print_cr("_added_methods --"); - for (j = 0; j < _added_methods_length; ++j) { - methodOop m = _added_methods[j]; - tty->print("%4d (%5d) ", j, m->vtable_index()); - m->access_flags().print_on(tty); - tty->print(" -- "); - m->print_name(tty); - tty->cr(); - } -} -#endif diff -r f5603a6e5042 src/share/vm/prims/jvmtiRedefineClasses.hpp --- a/src/share/vm/prims/jvmtiRedefineClasses.hpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/prims/jvmtiRedefineClasses.hpp Fri Dec 17 13:24:08 2010 +0100 @@ -1,351 +1,50 @@ /* - * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ +* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. +* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +* +* This code is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License version 2 only, as +* published by the Free Software Foundation. +* +* This code is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +* version 2 for more details (a copy is included in the LICENSE file that +* accompanied this code). +* +* You should have received a copy of the GNU General Public License version +* 2 along with this work; if not, write to the Free Software Foundation, +* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +* +* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +* or visit www.oracle.com if you need additional information or have any +* questions. +* +*/ -// Introduction: -// -// The RedefineClasses() API is used to change the definition of one or -// more classes. While the API supports redefining more than one class -// in a single call, in general, the API is discussed in the context of -// changing the definition of a single current class to a single new -// class. For clarity, the current class is will always be called -// "the_class" and the new class will always be called "scratch_class". -// -// The name "the_class" is used because there is only one structure -// that represents a specific class; redefinition does not replace the -// structure, but instead replaces parts of the structure. The name -// "scratch_class" is used because the structure that represents the -// new definition of a specific class is simply used to carry around -// the parts of the new definition until they are used to replace the -// appropriate parts in the_class. Once redefinition of a class is -// complete, scratch_class is thrown away. -// -// -// Implementation Overview: -// -// The RedefineClasses() API is mostly a wrapper around the VM op that -// does the real work. The work is split in varying degrees between -// doit_prologue(), doit() and doit_epilogue(). -// -// 1) doit_prologue() is called by the JavaThread on the way to a -// safepoint. It does parameter verification and loads scratch_class -// which involves: -// - parsing the incoming class definition using the_class' class -// loader and security context -// - linking scratch_class -// - merging constant pools and rewriting bytecodes as needed -// for the merged constant pool -// - verifying the bytecodes in scratch_class -// - setting up the constant pool cache and rewriting bytecodes -// as needed to use the cache -// - finally, scratch_class is compared to the_class to verify -// that it is a valid replacement class -// - if everything is good, then scratch_class is saved in an -// instance field in the VM operation for the doit() call -// -// Note: A JavaThread must do the above work. -// -// 2) doit() is called by the VMThread during a safepoint. It installs -// the new class definition(s) which involves: -// - retrieving the scratch_class from the instance field in the -// VM operation -// - house keeping (flushing breakpoints and caches, deoptimizing -// dependent compiled code) -// - replacing parts in the_class with parts from scratch_class -// - adding weak reference(s) to track the obsolete but interesting -// parts of the_class -// - adjusting constant pool caches and vtables in other classes -// that refer to methods in the_class. These adjustments use the -// SystemDictionary::classes_do() facility which only allows -// a helper method to be specified. The interesting parameters -// that we would like to pass to the helper method are saved in -// static global fields in the VM operation. -// - telling the SystemDictionary to notice our changes -// -// Note: the above work must be done by the VMThread to be safe. -// -// 3) doit_epilogue() is called by the JavaThread after the VM op -// is finished and the safepoint is done. It simply cleans up -// memory allocated in doit_prologue() and used in doit(). -// -// -// Constant Pool Details: -// -// When the_class is redefined, we cannot just replace the constant -// pool in the_class with the constant pool from scratch_class because -// that could confuse obsolete methods that may still be running. -// Instead, the constant pool from the_class, old_cp, is merged with -// the constant pool from scratch_class, scratch_cp. The resulting -// constant pool, merge_cp, replaces old_cp in the_class. -// -// The key part of any merging algorithm is the entry comparison -// function so we have to know the types of entries in a constant pool -// in order to merge two of them together. Constant pools can contain -// up to 12 different kinds of entries; the JVM_CONSTANT_Unicode entry -// is not presently used so we only have to worry about the other 11 -// entry types. For the purposes of constant pool merging, it is -// helpful to know that the 11 entry types fall into 3 different -// subtypes: "direct", "indirect" and "double-indirect". -// -// Direct CP entries contain data and do not contain references to -// other CP entries. The following are direct CP entries: -// JVM_CONSTANT_{Double,Float,Integer,Long,Utf8} -// -// Indirect CP entries contain 1 or 2 references to a direct CP entry -// and no other data. The following are indirect CP entries: -// JVM_CONSTANT_{Class,NameAndType,String} -// -// Double-indirect CP entries contain two references to indirect CP -// entries and no other data. The following are double-indirect CP -// entries: -// JVM_CONSTANT_{Fieldref,InterfaceMethodref,Methodref} -// -// When comparing entries between two constant pools, the entry types -// are compared first and if they match, then further comparisons are -// made depending on the entry subtype. Comparing direct CP entries is -// simply a matter of comparing the data associated with each entry. -// Comparing both indirect and double-indirect CP entries requires -// recursion. -// -// Fortunately, the recursive combinations are limited because indirect -// CP entries can only refer to direct CP entries and double-indirect -// CP entries can only refer to indirect CP entries. The following is -// an example illustration of the deepest set of indirections needed to -// access the data associated with a JVM_CONSTANT_Fieldref entry: -// -// JVM_CONSTANT_Fieldref { -// class_index => JVM_CONSTANT_Class { -// name_index => JVM_CONSTANT_Utf8 { -// -// } -// } -// name_and_type_index => JVM_CONSTANT_NameAndType { -// name_index => JVM_CONSTANT_Utf8 { -// -// } -// descriptor_index => JVM_CONSTANT_Utf8 { -// -// } -// } -// } -// -// The above illustration is not a data structure definition for any -// computer language. The curly braces ('{' and '}') are meant to -// delimit the context of the "fields" in the CP entry types shown. -// Each indirection from the JVM_CONSTANT_Fieldref entry is shown via -// "=>", e.g., the class_index is used to indirectly reference a -// JVM_CONSTANT_Class entry where the name_index is used to indirectly -// reference a JVM_CONSTANT_Utf8 entry which contains the interesting -// . In order to understand a JVM_CONSTANT_Fieldref entry, we -// have to do a total of 5 indirections just to get to the CP entries -// that contain the interesting pieces of data and then we have to -// fetch the three pieces of data. This means we have to do a total of -// (5 + 3) * 2 == 16 dereferences to compare two JVM_CONSTANT_Fieldref -// entries. -// -// Here is the indirection, data and dereference count for each entry -// type: -// -// JVM_CONSTANT_Class 1 indir, 1 data, 2 derefs -// JVM_CONSTANT_Double 0 indir, 1 data, 1 deref -// JVM_CONSTANT_Fieldref 2 indir, 3 data, 8 derefs -// JVM_CONSTANT_Float 0 indir, 1 data, 1 deref -// JVM_CONSTANT_Integer 0 indir, 1 data, 1 deref -// JVM_CONSTANT_InterfaceMethodref 2 indir, 3 data, 8 derefs -// JVM_CONSTANT_Long 0 indir, 1 data, 1 deref -// JVM_CONSTANT_Methodref 2 indir, 3 data, 8 derefs -// JVM_CONSTANT_NameAndType 1 indir, 2 data, 4 derefs -// JVM_CONSTANT_String 1 indir, 1 data, 2 derefs -// JVM_CONSTANT_Utf8 0 indir, 1 data, 1 deref -// -// So different subtypes of CP entries require different amounts of -// work for a proper comparison. -// -// Now that we've talked about the different entry types and how to -// compare them we need to get back to merging. This is not a merge in -// the "sort -u" sense or even in the "sort" sense. When we merge two -// constant pools, we copy all the entries from old_cp to merge_cp, -// preserving entry order. Next we append all the unique entries from -// scratch_cp to merge_cp and we track the index changes from the -// location in scratch_cp to the possibly new location in merge_cp. -// When we are done, any obsolete code that is still running that -// uses old_cp should not be able to observe any difference if it -// were to use merge_cp. As for the new code in scratch_class, it is -// modified to use the appropriate index values in merge_cp before it -// is used to replace the code in the_class. -// -// There is one small complication in copying the entries from old_cp -// to merge_cp. Two of the CP entry types are special in that they are -// lazily resolved. Before explaining the copying complication, we need -// to digress into CP entry resolution. -// -// JVM_CONSTANT_Class and JVM_CONSTANT_String entries are present in -// the class file, but are not stored in memory as such until they are -// resolved. The entries are not resolved unless they are used because -// resolution is expensive. During class file parsing the entries are -// initially stored in memory as JVM_CONSTANT_ClassIndex and -// JVM_CONSTANT_StringIndex entries. These special CP entry types -// indicate that the JVM_CONSTANT_Class and JVM_CONSTANT_String entries -// have been parsed, but the index values in the entries have not been -// validated. After the entire constant pool has been parsed, the index -// values can be validated and then the entries are converted into -// JVM_CONSTANT_UnresolvedClass and JVM_CONSTANT_UnresolvedString -// entries. During this conversion process, the UTF8 values that are -// indirectly referenced by the JVM_CONSTANT_ClassIndex and -// JVM_CONSTANT_StringIndex entries are changed into symbolOops and the -// entries are modified to refer to the symbolOops. This optimization -// eliminates one level of indirection for those two CP entry types and -// gets the entries ready for verification. During class file parsing -// it is also possible for JVM_CONSTANT_UnresolvedString entries to be -// resolved into JVM_CONSTANT_String entries. Verification expects to -// find JVM_CONSTANT_UnresolvedClass and either JVM_CONSTANT_String or -// JVM_CONSTANT_UnresolvedString entries and not JVM_CONSTANT_Class -// entries. -// -// Now we can get back to the copying complication. When we copy -// entries from old_cp to merge_cp, we have to revert any -// JVM_CONSTANT_Class entries to JVM_CONSTANT_UnresolvedClass entries -// or verification will fail. -// -// It is important to explicitly state that the merging algorithm -// effectively unresolves JVM_CONSTANT_Class entries that were in the -// old_cp when they are changed into JVM_CONSTANT_UnresolvedClass -// entries in the merge_cp. This is done both to make verification -// happy and to avoid adding more brittleness between RedefineClasses -// and the constant pool cache. By allowing the constant pool cache -// implementation to (re)resolve JVM_CONSTANT_UnresolvedClass entries -// into JVM_CONSTANT_Class entries, we avoid having to embed knowledge -// about those algorithms in RedefineClasses. -// -// Appending unique entries from scratch_cp to merge_cp is straight -// forward for direct CP entries and most indirect CP entries. For the -// indirect CP entry type JVM_CONSTANT_NameAndType and for the double- -// indirect CP entry types, the presence of more than one piece of -// interesting data makes appending the entries more complicated. -// -// For the JVM_CONSTANT_{Double,Float,Integer,Long,Utf8} entry types, -// the entry is simply copied from scratch_cp to the end of merge_cp. -// If the index in scratch_cp is different than the destination index -// in merge_cp, then the change in index value is tracked. -// -// Note: the above discussion for the direct CP entries also applies -// to the JVM_CONSTANT_Unresolved{Class,String} entry types. -// -// For the JVM_CONSTANT_{Class,String} entry types, since there is only -// one data element at the end of the recursion, we know that we have -// either one or two unique entries. If the JVM_CONSTANT_Utf8 entry is -// unique then it is appended to merge_cp before the current entry. -// If the JVM_CONSTANT_Utf8 entry is not unique, then the current entry -// is updated to refer to the duplicate entry in merge_cp before it is -// appended to merge_cp. Again, any changes in index values are tracked -// as needed. -// -// Note: the above discussion for JVM_CONSTANT_{Class,String} entry -// types is theoretical. Since those entry types have already been -// optimized into JVM_CONSTANT_Unresolved{Class,String} entry types, -// they are handled as direct CP entries. -// -// For the JVM_CONSTANT_NameAndType entry type, since there are two -// data elements at the end of the recursions, we know that we have -// between one and three unique entries. Any unique JVM_CONSTANT_Utf8 -// entries are appended to merge_cp before the current entry. For any -// JVM_CONSTANT_Utf8 entries that are not unique, the current entry is -// updated to refer to the duplicate entry in merge_cp before it is -// appended to merge_cp. Again, any changes in index values are tracked -// as needed. -// -// For the JVM_CONSTANT_{Fieldref,InterfaceMethodref,Methodref} entry -// types, since there are two indirect CP entries and three data -// elements at the end of the recursions, we know that we have between -// one and six unique entries. See the JVM_CONSTANT_Fieldref diagram -// above for an example of all six entries. The uniqueness algorithm -// for the JVM_CONSTANT_Class and JVM_CONSTANT_NameAndType entries is -// covered above. Any unique entries are appended to merge_cp before -// the current entry. For any entries that are not unique, the current -// entry is updated to refer to the duplicate entry in merge_cp before -// it is appended to merge_cp. Again, any changes in index values are -// tracked as needed. -// -// -// Other Details: -// -// Details for other parts of RedefineClasses need to be written. -// This is a placeholder section. -// -// -// Open Issues (in no particular order): -// -// - How do we serialize the RedefineClasses() API without deadlocking? -// -// - SystemDictionary::parse_stream() was called with a NULL protection -// domain since the initial version. This has been changed to pass -// the_class->protection_domain(). This change has been tested with -// all NSK tests and nothing broke, but what will adding it now break -// in ways that we don't test? -// -// - GenerateOopMap::rewrite_load_or_store() has a comment in its -// (indirect) use of the Relocator class that the max instruction -// size is 4 bytes. goto_w and jsr_w are 5 bytes and wide/iinc is -// 6 bytes. Perhaps Relocator only needs a 4 byte buffer to do -// what it does to the bytecodes. More investigation is needed. -// -// - java.lang.Object methods can be called on arrays. This is -// implemented via the arrayKlassOop vtable which we don't -// update. For example, if we redefine java.lang.Object.toString(), -// then the new version of the method will not be called for array -// objects. -// -// - How do we know if redefine_single_class() and the guts of -// instanceKlass are out of sync? I don't think this can be -// automated, but we should probably order the work in -// redefine_single_class() to match the order of field -// definitions in instanceKlass. We also need to add some -// comments about keeping things in sync. -// -// - set_new_constant_pool() is huge and we should consider refactoring -// it into smaller chunks of work. -// -// - The exception table update code in set_new_constant_pool() defines -// const values that are also defined in a local context elsewhere. -// The same literal values are also used in elsewhere. We need to -// coordinate a cleanup of these constants with Runtime. -// +// New version that allows arbitrary changes to already loaded classes. +// Modifications done by: Thomas Wuerthinger -class VM_RedefineClasses: public VM_Operation { +#define RC_ABORT(error) { _result = error; return false; } + +class VM_RedefineClasses: public VM_GC_Operation { private: + // These static fields are needed by SystemDictionary::classes_do() // facility and the adjust_cpool_cache_and_vtable() helper: static objArrayOop _old_methods; static objArrayOop _new_methods; - static methodOop* _matching_old_methods; - static methodOop* _matching_new_methods; - static methodOop* _deleted_methods; - static methodOop* _added_methods; + static int* _matching_old_methods; + static int* _matching_new_methods; + static int* _deleted_methods; + static int* _added_methods; static int _matching_methods_length; static int _deleted_methods_length; static int _added_methods_length; static klassOop _the_class_oop; + static int _revision_number; + // The instance fields are used to pass information from // doit_prologue() to doit() and doit_epilogue(). jint _class_count; @@ -359,36 +58,29 @@ // _index_map_p contains any entries. int _index_map_count; intArray * _index_map_p; - // ptr to _class_count scratch_classes - instanceKlassHandle * _scratch_classes; - jvmtiError _res; + GrowableArray* _new_classes; + GrowableArray* _updated_oops; + jvmtiError _result; + int _max_redefinition_flags; // Performance measurement support. These timers do not cover all // the work done for JVM/TI RedefineClasses() but they do cover // the heavy lifting. - elapsedTimer _timer_rsc_phase1; - elapsedTimer _timer_rsc_phase2; - elapsedTimer _timer_vm_op_prologue; + elapsedTimer _timer_total; + elapsedTimer _timer_prologue; + elapsedTimer _timer_class_linking; + elapsedTimer _timer_class_loading; + elapsedTimer _timer_check_type; + elapsedTimer _timer_prepare_redefinition; + elapsedTimer _timer_wait_for_locks; + elapsedTimer _timer_redefinition; + elapsedTimer _timer_vm_op_epilogue; - // These routines are roughly in call order unless otherwise noted. - - // Load the caller's new class definition(s) into _scratch_classes. - // Constant pool merging work is done here as needed. Also calls - // compare_and_normalize_class_versions() to verify the class - // definition(s). + jvmtiError check_redefinition_allowed(instanceKlassHandle new_class); + 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. - jvmtiError compare_and_normalize_class_versions( - instanceKlassHandle the_class, instanceKlassHandle scratch_class); - - // Swap annotations[i] with annotations[j] - // 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(); @@ -396,94 +88,82 @@ // Change jmethodIDs to point to the new methods void update_jmethod_ids(); - // In addition to marking methods as obsolete, this routine - // records which methods are EMCP (Equivalent Module Constant - // Pool) in the emcp_methods BitMap and returns the number of - // EMCP methods via emcp_method_count_p. This information is - // used when information about the previous version of the_class - // is squirreled away. - void check_methods_and_mark_as_obsolete(BitMap *emcp_methods, - int * emcp_method_count_p); - void transfer_old_native_function_registrations(instanceKlassHandle the_class); + class FindAffectedKlassesClosure : public ObjectClosure { - // 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. - static void adjust_cpool_cache_and_vtable(klassOop k_oop, oop loader, TRAPS); + private: + GrowableArray *_original_klasses; + GrowableArray *_result; + + public: + FindAffectedKlassesClosure(GrowableArray *original_klasses, GrowableArray *result); + + virtual void do_object(oop obj); + }; + + + static jvmtiError do_topological_class_sorting(const jvmtiClassDefinition *class_definitions, int class_count, GrowableArray *affected, GrowableArray *arr, TRAPS); // Install the redefinition of a class - void redefine_single_class(jclass the_jclass, - instanceKlassHandle scratch_class, TRAPS); + void redefine_single_class(instanceKlassHandle the_new_class, TRAPS); // Increment the classRedefinedCount field in the specific instanceKlass // and in all direct and indirect subclasses. void increment_class_counter(instanceKlass *ik, TRAPS); - // Support for constant pool merging (these routines are in alpha - // order): - void append_entry(constantPoolHandle scratch_cp, int scratch_i, - constantPoolHandle *merge_cp_p, int *merge_cp_length_p, TRAPS); - int find_new_index(int old_index); - bool is_unresolved_class_mismatch(constantPoolHandle cp1, int index1, - constantPoolHandle cp2, int index2); - bool is_unresolved_string_mismatch(constantPoolHandle cp1, int index1, - constantPoolHandle cp2, int index2); - void map_index(constantPoolHandle scratch_cp, int old_index, int new_index); - bool merge_constant_pools(constantPoolHandle old_cp, - constantPoolHandle scratch_cp, constantPoolHandle *merge_cp_p, - int *merge_cp_length_p, TRAPS); - jvmtiError merge_cp_and_rewrite(instanceKlassHandle the_class, - instanceKlassHandle scratch_class, TRAPS); - u2 rewrite_cp_ref_in_annotation_data( - typeArrayHandle annotations_typeArray, int &byte_i_ref, - const char * trace_mesg, TRAPS); - bool rewrite_cp_refs(instanceKlassHandle scratch_class, TRAPS); - bool rewrite_cp_refs_in_annotation_struct( - typeArrayHandle class_annotations, int &byte_i_ref, TRAPS); - bool rewrite_cp_refs_in_annotations_typeArray( - typeArrayHandle annotations_typeArray, int &byte_i_ref, TRAPS); - bool rewrite_cp_refs_in_class_annotations( - instanceKlassHandle scratch_class, TRAPS); - bool rewrite_cp_refs_in_element_value( - typeArrayHandle class_annotations, int &byte_i_ref, TRAPS); - bool rewrite_cp_refs_in_fields_annotations( - instanceKlassHandle scratch_class, TRAPS); - void rewrite_cp_refs_in_method(methodHandle method, - methodHandle * new_method_p, TRAPS); - bool rewrite_cp_refs_in_methods(instanceKlassHandle scratch_class, TRAPS); - bool rewrite_cp_refs_in_methods_annotations( - instanceKlassHandle scratch_class, TRAPS); - bool rewrite_cp_refs_in_methods_default_annotations( - instanceKlassHandle scratch_class, TRAPS); - bool rewrite_cp_refs_in_methods_parameter_annotations( - instanceKlassHandle scratch_class, TRAPS); - void rewrite_cp_refs_in_stack_map_table(methodHandle method, TRAPS); - void rewrite_cp_refs_in_verification_type_info( - address& stackmap_addr_ref, address stackmap_end, u2 frame_i, - u1 frame_size, TRAPS); - void set_new_constant_pool(instanceKlassHandle scratch_class, - constantPoolHandle scratch_cp, int scratch_cp_length, bool shrink, TRAPS); void flush_dependent_code(instanceKlassHandle k_h, TRAPS); - static void check_class(klassOop k_oop, oop initiating_loader, TRAPS) PRODUCT_RETURN; + static void check_class(klassOop k_oop,/* oop initiating_loader,*/ TRAPS) PRODUCT_RETURN; - static void dump_methods() PRODUCT_RETURN; + static void adjust_cpool_cache(klassOop k_oop, oop initiating_loader, TRAPS); + +#ifdef ASSERT + static void verify_classes(klassOop k_oop, oop initiating_loader, TRAPS); +#endif + + int calculate_redefinition_flags(instanceKlassHandle new_version); + void calculate_instance_update_information(klassOop new_version); + void check_methods_and_mark_as_obsolete(BitMap *emcp_methods, int * emcp_method_count_p); public: - VM_RedefineClasses(jint class_count, - const jvmtiClassDefinition *class_defs, - JvmtiClassLoadKind class_load_kind); - VMOp_Type type() const { return VMOp_RedefineClasses; } + VM_RedefineClasses(jint class_count, const jvmtiClassDefinition *class_defs, JvmtiClassLoadKind class_load_kind); + virtual ~VM_RedefineClasses(); + + bool check_arguments(); bool doit_prologue(); void doit(); void doit_epilogue(); + void rollback(); - bool allow_nested_vm_operations() const { return true; } - jvmtiError check_error() { return _res; } + jvmtiError check_exception() const; + VMOp_Type type() const { return VMOp_RedefineClasses; } + bool skip_operation() const { return false; } + bool allow_nested_vm_operations() const { return true; } + jvmtiError check_error() { return _result; } + + void update_active_methods(); + + void update_array_classes_to_newest_version(klassOop smallest_dimension); // Modifiable test must be shared between IsModifiableClass query // and redefine implementation static bool is_modifiable_class(oop klass_mirror); -}; + + // Method used during garbage collection, the VM operation must iterate over all oops. + void oops_do(OopClosure* f); + + // Utility methods for transfering field access flags + + static void transfer_special_access_flags(fieldDescriptor *from, fieldDescriptor *to); + static void update_klass_field_access_flag(fieldDescriptor *fd); + + void transfer_old_native_function_registrations(instanceKlassHandle the_class); + + void lock_threads(); + void unlock_threads(); + + template static void do_oop_work(T* p); + + static void swap_marks(oop first, oop second); + +}; \ No newline at end of file diff -r f5603a6e5042 src/share/vm/prims/jvmtiRedefineClassesTrace.hpp --- a/src/share/vm/prims/jvmtiRedefineClassesTrace.hpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/prims/jvmtiRedefineClassesTrace.hpp Fri Dec 17 13:24:08 2010 +0100 @@ -22,102 +22,26 @@ * */ -// RedefineClasses tracing support via the TraceRedefineClasses -// option. A bit is assigned to each group of trace messages. -// Groups of messages are individually selectable. We have to use -// decimal values on the command line since the command option -// parsing logic doesn't like non-decimal numerics. The HEX values -// are used in the actual RC_TRACE() calls for sanity. To achieve -// the old cumulative behavior, pick the level after the one in -// which you are interested and subtract one, e.g., 33554431 will -// print every tracing message. -// -// 0x00000000 | 0 - default; no tracing messages -// 0x00000001 | 1 - name each target class before loading, after -// loading and after redefinition is completed -// 0x00000002 | 2 - print info if parsing, linking or -// verification throws an exception -// 0x00000004 | 4 - print timer info for the VM operation -// 0x00000008 | 8 - print subclass counter updates -// 0x00000010 | 16 - unused -// 0x00000020 | 32 - unused -// 0x00000040 | 64 - unused -// 0x00000080 | 128 - unused -// 0x00000100 | 256 - previous class weak reference addition -// 0x00000200 | 512 - previous class weak reference mgmt during -// class unloading checks (GC) -// 0x00000400 | 1024 - previous class weak reference mgmt during -// add previous ops (GC) -// 0x00000800 | 2048 - previous class breakpoint mgmt -// 0x00001000 | 4096 - detect calls to obsolete methods -// 0x00002000 | 8192 - fail a guarantee() in addition to detection -// 0x00004000 | 16384 - unused -// 0x00008000 | 32768 - old/new method matching/add/delete -// 0x00010000 | 65536 - impl details: CP size info -// 0x00020000 | 131072 - impl details: CP merge pass info -// 0x00040000 | 262144 - impl details: CP index maps -// 0x00080000 | 524288 - impl details: modified CP index values -// 0x00100000 | 1048576 - impl details: vtable updates -// 0x00200000 | 2097152 - impl details: itable updates -// 0x00400000 | 4194304 - impl details: constant pool cache updates -// 0x00800000 | 8388608 - impl details: methodComparator info -// 0x01000000 | 16777216 - impl details: nmethod evolution info -// 0x02000000 | 33554432 - impl details: annotation updates -// 0x04000000 | 67108864 - impl details: StackMapTable updates -// 0x08000000 | 134217728 - impl details: OopMapCache updates -// 0x10000000 | 268435456 - unused -// 0x20000000 | 536870912 - unused -// 0x40000000 | 1073741824 - unused -// 0x80000000 | 2147483648 - unused -// -// Note: The ResourceMark is to cleanup resource allocated args. -// The "while (0)" is so we can use semi-colon at end of RC_TRACE(). -#define RC_TRACE(level, args) \ - if ((TraceRedefineClasses & level) != 0) { \ - ResourceMark rm; \ - tty->print("RedefineClasses-0x%x: ", level); \ - tty->print_cr args; \ - } while (0) +#define IF_TRACE_RC1 if (TraceRedefineClasses >= 1) +#define IF_TRACE_RC2 if (TraceRedefineClasses >= 2) +#define IF_TRACE_RC3 if (TraceRedefineClasses >= 3) +#define IF_TRACE_RC4 if (TraceRedefineClasses >= 4) +#define IF_TRACE_RC5 if (TraceRedefineClasses >= 5) -#define RC_TRACE_WITH_THREAD(level, thread, args) \ - if ((TraceRedefineClasses & level) != 0) { \ - ResourceMark rm(thread); \ - tty->print("RedefineClasses-0x%x: ", level); \ - tty->print_cr args; \ - } while (0) - -#define RC_TRACE_MESG(args) \ - { \ - ResourceMark rm; \ - tty->print("RedefineClasses: "); \ - tty->print_cr args; \ - } while (0) - -// Macro for checking if TraceRedefineClasses has a specific bit -// enabled. Returns true if the bit specified by level is set. -#define RC_TRACE_ENABLED(level) ((TraceRedefineClasses & level) != 0) - -// Macro for checking if TraceRedefineClasses has one or more bits -// set in a range of bit values. Returns true if one or more bits -// is set in the range from low..high inclusive. Assumes that low -// and high are single bit values. -// -// ((high << 1) - 1) -// Yields a mask that removes bits greater than the high bit value. -// This algorithm doesn't work with highest bit. -// ~(low - 1) -// Yields a mask that removes bits lower than the low bit value. -#define RC_TRACE_IN_RANGE(low, high) \ -(((TraceRedefineClasses & ((high << 1) - 1)) & ~(low - 1)) != 0) +#define TRACE_RC1 if (TraceRedefineClasses >= 1) tty->print("TraceRedefineClasses-1: "); if (TraceRedefineClasses >= 1) tty->print_cr +#define TRACE_RC2 if (TraceRedefineClasses >= 2) tty->print(" TraceRedefineClasses-2: "); if (TraceRedefineClasses >= 2) tty->print_cr +#define TRACE_RC3 if (TraceRedefineClasses >= 3) tty->print(" TraceRedefineClasses-3: "); if (TraceRedefineClasses >= 3) tty->print_cr +#define TRACE_RC4 if (TraceRedefineClasses >= 4) tty->print(" TraceRedefineClasses-4: "); if (TraceRedefineClasses >= 4) tty->print_cr +#define TRACE_RC5 if (TraceRedefineClasses >= 5) tty->print(" TraceRedefineClasses-5: "); if (TraceRedefineClasses >= 5) tty->print_cr // Timer support macros. Only do timer operations if timer tracing // is enabled. The "while (0)" is so we can use semi-colon at end of // the macro. #define RC_TIMER_START(t) \ - if (RC_TRACE_ENABLED(0x00000004)) { \ + if (TimeRedefineClasses) { \ t.start(); \ } while (0) #define RC_TIMER_STOP(t) \ - if (RC_TRACE_ENABLED(0x00000004)) { \ + if (TimeRedefineClasses) { \ t.stop(); \ } while (0) diff -r f5603a6e5042 src/share/vm/prims/methodComparator.cpp --- a/src/share/vm/prims/methodComparator.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/prims/methodComparator.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -37,10 +37,9 @@ if (old_method->code_size() != new_method->code_size()) return false; if (check_stack_and_locals_size(old_method, new_method) != 0) { - // RC_TRACE macro has an embedded ResourceMark - RC_TRACE(0x00800000, ("Methods %s non-comparable with diagnosis %d", + TRACE_RC4("Methods %s non-comparable with diagnosis %d", old_method->name()->as_C_string(), - check_stack_and_locals_size(old_method, new_method))); + check_stack_and_locals_size(old_method, new_method)); return false; } @@ -60,6 +59,37 @@ if (! args_same(c_old, c_new)) return false; } + + // (tw) Added exception table comparison to EMCP comparison + + typeArrayOop ex_old = old_method->constMethod()->exception_table(); + typeArrayOop ex_new = new_method->constMethod()->exception_table(); + + if (ex_old == NULL && ex_new != NULL) return false; + if (ex_old != NULL && ex_new == NULL) return false; + + if (ex_old != NULL && ex_new != NULL && ex_old->length() == ex_new->length()) { + // Per entry: + /* start */ + /* limit */ + /* goto pc */ + /* cp index */ + for (int i=0; ilength(); i++) { + int old_val = ex_old->int_at(i); + int new_val = ex_new->int_at(i); + if ((i + 1) % 4 == 0) { + if (old_val == 0 || new_val == 0) { + if (old_val != new_val) return false; + } else if ((_old_cp->klass_at_noresolve(old_val) != _new_cp->klass_at_noresolve(new_val))) + return false; + } else { + if (old_val != new_val) { + return false; + } + } + } + } + return true; } @@ -109,10 +139,9 @@ // Now we can test all forward jumps for (int i = 0; i < fwd_jmps.length() / 2; i++) { if (! bci_map.old_and_new_locations_same(fwd_jmps.at(i*2), fwd_jmps.at(i*2+1))) { - RC_TRACE(0x00800000, - ("Fwd jump miss: old dest = %d, calc new dest = %d, act new dest = %d", + TRACE_RC4("Fwd jump miss: old dest = %d, calc new dest = %d, act new dest = %d", fwd_jmps.at(i*2), bci_map.new_bci_for_old(fwd_jmps.at(i*2)), - fwd_jmps.at(i*2+1))); + fwd_jmps.at(i*2+1)); return false; } } diff -r f5603a6e5042 src/share/vm/prims/nativeLookup.cpp --- a/src/share/vm/prims/nativeLookup.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/prims/nativeLookup.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -1,4 +1,4 @@ -/* +* * Copyright (c) 1997, 2009, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -24,7 +24,7 @@ # include "incls/_precompiled.incl" # include "incls/_nativeLookup.cpp.incl" - +# include "incls/_jvmtiEnv.cpp.incl" static void mangle_name_on(outputStream* st, symbolOop name, int begin, int end) { char* bytes = (char*)name->bytes() + begin; @@ -82,6 +82,39 @@ void JNICALL JVM_RegisterPerfMethods(JNIEnv *env, jclass perfclass); } +// Helper function to call redefineClasses from Java Code +JVM_ENTRY(int, JVM_RedefineClassesHelper(JNIEnv *env, jclass cb, jclass target, jbyteArray bytes)) + ResourceMark rm(THREAD); + + JavaThread* current_thread = JavaThread::current(); + jbyte* bytecodes = NULL; + const int class_count = 1; + jvmtiClassDefinition* class_definitions = NEW_RESOURCE_ARRAY(jvmtiClassDefinition, class_count); + + { + ThreadToNativeFromVM ttnfv(thread); + bytecodes = env->GetByteArrayElements(bytes, false); + class_definitions[0].klass = target; + class_definitions[0].class_byte_count = env->GetArrayLength(bytes); + class_definitions[0].class_bytes = (unsigned char*)bytecodes; + } + + VM_RedefineClasses op(class_count, class_definitions, jvmti_class_load_kind_retransform); + VMThread::execute(&op); + int result = op.check_error(); + + { + ThreadToNativeFromVM ttnfv(thread); + if (env->ExceptionOccurred()) { + return -1; + } + env->ReleaseByteArrayElements(bytes, bytecodes, 0); + } + + return result; +JVM_END + + static address lookup_special_native(char* jni_name) { // NB: To ignore the jni prefix and jni postfix strstr is used matching. if (!JDK_Version::is_gte_jdk14x_version()) { @@ -135,6 +168,9 @@ return entry; } } + if(strstr(jni_name, "Java_at_ssw_hotswap_ClassRedefinition_redefineClasses") != NULL) { + return CAST_FROM_FN_PTR(address, JVM_RedefineClassesHelper); + } // Otherwise call static method findNative in ClassLoader KlassHandle klass (THREAD, SystemDictionary::ClassLoader_klass()); diff -r f5603a6e5042 src/share/vm/runtime/arguments.cpp --- a/src/share/vm/runtime/arguments.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/runtime/arguments.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -25,7 +25,7 @@ #include "incls/_precompiled.incl" #include "incls/_arguments.cpp.incl" -#define DEFAULT_VENDOR_URL_BUG "http://java.sun.com/webapps/bugreport/crash.jsp" +#define DEFAULT_VENDOR_URL_BUG "http://ssw.jku.at/dcevm/bugreport/" #define DEFAULT_JAVA_LAUNCHER "generic" char** Arguments::_jvm_flags_array = NULL; @@ -1704,6 +1704,15 @@ status = false; } + // (tw) Must use serial GC + if (!UseSerialGC && i >= 1) { + jio_fprintf(defaultStream::error_stream(), + "Must use the serial GC in the Dynamic Code Evolution VM\n"); + status = false; + } else { + UseSerialGC = true; + } + return status; } diff -r f5603a6e5042 src/share/vm/runtime/deoptimization.cpp --- a/src/share/vm/runtime/deoptimization.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/runtime/deoptimization.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -513,6 +513,29 @@ // Cleanup thread deopt data cleanup_deopt_info(thread, array); + // (tw) Redefinition support: Check if we need to transfer method execution points to new versions + { + ResourceMark res_mark; + + // Verify that the just-unpacked frames match the interpreter's + // notions of expression stack and locals + vframeArray* cur_array = thread->vframe_array_last(); + RegisterMap rm(thread, false); + rm.set_include_argument_oops(false); + for (int i = 0; i < cur_array->frames(); i++) { + vframeArrayElement* el = cur_array->element(i); + frame* frame = el->iframe(); + guarantee(frame->is_interpreted_frame(), "Wrong frame type"); + RegisterMap reg_map(thread); + vframe* vf = vframe::new_vframe(frame, ®_map, thread); + interpretedVFrame *iframe = (interpretedVFrame *)vf; + methodOop method = iframe->method(); + int bci = iframe->bci(); + method = method->newest_version(); + iframe->set_method(method, bci); + } + } + #ifndef PRODUCT if (VerifyStack) { ResourceMark res_mark; diff -r f5603a6e5042 src/share/vm/runtime/frame.cpp --- a/src/share/vm/runtime/frame.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/runtime/frame.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -357,6 +357,12 @@ *interpreter_frame_method_addr() = method; } +// (tw) Sets constant pool cache oop +void frame::interpreter_frame_set_cache(constantPoolCacheOop cp) { + assert(is_interpreted_frame(), "interpreted frame expected"); + *interpreter_frame_cache_addr() = cp; +} + void frame::interpreter_frame_set_bcx(intptr_t bcx) { assert(is_interpreted_frame(), "Not an interpreted frame"); if (ProfileInterpreter) { @@ -372,19 +378,27 @@ // The bcx was just converted from bci to bcp. // Convert the mdx in parallel. methodDataOop mdo = interpreter_frame_method()->method_data(); - assert(mdo != NULL, ""); - int mdi = mdx - 1; // We distinguish valid mdi from zero by adding one. - address mdp = mdo->di_to_dp(mdi); - interpreter_frame_set_mdx((intptr_t)mdp); + if (mdo == NULL) { + interpreter_frame_set_mdx(0); + } else { + assert(mdo != NULL, ""); + int mdi = mdx - 1; // We distinguish valid mdi from zero by adding one. + address mdp = mdo->di_to_dp(mdi); + interpreter_frame_set_mdx((intptr_t)mdp); + } } } else { if (is_now_bci) { // The bcx was just converted from bcp to bci. // Convert the mdx in parallel. methodDataOop mdo = interpreter_frame_method()->method_data(); - assert(mdo != NULL, ""); - int mdi = mdo->dp_to_di((address)mdx); - interpreter_frame_set_mdx((intptr_t)mdi + 1); // distinguish valid from 0. + if (mdo == NULL) { + interpreter_frame_set_mdx(0); + } else { + assert(mdo != NULL, ""); + int mdi = mdo->dp_to_di((address)mdx); + interpreter_frame_set_mdx((intptr_t)mdi + 1); // distinguish valid from 0. + } } } } diff -r f5603a6e5042 src/share/vm/runtime/frame.hpp --- a/src/share/vm/runtime/frame.hpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/runtime/frame.hpp Fri Dec 17 13:24:08 2010 +0100 @@ -295,6 +295,7 @@ // Method & constant pool cache methodOop interpreter_frame_method() const; void interpreter_frame_set_method(methodOop method); + void interpreter_frame_set_cache(constantPoolCacheOop method); methodOop* interpreter_frame_method_addr() const; constantPoolCacheOop* interpreter_frame_cache_addr() const; #ifdef PPC diff -r f5603a6e5042 src/share/vm/runtime/globals.hpp --- a/src/share/vm/runtime/globals.hpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/runtime/globals.hpp Fri Dec 17 13:24:08 2010 +0100 @@ -1130,6 +1130,11 @@ product(intx, TraceRedefineClasses, 0, \ "Trace level for JVMTI RedefineClasses") \ \ + product(bool, TimeRedefineClasses, false, \ + "Measure timing for JVMTI RedefineClasses") \ + \ + product(bool, AllowAdvancedClassRedefinition, true, \ + "Allow advanced class redefinition beyond swapping method bodies")\ develop(bool, StressMethodComparator, false, \ "run the MethodComparator on all loaded methods") \ \ diff -r f5603a6e5042 src/share/vm/runtime/interfaceSupport.hpp --- a/src/share/vm/runtime/interfaceSupport.hpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/runtime/interfaceSupport.hpp Fri Dec 17 13:24:08 2010 +0100 @@ -257,7 +257,7 @@ ThreadToNativeFromVM(JavaThread *thread) : ThreadStateTransition(thread) { // We are leaving the VM at this point and going directly to native code. // Block, if we are in the middle of a safepoint synchronization. - assert(!thread->owns_locks(), "must release all locks when leaving VM"); + assert(!thread->owns_locks_but_redefine_classes_lock(), "must release all locks when leaving VM"); thread->frame_anchor()->make_walkable(thread); trans_and_fence(_thread_in_vm, _thread_in_native); // Check for pending. async. exceptions or suspends. diff -r f5603a6e5042 src/share/vm/runtime/javaCalls.cpp --- a/src/share/vm/runtime/javaCalls.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/runtime/javaCalls.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -33,7 +33,7 @@ bool clear_pending_exception = true; guarantee(thread->is_Java_thread(), "crucial check - the VM thread cannot and must not escape to Java code"); - assert(!thread->owns_locks(), "must release all locks when leaving VM"); + assert(!thread->owns_locks_but_redefine_classes_lock(), "must release all locks when leaving VM"); guarantee(!thread->is_Compiler_thread(), "cannot make java calls from the compiler"); _result = result; diff -r f5603a6e5042 src/share/vm/runtime/jniHandles.cpp --- a/src/share/vm/runtime/jniHandles.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/runtime/jniHandles.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -96,6 +96,10 @@ } jmethodID JNIHandles::make_jmethod_id(methodHandle mh) { + if (mh->newest_version() != mh()) { + methodHandle mh_new(Thread::current(), mh()->newest_version()); + return (jmethodID) make_weak_global(mh_new); + } return (jmethodID) make_weak_global(mh); } diff -r f5603a6e5042 src/share/vm/runtime/mutex.cpp --- a/src/share/vm/runtime/mutex.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/runtime/mutex.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -1195,7 +1195,7 @@ // in increasing rank order (modulo any native ranks) for (tmp = locks; tmp != NULL; tmp = tmp->next()) { if (tmp->next() != NULL) { - assert(tmp->rank() == Mutex::native || + assert(tmp->rank() == Mutex::native || tmp->rank() == Mutex::redefine_classes || tmp->rank() <= tmp->next()->rank(), "mutex rank anomaly?"); } } @@ -1215,7 +1215,7 @@ // in increasing rank order (modulo any native ranks) for (tmp = locks; tmp != NULL; tmp = tmp->next()) { if (tmp->next() != NULL) { - assert(tmp->rank() == Mutex::native || + assert(tmp->rank() == Mutex::native || tmp->rank() == Mutex::redefine_classes || tmp->rank() <= tmp->next()->rank(), "mutex rank anomaly?"); } } @@ -1282,6 +1282,7 @@ // already hold Terminator_lock - may happen because of periodic safepoints if (this->rank() != Mutex::native && this->rank() != Mutex::suspend_resume && + this->rank() != Mutex::redefine_classes && locks != NULL && locks->rank() <= this->rank() && !SafepointSynchronize::is_at_safepoint() && this != Interrupt_lock && this != ProfileVM_lock && diff -r f5603a6e5042 src/share/vm/runtime/mutex.hpp --- a/src/share/vm/runtime/mutex.hpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/runtime/mutex.hpp Fri Dec 17 13:24:08 2010 +0100 @@ -102,7 +102,8 @@ barrier = safepoint + 1, nonleaf = barrier + 1, max_nonleaf = nonleaf + 900, - native = max_nonleaf + 1 + native = max_nonleaf + 1, + redefine_classes = native + 1 }; // The WaitSet and EntryList linked lists are composed of ParkEvents. diff -r f5603a6e5042 src/share/vm/runtime/mutexLocker.cpp --- a/src/share/vm/runtime/mutexLocker.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/runtime/mutexLocker.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -34,6 +34,7 @@ // Consider using GCC's __read_mostly. Mutex* Patching_lock = NULL; +Mutex* RedefineClasses_lock = NULL; Monitor* SystemDictionary_lock = NULL; Mutex* PackageTable_lock = NULL; Mutex* CompiledIC_lock = NULL; @@ -253,6 +254,7 @@ def(Debug2_lock , Mutex , nonleaf+4, true ); def(Debug3_lock , Mutex , nonleaf+4, true ); def(ProfileVM_lock , Monitor, nonleaf+4, false); // used for profiling of the VMThread + def(RedefineClasses_lock , Mutex, nonleaf+7, false ); // for ensuring that class redefinition is not done in parallel def(CompileThread_lock , Monitor, nonleaf+5, false ); } diff -r f5603a6e5042 src/share/vm/runtime/mutexLocker.hpp --- a/src/share/vm/runtime/mutexLocker.hpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/runtime/mutexLocker.hpp Fri Dec 17 13:24:08 2010 +0100 @@ -25,6 +25,7 @@ // Mutexes used in the VM. extern Mutex* Patching_lock; // a lock used to guard code patching of compiled code +extern Mutex* RedefineClasses_lock; // a lock on class redefinition 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 -r f5603a6e5042 src/share/vm/runtime/reflection.cpp --- a/src/share/vm/runtime/reflection.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/runtime/reflection.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -449,7 +449,8 @@ // sun/reflect/MagicAccessorImpl subclasses to succeed trivially. if ( JDK_Version::is_gte_jdk14x_version() && UseNewReflection - && Klass::cast(current_class)->is_subclass_of(SystemDictionary::reflect_MagicAccessorImpl_klass())) { + && (Klass::cast(current_class)->is_subclass_of(SystemDictionary::reflect_MagicAccessorImpl_klass()) || + Klass::cast(current_class)->is_subclass_of(SystemDictionary::reflect_MagicAccessorImpl_klass()->klass_part()->newest_version()))) { return true; } @@ -505,6 +506,12 @@ AccessFlags access, bool classloader_only, bool protected_restriction) { + + // (tw) Decide accessibility based on active version + if (current_class != NULL) { + current_class = current_class->klass_part()->active_version(); + } + // Verify that current_class can access a field of field_class, where that // field's access bits are "access". We assume that we've already verified // that current_class can access field_class. @@ -546,7 +553,8 @@ // sun/reflect/MagicAccessorImpl subclasses to succeed trivially. if ( JDK_Version::is_gte_jdk14x_version() && UseNewReflection - && Klass::cast(current_class)->is_subclass_of(SystemDictionary::reflect_MagicAccessorImpl_klass())) { + && (Klass::cast(current_class)->is_subclass_of(SystemDictionary::reflect_MagicAccessorImpl_klass()) || + Klass::cast(current_class)->is_subclass_of(SystemDictionary::reflect_MagicAccessorImpl_klass()->klass_part()->newest_version()))) { return true; } diff -r f5603a6e5042 src/share/vm/runtime/sharedRuntime.cpp --- a/src/share/vm/runtime/sharedRuntime.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/runtime/sharedRuntime.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -511,21 +511,13 @@ // JRT_LEAF(int, SharedRuntime::rc_trace_method_entry( JavaThread* thread, methodOopDesc* method)) - assert(RC_TRACE_IN_RANGE(0x00001000, 0x00002000), "wrong call"); + assert(TraceRedefineClasses >= 4, "wrong call"); if (method->is_obsolete()) { // We are calling an obsolete method, but this is not necessarily // an error. Our method could have been redefined just after we // fetched the methodOop from the constant pool. - - // RC_TRACE macro has an embedded ResourceMark - RC_TRACE_WITH_THREAD(0x00001000, thread, - ("calling obsolete method '%s'", - method->name_and_sig_as_C_string())); - if (RC_TRACE_ENABLED(0x00002000)) { - // this option is provided to debug calls to obsolete methods - guarantee(false, "faulting at call to an obsolete method."); - } + TRACE_RC4("calling obsolete method '%s'", method->name_and_sig_as_C_string()); } return 0; JRT_END @@ -986,7 +978,20 @@ if (JvmtiExport::can_hotswap_or_post_breakpoint()) { int retry_count = 0; while (!HAS_PENDING_EXCEPTION && callee_method->is_old() && - callee_method->method_holder() != SystemDictionary::Object_klass()) { + callee_method->method_holder()->klass_part()->newest_version() != SystemDictionary::Object_klass()->klass_part()->newest_version()) { + + // (tw) If we are executing an old method, this is OK! + { + ResourceMark rm(thread); + RegisterMap cbl_map(thread, false); + frame caller_frame = thread->last_frame().sender(&cbl_map); + + CodeBlob* caller_cb = caller_frame.cb(); + guarantee(caller_cb != NULL && caller_cb->is_nmethod(), "must be called from nmethod"); + nmethod* caller_nm = caller_cb->as_nmethod_or_null(); + if (caller_nm->method()->is_old()) break; + } + // 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 -r f5603a6e5042 src/share/vm/runtime/thread.cpp --- a/src/share/vm/runtime/thread.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/runtime/thread.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -109,6 +109,8 @@ _lgrp_id = -1; _osthread = NULL; + _redefine_classes_mutex = new Mutex(Mutex::redefine_classes, "redefine classes lock", false); + // allocated data structures set_resource_area(new ResourceArea()); set_handle_area(new HandleArea(NULL)); @@ -141,6 +143,7 @@ omFreeProvision = 32 ; omInUseList = NULL ; omInUseCount = 0 ; + _pretend_new_universe = false; _SR_lock = new Monitor(Mutex::suspend_resume, "SR_lock", true); _suspend_flags = 0; @@ -747,6 +750,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; +} + #endif @@ -1397,7 +1409,7 @@ ThreadStateTransition::transition_and_fence(this, _thread_new, _thread_in_vm); assert(JavaThread::current() == this, "sanity check"); - assert(!Thread::current()->owns_locks(), "sanity check"); + assert(!Thread::current()->owns_locks_but_redefine_classes_lock(), "sanity check"); DTRACE_THREAD_PROBE(start, this); @@ -2842,13 +2854,14 @@ // Create a CompilerThread CompilerThread::CompilerThread(CompileQueue* queue, CompilerCounters* counters) -: JavaThread(&compiler_thread_entry) { +: JavaThread(&compiler_thread_entry), _should_bailout(false) { _env = NULL; _log = NULL; _task = NULL; _queue = queue; _counters = counters; _buffer_blob = NULL; + _compilation_mutex = new Mutex(Mutex::redefine_classes, "compilationMutex", false); #ifndef PRODUCT _ideal_graph_printer = NULL; diff -r f5603a6e5042 src/share/vm/runtime/thread.hpp --- a/src/share/vm/runtime/thread.hpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/runtime/thread.hpp Fri Dec 17 13:24:08 2010 +0100 @@ -162,11 +162,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; } + Mutex* redefine_classes_mutex() { return _redefine_classes_mutex; } private: // Debug tracing static void trace(const char* msg, const Thread* const thread) PRODUCT_RETURN; + Mutex* _redefine_classes_mutex; + // Active_handles points to a block of handles JNIHandleBlock* _active_handles; @@ -438,10 +441,15 @@ uintptr_t _self_raw_id; // used by get_thread (mutable) int _lgrp_id; + + bool _pretend_new_universe; + public: // Stack overflow support address stack_base() const { assert(_stack_base != NULL,"Sanity check"); return _stack_base; } + void set_pretend_new_universe(bool b) { if (_pretend_new_universe != b) { if (TraceRedefineClasses >= 5) tty->print_cr("Changing pretend universe to %d", (int)b); _pretend_new_universe = b; } } + bool pretend_new_universe() { return _pretend_new_universe; } void set_stack_base(address base) { _stack_base = base; } size_t stack_size() const { return _stack_size; } void set_stack_size(size_t size) { _stack_size = size; } @@ -476,6 +484,7 @@ void print_owned_locks() const { print_owned_locks_on(tty); } Monitor * owned_locks() const { return _owned_locks; } bool owns_locks() const { return owned_locks() != NULL; } + bool owns_locks_but_redefine_classes_lock() const; bool owns_locks_but_compiled_lock() const; // Deadlock detection @@ -1611,6 +1620,8 @@ CompileTask* _task; CompileQueue* _queue; BufferBlob* _buffer_blob; + bool _should_bailout; + Mutex* _compilation_mutex; public: @@ -1618,12 +1629,16 @@ CompilerThread(CompileQueue* queue, CompilerCounters* counters); + bool should_bailout() const { return _should_bailout; } + void set_should_bailout(bool b) { _should_bailout = false; } + bool is_Compiler_thread() const { return true; } // Hide this compiler thread from external view. bool is_hidden_from_external_view() const { return true; } CompileQueue* queue() { return _queue; } CompilerCounters* counters() { return _counters; } + Mutex *compilation_mutex() { return _compilation_mutex; } // Get/set the thread's compilation environment. ciEnv* env() { return _env; } diff -r f5603a6e5042 src/share/vm/runtime/vframe.cpp --- a/src/share/vm/runtime/vframe.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/runtime/vframe.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -232,6 +232,46 @@ return fr().interpreter_frame_method(); } +// (tw) Sets interpreter frame method. +void interpretedVFrame::set_method(methodOop new_method, int new_bci) { + methodOop old_method = fr().interpreter_frame_method(); + int old_stack_size = fr().interpreter_frame_expression_stack_size(); + if (old_method == new_method) return; + u_char *old_bcp = bcp(); + int old_bci = bci(); + fr().interpreter_frame_set_method(new_method); + fr().interpreter_frame_set_cache(new_method->constants()->cache()); + u_char *new_bcp = new_method->code_base() + new_bci; + assert(new_method->bcp_from(new_bci) == new_bcp, ""); + + set_bcp(new_bcp); + + Bytecodes::Code code = Bytecodes::java_code_at(old_bcp); + assert(Bytecodes::java_code_at(new_bcp) == code, "must have same bytecode at this position"); + + switch (code) { + case Bytecodes::_invokevirtual : + case Bytecodes::_invokespecial : + case Bytecodes::_invokestatic : + case Bytecodes::_invokeinterface: { + int old_index = Bytes::get_native_u2(old_bcp+1); + int new_index = Bytes::get_native_u2(new_bcp+1); + new_method->constants()->cache()->entry_at(new_index)->copy_from(old_method->constants()->cache()->entry_at(old_index)); + break; + } + + case Bytecodes::_invokedynamic: { + int old_index = Bytes::get_native_u4(old_bcp+1); + int new_index = Bytes::get_native_u4(new_bcp+1); + new_method->constants()->cache()->secondary_entry_at(new_index)->copy_from(old_method->constants()->cache()->secondary_entry_at(old_index)); + break; + } + } + + int new_stack_size = fr().interpreter_frame_expression_stack_size(); + assert(new_method->validate_bci_from_bcx((intptr_t)new_bcp) == new_bci, ""); +} + StackValueCollection* interpretedVFrame::locals() const { int length = method()->max_locals(); diff -r f5603a6e5042 src/share/vm/runtime/vframe.hpp --- a/src/share/vm/runtime/vframe.hpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/runtime/vframe.hpp Fri Dec 17 13:24:08 2010 +0100 @@ -150,6 +150,7 @@ StackValueCollection* locals() const; StackValueCollection* expressions() const; GrowableArray* monitors() const; + void set_method(methodOop method, int new_bci); void set_locals(StackValueCollection* values) const; diff -r f5603a6e5042 src/share/vm/runtime/vmThread.cpp --- a/src/share/vm/runtime/vmThread.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/runtime/vmThread.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -624,6 +624,9 @@ void VMThread::oops_do(OopClosure* f, CodeBlobClosure* cf) { Thread::oops_do(f, cf); _vm_queue->oops_do(f); + if (_cur_vm_operation != NULL) { + _cur_vm_operation->oops_do(f); + } } //------------------------------------------------------------------------------------------------------------------ diff -r f5603a6e5042 src/share/vm/runtime/vm_version.cpp --- a/src/share/vm/runtime/vm_version.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/runtime/vm_version.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -40,10 +40,11 @@ #error HOTSPOT_BUILD_TARGET must be defined #endif +// (tw) added DCE VM build number #ifdef PRODUCT - #define VM_RELEASE HOTSPOT_RELEASE_VERSION + #define VM_RELEASE "0.2-b02-internal, " HOTSPOT_RELEASE_VERSION #else - #define VM_RELEASE HOTSPOT_RELEASE_VERSION "-" HOTSPOT_BUILD_TARGET + #define VM_RELEASE "0.2-b02-internal-debug, " HOTSPOT_RELEASE_VERSION "-" HOTSPOT_BUILD_TARGET #endif // HOTSPOT_RELEASE_VERSION must follow the release version naming convention @@ -110,6 +111,12 @@ #ifndef HOTSPOT_VM_DISTRO #error HOTSPOT_VM_DISTRO must be defined #endif + +// (tw) Set VM name to DCE +#undef HOTSPOT_VM_DISTRO +#define HOTSPOT_VM_DISTRO "Dynamic Code Evolution" +#define VM_RELEASE_HOTSP + #define VMNAME HOTSPOT_VM_DISTRO " " VMLP VMTYPE " VM" const char* Abstract_VM_Version::vm_name() { diff -r f5603a6e5042 src/share/vm/utilities/exceptions.cpp --- a/src/share/vm/utilities/exceptions.cpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/utilities/exceptions.cpp Fri Dec 17 13:24:08 2010 +0100 @@ -239,6 +239,8 @@ assert(thread->is_Java_thread(), "can only be called by a Java thread"); assert(!thread->has_pending_exception(), "already has exception"); + bool old_pretend_value = Thread::current()->pretend_new_universe(); + Thread::current()->set_pretend_new_universe(false); Handle h_exception; // Resolve exception klass @@ -286,6 +288,8 @@ h_exception = Handle(thread, thread->pending_exception()); thread->clear_pending_exception(); } + + Thread::current()->set_pretend_new_universe(old_pretend_value); return h_exception; } diff -r f5603a6e5042 src/share/vm/utilities/growableArray.hpp --- a/src/share/vm/utilities/growableArray.hpp Wed Nov 17 22:42:08 2010 -0800 +++ b/src/share/vm/utilities/growableArray.hpp Fri Dec 17 13:24:08 2010 +0100 @@ -124,6 +124,33 @@ assert(on_stack(), "fast ResourceObj path only"); return (void*)resource_allocate_bytes(thread, elementSize * _max); } + +}; + +template class Pair : public StackObj +{ +private: + E _left; + F _right; + +public: + + Pair() { + + } + + Pair(E left, F right) { + this->_left = left; + this->_right = right; + } + + E left() { + return _left; + } + + F right() { + return _right; + } }; template class GrowableArray : public GenericGrowableArray {