1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.callbackparams.internal.template;
18
19 import java.lang.reflect.Field;
20 import java.lang.reflect.InvocationTargetException;
21 import java.lang.reflect.Method;
22 import java.util.ArrayList;
23 import java.util.HashMap;
24 import java.util.Iterator;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Set;
28 import org.apache.bcel.Constants;
29 import org.apache.bcel.generic.ArrayType;
30 import org.apache.bcel.generic.ClassGen;
31 import org.apache.bcel.generic.ConstantPoolGen;
32 import org.apache.bcel.generic.IFNULL;
33 import org.apache.bcel.generic.IfInstruction;
34 import org.apache.bcel.generic.InstructionFactory;
35 import org.apache.bcel.generic.InstructionList;
36 import org.apache.bcel.generic.MethodGen;
37 import org.apache.bcel.generic.Type;
38 import org.callbackparams.AdaptiveRule;
39 import org.callbackparams.AdaptiveRule.TestRun;
40 import org.callbackparams.support.MethodHashKey;
41
42
43
44
45 public class AdaptiveRulesPackager
46 implements java.lang.reflect.InvocationHandler, Constants {
47
48
49
50
51
52
53 public static class VisitorParentGenerator {
54 private final String interfaceName;
55 private Type enclosingType;
56 private List
57 private List
58
59 public VisitorParentGenerator(Class enclosingClass,
60 List
61 this.enclosingType = Type.getType(enclosingClass);
62 this.adaptiveRuleFields = adaptiveRuleFields;
63 this.testMethods = testMethods;
64 this.interfaceName =
65 defineInterfaceNameOfNestedVisitorParent(enclosingClass);
66 }
67
68 public String getInterfaceName() {
69 return interfaceName;
70 }
71
72 public byte[] generateByteCode() {
73 return defineNestedVisitorParent(interfaceName, enclosingType,
74 adaptiveRuleFields, testMethods);
75 }
76 }
77
78
79
80
81
82
83
84
85 public interface Visitor {}
86
87 private static final Map
88 private static final List
89 private static final Class[] proxiedInterfaces = {Visitor.class};
90 private static final Class testClass;
91
92 private final TestrunCallbacks testInstance;
93
94 public AdaptiveRulesPackager(TestrunCallbacks testInstance) {
95 this.testInstance = testInstance;
96 }
97
98 static {
99 Class tempTestClass = null;
100 final Method[] visitorMethods = Visitor.class.getMethods();
101
102 Map
103 for (int i = 0; i < visitorMethods.length; ++i) {
104 final Method m = visitorMethods[i];
105 final Class[] params = m.getParameterTypes();
106
107 if (0 == params.length) {
108 tempTestClass = m.getReturnType();
109
110 } else if (params[0].isArray()) {
111 try {
112 Field f = params[0].getComponentType()
113 .getDeclaredField(m.getName());
114 try {
115 f.setAccessible(true);
116 } catch (SecurityException ignoreAndHopeForTheBest) {}
117 adaptiveRuleFields.add(f);
118 } catch (Exception x) {
119 throw new Error(x.getMessage());
120 }
121
122 } else {
123 MethodHashKey key = new MethodHashKey(
124 params[0].getName(),
125 m.getName(),
126 asTestMethodSignature(m.getReturnType(), params));
127 hashedVisitorMethods.put(key, m);
128 }
129 }
130 testClass = tempTestClass;
131 while (false == hashedVisitorMethods.isEmpty()) {
132 final Method[] testClassMethods =
133 tempTestClass.getDeclaredMethods();
134 for (int i = 0; i < testClassMethods.length; ++i) {
135 final Method m = testClassMethods[i];
136 final Method visitorMethod = (Method)
137 hashedVisitorMethods.remove(MethodHashKey.getHashKey(m));
138 if (null != visitorMethod) {
139 try {
140 m.setAccessible(true);
141 } catch (SecurityException ignoreAndHopeForTheBest) {}
142 testMethodMap.put(
143 MethodHashKey.getHashKey(visitorMethod), m);
144 }
145 }
146 tempTestClass = tempTestClass.getSuperclass();
147 }
148 }
149
150 private static String asTestMethodSignature(
151 Class returnType, Class[] visitorMethodParams) {
152 final Type[] argTypes = new Type[visitorMethodParams.length - 1];
153 for (int i = 0; i < argTypes.length; ++i) {
154 argTypes[i] = Type.getType(visitorMethodParams[i + 1]);
155 }
156 return Type.getMethodSignature(Type.getType(returnType), argTypes);
157 }
158
159 private static String defineInterfaceNameOfNestedVisitorParent(
160 Class enclosingClass) {
161 String interfNamePrefix = enclosingClass.getName() + "$VisitorParent_";
162 String interfName = null;
163 try {
164 for (int suffix = 0; true; ++suffix) {
165 interfName = interfNamePrefix + suffix;
166 enclosingClass.getClassLoader().loadClass(interfName);
167 }
168 } catch (ClassNotFoundException x) {
169 return interfName;
170 }
171 }
172
173 private static byte[] defineNestedVisitorParent(
174 String interfName, Type enclosingType,
175 List
176 ClassGen cg = new ClassGen(interfName, Object.class.getName(),
177 "<generated>", ACC_PUBLIC | ACC_INTERFACE | ACC_ABSTRACT, null);
178 ConstantPoolGen cp = cg.getConstantPool();
179 if (false == adaptiveRules.isEmpty()) {
180 Type[] arg_types = {new ArrayType(enclosingType, 1)};
181 for (Iterator
182 final Field ruleField = (Field) i.next();
183 cg.addMethod(new MethodGen(ACC_PUBLIC | ACC_ABSTRACT,
184 Type.VOID, arg_types, null,
185 ruleField.getName(), interfName, null, cp).getMethod());
186 }
187 }
188 if (false == testMethods.isEmpty()) {
189 VisitorMethodArgtypesFactory argsFactory =
190 new VisitorMethodArgtypesFactory(enclosingType);
191 for (Iterator
192 final Method m = (Method) i.next();
193 MethodGen mg = new MethodGen(ACC_PUBLIC | ACC_ABSTRACT,
194 Type.getType(m.getReturnType()),
195 argsFactory.asVisitorMethodArgtypes(m.getParameterTypes()),
196 null, m.getName(), interfName, null, cp);
197 if (0 < m.getExceptionTypes().length) {
198 mg.addException(Throwable.class.getName());
199 }
200 cg.addMethod(mg.getMethod());
201 }
202 }
203 return cg.getJavaClass().getBytes();
204 }
205
206 public static InstructionList defineTestMethodDetourCall(
207 InstructionFactory factory, Method m) {
208 final InstructionList il = new InstructionList();
209
210
211 il.append(factory.ALOAD_0);
212 il.append(factory.createInvoke(
213 AdaptiveRulesPackager.class.getName(),
214 "fetchTestrunVisitor",
215 Type.getType(Visitor.class),
216 new Type[] {Type.getType(TestrunCallbacks.class)},
217 INVOKESTATIC));
218
219
220
221
222
223
224 il.append(factory.DUP);
225
226
227
228
229 IfInstruction ifNull = new IFNULL(null);
230 il.append(ifNull);
231
232
233
234
235 Type declaringClassType = Type.getType(m.getDeclaringClass());
236 Type[] argTypes = new VisitorMethodArgtypesFactory(declaringClassType)
237 .asVisitorMethodArgtypes(m.getParameterTypes());
238 il.append(factory.ACONST_NULL);
239 for (int i = 1; i < argTypes.length; ++i) {
240 il.append(factory.createLoad(argTypes[i], i));
241 }
242 il.append(factory.createInvoke(
243 Visitor.class.getName(), m.getName(),
244 Type.getType(m.getReturnType()), argTypes,
245 INVOKEINTERFACE));
246
247
248
249
250
251 il.append(factory.createReturn(Type.getType(m.getReturnType())));
252
253
254
255
256
257
258
259 ifNull.setTarget(il.append(factory.POP));
260
261 return il;
262 }
263
264 public static Visitor fetchTestrunVisitor(TestrunCallbacks testInstance) {
265 if (testInstance.currentlyWrappedWithinAdaptiveRules
266 || false == testClass.isInstance(testInstance)
267 || adaptiveRuleFields.isEmpty()) {
268 return null;
269 } else {
270 return (Visitor) java.lang.reflect.Proxy.newProxyInstance(
271 Visitor.class.getClassLoader(),
272 proxiedInterfaces,
273 new AdaptiveRulesPackager(testInstance));
274 }
275 }
276
277 private TestRun wrapTestrunWithRule(
278 final AdaptiveRule rule, final TestRun testRun) {
279 return new TestRun() {
280 public Object executeTestMethod() throws Throwable {
281 return rule.evaluate(testRun);
282 }
283 };
284 }
285
286 private Object executeWithAdaptiveRules(TestRun testRun) throws Throwable {
287 for (Iterator i = adaptiveRuleFields.iterator(); i.hasNext();) {
288 final Field ruleField = (Field) i.next();
289 AdaptiveRule rule = (AdaptiveRule) ruleField.get(testInstance);
290 if (null != rule) {
291 testRun = wrapTestrunWithRule(rule, testRun);
292 }
293 }
294 return testRun.executeTestMethod();
295 }
296
297 public Object invoke(Object proxy,
298 final Method m, final Object[] args)
299 throws Throwable {
300 testInstance.currentlyWrappedWithinAdaptiveRules = true;
301 try {
302 return executeWithAdaptiveRules(new AdaptiveRule.TestRun() {
303 public Object executeTestMethod() throws Throwable {
304 try {
305 Method testMethod = (Method)
306 testMethodMap.get(MethodHashKey.getHashKey(m));
307 return testMethod.invoke(
308 testInstance, asTestMethodArgs(args));
309 } catch (InvocationTargetException ite) {
310 throw ite.getTargetException();
311 }
312 }
313 });
314 } finally {
315 testInstance.currentlyWrappedWithinAdaptiveRules = false;
316 }
317 }
318
319 private static Object[] asTestMethodArgs(final Object[] args) {
320 if (1 == args.length) {
321 return null;
322 } else {
323 final Object[] testMethodArgs = new Object[args.length - 1];
324 for (int i = 0; i < testMethodArgs.length; ++i) {
325 testMethodArgs[i] = args[i + 1];
326 }
327 return testMethodArgs;
328 }
329 }
330
331 public static byte[] generateRebytedVisitor(
332 Class testClass, Set superInterfaceNames) {
333 String[] superInterfaces = (String[]) superInterfaceNames.toArray(
334 new String[superInterfaceNames.size()]);
335 ClassGen cg = new ClassGen(
336 Visitor.class.getName(), Object.class.getName(),
337 "<regenerated>", ACC_PUBLIC | ACC_INTERFACE | ACC_ABSTRACT,
338 superInterfaces);
339 MethodGen mg = new MethodGen(ACC_PUBLIC | ACC_ABSTRACT,
340 Type.getType(testClass), new Type[]{}, null, "testClass",
341 Visitor.class.getName(), null, cg.getConstantPool());
342 cg.addMethod(mg.getMethod());
343 return cg.getJavaClass().getBytes();
344 }
345
346 private static class VisitorMethodArgtypesFactory {
347 final Type enclosingType;
348
349 VisitorMethodArgtypesFactory(Type enclosingType) {
350 this.enclosingType = enclosingType;
351 }
352
353 Type[] asVisitorMethodArgtypes(final Class[] testMethodParams) {
354 final Type[] argTypes = new Type[testMethodParams.length + 1];
355 argTypes[0] = enclosingType;
356 for (int i = 0; i < testMethodParams.length; ++i) {
357 argTypes[i + 1] = Type.getType(testMethodParams[i]);
358 }
359 return argTypes;
360 }
361 }
362 }