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
28 import jdk.internal.misc.Unsafe;
29
30 /**
31 * Implementation for Object::equals and Object::hashCode for value objects.
32 *
33 * ValueObjectMethods::isSubstitutable and valueObjectHashCode are
34 * private entry points called by VM.
35 */
36 final class ValueObjectMethods {
37 private static final Unsafe UNSAFE = Unsafe.getUnsafe();
38 private static final boolean VERBOSE =
39 System.getProperty("value.bsm.debug") != null;
40
41 private ValueObjectMethods() {
42 }
43
44 /**
45 * Return whether two value objects of the same class are statewise equivalent,
46 * as used by {@code ==} operator.
47 * The fields to compare are determined by Unsafe.getFieldMap.
48 * This method is called by the JVM.
49 *
50 * @param a a value class instance, non-null
51 * @param b a value class instance of the same class as {@code a}, non-null
52 * @return whether these two value objects are statewise equivalent
53 */
54 private static boolean isSubstitutable(Object a, Object b) {
55 if (VERBOSE) {
56 System.out.println("isSubstitutable " + a + " vs " + b);
57 }
58 // This method assumes a and b are not null and they are both instances of the same value class
59 final Unsafe U = UNSAFE;
60 int[] map = U.getFieldMap(a.getClass());
61 // map format is:
62 // [number_of_nonref_entries][offset0][size0][offset1][size1]...[ref_offset0][ref_offset1]...
63 int nbNonRef = map[0];
64 for (int i = 0; i < nbNonRef; i++) {
65 int offset = map[i * 2 + 1];
66 int size = map[i * 2 + 2];
67 int nlong = size / 8;
68 for (int j = 0; j < nlong; j++) {
69 long la = U.getLong(a, offset);
70 long lb = U.getLong(b, offset);
71 if (la != lb) return false;
72 offset += 8;
73 }
74 size -= nlong * 8;
75 int nint = size / 4;
76 for (int j = 0; j < nint; j++) {
77 int ia = U.getInt(a, offset);
78 int ib = U.getInt(b, offset);
79 if (ia != ib) return false;
80 offset += 4;
81 }
82 size -= nint * 4;
83 int nshort = size / 2;
84 for (int j = 0; j < nshort; j++) {
85 short sa = U.getShort(a, offset);
86 short sb = U.getShort(b, offset);
87 if (sa != sb) return false;
88 offset += 2;
89 }
90 size -= nshort * 2;
91 for (int j = 0; j < size; j++) {
92 byte ba = U.getByte(a, offset);
93 byte bb = U.getByte(b, offset);
94 if (ba != bb) return false;
95 offset++;
96 }
97 }
98 for (int i = nbNonRef * 2 + 1; i < map.length; i++) {
99 int offset = map[i];
100 Object oa = U.getReference(a, offset);
101 Object ob = U.getReference(b, offset);
102 if (oa != ob) return false;
103 }
104 return true;
105 }
106
107 /**
108 * Return the identity hashCode of a value object.
109 * Two statewise equivalent value objects produce the same hashCode.
110 * This method is called by the JVM.
111 *
112 * The generated identity hash must be invariantly immutable.
113 * We divide into two cases:
114 * 1. No references: the identity hash is computed from the immutable
115 * fields, no matter when this is called the same identity hash code
116 * is expected.
117 * 2. References: the above still applies, but the references' identity
118 * hash code must be used, the user overwriteable hashCode may change
119 * due to mutability.
120 *
121 * The hashCode is computed using Unsafe.getFieldMap.
122 *
123 * @param obj a value class instance, non-null
124 * @return the hashCode of the object
125 */
126 private static int valueObjectHashCode(Object obj) {
127 if (VERBOSE) {
128 System.out.println("valueObjectHashCode: obj.getClass:" + obj.getClass().getName());
129 }
130 // This method assumes a is not null and is an instance of a value class
131 Class<?> type = obj.getClass();
132 final Unsafe U = UNSAFE;
133 int[] map = U.getFieldMap(type);
134 int result = System.identityHashCode(type);
135 int nbNonRef = map[0];
136 for (int i = 0; i < nbNonRef; i++) {
137 int offset = map[i * 2 + 1];
138 int size = map[i * 2 + 2];
139 int nlong = size / 8;
140 for (int j = 0; j < nlong; j++) {
141 long la = U.getLong(obj, offset);
142 result = 31 * result + (int) la;
143 result = 31 * result + (int) (la >>> 32);
144 offset += 8;
145 }
146 size -= nlong * 8;
147 int nint = size / 4;
148 for (int j = 0; j < nint; j++) {
149 int ia = U.getInt(obj, offset);
150 result = 31 * result + ia;
151 offset += 4;
152 }
153 size -= nint * 4;
154 int nshort = size / 2;
155 for (int j = 0; j < nshort; j++) {
156 short sa = U.getShort(obj, offset);
157 result = 31 * result + sa;
158 offset += 2;
159 }
160 size -= nshort * 2;
161 for (int j = 0; j < size; j++) {
162 byte ba = U.getByte(obj, offset);
163 result = 31 * result + ba;
164 offset++;
165 }
166 }
167 for (int i = nbNonRef * 2 + 1; i < map.length; i++) {
168 int offset = map[i];
169 Object oa = U.getReference(obj, offset);
170 result = 31 * result + System.identityHashCode(oa);
171 }
172 return result;
173 }
174 }