1 /* 2 * Copyright (c) 2018, 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 /** 26 * @test 27 * @summary test auxiliary classes 28 * 29 * @modules java.base/jdk.internal.org.objectweb.asm 30 * java.instrument 31 * @compile ../asmlib/Instrumentor.java DefineAuxClassTest.java DefineAuxClassApp.java 32 * @run shell/timeout=240 MakeAgent.sh 'Can-Retransform-Classes: true' 33 * @run main/othervm -javaagent:DefineAuxClassTest.jar p.DefineAuxClassApp 34 */ 35 36 package p; 37 38 import java.lang.instrument.ClassFileTransformer; 39 import java.lang.instrument.ClassFileTransformer.ClassDefiner; 40 import java.lang.instrument.Instrumentation; 41 import java.lang.instrument.IllegalClassFormatException; 42 43 import java.security.ProtectionDomain; 44 import java.util.stream.Stream; 45 import java.util.concurrent.atomic.AtomicBoolean; 46 import java.nio.file.*; 47 import java.io.IOException; 48 import java.io.OutputStream; 49 import jdk.internal.org.objectweb.asm.ClassReader; 50 import jdk.internal.org.objectweb.asm.ClassVisitor; 51 import jdk.internal.org.objectweb.asm.ClassWriter; 52 import jdk.internal.org.objectweb.asm.MethodVisitor; 53 import jdk.internal.org.objectweb.asm.Opcodes; 54 import static jdk.internal.org.objectweb.asm.Opcodes.*; 55 56 import asmlib.*; 57 58 class DefineAuxClassTest { 59 60 static ClassFileTransformer t1, t2, t3, t4; 61 static Instrumentation inst; 62 static boolean succeeded = true; 63 static int markCount = 0; 64 static int[] markGolden = {30, 40, 20, 30, 40, 20, 30, 40, 20, 30, 40, 20, 65 11, 40, 20, 11, 40, 20, 11, 40, 20, 11, 40, 20}; 66 67 static class Tr implements ClassFileTransformer { 68 final String trname; 69 final boolean onLoad; 70 final int loadIndex; 71 final boolean onRedef; 72 final int redefIndex; 73 final String cname; 74 final String nname; 75 final String auxClassHelperName; 76 final AtomicBoolean auxClassDefined; 77 78 Tr(String trname, boolean onLoad, int loadIndex, boolean onRedef, int redefIndex, 79 String cname, String nname) { 80 this.trname = trname; 81 this.onLoad = onLoad; 82 this.loadIndex = loadIndex; 83 this.onRedef = onRedef; 84 this.redefIndex = redefIndex; 85 this.cname = cname; 86 this.nname = nname; 87 88 int index = cname.lastIndexOf("."); 89 if (index == -1) { 90 this.auxClassHelperName = trname + "AuxClassHelper"; 91 } else { 92 String pn = cname.substring(0, index); 93 this.auxClassHelperName = pn + "." + trname + "AuxClassHelper"; 94 95 } 96 this.auxClassDefined = new AtomicBoolean(false); 97 } 98 99 public byte[] transform(ClassDefiner classDefiner, 100 Module module, 101 ClassLoader loader, 102 String className, 103 Class<?> classBeingRedefined, 104 ProtectionDomain protectionDomain, 105 byte[] classfileBuffer) throws IllegalClassFormatException { 106 boolean redef = classBeingRedefined != null; 107 String name = className.replace('/', '.'); 108 if ((redef? onRedef : onLoad) && className != null && name.equals(cname)) { 109 byte[] bytes = auxClassHelper(); 110 if (!auxClassDefined.getAndSet(true)) { 111 // first time defining auxiliary class 112 Class<?> c = classDefiner.defineClass(bytes); 113 if (!c.getName().equals(auxClassHelperName)) { 114 throw new RuntimeException("unexpected class defined: " + c); 115 } 116 } else { 117 try { 118 Class<?> c = classDefiner.defineClass(bytes); 119 throw new RuntimeException(auxClassHelperName + " should fail to be defined"); 120 } catch (Throwable e) {} 121 } 122 123 int fixedIndex = redef ? redefIndex : loadIndex; 124 try { 125 String internalName = auxClassHelperName.replace('.', '/'); 126 byte[] newcf = Instrumentor.instrFor(classfileBuffer) 127 .addMethodEntryInjection( 128 nname, 129 (h)->{ 130 h.push(fixedIndex); 131 h.invokeStatic(internalName, "callTracker", "(I)V", false); 132 }) 133 .apply(); 134 System.err.println(trname + ": " + className + " index: " + fixedIndex + 135 (redef? " REDEF" : " LOAD") + 136 " len before: " + classfileBuffer.length + 137 " after: " + newcf.length); 138 return newcf; 139 } catch (Throwable ex) { 140 System.err.println("Injection failure: " + ex); 141 ex.printStackTrace(); 142 } 143 } 144 return null; 145 } 146 147 /* 148 * Generates the auxiliary class 149 */ 150 byte[] auxClassHelper() { 151 ClassWriter cw = new ClassWriter(0); 152 String internalName = auxClassHelperName.replace('.', '/'); 153 cw.visit(V1_8, ACC_PUBLIC, internalName, null, "java/lang/Object", null); 154 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "callTracker", "(I)V", null, null); 155 mv.visitCode(); 156 mv.visitVarInsn(ILOAD, 0); 157 mv.visitMethodInsn(INVOKESTATIC, "p/DefineAuxClassTest", "callTracker", "(I)V"); 158 mv.visitInsn(RETURN); 159 mv.visitMaxs(2, 2); 160 mv.visitEnd(); 161 cw.visitEnd(); 162 163 return cw.toByteArray(); 164 } 165 } 166 167 static void write_buffer(String fname, byte[] buffer) { 168 try { 169 Path path = Paths.get(fname); 170 if (path.getParent() != null) { 171 Files.createDirectories(path.getParent()); 172 } 173 try (OutputStream outStream = Files.newOutputStream(path)) { 174 outStream.write(buffer, 0, buffer.length); 175 } 176 } catch (IOException ex) { 177 System.err.println("EXCEPTION in write_buffer: " + ex); 178 } 179 } 180 181 public static void premain(String agentArgs, Instrumentation instArg) { 182 inst = instArg; 183 System.err.println("Premain"); 184 185 t1 = new Tr("TR1", false, 10, true, 11, "p.DefineAuxClassApp", "foo"); 186 inst.addTransformer(t1, true); 187 t2 = new Tr("TN2", true, 20, true, 21, "p.DefineAuxClassApp", "foo"); 188 inst.addTransformer(t2, false); 189 t3 = new Tr("TR3", true, 30, true, 31, "p.DefineAuxClassApp", "foo"); 190 inst.addTransformer(t3, true); 191 t4 = new Tr("TN4", true, 40, true, 41, "p.DefineAuxClassApp", "foo"); 192 inst.addTransformer(t4, false); 193 } 194 195 public static void undo() { 196 inst.removeTransformer(t3); 197 try { 198 System.err.println("RETRANSFORM"); 199 inst.retransformClasses(new DefineAuxClassApp().getClass()); 200 } catch (Exception ex) { 201 System.err.println("EXCEPTION on undo: " + ex); 202 } 203 } 204 205 public static boolean succeeded() { 206 return succeeded && markCount == markGolden.length; 207 } 208 209 public static void callTracker(int mark) { 210 System.err.println("got mark " + mark); 211 if (markCount >= markGolden.length || mark != markGolden[markCount++]) { 212 succeeded = false; 213 } 214 } 215 216 }