1 /*
   2  * reserved comment block
   3  * DO NOT REMOVE OR ALTER!
   4  */
   5 /*
   6  * Licensed to the Apache Software Foundation (ASF) under one or more
   7  * contributor license agreements.  See the NOTICE file distributed with
   8  * this work for additional information regarding copyright ownership.
   9  * The ASF licenses this file to You under the Apache License, Version 2.0
  10  * (the "License"); you may not use this file except in compliance with
  11  * the License.  You may obtain a copy of the License at
  12  *
  13  *      http://www.apache.org/licenses/LICENSE-2.0
  14  *
  15  * Unless required by applicable law or agreed to in writing, software
  16  * distributed under the License is distributed on an "AS IS" BASIS,
  17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18  * See the License for the specific language governing permissions and
  19  * limitations under the License.
  20  */
  21 
  22 package com.sun.org.apache.bcel.internal.util;
  23 
  24 import java.io.IOException;
  25 import java.io.OutputStream;
  26 import java.io.PrintWriter;
  27 import java.util.Locale;
  28 
  29 import com.sun.org.apache.bcel.internal.Const;
  30 import com.sun.org.apache.bcel.internal.Repository;
  31 import com.sun.org.apache.bcel.internal.classfile.ClassParser;
  32 import com.sun.org.apache.bcel.internal.classfile.ConstantValue;
  33 import com.sun.org.apache.bcel.internal.classfile.Field;
  34 import com.sun.org.apache.bcel.internal.classfile.JavaClass;
  35 import com.sun.org.apache.bcel.internal.classfile.Method;
  36 import com.sun.org.apache.bcel.internal.classfile.Utility;
  37 import com.sun.org.apache.bcel.internal.generic.ArrayType;
  38 import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
  39 import com.sun.org.apache.bcel.internal.generic.MethodGen;
  40 import com.sun.org.apache.bcel.internal.generic.Type;
  41 
  42 /**
  43  * This class takes a given JavaClass object and converts it to a
  44  * Java program that creates that very class using BCEL. This
  45  * gives new users of BCEL a useful example showing how things
  46  * are done with BCEL. It does not cover all features of BCEL,
  47  * but tries to mimic hand-written code as close as possible.
  48  *
  49  * @version $Id$
  50  */
  51 public class BCELifier extends com.sun.org.apache.bcel.internal.classfile.EmptyVisitor {
  52 
  53     /**
  54      * Enum corresponding to flag source.
  55      */
  56     public enum FLAGS {
  57         UNKNOWN,
  58         CLASS,
  59         METHOD,
  60     }
  61 
  62     // The base package name for imports; assumes Const is at the top level
  63     // N.B we use the class so renames will be detected by the compiler/IDE
  64     private static final String BASE_PACKAGE = Const.class.getPackage().getName();
  65     private static final String CONSTANT_PREFIX = Const.class.getSimpleName()+".";
  66 
  67     private final JavaClass _clazz;
  68     private final PrintWriter _out;
  69     private final ConstantPoolGen _cp;
  70 
  71     /** @param clazz Java class to "decompile"
  72      * @param out where to output Java program
  73      */
  74     public BCELifier(final JavaClass clazz, final OutputStream out) {
  75         _clazz = clazz;
  76         _out = new PrintWriter(out);
  77         _cp = new ConstantPoolGen(_clazz.getConstantPool());
  78     }
  79 
  80 
  81     /** Start Java code generation
  82      */
  83     public void start() {
  84         visitJavaClass(_clazz);
  85         _out.flush();
  86     }
  87 
  88 
  89     @Override
  90     public void visitJavaClass( final JavaClass clazz ) {
  91         String class_name = clazz.getClassName();
  92         final String super_name = clazz.getSuperclassName();
  93         final String package_name = clazz.getPackageName();
  94         final String inter = Utility.printArray(clazz.getInterfaceNames(), false, true);
  95         if (!"".equals(package_name)) {
  96             class_name = class_name.substring(package_name.length() + 1);
  97             _out.println("package " + package_name + ";");
  98             _out.println();
  99         }
 100         _out.println("import " + BASE_PACKAGE + ".generic.*;");
 101         _out.println("import " + BASE_PACKAGE + ".classfile.*;");
 102         _out.println("import " + BASE_PACKAGE + ".*;");
 103         _out.println("import java.io.*;");
 104         _out.println();
 105         _out.println("public class " + class_name + "Creator {");
 106         _out.println("  private InstructionFactory _factory;");
 107         _out.println("  private ConstantPoolGen    _cp;");
 108         _out.println("  private ClassGen           _cg;");
 109         _out.println();
 110         _out.println("  public " + class_name + "Creator() {");
 111         _out.println("    _cg = new ClassGen(\""
 112                 + (("".equals(package_name)) ? class_name : package_name + "." + class_name)
 113                 + "\", \"" + super_name + "\", " + "\"" + clazz.getSourceFileName() + "\", "
 114                 + printFlags(clazz.getAccessFlags(), FLAGS.CLASS) + ", "
 115                 + "new String[] { " + inter + " });");
 116         _out.println();
 117         _out.println("    _cp = _cg.getConstantPool();");
 118         _out.println("    _factory = new InstructionFactory(_cg, _cp);");
 119         _out.println("  }");
 120         _out.println();
 121         printCreate();
 122         final Field[] fields = clazz.getFields();
 123         if (fields.length > 0) {
 124             _out.println("  private void createFields() {");
 125             _out.println("    FieldGen field;");
 126             for (final Field field : fields) {
 127                 field.accept(this);
 128             }
 129             _out.println("  }");
 130             _out.println();
 131         }
 132         final Method[] methods = clazz.getMethods();
 133         for (int i = 0; i < methods.length; i++) {
 134             _out.println("  private void createMethod_" + i + "() {");
 135             methods[i].accept(this);
 136             _out.println("  }");
 137             _out.println();
 138         }
 139         printMain();
 140         _out.println("}");
 141     }
 142 
 143 
 144     private void printCreate() {
 145         _out.println("  public void create(OutputStream out) throws IOException {");
 146         final Field[] fields = _clazz.getFields();
 147         if (fields.length > 0) {
 148             _out.println("    createFields();");
 149         }
 150         final Method[] methods = _clazz.getMethods();
 151         for (int i = 0; i < methods.length; i++) {
 152             _out.println("    createMethod_" + i + "();");
 153         }
 154         _out.println("    _cg.getJavaClass().dump(out);");
 155         _out.println("  }");
 156         _out.println();
 157     }
 158 
 159 
 160     private void printMain() {
 161         final String class_name = _clazz.getClassName();
 162         _out.println("  public static void main(String[] args) throws Exception {");
 163         _out.println("    " + class_name + "Creator creator = new " + class_name + "Creator();");
 164         _out.println("    creator.create(new FileOutputStream(\"" + class_name + ".class\"));");
 165         _out.println("  }");
 166     }
 167 
 168 
 169     @Override
 170     public void visitField( final Field field ) {
 171         _out.println();
 172         _out.println("    field = new FieldGen(" + printFlags(field.getAccessFlags()) + ", "
 173                 + printType(field.getSignature()) + ", \"" + field.getName() + "\", _cp);");
 174         final ConstantValue cv = field.getConstantValue();
 175         if (cv != null) {
 176             final String value = cv.toString();
 177             _out.println("    field.setInitValue(" + value + ")");
 178         }
 179         _out.println("    _cg.addField(field.getField());");
 180     }
 181 
 182 
 183     @Override
 184     public void visitMethod( final Method method ) {
 185         final MethodGen mg = new MethodGen(method, _clazz.getClassName(), _cp);
 186         _out.println("    InstructionList il = new InstructionList();");
 187         _out.println("    MethodGen method = new MethodGen("
 188                 + printFlags(method.getAccessFlags(), FLAGS.METHOD) + ", "
 189                 + printType(mg.getReturnType()) + ", "
 190                 + printArgumentTypes(mg.getArgumentTypes()) + ", "
 191                 + "new String[] { " + Utility.printArray(mg.getArgumentNames(), false, true)
 192                 + " }, \"" + method.getName() + "\", \"" + _clazz.getClassName() + "\", il, _cp);");
 193         _out.println();
 194         final BCELFactory factory = new BCELFactory(mg, _out);
 195         factory.start();
 196         _out.println("    method.setMaxStack();");
 197         _out.println("    method.setMaxLocals();");
 198         _out.println("    _cg.addMethod(method.getMethod());");
 199         _out.println("    il.dispose();");
 200     }
 201 
 202 
 203     static String printFlags( final int flags ) {
 204         return printFlags(flags, FLAGS.UNKNOWN);
 205     }
 206 
 207     /**
 208      * Return a string with the flag settings
 209      * @param flags the flags field to interpret
 210      * @param location the item type
 211      * @return the formatted string
 212      * @since 6.0 made public
 213      */
 214     public static String printFlags( final int flags, final FLAGS location ) {
 215         if (flags == 0) {
 216             return "0";
 217         }
 218         final StringBuilder buf = new StringBuilder();
 219         for (int i = 0, pow = 1; pow <= Const.MAX_ACC_FLAG; i++) {
 220             if ((flags & pow) != 0) {
 221                 if ((pow == Const.ACC_SYNCHRONIZED) && (location == FLAGS.CLASS)) {
 222                     buf.append(CONSTANT_PREFIX+"ACC_SUPER | ");
 223                 } else if ((pow == Const.ACC_VOLATILE) && (location == FLAGS.METHOD)) {
 224                     buf.append(CONSTANT_PREFIX+"ACC_BRIDGE | ");
 225                 } else if ((pow == Const.ACC_TRANSIENT) && (location == FLAGS.METHOD)) {
 226                     buf.append(CONSTANT_PREFIX+"ACC_VARARGS | ");
 227                 } else {
 228                     if (i < Const.ACCESS_NAMES_LENGTH) {
 229                         buf.append(CONSTANT_PREFIX+"ACC_")
 230                                 .append(Const.getAccessName(i).toUpperCase(Locale.ENGLISH))
 231                                 .append( " | ");
 232                     } else {
 233                         buf.append(String.format (CONSTANT_PREFIX+"ACC_BIT %x | ", pow));
 234                     }
 235                 }
 236             }
 237             pow <<= 1;
 238         }
 239         final String str = buf.toString();
 240         return str.substring(0, str.length() - 3);
 241     }
 242 
 243 
 244     static String printArgumentTypes( final Type[] arg_types ) {
 245         if (arg_types.length == 0) {
 246             return "Type.NO_ARGS";
 247         }
 248         final StringBuilder args = new StringBuilder();
 249         for (int i = 0; i < arg_types.length; i++) {
 250             args.append(printType(arg_types[i]));
 251             if (i < arg_types.length - 1) {
 252                 args.append(", ");
 253             }
 254         }
 255         return "new Type[] { " + args.toString() + " }";
 256     }
 257 
 258 
 259     static String printType( final Type type ) {
 260         return printType(type.getSignature());
 261     }
 262 
 263 
 264     static String printType( final String signature ) {
 265         final Type type = Type.getType(signature);
 266         final byte t = type.getType();
 267         if (t <= Const.T_VOID) {
 268             return "Type." + Const.getTypeName(t).toUpperCase(Locale.ENGLISH);
 269         } else if (type.toString().equals("java.lang.String")) {
 270             return "Type.STRING";
 271         } else if (type.toString().equals("java.lang.Object")) {
 272             return "Type.OBJECT";
 273         } else if (type.toString().equals("java.lang.StringBuffer")) {
 274             return "Type.STRINGBUFFER";
 275         } else if (type instanceof ArrayType) {
 276             final ArrayType at = (ArrayType) type;
 277             return "new ArrayType(" + printType(at.getBasicType()) + ", " + at.getDimensions()
 278                     + ")";
 279         } else {
 280             return "new ObjectType(\"" + Utility.signatureToString(signature, false) + "\")";
 281         }
 282     }
 283 
 284 
 285     /** Default main method
 286      */
 287     public static void main( final String[] argv ) throws Exception {
 288         if (argv.length != 1) {
 289             System.out.println("Usage: BCELifier classname");
 290             System.out.println("\tThe class must exist on the classpath");
 291             return;
 292         }
 293         final JavaClass java_class = getJavaClass(argv[0]);
 294         final BCELifier bcelifier = new BCELifier(java_class, System.out);
 295         bcelifier.start();
 296     }
 297 
 298 
 299     // Needs to be accessible from unit test code
 300     static JavaClass getJavaClass(final String name) throws ClassNotFoundException, IOException {
 301         JavaClass java_class;
 302         if ((java_class = Repository.lookupClass(name)) == null) {
 303             java_class = new ClassParser(name).parse(); // May throw IOException
 304         }
 305         return java_class;
 306     }
 307 }