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