8366455: Move VarHandles.GuardMethodGenerator to execute on build

Reviewed-by: psandoz, redestad, erikj
This commit is contained in:
Chen Liang 2025-09-05 21:08:29 +00:00
parent 1ebe949507
commit cdc8b5eb83
5 changed files with 340 additions and 1857 deletions

View File

@ -130,6 +130,9 @@ TOOL_PUBLICSUFFIXLIST = $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_clas
TOOL_FIXUPPANDOC = $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes \
build.tools.fixuppandoc.Main
TOOL_VARHANDLEGUARDMETHODGENERATOR = $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes \
build.tools.methodhandle.VarHandleGuardMethodGenerator
################################################################################
# Executable javascript filter for man page generation using pandoc.

View File

@ -0,0 +1,325 @@
/*
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package build.tools.methodhandle;
import java.io.PrintWriter;
import java.lang.classfile.TypeKind;
import java.lang.invoke.MethodType;
import java.lang.invoke.VarHandle;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* A helper program to generate the VarHandleGuards class with a set of
* static guard methods each of which corresponds to a particular shape and
* performs a type check of the symbolic type descriptor with the VarHandle
* type descriptor before linking/invoking to the underlying operation as
* characterized by the operation member name on the VarForm of the
* VarHandle.
* <p>
* The generated class essentially encapsulates pre-compiled LambdaForms,
* one for each method, for the most common set of method signatures.
* This reduces static initialization costs, footprint costs, and circular
* dependencies that may arise if a class is generated per LambdaForm.
* <p>
* A maximum of L*T*S methods will be generated where L is the number of
* access modes kinds (or unique operation signatures) and T is the number
* of variable types and S is the number of shapes (such as instance field,
* static field, or array access).
* If there are 4 unique operation signatures, 5 basic types (Object, int,
* long, float, double), and 3 shapes then a maximum of 60 methods will be
* generated. However, the number is likely to be less since there may
* be duplicate signatures.
* <p>
* Each method is annotated with @LambdaForm.Compiled to inform the runtime
* that such methods should be treated as if a method of a class that is the
* result of compiling a LambdaForm. Annotation of such methods is
* important for correct evaluation of certain assertions and method return
* type profiling in HotSpot.
*
* @see java.lang.invoke.GenerateJLIClassesHelper
*/
public final class VarHandleGuardMethodGenerator {
static final String CLASS_HEADER = """
/*
* Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang.invoke;
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
import jdk.internal.vm.annotation.ForceInline;
import jdk.internal.vm.annotation.Hidden;
// This file is generated by build.tools.methodhandle.VarHandleGuardMethodGenerator.
// Do not edit!
@AOTSafeClassInitializer
final class VarHandleGuards {
""";
static final String GUARD_METHOD_SIG_TEMPLATE = "<RETURN> <NAME>_<SIGNATURE>(<PARAMS>)";
static final String GUARD_METHOD_TEMPLATE =
"""
@ForceInline
@LambdaForm.Compiled
@Hidden
static final <METHOD> throws Throwable {
boolean direct = handle.checkAccessModeThenIsDirect(ad);
if (direct && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) {
<RESULT_ERASED>MethodHandle.linkToStatic(<LINK_TO_STATIC_ARGS>);<RETURN_ERASED>
} else {
MethodHandle mh = handle.getMethodHandle(ad.mode);
<RETURN>mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(<LINK_TO_INVOKER_ARGS>);
}
}""";
static final String GUARD_METHOD_TEMPLATE_V =
"""
@ForceInline
@LambdaForm.Compiled
@Hidden
static final <METHOD> throws Throwable {
boolean direct = handle.checkAccessModeThenIsDirect(ad);
if (direct && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) {
MethodHandle.linkToStatic(<LINK_TO_STATIC_ARGS>);
} else if (direct && handle.vform.getMethodType_V(ad.type) == ad.symbolicMethodTypeErased) {
MethodHandle.linkToStatic(<LINK_TO_STATIC_ARGS>);
} else {
MethodHandle mh = handle.getMethodHandle(ad.mode);
mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(<LINK_TO_INVOKER_ARGS>);
}
}""";
// A template for deriving the operations
// could be supported by annotating VarHandle directly with the
// operation kind and shape
interface VarHandleTemplate {
Object get();
void set(Object value);
boolean compareAndSet(Object actualValue, Object expectedValue);
Object compareAndExchange(Object actualValue, Object expectedValue);
Object getAndUpdate(Object value);
}
record HandleType(Class<?> receiver, Class<?>... intermediates) {
}
public static void main(String... args) throws Throwable {
if (args.length != 1) {
System.err.println("Usage: java VarHandleGuardMethodGenerator VarHandleGuards.java");
System.exit(1);
}
Path outputFile = Path.of(args[0]);
try (PrintWriter pw = new PrintWriter(Files.newBufferedWriter(
outputFile,
StandardCharsets.UTF_8,
StandardOpenOption.CREATE,
StandardOpenOption.TRUNCATE_EXISTING))) {
print(pw);
}
}
public static void print(PrintWriter pw) {
pw.println(CLASS_HEADER);
// Declare the stream of shapes
List<HandleType> hts = List.of(
// Object->T
new HandleType(Object.class),
// <static>->T
new HandleType(null),
// Array[index]->T
new HandleType(Object.class, int.class),
// MS[base]->T
new HandleType(Object.class, long.class),
// MS[base][offset]->T
new HandleType(Object.class, long.class, long.class)
);
// The 5 JVM calling convention types
List<Class<?>> basicTypes = List.of(Object.class, int.class, long.class, float.class, double.class);
Stream.of(VarHandleTemplate.class.getMethods()).<MethodType>
mapMulti((m, sink) -> {
for (var ht : hts) {
for (var bt : basicTypes) {
sink.accept(generateMethodType(m, ht.receiver, bt, ht.intermediates));
}
}
}).
distinct().
map(VarHandleGuardMethodGenerator::generateMethod).
forEach(pw::println);
pw.println("}");
}
static MethodType generateMethodType(Method m, Class<?> receiver, Class<?> value, Class<?>... intermediates) {
Class<?> returnType = m.getReturnType() == Object.class
? value : m.getReturnType();
List<Class<?>> params = new ArrayList<>();
if (receiver != null)
params.add(receiver);
Collections.addAll(params, intermediates);
for (var p : m.getParameters()) {
params.add(value);
}
return MethodType.methodType(returnType, params);
}
static String generateMethod(MethodType mt) {
Class<?> returnType = mt.returnType();
var params = new LinkedHashMap<String, String>();
params.put("handle", className(VarHandle.class));
for (int i = 0; i < mt.parameterCount(); i++) {
params.put("arg" + i, className(mt.parameterType(i)));
}
params.put("ad", "VarHandle.AccessDescriptor");
// Generate method signature line
String RETURN = className(returnType);
String NAME = "guard";
String SIGNATURE = getSignature(mt);
String PARAMS = params.entrySet().stream().
map(e -> e.getValue() + " " + e.getKey()).
collect(Collectors.joining(", "));
String METHOD = GUARD_METHOD_SIG_TEMPLATE.
replace("<RETURN>", RETURN).
replace("<NAME>", NAME).
replace("<SIGNATURE>", SIGNATURE).
replace("<PARAMS>", PARAMS);
// Generate method
params.remove("ad");
List<String> LINK_TO_STATIC_ARGS = new ArrayList<>(params.keySet());
LINK_TO_STATIC_ARGS.add("handle.vform.getMemberName(ad.mode)");
List<String> LINK_TO_INVOKER_ARGS = new ArrayList<>(params.keySet());
LINK_TO_INVOKER_ARGS.set(0, LINK_TO_INVOKER_ARGS.get(0) + ".asDirect()");
RETURN = returnType == void.class
? ""
: returnType == Object.class
? "return "
: "return (" + returnType.getName() + ") ";
String RESULT_ERASED = returnType == void.class
? ""
: returnType != Object.class
? "return (" + returnType.getName() + ") "
: "Object r = ";
String RETURN_ERASED = returnType != Object.class
? ""
: "\n return ad.returnType.cast(r);";
String template = returnType == void.class
? GUARD_METHOD_TEMPLATE_V
: GUARD_METHOD_TEMPLATE;
return template.
replace("<METHOD>", METHOD).
replace("<NAME>", NAME).
replaceAll("<RETURN>", RETURN).
replace("<RESULT_ERASED>", RESULT_ERASED).
replace("<RETURN_ERASED>", RETURN_ERASED).
replaceAll("<LINK_TO_STATIC_ARGS>", String.join(", ", LINK_TO_STATIC_ARGS)).
replace("<LINK_TO_INVOKER_ARGS>", String.join(", ", LINK_TO_INVOKER_ARGS))
.indent(4);
}
static String className(Class<?> c) {
String n = c.getCanonicalName();
if (n == null)
throw new IllegalArgumentException("Not representable in source code: " + c);
if (!c.isPrimitive() && c.getPackageName().equals("java.lang")) {
n = n.substring("java.lang.".length());
} else if (c.getPackageName().equals("java.lang.invoke")) {
n = n.substring("java.lang.invoke.".length());
}
return n;
}
static String getSignature(MethodType m) {
StringBuilder sb = new StringBuilder(m.parameterCount() + 1);
for (int i = 0; i < m.parameterCount(); i++) {
Class<?> pt = m.parameterType(i);
sb.append(getCharType(pt));
}
sb.append('_').append(getCharType(m.returnType()));
return sb.toString();
}
static char getCharType(Class<?> pt) {
return TypeKind.from(pt).upperBound().descriptorString().charAt(0);
}
}

