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