Skip to content

Commit cdc8b5e

Browse files
committed
8366455: Move VarHandles.GuardMethodGenerator to execute on build
Reviewed-by: psandoz, redestad, erikj
1 parent 1ebe949 commit cdc8b5e

File tree

5 files changed

+340
-1857
lines changed

5 files changed

+340
-1857
lines changed

make/ToolsJdk.gmk

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,9 @@ TOOL_PUBLICSUFFIXLIST = $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_clas
130130
TOOL_FIXUPPANDOC = $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes \
131131
build.tools.fixuppandoc.Main
132132

133+
TOOL_VARHANDLEGUARDMETHODGENERATOR = $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes \
134+
build.tools.methodhandle.VarHandleGuardMethodGenerator
135+
133136
################################################################################
134137

135138
# Executable javascript filter for man page generation using pandoc.
Lines changed: 325 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,325 @@
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 build.tools.methodhandle;
27+
28+
import java.io.PrintWriter;
29+
import java.lang.classfile.TypeKind;
30+
import java.lang.invoke.MethodType;
31+
import java.lang.invoke.VarHandle;
32+
import java.lang.reflect.Method;
33+
import java.nio.charset.StandardCharsets;
34+
import java.nio.file.Files;
35+
import java.nio.file.Path;
36+
import java.nio.file.StandardOpenOption;
37+
import java.util.ArrayList;
38+
import java.util.Collections;
39+
import java.util.LinkedHashMap;
40+
import java.util.List;
41+
import java.util.stream.Collectors;
42+
import java.util.stream.Stream;
43+
44+
/**
45+
* A helper program to generate the VarHandleGuards class with a set of
46+
* static guard methods each of which corresponds to a particular shape and
47+
* performs a type check of the symbolic type descriptor with the VarHandle
48+
* type descriptor before linking/invoking to the underlying operation as
49+
* characterized by the operation member name on the VarForm of the
50+
* VarHandle.
51+
* <p>
52+
* The generated class essentially encapsulates pre-compiled LambdaForms,
53+
* one for each method, for the most common set of method signatures.
54+
* This reduces static initialization costs, footprint costs, and circular
55+
* dependencies that may arise if a class is generated per LambdaForm.
56+
* <p>
57+
* A maximum of L*T*S methods will be generated where L is the number of
58+
* access modes kinds (or unique operation signatures) and T is the number
59+
* of variable types and S is the number of shapes (such as instance field,
60+
* static field, or array access).
61+
* If there are 4 unique operation signatures, 5 basic types (Object, int,
62+
* long, float, double), and 3 shapes then a maximum of 60 methods will be
63+
* generated. However, the number is likely to be less since there may
64+
* be duplicate signatures.
65+
* <p>
66+
* Each method is annotated with @LambdaForm.Compiled to inform the runtime
67+
* that such methods should be treated as if a method of a class that is the
68+
* result of compiling a LambdaForm. Annotation of such methods is
69+
* important for correct evaluation of certain assertions and method return
70+
* type profiling in HotSpot.
71+
*
72+
* @see java.lang.invoke.GenerateJLIClassesHelper
73+
*/
74+
public final class VarHandleGuardMethodGenerator {
75+
76+
static final String CLASS_HEADER = """
77+
/*
78+
* Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved.
79+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
80+
*
81+
* This code is free software; you can redistribute it and/or modify it
82+
* under the terms of the GNU General Public License version 2 only, as
83+
* published by the Free Software Foundation. Oracle designates this
84+
* particular file as subject to the "Classpath" exception as provided
85+
* by Oracle in the LICENSE file that accompanied this code.
86+
*
87+
* This code is distributed in the hope that it will be useful, but WITHOUT
88+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
89+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
90+
* version 2 for more details (a copy is included in the LICENSE file that
91+
* accompanied this code).
92+
*
93+
* You should have received a copy of the GNU General Public License version
94+
* 2 along with this work; if not, write to the Free Software Foundation,
95+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
96+
*
97+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
98+
* or visit www.oracle.com if you need additional information or have any
99+
* questions.
100+
*/
101+
package java.lang.invoke;
102+
103+
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
104+
import jdk.internal.vm.annotation.ForceInline;
105+
import jdk.internal.vm.annotation.Hidden;
106+
107+
// This file is generated by build.tools.methodhandle.VarHandleGuardMethodGenerator.
108+
// Do not edit!
109+
@AOTSafeClassInitializer
110+
final class VarHandleGuards {
111+
""";
112+
113+
static final String GUARD_METHOD_SIG_TEMPLATE = "<RETURN> <NAME>_<SIGNATURE>(<PARAMS>)";
114+
115+
static final String GUARD_METHOD_TEMPLATE =
116+
"""
117+
@ForceInline
118+
@LambdaForm.Compiled
119+
@Hidden
120+
static final <METHOD> throws Throwable {
121+
boolean direct = handle.checkAccessModeThenIsDirect(ad);
122+
if (direct && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) {
123+
<RESULT_ERASED>MethodHandle.linkToStatic(<LINK_TO_STATIC_ARGS>);<RETURN_ERASED>
124+
} else {
125+
MethodHandle mh = handle.getMethodHandle(ad.mode);
126+
<RETURN>mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(<LINK_TO_INVOKER_ARGS>);
127+
}
128+
}""";
129+
130+
static final String GUARD_METHOD_TEMPLATE_V =
131+
"""
132+
@ForceInline
133+
@LambdaForm.Compiled
134+
@Hidden
135+
static final <METHOD> throws Throwable {
136+
boolean direct = handle.checkAccessModeThenIsDirect(ad);
137+
if (direct && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) {
138+
MethodHandle.linkToStatic(<LINK_TO_STATIC_ARGS>);
139+
} else if (direct && handle.vform.getMethodType_V(ad.type) == ad.symbolicMethodTypeErased) {
140+
MethodHandle.linkToStatic(<LINK_TO_STATIC_ARGS>);
141+
} else {
142+
MethodHandle mh = handle.getMethodHandle(ad.mode);
143+
mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(<LINK_TO_INVOKER_ARGS>);
144+
}
145+
}""";
146+
147+
// A template for deriving the operations
148+
// could be supported by annotating VarHandle directly with the
149+
// operation kind and shape
150+
interface VarHandleTemplate {
151+
Object get();
152+
153+
void set(Object value);
154+
155+
boolean compareAndSet(Object actualValue, Object expectedValue);
156+
157+
Object compareAndExchange(Object actualValue, Object expectedValue);
158+
159+
Object getAndUpdate(Object value);
160+
}
161+
162+
record HandleType(Class<?> receiver, Class<?>... intermediates) {
163+
}
164+
165+
public static void main(String... args) throws Throwable {
166+
if (args.length != 1) {
167+
System.err.println("Usage: java VarHandleGuardMethodGenerator VarHandleGuards.java");
168+
System.exit(1);
169+
}
170+
171+
Path outputFile = Path.of(args[0]);
172+
173+
try (PrintWriter pw = new PrintWriter(Files.newBufferedWriter(
174+
outputFile,
175+
StandardCharsets.UTF_8,
176+
StandardOpenOption.CREATE,
177+
StandardOpenOption.TRUNCATE_EXISTING))) {
178+
print(pw);
179+
}
180+
}
181+
182+
public static void print(PrintWriter pw) {
183+
pw.println(CLASS_HEADER);
184+
185+
// Declare the stream of shapes
186+
List<HandleType> hts = List.of(
187+
// Object->T
188+
new HandleType(Object.class),
189+
190+
// <static>->T
191+
new HandleType(null),
192+
193+
// Array[index]->T
194+
new HandleType(Object.class, int.class),
195+
196+
// MS[base]->T
197+
new HandleType(Object.class, long.class),
198+
199+
// MS[base][offset]->T
200+
new HandleType(Object.class, long.class, long.class)
201+
);
202+
203+
// The 5 JVM calling convention types
204+
List<Class<?>> basicTypes = List.of(Object.class, int.class, long.class, float.class, double.class);
205+
206+
Stream.of(VarHandleTemplate.class.getMethods()).<MethodType>
207+
mapMulti((m, sink) -> {
208+
for (var ht : hts) {
209+
for (var bt : basicTypes) {
210+
sink.accept(generateMethodType(m, ht.receiver, bt, ht.intermediates));
211+
}
212+
}
213+
}).
214+
distinct().
215+
map(VarHandleGuardMethodGenerator::generateMethod).
216+
forEach(pw::println);
217+
218+
pw.println("}");
219+
}
220+
221+
static MethodType generateMethodType(Method m, Class<?> receiver, Class<?> value, Class<?>... intermediates) {
222+
Class<?> returnType = m.getReturnType() == Object.class
223+
? value : m.getReturnType();
224+
225+
List<Class<?>> params = new ArrayList<>();
226+
if (receiver != null)
227+
params.add(receiver);
228+
Collections.addAll(params, intermediates);
229+
for (var p : m.getParameters()) {
230+
params.add(value);
231+
}
232+
return MethodType.methodType(returnType, params);
233+
}
234+
235+
static String generateMethod(MethodType mt) {
236+
Class<?> returnType = mt.returnType();
237+
238+
var params = new LinkedHashMap<String, String>();
239+
params.put("handle", className(VarHandle.class));
240+
for (int i = 0; i < mt.parameterCount(); i++) {
241+
params.put("arg" + i, className(mt.parameterType(i)));
242+
}
243+
params.put("ad", "VarHandle.AccessDescriptor");
244+
245+
// Generate method signature line
246+
String RETURN = className(returnType);
247+
String NAME = "guard";
248+
String SIGNATURE = getSignature(mt);
249+
String PARAMS = params.entrySet().stream().
250+
map(e -> e.getValue() + " " + e.getKey()).
251+
collect(Collectors.joining(", "));
252+
String METHOD = GUARD_METHOD_SIG_TEMPLATE.
253+
replace("<RETURN>", RETURN).
254+
replace("<NAME>", NAME).
255+
replace("<SIGNATURE>", SIGNATURE).
256+
replace("<PARAMS>", PARAMS);
257+
258+
// Generate method
259+
params.remove("ad");
260+
261+
List<String> LINK_TO_STATIC_ARGS = new ArrayList<>(params.keySet());
262+
LINK_TO_STATIC_ARGS.add("handle.vform.getMemberName(ad.mode)");
263+
264+
List<String> LINK_TO_INVOKER_ARGS = new ArrayList<>(params.keySet());
265+
LINK_TO_INVOKER_ARGS.set(0, LINK_TO_INVOKER_ARGS.get(0) + ".asDirect()");
266+
267+
RETURN = returnType == void.class
268+
? ""
269+
: returnType == Object.class
270+
? "return "
271+
: "return (" + returnType.getName() + ") ";
272+
273+
String RESULT_ERASED = returnType == void.class
274+
? ""
275+
: returnType != Object.class
276+
? "return (" + returnType.getName() + ") "
277+
: "Object r = ";
278+
279+
String RETURN_ERASED = returnType != Object.class
280+
? ""
281+
: "\n return ad.returnType.cast(r);";
282+
283+
String template = returnType == void.class
284+
? GUARD_METHOD_TEMPLATE_V
285+
: GUARD_METHOD_TEMPLATE;
286+
return template.
287+
replace("<METHOD>", METHOD).
288+
replace("<NAME>", NAME).
289+
replaceAll("<RETURN>", RETURN).
290+
replace("<RESULT_ERASED>", RESULT_ERASED).
291+
replace("<RETURN_ERASED>", RETURN_ERASED).
292+
replaceAll("<LINK_TO_STATIC_ARGS>", String.join(", ", LINK_TO_STATIC_ARGS)).
293+
replace("<LINK_TO_INVOKER_ARGS>", String.join(", ", LINK_TO_INVOKER_ARGS))
294+
.indent(4);
295+
}
296+
297+
static String className(Class<?> c) {
298+
String n = c.getCanonicalName();
299+
if (n == null)
300+
throw new IllegalArgumentException("Not representable in source code: " + c);
301+
if (!c.isPrimitive() && c.getPackageName().equals("java.lang")) {
302+
n = n.substring("java.lang.".length());
303+
} else if (c.getPackageName().equals("java.lang.invoke")) {
304+
n = n.substring("java.lang.invoke.".length());
305+
}
306+
return n;
307+
}
308+
309+
static String getSignature(MethodType m) {
310+
StringBuilder sb = new StringBuilder(m.parameterCount() + 1);
311+
312+
for (int i = 0; i < m.parameterCount(); i++) {
313+
Class<?> pt = m.parameterType(i);
314+
sb.append(getCharType(pt));
315+
}
316+
317+
sb.append('_').append(getCharType(m.returnType()));
318+
319+
return sb.toString();
320+
}
321+
322+
static char getCharType(Class<?> pt) {
323+
return TypeKind.from(pt).upperBound().descriptorString().charAt(0);
324+
}
325+
}

make/modules/java.base/gensrc/GensrcVarHandles.gmk

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,5 +302,17 @@ TARGETS += $(GENSRC_VARHANDLES)
302302

303303
################################################################################
304304

305+
GENSRC_VARHANDLEGUARDS := $(VARHANDLES_GENSRC_DIR)/VarHandleGuards.java
306+
307+
$(GENSRC_VARHANDLEGUARDS): $(BUILD_TOOLS_JDK)
308+
$(call LogInfo, Generating $@)
309+
$(call MakeTargetDir)
310+
$(TOOL_VARHANDLEGUARDMETHODGENERATOR) \
311+
$(GENSRC_VARHANDLEGUARDS)
312+
313+
TARGETS += $(GENSRC_VARHANDLEGUARDS)
314+
315+
################################################################################
316+
305317
endif # include guard
306318
include MakeIncludeEnd.gmk

0 commit comments

Comments
 (0)