1/* 2 * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24/* 25 * @test 26 * @bug 8076110 27 * @summary Redefine running methods that have cached resolution errors 28 * @library /test/lib 29 * @modules java.base/jdk.internal.misc 30 * @modules java.base/jdk.internal.org.objectweb.asm 31 * java.instrument 32 * jdk.jartool/sun.tools.jar 33 * @run main RedefineClassHelper 34 * @run main/othervm -javaagent:redefineagent.jar -Xlog:redefine+class+iklass+add=trace,redefine+class+iklass+purge=trace RedefineRunningMethodsWithResolutionErrors 35 */ 36 37import jdk.internal.org.objectweb.asm.ClassWriter; 38import jdk.internal.org.objectweb.asm.Label; 39import jdk.internal.org.objectweb.asm.MethodVisitor; 40import jdk.internal.org.objectweb.asm.Opcodes; 41 42import java.lang.reflect.InvocationTargetException; 43 44public class RedefineRunningMethodsWithResolutionErrors extends ClassLoader implements Opcodes { 45 46 @Override 47 protected Class<?> findClass(String name) throws ClassNotFoundException { 48 if (name.equals("C")) { 49 byte[] b = loadC(false); 50 return defineClass(name, b, 0, b.length); 51 } else { 52 return super.findClass(name); 53 } 54 } 55 56 private static byte[] loadC(boolean redefine) { 57 ClassWriter cw = new ClassWriter(0); 58 59 cw.visit(52, ACC_SUPER | ACC_PUBLIC, "C", null, "java/lang/Object", null); 60 { 61 MethodVisitor mv; 62 63 mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "m", "()V", null, null); 64 mv.visitCode(); 65 66 // First time we run we will: 67 // 1) Cache resolution errors 68 // 2) Redefine the class / method 69 // 3) Try to read the resolution errors that were cached 70 // 71 // The redefined method will never run, throw error to be sure 72 if (redefine) { 73 createThrowRuntimeExceptionCode(mv, "The redefined method was called"); 74 } else { 75 createMethodBody(mv); 76 } 77 mv.visitMaxs(3, 0); 78 mv.visitEnd(); 79 } 80 cw.visitEnd(); 81 return cw.toByteArray(); 82 } 83 84 private static void createMethodBody(MethodVisitor mv) { 85 Label classExists = new Label(); 86 87 // Cache resolution errors 88 createLoadNonExistentClassCode(mv, classExists); 89 90 // Redefine our own class and method 91 mv.visitMethodInsn(INVOKESTATIC, "RedefineRunningMethodsWithResolutionErrors", "redefine", "()V"); 92 93 // Provoke the same error again to make sure the resolution error cache works 94 createLoadNonExistentClassCode(mv, classExists); 95 96 // Test passed 97 mv.visitInsn(RETURN); 98 99 mv.visitFrame(F_SAME, 0, new Object[0], 0, new Object[0]); 100 mv.visitLabel(classExists); 101 102 createThrowRuntimeExceptionCode(mv, "Loaded class that shouldn't exist (\"NonExistentClass\")"); 103 } 104 105 private static void createLoadNonExistentClassCode(MethodVisitor mv, Label classExists) { 106 Label tryLoadBegin = new Label(); 107 Label tryLoadEnd = new Label(); 108 Label catchLoadBlock = new Label(); 109 mv.visitTryCatchBlock(tryLoadBegin, tryLoadEnd, catchLoadBlock, "java/lang/NoClassDefFoundError"); 110 111 // Try to load a class that does not exist to provoke resolution errors 112 mv.visitLabel(tryLoadBegin); 113 mv.visitMethodInsn(INVOKESTATIC, "NonExistentClass", "nonExistentMethod", "()V"); 114 mv.visitLabel(tryLoadEnd); 115 116 // No NoClassDefFoundError means NonExistentClass existed, which shouldn't happen 117 mv.visitJumpInsn(GOTO, classExists); 118 119 mv.visitFrame(F_SAME1, 0, new Object[0], 1, new Object[] { "java/lang/NoClassDefFoundError" }); 120 mv.visitLabel(catchLoadBlock); 121 122 // Ignore the expected NoClassDefFoundError 123 mv.visitInsn(POP); 124 } 125 126 private static void createThrowRuntimeExceptionCode(MethodVisitor mv, String msg) { 127 mv.visitTypeInsn(NEW, "java/lang/RuntimeException"); 128 mv.visitInsn(DUP); 129 mv.visitLdcInsn(msg); 130 mv.visitMethodInsn(INVOKESPECIAL, "java/lang/RuntimeException", "<init>", "(Ljava/lang/String;)V"); 131 mv.visitInsn(ATHROW); 132 } 133 134 private static Class<?> c; 135 136 public static void redefine() throws Exception { 137 RedefineClassHelper.redefineClass(c, loadC(true)); 138 } 139 140 public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException { 141 c = Class.forName("C", true, new RedefineRunningMethodsWithResolutionErrors()); 142 c.getMethod("m").invoke(null); 143 } 144} 145