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 }