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