1 /* 2 * Copyright (c) 2023, 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 26 * @modules java.base/java.lang.runtime:open 27 * java.base/jdk.internal.value 28 * java.base/jdk.internal.vm.annotation 29 * @enablePreview 30 * @run junit/othervm SubstitutabilityTest 31 */ 32 33 import java.lang.reflect.Method; 34 import java.util.stream.Stream; 35 36 import jdk.internal.value.ValueClass; 37 import jdk.internal.vm.annotation.ImplicitlyConstructible; 38 import jdk.internal.vm.annotation.NullRestricted; 39 40 import org.junit.jupiter.api.Test; 41 import org.junit.jupiter.params.ParameterizedTest; 42 import org.junit.jupiter.params.provider.Arguments; 43 import org.junit.jupiter.params.provider.MethodSource; 44 45 import static org.junit.jupiter.api.Assertions.*; 46 47 public class SubstitutabilityTest { 48 @ImplicitlyConstructible 49 static value class Point { 50 public int x; 51 public int y; 52 Point(int x, int y) { 53 this.x = x; 54 this.y = y; 55 } 56 } 57 58 @ImplicitlyConstructible 59 static value class Line { 60 @NullRestricted 61 Point p1; 62 @NullRestricted 63 Point p2; 64 65 Line(Point p1, Point p2) { 66 this.p1 = p1; 67 this.p2 = p2; 68 } 69 Line(int x1, int y1, int x2, int y2) { 70 this(new Point(x1, y1), new Point(x2, y2)); 71 } 72 } 73 74 // contains null-reference and null-restricted fields 75 @ImplicitlyConstructible 76 static value class MyValue { 77 MyValue2 v1; 78 @NullRestricted 79 MyValue2 v2; 80 public MyValue(MyValue2 v1, MyValue2 v2) { 81 this.v1 = v1; 82 this.v2 = v2; 83 } 84 } 85 86 @ImplicitlyConstructible 87 static value class MyValue2 { 88 static int cnt = 0; 89 int x; 90 MyValue2(int x) { 91 this.x = x; 92 } 93 } 94 95 @ImplicitlyConstructible 96 static value class MyFloat { 97 public static float NaN1 = Float.intBitsToFloat(0x7ff00001); 98 public static float NaN2 = Float.intBitsToFloat(0x7ff00002); 99 float x; 100 MyFloat(float x) { 101 this.x = x; 102 } 103 public String toString() { 104 return Float.toString(x); 105 } 106 } 107 108 @ImplicitlyConstructible 109 static value class MyDouble { 110 public static double NaN1 = Double.longBitsToDouble(0x7ff0000000000001L); 111 public static double NaN2 = Double.longBitsToDouble(0x7ff0000000000002L); 112 double x; 113 MyDouble(double x) { 114 this.x = x; 115 } 116 public String toString() { 117 return Double.toString(x); 118 } 119 } 120 121 static Stream<Arguments> substitutableCases() { 122 Point p1 = new Point(10, 10); 123 Point p2 = new Point(20, 20); 124 Line l1 = new Line(p1, p2); 125 MyValue v1 = new MyValue(null, ValueClass.zeroInstance(MyValue2.class)); 126 MyValue v2 = new MyValue(new MyValue2(2), new MyValue2(3)); 127 MyValue2 value2 = new MyValue2(2); 128 MyValue2 value3 = new MyValue2(3); 129 MyValue[] va = new MyValue[1]; 130 return Stream.of( 131 Arguments.of(new MyFloat(1.0f), new MyFloat(1.0f)), 132 Arguments.of(new MyDouble(1.0), new MyDouble(1.0)), 133 Arguments.of(new MyFloat(Float.NaN), new MyFloat(Float.NaN)), 134 Arguments.of(new MyDouble(Double.NaN), new MyDouble(Double.NaN)), 135 Arguments.of(p1, new Point(10, 10)), 136 Arguments.of(p2, new Point(20, 20)), 137 Arguments.of(l1, new Line(10,10, 20,20)), 138 Arguments.of(v1, ValueClass.zeroInstance(MyValue.class)), 139 Arguments.of(v2, new MyValue(value2, value3)), 140 Arguments.of(va[0], null) 141 ); 142 } 143 144 @ParameterizedTest 145 @MethodSource("substitutableCases") 146 public void substitutableTest(Object a, Object b) { 147 assertTrue(isSubstitutable(a, b)); 148 } 149 150 static Stream<Arguments> notSubstitutableCases() { 151 // MyValue![] va = new MyValue![1]; 152 MyValue[] va = new MyValue[] { ValueClass.zeroInstance(MyValue.class) }; 153 Object[] oa = new Object[] { va }; 154 return Stream.of( 155 Arguments.of(new MyFloat(1.0f), new MyFloat(2.0f)), 156 Arguments.of(new MyDouble(1.0), new MyDouble(2.0)), 157 Arguments.of(new MyFloat(MyFloat.NaN1), new MyFloat(MyFloat.NaN2)), 158 Arguments.of(new MyDouble(MyDouble.NaN1), new MyDouble(MyDouble.NaN2)), 159 Arguments.of(new Point(10, 10), new Point(20, 20)), 160 /* 161 * Verify ValueObjectMethods::isSubstitutable that does not 162 * throw an exception if any one of parameter is null or if 163 * the parameters are of different types. 164 */ 165 Arguments.of(va[0], null), 166 Arguments.of(null, va[0]), 167 Arguments.of(va[0], oa), 168 Arguments.of(va[0], oa[0]), 169 Arguments.of(va, oa), 170 Arguments.of(new Point(10, 10), Integer.valueOf(10)), 171 Arguments.of(Integer.valueOf(10), Integer.valueOf(20)) 172 ); 173 } 174 175 @ParameterizedTest 176 @MethodSource("notSubstitutableCases") 177 public void notSubstitutableTest(Object a, Object b) { 178 assertFalse(isSubstitutable(a, b)); 179 } 180 181 @Test 182 public void nullArguments() { 183 assertTrue(isSubstitutable(null, null)); 184 } 185 186 private static final Method IS_SUBSTITUTABLE; 187 static { 188 Method m = null; 189 try { 190 Class<?> c = Class.forName("java.lang.runtime.ValueObjectMethods"); 191 m = c.getDeclaredMethod("isSubstitutable", Object.class, Object.class); 192 m.setAccessible(true); 193 } catch (ReflectiveOperationException e) { 194 throw new RuntimeException(e); 195 } 196 IS_SUBSTITUTABLE = m; 197 } 198 private static boolean isSubstitutable(Object a, Object b) { 199 try { 200 return (boolean) IS_SUBSTITUTABLE.invoke(null, a, b); 201 } catch (ReflectiveOperationException e) { 202 throw new RuntimeException(e); 203 } 204 } 205 }