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 }