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