< prev index next >
src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java
Print this page
*** 27,44 ****
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
- import java.lang.invoke.MethodType;
import java.nio.file.Files;
import java.util.EnumSet;
import java.util.Map;
import java.util.Set;
- import java.util.TreeMap;
- import java.util.TreeSet;
- import java.util.stream.Stream;
import jdk.internal.access.JavaLangInvokeAccess;
import jdk.internal.access.SharedSecrets;
import jdk.tools.jlink.plugin.Plugin;
import jdk.tools.jlink.plugin.PluginException;
--- 27,41 ----
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.util.EnumSet;
+ import java.util.List;
import java.util.Map;
import java.util.Set;
import jdk.internal.access.JavaLangInvokeAccess;
import jdk.internal.access.SharedSecrets;
import jdk.tools.jlink.plugin.Plugin;
import jdk.tools.jlink.plugin.PluginException;
*** 70,106 ****
private static final String DESCRIPTION = PluginsResourceBundle.getDescription(NAME);
private static final String DEFAULT_TRACE_FILE = "default_jli_trace.txt";
! private static final String DIRECT_HOLDER = "java/lang/invoke/DirectMethodHandle$Holder";
! private static final String DMH_INVOKE_VIRTUAL = "invokeVirtual";
! private static final String DMH_INVOKE_STATIC = "invokeStatic";
! private static final String DMH_INVOKE_SPECIAL = "invokeSpecial";
! private static final String DMH_NEW_INVOKE_SPECIAL = "newInvokeSpecial";
! private static final String DMH_INVOKE_INTERFACE = "invokeInterface";
! private static final String DMH_INVOKE_STATIC_INIT = "invokeStaticInit";
! private static final String DMH_INVOKE_SPECIAL_IFC = "invokeSpecialIFC";
!
! private static final String DELEGATING_HOLDER = "java/lang/invoke/DelegatingMethodHandle$Holder";
! private static final String BASIC_FORMS_HOLDER = "java/lang/invoke/LambdaForm$Holder";
!
! private static final String INVOKERS_HOLDER_NAME = "java.lang.invoke.Invokers$Holder";
! private static final String INVOKERS_HOLDER_INTERNAL_NAME = INVOKERS_HOLDER_NAME.replace('.', '/');
!
! private static final JavaLangInvokeAccess JLIA
! = SharedSecrets.getJavaLangInvokeAccess();
!
! private final TreeSet<String> speciesTypes = new TreeSet<>();
!
! private final TreeSet<String> invokerTypes = new TreeSet<>();
!
! private final TreeSet<String> callSiteTypes = new TreeSet<>();
!
! private final Map<String, Set<String>> dmhMethods = new TreeMap<>();
String mainArgument;
public GenerateJLIClassesPlugin() {
}
@Override
--- 67,80 ----
private static final String DESCRIPTION = PluginsResourceBundle.getDescription(NAME);
private static final String DEFAULT_TRACE_FILE = "default_jli_trace.txt";
! private static final JavaLangInvokeAccess JLIA = SharedSecrets.getJavaLangInvokeAccess();
String mainArgument;
+ String[] lines = null;
public GenerateJLIClassesPlugin() {
}
@Override
*** 126,264 ****
@Override
public String getArgumentsDescription() {
return PluginsResourceBundle.getArgument(NAME);
}
- private static int DMH_INVOKE_VIRTUAL_TYPE = 0;
- private static int DMH_INVOKE_INTERFACE_TYPE = 4;
-
- // Map from DirectMethodHandle method type to internal ID, matching values
- // of the corresponding constants in java.lang.invoke.MethodTypeForm
- private static final Map<String, Integer> DMH_METHOD_TYPE_MAP =
- Map.of(
- DMH_INVOKE_VIRTUAL, DMH_INVOKE_VIRTUAL_TYPE,
- DMH_INVOKE_STATIC, 1,
- DMH_INVOKE_SPECIAL, 2,
- DMH_NEW_INVOKE_SPECIAL, 3,
- DMH_INVOKE_INTERFACE, DMH_INVOKE_INTERFACE_TYPE,
- DMH_INVOKE_STATIC_INIT, 5,
- DMH_INVOKE_SPECIAL_IFC, 20
- );
-
@Override
public void configure(Map<String, String> config) {
mainArgument = config.get(NAME);
}
! private void addSpeciesType(String type) {
! speciesTypes.add(expandSignature(type));
! }
!
! private void addInvokerType(String methodType) {
! validateMethodType(methodType);
! invokerTypes.add(methodType);
! }
!
! private void addCallSiteType(String csType) {
! validateMethodType(csType);
! callSiteTypes.add(csType);
! }
!
! public void initialize(ResourcePool in) {
! // Load configuration from the contents in the supplied input file
! // - if none was supplied we look for the default file
! if (mainArgument == null || !mainArgument.startsWith("@")) {
! try (InputStream traceFile =
! this.getClass().getResourceAsStream(DEFAULT_TRACE_FILE)) {
! if (traceFile != null) {
! readTraceConfig(
! new BufferedReader(
! new InputStreamReader(traceFile)).lines());
! }
! } catch (Exception e) {
! throw new PluginException("Couldn't read " + DEFAULT_TRACE_FILE, e);
! }
! } else {
! File file = new File(mainArgument.substring(1));
! if (file.exists()) {
! readTraceConfig(fileLines(file));
! }
! }
! }
!
! private void readTraceConfig(Stream<String> lines) {
! lines.map(line -> line.split(" "))
! .forEach(parts -> {
! switch (parts[0]) {
! case "[SPECIES_RESOLVE]":
! // Allow for new types of species data classes being resolved here
! if (parts.length == 3 && parts[1].startsWith("java.lang.invoke.BoundMethodHandle$Species_")) {
! String species = parts[1].substring("java.lang.invoke.BoundMethodHandle$Species_".length());
! if (!"L".equals(species)) {
! addSpeciesType(species);
! }
! }
! break;
! case "[LF_RESOLVE]":
! String methodType = parts[3];
! if (parts[1].equals(INVOKERS_HOLDER_NAME)) {
! if ("linkToTargetMethod".equals(parts[2]) ||
! "linkToCallSite".equals(parts[2])) {
! addCallSiteType(methodType);
! } else {
! addInvokerType(methodType);
! }
! } else if (parts[1].contains("DirectMethodHandle")) {
! String dmh = parts[2];
! // ignore getObject etc for now (generated
! // by default)
! if (DMH_METHOD_TYPE_MAP.containsKey(dmh)) {
! addDMHMethodType(dmh, methodType);
! }
! }
! break;
! default: break; // ignore
! }
! });
! }
!
! private void addDMHMethodType(String dmh, String methodType) {
! validateMethodType(methodType);
! Set<String> methodTypes = dmhMethods.get(dmh);
! if (methodTypes == null) {
! methodTypes = new TreeSet<>();
! dmhMethods.put(dmh, methodTypes);
! }
! methodTypes.add(methodType);
! }
!
! private Stream<String> fileLines(File file) {
! try {
! return Files.lines(file.toPath());
! } catch (IOException io) {
! throw new PluginException("Couldn't read file");
! }
! }
!
! private void validateMethodType(String type) {
! String[] typeParts = type.split("_");
! // check return type (second part)
! if (typeParts.length != 2 || typeParts[1].length() != 1
! || "LJIFDV".indexOf(typeParts[1].charAt(0)) == -1) {
! throw new PluginException(
! "Method type signature must be of form [LJIFD]*_[LJIFDV]");
! }
! // expand and check arguments (first part)
! expandSignature(typeParts[0]);
! }
!
! private static void requireBasicType(char c) {
! if ("LIJFD".indexOf(c) < 0) {
! throw new PluginException(
! "Character " + c + " must correspond to a basic field type: LIJFD");
! }
! }
@Override
public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) {
initialize(in);
// Copy all but DMH_ENTRY to out
--- 100,124 ----
@Override
public String getArgumentsDescription() {
return PluginsResourceBundle.getArgument(NAME);
}
@Override
public void configure(Map<String, String> config) {
mainArgument = config.get(NAME);
}
! // Repeat def, we do not have access right to InvokerBytecodeGeneratorHelper
! // Must be exact same!
! private static final String DIRECT_METHOD_HOLDER_ENTRY =
! "/java.base/java/lang/invoke/DirectMethodHandle$Holder.class";
! private static final String DELEGATING_METHOD_HOLDER_ENTRY =
! "/java.base/java/lang/invoke/DelegatingMethodHandle$Holder.class";
! private static final String INVOKERS_HOLDER_ENTRY =
! "/java.base/java/lang/invoke/Invokers$Holder.class";
! private static final String BASIC_FORMS_HOLDER_ENTRY =
! "/java.base/java/lang/invoke/LambdaForm$Holder.class";
@Override
public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) {
initialize(in);
// Copy all but DMH_ENTRY to out
*** 273,487 ****
} else {
return entry;
}
}, out);
- // Generate BMH Species classes
- speciesTypes.forEach(types -> generateBMHClass(types, out));
-
// Generate LambdaForm Holder classes
generateHolderClasses(out);
!
! // Let it go
! speciesTypes.clear();
! invokerTypes.clear();
! callSiteTypes.clear();
! dmhMethods.clear();
!
return out.build();
}
- private void generateBMHClass(String types, ResourcePoolBuilder out) {
- try {
- // Generate class
- Map.Entry<String, byte[]> result =
- JLIA.generateConcreteBMHClassBytes(types);
- String className = result.getKey();
- byte[] bytes = result.getValue();
-
- // Add class to pool
- ResourcePoolEntry ndata = ResourcePoolEntry.create(
- "/java.base/" + className + ".class",
- bytes);
- out.add(ndata);
- } catch (Exception ex) {
- throw new PluginException(ex);
- }
- }
-
private void generateHolderClasses(ResourcePoolBuilder out) {
- int count = 0;
- for (Set<String> entry : dmhMethods.values()) {
- count += entry.size();
- }
- MethodType[] directMethodTypes = new MethodType[count];
- int[] dmhTypes = new int[count];
- int index = 0;
- for (Map.Entry<String, Set<String>> entry : dmhMethods.entrySet()) {
- String dmhType = entry.getKey();
- for (String type : entry.getValue()) {
- // The DMH type to actually ask for is retrieved by removing
- // the first argument, which needs to be of Object.class
- MethodType mt = asMethodType(type);
- if (mt.parameterCount() < 1 ||
- mt.parameterType(0) != Object.class) {
- throw new PluginException(
- "DMH type parameter must start with L: " + dmhType + " " + type);
- }
-
- // Adapt the method type of the LF to retrieve
- directMethodTypes[index] = mt.dropParameterTypes(0, 1);
-
- // invokeVirtual and invokeInterface must have a leading Object
- // parameter, i.e., the receiver
- dmhTypes[index] = DMH_METHOD_TYPE_MAP.get(dmhType);
- if (dmhTypes[index] == DMH_INVOKE_INTERFACE_TYPE ||
- dmhTypes[index] == DMH_INVOKE_VIRTUAL_TYPE) {
- if (mt.parameterCount() < 2 ||
- mt.parameterType(1) != Object.class) {
- throw new PluginException(
- "DMH type parameter must start with LL: " + dmhType + " " + type);
- }
- }
- index++;
- }
- }
-
- // The invoker type to ask for is retrieved by removing the first
- // and the last argument, which needs to be of Object.class
- MethodType[] invokerMethodTypes = new MethodType[this.invokerTypes.size()];
- int i = 0;
- for (String invokerType : invokerTypes) {
- MethodType mt = asMethodType(invokerType);
- final int lastParam = mt.parameterCount() - 1;
- if (mt.parameterCount() < 2 ||
- mt.parameterType(0) != Object.class ||
- mt.parameterType(lastParam) != Object.class) {
- throw new PluginException(
- "Invoker type parameter must start and end with Object: " + invokerType);
- }
- mt = mt.dropParameterTypes(lastParam, lastParam + 1);
- invokerMethodTypes[i] = mt.dropParameterTypes(0, 1);
- i++;
- }
-
- // The callSite type to ask for is retrieved by removing the last
- // argument, which needs to be of Object.class
- MethodType[] callSiteMethodTypes = new MethodType[this.callSiteTypes.size()];
- i = 0;
- for (String callSiteType : callSiteTypes) {
- MethodType mt = asMethodType(callSiteType);
- final int lastParam = mt.parameterCount() - 1;
- if (mt.parameterCount() < 1 ||
- mt.parameterType(lastParam) != Object.class) {
- throw new PluginException(
- "CallSite type parameter must end with Object: " + callSiteType);
- }
- callSiteMethodTypes[i] = mt.dropParameterTypes(lastParam, lastParam + 1);
- i++;
- }
try {
! byte[] bytes = JLIA.generateDirectMethodHandleHolderClassBytes(
! DIRECT_HOLDER, directMethodTypes, dmhTypes);
! ResourcePoolEntry ndata = ResourcePoolEntry
! .create(DIRECT_METHOD_HOLDER_ENTRY, bytes);
! out.add(ndata);
!
! bytes = JLIA.generateDelegatingMethodHandleHolderClassBytes(
! DELEGATING_HOLDER, directMethodTypes);
! ndata = ResourcePoolEntry.create(DELEGATING_METHOD_HOLDER_ENTRY, bytes);
! out.add(ndata);
!
! bytes = JLIA.generateInvokersHolderClassBytes(INVOKERS_HOLDER_INTERNAL_NAME,
! invokerMethodTypes, callSiteMethodTypes);
! ndata = ResourcePoolEntry.create(INVOKERS_HOLDER_ENTRY, bytes);
! out.add(ndata);
!
! bytes = JLIA.generateBasicFormsClassBytes(BASIC_FORMS_HOLDER);
! ndata = ResourcePoolEntry.create(BASIC_FORMS_HOLDER_ENTRY, bytes);
out.add(ndata);
} catch (Exception ex) {
throw new PluginException(ex);
}
}
- private static final String DIRECT_METHOD_HOLDER_ENTRY =
- "/java.base/" + DIRECT_HOLDER + ".class";
- private static final String DELEGATING_METHOD_HOLDER_ENTRY =
- "/java.base/" + DELEGATING_HOLDER + ".class";
- private static final String BASIC_FORMS_HOLDER_ENTRY =
- "/java.base/" + BASIC_FORMS_HOLDER + ".class";
- private static final String INVOKERS_HOLDER_ENTRY =
- "/java.base/" + INVOKERS_HOLDER_INTERNAL_NAME + ".class";
! // Convert LL -> LL, L3 -> LLL
! public static String expandSignature(String signature) {
! StringBuilder sb = new StringBuilder();
! char last = 'X';
! int count = 0;
! for (int i = 0; i < signature.length(); i++) {
! char c = signature.charAt(i);
! if (c >= '0' && c <= '9') {
! count *= 10;
! count += (c - '0');
! } else {
! requireBasicType(c);
! for (int j = 1; j < count; j++) {
! sb.append(last);
! }
! sb.append(c);
! last = c;
! count = 0;
! }
! }
!
! // ended with a number, e.g., "L2": append last char count - 1 times
! if (count > 1) {
! requireBasicType(last);
! for (int j = 1; j < count; j++) {
! sb.append(last);
! }
}
! return sb.toString();
}
-
- private static MethodType asMethodType(String basicSignatureString) {
- String[] parts = basicSignatureString.split("_");
- assert(parts.length == 2);
- assert(parts[1].length() == 1);
- String parameters = expandSignature(parts[0]);
- Class<?> rtype = simpleType(parts[1].charAt(0));
- if (parameters.isEmpty()) {
- return MethodType.methodType(rtype);
} else {
! Class<?>[] ptypes = new Class<?>[parameters.length()];
! for (int i = 0; i < ptypes.length; i++) {
! ptypes[i] = simpleType(parameters.charAt(i));
! }
! return MethodType.methodType(rtype, ptypes);
}
}
-
- private static Class<?> simpleType(char c) {
- switch (c) {
- case 'F':
- return float.class;
- case 'D':
- return double.class;
- case 'I':
- return int.class;
- case 'L':
- return Object.class;
- case 'J':
- return long.class;
- case 'V':
- return void.class;
- case 'Z':
- case 'B':
- case 'S':
- case 'C':
- throw new IllegalArgumentException("Not a valid primitive: " + c +
- " (use I instead)");
- default:
- throw new IllegalArgumentException("Not a primitive: " + c);
}
}
}
--- 133,183 ----
} else {
return entry;
}
}, out);
// Generate LambdaForm Holder classes
generateHolderClasses(out);
! // clear input.
! lines = null;
return out.build();
}
private void generateHolderClasses(ResourcePoolBuilder out) {
try {
! Map<String, byte[]> result = JLIA.generateMethodHandleHolderClasses(lines);
! if (result != null) {
! result.forEach ((k,v) -> {
! ResourcePoolEntry ndata = ResourcePoolEntry.create(k, v);
out.add(ndata);
+ });
+ }
} catch (Exception ex) {
throw new PluginException(ex);
}
}
! public void initialize(ResourcePool in) {
! // Load configuration from the contents in the supplied input file
! // - if none was supplied we look for the default file
! if (mainArgument == null || !mainArgument.startsWith("@")) {
! try (InputStream traceFile =
! this.getClass().getResourceAsStream(DEFAULT_TRACE_FILE)) {
! if (traceFile != null) {
! lines = new BufferedReader(
! new InputStreamReader(traceFile)).lines().toArray(String[]::new);
}
! } catch (Exception e) {
! throw new PluginException("Couldn't read " + DEFAULT_TRACE_FILE, e);
}
} else {
! File file = new File(mainArgument.substring(1));
! try {
! if (file.exists()) {
! lines = Files.readAllLines(file.toPath()).toArray(new String[0]);
}
+ } catch (Exception e) {
+ throw new PluginException("Couldn't read " + file.getName(), e);
}
}
}
}
< prev index next >