1 /* 2 * Copyright (c) 2018, 2019, 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 test Object methods on inline types 28 * @compile -XDallowWithFieldOperator ObjectMethods.java 29 * @run testng/othervm -XX:+EnableValhalla -Dvalue.bsm.salt=1 ObjectMethods 30 * @run testng/othervm -XX:+EnableValhalla -Dvalue.bsm.salt=1 -XX:ValueFieldMaxFlatSize=0 ObjectMethods 31 */ 32 33 import java.lang.reflect.Modifier; 34 import java.util.Arrays; 35 import java.util.List; 36 import java.util.stream.Stream; 37 38 import org.testng.annotations.BeforeTest; 39 import org.testng.annotations.DataProvider; 40 import org.testng.annotations.Test; 41 import static org.testng.Assert.*; 42 43 public class ObjectMethods { 44 static final int SALT = 1; 45 static final Point P1 = Point.makePoint(1, 2); 46 static final Point P2 = Point.makePoint(30, 40); 47 static final Line LINE1 = Line.makeLine(1, 2, 3, 4); 48 static final Line LINE2 = Line.makeLine(10, 20, 3, 4); 49 static final MutablePath MUTABLE_PATH = MutablePath.makePath(10, 20, 30, 40); 50 static final MixedValues MIXED_VALUES = new MixedValues(P1, LINE1, MUTABLE_PATH, "value"); 51 static final Value VALUE = new Value.Builder() 52 .setChar('z') 53 .setBoolean(false) 54 .setByte((byte)0x1) 55 .setShort((short)3) 56 .setLong(4L) 57 .setPoint(Point.makePoint(200, 200)) 58 .setNumber(Value.Number.intValue(10)).build(); 59 60 @DataProvider(name="equalsTests") 61 Object[][] equalsTests() { 62 return new Object[][]{ 63 { P1, P1, true}, 64 { P1, Point.makePoint(1, 2), true}, 65 { P1, P2, false}, 66 { P1, LINE1, false}, 67 { LINE1, Line.makeLine(1, 2, 3, 4), true}, 68 { LINE1, LINE2, false}, 69 { LINE1, LINE1, true}, 70 { VALUE, new Value.Builder() 71 .setChar('z') 72 .setBoolean(false) 73 .setByte((byte)0x1) 74 .setShort((short)3) 75 .setLong(4L) 76 .setPoint(Point.makePoint(200, 200)) 77 .setNumber(Value.Number.intValue(10)).build(), true}, 78 { new Value.Builder().setNumber(new Value.IntNumber(10)).build(), 79 new Value.Builder().setNumber(new Value.IntNumber(10)).build(), false}, 80 // reference classes containing fields of inline type 81 { MUTABLE_PATH, MutablePath.makePath(10, 20, 30, 40), false}, 82 { MIXED_VALUES, MIXED_VALUES, true}, 83 { MIXED_VALUES, new MixedValues(P1, LINE1, MUTABLE_PATH, "value"), false}, 84 // uninitialized default value 85 { MyValue1.default, MyValue1.default, true}, 86 { MyValue1.default, MyValue1.make(0,0, null), true}, 87 { MyValue1.make(10, 20, P1), MyValue1.make(10, 20, Point.makePoint(1,2)), true}, 88 }; 89 } 90 91 @Test(dataProvider="equalsTests") 92 public void testEquals(Object o1, Object o2, boolean expected) { 93 assertTrue(o1.equals(o2) == expected); 94 } 95 96 @DataProvider(name="toStringTests") 97 Object[][] toStringTests() { 98 return new Object[][] { 99 { Point.makePoint(100, 200), "[Point x=100 y=200]" }, 100 { Line.makeLine(1, 2, 3, 4), "[Line p1=[Point x=1 y=2] p2=[Point x=3 y=4]]"}, 101 { new Value.Builder() 102 .setChar('z') 103 .setBoolean(false) 104 .setByte((byte)0x1) 105 .setShort((short)3) 106 .setLong(4L) 107 .setPoint(Point.makePoint(200, 200)) 108 .setNumber(Value.Number.intValue(10)).build(), 109 "[Value char_v=z byte_v=1 boolean_v=false int_v=0 short_v=3 long_v=4 double_v=0.0 " + 110 "float_v=0.0 number_v=[Value$IntValue i=10] point_v=[Point x=200 y=200] ref_v=null]" }, 111 { new Value.Builder() 112 .setReference(List.of("ref")) 113 .setNumber(new Value.IntNumber(99)).build(), 114 "[Value char_v=\u0000 byte_v=0 boolean_v=false int_v=0 short_v=0 long_v=0 double_v=0.0 " + 115 "float_v=0.0 number_v=99 point_v=[Point x=0 y=0] ref_v=[ref]]" }, 116 // enclosing instance field `this$0` should be filtered 117 { MyValue1.default, "[ObjectMethods$MyValue1 p=[Point x=0 y=0] box=null]" }, 118 { MyValue1.make(0,0, null), "[ObjectMethods$MyValue1 p=[Point x=0 y=0] box=null]" }, 119 { MyValue1.make(0,0, P1), "[ObjectMethods$MyValue1 p=[Point x=0 y=0] box=[Point x=1 y=2]]" }, 120 }; 121 } 122 123 @Test(dataProvider="toStringTests") 124 public void testToString(Object o, String s) { 125 assertTrue(o.toString().equals(s), o.toString()); 126 } 127 128 @DataProvider(name="hashcodeTests") 129 Object[][] hashcodeTests() { 130 Value v = new Value.Builder() 131 .setChar('z') 132 .setBoolean(false) 133 .setByte((byte)0x1) 134 .setShort((short)3) 135 .setLong(4L) 136 .setFloat(1.2f) 137 .setDouble(0.5) 138 .setPoint(Point.makePoint(200, 200)) 139 .setNumber(Value.Number.intValue(10)) 140 .setReference(new Object()).build(); 141 // this is sensitive to the order of the returned fields from Class::getDeclaredFields 142 return new Object[][]{ 143 { P1, hash(Point.class, 1, 2) }, 144 { LINE1, hash(Line.class, Point.makePoint(1, 2), Point.makePoint(3, 4)) }, 145 { v, hash(hashCodeComponents(v))}, 146 { Point.makePoint(0,0), hash(Point.class, 0, 0) }, 147 { Point.default, hash(Point.class, 0, 0) }, 148 { MyValue1.default, hash(MyValue1.class, Point.default, null) }, 149 { MyValue1.make(0,0, null), hash(MyValue1.class, Point.makePoint(0,0), null) }, 150 }; 151 } 152 153 @Test(dataProvider="hashcodeTests") 154 public void testHashCode(Object o, int hash) { 155 assertEquals(o.hashCode(), hash); 156 } 157 158 private static Object[] hashCodeComponents(Object o) { 159 Class<?> type = o.getClass(); 160 // filter static fields and synthetic fields 161 Stream<Object> fields = Arrays.stream(type.getDeclaredFields()) 162 .filter(f -> !Modifier.isStatic(f.getModifiers()) && !f.isSynthetic()) 163 .map(f -> { 164 try { 165 return f.get(o); 166 } catch (IllegalAccessException e) { 167 throw new RuntimeException(e); 168 } 169 }); 170 return Stream.concat(Stream.of(type), fields).toArray(Object[]::new); 171 } 172 173 private static int hash(Object... values) { 174 int hc = SALT; 175 for (Object o : values) { 176 hc = 31 * hc + (o != null ? o.hashCode() : 0); 177 } 178 return hc; 179 } 180 181 static inline class MyValue1 { 182 Point p = Point.default; 183 Point? box = null; 184 185 static MyValue1 make(int x, int y, Point? box) { 186 MyValue1 v = MyValue1.default; 187 v = __WithField(v.p, Point.makePoint(x, y)); 188 v = __WithField(v.box, box); 189 return v; 190 } 191 } 192 }