# HG changeset patch # User mchung # Date 1523774467 -28800 # Sun Apr 15 14:41:07 2018 +0800 # Node ID 5d0767e9f575c4cb9014a76a7cdad472ca9a88cd # Parent 09c01737ad27dc579bebef35bfe0ce3292cb5339 8200559: Java agents doing instrumentation need a means to define auxiliary classes Reviewed-by: duke diff --git a/src/java.base/share/classes/module-info.java b/src/java.base/share/classes/module-info.java --- a/src/java.base/share/classes/module-info.java +++ b/src/java.base/share/classes/module-info.java @@ -161,6 +161,7 @@ jdk.scripting.nashorn; exports jdk.internal.misc to java.desktop, + java.instrument, java.logging, java.management, java.naming, diff --git a/src/java.instrument/share/classes/java/lang/instrument/ClassFileTransformer.java b/src/java.instrument/share/classes/java/lang/instrument/ClassFileTransformer.java --- a/src/java.instrument/share/classes/java/lang/instrument/ClassFileTransformer.java +++ b/src/java.instrument/share/classes/java/lang/instrument/ClassFileTransformer.java @@ -25,8 +25,6 @@ package java.lang.instrument; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.security.ProtectionDomain; /* @@ -142,16 +140,22 @@ * redefinition of the first output class file. * *

