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 }