KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > quilt > cover > stmt > ClassAction


1 /* ClassAction.java */
2 package org.quilt.cover.stmt;
3
4 import java.util.List JavaDoc;
5 import java.util.Vector JavaDoc;
6
7 import org.apache.bcel.classfile.Field;
8 import org.apache.bcel.classfile.Method;
9 import org.apache.bcel.generic.*;
10 import org.quilt.cl.ClassTransformer;
11 import org.quilt.cl.CodeVertex;
12 import org.apache.bcel.Constants;
13
14 /**
15  * Add instrumentation at the class level, creating <clinit>
16  * if necessary. Three fields are added and initialized:
17  * <ul>
18  * <li><b>q$$q</b>, the int[] hit counts array
19  * <li><b>q$$qID</b>, a class identifier unique within this run
20  * <li><b>q$$qStmtReg</b>, reference to the StmtRegistry
21  * <li><b>q$$qVer</b>, a Quilt class file format version
22  * </ul>
23  *
24  * All of these fields are <em>public final static</em>. They are
25  * initialized by <code>clinit</code> when the class is loaded,
26  * running bytecode inserted by Quilt.
27  *
28  * @author <a HREF="mailto:jddixon@users.sourceforge.net">Jim Dixon</a>
29  */

30
31 public class ClassAction implements org.quilt.cl.ClassXformer {
32
33     // XFORMER VARIABLES ////////////////////////////////////////////
34
/** Name of the processor for use in reports. */
35     private static String JavaDoc name_ = null;
36
37     /** The ClassTransformer that invoked this Xformer */
38     private ClassTransformer classTrans;
39
40     /** The ClassGen we are working on. */
41     private ClassGen clazz_;
42
43     /** its name */
44     private String JavaDoc className;
45
46     /** the class name prefixed with "class$" XXX UNNECESSARY */
47     private String JavaDoc prefixedClassName;
48
49     /** Its constant pool */
50     private ConstantPoolGen cpGen_;
51
52     /** */
53     private InstructionFactory factory;
54
55     /** Does a static initializer class exist? */
56     boolean clinitExists = false;
57
58     /** Its index in the method array. */
59     int clinitIndex = -1;
60
61     // COVERAGE-RELATED VARIABLES ///////////////////////////////////
62
/** Current statement coverage registry */
63     private static StmtRegistry stmtReg = null;
64
65     /** temporary data shared between xformers */
66     private Ephemera eph;
67
68     // CONSTRUCTORS /////////////////////////////////////////////////
69
public ClassAction() {
70     }
71     public ClassAction (StmtRegistry reg) {
72         stmtReg = reg;
73         setName(this.getClass().getName()); // default name
74
}
75
76     /**
77      * Passes a reference to the controlling ClassTransformer.
78      * XXX Inelegant - and unnecessary. XXX REWORK TO USED stmtReg
79      */

80     public void setClassTransformer (ClassTransformer ct) {
81         classTrans = ct;
82     }
83     // PRE- AND POST-PROCESSING /////////////////////////////////////
84
/**
85      * Add a q$$q hit count field to the class using
86      * public static int [] q$$q;
87      * If there is already a field of this name, do not instrument
88      * the class.
89      *
90      * This is a preprocessor applied to the class before looking at
91      * methods. Any such preprocessors will be applied in the order of
92      * the ClassXformer vector.
93      *
94      * @param clazz ClassGen for the class being transformed.
95      */

96     public void preMethods( ClassGen clazz ) {
97         clazz_ = clazz;
98         cpGen_ = clazz.getConstantPool();
99         className = clazz_.getClassName();
100         // I have tried this with class_ ; still isn't found
101
prefixedClassName = "class$QIC";
102         eph = new Ephemera(className);
103         if (!stmtReg.putEphemera(className, eph)) {
104             // XXX should throw exception
105
System.out.println("ClassAction.preMethods INTERNAL ERRROR - "
106                 + " couldn't register ephemeral data");
107         }
108         FieldGen field;
109         if (clazz.containsField("q$$q") != null) {
110             System.out.println("ClassAction.preMethods WARNING - "
111                 + className + " already has q$$q field, aborting");
112             classTrans.abort();
113         } else {
114             // ADD: public static int [] q$$q
115
field = new FieldGen(Constants.ACC_PUBLIC | Constants.ACC_STATIC,
116                         new ArrayType(Type.INT, 1), "q$$q", cpGen_);
117             clazz.addField(field.getField());
118             // ADD: public static int q$$qID
119
field = new FieldGen(Constants.ACC_PUBLIC | Constants.ACC_STATIC,
120                         Type.INT, "q$$qID", cpGen_);
121             clazz.addField(field.getField());
122             // ADD: public static final org.quilt.cover.stmt.StmtRegistry
123
field = new FieldGen(Constants.ACC_PUBLIC | Constants.ACC_STATIC
124                 | Constants.ACC_FINAL,
125                 new ObjectType("org.quilt.cover.stmt.StmtRegistry"),
126                 "q$$qStmtReg", cpGen_);
127             clazz.addField(field.getField());
128             
129             // ADD: public static int q$$qVer
130
field = new FieldGen(Constants.ACC_PUBLIC | Constants.ACC_STATIC,
131                         Type.INT, "q$$qVer", cpGen_);
132             clazz.addField(field.getField());
133
134             // ADD: public static class class$QIC (for QIC.class value)
135
field = new FieldGen(Constants.ACC_PUBLIC |Constants.ACC_STATIC,
136                 new ObjectType("java.lang.Class"),
137                 "class$QIC", cpGen_);
138             clazz.addField(field.getField());
139
140             // do we have a <clinit> ?
141
Method[] m = clazz.getMethods();
142             for (int i = 0; i < m.length; i++) {
143                 if (m[i].getName().equals("<clinit>")) {
144                     clinitExists = true;
145                     clinitIndex = i;
146                     break;
147                 }
148             }
149         }
150     }
151
152     private void dumpIList(InstructionList ilist, String JavaDoc where) {
153         if (ilist != null) {
154             System.out.println(where + ": instruction list");
155             int i=0;
156             for (InstructionHandle ih = ilist.getStart(); ih != null;
157                                                     ih = ih.getNext()) {
158                 System.out.println(" " + (i++) + " " + ih);
159             }
160         }
161     }
162
163     // the class$ method added by the Java compiler do deal with
164
// NAME.class constructs
165
// VIRTUALLY IDENTICAL TO BCEL DUMP /////////////////////////////
166
private void addClass$Method() {
167         InstructionList il = new InstructionList();
168         MethodGen method = new MethodGen(Constants.ACC_STATIC,
169             new ObjectType("java.lang.Class"), new Type[] { Type.STRING },
170                 new String JavaDoc[] { "arg0" }, "class$", className, il, cpGen_);
171     
172         // TRY BLOCK
173
InstructionHandle ih_0 = il.append(factory.createLoad(Type.OBJECT, 0));
174         InstructionHandle ih_1 = il.append(factory
175                 .createInvoke("java.lang.Class", "forName",
176                 new ObjectType("java.lang.Class"), new Type[] { Type.STRING },
177                     Constants.INVOKESTATIC));
178         il.append(factory.createReturn(Type.OBJECT));
179         
180         // CATCH BLOCK
181
InstructionHandle ih_5 = il.append(factory
182                 .createStore(Type.OBJECT, 1));
183         InstructionHandle ih_6 = il.append(factory
184                 .createNew("java.lang.NoClassDefFoundError"));
185         il.append(InstructionConstants.DUP);
186         il.append(factory.createLoad(Type.OBJECT, 1));
187         il.append(factory
188                 .createInvoke("java.lang.ClassNotFoundException", "getMessage",
189                 Type.STRING, Type.NO_ARGS, Constants.INVOKEVIRTUAL));
190         il.append(factory
191                 .createInvoke("java.lang.NoClassDefFoundError", "<init>",
192                 Type.VOID, new Type[] { Type.STRING },
193                     Constants.INVOKESPECIAL));
194         InstructionHandle ih_17 = il.append(InstructionConstants.ATHROW);
195         
196         // EXCEPTION HANDLERS
197
method.addExceptionHandler(ih_0, ih_1, ih_5,
198                 new ObjectType("java.lang.ClassNotFoundException"));
199         method.setMaxStack();
200         method.setMaxLocals();
201         clazz_.addMethod(method.getMethod());
202         il.dispose();
203     } // END class$
204
/**
205      * Postprocessor applied to the class after looking at methods.
206      * These will be applied in reverse order after completion of
207      * method processing.
208      */

209     public void postMethods (ClassGen clazz ) {
210         int counterCount = eph.getCounterCount();
211         List JavaDoc methodNames = eph.getMethodNames();
212         List JavaDoc methodEnds = eph.getMethodEnds();
213         if (clazz != clazz_) {
214             // XXX modify to throw exception
215
System.out.println("ClassAction.postMethods: INTERNAL ERROR:"
216                 + " preMethods class different from postMethods");
217         }
218         factory = new InstructionFactory (clazz_, cpGen_);
219         addClass$Method(); // uses factory
220

221         MethodGen mg;
222         InstructionList ilist;
223         InstructionHandle ih;
224         if (clinitExists) {
225             mg = new MethodGen (clazz_.getMethodAt(clinitIndex),
226                     className, clazz_.getConstantPool() );
227             ilist = mg.getInstructionList();
228         } else {
229             ilist = new InstructionList();
230             mg = new MethodGen ( Constants.ACC_STATIC, Type.VOID, Type.NO_ARGS,
231                     new String JavaDoc[] {}, "<clinit>", className,
232                     ilist, clazz_.getConstantPool() );
233         }
234         // //////////////////////////////////////////////////////////
235
// q$$q = new int[counterCount];
236
// //////////////////////////////////////////////////////////
237

238         // the first instruction MUST be insert
239
ih = ilist.insert(new PUSH (cpGen_, counterCount));
240
241         // dunno why, but the following produces an array of references; also
242
// // the cast should not be necessary, according to the Javadocs,
243
// // but there is a compilation error without it
244
// ih = ilist.append(ih, (Instruction)factory.createNewArray(
245
// new ArrayType(Type.INT, 1), (short)1));
246

247         ih = ilist.append(ih, new NEWARRAY(Type.INT));
248
249         ih = ilist.append(ih, factory.createFieldAccess (
250                     className, "q$$q",
251                     new ArrayType (Type.INT, 1), Constants.PUTSTATIC));
252
253         /////////////////////////////////////////////////////////////
254
// q$$qVer = 0;
255
/////////////////////////////////////////////////////////////
256
ih = ilist.append(ih, new PUSH(cpGen_, 0));
257         ih = ilist.append(ih, factory.createFieldAccess(
258                     className, "q$$qVer", Type.INT,
259                     Constants.PUTSTATIC));
260
261         // //////////////////////////////////////////////////////////
262
// public final static StmtRegistry q$$qStmtRegistry
263
// = (StmtRegistry)
264
// (org.quilt.cl.QuiltClassLoader)QIC.class.getClassLoader())
265
// .getRegistry("org.quilt.cover.stmt.StmtRegistry");
266
// //////////////////////////////////////////////////////////
267

268         // GET QIC.class //////////////////////////////////////
269
ih = ilist.append(ih, new PUSH(cpGen_, "org.quilt.QIC"));
270         ih = ilist.append(ih, factory.createInvoke(className, "class$",
271             new ObjectType("java.lang.Class"), new Type[] { Type.STRING },
272                 Constants.INVOKESTATIC));
273         // this two instructions are unnecessary
274
ih = ilist.append(ih, InstructionConstants.DUP);
275         ih = ilist.append(ih, factory.createFieldAccess(className,
276             "class$QIC", new ObjectType("java.lang.Class"),
277                 Constants.PUTSTATIC));
278
279         // get the class loader
280
ih = ilist.append(ih, factory
281             .createInvoke("java.lang.Class", "getClassLoader",
282             new ObjectType("java.lang.ClassLoader"), Type.NO_ARGS,
283                 Constants.INVOKEVIRTUAL));
284         // cast to QuiltClassLoader
285
ih = ilist.append(ih, factory
286             .createCheckCast(new ObjectType("org.quilt.cl.QuiltClassLoader")));
287         // put method name on stack ...
288
ih = ilist.append(ih,
289             new PUSH(cpGen_, "org.quilt.cover.stmt.StmtRegistry"));
290         // invoke QuiltClassLoader.getRegistry("org.quilt.cover.stmt.S...")
291
ih = ilist.append(ih, factory
292             .createInvoke("org.quilt.cl.QuiltClassLoader", "getRegistry",
293             new ObjectType("org.quilt.reg.QuiltRegistry"),
294                 new Type[] { Type.STRING }, Constants.INVOKEVIRTUAL));
295         // cast to StmtRegistry
296
ih = ilist.append(ih, factory
297             .createCheckCast(
298                 new ObjectType("org.quilt.cover.stmt.StmtRegistry")));
299         // save to q$$qStmtReg
300
ih = ilist.append(ih, factory
301             .createFieldAccess(className, "q$$qStmtReg",
302             new ObjectType("org.quilt.cover.stmt.StmtRegistry"),
303                 Constants.PUTSTATIC));
304         
305         /////////////////////////////////////////////////////////////
306
// q$$qID = q$$qStmtRegistry.registerCounts(className, q$$q);
307
/////////////////////////////////////////////////////////////
308

309         ih = ilist.append(ih, factory.createFieldAccess(
310                     className, "q$$qStmtReg",
311                     new ObjectType("org.quilt.cover.stmt.StmtRegistry"),
312                         Constants.GETSTATIC));
313         ih = ilist.append(ih, new PUSH(cpGen_, className));
314         ih = ilist.append(ih, factory.createFieldAccess (
315                     className, "q$$q",
316                     new ArrayType (Type.INT, 1), Constants.GETSTATIC));
317         ih = ilist.append(ih, factory.createInvoke(
318                     "org.quilt.cover.stmt.StmtRegistry", "registerCounts",
319                     Type.INT,
320                     new Type[] { Type.STRING, new ArrayType(Type.INT,1) },
321                     Constants.INVOKEVIRTUAL));
322         ih = ilist.append(ih, factory.createFieldAccess (
323                     className, "q$$qID", Type.INT,
324                     Constants.PUTSTATIC));
325
326         /////////////////////////////////////////////////////////////
327
// return;
328
/////////////////////////////////////////////////////////////
329
if (!clinitExists) {
330             ih = ilist.append(ih, factory.createReturn (Type.VOID));
331         }
332         ilist.setPositions();
333         mg.setMaxStack();
334         mg.setMaxLocals();
335
336         boolean aborting = false;
337         if (clinitExists) {
338             /////////////////////////////////////////////////////////
339
// XXX KNOWN PROBLEM: error in setMethod if clinitExists
340
// probably because line number table not corrected
341
/////////////////////////////////////////////////////////
342
// aborting = true;
343
// classTrans.abort();
344
clazz_.setMethodAt(mg.getMethod(), clinitIndex);
345         } else {
346             clazz_.addMethod(mg.getMethod());
347         }
348         // ilist.dispose(); // when things are more stable ;-)
349

350         // REGISTER method names and ends ///////////////////////////
351
if (!aborting) {
352             int len = methodNames.size();
353             String JavaDoc [] myNames = new String JavaDoc [len];
354             int [] myEndCounts = new int[len];
355
356             for (int k = 0; k < len; k++) {
357                 myNames[k] = (String JavaDoc) methodNames.get(k);
358                 myEndCounts[k] = ((Integer JavaDoc)methodEnds. get(k)).intValue();
359             }
360             stmtReg.registerMethods(className, myNames, myEndCounts);
361         } // if not aborting
362
stmtReg.removeEphemera(className);
363     }
364     // OTHER METHODS ////////////////////////////////////////////////
365
/** Get the preprocessor's report name. */
366     public String JavaDoc getName() {
367         return name_;
368     }
369
370     /** Set the preprocessor's name for reports. */
371     public void setName(String JavaDoc name) {
372         name_ = name;
373     }
374 }
375
Popular Tags