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 int nbNonRef = map[0];
61 for (int i = 0; i < nbNonRef; i++) {
62 int offset = map[i * 2 + 1];
63 int size = map[i * 2 + 2];
64 int nlong = size / 8;
65 for (int j = 0; j < nlong; j++) {
66 long la = U.getLong(a, offset);
67 long lb = U.getLong(b, offset);
68 if (la != lb) return false;
69 offset += 8;
70 }
71 size -= nlong * 8;
72 int nint = size / 4;
73 for (int j = 0; j < nint; j++) {
74 int ia = U.getInt(a, offset);
75 int ib = U.getInt(b, offset);
76 if (ia != ib) return false;
77 offset += 4;
78 }
79 size -= nint * 4;
80 int nshort = size / 2;
81 for (int j = 0; j < nshort; j++) {
82 short sa = U.getShort(a, offset);
83 short sb = U.getShort(b, offset);
84 if (sa != sb) return false;
85 offset += 2;
86 }
87 size -= nshort * 2;
88 for (int j = 0; j < size; j++) {
89 byte ba = U.getByte(a, offset);
90 byte bb = U.getByte(b, offset);
91 if (ba != bb) return false;
92 offset++;
93 }
94 }
95 for (int i = nbNonRef * 2 + 1; i < map.length; i++) {
96 int offset = map[i];
97 Object oa = U.getReference(a, offset);
98 Object ob = U.getReference(b, offset);
99 if (oa != ob) return false;
100 }
101 return true;
102 }
103
104 /**
105 * Return the identity hashCode of a value object.
106 * Two statewise equivalent value objects produce the same hashCode.
107 * This method is called by the JVM.
108 *
109 * The generated identity hash must be invariantly immutable.
110 * We divide into two cases:
111 * 1. No references: the identity hash is computed from the immutable
112 * fields, no matter when this is called the same identity hash code
113 * is expected.
114 * 2. References: the above still applies, but the references' identity
115 * hash code must be used, the user overwriteable hashCode may change
116 * due to mutability.
117 *
118 * The hashCode is computed using Unsafe.getFieldMap.
119 *
120 * @param obj a value class instance, non-null
121 * @return the hashCode of the object
122 */
123 private static int valueObjectHashCode(Object obj) {
124 if (VERBOSE) {
125 System.out.println("valueObjectHashCode: obj.getClass:" + obj.getClass().getName());
126 }
127 // This method assumes a is not null and is an instance of a value class
128 Class<?> type = obj.getClass();
129 final Unsafe U = UNSAFE;
130 int[] map = U.getFieldMap(type);
131 int result = System.identityHashCode(type);
132 int nbNonRef = map[0];
133 for (int i = 0; i < nbNonRef; i++) {
134 int offset = map[i * 2 + 1];
135 int size = map[i * 2 + 2];
136 int nlong = size / 8;
137 for (int j = 0; j < nlong; j++) {
138 long la = U.getLong(obj, offset);
139 result = 31 * result + (int) la;
140 result = 31 * result + (int) (la >>> 32);
141 offset += 8;
142 }
143 size -= nlong * 8;
144 int nint = size / 4;
145 for (int j = 0; j < nint; j++) {
146 int ia = U.getInt(obj, offset);
147 result = 31 * result + ia;
148 offset += 4;
149 }
150 size -= nint * 4;
151 int nshort = size / 2;
152 for (int j = 0; j < nshort; j++) {
153 short sa = U.getShort(obj, offset);
154 result = 31 * result + sa;
155 offset += 2;
156 }
157 size -= nshort * 2;
158 for (int j = 0; j < size; j++) {
159 byte ba = U.getByte(obj, offset);
160 result = 31 * result + ba;
161 offset++;
162 }
163 }
164 for (int i = nbNonRef * 2 + 1; i < map.length; i++) {
165 int offset = map[i];
166 Object oa = U.getReference(obj, offset);
167 result = 31 * result + System.identityHashCode(oa);
168 }
169 return result;
170 }
171 }