+ * The transformers can define additional classes when a class is + * being transformed via {@link ClassDefiner#defineClass(byte[]) + * ClassDefiner.defineClass}. They will be defined to the same class loader and + * the same protection domain as the class being transformed. + * + *

* If the transformer throws an exception (which it doesn't catch), * subsequent transformers will still be called and the load, redefine * or retransform will still be attempted. - * Thus, throwing an exception has the same effect as returning null. + * Thus, throwing an exception has the same effect as returning {@code null}. * To prevent unexpected behavior when unchecked exceptions are generated * in transformer code, a transformer can catch Throwable. * If the transformer believes the classFileBuffer does not * represent a validly formatted class file, it should throw * an IllegalClassFormatException; - * while this has the same effect as returning null. it facilitates the + * while this has the same effect as returning {@code null}, it facilitates the * logging or debugging of format corruptions. * *

@@ -165,6 +169,50 @@ */ public interface ClassFileTransformer { + /** + * A {@code ClassDefiner} provides the mechanism for {@code ClassFileTransformer} + * to define additional classes during the + * {@linkplain ClassFileTransformer#transform(ClassDefiner, Module, ClassLoader, String, Class, ProtectionDomain, byte[]) + * transformation process} of a class. + * + *

When a {@linkplain ClassFileTransformer#transform(ClassDefiner, Module, ClassLoader, String, Class, ProtectionDomain, byte[]) + * transformer} is called to retransform a class, the transformer can call + * {@link ClassDefiner#defineClass(byte[])} to define additional classes. + * If it is called after {@link ClassFileTransformer#transform(ClassDefiner, Module, ClassLoader, String, Class, ProtectionDomain, byte[]) + * ClassFileTransformer.tranform} method returns or called by another + * thread, {@code IllegalStateException} will be thrown. + * + * @since 11 + */ + static interface ClassDefiner { + /** + * Defines a class of the given bytes to the runtime. + * The given bytes will be defined to the same class loader and in the + * same runtime package and + * {@linkplain java.security.ProtectionDomain protection domain} as + * the class being transformed. + * + *

No transformation is applied to the bytes passed to this + * {@code ClassDefiner.defineClass} method. + * {@link Instrumentation#retransformClasses(Class[])} can be called + * later to retransform the {@code Class} returned by this method + * if desired. + * + * @param bytes the class bytes + * @return the {@code Class} object for the class + * + * @throws IllegalArgumentException + * the bytes are for a class of a different package than + * the class being transformed + * @throws LinkageError if the class is malformed ({@code ClassFormatError}), + * cannot be verified ({@code VerifyError}), is already defined, + * or another linkage error occurs + * @throws IllegalStateException if this {@code ClassDefiner} is called + * by a thread other than the thread doing the transformation, + * or after all transformers are called. + */ + Class defineClass(byte[] bytes); + } /** * Transforms the given class file and returns a new replacement class file. @@ -249,4 +297,59 @@ protectionDomain, classfileBuffer); } + + /** + * Transforms the given class file and returns a new replacement class file. + * + *

A {@code ClassFileTransformer} may define additional classes + * to the same class loader and the same runtime package as the class being + * transformed by calling {@link ClassDefiner#defineClass(byte[])} + * classDefiner.defineClass} method. If an attempt to use {@code classDefiner} + * is made after this {@code transform} method returns, + * {@code IllegalStateException} will be thrown. + * + * @implSpec The default implementation of this method invokes the + * {@link #transform(Module,ClassLoader,String,Class,ProtectionDomain,byte[]) transform} + * method. + * + * @param classDefiner a {@code ClassDefiner} that this class transformer + * can define additional classes + * @param module the module of the class to be transformed + * @param loader the defining loader of the class to be transformed, + * may be {@code null} if the bootstrap loader + * @param className the name of the class in the internal form of fully + * qualified class and interface names as defined in + * The Java Virtual Machine Specification. + * For example, "java/util/List". + * @param classBeingRedefined if this is triggered by a redefine or retransform, + * the class being redefined or retransformed; + * if this is a class load, {@code null} + * @param protectionDomain the protection domain of the class being defined or redefined + * @param classfileBuffer the input byte buffer in class file format - must not be modified + * + * @throws IllegalClassFormatException + * if the input does not represent a well-formed class file + * @return a well-formed class file buffer (the result of the transform), + * or {@code null} if no transform is performed + * + * @since 11 + */ + default byte[] + transform( ClassFileTransformer.ClassDefiner classDefiner, + Module module, + ClassLoader loader, + String className, + Class classBeingRedefined, + ProtectionDomain protectionDomain, + byte[] classfileBuffer) + throws IllegalClassFormatException { + + // invoke the transform method + return transform(module, + loader, + className, + classBeingRedefined, + protectionDomain, + classfileBuffer); + } } diff --git a/src/java.instrument/share/classes/sun/instrument/CloseableClassDefiner.java b/src/java.instrument/share/classes/sun/instrument/CloseableClassDefiner.java new file mode 100644 --- /dev/null +++ b/src/java.instrument/share/classes/sun/instrument/CloseableClassDefiner.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2018, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +package sun.instrument; + +import jdk.internal.misc.SharedSecrets; +import jdk.internal.org.objectweb.asm.ClassReader; + +import java.lang.instrument.ClassFileTransformer.ClassDefiner; +import java.security.ProtectionDomain; +import java.util.Objects; + + +class CloseableClassDefiner implements ClassDefiner, AutoCloseable { + final Thread thread; + final String packageName; + final ClassLoader loader; + final ProtectionDomain protectionDomain; + volatile boolean active; + + CloseableClassDefiner(String classname, ClassLoader loader, ProtectionDomain pd) { + this.thread = Thread.currentThread(); + int index = classname.lastIndexOf("/"); + this.packageName = index == -1 ? "" : classname.substring(0, index).replace('/', '.'); + this.loader = loader; + this.protectionDomain = pd; + this.active = true; + } + + @Override + public Class defineClass(byte[] bytes) { + Objects.requireNonNull(bytes); + if (!active || Thread.currentThread() != thread) { + throw new IllegalStateException("this ClassDefiner is not valid"); + } + + // parse class bytes to get class name (in internal form) + bytes = bytes.clone(); + String name; + + try { + ClassReader reader = new ClassReader(bytes); + name = reader.getClassName(); + } catch (RuntimeException e) { + // ASM exceptions are poorly specified + ClassFormatError cfe = new ClassFormatError(); + cfe.initCause(e); + throw cfe; + } + + // get package and class name in binary form + String cn, pn; + int index = name.lastIndexOf('/'); + if (index == -1) { + cn = name; + pn = ""; + } else { + cn = name.replace('/', '.'); + pn = cn.substring(0, index); + } + + if (!packageName.equals(pn)) { + throw new IllegalArgumentException(cn + " not in same package as the class being transformed"); + } + + /* + * Defines auxiliary class + */ + String source = "__ClassFileTransformer_defineClass__"; + return SharedSecrets.getJavaLangAccess().defineClass(loader, cn, bytes, protectionDomain, source); + } + + @Override + public void close() { + this.active = false; + } + +} diff --git a/src/java.instrument/share/classes/sun/instrument/TransformerManager.java b/src/java.instrument/share/classes/sun/instrument/TransformerManager.java --- a/src/java.instrument/share/classes/sun/instrument/TransformerManager.java +++ b/src/java.instrument/share/classes/sun/instrument/TransformerManager.java @@ -25,7 +25,6 @@ package sun.instrument; -import java.lang.instrument.Instrumentation; import java.lang.instrument.ClassFileTransformer; import java.security.ProtectionDomain; @@ -174,35 +173,36 @@ byte[] classfileBuffer) { boolean someoneTouchedTheBytecode = false; - TransformerInfo[] transformerList = getSnapshotTransformerList(); + TransformerInfo[] transformerList = getSnapshotTransformerList(); + byte[] bufferToUse = classfileBuffer; - byte[] bufferToUse = classfileBuffer; + try (CloseableClassDefiner classDefiner = new CloseableClassDefiner(classname, loader, protectionDomain)) { + // order matters, gotta run 'em in the order they were added + for (int x = 0; x < transformerList.length; x++) { + TransformerInfo transformerInfo = transformerList[x]; + ClassFileTransformer transformer = transformerInfo.transformer(); + byte[] transformedBytes = null; + try { + transformedBytes = transformer.transform(classDefiner, + module, + loader, + classname, + classBeingRedefined, + protectionDomain, + bufferToUse); + } catch (Throwable t) { + // don't let any one transformer mess it up for the others. + // This is where we need to put some logging. What should go here? FIXME + } - // order matters, gotta run 'em in the order they were added - for ( int x = 0; x < transformerList.length; x++ ) { - TransformerInfo transformerInfo = transformerList[x]; - ClassFileTransformer transformer = transformerInfo.transformer(); - byte[] transformedBytes = null; - - try { - transformedBytes = transformer.transform( module, - loader, - classname, - classBeingRedefined, - protectionDomain, - bufferToUse); - } - catch (Throwable t) { - // don't let any one transformer mess it up for the others. - // This is where we need to put some logging. What should go here? FIXME - } - - if ( transformedBytes != null ) { - someoneTouchedTheBytecode = true; - bufferToUse = transformedBytes; + if (transformedBytes != null) { + someoneTouchedTheBytecode = true; + bufferToUse = transformedBytes; + } } } + // if someone modified it, return the modified buffer. // otherwise return null to mean "no transforms occurred" byte [] result; @@ -216,7 +216,6 @@ return result; } - int getTransformerCount() { TransformerInfo[] transformerList = getSnapshotTransformerList(); diff --git a/test/jdk/java/lang/instrument/ATransformerManagementTestCase.java b/test/jdk/java/lang/instrument/ATransformerManagementTestCase.java --- a/test/jdk/java/lang/instrument/ATransformerManagementTestCase.java +++ b/test/jdk/java/lang/instrument/ATransformerManagementTestCase.java @@ -316,6 +316,28 @@ protectionDomain, classfileBuffer); } + + public byte[] + transform( + ClassFileTransformer.ClassDefiner classDefiner, + Module module, + ClassLoader loader, + String className, + Class classBeingRedefined, + ProtectionDomain protectionDomain, + byte[] classfileBuffer) { + + // The transform testing is triggered by redefine, ignore others + if (classBeingRedefined != null) checkInTransformer(MyClassFileTransformer.this); + + return super.transform( classDefiner, + module, + loader, + className, + classBeingRedefined, + protectionDomain, + classfileBuffer); + } } diff --git a/test/jdk/java/lang/instrument/BootstrapClassPathAgent.java b/test/jdk/java/lang/instrument/BootstrapClassPathAgent.java --- a/test/jdk/java/lang/instrument/BootstrapClassPathAgent.java +++ b/test/jdk/java/lang/instrument/BootstrapClassPathAgent.java @@ -25,6 +25,7 @@ import java.io.*; import java.lang.instrument.ClassFileTransformer; +import java.lang.instrument.ClassFileTransformer.ClassDefiner; import java.lang.instrument.Instrumentation; import java.security.ProtectionDomain; @@ -37,7 +38,8 @@ static class MyTransformer implements ClassFileTransformer { - public byte[] transform(Module module, + public byte[] transform(ClassDefiner classDefiner, + Module module, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, diff --git a/test/jdk/java/lang/instrument/ClassDefiner/DefineAuxClassApp.java b/test/jdk/java/lang/instrument/ClassDefiner/DefineAuxClassApp.java new file mode 100644 --- /dev/null +++ b/test/jdk/java/lang/instrument/ClassDefiner/DefineAuxClassApp.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2018, 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. + */ + +package p; + +import java.io.PrintStream; +import java.util.*; + +public class DefineAuxClassApp { + + public static void main(String args[]) throws Exception { + (new DefineAuxClassApp()).run(args, System.out); + } + + int foo(int x) { + return x * x; + } + + public void run(String args[], PrintStream out) throws Exception { + out.println("start"); + for (int i = 0; i < 4; i++) { + if (foo(3) != 9) { + throw new Exception("ERROR: unexpected application behavior"); + } + } + out.println("undo"); + DefineAuxClassTest.undo(); + for (int i = 0; i < 4; i++) { + if (foo(3) != 9) { + throw new Exception("ERROR: unexpected application behavior"); + } + } + out.println("end"); + if (DefineAuxClassTest.succeeded()) { + out.println("Instrumentation succeeded."); + } else { + throw new Exception("ERROR: Instrumentation failed."); + } + } +} diff --git a/test/jdk/java/lang/instrument/ClassDefiner/DefineAuxClassTest.java b/test/jdk/java/lang/instrument/ClassDefiner/DefineAuxClassTest.java new file mode 100644 --- /dev/null +++ b/test/jdk/java/lang/instrument/ClassDefiner/DefineAuxClassTest.java @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2018, 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. + */ + + +/** + * @test + * @summary test auxiliary classes + * + * @modules java.base/jdk.internal.org.objectweb.asm + * java.instrument + * @compile ../asmlib/Instrumentor.java DefineAuxClassTest.java DefineAuxClassApp.java + * @run shell/timeout=240 MakeAgent.sh 'Can-Retransform-Classes: true' + * @run main/othervm -javaagent:DefineAuxClassTest.jar p.DefineAuxClassApp + */ + +package p; + +import java.lang.instrument.ClassFileTransformer; +import java.lang.instrument.ClassFileTransformer.ClassDefiner; +import java.lang.instrument.Instrumentation; +import java.lang.instrument.IllegalClassFormatException; + +import java.security.ProtectionDomain; +import java.util.stream.Stream; +import java.util.concurrent.atomic.AtomicBoolean; +import java.nio.file.*; +import java.io.IOException; +import java.io.OutputStream; +import jdk.internal.org.objectweb.asm.ClassReader; +import jdk.internal.org.objectweb.asm.ClassVisitor; +import jdk.internal.org.objectweb.asm.ClassWriter; +import jdk.internal.org.objectweb.asm.MethodVisitor; +import jdk.internal.org.objectweb.asm.Opcodes; +import static jdk.internal.org.objectweb.asm.Opcodes.*; + +import asmlib.*; + +class DefineAuxClassTest { + + static ClassFileTransformer t1, t2, t3, t4; + static Instrumentation inst; + static boolean succeeded = true; + static int markCount = 0; + static int[] markGolden = {30, 40, 20, 30, 40, 20, 30, 40, 20, 30, 40, 20, + 11, 40, 20, 11, 40, 20, 11, 40, 20, 11, 40, 20}; + + static class Tr implements ClassFileTransformer { + final String trname; + final boolean onLoad; + final int loadIndex; + final boolean onRedef; + final int redefIndex; + final String cname; + final String nname; + final String auxClassHelperName; + final AtomicBoolean auxClassDefined; + + Tr(String trname, boolean onLoad, int loadIndex, boolean onRedef, int redefIndex, + String cname, String nname) { + this.trname = trname; + this.onLoad = onLoad; + this.loadIndex = loadIndex; + this.onRedef = onRedef; + this.redefIndex = redefIndex; + this.cname = cname; + this.nname = nname; + + int index = cname.lastIndexOf("."); + if (index == -1) { + this.auxClassHelperName = trname + "AuxClassHelper"; + } else { + String pn = cname.substring(0, index); + this.auxClassHelperName = pn + "." + trname + "AuxClassHelper"; + + } + this.auxClassDefined = new AtomicBoolean(false); + } + + public byte[] transform(ClassDefiner classDefiner, + Module module, + ClassLoader loader, + String className, + Class classBeingRedefined, + ProtectionDomain protectionDomain, + byte[] classfileBuffer) throws IllegalClassFormatException { + boolean redef = classBeingRedefined != null; + String name = className.replace('/', '.'); + if ((redef? onRedef : onLoad) && className != null && name.equals(cname)) { + byte[] bytes = auxClassHelper(); + if (!auxClassDefined.getAndSet(true)) { + // first time defining auxiliary class + Class c = classDefiner.defineClass(bytes); + if (!c.getName().equals(auxClassHelperName)) { + throw new RuntimeException("unexpected class defined: " + c); + } + } else { + try { + Class c = classDefiner.defineClass(bytes); + throw new RuntimeException(auxClassHelperName + " should fail to be defined"); + } catch (Throwable e) {} + } + + int fixedIndex = redef ? redefIndex : loadIndex; + try { + String internalName = auxClassHelperName.replace('.', '/'); + byte[] newcf = Instrumentor.instrFor(classfileBuffer) + .addMethodEntryInjection( + nname, + (h)->{ + h.push(fixedIndex); + h.invokeStatic(internalName, "callTracker", "(I)V", false); + }) + .apply(); + System.err.println(trname + ": " + className + " index: " + fixedIndex + + (redef? " REDEF" : " LOAD") + + " len before: " + classfileBuffer.length + + " after: " + newcf.length); + return newcf; + } catch (Throwable ex) { + System.err.println("Injection failure: " + ex); + ex.printStackTrace(); + } + } + return null; + } + + /* + * Generates the auxiliary class + */ + byte[] auxClassHelper() { + ClassWriter cw = new ClassWriter(0); + String internalName = auxClassHelperName.replace('.', '/'); + cw.visit(V1_8, ACC_PUBLIC, internalName, null, "java/lang/Object", null); + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "callTracker", "(I)V", null, null); + mv.visitCode(); + mv.visitVarInsn(ILOAD, 0); + mv.visitMethodInsn(INVOKESTATIC, "p/DefineAuxClassTest", "callTracker", "(I)V"); + mv.visitInsn(RETURN); + mv.visitMaxs(2, 2); + mv.visitEnd(); + cw.visitEnd(); + + return cw.toByteArray(); + } + } + + static void write_buffer(String fname, byte[] buffer) { + try { + Path path = Paths.get(fname); + if (path.getParent() != null) { + Files.createDirectories(path.getParent()); + } + try (OutputStream outStream = Files.newOutputStream(path)) { + outStream.write(buffer, 0, buffer.length); + } + } catch (IOException ex) { + System.err.println("EXCEPTION in write_buffer: " + ex); + } + } + + public static void premain(String agentArgs, Instrumentation instArg) { + inst = instArg; + System.err.println("Premain"); + + t1 = new Tr("TR1", false, 10, true, 11, "p.DefineAuxClassApp", "foo"); + inst.addTransformer(t1, true); + t2 = new Tr("TN2", true, 20, true, 21, "p.DefineAuxClassApp", "foo"); + inst.addTransformer(t2, false); + t3 = new Tr("TR3", true, 30, true, 31, "p.DefineAuxClassApp", "foo"); + inst.addTransformer(t3, true); + t4 = new Tr("TN4", true, 40, true, 41, "p.DefineAuxClassApp", "foo"); + inst.addTransformer(t4, false); + } + + public static void undo() { + inst.removeTransformer(t3); + try { + System.err.println("RETRANSFORM"); + inst.retransformClasses(new DefineAuxClassApp().getClass()); + } catch (Exception ex) { + System.err.println("EXCEPTION on undo: " + ex); + } + } + + public static boolean succeeded() { + return succeeded && markCount == markGolden.length; + } + + public static void callTracker(int mark) { + System.err.println("got mark " + mark); + if (markCount >= markGolden.length || mark != markGolden[markCount++]) { + succeeded = false; + } + } + +} diff --git a/test/jdk/java/lang/instrument/ClassDefiner/MakeAgent.sh b/test/jdk/java/lang/instrument/ClassDefiner/MakeAgent.sh new file mode 100644 --- /dev/null +++ b/test/jdk/java/lang/instrument/ClassDefiner/MakeAgent.sh @@ -0,0 +1,84 @@ +#!/bin/sh + +# +# Copyright (c) 2018, 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. +# + +if [ "${TESTSRC}" = "" ] +then + echo "TESTSRC not set. Test cannot execute. Failed." + exit 1 +fi +echo "TESTSRC=${TESTSRC}" + +if [ "${TESTJAVA}" = "" ] +then + echo "TESTJAVA not set. Test cannot execute. Failed." + exit 1 +fi +echo "TESTJAVA=${TESTJAVA}" + +if [ "${COMPILEJAVA}" = "" ] +then + COMPILEJAVA="${TESTJAVA}" +fi +echo "COMPILEJAVA=${COMPILEJAVA}" + +if [ "${TESTCLASSES}" = "" ] +then + echo "TESTCLASSES not set. Test cannot execute. Failed." + exit 1 +fi + +OS=`uname -s` +case "$OS" in + SunOS | Linux | Darwin ) + PATHSEP=":" + ;; + + Windows* | CYGWIN*) + PATHSEP=";" + ;; + + # catch all other OSs + * ) + echo "Unrecognized system! $OS" + fail "Unrecognized system! $OS" + ;; +esac + +JAR="${COMPILEJAVA}/bin/jar" + +echo "Manifest-Version: 1.0" > agent.mf +echo Premain-Class: p.DefineAuxClassTest >> agent.mf +shift 2 +while [ $# != 0 ] ; do + echo $1 >> agent.mf + shift +done + +${JAR} ${TESTTOOLVMOPTS} cvfm DefineAuxClassTest.jar agent.mf \ + -C ${TESTCLASSES} p/DefineAuxClassTest.class \ + -C ${TESTCLASSES} p/DefineAuxClassTest\$Tr.class \ + -C ${TESTCLASSES} asmlib +rm -rf ${TESTCLASSES}/p/DefineAuxClassTest.class ${TESTCLASSES}/asmlib + diff --git a/test/jdk/java/lang/instrument/MakeJAR2.sh b/test/jdk/java/lang/instrument/MakeJAR2.sh --- a/test/jdk/java/lang/instrument/MakeJAR2.sh +++ b/test/jdk/java/lang/instrument/MakeJAR2.sh @@ -23,7 +23,6 @@ # questions. # - AGENT="$1" APP="$2" @@ -55,7 +54,7 @@ OS=`uname -s` case "$OS" in - SunOS | Linux ) + SunOS | Linux | Darwin ) PATHSEP=":" ;; diff --git a/test/jdk/java/lang/instrument/SimpleIdentityTransformer.java b/test/jdk/java/lang/instrument/SimpleIdentityTransformer.java --- a/test/jdk/java/lang/instrument/SimpleIdentityTransformer.java +++ b/test/jdk/java/lang/instrument/SimpleIdentityTransformer.java @@ -72,4 +72,19 @@ return newBuffer; } + + public byte[] + transform( + ClassFileTransformer.ClassDefiner classDefiner, + Module module, + ClassLoader loader, + String className, + Class classBeingRedefined, + ProtectionDomain protectionDomain, + byte[] classfileBuffer) { + byte[] newBuffer = new byte[classfileBuffer.length]; + System.arraycopy(classfileBuffer, 0, newBuffer, 0, classfileBuffer.length); + + return newBuffer; + } }