1 /* 2 * Copyright (c) 1997, 2012, 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 26 package com.sun.xml.internal.bind.v2.bytecode; 27 28 import java.io.ByteArrayOutputStream; 29 import java.io.DataInputStream; 30 import java.io.DataOutputStream; 31 import java.io.IOException; 32 import java.io.InputStream; 33 import java.util.logging.Level; 34 import java.util.logging.Logger; 35 36 import com.sun.xml.internal.bind.Util; 37 38 /** 39 * Replaces a few constant pool tokens from a class "template" and then loads it into the VM. 40 * 41 * @author Kohsuke Kawaguchi 42 */ 43 public final class ClassTailor { 44 45 private ClassTailor() {} // no instanciation please 46 47 private static final Logger logger = Util.getClassLogger(); 48 49 /** 50 * Returns the class name in the JVM format (such as "java/lang/String") 51 */ 52 public static String toVMClassName( Class c ) { 53 assert !c.isPrimitive(); 54 if(c.isArray()) 55 // I have no idea why it is designed like this, but javap says so. 56 return toVMTypeName(c); 57 return c.getName().replace('.','/'); 58 } 59 60 public static String toVMTypeName( Class c ) { 61 if(c.isArray()) { 62 // TODO: study how an array type is encoded. 63 return '['+toVMTypeName(c.getComponentType()); 64 } 65 if(c.isPrimitive()) { 66 if(c==Boolean.TYPE) return "Z"; 67 if(c==Character.TYPE) return "C"; 68 if(c==Byte.TYPE) return "B"; 69 if(c==Double.TYPE) return "D"; 70 if(c==Float.TYPE) return "F"; 71 if(c==Integer.TYPE) return "I"; 72 if(c==Long.TYPE) return "J"; 73 if(c==Short.TYPE) return "S"; 74 75 throw new IllegalArgumentException(c.getName()); 76 } 77 return 'L'+c.getName().replace('.','/')+';'; 78 } 79 80 81 82 public static byte[] tailor( Class templateClass, String newClassName, String... replacements ) { 83 String vmname = toVMClassName(templateClass); 84 return tailor( 85 SecureLoader.getClassClassLoader(templateClass).getResourceAsStream(vmname+".class"), 86 vmname, newClassName, replacements ); 87 } 88 89 90 /** 91 * Customizes a class file by replacing constant pools. 92 * 93 * @param image 94 * The image of the template class. 95 * @param replacements 96 * A list of pair of strings that specify the substitution 97 * {@code String[]{search_0, replace_0, search_1, replace_1, ..., search_n, replace_n }} 98 * 99 * The search strings found in the constant pool will be replaced by the corresponding 100 * replacement string. 101 */ 102 public static byte[] tailor( InputStream image, String templateClassName, String newClassName, String... replacements ) { 103 DataInputStream in = new DataInputStream(image); 104 105 try { 106 ByteArrayOutputStream baos = new ByteArrayOutputStream(1024); 107 DataOutputStream out = new DataOutputStream(baos); 108 109 // skip until the constant pool count 110 long l = in.readLong(); 111 out.writeLong(l); 112 113 // read the constant pool size 114 short count = in.readShort(); 115 out.writeShort(count); 116 117 // replace constant pools 118 for( int i=0; i<count; i++ ) { 119 byte tag = in.readByte(); 120 out.writeByte(tag); 121 switch(tag) { 122 case 0: 123 // this isn't described in the spec, 124 // but class files often seem to have this '0' tag. 125 // we can apparently just ignore it, but not sure 126 // what this really means. 127 break; 128 129 case 1: // CONSTANT_UTF8 130 { 131 String value = in.readUTF(); 132 if(value.equals(templateClassName)) 133 value = newClassName; 134 else { 135 for( int j=0; j<replacements.length; j+=2 ) 136 if(value.equals(replacements[j])) { 137 value = replacements[j+1]; 138 break; 139 } 140 } 141 out.writeUTF(value); 142 } 143 break; 144 145 case 3: // CONSTANT_Integer 146 case 4: // CONSTANT_Float 147 out.writeInt(in.readInt()); 148 break; 149 150 case 5: // CONSTANT_Long 151 case 6: // CONSTANT_Double 152 i++; // doubles and longs take two entries 153 out.writeLong(in.readLong()); 154 break; 155 156 case 7: // CONSTANT_Class 157 case 8: // CONSTANT_String 158 out.writeShort(in.readShort()); 159 break; 160 161 case 9: // CONSTANT_Fieldref 162 case 10: // CONSTANT_Methodref 163 case 11: // CONSTANT_InterfaceMethodref 164 case 12: // CONSTANT_NameAndType 165 out.writeInt(in.readInt()); 166 break; 167 168 default: 169 throw new IllegalArgumentException("Unknown constant type "+tag); 170 } 171 } 172 173 // then copy the rest 174 byte[] buf = new byte[512]; 175 int len; 176 while((len=in.read(buf))>0) 177 out.write(buf,0,len); 178 179 in.close(); 180 out.close(); 181 182 // by now we got the properly tailored class file image 183 return baos.toByteArray(); 184 185 } catch( IOException e ) { 186 // never happen 187 logger.log(Level.WARNING,"failed to tailor",e); 188 return null; 189 } 190 } 191 }