Skip to content
Permalink
Browse files
8262002: java/lang/instrument/VerifyLocalVariableTableOnRetransformTe…
…st.sh failed with "TestCaseScaffoldException: DummyClassWithLVT did not match .class file"

Reviewed-by: coleenp, sspitsyn
  • Loading branch information
Alex Menkov committed May 6, 2021
1 parent 04f7112 commit 52f1db6b6f7f5e5823ee84fc31edddf89ed21df4
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2021, 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
@@ -789,18 +789,12 @@ void JvmtiClassFileReconstituter::write_class_attributes() {
if (ik()->source_debug_extension() != NULL) {
write_source_debug_extension_attribute();
}
if (inner_classes_length > 0) {
write_inner_classes_attribute(inner_classes_length);
}
if (anno != NULL) {
write_annotations_attribute("RuntimeVisibleAnnotations", anno);
}
if (type_anno != NULL) {
write_annotations_attribute("RuntimeVisibleTypeAnnotations", type_anno);
}
if (cpool()->operands() != NULL) {
write_bootstrapmethod_attribute();
}
if (ik()->nest_host_index() != 0) {
write_nest_host_attribute();
}
@@ -813,6 +807,12 @@ void JvmtiClassFileReconstituter::write_class_attributes() {
if (ik()->record_components() != NULL) {
write_record_attribute();
}
if (cpool()->operands() != NULL) {
write_bootstrapmethod_attribute();
}
if (inner_classes_length > 0) {
write_inner_classes_attribute(inner_classes_length);
}
}

// Write the method information portion of ClassFile structure
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2021, 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
@@ -117,9 +117,11 @@ public ATransformerManagementTestCase(String name)
{
fTransformers.add(transformer);
}
// workaroud for JDK-8264667: create log message before addTransformer()
String msg = "Added transformer " + transformer
+ " with canRetransform=" + canRetransform;
manager.addTransformer(transformer, canRetransform);
verbosePrint("Added transformer " + transformer
+ " with canRetransform=" + canRetransform);
verbosePrint(msg);
}

/**
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2021, 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
@@ -21,11 +21,30 @@
* questions.
*/

/*
* @test
* @bug 7064927
* @summary Verify LocalVariableTable (LVT) exists when passed to transform() on a retransform operation.
* @author Daniel D. Daugherty
*
* @library /test/lib
* @run build VerifyLocalVariableTableOnRetransformTest
* @run compile -g DummyClassWithLVT.java
* @run shell MakeJAR.sh retransformAgent
* @run main/othervm -javaagent:retransformAgent.jar VerifyLocalVariableTableOnRetransformTest VerifyLocalVariableTableOnRetransformTest
*/

import java.io.*;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.ClassFileTransformer;
import java.net.*;
import java.security.ProtectionDomain;
import java.util.List;
import java.util.regex.Pattern;

import jdk.test.lib.JDKToolLauncher;
import jdk.test.lib.process.ProcessTools;
import jdk.test.lib.process.OutputAnalyzer;

public class
VerifyLocalVariableTableOnRetransformTest
@@ -34,6 +53,7 @@
private byte[] fTargetClassBytes;
private boolean fTargetClassMatches;
private String fTargetClassName = "DummyClassWithLVT";
private String classFileName = fTargetClassName + ".class";
private boolean fTargetClassSeen;

/**
@@ -44,8 +64,7 @@ public VerifyLocalVariableTableOnRetransformTest(String name)
{
super(name);

String resourceName = fTargetClassName + ".class";
File f = new File(System.getProperty("test.classes", "."), resourceName);
File f = originalClassFile();
System.out.println("Reading test class from " + f);
try
{
@@ -55,7 +74,7 @@ public VerifyLocalVariableTableOnRetransformTest(String name)
}
catch (IOException e)
{
fail("Could not load the class: "+resourceName);
fail("Could not load the class: " + f.getName());
}
}

@@ -105,8 +124,7 @@ public VerifyLocalVariableTableOnRetransformTest(String name)
// 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);
compareClassFileBytes(true);

fTargetClassSeen = false;
fTargetClassMatches = false;
@@ -120,8 +138,88 @@ public VerifyLocalVariableTableOnRetransformTest(String name)
// 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);
compareClassFileBytes(false);
}

private File originalClassFile() {
return new File(System.getProperty("test.classes", "."), classFileName);
}

private File transformedClassFile() {
// This file will get created in the test execution
// directory so there is no conflict with the file
// in the test classes directory.
return new File(classFileName);
}

private static final String[] expectedDifferentStrings = {
"^Classfile .+$",
"^[\\s]+SHA-256 checksum .[^\\s]+$"
};

private boolean expectedDifferent(String line) {
for (String s: expectedDifferentStrings) {
Pattern p = Pattern.compile(s);
if (p.matcher(line).find()) {
return true;
}
}
return false;
}

private void compareClassFileBytes(boolean initialLoad) throws Throwable {
if (fTargetClassMatches) {
return;
}
File f1 = originalClassFile();
File f2 = transformedClassFile();
System.out.println(fTargetClassName + " did not match .class file");
System.out.println("Disassembly difference (" + f1 + " vs " + f2 +"):");
// compare 'javap -v' output for the class files
List<String> out1 = disassembleClassFile(f1);
List<String> out2 = disassembleClassFile(f2);
boolean different = false;
boolean orderChanged = false;
int lineNum = 0;
for (String line: out1) {
if (!expectedDifferent(line)) {
if (!out2.contains(line)) {
different = true;
System.out.println("< (" + (lineNum + 1) + ") " + line);
} else {
if (lineNum < out2.size() && out1.get(lineNum) != out2.get(lineNum)) {
// out2 contains line, but at different position
orderChanged = true;
}
}
}
lineNum++;
}
lineNum = 0;
for (String line: out2) {
if (!expectedDifferent(line)) {
if (!out1.contains(line)) {
different = true;
System.out.println("> (" + (lineNum + 1) + ") " + line);
}
}
lineNum++;
}
// accordingly the spec orderChanged is fine, but we consider it as error
// (see comments in verifyClassFileBuffer())
if (different || orderChanged) {
fail(fTargetClassName + " (" + (initialLoad ? "load" : "retransform") + ") did not match .class file"
+ (different ? "" : " (only order changed)"));
}
}

private List<String> disassembleClassFile(File file) throws Throwable {
JDKToolLauncher javap = JDKToolLauncher.create("javap")
.addToolArg("-v")
.addToolArg(file.toString());
ProcessBuilder pb = new ProcessBuilder(javap.getCommand());
OutputAnalyzer out = ProcessTools.executeProcess(pb);
return out.asLines();
}

public class MyObserver implements ClassFileTransformer {
@@ -134,12 +232,7 @@ public String toString() {

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);
FileOutputStream fos = new FileOutputStream(transformedClassFile());
fos.write(classfileBuffer);
fos.close();
} catch (IOException ex) {

This file was deleted.

0 comments on commit 52f1db6

Please sign in to comment.