OpenJDK / amber / amber
changeset 58354:4cf6af46e13b records
Summary: Add support for class redefinition of classes with Record attributes
line wrap: on
line diff
--- a/src/hotspot/share/classfile/javaClasses.cpp Thu Oct 17 13:42:17 2019 -0700 +++ b/src/hotspot/share/classfile/javaClasses.cpp Fri Oct 18 11:49:23 2019 +0000 @@ -3055,7 +3055,7 @@ accessor_method = holder->find_instance_method(name, full_sig); } - if (accessor_method != NULL) { // TBD should a null accessor method be an error? + if (accessor_method != NULL) { methodHandle method(THREAD, accessor_method); oop m = Reflection::new_method(method, false, CHECK_0); java_lang_reflect_RecordComponent::set_accessor(element(), m); @@ -3065,7 +3065,7 @@ int sig_index = component->generic_signature_index(); if (sig_index > 0) { - Symbol* sig = holder->constants()->symbol_at(sig_index); // name_index is a utf8 + Symbol* sig = holder->constants()->symbol_at(sig_index); // sig_index is a utf8 oop component_sig = StringTable::intern(sig, CHECK_0); java_lang_reflect_RecordComponent::set_signature(element(), component_sig); } else {
--- a/src/hotspot/share/logging/logTag.hpp Thu Oct 17 13:42:17 2019 -0700 +++ b/src/hotspot/share/logging/logTag.hpp Fri Oct 18 11:49:23 2019 +0000 @@ -136,6 +136,7 @@ LOG_TAG(parser) \ LOG_TAG(ptrqueue) \ LOG_TAG(purge) \ + LOG_TAG(record) \ LOG_TAG(resolve) \ LOG_TAG(safepoint) \ LOG_TAG(sampling) \
--- a/src/hotspot/share/oops/instanceKlass.cpp Thu Oct 17 13:42:17 2019 -0700 +++ b/src/hotspot/share/oops/instanceKlass.cpp Fri Oct 18 11:49:23 2019 +0000 @@ -3245,8 +3245,9 @@ } st->print(BULLET"inner classes: "); inner_classes()->print_value_on(st); st->cr(); st->print(BULLET"nest members: "); nest_members()->print_value_on(st); st->cr(); - // TBD - need to check for NULL? - st->print(BULLET"record components: "); record_components()->print_value_on(st); st->cr(); + if (record_components() != NULL) { + st->print(BULLET"record components: "); record_components()->print_value_on(st); st->cr(); + } if (java_mirror() != NULL) { st->print(BULLET"java mirror: "); java_mirror()->print_value_on(st);
--- a/src/hotspot/share/oops/recordComponent.hpp Thu Oct 17 13:42:17 2019 -0700 +++ b/src/hotspot/share/oops/recordComponent.hpp Fri Oct 18 11:49:23 2019 +0000 @@ -63,12 +63,19 @@ void deallocate_contents(ClassLoaderData* loader_data); u2 name_index() const { return _name_index; } + void set_name_index(u2 name_index) { _name_index = name_index; } u2 descriptor_index() const { return _descriptor_index; } + void set_descriptor_index(u2 descriptor_index) { + _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; + } AnnotationArray* annotations() const { return _annotations; } AnnotationArray* type_annotations() const { return _type_annotations; }
--- a/src/hotspot/share/prims/jvm.cpp Thu Oct 17 13:42:17 2019 -0700 +++ b/src/hotspot/share/prims/jvm.cpp Fri Oct 18 11:49:23 2019 +0000 @@ -1682,9 +1682,16 @@ JVM_END JVM_ENTRY(jboolean, JVM_IsRecord(JNIEnv *env, jclass cls)) +{ JVMWrapper("JVM_IsRecord"); - InstanceKlass* k = InstanceKlass::cast(java_lang_Class::as_Klass(JNIHandles::resolve_non_null(cls))); - return k->is_record(); + Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(cls)); + if (k != NULL && k->is_instance_klass()) { + InstanceKlass* ik = InstanceKlass::cast(k); + return ik->is_record(); + } else { + return false; + } +} JVM_END JVM_ENTRY(jobjectArray, JVM_GetRecordComponents(JNIEnv* env, jclass ofClass))
--- a/src/hotspot/share/prims/jvmti.xml Thu Oct 17 13:42:17 2019 -0700 +++ b/src/hotspot/share/prims/jvmti.xml Fri Oct 18 11:49:23 2019 +0000 @@ -7633,8 +7633,8 @@ (unless explicitly prohibited). The retransformation must not add, remove or rename fields or methods, change the signatures of methods, change modifiers, or change inheritance. - The retransformation must not change the <code>NestHost</code> or - <code>NestMembers</code> attributes. + The retransformation must not change the <code>NestHost</code>, + <code>NestMembers</code>, or <code>Record</code> attributes. These restrictions may be lifted in future versions. See the error return description below for information on error codes returned if an unsupported retransformation is attempted. @@ -7786,8 +7786,8 @@ (unless explicitly prohibited). The redefinition must not add, remove or rename fields or methods, change the signatures of methods, change modifiers, or change inheritance. - The retransformation must not change the <code>NestHost</code> or - <code>NestMembers</code> attributes. + The retransformation must not change the <code>NestHost</code>, + <code>NestMembers</code>, or <code>Record</code> attributes. These restrictions may be lifted in future versions. See the error return description below for information on error codes returned if an unsupported redefinition is attempted. @@ -14974,6 +14974,11 @@ - The specified thread must be suspended or must be the current thread. (It was not allowed to be the current thread before.) </change> + <change date="10 October 2019" version="14.0.0"> + Minor update for new class file Record attribute: + - Specify that RedefineClasses and RetransformClasses are not allowed + to change the class file Record attribute. + </change> </changehistory> </specification>
--- a/src/hotspot/share/prims/jvmtiRedefineClasses.cpp Thu Oct 17 13:42:17 2019 -0700 +++ b/src/hotspot/share/prims/jvmtiRedefineClasses.cpp Fri Oct 18 11:49:23 2019 +0000 @@ -40,10 +40,12 @@ #include "memory/metaspaceShared.hpp" #include "memory/resourceArea.hpp" #include "memory/universe.hpp" +#include "oops/annotations.hpp" #include "oops/constantPool.hpp" #include "oops/fieldStreams.hpp" #include "oops/klassVtable.hpp" #include "oops/oop.inline.hpp" +#include "oops/recordComponent.hpp" #include "prims/jvmtiImpl.hpp" #include "prims/jvmtiRedefineClasses.hpp" #include "prims/jvmtiThreadState.inline.hpp" @@ -293,10 +295,6 @@ return false; } - // Cannot redefine or retransform a record. - if (InstanceKlass::cast(k)->is_record()) { - /* TBD: can we support redefining a record with annotations ? */ return false; - } return true; } @@ -790,6 +788,69 @@ return JVMTI_ERROR_NONE; } +// Return an error status if the class Record attribute was changed. +static jvmtiError check_record_attribute(InstanceKlass* the_class, InstanceKlass* scratch_class) { + // Get lists of record components. + Array<RecordComponent*>* the_record = the_class->record_components(); + Array<RecordComponent*>* scr_record = scratch_class->record_components(); + bool the_record_exists = the_record != NULL; + bool scr_record_exists = scr_record != NULL; + + if (the_record_exists && scr_record_exists) { + int the_num_components = the_record->length(); + int scr_num_components = scr_record->length(); + if (the_num_components != scr_num_components) { + log_trace(redefine, class, record) + ("redefined class %s attribute change error: Record num_components=%d changed to num_components=%d", + the_class->external_name(), the_num_components, scr_num_components); + return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_CLASS_ATTRIBUTE_CHANGED; + } + + // Compare each field in each record component. + ConstantPool* the_cp = the_class->constants(); + ConstantPool* scr_cp = scratch_class->constants(); + for (int x = 0; x < the_num_components; x++) { + RecordComponent* the_component = the_record->at(x); + RecordComponent* scr_component = scr_record->at(x); + const Symbol* const the_name = the_cp->symbol_at(the_component->name_index()); + const Symbol* const scr_name = scr_cp->symbol_at(scr_component->name_index()); + const Symbol* const the_descr = the_cp->symbol_at(the_component->descriptor_index()); + const Symbol* const scr_descr = scr_cp->symbol_at(scr_component->descriptor_index()); + if (the_name != scr_name || the_descr != scr_descr) { + log_trace(redefine, class, record) + ("redefined class %s attribute change error: Record name_index, descriptor_index, and/or attributes_count changed", + the_class->external_name()); + return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_CLASS_ATTRIBUTE_CHANGED; + } + + int the_gen_sig = the_component->generic_signature_index(); + int scr_gen_sig = scr_component->generic_signature_index(); + const Symbol* const the_gen_sig_sym = (the_gen_sig == 0 ? NULL : + the_cp->symbol_at(the_component->generic_signature_index())); + const Symbol* const scr_gen_sig_sym = (scr_gen_sig == 0 ? NULL : + scr_cp->symbol_at(scr_component->generic_signature_index())); + if (the_gen_sig_sym != scr_gen_sig_sym) { + log_trace(redefine, class, record) + ("redefined class %s attribute change error: Record generic_signature attribute changed", + the_class->external_name()); + return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_CLASS_ATTRIBUTE_CHANGED; + } + + // It's okay of a record component's annotations were changed. + } + + } else if (the_record_exists ^ scr_record_exists) { + const char* action_str = (the_record_exists) ? "removed" : "added"; + log_trace(redefine, class, record) + ("redefined class %s attribute change error: Record attribute %s", + the_class->external_name(), action_str); + return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_CLASS_ATTRIBUTE_CHANGED; + } + + return JVMTI_ERROR_NONE; +} + + static bool can_add_or_delete(Method* m) { // Compatibility mode return (AllowRedefinitionToAddDeleteMethods && @@ -843,6 +904,12 @@ return err; } + // Check whether the Record attribute has been changed. + err = check_record_attribute(the_class, scratch_class); + if (err != JVMTI_ERROR_NONE) { + return err; + } + // Check whether class modifiers are the same. jushort old_flags = (jushort) the_class->access_flags().get_flags(); jushort new_flags = (jushort) scratch_class->access_flags().get_flags(); @@ -1716,6 +1783,12 @@ return false; } + // rewrite constant pool references in the Record attribute: + if (!rewrite_cp_refs_in_record_attribute(scratch_class, THREAD)) { + // propagate failure back to caller + return false; + } + // rewrite constant pool references in the methods: if (!rewrite_cp_refs_in_methods(scratch_class, THREAD)) { // propagate failure back to caller @@ -1814,6 +1887,46 @@ return true; } +// Rewrite constant pool references in the Record attribute. +bool VM_RedefineClasses::rewrite_cp_refs_in_record_attribute( + InstanceKlass* scratch_class, TRAPS) { + Array<RecordComponent*>* components = scratch_class->record_components(); + if (components != NULL) { + for (int i = 0; i < components->length(); i++) { + RecordComponent* component = components->at(i); + u2 cp_index = component->name_index(); + component->set_name_index(find_new_index(cp_index)); + cp_index = component->descriptor_index(); + component->set_descriptor_index(find_new_index(cp_index)); + cp_index = component->generic_signature_index(); + if (cp_index != 0) { + component->set_generic_signature_index(find_new_index(cp_index)); + } + + AnnotationArray* annotations = component->annotations(); + if (annotations != NULL && annotations->length() != 0) { + int byte_i = 0; // byte index into annotations + if (!rewrite_cp_refs_in_annotations_typeArray(annotations, byte_i, THREAD)) { + log_debug(redefine, class, annotation)("bad record_component_annotations at %d", i); + // propagate failure back to caller + return false; + } + } + + AnnotationArray* type_annotations = component->type_annotations(); + if (type_annotations != NULL && type_annotations->length() != 0) { + int byte_i = 0; // byte index into annotations + if (!rewrite_cp_refs_in_annotations_typeArray(type_annotations, byte_i, THREAD)) { + log_debug(redefine, class, annotation)("bad record_component_type_annotations at %d", i); + // propagate failure back to caller + return false; + } + } + } + } + return true; +} + // Rewrite constant pool references in the methods. bool VM_RedefineClasses::rewrite_cp_refs_in_methods( InstanceKlass* scratch_class, TRAPS) {
--- a/src/hotspot/share/prims/jvmtiRedefineClasses.hpp Thu Oct 17 13:42:17 2019 -0700 +++ b/src/hotspot/share/prims/jvmtiRedefineClasses.hpp Fri Oct 18 11:49:23 2019 +0000 @@ -472,6 +472,7 @@ bool rewrite_cp_refs_in_fields_annotations( InstanceKlass* scratch_class, TRAPS); bool rewrite_cp_refs_in_nest_attributes(InstanceKlass* scratch_class); + bool rewrite_cp_refs_in_record_attribute(InstanceKlass* scratch_class, TRAPS); void rewrite_cp_refs_in_method(methodHandle method, methodHandle * new_method_p, TRAPS); bool rewrite_cp_refs_in_methods(InstanceKlass* scratch_class, TRAPS);
--- a/src/java.instrument/share/native/libinstrument/JavaExceptions.c Thu Oct 17 13:42:17 2019 -0700 +++ b/src/java.instrument/share/native/libinstrument/JavaExceptions.c Fri Oct 18 11:49:23 2019 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2019, 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 @@ -216,7 +216,7 @@ case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_CLASS_ATTRIBUTE_CHANGED: throwableClassName = "java/lang/UnsupportedOperationException"; - message = "class redefinition failed: attempted to change the class NestHost or NestMembers attribute"; + message = "class redefinition failed: attempted to change the class NestHost, NestMembers, or Record attribute"; break; case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_MODIFIERS_CHANGED:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/hotspot/jtreg/runtime/records/RedefineRecord.java Fri Oct 18 11:49:23 2019 +0000 @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2019, 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 + * @library /test/lib + * @summary Test that a class that is a record can be redefined. + * @modules java.base/jdk.internal.misc + * @modules java.instrument + * jdk.jartool/sun.tools.jar + * @run main RedefineRecord buildagent + * @run main/othervm/timeout=6000 RedefineRecord runtest + */ + +import java.io.FileNotFoundException; +import java.io.PrintWriter; +import java.lang.RuntimeException; +import java.lang.instrument.ClassFileTransformer; +import java.lang.instrument.Instrumentation; +import java.security.ProtectionDomain; +import java.lang.instrument.IllegalClassFormatException; +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.process.OutputAnalyzer; + +public class RedefineRecord { + + record Tester(int x, String y, long z) { } + + static class LoggingTransformer implements ClassFileTransformer { + + public LoggingTransformer() {} + + public byte[] transform(ClassLoader loader, String className, + Class classBeingRedefined, ProtectionDomain protectionDomain, + byte[] classfileBuffer) throws IllegalClassFormatException { + return null; + } + } + + public static void premain(String agentArgs, Instrumentation inst) throws Exception { + LoggingTransformer t = new LoggingTransformer(); + inst.addTransformer(t, true); + { + Class demoClass = Class.forName("RedefineRecord$Tester"); + inst.retransformClasses(demoClass); + } + } + private static void buildAgent() { + try { + ClassFileInstaller.main("RedefineRecord"); + } catch (Exception e) { + throw new RuntimeException("Could not write agent classfile", e); + } + + try { + PrintWriter pw = new PrintWriter("MANIFEST.MF"); + pw.println("Premain-Class: RedefineRecord"); + pw.println("Agent-Class: RedefineRecord"); + pw.println("Can-Redefine-Classes: true"); + pw.println("Can-Retransform-Classes: true"); + pw.close(); + } catch (FileNotFoundException e) { + throw new RuntimeException("Could not write manifest file for the agent", e); + } + + sun.tools.jar.Main jarTool = new sun.tools.jar.Main(System.out, System.err, "jar"); + if (!jarTool.run(new String[] { "-cmf", "MANIFEST.MF", "redefineagent.jar", "RedefineRecord.class" })) { + throw new RuntimeException("Could not write the agent jar file"); + } + } + public static void main(String argv[]) throws Exception { + if (argv.length == 1 && argv[0].equals("buildagent")) { + buildAgent(); + return; + } + if (argv.length == 1 && argv[0].equals("runtest")) { + String[] javaArgs1 = { "-XX:MetaspaceSize=12m", "-XX:MaxMetaspaceSize=12m", + "-javaagent:redefineagent.jar", "RedefineRecord"}; + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(javaArgs1); + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldNotContain("processing of -javaagent failed"); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/instrument/RedefineRecordAttr/Host/Host.java Fri Oct 18 11:49:23 2019 +0000 @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2019, 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. + */ + +public record Host() { + public static String getID() { return "Host/Host.java";} + public int m() { + return 1; // original class + } + public Host(int A, long B, char C) { + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/instrument/RedefineRecordAttr/Host/redef/Host.java Fri Oct 18 11:49:23 2019 +0000 @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2019, 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. + */ + +public record Host() { + public static String getID() { return "Host/redef/Host.java";} + public int m() { + return 2; // redefined class + } + public Host(int A, long B, char C) { + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/instrument/RedefineRecordAttr/HostA/Host.java Fri Oct 18 11:49:23 2019 +0000 @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2019, 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. + */ + +public record Host(int A) { + public static String getID() { return "HostA/Host.java";} + public int m() { + return 1; // original class + } + public Host(int A, long B, char C) { + this.A = A; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/instrument/RedefineRecordAttr/HostA/redef/Host.java Fri Oct 18 11:49:23 2019 +0000 @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2019, 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. + */ + +public record Host(int A) { + public static String getID() { return "HostA/redef/Host.java"; } + public int m() { + return 2; // redefined class + } + public Host(int A, long B, char C) { + this.A = A; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/instrument/RedefineRecordAttr/HostAB/Host.java Fri Oct 18 11:49:23 2019 +0000 @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2019, 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. + */ + +public record Host(int A, long B) { + public static String getID() { return "HostAB/Host.java";} + public int m() { + return 1; // original class + } + public Host(int A, long B, char C) { + this.A = A; + this.B = B; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/instrument/RedefineRecordAttr/HostAB/redef/Host.java Fri Oct 18 11:49:23 2019 +0000 @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2019, 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. + */ + +public record Host(int A, long B) { + public static String getID() { return "HostAB/redef/Host.java"; } + public int m() { + return 2; // redefined class + } + public Host(int A, long B, char C) { + this.A = A; + this.B = B; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/instrument/RedefineRecordAttr/HostABC/Host.java Fri Oct 18 11:49:23 2019 +0000 @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019, 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. + */ + +public record Host(int A, long B, char C) { + public static String getID() { return "HostABC/Host.java";} + public int m() { + return 1; // original class + } + public Host(int A, long B, char C) { + this.A = A; + this.B = B; + this.C = C; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/instrument/RedefineRecordAttr/HostABC/redef/Host.java Fri Oct 18 11:49:23 2019 +0000 @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019, 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. + */ + +public record Host(int A, long B, char C) { + public static String getID() { return "HostABC/redef/Host.java"; } + public int m() { + return 2; // redefined class + } + public Host(int A, long B, char C) { + this.A = A; + this.B = B; + this.C = C; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/instrument/RedefineRecordAttr/HostABCD/redef/Host.java Fri Oct 18 11:49:23 2019 +0000 @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2019, 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. + */ + +public record Host(int A, long B, char C, String D) { + public static String getID() { return "HostABCD/redef/Host.java"; } + public int m() { + return 2; // redefined class + } + public Host(int A, long B, char C) { + this.A = A; + this.B = B; + this.C = C; + this.D = "abc"; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/instrument/RedefineRecordAttr/HostABD/redef/Host.java Fri Oct 18 11:49:23 2019 +0000 @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019, 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. + */ + +public record Host(int A, long B, String D) { + public static String getID() { return "HostABD/redef/Host.java"; } + public int m() { + return 2; // redefined class + } + public Host(int A, long B, char C) { + this.A = A; + this.B = B; + this.D = "abc"; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/instrument/RedefineRecordAttr/HostAC/redef/Host.java Fri Oct 18 11:49:23 2019 +0000 @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2019, 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. + */ + +public record Host(int A, char C) { + public static String getID() { return "HostAC/redef/Host.java"; } + public int m() { + return 2; // redefined class + } + public Host(int A, long B, char C) { + this.A = A; + this.C = C; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/instrument/RedefineRecordAttr/HostACB/redef/Host.java Fri Oct 18 11:49:23 2019 +0000 @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019, 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. + */ + +public record Host(int A, char C, long B) { + public static String getID() { return "HostACB/redef/Host.java"; } + public int m() { + return 2; // redefined class + } + public Host(int A, long B, char C) { + this.A = A; + this.C = C; + this.B = B; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/instrument/RedefineRecordAttr/HostB/redef/Host.java Fri Oct 18 11:49:23 2019 +0000 @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2019, 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. + */ + +public record Host(long B) { + public static String getID() { return "HostB/redef/Host.java"; } + public int m() { + return 2; // redefined class + } + public Host(int A, long B, char C) { + this.B = B; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/instrument/RedefineRecordAttr/HostBA/redef/Host.java Fri Oct 18 11:49:23 2019 +0000 @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2019, 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. + */ + +public record Host(long B, int A) { + public static String getID() { return "HostBA/redef/Host.java"; } + public int m() { + return 2; // redefined class + } + public Host(int A, long B, char C) { + this.A = A; + this.B = B; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/instrument/RedefineRecordAttr/HostBAC/redef/Host.java Fri Oct 18 11:49:23 2019 +0000 @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019, 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. + */ + +public record Host(long B, int A, char C) { + public static String getID() { return "HostBAC/redef/Host.java"; } + public int m() { + return 2; // redefined class + } + public Host(int A, long B, char C) { + this.B = B; + this.A = A; + this.C = C; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/instrument/RedefineRecordAttr/HostBCA/redef/Host.java Fri Oct 18 11:49:23 2019 +0000 @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019, 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. + */ + +public record Host(long B, char C, int A) { + public static String getID() { return "HostBCA/redef/Host.java"; } + public int m() { + return 2; // redefined class + } + public Host(int A, long B, char C) { + this.B = B; + this.C = C; + this.A = A; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/instrument/RedefineRecordAttr/HostCAB/redef/Host.java Fri Oct 18 11:49:23 2019 +0000 @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019, 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. + */ + +public record Host(char C, int A, long B) { + public static String getID() { return "HostCAB/redef/Host.java"; } + public int m() { + return 2; // redefined class + } + public Host(int A, long B, char C) { + this.C = C; + this.A = A; + this.B = B; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/instrument/RedefineRecordAttr/HostCBA/redef/Host.java Fri Oct 18 11:49:23 2019 +0000 @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019, 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. + */ + +public record Host(char C, long B, int A) { + public static String getID() { return "HostCBA/redef/Host.java"; } + public int m() { + return 2; // redefined class + } + public Host(int A, long B, char C) { + this.C = C; + this.B = B; + this.A = A; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/instrument/RedefineRecordAttr/TestRecordAttr.java Fri Oct 18 11:49:23 2019 +0000 @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2019, 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 Class redefinition must preclude changes to Record attributes + * @comment This is a copy of test/jdk/java/lang/instrument/RedefineNestmateAttr/ + * @comment modified for records and the Record attribute. + * + * @library /test/lib + * @modules java.base/jdk.internal.misc + * @modules java.compiler + * java.instrument + * jdk.jartool/sun.tools.jar + * @compile ../NamedBuffer.java + * @run main RedefineClassHelper + * @compile Host/Host.java + * @run main/othervm -javaagent:redefineagent.jar -Xlog:redefine+class+record=trace TestRecordAttr Host + * @compile HostA/Host.java + * @run main/othervm -javaagent:redefineagent.jar -Xlog:redefine+class+record=trace TestRecordAttr HostA + * @compile HostAB/Host.java + * @run main/othervm -javaagent:redefineagent.jar -Xlog:redefine+class+record=trace TestRecordAttr HostAB + * @compile HostABC/Host.java + * @run main/othervm -javaagent:redefineagent.jar -Xlog:redefine+class+record=trace TestRecordAttr HostABC + */ + +/* Test Description + +The basic test class is call Host and we have variants that have zero or more +record components. Each variant of Host is defined in source code in its own +directory i.e. + +Host/Host.java defines zero record components +Record HostA/Host.java has record component "int A" +Record HostAB/Host.java has record components "int A" and "long B" (in that order) +Record HostABC/Host.java has record components "int A", "long B", and "char C" (in that order) +etc. + +Each Host class has the form: + + public record Host <zero or more record components> { + public static String getID() { return "<directory name>/Host.java"; } + + public int m() { + return 1; // original class + } + + public Host(int A, long B, char C) { + ... + } + } + +Under each directory is a directory "redef" with a modified version of the Host +class that changes the ID to e.g. Host/redef/Host.java, and the method m() +returns 2. This allows us to check we have the redefined class loaded. + +Using Host' to represent the redefined version we test redefinition +combinations as follows: + +Host: + Host -> Host' - succeeds m() returns 2 + Host -> HostA' - fails - added a record component + +HostA: + HostA -> HostA' - succeeds m() returns 2 + HostA -> Host' - fails - removed a record component + HostA -> HostAB' - fails - added a record component + HostA -> HostB' - fails - replaced a record component + +HostAB: + HostAB -> HostAB' - succeeds m() returns 2 + HostAB -> HostBA' - fails - reordered record components + HostAB -> HostA' - fails - removed a record component + HostAB -> HostABC' - fails - added a record component + HostAB -> HostAC' - fails - replaced a record component + +HostABC: + HostABC -> HostABC' - succeeds m() returns 2 + HostABC -> HostACB' - fails - reordered record components + HostABC -> HostBAC' - fails - reordered record components + HostABC -> HostBCA' - fails - reordered record components + HostABC -> HostCAB' - fails - reordered record components + HostABC -> HostCBA' - fails - reordered record components + HostABC -> HostAB' - fails - removed a record component + HostABC -> HostABCD' - fails - added a record component + HostABC -> HostABD' - fails - replaced a record component + +More than three record components doesn't add to the code coverage so +we stop here. + +Note that we always try to load the redefined version even when we expect it +to fail. + +We can only directly load one class Host per classloader, so to run all the +groups we either need to use new classloaders, or we reinvoke the test +requesting a different primary directory. We chose the latter using +multiple @run tags. So we preceed as follows: + + @compile Host/Host.java + @run TestRecordAttr Host + @compile HostA/Host.java - replaces previous Host.class + @run TestRecordAttr HostA + @compile HostAB/Host.java - replaces previous Host.class + @run TestRecordAttr HostAB +etc. + +Within the test we directly compile redefined versions of the classes, +using CompilerUtil, and then read the .class file directly as a byte[] +to use with the RedefineClassHelper. + +*/ + +import java.io.File; +import java.io.FileInputStream; +import jdk.test.lib.ByteCodeLoader; +import jdk.test.lib.compiler.CompilerUtils; +import jdk.test.lib.compiler.InMemoryJavaCompiler; +import static jdk.test.lib.Asserts.assertTrue; + +public class TestRecordAttr { + + static final String SRC = System.getProperty("test.src"); + static final String DEST = System.getProperty("test.classes"); + static final boolean VERBOSE = Boolean.getBoolean("verbose"); + + public static void main(String[] args) throws Throwable { + String origin = args[0]; + System.out.println("Testing original Host class from " + origin); + + // Make sure the Host class loaded directly is an original version + // and from the expected location. Use a ctor common to all Host + // classes. + Host h = new Host(3, 4, 'a'); + assertTrue(h.m() == 1); + assertTrue(Host.getID().startsWith(origin + "/")); + + String[] badTransforms; // directories of bad classes + String[] goodTransforms; // directories of good classes + + switch (origin) { + case "Host": + badTransforms = new String[] { + "HostA" // add record component + }; + goodTransforms = new String[] { + origin + }; + break; + + case "HostA": + badTransforms = new String[] { + "Host", // remove record component + "HostAB", // add record component + "HostB" // change record component + }; + goodTransforms = new String[] { + origin + }; + break; + + case "HostAB": + badTransforms = new String[] { + "HostA", // remove record component + "HostABC", // add record component + "HostAC", // change record component + "HostBA" // reorder record components + }; + goodTransforms = new String[] { + origin, + }; + break; + + case "HostABC": + badTransforms = new String[] { + "HostAB", // remove record component + "HostABCD", // add record component + "HostABD", // change record component + "HostACB", // reorder record components + "HostBAC", // reorder record components + "HostBCA", // reorder record components + "HostCAB", // reorder record components + "HostCBA" // reorder record components + }; + goodTransforms = new String[] { + origin + }; + break; + + default: throw new Error("Unknown test directory: " + origin); + } + + // Compile and check bad transformations + checkBadTransforms(Host.class, badTransforms); + + // Compile and check good transformations + checkGoodTransforms(Host.class, goodTransforms); + } + + static void checkGoodTransforms(Class<?> c, String[] dirs) throws Throwable { + for (String dir : dirs) { + dir += "/redef"; + System.out.println("Trying good retransform from " + dir); + byte[] buf = bytesForHostClass(dir); + RedefineClassHelper.redefineClass(c, buf); + + // Test redefintion worked + Host h = new Host(3, 4, 'a'); + assertTrue(h.m() == 2); + if (VERBOSE) System.out.println("Redefined ID: " + Host.getID()); + assertTrue(Host.getID().startsWith(dir)); + } + } + + static void checkBadTransforms(Class<?> c, String[] dirs) throws Throwable { + for (String dir : dirs) { + dir += "/redef"; + System.out.println("Trying bad retransform from " + dir); + byte[] buf = bytesForHostClass(dir); + try { + RedefineClassHelper.redefineClass(c, buf); + throw new Error("Retransformation from directory " + dir + + " succeeded unexpectedly"); + } + catch (UnsupportedOperationException uoe) { + if (uoe.getMessage().contains("attempted to change the class") && + uoe.getMessage().contains(" Record")) { + System.out.println("Got expected exception " + uoe); + } + else throw new Error("Wrong UnsupportedOperationException", uoe); + } + } + } + + static byte[] bytesForHostClass(String dir) throws Throwable { + compile("/" + dir); + File clsfile = new File(DEST + "/" + dir + "/Host.class"); + if (VERBOSE) System.out.println("Reading bytes from " + clsfile); + byte[] buf = null; + try (FileInputStream str = new FileInputStream(clsfile)) { + return buf = NamedBuffer.loadBufferFromStream(str); + } + } + + static void compile(String dir) throws Throwable { + File src = new File(SRC + dir); + File dst = new File(DEST + dir); + if (VERBOSE) System.out.println("Compiling from: " + src + "\n" + + " to: " + dst); + CompilerUtils.compile(src.toPath(), + dst.toPath(), + false /* don't recurse */, + new String[0]); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/instrument/RedefineRecordAttrGenericSig/Host/Host.java Fri Oct 18 11:49:23 2019 +0000 @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2019, 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. + */ + +public record Host(java.lang.Object a, java.lang.String b) { + public static String getID() { return "Host/redef/Host.java";} + public int m() { + return 1; // redefined class + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/instrument/RedefineRecordAttrGenericSig/Host/redef/Host.java Fri Oct 18 11:49:23 2019 +0000 @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2019, 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. + */ + +public record Host(java.lang.Object a, java.lang.String b) { + public static String getID() { return "Host/redef/Host.java";} + public int m() { + return 2; // redefined class + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/instrument/RedefineRecordAttrGenericSig/HostA/Host.java Fri Oct 18 11:49:23 2019 +0000 @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2019, 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. + */ + +public record Host<T>(T a, java.lang.String b) { + public static String getID() { return "HostA/Host.java";} + public int m() { + return 1; // original class + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/instrument/RedefineRecordAttrGenericSig/HostA/redef/Host.java Fri Oct 18 11:49:23 2019 +0000 @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2019, 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. + */ + +public record Host<T>(T a, java.lang.String b) { + public static String getID() { return "HostA/redef/Host.java"; } + public int m() { + return 2; // redefined class + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/instrument/RedefineRecordAttrGenericSig/HostB/redef/Host.java Fri Oct 18 11:49:23 2019 +0000 @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2019, 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. + */ + +public record Host<X>(X a, java.lang.String b) { + public static String getID() { return "HostB/redef/Host.java"; } + public int m() { + return 2; // redefined class + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/instrument/RedefineRecordAttrGenericSig/TestRecordAttrGenericSig.java Fri Oct 18 11:49:23 2019 +0000 @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2019, 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 Class redefinition must preclude changes to Record attributes + * @comment This is a copy of test/jdk/java/lang/instrument/RedefineNestmateAttr/ + * @comment modified for records and the Record attribute. + * + * @library /test/lib + * @modules java.base/jdk.internal.misc + * @modules java.compiler + * java.instrument + * jdk.jartool/sun.tools.jar + * @compile ../NamedBuffer.java + * @run main RedefineClassHelper + * @compile Host/Host.java + * @run main/othervm -javaagent:redefineagent.jar -Xlog:redefine+class+record=trace TestRecordAttrGenericSig Host + * @compile HostA/Host.java + * @run main/othervm -javaagent:redefineagent.jar -Xlog:redefine+class+record=trace TestRecordAttrGenericSig HostA + */ + +/* Test Description + +The basic test class is call Host and we have variants that have record components +with no or different generic_signature attributes. Each variant of Host is defined +in source code in its own directory i.e. + +Host/Host.java has record components with no generic signature attributes. +HostA/Host.java has a record component with generic signature "TT". +HostB/Host.java has a record component with generic signature "TX". + +Each Host class has the form: + + public record Host <possible generic>(<java.lang.Object or generic> a, java.lang.String b) { + public static String getID() { return "<directory name>/Host.java"; } + + public int m() { + return 1; // original class + } + + } + +Under each directory is a directory "redef" with a modified version of the Host +class that changes the ID to e.g. Host/redef/Host.java, and the method m() +returns 2. This allows us to check we have the redefined class loaded. + +Using Host' to represent the redefined version we test redefinition +combinations as follows: + +Host: + Host -> Host' - succeeds m() returns 2 + Host -> HostA' - fails - added a generic signature + +HostA: + HostA -> HostA' - succeeds m() returns 2 + HostA -> Host' - fails - removed a generic signature + HostA -> HostB' - fails - replaced a generic signature + +Note that we always try to load the redefined version even when we expect it +to fail. + +We can only directly load one class Host per classloader, so to run all the +groups we either need to use new classloaders, or we reinvoke the test +requesting a different primary directory. We chose the latter using +multiple @run tags. So we preceed as follows: + + @compile Host/Host.java + @run TestRecordAttrGenericSig Host + @compile HostA/Host.java - replaces previous Host.class + @run TestRecordAttrGenericSig HostA +etc. + +Within the test we directly compile redefined versions of the classes, +using CompilerUtil, and then read the .class file directly as a byte[] +to use with the RedefineClassHelper. + +*/ + +import java.io.File; +import java.io.FileInputStream; +import jdk.test.lib.ByteCodeLoader; +import jdk.test.lib.compiler.CompilerUtils; +import jdk.test.lib.compiler.InMemoryJavaCompiler; +import static jdk.test.lib.Asserts.assertTrue; + +public class TestRecordAttrGenericSig { + + static final String SRC = System.getProperty("test.src"); + static final String DEST = System.getProperty("test.classes"); + static final boolean VERBOSE = Boolean.getBoolean("verbose"); + + public static void main(String[] args) throws Throwable { + String origin = args[0]; + System.out.println("Testing original Host class from " + origin); + + // Make sure the Host class loaded directly is an original version + // and from the expected location. Use a ctor common to all Host + // classes. + Host h = new Host("abc", "def"); + assertTrue(h.m() == 1); + assertTrue(Host.getID().startsWith(origin + "/")); + + String[] badTransforms; // directories of bad classes + String[] goodTransforms; // directories of good classes + + switch (origin) { + case "Host": + badTransforms = new String[] { + "HostA" // add a generic signature to a record component + }; + goodTransforms = new String[] { + origin + }; + break; + + case "HostA": + badTransforms = new String[] { + "Host", // remove a generic signature from a record component + "HostB" // change a record component generic signature + }; + goodTransforms = new String[] { + origin + }; + break; + + default: throw new Error("Unknown test directory: " + origin); + } + + // Compile and check bad transformations + checkBadTransforms(Host.class, badTransforms); + + // Compile and check good transformations + checkGoodTransforms(Host.class, goodTransforms); + } + + static void checkGoodTransforms(Class<?> c, String[] dirs) throws Throwable { + for (String dir : dirs) { + dir += "/redef"; + System.out.println("Trying good retransform from " + dir); + byte[] buf = bytesForHostClass(dir); + RedefineClassHelper.redefineClass(c, buf); + + // Test redefintion worked + Host h = new Host("abc", "def"); + assertTrue(h.m() == 2); + if (VERBOSE) System.out.println("Redefined ID: " + Host.getID()); + assertTrue(Host.getID().startsWith(dir)); + } + } + + static void checkBadTransforms(Class<?> c, String[] dirs) throws Throwable { + for (String dir : dirs) { + dir += "/redef"; + System.out.println("Trying bad retransform from " + dir); + byte[] buf = bytesForHostClass(dir); + try { + RedefineClassHelper.redefineClass(c, buf); + throw new Error("Retransformation from directory " + dir + + " succeeded unexpectedly"); + } + catch (UnsupportedOperationException uoe) { + if (uoe.getMessage().contains("attempted to change the class") && + uoe.getMessage().contains(" Record")) { + System.out.println("Got expected exception " + uoe); + } + else throw new Error("Wrong UnsupportedOperationException", uoe); + } + } + } + + static byte[] bytesForHostClass(String dir) throws Throwable { + compile("/" + dir); + File clsfile = new File(DEST + "/" + dir + "/Host.class"); + if (VERBOSE) System.out.println("Reading bytes from " + clsfile); + byte[] buf = null; + try (FileInputStream str = new FileInputStream(clsfile)) { + return buf = NamedBuffer.loadBufferFromStream(str); + } + } + + static void compile(String dir) throws Throwable { + File src = new File(SRC + dir); + File dst = new File(DEST + dir); + if (VERBOSE) System.out.println("Compiling from: " + src + "\n" + + " to: " + dst); + CompilerUtils.compile(src.toPath(), + dst.toPath(), + false /* don't recurse */, + new String[0]); + } +}