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.optim; 26 27 import java.util.ArrayList; 28 import java.util.Collections; 29 import java.util.HashMap; 30 import java.util.IdentityHashMap; 31 import java.util.Iterator; 32 import java.util.List; 33 import java.util.Map; 34 import java.util.Set; 35 import java.util.TreeSet; 36 import jdk.internal.org.objectweb.asm.ClassReader; 37 import jdk.internal.org.objectweb.asm.Type; 38 import jdk.internal.org.objectweb.asm.tree.AbstractInsnNode; 39 import jdk.internal.org.objectweb.asm.tree.ClassNode; 40 import jdk.internal.org.objectweb.asm.tree.LabelNode; 41 import jdk.internal.org.objectweb.asm.tree.LdcInsnNode; 42 import jdk.internal.org.objectweb.asm.tree.LineNumberNode; 43 import jdk.internal.org.objectweb.asm.tree.MethodInsnNode; 44 import jdk.internal.org.objectweb.asm.tree.MethodNode; 45 import jdk.internal.org.objectweb.asm.tree.TryCatchBlockNode; 46 import jdk.tools.jlink.internal.plugins.optim.ControlFlow.Block; 47 48 /** 49 * Implement the reflection optimization. 50 */ 51 public class ReflectionOptimizer { 52 53 public static class Data { 54 55 private int removedInstructions; 56 private final Map<String, Set<Block>> removedHandlers = new HashMap<>(); 57 58 private Data() { 59 } 60 61 public int removedInstructions() { 62 return removedInstructions; 63 } 64 65 public Map<String, Set<Block>> removedHandlers() { 66 return Collections.unmodifiableMap(removedHandlers); 67 } 68 } 69 70 public interface TypeResolver { 71 72 public ClassReader resolve(ClassNode cn, MethodNode m, String type); 73 } 74 75 public static Data replaceWithClassConstant(ClassNode cn, MethodNode m, 76 TypeResolver cch) 77 throws Exception { 78 Iterator<AbstractInsnNode> it = m.instructions.iterator(); 79 LdcInsnNode insNode = null; 80 Map<LdcInsnNode, LdcInsnNode> replacement = new IdentityHashMap<>(); 81 Data data = new Data(); 82 while (it.hasNext()) { 83 AbstractInsnNode n = it.next(); 84 if (n instanceof LdcInsnNode) { 85 LdcInsnNode ldc = (LdcInsnNode) n; 86 if (ldc.cst instanceof String) { 87 insNode = ldc; 88 } 89 } else { 90 if (n instanceof MethodInsnNode && insNode != null) { 91 MethodInsnNode met = (MethodInsnNode) n; 92 if (met.name.equals("forName") 93 && met.owner.equals("java/lang/Class") 94 && met.desc.equals("(Ljava/lang/String;)Ljava/lang/Class;")) { 95 // Can we load the type? 96 Type type = null; 97 String binaryName = insNode.cst.toString().replaceAll("\\.", "/"); 98 String unaryClassName = binaryName; 99 int arrayIndex = binaryName.lastIndexOf("["); 100 if (arrayIndex >= 0) { 101 int objIndex = unaryClassName.indexOf("L"); 102 if (objIndex >= 0) { 103 unaryClassName = unaryClassName.substring(objIndex + 1); 104 unaryClassName = unaryClassName.substring(0, 105 unaryClassName.length() - 1); 106 } else { 107 //primitive, this is just fine. 108 type = Type.getObjectType(binaryName); 109 } 110 } 111 if (type == null) { 112 if (cch.resolve(cn, m, unaryClassName) != null) { 113 type = Type.getObjectType(binaryName); 114 } 115 } 116 if (type != null) { 117 replacement.put(insNode, new LdcInsnNode(type)); 118 it.remove(); 119 data.removedInstructions += 1; 120 } 121 } else { 122 insNode = null; 123 } 124 // Virtual node, not taken into account 125 } else if (!(n instanceof LabelNode) && !(n instanceof LineNumberNode)) { 126 insNode = null; 127 } 128 } 129 } 130 for (Map.Entry<LdcInsnNode, LdcInsnNode> entry : replacement.entrySet()) { 131 m.instructions.set(entry.getKey(), entry.getValue()); 132 } 133 if (!replacement.isEmpty()) { 134 String[] types = {"java/lang/ClassNotFoundException"}; 135 data.removedInstructions += deleteExceptionHandlers(cch, data, cn, m, types); 136 137 } 138 return data; 139 } 140 141 private static int deleteExceptionHandlers(TypeResolver cch, Data data, 142 ClassNode cn, MethodNode m, String[] exTypes) 143 throws Exception { 144 int instructionsRemoved = 0; 145 for (String ex : exTypes) { 146 ControlFlow f = ControlFlow.createControlFlow(cn.name, m); 147 List<Integer> removed = new ArrayList<>(); 148 Set<ControlFlow.Block> blocksToRemove = new TreeSet<>(); 149 Iterator<TryCatchBlockNode> it = m.tryCatchBlocks.iterator(); 150 List<TryCatchBlockNode> tcbToRemove = new ArrayList<>(); 151 while (it.hasNext()) { 152 TryCatchBlockNode bn = it.next(); 153 if (bn.type == null 154 || !bn.type.equals(ex) // An empty block 155 || tcbToRemove.contains(bn)) { 156 continue; 157 } 158 // Check that the handler is still required 159 if (!Utils.canThrowCheckedException(cch, cn, m, bn)) { 160 // try to suppress it. 161 int block = m.instructions.indexOf(bn.handler); 162 ControlFlow.Block blockHandler = f.getBlock(block); 163 if (blockHandler == null) { 164 if (removed.contains(block)) { 165 continue; 166 } else { 167 throw new Exception(cn.name 168 + ", no block for handler " + block); 169 } 170 } 171 tcbToRemove.add(bn); 172 // Don't delete block if shared (eg: ClassNotFoundException | NoSuchMethodException | 173 Iterator<TryCatchBlockNode> it2 = m.tryCatchBlocks.iterator(); 174 boolean cont = false; 175 while (it2.hasNext()) { 176 TryCatchBlockNode bn2 = it2.next(); 177 if (bn2 != bn) { 178 if (bn2.start.equals(bn.start)) { 179 cont = true; 180 } 181 } 182 } 183 if (cont) { 184 continue; 185 } 186 // An handler is a root, blocks that are only reachable by it 187 // can be removed. 188 Set<ControlFlow.Block> blocks = f.getClosure(blockHandler); 189 StringBuilder sb = new StringBuilder(); 190 for (ControlFlow.Block b : blocks) { 191 sb.append(b).append("\n"); 192 removed.add(b.getFirstInstruction().getIndex()); 193 // Remove Exception handler if the associated block has been removed 194 for (TryCatchBlockNode tcb : m.tryCatchBlocks) { 195 if (tcb != bn) { 196 // An exception handler removed as a side effect. 197 if (b.isExceptionHandler() 198 && b.getFirstInstruction().getInstr() == tcb.handler) { 199 tcbToRemove.add(tcb); 200 } 201 } 202 } 203 } 204 blocksToRemove.addAll(blocks); 205 206 data.removedHandlers.put(ex, blocks); 207 208 } 209 } 210 211 m.tryCatchBlocks.removeAll(tcbToRemove); 212 213 if (!blocksToRemove.isEmpty()) { 214 for (ControlFlow.Block b : blocksToRemove) { 215 for (ControlFlow.InstructionNode ins : b.getInstructions()) { 216 if (ins.getInstr().getOpcode() > 0) { 217 instructionsRemoved += 1; 218 } 219 } 220 } 221 Utils.suppressBlocks(m, blocksToRemove); 222 } 223 } 224 return instructionsRemoved; 225 } 226 }