1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.bcel.util;
20
21 import java.io.IOException;
22 import java.io.OutputStream;
23 import java.io.OutputStreamWriter;
24 import java.io.PrintWriter;
25 import java.nio.charset.StandardCharsets;
26
27 import org.apache.bcel.Const;
28 import org.apache.bcel.Repository;
29 import org.apache.bcel.classfile.ClassParser;
30 import org.apache.bcel.classfile.Code;
31 import org.apache.bcel.classfile.ConstantValue;
32 import org.apache.bcel.classfile.ExceptionTable;
33 import org.apache.bcel.classfile.Field;
34 import org.apache.bcel.classfile.JavaClass;
35 import org.apache.bcel.classfile.Method;
36 import org.apache.bcel.classfile.StackMap;
37 import org.apache.bcel.classfile.StackMapEntry;
38 import org.apache.bcel.classfile.StackMapType;
39 import org.apache.bcel.classfile.Utility;
40 import org.apache.bcel.generic.ArrayType;
41 import org.apache.bcel.generic.ConstantPoolGen;
42 import org.apache.bcel.generic.MethodGen;
43 import org.apache.bcel.generic.Type;
44 import org.apache.commons.lang3.ArrayUtils;
45 import org.apache.commons.lang3.StringUtils;
46
47
48
49
50
51
52 public class BCELifier extends org.apache.bcel.classfile.EmptyVisitor {
53
54
55
56
57 public enum FLAGS {
58 UNKNOWN, CLASS, METHOD,
59 }
60
61
62
63 private static final String BASE_PACKAGE = Const.class.getPackage().getName();
64 private static final String CONSTANT_PREFIX = Const.class.getSimpleName() + ".";
65
66
67 static JavaClass getJavaClass(final String name) throws ClassNotFoundException, IOException {
68 JavaClass javaClass;
69 if ((javaClass = Repository.lookupClass(name)) == null) {
70 javaClass = new ClassParser(name).parse();
71 }
72 return javaClass;
73 }
74
75
76
77
78 public static void main(final String[] argv) throws Exception {
79 if (argv.length != 1) {
80 System.out.println("Usage: BCELifier className");
81 System.out.println("\tThe class must exist on the classpath");
82 return;
83 }
84 final BCELifier bcelifier = new BCELifier(getJavaClass(argv[0]), System.out);
85 bcelifier.start();
86 }
87
88 static String printArgumentTypes(final Type[] argTypes) {
89 if (argTypes.length == 0) {
90 return "Type.NO_ARGS";
91 }
92 final StringBuilder args = new StringBuilder();
93 for (int i = 0; i < argTypes.length; i++) {
94 args.append(printType(argTypes[i]));
95 if (i < argTypes.length - 1) {
96 args.append(", ");
97 }
98 }
99 return "new Type[] { " + args.toString() + " }";
100 }
101
102 static String printFlags(final int flags) {
103 return printFlags(flags, FLAGS.UNKNOWN);
104 }
105
106
107
108
109
110
111
112
113
114 public static String printFlags(final int flags, final FLAGS location) {
115 if (flags == 0) {
116 return "0";
117 }
118 final StringBuilder buf = new StringBuilder();
119 for (int i = 0, pow = 1; pow <= Const.MAX_ACC_FLAG_I; i++) {
120 if ((flags & pow) != 0) {
121 if (pow == Const.ACC_SYNCHRONIZED && location == FLAGS.CLASS) {
122 buf.append(CONSTANT_PREFIX).append("ACC_SUPER | ");
123 } else if (pow == Const.ACC_VOLATILE && location == FLAGS.METHOD) {
124 buf.append(CONSTANT_PREFIX).append("ACC_BRIDGE | ");
125 } else if (pow == Const.ACC_TRANSIENT && location == FLAGS.METHOD) {
126 buf.append(CONSTANT_PREFIX).append("ACC_VARARGS | ");
127 } else if (i < Const.ACCESS_NAMES_LENGTH) {
128 buf.append(CONSTANT_PREFIX).append("ACC_").append(StringUtils.toRootUpperCase(Const.getAccessName(i))).append(" | ");
129 } else {
130 buf.append(String.format(CONSTANT_PREFIX + "ACC_BIT %x | ", pow));
131 }
132 }
133 pow <<= 1;
134 }
135 final String str = buf.toString();
136 return str.substring(0, str.length() - 3);
137 }
138
139 static String printType(final String signature) {
140 final Type type = Type.getType(signature);
141 final byte t = type.getType();
142 if (t <= Const.T_VOID) {
143 return "Type." + StringUtils.toRootUpperCase(Const.getTypeName(t));
144 }
145 if (type.toString().equals("java.lang.String")) {
146 return "Type.STRING";
147 }
148 if (type.toString().equals("java.lang.Object")) {
149 return "Type.OBJECT";
150 }
151 if (type.toString().equals("java.lang.StringBuffer")) {
152 return "Type.STRINGBUFFER";
153 }
154 if (type instanceof ArrayType) {
155 final ArrayType at = (ArrayType) type;
156 return "new ArrayType(" + printType(at.getBasicType()) + ", " + at.getDimensions() + ")";
157 }
158 return "new ObjectType(\"" + Utility.signatureToString(signature, false) + "\")";
159 }
160
161 static String printType(final Type type) {
162 return printType(type.getSignature());
163 }
164
165 private final JavaClass clazz;
166
167 private final PrintWriter printWriter;
168
169 private final ConstantPoolGen constantPoolGen;
170
171
172
173
174
175
176
177 public BCELifier(final JavaClass clazz, final OutputStream out) {
178 this.clazz = clazz;
179 this.printWriter = new PrintWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8), false);
180 this.constantPoolGen = new ConstantPoolGen(this.clazz.getConstantPool());
181 }
182
183 private void printCreate() {
184 printWriter.println(" public void create(OutputStream out) throws IOException {");
185 final Field[] fields = clazz.getFields();
186 if (fields.length > 0) {
187 printWriter.println(" createFields();");
188 }
189 final Method[] methods = clazz.getMethods();
190 for (int i = 0; i < methods.length; i++) {
191 printWriter.println(" createMethod_" + i + "();");
192 }
193 printWriter.println(" _cg.getJavaClass().dump(out);");
194 printWriter.println(" }");
195 printWriter.println();
196 }
197
198 private void printMain() {
199 final String className = clazz.getClassName();
200 printWriter.println(" public static void main(String[] args) throws Exception {");
201 printWriter.println(" " + className + "Creator creator = new " + className + "Creator();");
202 printWriter.println(" creator.create(new FileOutputStream(\"" + className + ".class\"));");
203 printWriter.println(" }");
204 }
205
206
207
208
209 public void start() {
210 visitJavaClass(clazz);
211 printWriter.flush();
212 }
213
214 @Override
215 public void visitField(final Field field) {
216 printWriter.println();
217 printWriter.println(
218 " field = new FieldGen(" + printFlags(field.getAccessFlags()) + ", " + printType(field.getSignature()) + ", \"" + field.getName() + "\", _cp);");
219 final ConstantValue cv = field.getConstantValue();
220 if (cv != null) {
221 printWriter.print(" field.setInitValue(");
222 if (field.getType() == Type.CHAR) {
223 printWriter.print("(char)");
224 }
225 if (field.getType() == Type.SHORT) {
226 printWriter.print("(short)");
227 }
228 if (field.getType() == Type.BYTE) {
229 printWriter.print("(byte)");
230 }
231 printWriter.print(cv);
232 if (field.getType() == Type.LONG) {
233 printWriter.print("L");
234 }
235 if (field.getType() == Type.FLOAT) {
236 printWriter.print("F");
237 }
238 if (field.getType() == Type.DOUBLE) {
239 printWriter.print("D");
240 }
241 printWriter.println(");");
242 }
243 printWriter.println(" _cg.addField(field.getField());");
244 }
245
246 @Override
247 public void visitJavaClass(final JavaClass clazz) {
248 String className = clazz.getClassName();
249 final String superName = clazz.getSuperclassName();
250 final String packageName = clazz.getPackageName();
251 final String inter = Utility.printArray(clazz.getInterfaceNames(), false, true);
252 if (StringUtils.isNotEmpty(packageName)) {
253 className = className.substring(packageName.length() + 1);
254 printWriter.println("package " + packageName + ";");
255 printWriter.println();
256 }
257 printWriter.println("import " + BASE_PACKAGE + ".generic.*;");
258 printWriter.println("import " + BASE_PACKAGE + ".classfile.*;");
259 printWriter.println("import " + BASE_PACKAGE + ".*;");
260 printWriter.println("import java.io.*;");
261 printWriter.println();
262 printWriter.println("public class " + className + "Creator {");
263 printWriter.println(" private InstructionFactory _factory;");
264 printWriter.println(" private ConstantPoolGen _cp;");
265 printWriter.println(" private ClassGen _cg;");
266 printWriter.println();
267 printWriter.println(" public " + className + "Creator() {");
268 printWriter.println(" _cg = new ClassGen(\"" + (packageName.isEmpty() ? className : packageName + "." + className) + "\", \"" + superName
269 + "\", \"" + clazz.getSourceFileName() + "\", " + printFlags(clazz.getAccessFlags(), FLAGS.CLASS) + ", " + "new String[] { " + inter + " });");
270 printWriter.println(" _cg.setMajor(" + clazz.getMajor() + ");");
271 printWriter.println(" _cg.setMinor(" + clazz.getMinor() + ");");
272 printWriter.println();
273 printWriter.println(" _cp = _cg.getConstantPool();");
274 printWriter.println(" _factory = new InstructionFactory(_cg, _cp);");
275 printWriter.println(" }");
276 printWriter.println();
277 printCreate();
278 final Field[] fields = clazz.getFields();
279 if (fields.length > 0) {
280 printWriter.println(" private void createFields() {");
281 printWriter.println(" FieldGen field;");
282 for (final Field field : fields) {
283 field.accept(this);
284 }
285 printWriter.println(" }");
286 printWriter.println();
287 }
288 final Method[] methods = clazz.getMethods();
289 for (int i = 0; i < methods.length; i++) {
290 printWriter.println(" private void createMethod_" + i + "() {");
291 methods[i].accept(this);
292 printWriter.println(" }");
293 printWriter.println();
294 }
295 printMain();
296 printWriter.println("}");
297 }
298
299 @Override
300 public void visitMethod(final Method method) {
301 final MethodGen mg = new MethodGen(method, clazz.getClassName(), constantPoolGen);
302 printWriter.println(" InstructionList il = new InstructionList();");
303 printWriter.println(" MethodGen method = new MethodGen(" + printFlags(method.getAccessFlags(), FLAGS.METHOD) + ", " + printType(mg.getReturnType())
304 + ", " + printArgumentTypes(mg.getArgumentTypes()) + ", new String[] { " + Utility.printArray(mg.getArgumentNames(), false, true) + " }, \""
305 + method.getName() + "\", \"" + clazz.getClassName() + "\", il, _cp);");
306 final ExceptionTable exceptionTable = method.getExceptionTable();
307 if (exceptionTable != null) {
308 final String[] exceptionNames = exceptionTable.getExceptionNames();
309 for (final String exceptionName : exceptionNames) {
310 printWriter.print(" method.addException(\"");
311 printWriter.print(exceptionName);
312 printWriter.println("\");");
313 }
314 }
315 final Code code = method.getCode();
316 if (code != null) {
317 final StackMap stackMap = code.getStackMap();
318 if (stackMap != null) {
319 stackMap.accept(this);
320 }
321 }
322 printWriter.println();
323 final BCELFactory factory = new BCELFactory(mg, printWriter);
324 factory.start();
325 printWriter.println(" method.setMaxStack();");
326 printWriter.println(" method.setMaxLocals();");
327 printWriter.println(" _cg.addMethod(method.getMethod());");
328 printWriter.println(" il.dispose();");
329 }
330
331 @Override
332 public void visitStackMap(final StackMap stackMap) {
333 super.visitStackMap(stackMap);
334 printWriter.print(" method.addCodeAttribute(");
335 printWriter.print("new StackMap(_cp.addUtf8(\"");
336 printWriter.print(stackMap.getName());
337 printWriter.print("\"), ");
338 printWriter.print(stackMap.getLength());
339 printWriter.print(", ");
340 printWriter.print("new StackMapEntry[] {");
341 final StackMapEntry[] table = stackMap.getStackMap();
342 for (int i = 0; i < table.length; i++) {
343 table[i].accept(this);
344 if (i < table.length - 1) {
345 printWriter.print(", ");
346 } else {
347 printWriter.print(" }");
348 }
349 }
350 printWriter.print(", _cp.getConstantPool())");
351 printWriter.println(");");
352 }
353
354 @Override
355 public void visitStackMapEntry(final StackMapEntry stackMapEntry) {
356 super.visitStackMapEntry(stackMapEntry);
357 printWriter.print("new StackMapEntry(");
358 printWriter.print(stackMapEntry.getFrameType());
359 printWriter.print(", ");
360 printWriter.print(stackMapEntry.getByteCodeOffset());
361 printWriter.print(", ");
362 visitStackMapTypeArray(stackMapEntry.getTypesOfLocals());
363 printWriter.print(", ");
364 visitStackMapTypeArray(stackMapEntry.getTypesOfStackItems());
365 printWriter.print(", _cp.getConstantPool())");
366 }
367
368
369
370
371
372
373 @Override
374 public void visitStackMapType(final StackMapType stackMapType) {
375 super.visitStackMapType(stackMapType);
376 printWriter.print("new StackMapType((byte)");
377 printWriter.print(stackMapType.getType());
378 printWriter.print(", ");
379 if (stackMapType.hasIndex()) {
380 printWriter.print("_cp.addClass(\"");
381 printWriter.print(stackMapType.getClassName());
382 printWriter.print("\")");
383 } else {
384 printWriter.print("-1");
385 }
386 printWriter.print(", _cp.getConstantPool())");
387 }
388
389 private void visitStackMapTypeArray(final StackMapType[] types) {
390 if (ArrayUtils.isEmpty(types)) {
391 printWriter.print("null");
392 } else {
393 printWriter.print("new StackMapType[] {");
394 for (int i = 0; i < types.length; i++) {
395 types[i].accept(this);
396 if (i < types.length - 1) {
397 printWriter.print(", ");
398 } else {
399 printWriter.print(" }");
400 }
401 }
402 }
403 }
404 }