View Javadoc
1 /*
2 Copyright (c) 2003, Laurent Caillette.
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without modifica-
6 tion, are permitted provided that the following conditions are met:
7
8 1. Redistributions of source code must retain the above copyright notice,
9 this list of conditions and the following disclaimer.
10
11 2. Redistributions in binary form must reproduce the above copyright notice,
12 this list of conditions and the following disclaimer in the documentation
13 and/or other materials provided with the distribution.
14
15 3. The end-user documentation included with the redistribution, if any, must
16 include the following acknowledgment: "This product includes software
17 written by Laurent Caillette."
18 Alternately, this acknowledgment may appear in the software itself, if
19 and wherever such third-party acknowledgments normally appear.
20
21 4. The name "Laurent Caillette" must not be used to endorse or
22 promote products derived from this software without
23 prior written permission. For written permission, please contact
24 laurent.caillette@laposte.net
25
26 5. Products derived from this software may not be called
27 "Laurent Caillette", nor may "Laurent Caillette" appear
28 in their name, without prior written permission of the
29 author.
30
31 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
32 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
33 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
34 AUTHOR (LAURENT CAILLETTE) BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
36 TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
37 OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
38 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
39 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
40 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41 */
42 package tweed.system.interceptor;
43 import java.io.IOException;
44 import java.io.OutputStream;
45 import java.lang.reflect.Method;
46 import org.apache.bcel.Constants;
47 import org.apache.bcel.generic.ArrayType;
48 import org.apache.bcel.generic.ClassGen;
49 import org.apache.bcel.generic.ConstantPoolGen;
50 import org.apache.bcel.generic.FieldGen;
51 import org.apache.bcel.generic.InstructionConstants;
52 import org.apache.bcel.generic.InstructionFactory;
53 import org.apache.bcel.generic.InstructionHandle;
54 import org.apache.bcel.generic.InstructionList;
55 import org.apache.bcel.generic.MethodGen;
56 import org.apache.bcel.generic.ObjectType;
57 import org.apache.bcel.generic.PUSH;
58 import org.apache.bcel.generic.Type;
59
60 /***
61 * Build the bytecode of a class deriving the subject class, with calls to
62 * a {@link InterceptionHandler} when
63 * {@link Override#getShouldIntercept override}
64 * indicate to do so.
65 *
66 * @author Laurent Caillette
67 * @version $Id$
68 */
69 public class CodeBuilder implements Constants {
70
71 // Ugly comments in the source code are supposed to be kept,
72 // they can be useful for debugging. Commented code is the one
73 // generated by the BCELIfier ran on a hard-coded interceptor class
74 // to get inspiration from.
75
76 private InstructionFactory _factory;
77 private ConstantPoolGen _cp;
78 private ClassGen _cg;
79
80 private final Class subjectClass ;
81 private final Override[] overrides ;
82 private final String interceptorClassName ;
83 private final String subjectClassName ;
84
85 public final static String createInterceptorClassName( Class subjectClass ) {
86 return subjectClass.getName() + "$Interceptor" ;
87 }
88
89 public CodeBuilder(
90 Class subjectClass,
91 Override[] overrides
92 ) {
93
94 this.subjectClass = subjectClass ;
95 subjectClassName = subjectClass.getName() ;
96 this.interceptorClassName = createInterceptorClassName( subjectClass ) ;
97 this.overrides = overrides ;
98
99 // _cg = new ClassGen(
100 // "tweed.system.interceptor.MyBeanInterceptor",
101 // "tweed.system.interceptor.MyBean",
102 // "MyBeanInterceptor.java",
103 // ACC_PUBLIC | ACC_SUPER,
104 // new String[] { }
105 // ) ;
106 _cg = new ClassGen(
107 interceptorClassName,
108 subjectClassName,
109 interceptorClassName + ".java",
110 ACC_PUBLIC | ACC_SUPER,
111 new String[] { }
112 ) ;
113 _cp = _cg.getConstantPool();
114 _factory = new InstructionFactory(_cg, _cp);
115 }
116
117 public void create(OutputStream out) throws IOException {
118
119 createFields();
120 createConstructor();
121
122 for( int i = 0 ; i < overrides.length ; i++ ) {
123 Override override = overrides[ i ] ;
124 if( override.getShouldIntercept() ) {
125 createInterceptedMethod( override.getMethod(), i ) ;
126 } else {
127 createNonInterceptedMethod( override.getMethod() ) ;
128 }
129 }
130
131 _cg.getJavaClass().dump(out);
132 }
133
134 private void createFields() {
135 FieldGen field;
136
137 field = new FieldGen(
138 ACC_PRIVATE | ACC_FINAL,
139 // new ObjectType( "tweed.system.interceptor.MySimpleBean" ),
140 new ObjectType( subjectClassName ),
141 "subject",
142 _cp
143 ) ;
144 _cg.addField( field.getField() ) ;
145
146 field = new FieldGen(
147 ACC_PRIVATE | ACC_FINAL,
148 // new ObjectType( "tweed.system.interceptor.InterceptionHandler" ),
149 new ObjectType( InterceptionHandler.class.getName() ),
150 "interceptor",
151 _cp
152 ) ;
153 _cg.addField( field.getField() ) ;
154
155 field = new FieldGen(
156 ACC_PUBLIC | ACC_FINAL,
157 new ArrayType(
158 // new ObjectType( "tweed.system.interceptor.MethodDescriptor" ),
159 new ObjectType( MethodDescriptor.class.getName() ),
160 1
161 ),
162 "methodDescriptors",
163 _cp
164 ) ;
165 _cg.addField( field.getField() ) ;
166 }
167
168
169 private void createConstructor() {
170
171 InstructionList il = new InstructionList() ;
172 MethodGen method = new MethodGen(
173 ACC_PUBLIC,
174 Type.VOID,
175 new Type[] {
176 // new ObjectType( "tweed.system.interceptor.MySimpleBean" ),
177 new ObjectType( subjectClassName ),
178 // new ObjectType( "tweed.system.interceptor.InterceptionHandler" )
179 new ObjectType( InterceptionHandler.class.getName() )
180 },
181 new String[] { "arg0", "arg1" },
182 "<init>",
183 // "tweed.system.interceptor.MySimpleBean$Interceptor",
184 interceptorClassName,
185 il,
186 _cp
187 ) ;
188
189 InstructionHandle ih_0 = il.append(
190 _factory.createLoad( Type.OBJECT, 0 ) ) ;
191 il.append(
192 _factory.createInvoke(
193 // "tweed.system.interceptor.MySimpleBean",
194 subjectClassName,
195 "<init>",
196 Type.VOID,
197 Type.NO_ARGS,
198 Constants.INVOKESPECIAL
199 )
200 ) ;
201 InstructionHandle ih_4 =
202 il.append( _factory.createLoad( Type.OBJECT, 0 ) ) ;
203 // il.append( new PUSH( _cp, 2 ) ) ;
204 il.append( new PUSH( _cp, overrides.length ) ) ;
205 il.append( _factory.createNewArray(
206 new ObjectType(
207 // "tweed.system.interceptor.MethodDescriptor"
208 MethodDescriptor.class.getName()
209 ),
210 ( short ) 1
211 ) ) ;
212 il.append(
213 _factory.createFieldAccess(
214 // "tweed.system.interceptor.MySimpleBean$Interceptor",
215 interceptorClassName,
216 "methodDescriptors",
217 new ArrayType(
218 // new ObjectType( "tweed.system.interceptor.MethodDescriptor" ),
219 new ObjectType( MethodDescriptor.class.getName() ),
220 1
221 ),
222 Constants.PUTFIELD
223 )
224 ) ;
225 InstructionHandle ih_12 = il.append(
226 _factory.createLoad( Type.OBJECT, 0 ) ) ;
227 il.append( _factory.createLoad( Type.OBJECT, 1 ) ) ;
228 il.append( _factory.createFieldAccess(
229 // "tweed.system.interceptor.MySimpleBean$Interceptor",
230 interceptorClassName,
231 "subject",
232 // new ObjectType( "tweed.system.interceptor.MySimpleBean" ),
233 new ObjectType( subjectClassName ),
234 Constants.PUTFIELD
235 ) ) ;
236 InstructionHandle ih_17 = il.append( _factory.createLoad(
237 Type.OBJECT, 0 ) ) ;
238 il.append( _factory.createLoad( Type.OBJECT, 2 ) ) ;
239 il.append(
240 _factory.createFieldAccess(
241 // "tweed.system.interceptor.MySimpleBean$Interceptor",
242 interceptorClassName,
243 "interceptor",
244 // new ObjectType( "tweed.system.interceptor.InterceptionHandler" ),
245 new ObjectType( InterceptionHandler.class.getName() ),
246 Constants.PUTFIELD
247 )
248 ) ;
249 InstructionHandle ih_22 = il.append( _factory.createReturn( Type.VOID ) ) ;
250 method.setMaxStack() ;
251 method.setMaxLocals() ;
252 _cg.addMethod( method.getMethod() ) ;
253 il.dispose() ;
254 }
255
256 private void createInterceptedMethod( Method template, int methodIndex ) {
257
258 InstructionList il = new InstructionList();
259
260 Class[] parameterTypes = template.getParameterTypes() ;
261 int parameterCount = parameterTypes.length ;
262 Type[] argTypes = new Type[ parameterCount ] ;
263 String[] argNames = new String[ parameterCount ] ;
264 for( int i = 0 ; i < parameterCount ; i++ ) {
265 argTypes[ i ] = Type.getType( parameterTypes[ i ] ) ;
266 argNames[ i ] = "p" + i ;
267 }
268
269 MethodGen method = new MethodGen(
270 ACC_PUBLIC,
271 // Type.OBJECT,
272 Type.getType( template.getReturnType() ),
273 // Type.NO_ARGS,
274 argTypes,
275 // new String[] { },
276 argNames,
277 // "getMyObject",
278 template.getName(),
279 // "tweed.system.interceptor.MySimpleBean$Interceptor",
280 interceptorClassName,
281 il,
282 _cp
283 ) ;
284
285 InstructionHandle ih_0 = il.append(
286 _factory.createLoad( Type.OBJECT, 0 ) ) ;
287 il.append(
288 _factory.createFieldAccess(
289 // "tweed.system.interceptor.MySimpleBean$Interceptor",
290 interceptorClassName,
291 "interceptor",
292 new ObjectType(
293 // "tweed.system.interceptor.InterceptionHandler"
294 InterceptionHandler.class.getName()
295 ),
296 Constants.GETFIELD
297 )
298 ) ;
299 il.append( _factory.createLoad( Type.OBJECT, 0 ) ) ;
300 il.append( _factory.createFieldAccess(
301 // "tweed.system.interceptor.MySimpleBean$Interceptor",
302 interceptorClassName,
303 "subject",
304 // new ObjectType( "tweed.system.interceptor.MySimpleBean" ),
305 new ObjectType( subjectClassName ),
306 Constants.GETFIELD
307 ) ) ;
308 il.append( _factory.createLoad( Type.OBJECT, 0 ) ) ;
309 il.append( _factory.createFieldAccess(
310 // "tweed.system.interceptor.MySimpleBean$Interceptor",
311 interceptorClassName,
312 "methodDescriptors",
313 new ArrayType(
314 // new ObjectType( "tweed.system.interceptor.MethodDescriptor" ),
315 new ObjectType( MethodDescriptor.class.getName() ),
316 1
317 ),
318 Constants.GETFIELD
319 ) ) ;
320 // il.append( new PUSH( _cp, 0 ) ) ;
321 il.append( new PUSH( _cp, methodIndex ) ) ;
322
323 il.append( InstructionConstants.AALOAD ) ;
324 il.append(
325 _factory.createInvoke(
326 // "tweed.system.interceptor.InterceptionHandler",
327 InterceptionHandler.class.getName(),
328 "handleMethod",
329 Type.VOID,
330 new Type[] {
331 Type.OBJECT,
332 // new ObjectType( "tweed.system.interceptor.MethodDescriptor" )
333 new ObjectType( MethodDescriptor.class.getName() )
334 },
335 Constants.INVOKEVIRTUAL
336 )
337 ) ;
338
339 InstructionHandle ih_17 = il.append(
340 _factory.createLoad( Type.OBJECT, 0 ) ) ;
341 il.append(
342 _factory.createFieldAccess(
343 // "tweed.system.interceptor.MySimpleBean$Interceptor",
344 interceptorClassName,
345 "subject",
346 new ObjectType( subjectClassName ),
347 Constants.GETFIELD
348 )
349 ) ;
350 il.append(
351 _factory.createInvoke(
352 // "tweed.system.interceptor.MySimpleBean",
353 subjectClassName,
354 // "getMyObject",
355 template.getName(),
356 // Type.OBJECT,
357 Type.getType( template.getReturnType() ),
358 // Type.NO_ARGS,
359 argTypes,
360 Constants.INVOKEVIRTUAL
361 // Oops, code below doesn't seem to work, what's wrong with it?
362 // template.getDeclaringClass().equals( subjectClass ) ?
363 // Constants.INVOKEVIRTUAL :
364 // Constants.INVOKESPECIAL
365 )
366 ) ;
367 InstructionHandle ih_24 = il.append(
368 // _factory.createReturn( Type.OBJECT ) ) ;
369 _factory.createReturn( Type.getType( template.getReturnType() ) ) ) ;
370 method.setMaxStack();
371 method.setMaxLocals();
372 _cg.addMethod(method.getMethod());
373 il.dispose();
374
375 }
376
377 private void createNonInterceptedMethod( Method template ) {
378
379 InstructionList il = new InstructionList();
380
381 Class[] parameterTypes = template.getParameterTypes() ;
382 int parameterCount = parameterTypes.length ;
383 Type[] argTypes = new Type[ parameterCount ] ;
384 String[] argNames = new String[ parameterCount ] ;
385 for( int i = 0 ; i < parameterCount ; i++ ) {
386 argTypes[ i ] = Type.getType( parameterTypes[ i ] ) ;
387 argNames[ i ] = "p" + i ;
388 }
389 Type returnType = Type.getType( template.getReturnType() ) ;
390
391 MethodGen method = new MethodGen(
392 ACC_PUBLIC,
393 // Type.VOID,
394 returnType,
395 // new Type[] { Type.OBJECT },
396 argTypes,
397 // new String[] { "arg0" },
398 argNames,
399 // "setMyObject",
400 template.getName(),
401 // "tweed.system.interceptor.MySimpleBean$Interceptor",
402 interceptorClassName,
403 il,
404 _cp
405 ) ;
406
407 createDelegateCall( il, template ) ;
408
409 method.setMaxStack();
410 method.setMaxLocals();
411 _cg.addMethod(method.getMethod());
412 il.dispose();
413 }
414
415 private void createDelegateCall( InstructionList il, Method template ) {
416
417 Type returnType = Type.getType( template.getReturnType() ) ;
418 Class[] parameterTypes = template.getParameterTypes() ;
419 int parameterCount = parameterTypes.length ;
420 Type[] argTypes = new Type[ parameterCount ] ;
421 String[] argNames = new String[ parameterCount ] ;
422 for( int i = 0 ; i < parameterCount ; i++ ) {
423 argTypes[ i ] = Type.getType( parameterTypes[ i ] ) ;
424 argNames[ i ] = "p" + i ;
425 }
426
427 InstructionHandle ih_0 = il.append( _factory.createLoad(
428 Type.OBJECT,
429 0
430 ) ) ;
431 il.append(_factory.createFieldAccess(
432 // "tweed.system.interceptor.MySimpleBean$Interceptor",
433 interceptorClassName,
434 "subject",
435 // new ObjectType("tweed.system.interceptor.MySimpleBean"),
436 new ObjectType( subjectClassName ),
437 Constants.GETFIELD
438 ) ) ;
439
440 for( int i = 0 ; i < argTypes.length ; i++ ) {
441 il.append( _factory.createLoad( argTypes[ i ], i + 1 ) ) ;
442 }
443
444 il.append( _factory.createInvoke(
445 // "tweed.system.interceptor.MySimpleBean",
446 subjectClassName,
447 // "setMyObject",
448 template.getName(),
449 // Type.VOID,
450 returnType,
451 // new Type[] { Type.OBJECT },
452 argTypes,
453 Constants.INVOKEVIRTUAL
454 // template.getDeclaringClass().equals( subjectClass ) ?
455 // Constants.INVOKEVIRTUAL :
456 // Constants.INVOKESPECIAL
457 ));
458 // InstructionHandle ih_8 = il.append( _factory.createReturn( Type.VOID ) ) ;
459 InstructionHandle ih_8 = il.append( _factory.createReturn( returnType ) ) ;
460
461
462 }
463
464 }
This page was automatically generated by Maven