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 }