001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 * 017 */ 018package org.apache.bcel.util; 019 020import java.io.IOException; 021import java.io.OutputStream; 022import java.io.PrintWriter; 023import java.util.Locale; 024 025import org.apache.bcel.Const; 026import org.apache.bcel.Repository; 027import org.apache.bcel.classfile.ClassParser; 028import org.apache.bcel.classfile.ConstantValue; 029import org.apache.bcel.classfile.Field; 030import org.apache.bcel.classfile.JavaClass; 031import org.apache.bcel.classfile.Method; 032import org.apache.bcel.classfile.Utility; 033import org.apache.bcel.generic.ArrayType; 034import org.apache.bcel.generic.ConstantPoolGen; 035import org.apache.bcel.generic.MethodGen; 036import org.apache.bcel.generic.Type; 037 038/** 039 * This class takes a given JavaClass object and converts it to a 040 * Java program that creates that very class using BCEL. This 041 * gives new users of BCEL a useful example showing how things 042 * are done with BCEL. It does not cover all features of BCEL, 043 * but tries to mimic hand-written code as close as possible. 044 * 045 */ 046public class BCELifier extends org.apache.bcel.classfile.EmptyVisitor { 047 048 /** 049 * Enum corresponding to flag source. 050 */ 051 public enum FLAGS { 052 UNKNOWN, 053 CLASS, 054 METHOD, 055 } 056 057 // The base package name for imports; assumes Const is at the top level 058 // N.B we use the class so renames will be detected by the compiler/IDE 059 private static final String BASE_PACKAGE = Const.class.getPackage().getName(); 060 private static final String CONSTANT_PREFIX = Const.class.getSimpleName()+"."; 061 062 private final JavaClass _clazz; 063 private final PrintWriter _out; 064 private final ConstantPoolGen _cp; 065 066 /** @param clazz Java class to "decompile" 067 * @param out where to output Java program 068 */ 069 public BCELifier(final JavaClass clazz, final OutputStream out) { 070 _clazz = clazz; 071 _out = new PrintWriter(out); 072 _cp = new ConstantPoolGen(_clazz.getConstantPool()); 073 } 074 075 076 /** Start Java code generation 077 */ 078 public void start() { 079 visitJavaClass(_clazz); 080 _out.flush(); 081 } 082 083 084 @Override 085 public void visitJavaClass( final JavaClass clazz ) { 086 String class_name = clazz.getClassName(); 087 final String super_name = clazz.getSuperclassName(); 088 final String package_name = clazz.getPackageName(); 089 final String inter = Utility.printArray(clazz.getInterfaceNames(), false, true); 090 if (!"".equals(package_name)) { 091 class_name = class_name.substring(package_name.length() + 1); 092 _out.println("package " + package_name + ";"); 093 _out.println(); 094 } 095 _out.println("import " + BASE_PACKAGE + ".generic.*;"); 096 _out.println("import " + BASE_PACKAGE + ".classfile.*;"); 097 _out.println("import " + BASE_PACKAGE + ".*;"); 098 _out.println("import java.io.*;"); 099 _out.println(); 100 _out.println("public class " + class_name + "Creator {"); 101 _out.println(" private InstructionFactory _factory;"); 102 _out.println(" private ConstantPoolGen _cp;"); 103 _out.println(" private ClassGen _cg;"); 104 _out.println(); 105 _out.println(" public " + class_name + "Creator() {"); 106 _out.println(" _cg = new ClassGen(\"" 107 + (("".equals(package_name)) ? class_name : package_name + "." + class_name) 108 + "\", \"" + super_name + "\", " + "\"" + clazz.getSourceFileName() + "\", " 109 + printFlags(clazz.getAccessFlags(), FLAGS.CLASS) + ", " 110 + "new String[] { " + inter + " });"); 111 _out.println(" _cg.setMajor(" + clazz.getMajor() +");"); 112 _out.println(" _cg.setMinor(" + clazz.getMinor() +");"); 113 _out.println(); 114 _out.println(" _cp = _cg.getConstantPool();"); 115 _out.println(" _factory = new InstructionFactory(_cg, _cp);"); 116 _out.println(" }"); 117 _out.println(); 118 printCreate(); 119 final Field[] fields = clazz.getFields(); 120 if (fields.length > 0) { 121 _out.println(" private void createFields() {"); 122 _out.println(" FieldGen field;"); 123 for (final Field field : fields) { 124 field.accept(this); 125 } 126 _out.println(" }"); 127 _out.println(); 128 } 129 final Method[] methods = clazz.getMethods(); 130 for (int i = 0; i < methods.length; i++) { 131 _out.println(" private void createMethod_" + i + "() {"); 132 methods[i].accept(this); 133 _out.println(" }"); 134 _out.println(); 135 } 136 printMain(); 137 _out.println("}"); 138 } 139 140 141 private void printCreate() { 142 _out.println(" public void create(OutputStream out) throws IOException {"); 143 final Field[] fields = _clazz.getFields(); 144 if (fields.length > 0) { 145 _out.println(" createFields();"); 146 } 147 final Method[] methods = _clazz.getMethods(); 148 for (int i = 0; i < methods.length; i++) { 149 _out.println(" createMethod_" + i + "();"); 150 } 151 _out.println(" _cg.getJavaClass().dump(out);"); 152 _out.println(" }"); 153 _out.println(); 154 } 155 156 157 private void printMain() { 158 final String class_name = _clazz.getClassName(); 159 _out.println(" public static void main(String[] args) throws Exception {"); 160 _out.println(" " + class_name + "Creator creator = new " + class_name + "Creator();"); 161 _out.println(" creator.create(new FileOutputStream(\"" + class_name + ".class\"));"); 162 _out.println(" }"); 163 } 164 165 166 @Override 167 public void visitField( final Field field ) { 168 _out.println(); 169 _out.println(" field = new FieldGen(" + printFlags(field.getAccessFlags()) + ", " 170 + printType(field.getSignature()) + ", \"" + field.getName() + "\", _cp);"); 171 final ConstantValue cv = field.getConstantValue(); 172 if (cv != null) { 173 final String value = cv.toString(); 174 _out.println(" field.setInitValue(" + value + ")"); 175 } 176 _out.println(" _cg.addField(field.getField());"); 177 } 178 179 180 @Override 181 public void visitMethod( final Method method ) { 182 final MethodGen mg = new MethodGen(method, _clazz.getClassName(), _cp); 183 _out.println(" InstructionList il = new InstructionList();"); 184 _out.println(" MethodGen method = new MethodGen(" 185 + printFlags(method.getAccessFlags(), FLAGS.METHOD) + ", " 186 + printType(mg.getReturnType()) + ", " 187 + printArgumentTypes(mg.getArgumentTypes()) + ", " 188 + "new String[] { " + Utility.printArray(mg.getArgumentNames(), false, true) 189 + " }, \"" + method.getName() + "\", \"" + _clazz.getClassName() + "\", il, _cp);"); 190 _out.println(); 191 final BCELFactory factory = new BCELFactory(mg, _out); 192 factory.start(); 193 _out.println(" method.setMaxStack();"); 194 _out.println(" method.setMaxLocals();"); 195 _out.println(" _cg.addMethod(method.getMethod());"); 196 _out.println(" il.dispose();"); 197 } 198 199 200 static String printFlags( final int flags ) { 201 return printFlags(flags, FLAGS.UNKNOWN); 202 } 203 204 /** 205 * Return a string with the flag settings 206 * @param flags the flags field to interpret 207 * @param location the item type 208 * @return the formatted string 209 * @since 6.0 made public 210 */ 211 public static String printFlags( final int flags, final FLAGS location ) { 212 if (flags == 0) { 213 return "0"; 214 } 215 final StringBuilder buf = new StringBuilder(); 216 for (int i = 0, pow = 1; pow <= Const.MAX_ACC_FLAG_I; i++) { 217 if ((flags & pow) != 0) { 218 if ((pow == Const.ACC_SYNCHRONIZED) && (location == FLAGS.CLASS)) { 219 buf.append(CONSTANT_PREFIX+"ACC_SUPER | "); 220 } else if ((pow == Const.ACC_VOLATILE) && (location == FLAGS.METHOD)) { 221 buf.append(CONSTANT_PREFIX+"ACC_BRIDGE | "); 222 } else if ((pow == Const.ACC_TRANSIENT) && (location == FLAGS.METHOD)) { 223 buf.append(CONSTANT_PREFIX+"ACC_VARARGS | "); 224 } else { 225 if (i < Const.ACCESS_NAMES_LENGTH) { 226 buf.append(CONSTANT_PREFIX+"ACC_").append(Const.getAccessName(i).toUpperCase(Locale.ENGLISH)).append( " | "); 227 } else { 228 buf.append(String.format (CONSTANT_PREFIX+"ACC_BIT %x | ", pow)); 229 } 230 } 231 } 232 pow <<= 1; 233 } 234 final String str = buf.toString(); 235 return str.substring(0, str.length() - 3); 236 } 237 238 239 static String printArgumentTypes( final Type[] arg_types ) { 240 if (arg_types.length == 0) { 241 return "Type.NO_ARGS"; 242 } 243 final StringBuilder args = new StringBuilder(); 244 for (int i = 0; i < arg_types.length; i++) { 245 args.append(printType(arg_types[i])); 246 if (i < arg_types.length - 1) { 247 args.append(", "); 248 } 249 } 250 return "new Type[] { " + args.toString() + " }"; 251 } 252 253 254 static String printType( final Type type ) { 255 return printType(type.getSignature()); 256 } 257 258 259 static String printType( final String signature ) { 260 final Type type = Type.getType(signature); 261 final byte t = type.getType(); 262 if (t <= Const.T_VOID) { 263 return "Type." + Const.getTypeName(t).toUpperCase(Locale.ENGLISH); 264 } else if (type.toString().equals("java.lang.String")) { 265 return "Type.STRING"; 266 } else if (type.toString().equals("java.lang.Object")) { 267 return "Type.OBJECT"; 268 } else if (type.toString().equals("java.lang.StringBuffer")) { 269 return "Type.STRINGBUFFER"; 270 } else if (type instanceof ArrayType) { 271 final ArrayType at = (ArrayType) type; 272 return "new ArrayType(" + printType(at.getBasicType()) + ", " + at.getDimensions() 273 + ")"; 274 } else { 275 return "new ObjectType(\"" + Utility.signatureToString(signature, false) + "\")"; 276 } 277 } 278 279 280 /** Default main method 281 */ 282 public static void main( final String[] argv ) throws Exception { 283 if (argv.length != 1) { 284 System.out.println("Usage: BCELifier classname"); 285 System.out.println("\tThe class must exist on the classpath"); 286 return; 287 } 288 final JavaClass java_class = getJavaClass(argv[0]); 289 final BCELifier bcelifier = new BCELifier(java_class, System.out); 290 bcelifier.start(); 291 } 292 293 294 // Needs to be accessible from unit test code 295 static JavaClass getJavaClass(final String name) throws ClassNotFoundException, IOException { 296 JavaClass java_class; 297 if ((java_class = Repository.lookupClass(name)) == null) { 298 java_class = new ClassParser(name).parse(); // May throw IOException 299 } 300 return java_class; 301 } 302}