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     /**
 41      * A "salt" value used for this internal hashcode implementation that
 42      * needs to vary sufficiently from one run to the next so that
 43      * the default hashcode for value classes will vary between JVM runs.
 44      */
 45     static final int SALT;
 46     static {
 47         long nt = System.nanoTime();
 48         int value = (int)((nt >>> 32) ^ nt);
 49         SALT = Integer.getInteger("value.bsm.salt", value);
 50     }
 51 
 52     private ValueObjectMethods() {
 53     }
 54 
 55     /**
 56      * Return whether two value objects of the same class are statewise equivalent,
 57      * as used by {@code ==} operator.
 58      * The fields to compare are determined by Unsafe.getFieldMap.
 59      * This method is called by the JVM.
 60      *
 61      * @param a a value class instance, non-null
 62      * @param b a value class instance of the same class as {@code a}, non-null
 63      * @return whether these two value objects are statewise equivalent
 64      */
 65     private static boolean isSubstitutable(Object a, Object b) {
 66         if (VERBOSE) {
 67             System.out.println("isSubstitutable " + a + " vs " + b);
 68         }
 69         // This method assumes a and b are not null and they are both instances of the same value class
 70         final Unsafe U = UNSAFE;
 71         int[] map = U.getFieldMap(a.getClass());
 72         int nbNonRef = map[0];
 73         for (int i = 0; i < nbNonRef; i++) {
 74             int offset = map[i * 2 + 1];
 75             int size = map[i * 2 + 2];
 76             int nlong = size / 8;
 77             for (int j = 0; j < nlong; j++) {
 78                 long la = U.getLong(a, offset);
 79                 long lb = U.getLong(b, offset);
 80                 if (la != lb) return false;
 81                 offset += 8;
 82             }
 83             size -= nlong * 8;
 84             int nint = size / 4;
 85             for (int j = 0; j < nint; j++) {
 86                 int ia = U.getInt(a, offset);
 87                 int ib = U.getInt(b, offset);
 88                 if (ia != ib) return false;
 89                 offset += 4;
 90             }
 91             size -= nint * 4;
 92             int nshort = size / 2;
 93             for (int j = 0; j < nshort; j++) {
 94                 short sa = U.getShort(a, offset);
 95                 short sb = U.getShort(b, offset);
 96                 if (sa != sb) return false;
 97                 offset += 2;
 98             }
 99             size -= nshort * 2;
100             for (int j = 0; j < size; j++) {
101                 byte ba = U.getByte(a, offset);
102                 byte bb = U.getByte(b, offset);
103                 if (ba != bb) return false;
104                 offset++;
105             }
106         }
107         for (int i = nbNonRef * 2 + 1; i < map.length; i++) {
108             int offset = map[i];
109             Object oa = U.getReference(a, offset);
110             Object ob = U.getReference(b, offset);
111             if (oa != ob) return false;
112         }
113         return true;
114     }
115 
116     /**
117      * Return the identity hashCode of a value object.
118      * Two statewise equivalent value objects produce the same hashCode.
119      * The hashCode is computed using Unsafe.getFieldMap.
120      * This method is called by the JVM.
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 = SALT;
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 }