001: /* MethodTransformer.java */
002: package org.quilt.cl;
003:
004: import java.util.Hashtable;
005: import java.util.List;
006:
007: import org.apache.bcel.classfile.*;
008: import org.apache.bcel.Constants;
009: import org.apache.bcel.generic.*;
010: import org.quilt.graph.*;
011:
012: /**
013: * Optionally preprocesses a method, optionally transforms
014: * its control flow graph, optionally postprocesses it. The list of
015: * preprocessors is applied in forward order before graph transformation.
016: * The corresponding list of postprocessors is applied in reverse order
017: * after graph transformation.
018: *
019: * @author <a href="jddixon@users.sourceforge.net">Jim Dixon</a>
020: */
021: public class MethodTransformer {
022:
023: /** For passing application-specific data. XXX Not used, probably
024: * best dropped. */
025: private Hashtable methodHash = new Hashtable();
026:
027: /** Ordered list of method pre- and post-processors.*/
028: private List mxf;
029:
030: /** Graph processors. */
031: private List gxf;
032:
033: /** Main graph processor. */
034: private GraphTransformer xformer;
035:
036: /**
037: * Sets up method transformer, saving MethodXformer vector here
038: * and passing GraphXformer vector to that transformer.
039: */
040: public MethodTransformer(List mxf, List gxf) {
041: this .mxf = mxf;
042: this .gxf = gxf;
043: xformer = new GraphTransformer(gxf);
044: }
045:
046: /**
047: * Get a reference to the hash used to share data between this
048: * and other transformers.
049: */
050: public Hashtable getMethodHash() {
051: return methodHash;
052: }
053:
054: /**
055: * Zero out a badly-behaved method Xformer.
056: * @param mxf The tranformer being killed.
057: * @param e The exception responsible.
058: */
059: private void zapMethodXformer(MethodXformer mxf, Exception e) {
060: System.err.println("WARNING: exception in " + mxf.getName()
061: + ": transformation will not be applied");
062: e.printStackTrace();
063: mxf = null;
064: }
065:
066: /**
067: * <p>Transform a specific method, first applying preprocessors,
068: * then transforming the graph, then applying method postprocessors.</p>
069: *
070: * @param clazz The class to which the method belongs
071: * @param method The MethodGen being transformed.
072: */
073: public MethodGen xform(ClassGen clazz, Method orig) {
074: if (orig == null) {
075: throw new IllegalArgumentException("null method");
076: }
077: MethodGen method = new MethodGen(orig, clazz.getClassName(),
078: clazz.getConstantPool());
079: // New instances of the MethodXformers.
080: MethodXformer[] xf = new MethodXformer[mxf.size()];
081:
082: // apply preprocessors in order
083: for (int i = 0; i < xf.length; i++) {
084: try {
085: xf[i] = (MethodXformer) ((mxf.get(i)).getClass()
086: .newInstance());
087: } catch (IllegalAccessException e) {
088: zapMethodXformer(xf[i], e);
089: } catch (InstantiationException e) {
090: zapMethodXformer(xf[i], e);
091: }
092: if (xf[i] != null && method != null) {
093: xf[i].preGraph(clazz, method);
094: }
095: }
096: // transform the graph
097: if (gxf.size() > 0 && method != null) {
098: InstructionList ilist = xformer.xform(clazz, method);
099: if (ilist == null) {
100: System.out
101: .println("MethodTransformer.xformer: WARNING: "
102: + "xformer returned null instruction list");
103: return null;
104: }
105: // Quilt 0.5 approach, turns out to be unnecessary; the
106: // exception handlers are based on instruction handles,
107: // which in turn contain references to the method's instructions,
108: // which remain valid while the graph is being transformed.
109: //
110: // XXX But we may want to allow handlers to be added or changed.
111:
112: method.removeExceptionHandlers(); // new ones come from graph
113:
114: //checkAndSetPos(ilist);
115: ilist.setPositions(true);
116: method.setInstructionList(ilist);
117:
118: CodeExceptionGen[] cgs = xformer.getExceptionHandlers();
119: for (int i = 0; i < cgs.length; i++) {
120: // DEBUG
121: System.out.println("adding exception " + i
122: + " to method " + method.getName() + ": ["
123: + cgs[i].getStartPC().getPosition() + ".."
124: + cgs[i].getEndPC().getPosition() + "] --> "
125: + cgs[i].getHandlerPC().getPosition());
126: // END
127:
128: method.addExceptionHandler(cgs[i].getStartPC(), cgs[i]
129: .getEndPC(), cgs[i].getHandlerPC(), cgs[i]
130: .getCatchType());
131: }
132: method.removeNOPs();
133: method.update(); // to be called after editing MethodGen
134: // during development, exceptions were thrown in setMaxStack
135: try {
136: method.setMaxStack();
137: method.setMaxLocals();
138: // method.stripAttributes(true); // Quilt 0.5 legacy
139: } catch (RuntimeException e) {
140: e.printStackTrace();
141: System.out.println("GraphTransformer.xformer:\n"
142: + " EXCEPTION finishing method " + e);
143: throw e;
144: }
145: // // DEBUG ONLY ///////////////////////////////////////////
146: // dumpIList (method, "after fixups");
147: // cgs = method.getExceptionHandlers();
148: // dumpExceptionHandlers(method, "after fixups", cgs.length);
149: // /////////////////////////////////////////////////////////
150: }
151:
152: // apply postprocessors in reverse order
153: for (int i = xf.length - 1; i >= 0; i--) {
154: if (xf[i] != null && method != null) {
155: xf[i].postGraph(clazz, method);
156: }
157: }
158: // IF THERE HAVE BEEN ANY FATAL ERRORS, SHOULD RETURN NULL
159: return method;
160: }
161:
162: // UTILITY METHODS //////////////////////////////////////////////
163: void dumpExceptionHandlers(MethodGen method, String where, int len) {
164: CodeExceptionGen handlers[] = method.getExceptionHandlers();
165: if (handlers.length != len) {
166: System.out.println("EXPECTED " + len
167: + " exception handlers, found " + handlers.length);
168: }
169: if (handlers.length > 0) {
170: System.out.println("Exception handlers for method "
171: + method.getName() + " " + where + ":");
172:
173: for (int j = 0; j < handlers.length; j++) {
174: System.out.println(" " + j + ": ["
175: + handlers[j].getStartPC().getPosition() + ".."
176: + handlers[j].getEndPC().getPosition()
177: + "] --> "
178: + handlers[j].getHandlerPC().getPosition());
179: }
180: }
181: }
182:
183: void dumpIList(MethodGen method, String where) {
184: InstructionList myList = method.getInstructionList();
185: System.out.println("MethodTransformer: instruction list "
186: + where + ":");
187: int i = 0;
188: for (InstructionHandle ih = myList.getStart(); ih != null; ih = ih
189: .getNext()) {
190: System.out.println(" " + (i++) + " " + ih);
191: }
192: }
193: }
|