1 /* 2 * Copyright (c) 2016, 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.util.Iterator; 28 import java.util.List; 29 import java.util.Map; 30 import java.util.Objects; 31 import java.util.stream.Collectors; 32 import jdk.tools.jlink.plugin.ModulePool; 33 import jdk.tools.jlink.plugin.Plugin.Category; 34 import jdk.internal.org.objectweb.asm.ClassReader; 35 import static jdk.internal.org.objectweb.asm.ClassReader.*; 36 import jdk.internal.org.objectweb.asm.ClassWriter; 37 import jdk.internal.org.objectweb.asm.Opcodes; 38 import jdk.internal.org.objectweb.asm.Type; 39 import jdk.internal.org.objectweb.asm.tree.AbstractInsnNode; 40 import jdk.internal.org.objectweb.asm.tree.ClassNode; 41 import jdk.internal.org.objectweb.asm.tree.InsnList; 42 import jdk.internal.org.objectweb.asm.tree.LabelNode; 43 import jdk.internal.org.objectweb.asm.tree.LdcInsnNode; 44 import jdk.internal.org.objectweb.asm.tree.LineNumberNode; 45 import jdk.internal.org.objectweb.asm.tree.MethodInsnNode; 46 import jdk.internal.org.objectweb.asm.tree.MethodNode; 47 import jdk.tools.jlink.plugin.ModuleEntry; 48 import jdk.tools.jlink.plugin.Plugin; 49 50 public final class ClassForNamePlugin implements Plugin { 51 public static final String NAME = "class-for-name"; 52 53 private static String binaryClassName(String path) { 54 return path.substring(path.indexOf('/', 1) + 1, 55 path.length() - ".class".length()); 56 } 57 58 private static int getAccess(ModuleEntry resource) { 59 ClassReader cr = new ClassReader(resource.getBytes()); 60 61 return cr.getAccess(); 62 } 63 64 private static String getPackage(String binaryName) { 65 int index = binaryName.lastIndexOf("/"); 66 67 return index == -1 ? "" : binaryName.substring(0, index); 68 } 69 70 private ModuleEntry transform(ModuleEntry resource, Map<String, ModuleEntry> classes) { 71 byte[] inBytes = resource.getBytes(); 72 ClassReader cr = new ClassReader(inBytes); 73 ClassNode cn = new ClassNode(); 74 cr.accept(cn, EXPAND_FRAMES); 75 List<MethodNode> ms = cn.methods; 76 boolean modified = false; 77 LdcInsnNode ldc = null; 78 79 String thisPackage = getPackage(binaryClassName(resource.getPath())); 80 81 for (MethodNode mn : ms) { 82 InsnList il = mn.instructions; 83 Iterator<AbstractInsnNode> it = il.iterator(); 84 85 while (it.hasNext()) { 86 AbstractInsnNode insn = it.next(); 87 88 if (insn instanceof LdcInsnNode) { 89 ldc = (LdcInsnNode)insn; 90 } else if (insn instanceof MethodInsnNode && ldc != null) { 91 MethodInsnNode min = (MethodInsnNode)insn; 92 93 if (min.getOpcode() == Opcodes.INVOKESTATIC && 94 min.name.equals("forName") && 95 min.owner.equals("java/lang/Class") && 96 min.desc.equals("(Ljava/lang/String;)Ljava/lang/Class;")) { 97 String ldcClassName = ldc.cst.toString(); 98 String thatClassName = ldcClassName.replaceAll("\\.", "/"); 99 ModuleEntry thatClass = classes.get(thatClassName); 100 101 if (thatClass != null) { 102 int thatAccess = getAccess(thatClass); 103 String thatPackage = getPackage(thatClassName); 104 105 if ((thatAccess & Opcodes.ACC_PRIVATE) != Opcodes.ACC_PRIVATE && 106 ((thatAccess & Opcodes.ACC_PUBLIC) == Opcodes.ACC_PUBLIC || 107 thisPackage.equals(thatPackage))) { 108 Type type = Type.getObjectType(thatClassName); 109 il.remove(ldc); 110 il.set(min, new LdcInsnNode(type)); 111 modified = true; 112 } 113 } 114 } 115 116 ldc = null; 117 } else if (!(insn instanceof LabelNode) && 118 !(insn instanceof LineNumberNode)) { 119 ldc = null; 120 } 121 122 } 123 } 124 125 if (modified) { 126 ClassWriter cw = new ClassWriter(cr, 0); 127 cn.accept(cw); 128 byte[] outBytes = cw.toByteArray(); 129 130 return resource.create(outBytes); 131 } 132 133 return resource; 134 } 135 136 @Override 137 public String getName() { 138 return NAME; 139 } 140 141 @Override 142 public void visit(ModulePool in, ModulePool out) { 143 Objects.requireNonNull(in); 144 Objects.requireNonNull(out); 145 Map<String, ModuleEntry> classes = in.entries() 146 .filter(resource -> resource != null && 147 resource.getPath().endsWith(".class") && 148 !resource.getPath().endsWith("/module-info.class")) 149 .collect(Collectors.toMap(resource -> binaryClassName(resource.getPath()), 150 resource -> resource)); 151 in.entries() 152 .filter(resource -> resource != null) 153 .forEach(resource -> { 154 String path = resource.getPath(); 155 156 if (path.endsWith(".class") && !path.endsWith("/module-info.class")) { 157 out.add(transform(resource, classes)); 158 } else { 159 out.add(resource); 160 } 161 }); 162 } 163 164 @Override 165 public Category getType() { 166 return Category.TRANSFORMER; 167 } 168 169 @Override 170 public boolean hasArguments() { 171 return false; 172 } 173 174 @Override 175 public String getDescription() { 176 return PluginsResourceBundle.getDescription(NAME); 177 } 178 179 @Override 180 public String getArgumentsDescription() { 181 return PluginsResourceBundle.getArgument(NAME); 182 } 183 184 @Override 185 public void configure(Map<String, String> config) { 186 187 } 188 }