38 import jdk.tools.jlink.plugin.PluginException;
39 import jdk.tools.jlink.plugin.ResourcePool;
40 import jdk.tools.jlink.plugin.ResourcePoolBuilder;
41 import jdk.tools.jlink.plugin.Plugin;
42
43 /**
44 * Plugin to generate java.lang.invoke classes.
45 */
46 public final class GenerateJLIClassesPlugin implements Plugin {
47
48 private static final String NAME = "generate-jli-classes";
49
50 private static final String BMH_PARAM = "bmh";
51
52 private static final String BMH_SPECIES_PARAM = "bmh-species";
53
54 private static final String DMH_PARAM = "dmh";
55
56 private static final String DESCRIPTION = PluginsResourceBundle.getDescription(NAME);
57
58 private static final String DMH = "java/lang/invoke/DirectMethodHandle$Holder";
59 private static final String DMH_INVOKE_VIRTUAL = "invokeVirtual";
60 private static final String DMH_INVOKE_STATIC = "invokeStatic";
61 private static final String DMH_INVOKE_SPECIAL = "invokeSpecial";
62 private static final String DMH_NEW_INVOKE_SPECIAL = "newInvokeSpecial";
63 private static final String DMH_INVOKE_INTERFACE = "invokeInterface";
64 private static final String DMH_INVOKE_STATIC_INIT = "invokeStaticInit";
65
66 private static final JavaLangInvokeAccess JLIA
67 = SharedSecrets.getJavaLangInvokeAccess();
68
69 List<String> speciesTypes;
70
71 Map<String, List<String>> dmhMethods;
72
73 public GenerateJLIClassesPlugin() {
74 }
75
76 @Override
77 public String getName() {
78 return NAME;
79 }
80
81 @Override
82 public String getDescription() {
83 return DESCRIPTION;
84 }
85
205 expandSignature(typeParts[0]);
206 }
207 }
208 }
209 if (dmhMethods.isEmpty()) {
210 dmhMethods = defaultDMHMethods();
211 }
212 }
213 }
214
215 private static void requireBasicType(char c) {
216 if ("LIJFD".indexOf(c) < 0) {
217 throw new PluginException(
218 "Character " + c + " must correspond to a basic field type: LIJFD");
219 }
220 }
221
222 @Override
223 public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) {
224 // Copy all but DMH_ENTRY to out
225 in.transformAndCopy(entry -> entry.path().equals(DMH_ENTRY) ? null : entry, out);
226 speciesTypes.forEach(types -> generateBMHClass(types, out));
227 generateDMHClass(out);
228 return out.build();
229 }
230
231 @SuppressWarnings("unchecked")
232 private void generateBMHClass(String types, ResourcePoolBuilder out) {
233 try {
234 // Generate class
235 Map.Entry<String, byte[]> result =
236 JLIA.generateConcreteBMHClassBytes(types);
237 String className = result.getKey();
238 byte[] bytes = result.getValue();
239
240 // Add class to pool
241 ResourcePoolEntry ndata = ResourcePoolEntry.create(
242 "/java.base/" + className + ".class",
243 bytes);
244 out.add(ndata);
245 } catch (Exception ex) {
247 }
248 }
249
250 private void generateDMHClass(ResourcePoolBuilder out) {
251 int count = 0;
252 for (List<String> entry : dmhMethods.values()) {
253 count += entry.size();
254 }
255 MethodType[] methodTypes = new MethodType[count];
256 int[] dmhTypes = new int[count];
257 int index = 0;
258 for (Map.Entry<String, List<String>> entry : dmhMethods.entrySet()) {
259 String dmhType = entry.getKey();
260 for (String type : entry.getValue()) {
261 methodTypes[index] = asMethodType(type);
262 dmhTypes[index] = DMH_METHOD_TYPE_MAP.get(dmhType);
263 index++;
264 }
265 }
266 try {
267 byte[] bytes =
268 JLIA.generateDMHClassBytes(DMH, methodTypes, dmhTypes);
269 ResourcePoolEntry ndata = ResourcePoolEntry.create(DMH_ENTRY, bytes);
270 out.add(ndata);
271 } catch (Exception ex) {
272 throw new PluginException(ex);
273 }
274 }
275 private static final String DMH_ENTRY = "/java.base/" + DMH + ".class";
276
277 // Convert LL -> LL, L3 -> LLL
278 private static String expandSignature(String signature) {
279 StringBuilder sb = new StringBuilder();
280 char last = 'X';
281 int count = 0;
282 for (int i = 0; i < signature.length(); i++) {
283 char c = signature.charAt(i);
284 if (c >= '0' && c <= '9') {
285 count *= 10;
286 count += (c - '0');
287 } else {
288 requireBasicType(c);
289 for (int j = 1; j < count; j++) {
290 sb.append(last);
291 }
292 sb.append(c);
293 last = c;
294 count = 0;
295 }
296 }
297
298 // ended with a number, e.g., "L2": append last char count - 1 times
299 if (count > 1) {
300 requireBasicType(last);
301 for (int j = 1; j < count; j++) {
302 sb.append(last);
303 }
304 }
305 return sb.toString();
306 }
307
308 private static MethodType asMethodType(String basicSignatureString) {
309 String[] parts = basicSignatureString.split("_");
310 assert(parts.length == 2);
311 assert(parts[1].length() == 1);
312 String parameters = expandSignature(parts[0]);
313 Class<?> rtype = primitiveType(parts[1].charAt(0));
314 Class<?>[] ptypes = new Class<?>[parameters.length()];
315 for (int i = 0; i < ptypes.length; i++) {
316 ptypes[i] = primitiveType(parameters.charAt(i));
317 }
318 return MethodType.methodType(rtype, ptypes);
319 }
320
321 private static Class<?> primitiveType(char c) {
322 switch (c) {
323 case 'F':
324 return float.class;
325 case 'D':
326 return double.class;
327 case 'I':
328 return int.class;
329 case 'L':
330 return Object.class;
331 case 'J':
332 return long.class;
333 case 'V':
334 return void.class;
335 case 'Z':
336 case 'B':
337 case 'S':
338 case 'C':
339 throw new IllegalArgumentException("Not a valid primitive: " + c +
340 " (use I instead)");
341 default:
|
38 import jdk.tools.jlink.plugin.PluginException;
39 import jdk.tools.jlink.plugin.ResourcePool;
40 import jdk.tools.jlink.plugin.ResourcePoolBuilder;
41 import jdk.tools.jlink.plugin.Plugin;
42
43 /**
44 * Plugin to generate java.lang.invoke classes.
45 */
46 public final class GenerateJLIClassesPlugin implements Plugin {
47
48 private static final String NAME = "generate-jli-classes";
49
50 private static final String BMH_PARAM = "bmh";
51
52 private static final String BMH_SPECIES_PARAM = "bmh-species";
53
54 private static final String DMH_PARAM = "dmh";
55
56 private static final String DESCRIPTION = PluginsResourceBundle.getDescription(NAME);
57
58 private static final String DIRECT_METHOD_HANDLE = "java/lang/invoke/DirectMethodHandle$Holder";
59 private static final String DMH_INVOKE_VIRTUAL = "invokeVirtual";
60 private static final String DMH_INVOKE_STATIC = "invokeStatic";
61 private static final String DMH_INVOKE_SPECIAL = "invokeSpecial";
62 private static final String DMH_NEW_INVOKE_SPECIAL = "newInvokeSpecial";
63 private static final String DMH_INVOKE_INTERFACE = "invokeInterface";
64 private static final String DMH_INVOKE_STATIC_INIT = "invokeStaticInit";
65
66 private static final String DELEGATING_METHOD_HANDLE = "java/lang/invoke/DelegatingMethodHandle$Holder";
67
68 private static final JavaLangInvokeAccess JLIA
69 = SharedSecrets.getJavaLangInvokeAccess();
70
71 List<String> speciesTypes;
72
73 Map<String, List<String>> dmhMethods;
74
75 public GenerateJLIClassesPlugin() {
76 }
77
78 @Override
79 public String getName() {
80 return NAME;
81 }
82
83 @Override
84 public String getDescription() {
85 return DESCRIPTION;
86 }
87
207 expandSignature(typeParts[0]);
208 }
209 }
210 }
211 if (dmhMethods.isEmpty()) {
212 dmhMethods = defaultDMHMethods();
213 }
214 }
215 }
216
217 private static void requireBasicType(char c) {
218 if ("LIJFD".indexOf(c) < 0) {
219 throw new PluginException(
220 "Character " + c + " must correspond to a basic field type: LIJFD");
221 }
222 }
223
224 @Override
225 public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) {
226 // Copy all but DMH_ENTRY to out
227 in.transformAndCopy(entry -> {
228 // filter out placeholder entries
229 if (entry.path().equals(DIRECT_METHOD_HANDLE_ENTRY) ||
230 entry.path().equals(DELEGATING_METHOD_HANDLE_ENTRY)) {
231 return null;
232 } else {
233 return entry;
234 }
235 }, out);
236 speciesTypes.forEach(types -> generateBMHClass(types, out));
237 generateDMHClass(out);
238 return out.build();
239 }
240
241 @SuppressWarnings("unchecked")
242 private void generateBMHClass(String types, ResourcePoolBuilder out) {
243 try {
244 // Generate class
245 Map.Entry<String, byte[]> result =
246 JLIA.generateConcreteBMHClassBytes(types);
247 String className = result.getKey();
248 byte[] bytes = result.getValue();
249
250 // Add class to pool
251 ResourcePoolEntry ndata = ResourcePoolEntry.create(
252 "/java.base/" + className + ".class",
253 bytes);
254 out.add(ndata);
255 } catch (Exception ex) {
257 }
258 }
259
260 private void generateDMHClass(ResourcePoolBuilder out) {
261 int count = 0;
262 for (List<String> entry : dmhMethods.values()) {
263 count += entry.size();
264 }
265 MethodType[] methodTypes = new MethodType[count];
266 int[] dmhTypes = new int[count];
267 int index = 0;
268 for (Map.Entry<String, List<String>> entry : dmhMethods.entrySet()) {
269 String dmhType = entry.getKey();
270 for (String type : entry.getValue()) {
271 methodTypes[index] = asMethodType(type);
272 dmhTypes[index] = DMH_METHOD_TYPE_MAP.get(dmhType);
273 index++;
274 }
275 }
276 try {
277 byte[] bytes = JLIA.generateDirectMethodHandleHolderClassBytes(
278 DIRECT_METHOD_HANDLE, methodTypes, dmhTypes);
279 ResourcePoolEntry ndata = ResourcePoolEntry
280 .create(DIRECT_METHOD_HANDLE_ENTRY, bytes);
281 out.add(ndata);
282
283 bytes = JLIA.generateDelegatingMethodHandleHolderClassBytes(
284 DELEGATING_METHOD_HANDLE, methodTypes);
285 ndata = ResourcePoolEntry.create(DELEGATING_METHOD_HANDLE_ENTRY, bytes);
286 out.add(ndata);
287 } catch (Exception ex) {
288 throw new PluginException(ex);
289 }
290 }
291 private static final String DIRECT_METHOD_HANDLE_ENTRY =
292 "/java.base/" + DIRECT_METHOD_HANDLE + ".class";
293 private static final String DELEGATING_METHOD_HANDLE_ENTRY =
294 "/java.base/" + DELEGATING_METHOD_HANDLE + ".class";
295
296 // Convert LL -> LL, L3 -> LLL
297 private static String expandSignature(String signature) {
298 StringBuilder sb = new StringBuilder();
299 char last = 'X';
300 int count = 0;
301 for (int i = 0; i < signature.length(); i++) {
302 char c = signature.charAt(i);
303 if (c >= '0' && c <= '9') {
304 count *= 10;
305 count += (c - '0');
306 } else {
307 requireBasicType(c);
308 for (int j = 1; j < count; j++) {
309 sb.append(last);
310 }
311 sb.append(c);
312 last = c;
313 count = 0;
314 }
315 }
316
317 // ended with a number, e.g., "L2": append last char count - 1 times
318 if (count > 1) {
319 requireBasicType(last);
320 for (int j = 1; j < count; j++) {
321 sb.append(last);
322 }
323 }
324 return sb.toString();
325 }
326
327 private static MethodType asMethodType(String basicSignatureString) {
328 String[] parts = basicSignatureString.split("_");
329 assert(parts.length == 2);
330 assert(parts[1].length() == 1);
331 String parameters = expandSignature(parts[0]);
332 Class<?> rtype = simpleType(parts[1].charAt(0));
333 Class<?>[] ptypes = new Class<?>[parameters.length()];
334 if (ptypes.length == 0) {
335 return MethodType.methodType(rtype);
336 } else {
337 for (int i = 0; i < ptypes.length; i++) {
338 ptypes[i] = simpleType(parameters.charAt(i));
339 }
340 return MethodType.methodType(rtype, ptypes);
341 }
342 }
343
344 private static Class<?> simpleType(char c) {
345 switch (c) {
346 case 'F':
347 return float.class;
348 case 'D':
349 return double.class;
350 case 'I':
351 return int.class;
352 case 'L':
353 return Object.class;
354 case 'J':
355 return long.class;
356 case 'V':
357 return void.class;
358 case 'Z':
359 case 'B':
360 case 'S':
361 case 'C':
362 throw new IllegalArgumentException("Not a valid primitive: " + c +
363 " (use I instead)");
364 default:
|