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 }