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", "L7", "L7I",
  99                     "L7II", "L7IIL", "L8", "L9", "L10", "L11", "L11I", "L11II",
 100                     "L12", "L13", "LI", "D", "L3I", "LIL", "LLI", "LLIL",
 101                     "LILL", "I", "LLILL");
 102         }
 103     }
 104 
 105     @Override
 106     public void visit(Pool in, Pool out) {
 107         for (Pool.ModuleData data : in.getContent()) {
 108             if (("/java.base/" + BMH + ".class").equals(data.getPath())) {
 109                 // Add BoundMethodHandle unchanged
 110                 out.add(data);
 111                 speciesTypes.forEach(types -> generateConcreteClass(types, data, out));
 112             } else {
 113                 out.add(data);
 114             }
 115         }
 116     }
 117 
 118     @SuppressWarnings("unchecked")
 119     private void generateConcreteClass(String shortTypes, Pool.ModuleData data, Pool out) {
 120         try {
 121             String types = expandSignature(shortTypes);
 122 
 123             // Generate class
 124             Map.Entry<String, byte[]> result = (Map.Entry<String, byte[]>)
 125                     FACTORY_METHOD.invoke(null, types);
 126             String className = result.getKey();
 127             byte[] bytes = result.getValue();
 128 
 129             // Add class to pool
 130             Pool.ModuleData ndata = new Pool.ModuleData(data.getModule(),
 131                     "/java.base/" + className + ".class",
 132                     Pool.ModuleDataType.CLASS_OR_RESOURCE,
 133                     new ByteArrayInputStream(bytes), bytes.length);
 134             out.add(ndata);
 135         } catch (Exception ex) {
 136             System.err.println("Warning: Unable to generate BMH class for " + shortTypes);
 137         }
 138     }
 139 
 140     static {
 141         try {
 142             Class<?> BMHFactory = Class.forName("java.lang.invoke.BoundMethodHandle$Factory");
 143             Method genClassMethod = BMHFactory.getDeclaredMethod("generateConcreteBMHClassBytes",
 144                     String.class);
 145             genClassMethod.setAccessible(true);
 146             FACTORY_METHOD = genClassMethod;
 147         } catch (Exception e) {
 148             throw new PluginException(e);
 149         }
 150     }
 151 
 152     // Convert LL -> LL, L3 -> LLL
 153     private static String expandSignature(String signature) {
 154         StringBuilder sb = new StringBuilder();
 155         char last = 'X';
 156         int count = 0;
 157         for (int i = 0; i < signature.length(); i++) {
 158             char c = signature.charAt(i);
 159             if (c >= '0' && c <= '9') {
 160                 count *= 10;
 161                 count += (c - '0');
 162             } else {
 163                 for (int j = 1; j < count; j++) {
 164                     sb.append(last);
 165                 }
 166                 sb.append(c);
 167                 last = c;
 168                 count = 0;
 169             }
 170         }
 171         for (int j = 1; j < count; j++) {
 172             sb.append(last);
 173         }
 174         return sb.toString();
 175     }
 176 }