Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: edu/ucsb/ccs/jcontractor/transformation/ClassTransformer.java


1   package edu.ucsb.ccs.jcontractor.transformation;
2   
3   import java.util.*;
4   
5   import org.apache.bcel.classfile.JavaClass;
6   import org.apache.bcel.classfile.Method;
7   import org.apache.bcel.generic.MethodGen;
8   import org.apache.bcel.generic.ConstantPoolGen;
9   import org.apache.bcel.generic.InstructionFactory;
10  
11  import edu.ucsb.ccs.jcontractor.jContractor;
12  import edu.ucsb.ccs.jcontractor.InstrumentationFilter;
13  import edu.ucsb.ccs.jaqual.ForAll;
14  import edu.ucsb.ccs.jaqual.Logical;
15  import edu.ucsb.ccs.jaqual.standard.InstanceOf;
16  
17  /**
18   * A ClassTransformer executes a complex transformation on a JavaClass
19   * object.  The complex transform is made up of simpler transforms
20   * that are carried out in sequence on the class itself and on each
21   * method.
22   *
23   * <p>Transformations are stored as a list, and are executed in the
24   * order that they appear.  The list may be modified with
25   * <code>insert(Transformation)</code> and
26   * <code>append(Transformation)</code>.  Use the
27   * <code>transform(JavaClass)</code> method to apply the
28   * transformations to a class.
29   *
30   * @see Transformation
31   *
32   * @author Parker Abercrombie
33   * @version $Id: ClassTransformer.java,v 1.11 2002/07/13 22:57:05 parkera Exp $
34   */
35  
36  public class ClassTransformer {
37  
38    /**
39     * The class currently being transformed.
40     */
41    protected JavaClass currentClass;
42  
43    /**
44     * A ConstantPoolGen for <code>currentClass</code>.
45     */
46    protected ConstantPoolGen constantPoolGen;
47  
48    /**
49     * A set of methods that will be in the current class after
50     * transformation.  Note that the result of
51     * <code>currentClass.getMethods()</code> will not neccessarily
52     * match this set because  Transformations may add and remove
53     * methods from the set.
54     */
55    protected MethodSet methodSet;
56  
57    /**
58     * A hashtable of data that is shared between transformations.  A
59     * transformation that has data needed by other transformations may
60     * store it in this table, under some published key.
61     */
62    protected Hashtable sharedInfo;
63  
64    /**
65     * List to store transforms.  The transforms are applied to a class
66     * in the order that they appear in this list.
67     */
68    protected LinkedList transforms;
69  
70    /**
71     * An instruction factory to use when creating bytecode into a
72     * class.  The `transform(JavaClass)' method sets the constant pool
73     * used by this factory to the correct constant pool for the current
74     * class.  The default factory can be overridden with
75     * <code>setInstructionFactory(InstructionFactory)</code>.
76     */
77    protected InstructionFactory instructionFactory;
78  
79    protected InstrumentationFilter instrumentationFilter;
80  
81    /**
82     * Create an empty ClassTransformer.
83     */
84    public ClassTransformer (InstrumentationFilter filter) {
85      transforms = new LinkedList();
86      sharedInfo = new Hashtable();
87  
88      // Create the default instruction factory.  The constant pool will
89      // be set in the `transform(JavaClass)' method.
90      instructionFactory = new InstructionFactory((ConstantPoolGen) null);
91      instrumentationFilter = filter;
92    }
93  
94    /**
95     * Transform a class.  Each Transform in `transforms' will be
96     * offered the opportunity to operate on the class.  The transforms
97     * will be applied in the order in which they appear in the
98     * `transforms' list.
99     *
100    * @param javaclass a class to transform.
101    */
102   public void transform (JavaClass javaclass)
103       throws AbortTransformationException {
104     // Set `currentClass' and its ConstantPoolGen.  Clear the shared
105     // data table.
106     currentClass = javaclass;
107     constantPoolGen = new ConstantPoolGen(javaclass.getConstantPool());
108     sharedInfo.clear();
109     instructionFactory.setConstantPool(constantPoolGen);
110 
111     // Create an array of Methods.  These methods will be offered to
112     // each Transformation.  Since each transformation can
113     // potentially add and remove methods, we will also create
114     // MethodSet to hold the working MethodGens.
115     Method [] methods = javaclass.getMethods();
116     methodSet = new MethodSet();
117     for (int j = 0; j < methods.length; j++) {
118       methodSet.putMethod(new MethodGen(methods[j],
119                                         currentClass.getClassName(),
120                                         constantPoolGen));
121     }
122 
123     Iterator i = transforms.iterator();
124     Transformation transform;
125     MethodGen mg;
126 
127     // Execute each transformation.
128     while (i.hasNext()) {
129       ((Transformation) i.next()).transform();
130     }
131 
132     // Convert MethodGen[] to Method[]
133     MethodGen [] newMethods = methodSet.toArray();
134     methods = new Method [newMethods.length];
135     for (int j = 0; j < methods.length; j++) {
136       newMethods[j].setMaxStack();
137       newMethods[j].setMaxLocals();
138       newMethods[j].removeNOPs();
139       methods[j] = newMethods[j].getMethod();
140     }
141 
142     // Set methods and constant pool in the class.
143     javaclass.setMethods(methods);
144     javaclass.setConstantPool(constantPoolGen.getFinalConstantPool());
145   }
146 
147   protected boolean transform_Precondition (JavaClass javaclass) {
148     return javaclass != null;
149   }
150 
151   /**
152    * Insert a transformation at the beginning of the list.
153    *
154    * @param t a transformation to insert.  Note: a Transformation can
155    *          be added to only one ClassTransformer.  Henceforth,
156    *          <code>t</code> will be attached to this
157    *          ClassTransformation, overriding any previous
158    *          affiliation.
159    */
160   public void insert (Transformation t) {
161     t.setTransformer(this);
162     transforms.addFirst(t);
163   }
164 
165   protected boolean insert_Precondition (Transformation t) {
166     return t != null;
167   }
168 
169   protected boolean insert_Postcondition (Transformation t, Void RESULT) {
170     // transforms.size() == old transforms.size() + 1
171     return transforms.contains(t)
172       && (transforms.getFirst() == t);
173   }
174 
175   /**
176    * Add a Transformation at the end of the transformation list.
177    *
178    * @param t a transformation to insert.  Note: a Transformation can
179    *          be added to only one ClassTransformer.  Henceforth,
180    *          <code>t</code> will be attached to this
181    *          ClassTransformation, overriding any previous
182    *          affiliation.
183    */
184   public void append (Transformation t) {
185     t.setTransformer(this);
186     transforms.addLast(t);
187   }
188 
189   protected boolean append_Precondition (Transformation t) {
190     return t != null;
191   }
192 
193   protected boolean append_Postcondition (Transformation t, Void RESULT) {
194     // transforms.size() == old transforms.size() + 1
195     return transforms.contains(t)
196       && (transforms.getLast() == t);
197   }
198 
199   /**
200    * Get the number of transformations in the transformer.
201    *
202    * @return the number of transformations.
203    */
204   public int size () {
205     return transforms.size();
206   }
207 
208   protected boolean size_Postcondition (int RESULT) {
209     return RESULT >= 0;
210   }
211 
212   /**
213    * Get the class that the transformer is transforming.  The current
214    * class is only meaningful when executing the
215    * <code>transform</code> method, so only Transformation objects
216    * should call this method.
217    *
218    * @return the class being transformed.
219    */
220   public JavaClass getCurrentClass () {
221     return currentClass;
222   }
223 
224   /**
225    * Get the ConstantPoolGen object that is to be used to build the
226    * constant pool for the current class.  This is only meaningful
227    * when executing the <code>transform</code> method (when there is a
228    * current class), so only Transformation objects should call this
229    * method.
230    *
231    * @return a ConstantPoolGen for the current class.
232    */
233   public ConstantPoolGen getConstantPoolGen () {
234     return constantPoolGen;
235   }
236 
237   /**
238    * Get the method set that holds working copies of the methods in
239    * the current class.  This set is only meaningful when executing
240    * the <code>transform</code> method, so only Transformation objects
241    * should call this method.
242    *
243    * @return a MethodSet containing working copies of the methods
244    *         contained in the current class.  Transformations are
245    *         allowed to add, modify, and remove methods in the set.
246    */
247   public MethodSet getMethodSet () {
248     return methodSet;
249   }
250 
251   /**
252    * Get the shared data table.  This table is provided so that
253    * Transformations can communicate with each other in a crude way.
254    * If one transformation has a piece of data needed by a subsequent
255    * transformation, it may store it in this table, indexed by a
256    * published key.  This table is cleared between calls to
257    * <code>transform</code>.
258    *
259    * @return the shared data table.
260    */
261   public Hashtable getSharedInfo () {
262     return sharedInfo;
263   }
264 
265   /**
266    * Get the instruction factory that should be used to create
267    * instructions.  This factory has been initialized with the correct
268    * constant pool for the current class.
269    *
270    * @return the instruction factory that is to be used to create
271    *         bytecode to insert into the current class.
272    */
273   public InstructionFactory getInstructionFactory () {
274     return instructionFactory;
275   }
276 
277   protected boolean getInstructionFactory_Postcondition (InstructionFactory RESULT) {
278     return (RESULT != null);
279   }
280 
281   /**
282    * Set the instruction factory to be used to create bytecode for the
283    * current class.  The constant pool in this factory will be set by
284    * the <code>transform(JavaClass)</code> method, so it is not
285    * neccessary to reset the factory after every class is transformed.
286    *
287    * @param factory the new instruction factory.
288    */
289   public void setInstructionFactory (InstructionFactory factory) {
290     instructionFactory = factory;
291   }
292 
293   protected boolean setInstructionFactory_Precondition (InstructionFactory factory) {
294     return (factory != null);
295   }
296 
297   protected boolean setInstructionFactory_Postcondition (InstructionFactory factory) {
298     return getInstructionFactory() == factory;
299   }
300 
301   /**
302    * Set the instrumentation filter that determines which classes are
303    * instrumented and to what level.
304    *
305    * @param filter the new filter.
306    *
307    * @see #getInstrumentationFilter()
308    */
309   public void setInstrumentationFilter (InstrumentationFilter filter) {
310     instrumentationFilter = filter;
311   }
312 
313   protected boolean setInstrumentationFilter_Postcondition (InstrumentationFilter filter) {
314     return filter != null;
315   }
316 
317   /**
318    * Get the instrumentation filter that determines which classes are
319    * instrumented and to what level.
320    *
321    * @return the filter that determines which classes are
322    *         instrumented.
323    *
324    * @see #setInstrumentationFilter(InstrumentationFilter)
325    */
326   public InstrumentationFilter getInstrumentationFilter () {
327     return instrumentationFilter;
328   }
329 
330   protected boolean getInstrumentationFilter_Postcondition (InstrumentationFilter RESULT) {
331     return RESULT != null;
332   }
333 
334   protected boolean _Invariant () {
335     return (transforms != null)
336       && Logical.implies(getCurrentClass() != null,
337                          getMethodSet() != null)
338       && Logical.implies(getCurrentClass() != null,
339                          getConstantPoolGen() != null)
340       && (getSharedInfo() != null)
341       && (getInstructionFactory() != null)
342       && ForAll.in(transforms).ensure(new InstanceOf(Transformation.class));
343   }
344 }