1 /*
  2  * Copyright (c) 2024, 2024, 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  * @test id=Oops32
 26  * @requires vm.bits == 32
 27  * @requires vm.flagless
 28  * @library /test/lib
 29  * @modules java.base/jdk.internal.vm.annotation
 30  * @enablePreview
 31  * @compile FieldLayoutAnalyzer.java FieldAlignmentTest.java
 32  * @run main/othervm FieldAlignmentTest 0
 33  */
 34 
 35   /*
 36  * @test id=CompressedOops
 37  * @requires vm.bits == 64
 38  * @requires vm.flagless
 39  * @library /test/lib
 40  * @modules java.base/jdk.internal.vm.annotation
 41  * @enablePreview
 42  * @compile FieldLayoutAnalyzer.java FieldAlignmentTest.java
 43  * @run main/othervm FieldAlignmentTest 1
 44  */
 45 
 46   /*
 47  * @test id=NoCompressedOops
 48  * @requires vm.bits == 64
 49  * @requires vm.flagless
 50  * @library /test/lib
 51  * @modules java.base/jdk.internal.vm.annotation
 52  * @enablePreview
 53  * @compile FieldLayoutAnalyzer.java FieldAlignmentTest.java
 54  * @run main/othervm FieldAlignmentTest 2
 55  */
 56 
 57  import java.util.ArrayList;
 58  import java.util.Collections;
 59  import java.util.List;
 60 
 61  import jdk.internal.vm.annotation.LooselyConsistentValue;
 62  import jdk.internal.vm.annotation.NullRestricted;
 63  import jdk.internal.vm.annotation.Strict;
 64 
 65 
 66  import jdk.test.lib.Asserts;
 67  import jdk.test.lib.ByteCodeLoader;
 68  import jdk.test.lib.helpers.ClassFileInstaller;
 69  import jdk.test.lib.compiler.InMemoryJavaCompiler;
 70  import jdk.test.lib.process.OutputAnalyzer;
 71  import jdk.test.lib.process.ProcessTools;
 72 
 73  public class FieldAlignmentTest {
 74   public static class ZeroByte { }
 75   public static class OneByte { byte b; }
 76   public static class TwoByte { byte b0; byte b1; }
 77   public static class ThreeByte { byte b0; byte b1; byte b2; }
 78   public static class FourByte { byte b0; byte b1; byte b2; byte b3; }
 79   public static class FiveByte { byte b0; byte b1; byte b2; byte b3; byte b4; }
 80   public static class SixByte { byte b0; byte b1; byte b2; byte b3; byte b4; byte b5; }
 81   public static class SevenByte { byte b0; byte b1; byte b2; byte b3; byte b4; byte b5; byte b6; }
 82   public static final String[] superNames = { ZeroByte.class.getCanonicalName(),
 83                                               OneByte.class.getCanonicalName(),
 84                                               TwoByte.class.getCanonicalName(),
 85                                               ThreeByte.class.getCanonicalName(),
 86                                               FourByte.class.getCanonicalName(),
 87                                               FiveByte.class.getCanonicalName(),
 88                                               SixByte.class.getCanonicalName(),
 89                                               SevenByte.class.getCanonicalName() };
 90   public static final String[] valueNames = { ValueOneByte.class.getCanonicalName(),
 91                                               ValueOneChar.class.getCanonicalName(),
 92                                               ValueOneShort.class.getCanonicalName(),
 93                                               ValueOneInt.class.getCanonicalName(),
 94                                               ValueOneLong.class.getCanonicalName(),
 95                                               ValueOneFloat.class.getCanonicalName(),
 96                                               ValueOneDouble.class.getCanonicalName(),
 97                                               ValueByteLong.class.getCanonicalName(),
 98                                               ValueByteInt.class.getCanonicalName() };
 99 
100   List<String> testNames = new ArrayList<String>();
101 
102   @LooselyConsistentValue static value class ValueOneByte { byte val = 0; }
103   @LooselyConsistentValue static value class ValueOneChar { char val = 0; }
104   @LooselyConsistentValue static value class ValueOneShort { short val = 0; }
105   @LooselyConsistentValue static value class ValueOneInt { int val = 0; }
106   @LooselyConsistentValue static value class ValueOneLong { long val = 0; }
107   @LooselyConsistentValue static value class ValueOneFloat { float val = 0f; }
108   @LooselyConsistentValue static value class ValueOneDouble { double val = 0d; }
109 
110   @LooselyConsistentValue static value class ValueByteLong { byte b = 0; long l = 0; }
111   @LooselyConsistentValue static value class ValueByteInt { byte b = 0; int i = 0; }
112 
113   void generateTests() throws Exception {
114     for (String vName : valueNames) {
115       for (String sName : superNames) {
116         String vNameShort = vName.substring(vName.lastIndexOf('.') + 1);
117         String sNameShort = sName.substring(sName.lastIndexOf('.') + 1);
118         String className = "Test" + vNameShort + "With" + sNameShort;
119         String sourceCode = "import jdk.internal.vm.annotation.NullRestricted;" +
120                             "import jdk.internal.vm.annotation.Strict;" +
121                             "public class " + className + " extends " + sName + " { " +
122                             "    @Strict" +
123                             "    @NullRestricted" +
124                             "    " + vName + " v1 = new " + vName + "();" +
125                             "}";
126         String java_version = System.getProperty("java.specification.version");
127         byte[] byteCode = InMemoryJavaCompiler.compile(className, sourceCode,
128                                                       "-source", java_version, "--enable-preview",
129                                                       "--add-exports", "java.base/jdk.internal.vm.annotation=ALL-UNNAMED");
130         jdk.test.lib.helpers.ClassFileInstaller.writeClassToDisk(className, byteCode);
131         testNames.add(className);
132       }
133     }
134   }
135 
136   void generateTestRunner() throws Exception {
137     String className = "TestRunner";
138     StringBuilder sb = new StringBuilder();
139     sb.append("public class ").append(className).append(" {");
140     sb.append("    public void main(String[] args) {");
141     for (String name : testNames) {
142       sb.append("        ").append(name).append(" var").append(name).append(" = new ").append(name).append("();");
143     }
144     sb.append("    }");
145     sb.append("}");
146     String java_version = System.getProperty("java.specification.version");
147     byte[] byteCode = InMemoryJavaCompiler.compile(className, sb.toString(),
148                                                    "-source", java_version, "--enable-preview",
149                                                    "-cp", ".");
150     jdk.test.lib.helpers.ClassFileInstaller.writeClassToDisk(className, byteCode);
151   }
152 
153   static ProcessBuilder exec(String compressedOopsArg, String... args) throws Exception {
154     List<String> argsList = new ArrayList<>();
155     Collections.addAll(argsList, "--enable-preview");
156     Collections.addAll(argsList, "-XX:+UnlockDiagnosticVMOptions");
157     Collections.addAll(argsList, "-XX:+PrintFieldLayout");
158     Collections.addAll(argsList, "-Xshare:off");
159     if (compressedOopsArg != null) {
160       Collections.addAll(argsList, compressedOopsArg);
161     }
162     Collections.addAll(argsList, "-Xmx256m");
163     Collections.addAll(argsList, "-cp", System.getProperty("java.class.path") + System.getProperty("path.separator") +".");
164     Collections.addAll(argsList, args);
165     return ProcessTools.createTestJavaProcessBuilder(argsList);
166   }
167 
168   public static void main(String[] args) throws Exception {
169     String compressedOopsArg;
170 
171     switch(args[0]) {
172       case "0": compressedOopsArg = null;
173                 break;
174       case "1": compressedOopsArg = "-XX:+UseCompressedOops";
175                 break;
176       case "2": compressedOopsArg = "-XX:-UseCompressedOops";
177                 break;
178       default: throw new RuntimeException("Unrecognized configuration");
179     }
180 
181     // Generate test classes
182     FieldAlignmentTest fat = new FieldAlignmentTest();
183     fat.generateTests();
184     fat.generateTestRunner();
185 
186     // Execute the test runner in charge of loading all test classes
187     ProcessBuilder pb = exec(compressedOopsArg, "TestRunner");
188     OutputAnalyzer out = new OutputAnalyzer(pb.start());
189 
190     if (out.getExitValue() != 0) {
191       out.outputTo(System.out);
192     }
193     Asserts.assertEquals(out.getExitValue(), 0, "Something went wrong while running the tests");
194 
195     // Analyze the test runner output
196     FieldLayoutAnalyzer.LogOutput lo = new FieldLayoutAnalyzer.LogOutput(out.asLines());
197 
198     FieldLayoutAnalyzer fla =  FieldLayoutAnalyzer.createFieldLayoutAnalyzer(lo);
199     try {
200       fla.check();
201     } catch (Throwable t) {
202       out.outputTo(System.out);
203       throw t;
204     }
205   }
206  }