1 /*
2 * Copyright (c) 2015, 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.FileNotFoundException;
28 import java.io.FileOutputStream;
29 import java.io.IOException;
30 import java.io.OutputStream;
31 import java.io.UncheckedIOException;
32 import java.nio.charset.StandardCharsets;
33 import java.util.ArrayList;
34 import java.util.Collections;
35 import java.util.HashSet;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.Objects;
39 import java.util.Set;
40 import java.util.function.Consumer;
41 import jdk.internal.org.objectweb.asm.ClassReader;
42 import jdk.internal.org.objectweb.asm.ClassWriter;
43 import jdk.internal.org.objectweb.asm.Opcodes;
44 import jdk.tools.jlink.internal.plugins.asm.AsmPools;
45 import jdk.tools.jlink.internal.plugins.asm.AsmPlugin;
46 import jdk.internal.org.objectweb.asm.tree.ClassNode;
47 import jdk.internal.org.objectweb.asm.tree.MethodNode;
48 import jdk.internal.org.objectweb.asm.util.CheckClassAdapter;
49 import jdk.tools.jlink.internal.plugins.asm.AsmModulePool;
50 import jdk.tools.jlink.internal.plugins.optim.ForNameFolding;
51 import jdk.tools.jlink.internal.plugins.optim.ReflectionOptimizer.TypeResolver;
52 import jdk.tools.jlink.plugin.PluginException;
53
54 /**
55 *
56 * Optimize Classes following various strategies. Strategies are implementation
57 * of <code>ClassOptimizer</code> and <code>MethodOptimizer</code>.
58 */
59 public final class OptimizationPlugin extends AsmPlugin {
60
61 public static final String NAME = "class-optim";
62 public static final String LOG = "log";
63 public static final String ALL = "all";
64 public static final String FORNAME_REMOVAL = "forName-folding";
65
66 /**
67 * Default resolver. A resolver that retrieve types that are in an
68 * accessible package, are public or are located in the same package as the
69 * caller.
70 */
71 private static final class DefaultTypeResolver implements TypeResolver {
72
73 private final Set<String> packages;
74 private final AsmPools pools;
75
76 DefaultTypeResolver(AsmPools pools, AsmModulePool modulePool) {
77 Objects.requireNonNull(pools);
78 Objects.requireNonNull(modulePool);
79 this.pools = pools;
80 packages = pools.getGlobalPool().getAccessiblePackages(modulePool.getModuleName());
81 }
82
83 @Override
84 public ClassReader resolve(ClassNode cn, MethodNode mn, String type) {
85 int classIndex = cn.name.lastIndexOf("/");
86 String callerPkg = classIndex == -1 ? ""
87 : cn.name.substring(0, classIndex);
88 int typeClassIndex = type.lastIndexOf("/");
89 String pkg = typeClassIndex == - 1 ? ""
90 : type.substring(0, typeClassIndex);
91 ClassReader reader = null;
92 if (packages.contains(pkg) || pkg.equals(callerPkg)) {
93 ClassReader r = pools.getGlobalPool().getClassReader(type);
94 if (r != null) {
95 // if not private
96 if ((r.getAccess() & Opcodes.ACC_PRIVATE)
97 != Opcodes.ACC_PRIVATE) {
98 // public
99 if (((r.getAccess() & Opcodes.ACC_PUBLIC)
100 == Opcodes.ACC_PUBLIC)) {
101 reader = r;
102 } else if (pkg.equals(callerPkg)) {
103 reader = r;
104 }
105 }
106 }
107 }
108 return reader;
109 }
110 }
111
112 public interface Optimizer {
113
114 void close() throws IOException;
115 }
116
117 public interface ClassOptimizer extends Optimizer {
118
119 boolean optimize(Consumer<String> logger, AsmPools pools,
120 AsmModulePool modulePool,
121 ClassNode cn) throws Exception;
122 }
123
124 public interface MethodOptimizer extends Optimizer {
125
126 boolean optimize(Consumer<String> logger, AsmPools pools,
127 AsmModulePool modulePool,
128 ClassNode cn, MethodNode m, TypeResolver resolver) throws Exception;
129 }
130
131 private List<Optimizer> optimizers = new ArrayList<>();
132
133 private OutputStream stream;
134 private int numMethods;
135
136 private void log(String content) {
137 if (stream != null) {
138 try {
139 content = content + "\n";
140 stream.write(content.getBytes(StandardCharsets.UTF_8));
141 } catch (IOException ex) {
142 System.err.println(ex);
143 }
144 }
145 }
146
147 private void close() throws IOException {
148 log("Num analyzed methods " + numMethods);
149
150 for (Optimizer optimizer : optimizers) {
151 try {
152 optimizer.close();
153 } catch (IOException ex) {
154 System.err.println("Error closing optimizer " + ex);
155 }
156 }
157 if (stream != null) {
158 stream.close();
159 }
160 }
161
162 @Override
163 public String getName() {
164 return NAME;
165 }
166
167 @Override
168 public void visit(AsmPools pools) {
169 try {
170 for (AsmModulePool p : pools.getModulePools()) {
171 DefaultTypeResolver resolver = new DefaultTypeResolver(pools, p);
172 p.visitClassReaders((reader) -> {
173 ClassWriter w = null;
174 try {
175 w = optimize(pools, p, reader, resolver);
176 } catch (IOException ex) {
177 throw new PluginException("Problem optimizing "
178 + reader.getClassName(), ex);
179 }
180 return w;
181 });
182 }
183 } finally {
184 try {
185 close();
186 } catch (IOException ex) {
187 throw new UncheckedIOException(ex);
188 }
189 }
190 }
191
192 private ClassWriter optimize(AsmPools pools, AsmModulePool modulePool,
193 ClassReader reader, TypeResolver resolver)
194 throws IOException {
195 ClassNode cn = new ClassNode();
196 ClassWriter writer = null;
197 if ((reader.getAccess() & Opcodes.ACC_INTERFACE) == 0) {
198 reader.accept(cn, ClassReader.EXPAND_FRAMES);
199 boolean optimized = false;
200 for (Optimizer optimizer : optimizers) {
201 if (optimizer instanceof ClassOptimizer) {
202 try {
203 boolean optim = ((ClassOptimizer) optimizer).
204 optimize(this::log, pools, modulePool, cn);
205 if (optim) {
206 optimized = true;
207 }
208 } catch (Throwable ex) {
209 throw new PluginException("Exception optimizing "
210 + reader.getClassName(), ex);
211 }
212 } else {
213 MethodOptimizer moptimizer = (MethodOptimizer) optimizer;
214 for (MethodNode m : cn.methods) {
215 if ((m.access & Opcodes.ACC_ABSTRACT) == 0
216 && (m.access & Opcodes.ACC_NATIVE) == 0) {
217 numMethods += 1;
218 try {
219 boolean optim = moptimizer.
220 optimize(this::log, pools, modulePool, cn,
221 m, resolver);
222 if (optim) {
223 optimized = true;
224 }
225 } catch (Throwable ex) {
226 throw new PluginException("Exception optimizing "
227 + reader.getClassName() + "." + m.name, ex);
228 }
229
230 }
231 }
232 }
233 }
234
235 if (optimized) {
236 writer = new ClassWriter(ClassWriter.COMPUTE_MAXS);
237 try {
238 // add a validation layer in between to check for class vallidity
239 CheckClassAdapter ca = new CheckClassAdapter(writer);
240 cn.accept(ca);
241 } catch (Exception ex) {
242 throw new PluginException("Exception optimizing class " + cn.name, ex);
243 }
244 }
245 }
246 return writer;
247 }
248
249 @Override
250 public String getDescription() {
251 return PluginsResourceBundle.getDescription(NAME);
252 }
253
254 @Override
255 public boolean hasArguments() {
256 return true;
257 }
258
259 @Override
260 public String getArgumentsDescription() {
261 return PluginsResourceBundle.getArgument(NAME);
262 }
263
264 @Override
265 public void configure(Map<String, String> config) {
266 String strategies = config.get(NAME);
267 String[] arr = strategies.split(",");
268 for (String s : arr) {
269 if (s.equals(ALL)) {
270 optimizers.clear();
271 optimizers.add(new ForNameFolding());
272 break;
273 } else if (s.equals(FORNAME_REMOVAL)) {
274 optimizers.add(new ForNameFolding());
275 } else {
276 throw new IllegalArgumentException("Unknown optimization: " + s);
277 }
278 }
279 String f = config.get(LOG);
280 if (f != null) {
281 try {
282 stream = new FileOutputStream(f);
283 } catch (IOException ex) {
284 throw new UncheckedIOException(ex);
285 }
286 }
287 }
288
289 @Override
290 public Set<PluginType> getType() {
291 Set<PluginType> set = new HashSet<>();
292 set.add(CATEGORY.TRANSFORMER);
293 return Collections.unmodifiableSet(set);
294 }
295 }
--- EOF ---