1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.callbackparams.support;
18
19 import java.util.ArrayList;
20 import java.util.HashMap;
21 import java.util.Iterator;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.Map.Entry;
25 import org.apache.bcel.Constants;
26 import org.apache.bcel.classfile.Attribute;
27 import org.apache.bcel.classfile.Code;
28 import org.apache.bcel.classfile.Method;
29 import org.apache.bcel.classfile.Unknown;
30 import org.apache.bcel.generic.ALOAD;
31 import org.apache.bcel.generic.ClassGen;
32 import org.apache.bcel.generic.ConstantPoolGen;
33 import org.apache.bcel.generic.DUP;
34 import org.apache.bcel.generic.DUP2;
35 import org.apache.bcel.generic.DUP2_X1;
36 import org.apache.bcel.generic.DUP2_X2;
37 import org.apache.bcel.generic.DUP_X1;
38 import org.apache.bcel.generic.DUP_X2;
39 import org.apache.bcel.generic.EmptyVisitor;
40 import org.apache.bcel.generic.INVOKESPECIAL;
41 import org.apache.bcel.generic.Instruction;
42 import org.apache.bcel.generic.InstructionConstants;
43 import org.apache.bcel.generic.InstructionFactory;
44 import org.apache.bcel.generic.InstructionHandle;
45 import org.apache.bcel.generic.InstructionList;
46 import org.apache.bcel.generic.MethodGen;
47 import org.apache.bcel.generic.SWAP;
48 import org.apache.bcel.generic.Type;
49 import org.apache.bcel.generic.Visitor;
50 import org.apache.bcel.util.ClassLoaderRepository;
51
52
53
54
55
56
57
58
59
60
61
62
63 public class ClassBytecodeBuilder {
64
65
66
67
68 private static class VisitorTaskPerformed extends RuntimeException {
69 private final static VisitorTaskPerformed instance =
70 new VisitorTaskPerformed();
71 }
72
73 private static final int defaultAccessModifiers =
74 ~Constants.ACC_PRIVATE
75 & ~Constants.ACC_PROTECTED & ~Constants.ACC_PUBLIC;
76
77 private final ClassGen cg;
78
79
80
81
82 private final Map methodMap = new HashMap();
83
84 private ClassBytecodeBuilder(final ClassGen cg) {
85 this.cg = cg;
86 final Method[] methods = cg.getMethods();
87 for (int i = 0 ; i < methods.length ; ++i) {
88 final Method m = methods[i];
89 methodMap.put(MethodHashKey.getHashKey(cg, m), m);
90 }
91 }
92
93 public static ClassBytecodeBuilder newInstance(Class templateClass) {
94 ClassLoaderRepository loaderRepo =
95 new ClassLoaderRepository(templateClass.getClassLoader());
96 try {
97 return new ClassBytecodeBuilder(new ClassGen(
98 loaderRepo.loadClass(templateClass)));
99 } catch (ClassNotFoundException ex) {
100 throw new Error(ex);
101 }
102 }
103
104 public InstructionFactory getInstructionFactory() {
105 return new InstructionFactory(cg);
106 }
107
108 private static void filterUnknownAttributes(Code codeAttribute) {
109 final Attribute[] subAttrs = codeAttribute.getAttributes();
110 List list = new ArrayList(subAttrs.length);
111 for (int i = 0; i < subAttrs.length; ++i) {
112 if (false == subAttrs[i] instanceof Unknown) {
113 list.add(subAttrs[i]);
114 }
115 }
116 if (subAttrs.length != list.size()) {
117 codeAttribute.setAttributes(
118 (Attribute[]) list.toArray(new Attribute[list.size()]));
119 }
120 }
121
122 private static Method filterUnknownCodeAttributes(Method m) {
123 Attribute[] attributes = m.getAttributes();
124 for (int i = 0; i < attributes.length; ++i) {
125 if (attributes[i] instanceof Code) {
126 filterUnknownAttributes((Code) attributes[i]);
127 }
128 }
129 return m;
130 }
131
132 public void prependMethod(Method m, InstructionList instructionList) {
133 MethodGen mg = new MethodGen(
134 m, cg.getClassName(), cg.getConstantPool());
135 mg.getInstructionList().insert(instructionList);
136 mg.setMaxStack();
137 replaceMethod(filterUnknownCodeAttributes(mg.getMethod()));
138 }
139
140 public Method getMethod(java.lang.reflect.Method m) {
141 return (Method) methodMap.get(MethodHashKey.getHashKey(m));
142 }
143
144 public ClassBytecodeBuilder replaceMethod(Method newMethod) {
145 MethodHashKey hashkey = MethodHashKey.getHashKey(cg, newMethod);
146 Method oldMethod = (Method) methodMap.get(hashkey);
147 if (null == oldMethod) {
148 throw new NullPointerException(
149 "There is no equivalent method to replace - " + newMethod);
150 }
151 cg.replaceMethod(oldMethod, newMethod);
152 methodMap.put(hashkey, newMethod);
153 return this;
154 }
155
156 public ClassBytecodeBuilder addMethod(Method m) {
157 cg.addMethod(m);
158 methodMap.put(MethodHashKey.getHashKey(cg, m), m);
159 return this;
160 }
161
162 public byte[] getByteCode() {
163 return cg.getJavaClass().getBytes();
164 }
165
166 public void setPublic() {
167 cg.setModifiers(cg.getModifiers() & defaultAccessModifiers
168 | Constants.ACC_PUBLIC);
169 Method[] methods = cg.getMethods();
170 for (int i = 0; i < methods.length; ++i) {
171 final Method m = methods[i];
172 if ("<init>".equals(m.getName())) {
173 m.setModifiers(m.getModifiers() & defaultAccessModifiers
174 | Constants.ACC_PUBLIC);
175 }
176 }
177 }
178
179 public void setNewSuperClass(String newSuperClassName) {
180 for (Iterator i = methodMap.values().iterator() ; i.hasNext() ;) {
181 final Method m = (Method) i.next();
182 if ("<init>".equals(m.getName())) {
183 modifyConstructor(m, newSuperClassName);
184 }
185 }
186 cg.setSuperclassName(newSuperClassName);
187 }
188
189 public void setupTransparentConstructors(
190 List constructorParamTypes, String newSuperClassName) {
191 Method defaultConstructor = findDefaultConstructor();
192 List instructionsToCopy = listInstructionsToKeep(defaultConstructor);
193
194
195 removeMethod(defaultConstructor);
196
197 for (Iterator i = constructorParamTypes.iterator() ; i.hasNext() ;) {
198 final Type[] paramTypes = (Type[]) i.next();
199 addNewTransparentConstructor(
200 paramTypes, newSuperClassName, instructionsToCopy);
201 }
202 cg.setSuperclassName(newSuperClassName);
203 }
204
205 private void addNewTransparentConstructor(final Type[] paramTypes,
206 String newSuperClassName, List instructionsToCopy) {
207 InstructionList il = new InstructionList();
208 MethodGen mg = new MethodGen(Constants.ACC_PUBLIC, Type.VOID,
209 paramTypes, null, "<init>", cg.getClassName(), il,
210 cg.getConstantPool());
211
212 il.append(InstructionConstants.ALOAD_0);
213 for (int i = 0 ; i < paramTypes.length ; ++i) {
214 il.append(InstructionFactory.createLoad(paramTypes[i], i + 1));
215 }
216 il.append(getInstructionFactory().createInvoke(newSuperClassName,
217 "<init>", Type.VOID, paramTypes, Constants.INVOKESPECIAL));
218 for (Iterator i = instructionsToCopy.iterator() ; i.hasNext() ;) {
219 il.append((Instruction) i.next());
220 }
221 mg.setMaxStack();
222 addMethod(mg.getMethod());
223 }
224
225
226
227
228 private Method findDefaultConstructor() {
229 for (Iterator i = methodMap.entrySet().iterator() ; i.hasNext() ;) {
230 final Map.Entry entry = (Entry) i.next();
231 final MethodHashKey key = (MethodHashKey) entry.getKey();
232 if ("<init>".equals(key.methodName)) {
233 return (Method) entry.getValue();
234 }
235 }
236 throw new Error("No constructor was found");
237 }
238
239 private void modifyConstructor(
240 Method method, final String newSuperClassName) {
241
242 final String className = cg.getClassName();
243 final Type thisType = Type.getType('L' + className + ';');
244 final ConstantPoolGen cpg = cg.getConstantPool();
245 final MethodGen mg = new MethodGen(method, className, cpg);
246
247 new org.apache.bcel.generic.EmptyVisitor() {
248
249 int stackDistanceToA0 = Integer.MAX_VALUE/2;
250 InstructionHandle ih;
251 boolean pending = true;
252
253
254 public void visitALOAD(ALOAD obj) {
255 if (0 == obj.getIndex()) {
256 stackDistanceToA0 = 0;
257 throw VisitorTaskPerformed.instance;
258 }
259 }
260
261
262 void donotModifyStackDistanceLessThan(int sizeOfUnchangedStackTop) {
263 if (stackDistanceToA0 < sizeOfUnchangedStackTop) {
264 throw VisitorTaskPerformed.instance;
265 }
266 }
267
268
269 public void visitDUP(DUP obj) {
270 donotModifyStackDistanceLessThan(1);
271 }
272
273
274 public void visitDUP2(DUP2 obj) {
275 donotModifyStackDistanceLessThan(2);
276 }
277
278
279 public void visitDUP2_X1(DUP2_X1 obj) {
280 donotModifyStackDistanceLessThan(3);
281 }
282
283
284 public void visitDUP2_X2(DUP2_X2 obj) {
285 donotModifyStackDistanceLessThan(4);
286 }
287
288
289 public void visitDUP_X1(DUP_X1 obj) {
290 donotModifyStackDistanceLessThan(2);
291 }
292
293
294 public void visitDUP_X2(DUP_X2 obj) {
295 donotModifyStackDistanceLessThan(3);
296 }
297
298
299 public void visitSWAP(SWAP obj) {
300 if (stackDistanceToA0 < 2) {
301 stackDistanceToA0 = 1 - stackDistanceToA0;
302 }
303 throw VisitorTaskPerformed.instance;
304 }
305
306
307 public void visitINVOKESPECIAL(INVOKESPECIAL obj) {
308
309
310
311
312
313 if (stackDistanceToA0 < obj.consumeStack(cpg)) {
314 assert "<init>".equals(obj.getName(cpg));
315 if (false == thisType.equals(obj.getReferenceType(cpg))) {
316 ih.setInstruction(getInstructionFactory().createInvoke(
317 newSuperClassName,
318 "<init>",
319 Type.VOID,
320 obj.getArgumentTypes(cpg),
321 Constants.INVOKESPECIAL));
322 mg.setMaxStack();
323 replaceMethod(mg.getMethod());
324 }
325 pending = false;
326 throw VisitorTaskPerformed.instance;
327 }
328 }
329
330 void replaceSuperConstructorInvocation() {
331 for (ih = mg.getInstructionList().getStart()
332 ; pending ; ih = ih.getNext()) {
333 try {
334 ih.accept(this);
335
336 Instruction instr = ih.getInstruction();
337 int produceStack = instr.produceStack(cpg);
338 if (0 < produceStack) {
339 stackDistanceToA0 += produceStack;
340 } else {
341 int consumeStack= instr.consumeStack(cpg);
342 if (0 < consumeStack) {
343 stackDistanceToA0 -= consumeStack;
344 }
345 }
346 } catch (VisitorTaskPerformed x) {
347 continue;
348 }
349 }
350 }
351
352
353
354
355 {replaceSuperConstructorInvocation();}
356 };
357 }
358
359 private List listInstructionsToKeep(Method defaultConstructor) {
360 List instructionsToKeepInNewConstructors = new ArrayList();
361 InstructionList il = new MethodGen(
362 defaultConstructor, cg.getClassName(), cg.getConstantPool()).getInstructionList();
363 Visitor superConstructorInvocationDetector = new EmptyVisitor() {
364
365 public void visitINVOKESPECIAL(INVOKESPECIAL obj) {
366 throw VisitorTaskPerformed.instance;
367 }
368 };
369 Iterator i = il.iterator();
370 try {
371 while (i.hasNext()) {
372 final InstructionHandle ih = (InstructionHandle) i.next();
373 ih.accept(superConstructorInvocationDetector);
374 }
375 throw new Error("Found no super() invocation in instruction list: " + il);
376 } catch (VisitorTaskPerformed x) {
377 while (i.hasNext()) {
378 final InstructionHandle ih = (InstructionHandle) i.next();
379 instructionsToKeepInNewConstructors.add(ih.getInstruction());
380 }
381 }
382 return instructionsToKeepInNewConstructors;
383 }
384
385 private void removeMethod(Method methodToRemove) {
386 cg.removeMethod(methodToRemove);
387 for (Iterator i = methodMap.entrySet().iterator() ; i.hasNext() ;) {
388 Map.Entry entry = (Entry) i.next();
389 if (methodToRemove == entry.getValue()) {
390 i.remove();
391 break;
392 }
393 }
394 }
395 }