1 /*
  2  * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  *
 23  */
 24 
 25 /*
 26  * @test
 27  * @summary Ensure archived nullable flat field and static null-restricted field metadata are validated when the field class is substituted.
 28  * @bug 8382584
 29  * @requires vm.cds
 30  * @library /test/lib
 31  * @enablePreview
 32  * @modules java.base/jdk.internal.vm.annotation
 33  * @compile ArchivedFieldMetadataMismatchTest.java
 34  * @run driver jdk.test.lib.helpers.StrictProcessor ArchivedFieldMetadataInstanceHolder
 35  * @run main/othervm ArchivedFieldMetadataMismatchTest
 36  */
 37 
 38 import java.io.File;
 39 import java.lang.invoke.MethodHandles;
 40 import jdk.test.lib.helpers.ClassFileInstaller;
 41 import java.nio.file.Files;
 42 import java.nio.file.Path;
 43 
 44 import jdk.internal.vm.annotation.LooselyConsistentValue;
 45 import jdk.internal.vm.annotation.NullRestricted;
 46 import jdk.test.lib.helpers.StrictInit;
 47 import jdk.test.lib.process.OutputAnalyzer;
 48 
 49 public class ArchivedFieldMetadataMismatchTest {
 50     static final String[] appClasses = {
 51         "ArchivedFieldMetadataInstanceApp",
 52         "ArchivedFieldMetadataInstancePoint",
 53         "ArchivedFieldMetadataInstanceHolder",
 54         "ArchivedFieldMetadataStaticApp",
 55         "ArchivedFieldMetadataStaticPoint",
 56         "ArchivedFieldMetadataStaticHolder",
 57     };
 58     static String appJar;
 59 
 60     public static void main(String[] args) throws Exception {
 61        appJar = ClassFileInstaller.writeJar("app.jar", appClasses);
 62        testInstanceField();
 63        testStaticField();
 64     }
 65 
 66     private static void testInstanceField() throws Exception {
 67         String pointClassFile = new File(System.getProperty("test.classes", "."),
 68                                          "ArchivedFieldMetadataInstancePoint.class").getPath();
 69 
 70         TestCommon.checkDump(TestCommon.dump(appJar,
 71                         appClasses,
 72                         "--enable-preview",
 73                         "-XX:+UnlockDiagnosticVMOptions",
 74                         "-XX:+UseNullableNonAtomicValueFlattening"));
 75 
 76         OutputAnalyzer output = TestCommon.exec(appJar,
 77                         "--enable-preview",
 78                         "-Xlog:class+load,class+preload",
 79                         "-XX:+UnlockDiagnosticVMOptions",
 80                         "-XX:+UseNullableNonAtomicValueFlattening",
 81                         "ArchivedFieldMetadataInstanceApp", pointClassFile);
 82         TestCommon.checkExec(output);
 83 
 84         output.shouldContain("Preloading of class ArchivedFieldMetadataInstancePoint during loading of shared class " +
 85                              "ArchivedFieldMetadataInstanceHolder (cause: archived flat/null-restricted field metadata) " +
 86                              "failed : app substituted a different version");
 87     }
 88 
 89     private static void testStaticField() throws Exception {
 90         String pointClassFile = new File(System.getProperty("test.classes", "."),
 91                                          "ArchivedFieldMetadataStaticPoint.class").getPath();
 92 
 93         TestCommon.checkDump(TestCommon.dump(appJar,
 94                         appClasses,
 95                         "--enable-preview"));
 96 
 97         OutputAnalyzer output = TestCommon.exec(appJar,
 98                         "--enable-preview",
 99                         "-Xlog:class+load,class+preload",
100                         "ArchivedFieldMetadataStaticApp", pointClassFile);
101         TestCommon.checkExec(output);
102 
103         output.shouldContain("Preloading of class ArchivedFieldMetadataStaticPoint during loading of shared class " +
104                              "ArchivedFieldMetadataStaticHolder (cause: archived flat/null-restricted field metadata) " +
105                              "failed : app substituted a different version");
106     }
107 }
108 
109 class ArchivedFieldMetadataInstanceApp {
110     public static void main(String[] args) throws Throwable {
111         Path classFile = Path.of(args[0]);
112         byte[] classBytes = Files.readAllBytes(classFile);
113         Class<?> fieldClass = MethodHandles.lookup().defineClass(classBytes);
114 
115         ArchivedFieldMetadataInstanceHolder holder = new ArchivedFieldMetadataInstanceHolder();
116 
117         if (holder.p.getClass() != fieldClass) {
118             throw new RuntimeException("Mismatched field class");
119         }
120     }
121 }
122 
123 class ArchivedFieldMetadataInstanceHolder {
124     @StrictInit
125     final ArchivedFieldMetadataInstancePoint p;
126 
127     ArchivedFieldMetadataInstanceHolder() {
128         p = new ArchivedFieldMetadataInstancePoint();
129         super();
130     }
131 }
132 
133 @LooselyConsistentValue
134 value class ArchivedFieldMetadataInstancePoint {
135     int x = 0;
136     int y = 0;
137 }
138 
139 class ArchivedFieldMetadataStaticApp {
140     public static void main(String[] args) throws Throwable {
141         Path classFile = Path.of(args[0]);
142         byte[] classBytes = Files.readAllBytes(classFile);
143         Class<?> fieldClass = MethodHandles.lookup().defineClass(classBytes);
144 
145         if (ArchivedFieldMetadataStaticHolder.p.getClass() != fieldClass) {
146             throw new RuntimeException("Mismatched static field class");
147         }
148     }
149 }
150 
151 class ArchivedFieldMetadataStaticHolder {
152     @NullRestricted
153     static ArchivedFieldMetadataStaticPoint p = new ArchivedFieldMetadataStaticPoint();
154 }
155 
156 value class ArchivedFieldMetadataStaticPoint {
157     int x = 0;
158     int y = 0;
159 }