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 }