1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package crossvmtest.java.lang; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertFalse; 21 import static org.junit.Assert.assertNotEquals; 22 import static org.junit.Assert.assertThrows; 23 import static org.junit.Assert.assertTrue; 24 import static org.junit.Assert.fail; 25 26 import org.junit.Assert; 27 import org.junit.Test; 28 29 import java.io.ByteArrayInputStream; 30 import java.io.ByteArrayOutputStream; 31 import java.io.IOException; 32 import java.io.NotSerializableException; 33 import java.io.ObjectInputStream; 34 import java.io.ObjectOutputStream; 35 import java.io.Serializable; 36 import java.lang.annotation.ElementType; 37 import java.lang.annotation.Retention; 38 import java.lang.annotation.RetentionPolicy; 39 import java.lang.annotation.Target; 40 import java.lang.invoke.MethodHandles; 41 import java.lang.invoke.VarHandle; 42 import java.lang.reflect.Constructor; 43 import java.lang.reflect.Field; 44 import java.util.Arrays; 45 import java.util.function.IntFunction; 46 import java.util.function.Supplier; 47 48 public class RecordTest { 49 RecordInteger(int x)50 record RecordInteger(int x) {} 51 52 private static class NonRecordInteger { 53 NonRecordInteger(int x)54 NonRecordInteger(int x) { 55 this.x = x; 56 } 57 private final int x; 58 } 59 60 @Retention(RetentionPolicy.RUNTIME) 61 @Target({ElementType.FIELD, ElementType.RECORD_COMPONENT}) 62 public @interface CustomAnnotation { value()63 String value(); 64 } 65 @Retention(RetentionPolicy.RUNTIME) 66 @Target({ElementType.FIELD, ElementType.RECORD_COMPONENT}) 67 public @interface CustomAnnotation2 { 68 customAnnotations()69 CustomAnnotation[] customAnnotations(); 70 } RecordInteger2(@ustomAnnotation2customAnnotations = {@CustomAnnotation("a")}) @ustomAnnotation"b") int x)71 record RecordInteger2(@CustomAnnotation2(customAnnotations = {@CustomAnnotation("a")}) 72 @CustomAnnotation("b") int x) {} 73 RecordString(String s)74 record RecordString(String s) { 75 public static final int Y = 1; 76 public static final String A = "A"; 77 78 } 79 80 public record SerializableRecord(int x, String s) implements Serializable {} 81 82 @Test testHashCode()83 public void testHashCode() { 84 RecordInteger a = new RecordInteger(9); 85 RecordInteger b = new RecordInteger(9); 86 RecordInteger c = new RecordInteger(0); 87 88 assertEquals(a.hashCode(), b.hashCode()); 89 assertNotEquals(a.hashCode(), c.hashCode()); 90 } 91 92 @Test testEquals()93 public void testEquals() { 94 RecordInteger a = new RecordInteger(9); 95 RecordInteger b = new RecordInteger(9); 96 RecordInteger c = new RecordInteger(0); 97 98 assertTrue(a.equals(b)); 99 assertEquals(a, b); 100 assertFalse(a.equals(c)); 101 assertNotEquals(a, c); 102 } 103 104 @Test testToString()105 public void testToString() { 106 RecordInteger a = new RecordInteger(9); 107 RecordInteger b = new RecordInteger(9); 108 RecordInteger c = new RecordInteger(0); 109 110 assertEquals(a.toString(), b.toString()); 111 assertNotEquals(a.toString(), c.toString()); 112 } 113 114 @Test testIsRecord()115 public void testIsRecord() throws Exception { 116 assertFalse(Object.class.isRecord()); 117 assertFalse(Record.class.isRecord()); 118 assertFalse(String.class.isRecord()); 119 assertFalse(NonRecordInteger.class.isRecord()); 120 121 RecordInteger a = new RecordInteger(9); 122 assertTrue(a.getClass().isRecord()); 123 assertTrue(RecordInteger2.class.isRecord()); 124 } 125 126 @Test testReflectedConstructor()127 public void testReflectedConstructor() throws ReflectiveOperationException { 128 RecordInteger a = new RecordInteger(9); 129 130 Constructor<?> c = RecordInteger.class.getDeclaredConstructors()[0]; 131 assertEquals(Arrays.deepToString(c.getParameters()), 1, c.getParameters().length); 132 assertEquals(c.getParameters()[0].toString(), "x", c.getParameters()[0].getName()); 133 RecordInteger b = (RecordInteger) c.newInstance(9); 134 assertEquals(a.x, b.x); 135 assertEquals(a.x(), b.x()); 136 assertEquals(a, b); 137 } 138 139 @Test testReadField()140 public void testReadField() throws ReflectiveOperationException { 141 RecordInteger a = new RecordInteger(9); 142 assertEquals(9, a.x); 143 assertEquals(9, a.x()); 144 145 Field[] fields = RecordInteger.class.getDeclaredFields(); 146 assertEquals(Arrays.deepToString(fields), 1, fields.length); 147 Field field = fields[0]; 148 field.setAccessible(true); 149 assertEquals(field.toString(), "x", field.getName()); 150 assertEquals(9, field.get(a)); 151 } 152 153 @Test testWriteField()154 public void testWriteField() throws ReflectiveOperationException { 155 NonRecordInteger a = new NonRecordInteger(8); 156 Field fieldA = NonRecordInteger.class.getDeclaredField("x"); 157 fieldA.setAccessible(true); 158 fieldA.set(a, 7); 159 assertEquals(7, a.x); 160 161 RecordInteger b = new RecordInteger(8); 162 163 Field fieldB = RecordInteger.class.getDeclaredField("x"); 164 assertThrows(IllegalAccessException.class, () -> fieldB.setInt(b, 7)); 165 assertThrows(IllegalAccessException.class, () -> fieldB.set(b, 7)); 166 fieldB.setAccessible(true); 167 assertThrows(IllegalAccessException.class, () -> fieldB.setInt(b, 7)); 168 assertThrows(IllegalAccessException.class, () -> fieldB.set(b, 7)); 169 assertEquals(8, b.x); 170 171 Field fieldC = RecordString.class.getDeclaredField("s"); 172 RecordString c = new RecordString("a"); 173 assertThrows(IllegalAccessException.class, () -> fieldC.set(c, "b")); 174 fieldC.setAccessible(true); 175 assertThrows(IllegalAccessException.class, () -> fieldC.set(c, "b")); 176 assertEquals("a", c.s); 177 } 178 179 @Test testWriteStaticField()180 public void testWriteStaticField() throws ReflectiveOperationException { 181 Field field = RecordString.class.getField("A"); 182 assertThrows(IllegalAccessException.class, () -> field.set(null, "B")); 183 field.setAccessible(true); 184 assertThrows(IllegalAccessException.class, () -> field.set(null, "B")); 185 assertEquals("A", field.get(null)); 186 187 Field fieldB = RecordString.class.getDeclaredField("Y"); 188 assertThrows(IllegalAccessException.class, () -> fieldB.setInt(null, 0)); 189 fieldB.setAccessible(true); 190 assertThrows(IllegalAccessException.class, () -> fieldB.setInt(null, 0)); 191 assertEquals(1, fieldB.getInt(null)); 192 } 193 194 @Test testVarHandleWrite()195 public void testVarHandleWrite() throws ReflectiveOperationException { 196 NonRecordInteger a = new NonRecordInteger(8); 197 198 MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(NonRecordInteger.class, 199 MethodHandles.lookup()); 200 VarHandle varHandle = lookup.findVarHandle(NonRecordInteger.class, "x", int.class); 201 assertEquals(8, varHandle.get(a)); 202 assertThrows(UnsupportedOperationException.class, () -> varHandle.set(a, 6)); 203 assertEquals(8, a.x); 204 205 RecordInteger b = new RecordInteger(8); 206 207 lookup = MethodHandles.privateLookupIn(RecordInteger.class, MethodHandles.lookup()); 208 VarHandle varHandleB = lookup.findVarHandle(RecordInteger.class, "x", int.class); 209 assertThrows(UnsupportedOperationException.class, () -> varHandleB.set(b, 7)); 210 assertEquals(8, b.x); 211 } 212 213 @Test testSerializedNonSerializableRecordFailure()214 public void testSerializedNonSerializableRecordFailure() 215 throws IOException, ClassNotFoundException { 216 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 217 RecordInteger recordInteger = new RecordInteger(9); 218 try (ObjectOutputStream oos = new ObjectOutputStream(baos)) { 219 oos.writeObject(recordInteger); 220 fail("Expect NotSerializableException"); 221 } catch (NotSerializableException e) { 222 // expected 223 } 224 } 225 226 @Test testSerializedSimpleRecords()227 public void testSerializedSimpleRecords() throws IOException, ClassNotFoundException { 228 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 229 SerializableRecord recordInteger = new SerializableRecord(9, "abc"); 230 try (ObjectOutputStream oos = new ObjectOutputStream(baos)) { 231 oos.writeObject(recordInteger); 232 } 233 byte[] bytes = baos.toByteArray(); 234 try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes))) { 235 Object obj = ois.readObject(); 236 assertEquals(SerializableRecord.class, obj.getClass()); 237 SerializableRecord r = (SerializableRecord) obj; 238 assertEquals(9, r.x()); 239 assertEquals("abc", r.s()); 240 } 241 } 242 243 @Test testLocalRecord()244 public void testLocalRecord() { 245 record Point(int x, int y) { 246 @Override 247 public int y() { 248 return Math.abs(y); 249 } 250 251 public long sum() { 252 return (long) x + y; 253 } 254 } 255 var r = new Point(3, 4); 256 assertEquals(3, r.x); 257 assertEquals(4, r.y()); 258 assertEquals(7, r.sum()); 259 r = new Point(-6, -7); 260 assertEquals(-6, r.x); 261 assertEquals(-6, r.x()); 262 assertEquals(-7, r.y); 263 assertEquals(7, r.y()); 264 assertEquals(-13, r.sum()); 265 } 266 267 record SupplierRecord(int x) implements Supplier<String> { 268 269 private static int A = 9; 270 setStatic(int a)271 static void setStatic(int a) { 272 A = a; 273 } 274 getStatic()275 static int getStatic() { 276 return A; 277 } 278 279 @Override get()280 public String get() { 281 return String.valueOf(x); 282 } 283 } 284 285 @Test testOverriddenInterfaceMethod()286 public void testOverriddenInterfaceMethod() { 287 var r = new SupplierRecord(5); 288 assertEquals(5, r.x); 289 assertEquals("5", r.get()); 290 } 291 292 @Test testStaticMethods()293 public void testStaticMethods() { 294 SupplierRecord.setStatic(5); 295 assertEquals(5, SupplierRecord.getStatic()); 296 SupplierRecord.setStatic(3); 297 assertEquals(3, SupplierRecord.getStatic()); 298 } 299 } 300