8315575: Retransform of record class with record component annotation fails with CFE
Reviewed-by: sspitsyn, coleenp
This commit is contained in:
parent
2725405ac9
commit
8fc9097b37
src/hotspot/share
classfile
oops
prims
test/jdk/java/lang/instrument
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2024, 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
|
||||
@ -3408,8 +3408,7 @@ u4 ClassFileParser::parse_classfile_record_attribute(const ClassFileStream* cons
|
||||
CHECK_0);
|
||||
|
||||
RecordComponent* record_component =
|
||||
RecordComponent::allocate(_loader_data, name_index, descriptor_index,
|
||||
attributes_count, generic_sig_index,
|
||||
RecordComponent::allocate(_loader_data, name_index, descriptor_index, generic_sig_index,
|
||||
annotations, type_annotations, CHECK_0);
|
||||
record_components->at_put(x, record_component);
|
||||
} // End of component processing loop
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 2024, 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
|
||||
@ -33,12 +33,11 @@
|
||||
|
||||
RecordComponent* RecordComponent::allocate(ClassLoaderData* loader_data,
|
||||
u2 name_index, u2 descriptor_index,
|
||||
u2 attributes_count,
|
||||
u2 generic_signature_index,
|
||||
AnnotationArray* annotations,
|
||||
AnnotationArray* type_annotations, TRAPS) {
|
||||
return new (loader_data, size(), MetaspaceObj::RecordComponentType, THREAD)
|
||||
RecordComponent(name_index, descriptor_index, attributes_count,
|
||||
RecordComponent(name_index, descriptor_index,
|
||||
generic_signature_index, annotations, type_annotations);
|
||||
}
|
||||
|
||||
@ -65,7 +64,6 @@ void RecordComponent::print_value_on(outputStream* st) const {
|
||||
void RecordComponent::print_on(outputStream* st) const {
|
||||
st->print("name_index: %d", _name_index);
|
||||
st->print(" - descriptor_index: %d", _descriptor_index);
|
||||
st->print(" - attributes_count: %d", _attributes_count);
|
||||
if (_generic_signature_index != 0) {
|
||||
st->print(" - generic_signature_index: %d", _generic_signature_index);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 2024, 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
|
||||
@ -36,25 +36,21 @@ class RecordComponent: public MetaspaceObj {
|
||||
AnnotationArray* _type_annotations;
|
||||
u2 _name_index;
|
||||
u2 _descriptor_index;
|
||||
u2 _attributes_count;
|
||||
|
||||
// generic_signature_index gets set if the Record component has a Signature
|
||||
// attribute. A zero value indicates that there was no Signature attribute.
|
||||
u2 _generic_signature_index;
|
||||
|
||||
public:
|
||||
RecordComponent(u2 name_index, u2 descriptor_index, u2 attributes_count,
|
||||
u2 generic_signature_index, AnnotationArray* annotations,
|
||||
AnnotationArray* type_annotations):
|
||||
RecordComponent(u2 name_index, u2 descriptor_index, u2 generic_signature_index,
|
||||
AnnotationArray* annotations, AnnotationArray* type_annotations):
|
||||
_annotations(annotations), _type_annotations(type_annotations),
|
||||
_name_index(name_index), _descriptor_index(descriptor_index),
|
||||
_attributes_count(attributes_count),
|
||||
_generic_signature_index(generic_signature_index) { }
|
||||
|
||||
// Allocate instance of this class
|
||||
static RecordComponent* allocate(ClassLoaderData* loader_data,
|
||||
u2 name_index, u2 descriptor_index,
|
||||
u2 attributes_count,
|
||||
u2 generic_signature_index,
|
||||
AnnotationArray* annotations,
|
||||
AnnotationArray* type_annotations, TRAPS);
|
||||
@ -69,8 +65,6 @@ class RecordComponent: public MetaspaceObj {
|
||||
_descriptor_index = descriptor_index;
|
||||
}
|
||||
|
||||
u2 attributes_count() const { return _attributes_count; }
|
||||
|
||||
u2 generic_signature_index() const { return _generic_signature_index; }
|
||||
void set_generic_signature_index(u2 generic_signature_index) {
|
||||
_generic_signature_index = generic_signature_index;
|
||||
|
@ -494,7 +494,6 @@ void JvmtiClassFileReconstituter::write_record_attribute() {
|
||||
RecordComponent* component = components->at(x);
|
||||
if (component->generic_signature_index() != 0) {
|
||||
length += 8; // Signature attribute size
|
||||
assert(component->attributes_count() > 0, "Bad component attributes count");
|
||||
}
|
||||
if (component->annotations() != nullptr) {
|
||||
length += 6 + component->annotations()->length();
|
||||
@ -511,7 +510,11 @@ void JvmtiClassFileReconstituter::write_record_attribute() {
|
||||
RecordComponent* component = components->at(i);
|
||||
write_u2(component->name_index());
|
||||
write_u2(component->descriptor_index());
|
||||
write_u2(component->attributes_count());
|
||||
u2 attributes_count = (component->generic_signature_index() != 0 ? 1 : 0)
|
||||
+ (component->annotations() != nullptr ? 1 : 0)
|
||||
+ (component->type_annotations() != nullptr ? 1 : 0);
|
||||
|
||||
write_u2(attributes_count);
|
||||
if (component->generic_signature_index() != 0) {
|
||||
write_signature_attribute(component->generic_signature_index());
|
||||
}
|
||||
|
186
test/jdk/java/lang/instrument/RetransformRecordAnnotation.java
Normal file
186
test/jdk/java/lang/instrument/RetransformRecordAnnotation.java
Normal file
@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 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
|
||||
* @bug 8315575
|
||||
* @summary test that records with invisible annotation can be retransformed
|
||||
*
|
||||
* @library /test/lib
|
||||
* @run shell MakeJAR.sh retransformAgent
|
||||
* @run main/othervm -javaagent:retransformAgent.jar -Xlog:redefine+class=trace RetransformRecordAnnotation
|
||||
*/
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.lang.instrument.ClassFileTransformer;
|
||||
import java.nio.file.Files;
|
||||
import java.security.ProtectionDomain;
|
||||
|
||||
public class RetransformRecordAnnotation extends AInstrumentationTestCase {
|
||||
|
||||
@Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface RuntimeTypeAnno {}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface RuntimeParamAnno {
|
||||
String s() default "foo";
|
||||
}
|
||||
|
||||
@Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
|
||||
@Retention(RetentionPolicy.CLASS)
|
||||
@interface ClassTypeAnno {}
|
||||
|
||||
@Retention(RetentionPolicy.CLASS)
|
||||
@interface ClassParamAnno {
|
||||
String s() default "bar";
|
||||
}
|
||||
|
||||
@RuntimeTypeAnno
|
||||
@RuntimeParamAnno(s = "1")
|
||||
public record VisibleAnnos(@RuntimeTypeAnno @RuntimeParamAnno(s = "2") Object o, Object other) {
|
||||
}
|
||||
|
||||
@ClassTypeAnno
|
||||
@ClassParamAnno(s = "3")
|
||||
public record InvisibleAnnos(@ClassTypeAnno @ClassParamAnno(s = "4") Object o, Object other) {
|
||||
}
|
||||
|
||||
@RuntimeTypeAnno
|
||||
@RuntimeParamAnno(s = "5")
|
||||
@ClassTypeAnno
|
||||
@ClassParamAnno(s = "6")
|
||||
public record MixedAnnos(@RuntimeTypeAnno @RuntimeParamAnno(s = "7")
|
||||
@ClassTypeAnno @ClassParamAnno(s = "8") Object o, Object other) {
|
||||
}
|
||||
|
||||
public static void main (String[] args) throws Throwable {
|
||||
ATestCaseScaffold test = new RetransformRecordAnnotation();
|
||||
test.beVerbose();
|
||||
test.runTest();
|
||||
}
|
||||
|
||||
private Transformer transformer;
|
||||
|
||||
public RetransformRecordAnnotation() throws Throwable {
|
||||
super("RetransformRecordAnnotation");
|
||||
}
|
||||
|
||||
private void log(Object o) {
|
||||
System.out.println(String.valueOf(o));
|
||||
}
|
||||
|
||||
// Retransforms target class using provided class bytes;
|
||||
private void retransform(Class targetClass, byte[] classBytes) throws Throwable {
|
||||
transformer.prepare(targetClass, classBytes);
|
||||
fInst.retransformClasses(targetClass);
|
||||
assertTrue(targetClass.getName() + " was not seen by transform()",
|
||||
transformer.getSeenClassBytes() != null);
|
||||
}
|
||||
|
||||
protected final void doRunTest() throws Throwable {
|
||||
transformer = new Transformer();
|
||||
fInst.addTransformer(transformer, true);
|
||||
|
||||
{
|
||||
log("Sanity: retransform to original class bytes");
|
||||
retransform(InvisibleAnnos.class, loadClassBytes(InvisibleAnnos.class));
|
||||
log("");
|
||||
}
|
||||
|
||||
// The following testcases use null as new class bytes (i.e. no transform is performed).
|
||||
// However, it is enough for testing purposes as the JvmtiClassFileReconstituter is still involved
|
||||
// in preparation of the initial class bytes.
|
||||
{
|
||||
log("Test: retransform VisibleAnnos to null");
|
||||
retransform(VisibleAnnos.class, null);
|
||||
log("");
|
||||
}
|
||||
|
||||
{
|
||||
log("Test: retransform InvisibleAnnos to null");
|
||||
retransform(InvisibleAnnos.class, null);
|
||||
log("");
|
||||
}
|
||||
|
||||
{
|
||||
log("Test: retransform MixedAnnos to null");
|
||||
retransform(MixedAnnos.class, null);
|
||||
log("");
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] loadClassBytes(Class cls) throws Exception {
|
||||
String classFileName = cls.getName() + ".class";
|
||||
File classFile = new File(System.getProperty("test.classes", "."), classFileName);
|
||||
log("Reading test class from " + classFile);
|
||||
byte[] classBytes = Files.readAllBytes(classFile.toPath());
|
||||
log("Read " + classBytes.length + " bytes.");
|
||||
return classBytes;
|
||||
}
|
||||
|
||||
public class Transformer implements ClassFileTransformer {
|
||||
private String targetClassName;
|
||||
private byte[] seenClassBytes;
|
||||
private byte[] newClassBytes;
|
||||
|
||||
public Transformer() {
|
||||
}
|
||||
|
||||
// Prepares transformer for Instrumentation.retransformClasses.
|
||||
public void prepare(Class targetClass, byte[] classBytes) {
|
||||
targetClassName = targetClass.getName();
|
||||
newClassBytes = classBytes;
|
||||
seenClassBytes = null;
|
||||
}
|
||||
|
||||
byte[] getSeenClassBytes() {
|
||||
return seenClassBytes;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return Transformer.this.getClass().getName();
|
||||
}
|
||||
|
||||
public byte[] transform(ClassLoader loader, String className,
|
||||
Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {
|
||||
|
||||
if (className.equals(targetClassName)) {
|
||||
log(this + ".transform() sees '" + className
|
||||
+ "' of " + classfileBuffer.length + " bytes.");
|
||||
seenClassBytes = classfileBuffer;
|
||||
if (newClassBytes != null) {
|
||||
log(this + ".transform() sets new classbytes for '" + className
|
||||
+ "' of " + newClassBytes.length + " bytes.");
|
||||
}
|
||||
return newClassBytes;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user