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 }