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 (!canHaveFlatLayout(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     // Ensures the given class has a potential a flat layout
 74     private static boolean canHaveFlatLayout(Class<?> flatType) {
 75         return flatType.isValue() && Modifier.isFinal(flatType.getModifiers());
 76     }
 77 
 78     private static final class Sink {
 79         final Class<?> receiverType;
 80         final List<MethodHandle> getters = new ArrayList<>();
 81 
 82         Sink(Class<?> receiverType) {
 83             this.receiverType = receiverType;
 84         }
 85 
 86         void accept(long offsetNoHeader, Class<?> itemType) {
 87             Wrapper w = itemType.isPrimitive() ? Wrapper.forPrimitiveType(itemType) : Wrapper.OBJECT;
 88             var mh = MethodHandles.insertArguments(FIELD_GETTERS.get(w.ordinal()), 1, offsetNoHeader);
 89             assert mh.type() == MethodType.methodType(w.primitiveType(), Object.class);
 90             mh = JLIA.assertAsType(mh, MethodType.methodType(itemType, receiverType));
 91             getters.add(mh);
 92         }
 93     }
 94 
 95     // Sink is good for one to many mappings
 96     private static void iterateFields(long enclosingOffset, Class<?> currentClass, Sink sink) {
 97         assert canHaveFlatLayout(currentClass) : currentClass + " cannot be flat";
 98         long memberOffsetDelta = enclosingOffset - U.valueHeaderSize(currentClass);
 99         for (Field f : currentClass.getDeclaredFields()) {
100             if (Modifier.isStatic(f.getModifiers()))
101                 continue;
102             var type = f.getType();
103             long memberOffset = U.objectFieldOffset(f) + memberOffsetDelta;
104             if (!U.isFlatField(f)) {
105                 sink.accept(memberOffset, type);
106             } else {
107                 iterateFields(memberOffset, type, sink);
108             }
109         }
110     }
111 
112     private static boolean getBoolean(Object o, long offset) {
113         return U.getBoolean(o, offset);
114     }
115     private static byte getByte(Object o, long offset) {
116         return U.getByte(o, offset);
117     }
118     private static short getShort(Object o, long offset) {
119         return U.getShort(o, offset);
120     }
121     private static char getCharacter(Object o, long offset) {
122         return U.getChar(o, offset);
123     }
124     private static int getInteger(Object o, long offset) {
125         return U.getInt(o, offset);
126     }
127     private static long getLong(Object o, long offset) {
128         return U.getLong(o, offset);
129     }
130     private static float getFloat(Object o, long offset) {
131         return U.getFloat(o, offset);
132     }
133     private static double getDouble(Object o, long offset) {
134         return U.getDouble(o, offset);
135     }
136     public static Object getObject(Object o, long offset) {
137         return U.getReference(o, offset);
138     }
139 
140     private static final Unsafe U = Unsafe.getUnsafe();
141     private static final JavaLangInvokeAccess JLIA = SharedSecrets.getJavaLangInvokeAccess();
142     private static final List<MethodHandle> FIELD_GETTERS;
143     static {
144         MethodHandle[] fieldGetters = new MethodHandle[9];
145         var lookup = MethodHandles.lookup();
146         var type = MethodType.methodType(void.class, Object.class, long.class);
147         try {
148             for (Wrapper w : Wrapper.values()) {
149                 if (w != Wrapper.VOID) {
150                     fieldGetters[w.ordinal()] = lookup.findStatic(LayoutIteration.class,
151                             "get" + w.wrapperSimpleName(), type.changeReturnType(w.primitiveType()));
152                 }
153             }
154         } catch (ReflectiveOperationException ex) {
155             throw new ExceptionInInitializerError(ex);
156         }
157         FIELD_GETTERS = List.of(fieldGetters);
158         ELEMENTS = new ClassValue<>() {
159             @Override
160             protected List<MethodHandle> computeValue(Class<?> type) {
161                 return computeElementGetters(type);
162             }
163         };
164     }
165 
166     private LayoutIteration() {}
167 }