1 /*
   2  * Copyright (c) 2016, 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 package jdk.tools.jlink.internal.plugins;
  26 
  27 import java.io.ByteArrayInputStream;
  28 import java.lang.reflect.Method;
  29 import java.util.Arrays;
  30 import java.util.Collections;
  31 import java.util.EnumSet;
  32 import java.util.List;
  33 import java.util.Map;
  34 import java.util.Set;
  35 import java.util.stream.Collectors;
  36 import jdk.tools.jlink.plugin.PluginException;
  37 import jdk.tools.jlink.plugin.Pool;
  38 import jdk.tools.jlink.plugin.TransformerPlugin;
  39 
  40 /**
  41  * Plugin to generate BoundMethodHandle classes.
  42  */
  43 public final class GenerateBMHClassesPlugin implements TransformerPlugin {
  44 
  45     private static final String NAME = "generate-bmh";
  46 
  47     private static final String DESCRIPTION = PluginsResourceBundle.getDescription(NAME);
  48 
  49     private static final String BMH = "java/lang/invoke/BoundMethodHandle";
  50 
  51     private static final Method FACTORY_METHOD;
  52 
  53     List<String> speciesTypes;
  54 
  55     public GenerateBMHClassesPlugin() {
  56     }
  57 
  58     @Override
  59     public Set<PluginType> getType() {
  60         return Collections.singleton(CATEGORY.TRANSFORMER);
  61     }
  62 
  63     @Override
  64     public String getName() {
  65         return NAME;
  66     }
  67 
  68     @Override
  69     public String getDescription() {
  70         return DESCRIPTION;
  71     }
  72 
  73     @Override
  74     public Set<STATE> getState() {
  75         return EnumSet.of(STATE.AUTO_ENABLED, STATE.FUNCTIONAL);
  76     }
  77 
  78     @Override
  79     public boolean hasArguments() {
  80         return true;
  81     }
  82 
  83     @Override
  84     public String getArgumentsDescription() {
  85        return PluginsResourceBundle.getArgument(NAME);
  86     }
  87 
  88     @Override
  89     public void configure(Map<String, String> config) {
  90         String args = config.get(NAME);
  91         if (args != null && !args.isEmpty()) {
  92             speciesTypes = Arrays.stream(args.split(","))
  93                 .map(String::trim)
  94                 .filter(s -> !s.isEmpty())
  95                 .collect(Collectors.toList());
  96         } else {
  97             // Default set of Species forms to generate
  98             speciesTypes = List.of("LL", "L3", "L4", "L5", "L6", "L6I", "L6II",
  99                     "L7", "L8", "L9", "L10", "L10I", "L10II", "L10IIL");
 100         }
 101     }
 102 
 103     @Override
 104     public void visit(Pool in, Pool out) {
 105         for (Pool.ModuleData data : in.getContent()) {
 106             if (("/java.base/" + BMH + ".class").equals(data.getPath())) {
 107                 out.add(data); // Add BoundMethodHandle unchanged
 108                 speciesTypes.forEach(types -> generateConcreteClass(types, data, out));
 109             } else {
 110                 out.add(data);
 111             }
 112         }
 113     }
 114 
 115     private void generateConcreteClass(String shortTypes, Pool.ModuleData data, Pool out) {
 116         try {
 117             String types = expandSignature(shortTypes);
 118 
 119             // Generate class
 120             byte[] bytes = (byte[])FACTORY_METHOD.invoke(null,
 121                     BMH + "$Species_" + shortTypes, // class name
 122                     "Species_" + shortTypes, // source name
 123                     types);
 124 
 125             // Add class to pool
 126             Pool.ModuleData ndata = new Pool.ModuleData(data.getModule(),
 127                     "/java.base/" + BMH + "$Species_" + shortTypes + ".class",
 128                     Pool.ModuleDataType.CLASS_OR_RESOURCE,
 129                     new ByteArrayInputStream(bytes), bytes.length);
 130             out.add(ndata);
 131         } catch (Exception ex) {
 132             throw new PluginException(ex);
 133         }
 134     }
 135 
 136     static {
 137         try {
 138             Class<?> BMHFactory = Class.forName("java.lang.invoke.BoundMethodHandle$Factory");
 139             Method genClassMethod = BMHFactory.getDeclaredMethod("generateConcreteBMHClassBytes",
 140                     String.class, String.class, String.class);
 141             genClassMethod.setAccessible(true);
 142             FACTORY_METHOD = genClassMethod;
 143         } catch (Exception e) {
 144             throw new PluginException(e);
 145         }
 146     }
 147 
 148     // Convert LL -> LL, L3 -> LLL
 149     private static String expandSignature(String signature) {
 150         StringBuilder sb = new StringBuilder();
 151         char last = 'X';
 152         int count = 0;
 153         for (int i = 0; i < signature.length(); i++) {
 154             char c = signature.charAt(i);
 155             if (c >= '0' && c <= '9') {
 156                 count *= 10;
 157                 count += (c - '0');
 158             } else {
 159                 for (int j = 1; j < count; j++) {
 160                     sb.append(last);
 161                 }
 162                 sb.append(c);
 163                 last = c;
 164                 count = 0;
 165             }
 166         }
 167         for (int j = 1; j < count; j++) {
 168             sb.append(last);
 169         }
 170         return sb.toString();
 171     }
 172 }