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 }