1 /*
2 * Copyright (c) 2018, 2026, 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package java.lang.runtime;
27 import jdk.internal.misc.Unsafe;
28
29 /**
30 * Implementation for Object::equals and Object::hashCode for value objects.
31 *
32 * ValueObjectMethods::isSubstitutable and valueObjectHashCode are
33 * private entry points called by VM.
34 */
35 final class ValueObjectMethods {
36 private static final Unsafe UNSAFE = Unsafe.getUnsafe();
37 private static final boolean VERBOSE =
38 System.getProperty("value.bsm.debug") != null;
39
40 /**
41 * A "salt" value used for this internal hashcode implementation that
42 * needs to vary sufficiently from one run to the next so that
43 * the default hashcode for value classes will vary between JVM runs.
44 */
45 static final int SALT;
46 static {
47 long nt = System.nanoTime();
48 int value = (int)((nt >>> 32) ^ nt);
49 SALT = Integer.getInteger("value.bsm.salt", value);
50 }
51
52 private ValueObjectMethods() {
53 }
54
55 /**
56 * Return whether two value objects of the same class are statewise equivalent,
57 * as used by {@code ==} operator.
58 * The fields to compare are determined by Unsafe.getFieldMap.
59 * This method is called by the JVM.
60 *
61 * @param a a value class instance, non-null
62 * @param b a value class instance of the same class as {@code a}, non-null
63 * @return whether these two value objects are statewise equivalent
64 */
65 private static boolean isSubstitutable(Object a, Object b) {
66 if (VERBOSE) {
67 System.out.println("isSubstitutable " + a + " vs " + b);
68 }
69 // This method assumes a and b are not null and they are both instances of the same value class
70 final Unsafe U = UNSAFE;
71 int[] map = U.getFieldMap(a.getClass());
72 int nbNonRef = map[0];
73 for (int i = 0; i < nbNonRef; i++) {
74 int offset = map[i * 2 + 1];
75 int size = map[i * 2 + 2];
76 int nlong = size / 8;
77 for (int j = 0; j < nlong; j++) {
78 long la = U.getLong(a, offset);
79 long lb = U.getLong(b, offset);
80 if (la != lb) return false;
81 offset += 8;
82 }
83 size -= nlong * 8;
84 int nint = size / 4;
85 for (int j = 0; j < nint; j++) {
86 int ia = U.getInt(a, offset);
87 int ib = U.getInt(b, offset);
88 if (ia != ib) return false;
89 offset += 4;
90 }
91 size -= nint * 4;
92 int nshort = size / 2;
93 for (int j = 0; j < nshort; j++) {
94 short sa = U.getShort(a, offset);
95 short sb = U.getShort(b, offset);
96 if (sa != sb) return false;
97 offset += 2;
98 }
99 size -= nshort * 2;
100 for (int j = 0; j < size; j++) {
101 byte ba = U.getByte(a, offset);
102 byte bb = U.getByte(b, offset);
103 if (ba != bb) return false;
104 offset++;
105 }
106 }
107 for (int i = nbNonRef * 2 + 1; i < map.length; i++) {
108 int offset = map[i];
109 Object oa = U.getReference(a, offset);
110 Object ob = U.getReference(b, offset);
111 if (oa != ob) return false;
112 }
113 return true;
114 }
115
116 /**
117 * Return the identity hashCode of a value object.
118 * Two statewise equivalent value objects produce the same hashCode.
119 * The hashCode is computed using Unsafe.getFieldMap.
120 * This method is called by the JVM.
121 *
122 * @param obj a value class instance, non-null
123 * @return the hashCode of the object
124 */
125 private static int valueObjectHashCode(Object obj) {
126 if (VERBOSE) {
127 System.out.println("valueObjectHashCode: obj.getClass:" + obj.getClass().getName());
128 }
129 // This method assumes a is not null and is an instance of a value class
130 Class<?> type = obj.getClass();
131 final Unsafe U = UNSAFE;
132 int[] map = U.getFieldMap(type);
133 int result = SALT;
134 int nbNonRef = map[0];
135 for (int i = 0; i < nbNonRef; i++) {
136 int offset = map[i * 2 + 1];
137 int size = map[i * 2 + 2];
138 int nlong = size / 8;
139 for (int j = 0; j < nlong; j++) {
140 long la = U.getLong(obj, offset);
141 result = 31 * result + (int) la;
142 result = 31 * result + (int) (la >>> 32);
143 offset += 8;
144 }
145 size -= nlong * 8;
146 int nint = size / 4;
147 for (int j = 0; j < nint; j++) {
148 int ia = U.getInt(obj, offset);
149 result = 31 * result + ia;
150 offset += 4;
151 }
152 size -= nint * 4;
153 int nshort = size / 2;
154 for (int j = 0; j < nshort; j++) {
155 short sa = U.getShort(obj, offset);
156 result = 31 * result + sa;
157 offset += 2;
158 }
159 size -= nshort * 2;
160 for (int j = 0; j < size; j++) {
161 byte ba = U.getByte(obj, offset);
162 result = 31 * result + ba;
163 offset++;
164 }
165 }
166 for (int i = nbNonRef * 2 + 1; i < map.length; i++) {
167 int offset = map[i];
168 Object oa = U.getReference(obj, offset);
169 result = 31 * result + System.identityHashCode(oa);
170 }
171 return result;
172 }
173 }