/* * Copyright (c) 2012, 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. */ import java.io.*; import java.lang.instrument.Instrumentation; import java.lang.instrument.ClassFileTransformer; import java.net.*; import java.security.ProtectionDomain; public class VerifyLocalVariableTableOnRetransformTest extends ATransformerManagementTestCase { private byte[] fTargetClassBytes; private boolean fTargetClassMatches; private String fTargetClassName = "DummyClassWithLVT"; private boolean fTargetClassSeen; /** * Constructor for VerifyLocalVariableTableOnRetransformTest. * @param name */ public VerifyLocalVariableTableOnRetransformTest(String name) { super(name); String resourceName = fTargetClassName + ".class"; File f = new File(System.getProperty("test.classes", "."), resourceName); System.out.println("Reading test class from " + f); try { InputStream redefineStream = new FileInputStream(f); fTargetClassBytes = NamedBuffer.loadBufferFromStream(redefineStream); System.out.println("Read " + fTargetClassBytes.length + " bytes."); } catch (IOException e) { fail("Could not load the class: "+resourceName); } } public static void main (String[] args) throws Throwable { ATestCaseScaffold test = new VerifyLocalVariableTableOnRetransformTest(args[0]); test.runTest(); } protected final void doRunTest() throws Throwable { verifyClassFileBuffer(); } public void verifyClassFileBuffer() throws Throwable { beVerbose(); // With this call here, we will see the target class twice: // first when it gets loaded and second when it gets retransformed. addTransformerToManager(fInst, new MyObserver(), true); ClassLoader loader = getClass().getClassLoader(); Class target = loader.loadClass(fTargetClassName); assertEquals(fTargetClassName, target.getName()); // make an instance to prove the class was really loaded Object testInstance = target.newInstance(); // With this call here, we will see the target class once: // when it gets retransformed. //addTransformerToManager(fInst, new MyObserver(), true); assertTrue(fTargetClassName + " was not seen by transform()", fTargetClassSeen); // The HotSpot VM hands us class file bytes at initial class // load time that match the .class file contents. However, // according to the following spec that is not required: // http://javaweb.sfbay/javare/jdk/6.0/promoted/latest/doc/api/java/lang/instrument/Instrumentation.html#retransformClasses(java.lang.Class...) // This test exists to catch any unintentional change in // behavior by the HotSpot VM. If this behavior is intentionally // changed in the future, then this test will need to be // updated. assertTrue(fTargetClassName + " did not match .class file", fTargetClassMatches); fTargetClassSeen = false; fTargetClassMatches = false; fInst.retransformClasses(target); assertTrue(fTargetClassName + " was not seen by transform()", fTargetClassSeen); // The HotSpot VM doesn't currently preserve the LocalVariable // Table (LVT) attribute so the class file bytes seen by the // retransformClasses() call will not match the class file bytes // seen at initial class load time. assertTrue(fTargetClassName + " did not match .class file", fTargetClassMatches); } public class MyObserver implements ClassFileTransformer { public MyObserver() { } public String toString() { return MyObserver.this.getClass().getName(); } private void saveMismatchedBytes(byte[] classfileBuffer) { try { FileOutputStream fos = null; // This file will get created in the test execution // directory so there is no conflict with the file // in the test classes directory. String resourceName = fTargetClassName + ".class"; fos = new FileOutputStream(resourceName); fos.write(classfileBuffer); fos.close(); } catch (IOException ex) { } } public byte[] transform( ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) { System.out.println(this + ".transform() sees '" + className + "' of " + classfileBuffer.length + " bytes."); if (className.equals(fTargetClassName)) { fTargetClassSeen = true; if (classfileBuffer.length != fTargetClassBytes.length) { System.out.println("Warning: " + fTargetClassName + " lengths do not match."); fTargetClassMatches = false; saveMismatchedBytes(classfileBuffer); return null; } else { System.out.println("Info: " + fTargetClassName + " lengths match."); } for (int i = 0; i < classfileBuffer.length; i++) { if (classfileBuffer[i] != fTargetClassBytes[i]) { System.out.println("Warning: " + fTargetClassName + "[" + i + "]: '" + classfileBuffer[i] + "' != '" + fTargetClassBytes[i] + "'"); fTargetClassMatches = false; saveMismatchedBytes(classfileBuffer); return null; } } fTargetClassMatches = true; System.out.println("Info: verified '" + fTargetClassName + ".class' matches 'classfileBuffer'."); } return null; } } }