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