1 /*
  2  * Copyright (c) 2025, 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 jdk.internal.value;
 27 
 28 import java.lang.invoke.MethodHandle;
 29 import java.lang.invoke.MethodHandles;
 30 import java.lang.invoke.MethodType;
 31 import java.lang.reflect.Field;
 32 import java.lang.reflect.Modifier;
 33 import java.util.ArrayList;
 34 import java.util.List;
 35 
 36 import jdk.internal.access.JavaLangInvokeAccess;
 37 import jdk.internal.access.SharedSecrets;
 38 import jdk.internal.misc.Unsafe;
 39 import sun.invoke.util.Wrapper;
 40 
 41 /**
 42  * Iterates the layout elements of a value type.
 43  * <p>
 44  * In the long run, we should do this:
 45  * Iterate the layout, create a mask masking bytes used by Object/abstract value
 46  * class reference fields.  Do a byte-wise compare and get the mask of value
 47  * mismatch; if the mask's all clear, fine; if the mask has bits beyond our
 48  * mask, fail; otherwise, compare reference fields indicated by the mismatch
 49  * mask. There may be paddings to ignore, too, depends...
 50  */
 51 public final class LayoutIteration {
 52     // Initializer in static initializers below, order dependent
 53     public static final ClassValue<List<MethodHandle>> ELEMENTS;
 54 
 55     /**
 56      * {@return a list of method handles accessing the basic elements}
 57      * Basic elements are 8 primitives and pointers (to identity or value objects).
 58      * Primitives and pointers are distinguished by the MH return types.
 59      * The MH types are {@code flatType -> fieldType}.
 60      *
 61      * @param flatType the class that has a flat layout
 62      * @return the accessors
 63      * @throws IllegalArgumentException if argument has no flat layout
 64      */
 65     public static List<MethodHandle> computeElementGetters(Class<?> flatType) {
 66         if (!ValueClass.isConcreteValueClass(flatType))
 67             throw new IllegalArgumentException(flatType + " cannot be flat");
 68         var sink = new Sink(flatType);
 69         iterateFields(U.valueHeaderSize(flatType), flatType, sink);
 70         return List.copyOf(sink.getters);
 71     }
 72 
 73     private static final class Sink {
 74         final Class<?> receiverType;
 75         final List<MethodHandle> getters = new ArrayList<>();
 76 
 77         Sink(Class<?> receiverType) {
 78             this.receiverType = receiverType;
 79         }
 80 
 81         void accept(long offsetNoHeader, Class<?> itemType) {
 82             Wrapper w = itemType.isPrimitive() ? Wrapper.forPrimitiveType(itemType) : Wrapper.OBJECT;
 83             var mh = MethodHandles.insertArguments(FIELD_GETTERS.get(w.ordinal()), 1, offsetNoHeader);
 84             assert mh.type() == MethodType.methodType(w.primitiveType(), Object.class);
 85             mh = JLIA.assertAsType(mh, MethodType.methodType(itemType, receiverType));
 86             getters.add(mh);
 87         }
 88     }
 89 
 90     // Sink is good for one to many mappings
 91     private static void iterateFields(long enclosingOffset, Class<?> currentClass, Sink sink) {
 92         assert ValueClass.isConcreteValueClass(currentClass) : currentClass + " cannot be flat";
 93         long memberOffsetDelta = enclosingOffset - U.valueHeaderSize(currentClass);
 94         for (Field f : currentClass.getDeclaredFields()) {
 95             if (Modifier.isStatic(f.getModifiers()))
 96                 continue;
 97             var type = f.getType();
 98             long memberOffset = U.objectFieldOffset(f) + memberOffsetDelta;
 99             if (!U.isFlatField(f)) {
100                 sink.accept(memberOffset, type);
101             } else {
102                 iterateFields(memberOffset, type, sink);
103             }
104         }
105     }
106 
107     private static boolean getBoolean(Object o, long offset) {
108         return U.getBoolean(o, offset);
109     }
110     private static byte getByte(Object o, long offset) {
111         return U.getByte(o, offset);
112     }
113     private static short getShort(Object o, long offset) {
114         return U.getShort(o, offset);
115     }
116     private static char getCharacter(Object o, long offset) {
117         return U.getChar(o, offset);
118     }
119     private static int getInteger(Object o, long offset) {
120         return U.getInt(o, offset);
121     }
122     private static long getLong(Object o, long offset) {
123         return U.getLong(o, offset);
124     }
125     private static float getFloat(Object o, long offset) {
126         return U.getFloat(o, offset);
127     }
128     private static double getDouble(Object o, long offset) {
129         return U.getDouble(o, offset);
130     }
131     public static Object getObject(Object o, long offset) {
132         return U.getReference(o, offset);
133     }
134 
135     private static final Unsafe U = Unsafe.getUnsafe();
136     private static final JavaLangInvokeAccess JLIA = SharedSecrets.getJavaLangInvokeAccess();
137     private static final List<MethodHandle> FIELD_GETTERS;
138     static {
139         MethodHandle[] fieldGetters = new MethodHandle[9];
140         var lookup = MethodHandles.lookup();
141         var type = MethodType.methodType(void.class, Object.class, long.class);
142         try {
143             for (Wrapper w : Wrapper.values()) {
144                 if (w != Wrapper.VOID) {
145                     fieldGetters[w.ordinal()] = lookup.findStatic(LayoutIteration.class,
146                             "get" + w.wrapperSimpleName(), type.changeReturnType(w.primitiveType()));
147                 }
148             }
149         } catch (ReflectiveOperationException ex) {
150             throw new ExceptionInInitializerError(ex);
151         }
152         FIELD_GETTERS = List.of(fieldGetters);
153         ELEMENTS = new ClassValue<>() {
154             @Override
155             protected List<MethodHandle> computeValue(Class<?> type) {
156                 return computeElementGetters(type);
157             }
158         };
159     }
160 
161     private LayoutIteration() {}
162 }