1 /* 2 * Copyright (c) 2016, 2020, 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.BufferedReader; 28 import java.io.File; 29 import java.io.IOException; 30 import java.io.InputStream; 31 import java.io.InputStreamReader; 32 import java.lang.invoke.MethodType; 33 import java.nio.file.Files; 34 import java.util.EnumSet; 35 import java.util.List; 36 import java.util.Map; 37 import java.util.Set; 38 import java.util.TreeMap; 39 import java.util.TreeSet; 40 import java.util.stream.Stream; 41 42 import jdk.internal.access.JavaLangInvokeAccess; 43 import jdk.internal.access.SharedSecrets; 44 import jdk.tools.jlink.plugin.Plugin; 45 import jdk.tools.jlink.plugin.PluginException; 46 import jdk.tools.jlink.plugin.ResourcePool; 47 import jdk.tools.jlink.plugin.ResourcePoolBuilder; 48 import jdk.tools.jlink.plugin.ResourcePoolEntry; 49 50 /** 51 * Plugin to generate java.lang.invoke classes. 52 * 53 * The plugin reads in a file generated by running any application with 54 * {@code -Djava.lang.invoke.MethodHandle.TRACE_RESOLVE=true}. This is done 55 * automatically during build, see make/GenerateLinkOptData.gmk. See 56 * build/tools/classlist/HelloClasslist.java for the training application. 57 * 58 * HelloClasslist tries to reflect common use of java.lang.invoke during early 59 * startup and warmup in various applications. To ensure a good default 60 * trade-off between static footprint and startup the application should be 61 * relatively conservative. 62 * 63 * When using jlink to build a custom application runtime, generating a trace 64 * file using {@code -Djava.lang.invoke.MethodHandle.TRACE_RESOLVE=true} and 65 * feeding that into jlink using {@code --generate-jli-classes=@trace_file} can 66 * help improve startup time. 67 */ 68 public final class GenerateJLIClassesPlugin implements Plugin { 69 70 private static final String NAME = "generate-jli-classes"; 71 72 private static final String DESCRIPTION = PluginsResourceBundle.getDescription(NAME); 73 74 private static final String DEFAULT_TRACE_FILE = "default_jli_trace.txt"; 75 76 private static final JavaLangInvokeAccess JLIA = SharedSecrets.getJavaLangInvokeAccess(); 77 78 String mainArgument; 79 String[] lines = null; 80 81 public GenerateJLIClassesPlugin() { 82 } 83 84 @Override 85 public String getName() { 86 return NAME; 87 } 88 89 @Override 90 public String getDescription() { 91 return DESCRIPTION; 92 } 93 94 @Override 95 public Set<State> getState() { 96 return EnumSet.of(State.AUTO_ENABLED, State.FUNCTIONAL); 97 } 98 99 @Override 100 public boolean hasArguments() { 101 return true; 102 } 103 104 @Override 105 public String getArgumentsDescription() { 106 return PluginsResourceBundle.getArgument(NAME); 107 } 108 109 @Override 110 public void configure(Map<String, String> config) { 111 mainArgument = config.get(NAME); 112 } 113 114 // Repeat def, we do not have access right to InvokerBytecodeGeneratorHelper 115 // Must be exact same! 116 private static final String DIRECT_METHOD_HOLDER_ENTRY = 117 "/java.base/java/lang/invoke/DirectMethodHandle$Holder.class"; 118 private static final String DELEGATING_METHOD_HOLDER_ENTRY = 119 "/java.base/java/lang/invoke/DelegatingMethodHandle$Holder.class"; 120 private static final String INVOKERS_HOLDER_ENTRY = 121 "/java.base/java/lang/invoke/Invokers$Holder.class"; 122 private static final String BASIC_FORMS_HOLDER_ENTRY = 123 "/java.base/java/lang/invoke/LambdaForm$Holder.class"; 124 125 @Override 126 public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) { 127 initialize(in); 128 // Copy all but DMH_ENTRY to out 129 in.transformAndCopy(entry -> { 130 // filter out placeholder entries 131 String path = entry.path(); 132 if (path.equals(DIRECT_METHOD_HOLDER_ENTRY) || 133 path.equals(DELEGATING_METHOD_HOLDER_ENTRY) || 134 path.equals(INVOKERS_HOLDER_ENTRY) || 135 path.equals(BASIC_FORMS_HOLDER_ENTRY)) { 136 return null; 137 } else { 138 return entry; 139 } 140 }, out); 141 142 // Generate LambdaForm Holder classes 143 generateHolderClasses(out); 144 // clear input. 145 lines = null; 146 return out.build(); 147 } 148 149 private void generateHolderClasses(ResourcePoolBuilder out) { 150 try { 151 Map<String, byte[]> result = JLIA.generateMethodHandleHolderClasses(lines); 152 if (result != null) { 153 result.forEach ((k,v) -> { 154 ResourcePoolEntry ndata = ResourcePoolEntry.create(k, v); 155 out.add(ndata); 156 }); 157 } 158 } catch (Exception ex) { 159 throw new PluginException(ex); 160 } 161 } 162 163 public void initialize(ResourcePool in) { 164 // Load configuration from the contents in the supplied input file 165 // - if none was supplied we look for the default file 166 File file = null; 167 if (mainArgument == null || !mainArgument.startsWith("@")) { 168 file = new File(DEFAULT_TRACE_FILE); 169 } else { 170 file = new File(mainArgument.substring(1)); 171 } 172 173 if (file.exists()) { 174 try { 175 List<String> all = Files.readAllLines(file.toPath()); 176 // convert to String[] 177 int size = all.size(); 178 lines = new String[size]; 179 lines = all.toArray(lines); 180 } catch (Exception e) { 181 throw new PluginException("Couldn't read " + file.getName(), e); 182 } 183 } 184 } 185 }