325N/A/*
325N/A * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
325N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
325N/A *
325N/A * This code is free software; you can redistribute it and/or modify it
325N/A * under the terms of the GNU General Public License version 2 only, as
325N/A * published by the Free Software Foundation. Oracle designates this
325N/A * particular file as subject to the "Classpath" exception as provided
325N/A * by Oracle in the LICENSE file that accompanied this code.
325N/A *
325N/A * This code is distributed in the hope that it will be useful, but WITHOUT
325N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
325N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
325N/A * version 2 for more details (a copy is included in the LICENSE file that
325N/A * accompanied this code).
325N/A *
325N/A * You should have received a copy of the GNU General Public License version
325N/A * 2 along with this work; if not, write to the Free Software Foundation,
325N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
325N/A *
325N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
325N/A * or visit www.oracle.com if you need additional information or have any
325N/A * questions.
325N/A */
325N/A
325N/Apackage com.sun.xml.internal.bind.v2.bytecode;
325N/A
325N/Aimport java.io.ByteArrayOutputStream;
325N/Aimport java.io.DataInputStream;
325N/Aimport java.io.DataOutputStream;
325N/Aimport java.io.IOException;
325N/Aimport java.io.InputStream;
325N/Aimport java.util.logging.Level;
325N/Aimport java.util.logging.Logger;
325N/A
325N/Aimport com.sun.xml.internal.bind.Util;
325N/A
325N/A/**
325N/A * Replaces a few constant pool tokens from a class "template" and then loads it into the VM.
325N/A *
325N/A * @author Kohsuke Kawaguchi
325N/A */
325N/Apublic final class ClassTailor {
325N/A
325N/A private ClassTailor() {} // no instanciation please
325N/A
325N/A private static final Logger logger = Util.getClassLogger();
325N/A
325N/A /**
325N/A * Returns the class name in the JVM format (such as "java/lang/String")
325N/A */
325N/A public static String toVMClassName( Class c ) {
325N/A assert !c.isPrimitive();
325N/A if(c.isArray())
325N/A // I have no idea why it is designed like this, but javap says so.
325N/A return toVMTypeName(c);
325N/A return c.getName().replace('.','/');
325N/A }
325N/A
325N/A public static String toVMTypeName( Class c ) {
325N/A if(c.isArray()) {
325N/A // TODO: study how an array type is encoded.
325N/A return '['+toVMTypeName(c.getComponentType());
325N/A }
325N/A if(c.isPrimitive()) {
325N/A if(c==Boolean.TYPE) return "Z";
325N/A if(c==Character.TYPE) return "C";
325N/A if(c==Byte.TYPE) return "B";
325N/A if(c==Double.TYPE) return "D";
325N/A if(c==Float.TYPE) return "F";
325N/A if(c==Integer.TYPE) return "I";
325N/A if(c==Long.TYPE) return "J";
325N/A if(c==Short.TYPE) return "S";
325N/A
325N/A throw new IllegalArgumentException(c.getName());
325N/A }
325N/A return 'L'+c.getName().replace('.','/')+';';
325N/A }
325N/A
325N/A
325N/A
325N/A public static byte[] tailor( Class templateClass, String newClassName, String... replacements ) {
325N/A String vmname = toVMClassName(templateClass);
325N/A return tailor(
325N/A templateClass.getClassLoader().getResourceAsStream(vmname+".class"),
325N/A vmname, newClassName, replacements );
325N/A }
325N/A
325N/A
325N/A /**
325N/A * Customizes a class file by replacing constant pools.
325N/A *
325N/A * @param image
325N/A * The image of the template class.
325N/A * @param replacements
325N/A * A list of pair of strings that specify the substitution
325N/A * {@code String[]{search_0, replace_0, search_1, replace_1, ..., search_n, replace_n }}
325N/A *
325N/A * The search strings found in the constant pool will be replaced by the corresponding
325N/A * replacement string.
325N/A */
325N/A public static byte[] tailor( InputStream image, String templateClassName, String newClassName, String... replacements ) {
325N/A DataInputStream in = new DataInputStream(image);
325N/A
325N/A try {
325N/A ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
325N/A DataOutputStream out = new DataOutputStream(baos);
325N/A
325N/A // skip until the constant pool count
325N/A long l = in.readLong();
325N/A out.writeLong(l);
325N/A
325N/A // read the constant pool size
325N/A short count = in.readShort();
325N/A out.writeShort(count);
325N/A
325N/A // replace constant pools
325N/A for( int i=0; i<count; i++ ) {
325N/A byte tag = in.readByte();
325N/A out.writeByte(tag);
325N/A switch(tag) {
325N/A case 0:
325N/A // this isn't described in the spec,
325N/A // but class files often seem to have this '0' tag.
325N/A // we can apparently just ignore it, but not sure
325N/A // what this really means.
325N/A break;
325N/A
325N/A case 1: // CONSTANT_UTF8
325N/A {
325N/A String value = in.readUTF();
325N/A if(value.equals(templateClassName))
325N/A value = newClassName;
325N/A else {
325N/A for( int j=0; j<replacements.length; j+=2 )
325N/A if(value.equals(replacements[j])) {
325N/A value = replacements[j+1];
325N/A break;
325N/A }
325N/A }
325N/A out.writeUTF(value);
325N/A }
325N/A break;
325N/A
325N/A case 3: // CONSTANT_Integer
325N/A case 4: // CONSTANT_Float
325N/A out.writeInt(in.readInt());
325N/A break;
325N/A
325N/A case 5: // CONSTANT_Long
325N/A case 6: // CONSTANT_Double
325N/A i++; // doubles and longs take two entries
325N/A out.writeLong(in.readLong());
325N/A break;
325N/A
325N/A case 7: // CONSTANT_Class
325N/A case 8: // CONSTANT_String
325N/A out.writeShort(in.readShort());
325N/A break;
325N/A
325N/A case 9: // CONSTANT_Fieldref
325N/A case 10: // CONSTANT_Methodref
325N/A case 11: // CONSTANT_InterfaceMethodref
325N/A case 12: // CONSTANT_NameAndType
325N/A out.writeInt(in.readInt());
325N/A break;
325N/A
325N/A default:
325N/A throw new IllegalArgumentException("Unknown constant type "+tag);
325N/A }
325N/A }
325N/A
325N/A // then copy the rest
325N/A byte[] buf = new byte[512];
325N/A int len;
325N/A while((len=in.read(buf))>0)
325N/A out.write(buf,0,len);
325N/A
325N/A in.close();
325N/A out.close();
325N/A
325N/A // by now we got the properly tailored class file image
325N/A return baos.toByteArray();
325N/A
325N/A } catch( IOException e ) {
325N/A // never happen
325N/A logger.log(Level.WARNING,"failed to tailor",e);
325N/A return null;
325N/A }
325N/A }
325N/A}