/* * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package jdk.tools.jlink.internal.plugins; import java.io.ByteArrayInputStream; import java.lang.reflect.Method; import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import jdk.tools.jlink.plugin.PluginException; import jdk.tools.jlink.plugin.Pool; import jdk.tools.jlink.plugin.TransformerPlugin; /** * Plugin to generate BoundMethodHandle classes. */ public final class GenerateBMHClassesPlugin implements TransformerPlugin { private static final String NAME = "generate-bmh"; private static final String DESCRIPTION = PluginsResourceBundle.getDescription(NAME); private static final String BMH = "java/lang/invoke/BoundMethodHandle"; private static final Method FACTORY_METHOD; List speciesTypes; public GenerateBMHClassesPlugin() { } @Override public Set getType() { return Collections.singleton(CATEGORY.TRANSFORMER); } @Override public String getName() { return NAME; } @Override public String getDescription() { return DESCRIPTION; } @Override public Set getState() { return EnumSet.of(STATE.AUTO_ENABLED, STATE.FUNCTIONAL); } @Override public boolean hasArguments() { return true; } @Override public String getArgumentsDescription() { return PluginsResourceBundle.getArgument(NAME); } @Override public void configure(Map config) { String args = config.get(NAME); if (args != null && !args.isEmpty()) { speciesTypes = Arrays.stream(args.split(",")) .map(String::trim) .filter(s -> !s.isEmpty()) .collect(Collectors.toList()); } else { // Default set of Species forms to generate speciesTypes = List.of("LL", "L3", "L4", "L5", "L6", "L6I", "L6II", "L7", "L8", "L9", "L10", "L10I", "L10II", "L10IIL"); } } @Override public void visit(Pool in, Pool out) { for (Pool.ModuleData data : in.getContent()) { if (("/java.base/" + BMH + ".class").equals(data.getPath())) { out.add(data); // Add BoundMethodHandle unchanged speciesTypes.forEach(types -> generateConcreteClass(types, data, out)); } else { out.add(data); } } } private void generateConcreteClass(String shortTypes, Pool.ModuleData data, Pool out) { try { String types = expandSignature(shortTypes); // Generate class byte[] bytes = (byte[])FACTORY_METHOD.invoke(null, BMH + "$Species_" + shortTypes, // class name "Species_" + shortTypes, // source name types); // Add class to pool Pool.ModuleData ndata = new Pool.ModuleData(data.getModule(), "/java.base/" + BMH + "$Species_" + shortTypes + ".class", Pool.ModuleDataType.CLASS_OR_RESOURCE, new ByteArrayInputStream(bytes), bytes.length); out.add(ndata); } catch (Exception ex) { throw new PluginException(ex); } } static { try { Class BMHFactory = Class.forName("java.lang.invoke.BoundMethodHandle$Factory"); Method genClassMethod = BMHFactory.getDeclaredMethod("generateConcreteBMHClassBytes", String.class, String.class, String.class); genClassMethod.setAccessible(true); FACTORY_METHOD = genClassMethod; } catch (Exception e) { throw new PluginException(e); } } // Convert LL -> LL, L3 -> LLL private static String expandSignature(String signature) { StringBuilder sb = new StringBuilder(); char last = 'X'; int count = 0; for (int i = 0; i < signature.length(); i++) { char c = signature.charAt(i); if (c >= '0' && c <= '9') { count *= 10; count += (c - '0'); } else { for (int j = 1; j < count; j++) { sb.append(last); } sb.append(c); last = c; count = 0; } } for (int j = 1; j < count; j++) { sb.append(last); } return sb.toString(); } }