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 * @version $Id: BCELifier.java 1806200 2017-08-25 16:33:06Z ggregory $ 046 */ 047public class BCELifier extends org.apache.bcel.classfile.EmptyVisitor { 048 049 /** 050 * Enum corresponding to flag source. 051 */ 052 public enum FLAGS { 053 UNKNOWN, 054 CLASS, 055 METHOD, 056 } 057 058 // The base package name for imports; assumes Const is at the top level 059 // N.B we use the class so renames will be detected by the compiler/IDE 060 private static final String BASE_PACKAGE = Const.class.getPackage().getName(); 061 private static final String CONSTANT_PREFIX = Const.class.getSimpleName()+"."; 062 063 private final JavaClass _clazz; 064 private final PrintWriter _out; 065 private final ConstantPoolGen _cp; 066 067 /** @param clazz Java class to "decompile" 068 * @param out where to output Java program 069 */ 070 public BCELifier(final JavaClass clazz, final OutputStream out) { 071 _clazz = clazz; 072 _out = new PrintWriter(out); 073 _cp = new ConstantPoolGen(_clazz.getConstantPool()); 074 } 075 076 077 /** Start Java code generation 078 */ 079 public void start() { 080 visitJavaClass(_clazz); 081 _out.flush(); 082 } 083 084 085 @Override 086 public void visitJavaClass( final JavaClass clazz ) { 087 String class_name = clazz.getClassName(); 088 final String super_name = clazz.getSuperclassName(); 089 final String package_name = clazz.getPackageName(); 090 final String inter = Utility.printArray(clazz.getInterfaceNames(), false, true); 091 if (!"".equals(package_name)) { 092 class_name = class_name.substring(package_name.length() + 1); 093 _out.println("package " + package_name + ";"); 094 _out.println(); 095 } 096 _out.println("import " + BASE_PACKAGE + ".generic.*;"); 097 _out.println("import " + BASE_PACKAGE + ".classfile.*;"); 098 _out.println("import " + BASE_PACKAGE + ".*;"); 099 _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 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 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 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 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 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 */ 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 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 253 static String printType( final Type type ) { 254 return printType(type.getSignature()); 255 } 256 257 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 */ 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 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}