< prev index next >

src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java

Print this page
rev 49207 : 8199471: Enable generation of callSiteForms at link time
Reviewed-by: TBD


  52  * Plugin to generate java.lang.invoke classes.
  53  */
  54 public final class GenerateJLIClassesPlugin implements Plugin {
  55 
  56     private static final String NAME = "generate-jli-classes";
  57 
  58     private static final String DESCRIPTION = PluginsResourceBundle.getDescription(NAME);
  59 
  60     private static final String DEFAULT_TRACE_FILE = "default_jli_trace.txt";
  61 
  62     private static final String DIRECT_HOLDER = "java/lang/invoke/DirectMethodHandle$Holder";
  63     private static final String DMH_INVOKE_VIRTUAL = "invokeVirtual";
  64     private static final String DMH_INVOKE_STATIC = "invokeStatic";
  65     private static final String DMH_INVOKE_SPECIAL = "invokeSpecial";
  66     private static final String DMH_NEW_INVOKE_SPECIAL = "newInvokeSpecial";
  67     private static final String DMH_INVOKE_INTERFACE = "invokeInterface";
  68     private static final String DMH_INVOKE_STATIC_INIT = "invokeStaticInit";
  69 
  70     private static final String DELEGATING_HOLDER = "java/lang/invoke/DelegatingMethodHandle$Holder";
  71     private static final String BASIC_FORMS_HOLDER = "java/lang/invoke/LambdaForm$Holder";
  72     private static final String INVOKERS_HOLDER = "java/lang/invoke/Invokers$Holder";





  73 
  74     private static final JavaLangInvokeAccess JLIA
  75             = SharedSecrets.getJavaLangInvokeAccess();
  76 
  77     Set<String> speciesTypes = Set.of();
  78 
  79     Set<String> invokerTypes = Set.of();
  80 


  81     Map<String, Set<String>> dmhMethods = Map.of();
  82 
  83     String mainArgument;
  84 
  85     public GenerateJLIClassesPlugin() {
  86     }
  87 
  88     @Override
  89     public String getName() {
  90         return NAME;
  91     }
  92 
  93     @Override
  94     public String getDescription() {
  95         return DESCRIPTION;
  96     }
  97 
  98     @Override
  99     public Set<State> getState() {
 100         return EnumSet.of(State.AUTO_ENABLED, State.FUNCTIONAL);


 192                         new BufferedReader(
 193                             new InputStreamReader(traceFile)).lines());
 194                 }
 195             } catch (Exception e) {
 196                 throw new PluginException("Couldn't read " + DEFAULT_TRACE_FILE, e);
 197             }
 198         } else {
 199             File file = new File(mainArgument.substring(1));
 200             if (file.exists()) {
 201                 readTraceConfig(fileLines(file));
 202             }
 203         }
 204     }
 205 
 206     private void readTraceConfig(Stream<String> lines) {
 207         // Use TreeSet/TreeMap to keep things sorted in a deterministic
 208         // order to avoid scrambling the layout on small changes and to
 209         // ease finding methods in the generated code
 210         speciesTypes = new TreeSet<>(speciesTypes);
 211         invokerTypes = new TreeSet<>(invokerTypes);


 212         TreeMap<String, Set<String>> newDMHMethods = new TreeMap<>();
 213         for (Map.Entry<String, Set<String>> entry : dmhMethods.entrySet()) {
 214             newDMHMethods.put(entry.getKey(), new TreeSet<>(entry.getValue()));
 215         }
 216         dmhMethods = newDMHMethods;
 217         lines.map(line -> line.split(" "))
 218              .forEach(parts -> {
 219                 switch (parts[0]) {
 220                     case "[SPECIES_RESOLVE]":
 221                         // Allow for new types of species data classes being resolved here
 222                         if (parts.length == 3 && parts[1].startsWith("java.lang.invoke.BoundMethodHandle$Species_")) {
 223                             String species = parts[1].substring("java.lang.invoke.BoundMethodHandle$Species_".length());
 224                             if (!"L".equals(species)) {
 225                                 speciesTypes.add(expandSignature(species));
 226                             }
 227                         }
 228                         break;
 229                     case "[LF_RESOLVE]":
 230                         String methodType = parts[3];
 231                         validateMethodType(methodType);
 232                         if (parts[1].contains("Invokers")) {
 233                             invokerTypes.add(methodType);


 234                         } else if (parts[1].contains("DirectMethodHandle")) {
 235                             String dmh = parts[2];
 236                             // ignore getObject etc for now (generated
 237                             // by default)
 238                             if (DMH_METHOD_TYPE_MAP.containsKey(dmh)) {
 239                                 addDMHMethodType(dmh, methodType);
 240                             }
 241                         }
 242                         break;
 243                     default: break; // ignore
 244                 }
 245             });
 246     }
 247 
 248     private void addDMHMethodType(String dmh, String methodType) {
 249         validateMethodType(methodType);
 250         Set<String> methodTypes = dmhMethods.get(dmh);
 251         if (methodTypes == null) {
 252             methodTypes = new TreeSet<>();
 253             dmhMethods.put(dmh, methodTypes);


 277             throw new PluginException(
 278                     "Method type signature must be of form [LJIFD]*_[LJIFDV]");
 279         }
 280         // expand and check arguments (first part)
 281         expandSignature(typeParts[0]);
 282     }
 283 
 284     private static void requireBasicType(char c) {
 285         if ("LIJFD".indexOf(c) < 0) {
 286             throw new PluginException(
 287                     "Character " + c + " must correspond to a basic field type: LIJFD");
 288         }
 289     }
 290 
 291     @Override
 292     public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) {
 293         initialize(in);
 294         // Copy all but DMH_ENTRY to out
 295         in.transformAndCopy(entry -> {
 296                 // filter out placeholder entries
 297                 if (entry.path().equals(DIRECT_METHOD_HOLDER_ENTRY) ||
 298                     entry.path().equals(DELEGATING_METHOD_HOLDER_ENTRY) ||
 299                     entry.path().equals(INVOKERS_HOLDER_ENTRY) ||
 300                     entry.path().equals(BASIC_FORMS_HOLDER_ENTRY)) {


 301                     return null;
 302                 } else {
 303                     return entry;
 304                 }
 305             }, out);
 306 
 307         // Generate BMH Species classes
 308         speciesTypes.forEach(types -> generateBMHClass(types, out));
 309 
 310         // Generate LambdaForm Holder classes
 311         generateHolderClasses(out);
 312 
 313         // Let it go
 314         speciesTypes = null;
 315         invokerTypes = null;
 316         dmhMethods = null;
 317 
 318         return out.build();
 319     }
 320 


 344         }
 345         MethodType[] directMethodTypes = new MethodType[count];
 346         int[] dmhTypes = new int[count];
 347         int index = 0;
 348         for (Map.Entry<String, Set<String>> entry : dmhMethods.entrySet()) {
 349             String dmhType = entry.getKey();
 350             for (String type : entry.getValue()) {
 351                 // The DMH type to actually ask for is retrieved by removing
 352                 // the first argument, which needs to be of Object.class
 353                 MethodType mt = asMethodType(type);
 354                 if (mt.parameterCount() < 1 ||
 355                     mt.parameterType(0) != Object.class) {
 356                     throw new PluginException(
 357                             "DMH type parameter must start with L");
 358                 }
 359                 directMethodTypes[index] = mt.dropParameterTypes(0, 1);
 360                 dmhTypes[index] = DMH_METHOD_TYPE_MAP.get(dmhType);
 361                 index++;
 362             }
 363         }



 364         MethodType[] invokerMethodTypes = new MethodType[this.invokerTypes.size()];
 365         int i = 0;
 366         for (String invokerType : invokerTypes) {
 367             // The invoker type to ask for is retrieved by removing the first
 368             // and the last argument, which needs to be of Object.class
 369             MethodType mt = asMethodType(invokerType);
 370             final int lastParam = mt.parameterCount() - 1;
 371             if (mt.parameterCount() < 2 ||
 372                     mt.parameterType(0) != Object.class ||
 373                     mt.parameterType(lastParam) != Object.class) {
 374                 throw new PluginException(
 375                         "Invoker type parameter must start and end with L");
 376             }
 377             mt = mt.dropParameterTypes(lastParam, lastParam + 1);
 378             invokerMethodTypes[i] = mt.dropParameterTypes(0, 1);
 379             i++;
 380         }
















 381         try {
 382             byte[] bytes = JLIA.generateDirectMethodHandleHolderClassBytes(
 383                     DIRECT_HOLDER, directMethodTypes, dmhTypes);
 384             ResourcePoolEntry ndata = ResourcePoolEntry
 385                     .create(DIRECT_METHOD_HOLDER_ENTRY, bytes);
 386             out.add(ndata);
 387 
 388             bytes = JLIA.generateDelegatingMethodHandleHolderClassBytes(
 389                     DELEGATING_HOLDER, directMethodTypes);
 390             ndata = ResourcePoolEntry.create(DELEGATING_METHOD_HOLDER_ENTRY, bytes);
 391             out.add(ndata);
 392 
 393             bytes = JLIA.generateInvokersHolderClassBytes(INVOKERS_HOLDER,
 394                     invokerMethodTypes);
 395             ndata = ResourcePoolEntry.create(INVOKERS_HOLDER_ENTRY, bytes);
 396             out.add(ndata);
 397 





 398             bytes = JLIA.generateBasicFormsClassBytes(BASIC_FORMS_HOLDER);
 399             ndata = ResourcePoolEntry.create(BASIC_FORMS_HOLDER_ENTRY, bytes);
 400             out.add(ndata);
 401         } catch (Exception ex) {
 402             throw new PluginException(ex);
 403         }
 404     }
 405     private static final String DIRECT_METHOD_HOLDER_ENTRY =
 406             "/java.base/" + DIRECT_HOLDER + ".class";
 407     private static final String DELEGATING_METHOD_HOLDER_ENTRY =
 408             "/java.base/" + DELEGATING_HOLDER + ".class";
 409     private static final String BASIC_FORMS_HOLDER_ENTRY =
 410             "/java.base/" + BASIC_FORMS_HOLDER + ".class";
 411     private static final String INVOKERS_HOLDER_ENTRY =
 412             "/java.base/" + INVOKERS_HOLDER + ".class";


 413 
 414     // Convert LL -> LL, L3 -> LLL
 415     public static String expandSignature(String signature) {
 416         StringBuilder sb = new StringBuilder();
 417         char last = 'X';
 418         int count = 0;
 419         for (int i = 0; i < signature.length(); i++) {
 420             char c = signature.charAt(i);
 421             if (c >= '0' && c <= '9') {
 422                 count *= 10;
 423                 count += (c - '0');
 424             } else {
 425                 requireBasicType(c);
 426                 for (int j = 1; j < count; j++) {
 427                     sb.append(last);
 428                 }
 429                 sb.append(c);
 430                 last = c;
 431                 count = 0;
 432             }




  52  * Plugin to generate java.lang.invoke classes.
  53  */
  54 public final class GenerateJLIClassesPlugin implements Plugin {
  55 
  56     private static final String NAME = "generate-jli-classes";
  57 
  58     private static final String DESCRIPTION = PluginsResourceBundle.getDescription(NAME);
  59 
  60     private static final String DEFAULT_TRACE_FILE = "default_jli_trace.txt";
  61 
  62     private static final String DIRECT_HOLDER = "java/lang/invoke/DirectMethodHandle$Holder";
  63     private static final String DMH_INVOKE_VIRTUAL = "invokeVirtual";
  64     private static final String DMH_INVOKE_STATIC = "invokeStatic";
  65     private static final String DMH_INVOKE_SPECIAL = "invokeSpecial";
  66     private static final String DMH_NEW_INVOKE_SPECIAL = "newInvokeSpecial";
  67     private static final String DMH_INVOKE_INTERFACE = "invokeInterface";
  68     private static final String DMH_INVOKE_STATIC_INIT = "invokeStaticInit";
  69 
  70     private static final String DELEGATING_HOLDER = "java/lang/invoke/DelegatingMethodHandle$Holder";
  71     private static final String BASIC_FORMS_HOLDER = "java/lang/invoke/LambdaForm$Holder";
  72 
  73     private static final String INVOKERS_HOLDER_NAME = "java.lang.invoke.Invokers$Holder";
  74     private static final String INVOKERS_HOLDER_INTERNAL_NAME = INVOKERS_HOLDER_NAME.replace('.', '/');
  75 
  76     private static final String INVOKERS_CS_HOLDER_NAME = "java.lang.invoke.Invokers$CSHolder";
  77     private static final String INVOKERS_CS_HOLDER_INTERNAL_NAME = INVOKERS_CS_HOLDER_NAME.replace('.', '/');
  78 
  79     private static final JavaLangInvokeAccess JLIA
  80             = SharedSecrets.getJavaLangInvokeAccess();
  81 
  82     Set<String> speciesTypes = Set.of();
  83 
  84     Set<String> invokerTypes = Set.of();
  85 
  86     Set<String> callSiteTypes = Set.of();
  87 
  88     Map<String, Set<String>> dmhMethods = Map.of();
  89 
  90     String mainArgument;
  91 
  92     public GenerateJLIClassesPlugin() {
  93     }
  94 
  95     @Override
  96     public String getName() {
  97         return NAME;
  98     }
  99 
 100     @Override
 101     public String getDescription() {
 102         return DESCRIPTION;
 103     }
 104 
 105     @Override
 106     public Set<State> getState() {
 107         return EnumSet.of(State.AUTO_ENABLED, State.FUNCTIONAL);


 199                         new BufferedReader(
 200                             new InputStreamReader(traceFile)).lines());
 201                 }
 202             } catch (Exception e) {
 203                 throw new PluginException("Couldn't read " + DEFAULT_TRACE_FILE, e);
 204             }
 205         } else {
 206             File file = new File(mainArgument.substring(1));
 207             if (file.exists()) {
 208                 readTraceConfig(fileLines(file));
 209             }
 210         }
 211     }
 212 
 213     private void readTraceConfig(Stream<String> lines) {
 214         // Use TreeSet/TreeMap to keep things sorted in a deterministic
 215         // order to avoid scrambling the layout on small changes and to
 216         // ease finding methods in the generated code
 217         speciesTypes = new TreeSet<>(speciesTypes);
 218         invokerTypes = new TreeSet<>(invokerTypes);
 219         callSiteTypes = new TreeSet<>(callSiteTypes);
 220 
 221         TreeMap<String, Set<String>> newDMHMethods = new TreeMap<>();
 222         for (Map.Entry<String, Set<String>> entry : dmhMethods.entrySet()) {
 223             newDMHMethods.put(entry.getKey(), new TreeSet<>(entry.getValue()));
 224         }
 225         dmhMethods = newDMHMethods;
 226         lines.map(line -> line.split(" "))
 227              .forEach(parts -> {
 228                 switch (parts[0]) {
 229                     case "[SPECIES_RESOLVE]":
 230                         // Allow for new types of species data classes being resolved here
 231                         if (parts.length == 3 && parts[1].startsWith("java.lang.invoke.BoundMethodHandle$Species_")) {
 232                             String species = parts[1].substring("java.lang.invoke.BoundMethodHandle$Species_".length());
 233                             if (!"L".equals(species)) {
 234                                 speciesTypes.add(expandSignature(species));
 235                             }
 236                         }
 237                         break;
 238                     case "[LF_RESOLVE]":
 239                         String methodType = parts[3];
 240                         validateMethodType(methodType);
 241                         if (parts[1].equals(INVOKERS_HOLDER_NAME)) {
 242                             invokerTypes.add(methodType);
 243                         } else if (parts[1].equals(INVOKERS_CS_HOLDER_NAME)) {
 244                             callSiteTypes.add(methodType);
 245                         } else if (parts[1].contains("DirectMethodHandle")) {
 246                             String dmh = parts[2];
 247                             // ignore getObject etc for now (generated
 248                             // by default)
 249                             if (DMH_METHOD_TYPE_MAP.containsKey(dmh)) {
 250                                 addDMHMethodType(dmh, methodType);
 251                             }
 252                         }
 253                         break;
 254                     default: break; // ignore
 255                 }
 256             });
 257     }
 258 
 259     private void addDMHMethodType(String dmh, String methodType) {
 260         validateMethodType(methodType);
 261         Set<String> methodTypes = dmhMethods.get(dmh);
 262         if (methodTypes == null) {
 263             methodTypes = new TreeSet<>();
 264             dmhMethods.put(dmh, methodTypes);


 288             throw new PluginException(
 289                     "Method type signature must be of form [LJIFD]*_[LJIFDV]");
 290         }
 291         // expand and check arguments (first part)
 292         expandSignature(typeParts[0]);
 293     }
 294 
 295     private static void requireBasicType(char c) {
 296         if ("LIJFD".indexOf(c) < 0) {
 297             throw new PluginException(
 298                     "Character " + c + " must correspond to a basic field type: LIJFD");
 299         }
 300     }
 301 
 302     @Override
 303     public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) {
 304         initialize(in);
 305         // Copy all but DMH_ENTRY to out
 306         in.transformAndCopy(entry -> {
 307                 // filter out placeholder entries
 308                 String path = entry.path();
 309                 if (path.equals(DIRECT_METHOD_HOLDER_ENTRY) ||
 310                     path.equals(DELEGATING_METHOD_HOLDER_ENTRY) ||
 311                     path.equals(INVOKERS_HOLDER_ENTRY) ||
 312                     path.equals(INVOKERS_CS_HOLDER_ENTRY) ||
 313                     path.equals(BASIC_FORMS_HOLDER_ENTRY)) {
 314                     return null;
 315                 } else {
 316                     return entry;
 317                 }
 318             }, out);
 319 
 320         // Generate BMH Species classes
 321         speciesTypes.forEach(types -> generateBMHClass(types, out));
 322 
 323         // Generate LambdaForm Holder classes
 324         generateHolderClasses(out);
 325 
 326         // Let it go
 327         speciesTypes = null;
 328         invokerTypes = null;
 329         dmhMethods = null;
 330 
 331         return out.build();
 332     }
 333 


 357         }
 358         MethodType[] directMethodTypes = new MethodType[count];
 359         int[] dmhTypes = new int[count];
 360         int index = 0;
 361         for (Map.Entry<String, Set<String>> entry : dmhMethods.entrySet()) {
 362             String dmhType = entry.getKey();
 363             for (String type : entry.getValue()) {
 364                 // The DMH type to actually ask for is retrieved by removing
 365                 // the first argument, which needs to be of Object.class
 366                 MethodType mt = asMethodType(type);
 367                 if (mt.parameterCount() < 1 ||
 368                     mt.parameterType(0) != Object.class) {
 369                     throw new PluginException(
 370                             "DMH type parameter must start with L");
 371                 }
 372                 directMethodTypes[index] = mt.dropParameterTypes(0, 1);
 373                 dmhTypes[index] = DMH_METHOD_TYPE_MAP.get(dmhType);
 374                 index++;
 375             }
 376         }
 377 
 378         // The invoker type to ask for is retrieved by removing the first
 379         // and the last argument, which needs to be of Object.class
 380         MethodType[] invokerMethodTypes = new MethodType[this.invokerTypes.size()];
 381         int i = 0;
 382         for (String invokerType : invokerTypes) {


 383             MethodType mt = asMethodType(invokerType);
 384             final int lastParam = mt.parameterCount() - 1;
 385             if (mt.parameterCount() < 2 ||
 386                     mt.parameterType(0) != Object.class ||
 387                     mt.parameterType(lastParam) != Object.class) {
 388                 throw new PluginException(
 389                         "Invoker type parameter must start and end with Object: " + invokerType);
 390             }
 391             mt = mt.dropParameterTypes(lastParam, lastParam + 1);
 392             invokerMethodTypes[i] = mt.dropParameterTypes(0, 1);
 393             i++;
 394         }
 395 
 396         // The callSite type to ask for is retrieved by removing the last
 397         // argument, which needs to be of Object.class
 398         MethodType[] callSiteMethodTypes = new MethodType[this.callSiteTypes.size()];
 399         i = 0;
 400         for (String callSiteType : callSiteTypes) {
 401             MethodType mt = asMethodType(callSiteType);
 402             final int lastParam = mt.parameterCount() - 1;
 403             if (mt.parameterCount() < 1 ||
 404                     mt.parameterType(lastParam) != Object.class) {
 405                 throw new PluginException(
 406                         "CallSite type parameter must end with Object: " + callSiteType);
 407             }
 408             callSiteMethodTypes[i] = mt.dropParameterTypes(lastParam, lastParam + 1);
 409             i++;
 410         }
 411         try {
 412             byte[] bytes = JLIA.generateDirectMethodHandleHolderClassBytes(
 413                     DIRECT_HOLDER, directMethodTypes, dmhTypes);
 414             ResourcePoolEntry ndata = ResourcePoolEntry
 415                     .create(DIRECT_METHOD_HOLDER_ENTRY, bytes);
 416             out.add(ndata);
 417 
 418             bytes = JLIA.generateDelegatingMethodHandleHolderClassBytes(
 419                     DELEGATING_HOLDER, directMethodTypes);
 420             ndata = ResourcePoolEntry.create(DELEGATING_METHOD_HOLDER_ENTRY, bytes);
 421             out.add(ndata);
 422 
 423             bytes = JLIA.generateInvokersHolderClassBytes(INVOKERS_HOLDER_INTERNAL_NAME,
 424                     invokerMethodTypes);
 425             ndata = ResourcePoolEntry.create(INVOKERS_HOLDER_ENTRY, bytes);
 426             out.add(ndata);
 427 
 428             bytes = JLIA.generateCallSiteHolderClassBytes(INVOKERS_CS_HOLDER_INTERNAL_NAME,
 429                     callSiteMethodTypes);
 430             ndata = ResourcePoolEntry.create(INVOKERS_CS_HOLDER_ENTRY, bytes);
 431             out.add(ndata);
 432 
 433             bytes = JLIA.generateBasicFormsClassBytes(BASIC_FORMS_HOLDER);
 434             ndata = ResourcePoolEntry.create(BASIC_FORMS_HOLDER_ENTRY, bytes);
 435             out.add(ndata);
 436         } catch (Exception ex) {
 437             throw new PluginException(ex);
 438         }
 439     }
 440     private static final String DIRECT_METHOD_HOLDER_ENTRY =
 441             "/java.base/" + DIRECT_HOLDER + ".class";
 442     private static final String DELEGATING_METHOD_HOLDER_ENTRY =
 443             "/java.base/" + DELEGATING_HOLDER + ".class";
 444     private static final String BASIC_FORMS_HOLDER_ENTRY =
 445             "/java.base/" + BASIC_FORMS_HOLDER + ".class";
 446     private static final String INVOKERS_HOLDER_ENTRY =
 447             "/java.base/" + INVOKERS_HOLDER_INTERNAL_NAME + ".class";
 448     private static final String INVOKERS_CS_HOLDER_ENTRY =
 449             "/java.base/" + INVOKERS_CS_HOLDER_INTERNAL_NAME + ".class";
 450 
 451     // Convert LL -> LL, L3 -> LLL
 452     public static String expandSignature(String signature) {
 453         StringBuilder sb = new StringBuilder();
 454         char last = 'X';
 455         int count = 0;
 456         for (int i = 0; i < signature.length(); i++) {
 457             char c = signature.charAt(i);
 458             if (c >= '0' && c <= '9') {
 459                 count *= 10;
 460                 count += (c - '0');
 461             } else {
 462                 requireBasicType(c);
 463                 for (int j = 1; j < count; j++) {
 464                     sb.append(last);
 465                 }
 466                 sb.append(c);
 467                 last = c;
 468                 count = 0;
 469             }


< prev index next >