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 private ValueObjectMethods() {
41 }
42
43 /**
44 * Return whether two value objects of the same class are statewise equivalent,
45 * as used by {@code ==} operator.
46 * The fields to compare are determined by Unsafe.getFieldMap.
47 * This method is called by the JVM.
48 *
49 * @param a a value class instance, non-null
50 * @param b a value class instance of the same class as {@code a}, non-null
51 * @return whether these two value objects are statewise equivalent
52 */
53 private static boolean isSubstitutable(Object a, Object b) {
54 if (VERBOSE) {
55 System.out.println("isSubstitutable " + a + " vs " + b);
56 }
57 // This method assumes a and b are not null and they are both instances of the same value class
58 final Unsafe U = UNSAFE;
59 int[] map = U.getFieldMap(a.getClass());
60 // map format is:
61 // [number_of_nonref_entries][offset0][size0][offset1][size1]...[ref_offset0][ref_offset1]...
62 int nbNonRef = map[0];
63 for (int i = 0; i < nbNonRef; i++) {
64 int offset = map[i * 2 + 1];
65 int size = map[i * 2 + 2];
66 int nlong = size / 8;
67 for (int j = 0; j < nlong; j++) {
68 long la = U.getLong(a, offset);
69 long lb = U.getLong(b, offset);
70 if (la != lb) return false;
71 offset += 8;
72 }
73 size -= nlong * 8;
74 int nint = size / 4;
75 for (int j = 0; j < nint; j++) {
76 int ia = U.getInt(a, offset);
77 int ib = U.getInt(b, offset);
78 if (ia != ib) return false;
79 offset += 4;
80 }
81 size -= nint * 4;
82 int nshort = size / 2;
83 for (int j = 0; j < nshort; j++) {
84 short sa = U.getShort(a, offset);
85 short sb = U.getShort(b, offset);
86 if (sa != sb) return false;
87 offset += 2;
88 }
89 size -= nshort * 2;
90 for (int j = 0; j < size; j++) {
91 byte ba = U.getByte(a, offset);
92 byte bb = U.getByte(b, offset);
93 if (ba != bb) return false;
94 offset++;
95 }
96 }
97 for (int i = nbNonRef * 2 + 1; i < map.length; i++) {
98 int offset = map[i];
99 Object oa = U.getReference(a, offset);
100 Object ob = U.getReference(b, offset);
101 if (oa != ob) return false;
102 }
103 return true;
104 }
105
106 /**
107 * Return the identity hashCode of a value object.
108 * Two statewise equivalent value objects produce the same hashCode.
109 * This method is called by the JVM.
110 *
111 * The generated identity hash must be invariantly immutable.
112 * We divide into two cases:
113 * 1. No references: the identity hash is computed from the immutable
114 * fields, no matter when this is called the same identity hash code
115 * is expected.
116 * 2. References: the above still applies, but the references' identity
117 * hash code must be used, the user overwriteable hashCode may change
118 * due to mutability.
119 *
120 * The hashCode is computed using Unsafe.getFieldMap.
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 = System.identityHashCode(type);
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 }