1 /* 2 * Copyright (c) 2019, 2021, 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 8232613 27 * @summary Ensure Object natives stay registered after redefinition 28 * @requires vm.jvmti 29 * @library /test/lib 30 * @modules java.base/jdk.internal.misc 31 * java.base/jdk.internal.org.objectweb.asm 32 * java.compiler 33 * java.instrument 34 * jdk.jartool/sun.tools.jar 35 * @run main RedefineObject buildagent 36 * @run main/othervm -javaagent:redefineagent.jar RedefineObject 37 */ 38 39 import static jdk.test.lib.Asserts.assertTrue; 40 import jdk.test.lib.helpers.ClassFileInstaller; 41 import java.io.FileNotFoundException; 42 import java.io.PrintWriter; 43 import java.lang.RuntimeException; 44 import java.lang.instrument.ClassFileTransformer; 45 import java.lang.instrument.IllegalClassFormatException; 46 import java.lang.instrument.Instrumentation; 47 import java.lang.instrument.UnmodifiableClassException; 48 import java.security.ProtectionDomain; 49 import java.util.Arrays; 50 51 import jdk.internal.org.objectweb.asm.ClassReader; 52 import jdk.internal.org.objectweb.asm.ClassVisitor; 53 import jdk.internal.org.objectweb.asm.ClassWriter; 54 55 import static jdk.internal.org.objectweb.asm.Opcodes.ASM6; 56 import static jdk.internal.org.objectweb.asm.Opcodes.V1_8; 57 58 public class RedefineObject { 59 60 static Instrumentation inst; 61 62 public static void premain(String agentArgs, Instrumentation inst) { 63 RedefineObject.inst = inst; 64 } 65 66 static class Transformer implements ClassFileTransformer { 67 68 public byte[] asm(ClassLoader loader, String className, 69 Class<?> classBeingRedefined, 70 ProtectionDomain protectionDomain, byte[] classfileBuffer) 71 throws IllegalClassFormatException { 72 ClassWriter cw = new ClassWriter(0); 73 // Force an older ASM to force a bytecode update 74 ClassVisitor cv = new DummyClassVisitor(ASM6, cw) { }; 75 ClassReader cr = new ClassReader(classfileBuffer); 76 cr.accept(cv, 0); 77 byte[] bytes = cw.toByteArray(); 78 return bytes; 79 } 80 81 public class DummyClassVisitor extends ClassVisitor { 82 83 public DummyClassVisitor(int api, ClassVisitor cv) { 84 super(api, cv); 85 } 86 87 public void visit( 88 final int version, 89 final int access, 90 final String name, 91 final String signature, 92 final String superName, 93 final String[] interfaces) { 94 // Artificially lower to JDK 8 version to force a redefine 95 cv.visit(V1_8, access, name, signature, superName, interfaces); 96 } 97 } 98 99 @Override public byte[] transform(ClassLoader loader, String className, 100 Class<?> classBeingRedefined, 101 ProtectionDomain protectionDomain, byte[] classfileBuffer) 102 throws IllegalClassFormatException { 103 104 if (className.contains("java/lang/Object")) { 105 try { 106 // Here we remove and re-add the dummy fields. This shuffles the constant pool 107 return asm(loader, className, classBeingRedefined, protectionDomain, classfileBuffer); 108 } catch (Throwable e) { 109 // The retransform native code that called this method does not propagate 110 // exceptions. Instead of getting an uninformative generic error, catch 111 // problems here and print it, then exit. 112 e.printStackTrace(); 113 System.exit(1); 114 } 115 } 116 return null; 117 } 118 } 119 120 private static void buildAgent() { 121 try { 122 ClassFileInstaller.main("RedefineObject"); 123 } catch (Exception e) { 124 throw new RuntimeException("Could not write agent classfile", e); 125 } 126 127 try { 128 PrintWriter pw = new PrintWriter("MANIFEST.MF"); 129 pw.println("Premain-Class: RedefineObject"); 130 pw.println("Agent-Class: RedefineObject"); 131 pw.println("Can-Retransform-Classes: true"); 132 pw.close(); 133 } catch (FileNotFoundException e) { 134 throw new RuntimeException("Could not write manifest file for the agent", e); 135 } 136 137 sun.tools.jar.Main jarTool = new sun.tools.jar.Main(System.out, System.err, "jar"); 138 if (!jarTool.run(new String[] { "-cmf", "MANIFEST.MF", "redefineagent.jar", "RedefineObject.class" })) { 139 throw new RuntimeException("Could not write the agent jar file"); 140 } 141 } 142 143 public static void main(String[] args) throws Exception { 144 145 int objHash = System.identityHashCode(Object.class); 146 System.out.println("Object hashCode: " + objHash); 147 if (args.length == 1 && args[0].equals("buildagent")) { 148 buildAgent(); 149 return; 150 } 151 152 if (inst == null) { 153 throw new RuntimeException("Instrumentation object was null"); 154 } 155 156 try { 157 inst.addTransformer(new RedefineObject.Transformer(), true); 158 inst.retransformClasses(Object.class); 159 } catch (UnmodifiableClassException e) { 160 throw new RuntimeException(e); 161 } 162 163 // Exercise native methods on Object after transform 164 Object b = new Object(); 165 b.hashCode(); 166 167 C c = new C(); 168 assertTrue(c.hashCode() != c.clone().hashCode() || c != c.clone()); 169 assertTrue(c.clone() instanceof C); 170 c = (C)c.clone(); // native method on new Object 171 } 172 173 private static class C implements Cloneable { 174 @Override 175 protected Object clone() throws CloneNotSupportedException { 176 return super.clone(); 177 } 178 } 179 }