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 }