View File

@ -302,5 +302,17 @@ TARGETS += $(GENSRC_VARHANDLES)
################################################################################
GENSRC_VARHANDLEGUARDS := $(VARHANDLES_GENSRC_DIR)/VarHandleGuards.java
$(GENSRC_VARHANDLEGUARDS): $(BUILD_TOOLS_JDK)
$(call LogInfo, Generating $@)
$(call MakeTargetDir)
$(TOOL_VARHANDLEGUARDMETHODGENERATOR) \
$(GENSRC_VARHANDLEGUARDS)
TARGETS += $(GENSRC_VARHANDLEGUARDS)
################################################################################
endif # include guard
include MakeIncludeEnd.gmk

View File

@ -657,242 +657,4 @@ final class VarHandles {
!RuntimeException.class.isAssignableFrom(clazz) &&
!Error.class.isAssignableFrom(clazz);
}
// /**
// * A helper program to generate the VarHandleGuards class with a set of
// * static guard methods each of which corresponds to a particular shape and
// * performs a type check of the symbolic type descriptor with the VarHandle
// * type descriptor before linking/invoking to the underlying operation as
// * characterized by the operation member name on the VarForm of the
// * VarHandle.
// * <p>
// * The generated class essentially encapsulates pre-compiled LambdaForms,
// * one for each method, for the most set of common method signatures.
// * This reduces static initialization costs, footprint costs, and circular
// * dependencies that may arise if a class is generated per LambdaForm.
// * <p>
// * A maximum of L*T*S methods will be generated where L is the number of
// * access modes kinds (or unique operation signatures) and T is the number
// * of variable types and S is the number of shapes (such as instance field,
// * static field, or array access).
// * If there are 4 unique operation signatures, 5 basic types (Object, int,
// * long, float, double), and 3 shapes then a maximum of 60 methods will be
// * generated. However, the number is likely to be less since there
// * be duplicate signatures.
// * <p>
// * Each method is annotated with @LambdaForm.Compiled to inform the runtime
// * that such methods should be treated as if a method of a class that is the
// * result of compiling a LambdaForm. Annotation of such methods is
// * important for correct evaluation of certain assertions and method return
// * type profiling in HotSpot.
// */
// public static class GuardMethodGenerator {
//
// static final String GUARD_METHOD_SIG_TEMPLATE = "<RETURN> <NAME>_<SIGNATURE>(<PARAMS>)";
//
// static final String GUARD_METHOD_TEMPLATE =
// """
// @ForceInline
// @LambdaForm.Compiled
// @Hidden
// static final <METHOD> throws Throwable {
// boolean direct = handle.checkAccessModeThenIsDirect(ad);
// if (direct && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) {
// <RESULT_ERASED>MethodHandle.linkToStatic(<LINK_TO_STATIC_ARGS>);<RETURN_ERASED>
// } else {
// MethodHandle mh = handle.getMethodHandle(ad.mode);
// <RETURN>mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(<LINK_TO_INVOKER_ARGS>);
// }
// }""";
//
// static final String GUARD_METHOD_TEMPLATE_V =
// """
// @ForceInline
// @LambdaForm.Compiled
// @Hidden
// static final <METHOD> throws Throwable {
// boolean direct = handle.checkAccessModeThenIsDirect(ad);
// if (direct && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) {
// MethodHandle.linkToStatic(<LINK_TO_STATIC_ARGS>);
// } else if (direct && handle.vform.getMethodType_V(ad.type) == ad.symbolicMethodTypeErased) {
// MethodHandle.linkToStatic(<LINK_TO_STATIC_ARGS>);
// } else {
// MethodHandle mh = handle.getMethodHandle(ad.mode);
// mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(<LINK_TO_INVOKER_ARGS>);
// }
// }""";
//
// // A template for deriving the operations
// // could be supported by annotating VarHandle directly with the
// // operation kind and shape
// interface VarHandleTemplate {
// Object get();
//
// void set(Object value);
//
// boolean compareAndSet(Object actualValue, Object expectedValue);
//
// Object compareAndExchange(Object actualValue, Object expectedValue);
//
// Object getAndUpdate(Object value);
// }
//
// record HandleType(Class<?> receiver, Class<?>... intermediates) {
// }
//
// /**
// * @param args parameters
// */
// public static void main(String[] args) {
// System.out.println("package java.lang.invoke;");
// System.out.println();
// System.out.println("import jdk.internal.vm.annotation.AOTSafeClassInitializer;");
// System.out.println("import jdk.internal.vm.annotation.ForceInline;");
// System.out.println("import jdk.internal.vm.annotation.Hidden;");
// System.out.println();
// System.out.println("// This class is auto-generated by " +
// GuardMethodGenerator.class.getName() +
// ". Do not edit.");
// System.out.println("@AOTSafeClassInitializer");
// System.out.println("final class VarHandleGuards {");
//
// System.out.println();
//
// // Declare the stream of shapes
// List<HandleType> hts = List.of(
// // Object->T
// new HandleType(Object.class),
//
// // <static>->T
// new HandleType(null),
//
// // Array[index]->T
// new HandleType(Object.class, int.class),
//
// // MS[base]->T
// new HandleType(Object.class, long.class),
//
// // MS[base][offset]->T
// new HandleType(Object.class, long.class, long.class)
// );
//
// Stream.of(VarHandleTemplate.class.getMethods()).<MethodType>
// mapMulti((m, sink) -> {
// for (var ht : hts) {
// for (var bt : LambdaForm.BasicType.ARG_TYPES) {
// sink.accept(generateMethodType(m, ht.receiver, bt.btClass, ht.intermediates));
// }
// }
// }).
// distinct().
// map(GuardMethodGenerator::generateMethod).
// forEach(System.out::println);
//
// System.out.println("}");
// }
//
// static MethodType generateMethodType(Method m, Class<?> receiver, Class<?> value, Class<?>... intermediates) {
// Class<?> returnType = m.getReturnType() == Object.class
// ? value : m.getReturnType();
//
// List<Class<?>> params = new ArrayList<>();
// if (receiver != null)
// params.add(receiver);
// java.util.Collections.addAll(params, intermediates);
// for (var p : m.getParameters()) {
// params.add(value);
// }
// return MethodType.methodType(returnType, params);
// }
//
// static String generateMethod(MethodType mt) {
// Class<?> returnType = mt.returnType();
//
// var params = new java.util.LinkedHashMap<String, Class<?>>();
// params.put("handle", VarHandle.class);
// for (int i = 0; i < mt.parameterCount(); i++) {
// params.put("arg" + i, mt.parameterType(i));
// }
// params.put("ad", VarHandle.AccessDescriptor.class);
//
// // Generate method signature line
// String RETURN = className(returnType);
// String NAME = "guard";
// String SIGNATURE = getSignature(mt);
// String PARAMS = params.entrySet().stream().
// map(e -> className(e.getValue()) + " " + e.getKey()).
// collect(java.util.stream.Collectors.joining(", "));
// String METHOD = GUARD_METHOD_SIG_TEMPLATE.
// replace("<RETURN>", RETURN).
// replace("<NAME>", NAME).
// replace("<SIGNATURE>", SIGNATURE).
// replace("<PARAMS>", PARAMS);
//
// // Generate method
// params.remove("ad");
//
// List<String> LINK_TO_STATIC_ARGS = new ArrayList<>(params.keySet());
// LINK_TO_STATIC_ARGS.add("handle.vform.getMemberName(ad.mode)");
//
// List<String> LINK_TO_INVOKER_ARGS = new ArrayList<>(params.keySet());
// LINK_TO_INVOKER_ARGS.set(0, LINK_TO_INVOKER_ARGS.get(0) + ".asDirect()");
//
// RETURN = returnType == void.class
// ? ""
// : returnType == Object.class
// ? "return "
// : "return (" + returnType.getName() + ") ";
//
// String RESULT_ERASED = returnType == void.class
// ? ""
// : returnType != Object.class
// ? "return (" + returnType.getName() + ") "
// : "Object r = ";
//
// String RETURN_ERASED = returnType != Object.class
// ? ""
// : "\n return ad.returnType.cast(r);";
//
// String template = returnType == void.class
// ? GUARD_METHOD_TEMPLATE_V
// : GUARD_METHOD_TEMPLATE;
// return template.
// replace("<METHOD>", METHOD).
// replace("<NAME>", NAME).
// replaceAll("<RETURN>", RETURN).
// replace("<RESULT_ERASED>", RESULT_ERASED).
// replace("<RETURN_ERASED>", RETURN_ERASED).
// replaceAll("<LINK_TO_STATIC_ARGS>", String.join(", ", LINK_TO_STATIC_ARGS)).
// replace("<LINK_TO_INVOKER_ARGS>", String.join(", ", LINK_TO_INVOKER_ARGS))
// .indent(4);
// }
//
// static String className(Class<?> c) {
// String n = c.getName();
// if (n.startsWith("java.lang.")) {
// n = n.replace("java.lang.", "");
// if (n.startsWith("invoke.")) {
// n = n.replace("invoke.", "");
// }
// }
// return n.replace('$', '.');
// }
//
// static String getSignature(MethodType m) {
// StringBuilder sb = new StringBuilder(m.parameterCount() + 1);
//
// for (int i = 0; i < m.parameterCount(); i++) {
// Class<?> pt = m.parameterType(i);
// sb.append(getCharType(pt));
// }
//
// sb.append('_').append(getCharType(m.returnType()));
//
// return sb.toString();
// }
//
// static char getCharType(Class<?> pt) {
// return Wrapper.forBasicType(pt).basicTypeChar();
// }
// }
}