PageRenderTime 37ms CodeModel.GetById 10ms app.highlight 22ms RepoModel.GetById 2ms app.codeStats 0ms

/src/kilim/analysis/ClassWeaver.java

http://github.com/kilim/kilim
Java | 306 lines | 202 code | 39 blank | 65 comment | 48 complexity | c05c72800ba09a2ad08aa74d8e3c147e MD5 | raw file
  1/* Copyright (c) 2006, Sriram Srinivasan
  2 *
  3 * You may distribute this software under the terms of the license 
  4 * specified in the file "License"
  5 */
  6
  7package kilim.analysis;
  8import static kilim.Constants.STATE_CLASS;
  9import static kilim.Constants.WOVEN_FIELD;
 10import static org.objectweb.asm.Opcodes.ACC_FINAL;
 11import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
 12import static org.objectweb.asm.Opcodes.ACC_STATIC;
 13import static org.objectweb.asm.Opcodes.ALOAD;
 14import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
 15import static org.objectweb.asm.Opcodes.RETURN;
 16import static org.objectweb.asm.Opcodes.V1_1;
 17
 18import java.io.IOException;
 19import java.io.InputStream;
 20import java.util.ArrayList;
 21import java.util.HashMap;
 22import java.util.LinkedList;
 23import java.util.List;
 24
 25import kilim.Constants;
 26import kilim.KilimException;
 27import kilim.mirrors.Detector;
 28
 29import org.objectweb.asm.Attribute;
 30import org.objectweb.asm.ClassVisitor;
 31import org.objectweb.asm.ClassWriter;
 32import org.objectweb.asm.MethodVisitor;
 33import org.objectweb.asm.tree.AnnotationNode;
 34import org.objectweb.asm.tree.FieldNode;
 35import org.objectweb.asm.tree.InnerClassNode;
 36
 37/**
 38 * This class is the main entry point for the Weave tool. It uses
 39 * ClassFlow to parse and analyze a class file, and writes out a
 40 * CPS transformed file if needed
 41 */
 42public class ClassWeaver {
 43    public ClassFlow classFlow;
 44    List<ClassInfo> classInfoList = new LinkedList<ClassInfo>();
 45    static ThreadLocal<HashMap<String, ClassInfo>> stateClasses_ = 
 46    		new ThreadLocal<HashMap<String, ClassInfo>>() {
 47    	protected HashMap<String, ClassInfo> initialValue() {
 48    		return new HashMap<String, ClassInfo>();
 49    	}
 50    };
 51    
 52    public static void reset() {
 53    	stateClasses_.set(new HashMap<String, ClassInfo>() );
 54    }
 55    
 56
 57    public KilimContext context;
 58    
 59    
 60    
 61    
 62    
 63    public ClassWeaver(KilimContext context,InputStream is) throws IOException {
 64        this.context = context;
 65        classFlow = new ClassFlow(context,is);
 66    }
 67    
 68    
 69    public void weave() throws KilimException {
 70        classFlow.analyze(false);
 71        if (needsWeaving() && classFlow.isPausable()) {
 72            boolean computeFrames = (classFlow.version & 0x00FF) >= 50;
 73            ClassWriter cw = new kilim.analysis.ClassWriter(
 74                    computeFrames ? ClassWriter.COMPUTE_FRAMES : 0, context.detector);
 75            accept(cw);
 76            addClassInfo(new ClassInfo(classFlow.getClassName(), cw.toByteArray()));
 77        }
 78    }
 79    
 80
 81    private void accept(final ClassVisitor cv) {
 82        ClassFlow cf = classFlow;
 83        // visits header
 84        String[] interfaces = toStringArray(cf.interfaces);
 85        cv.visit(cf.version, cf.access, cf.name, cf.signature, cf.superName, interfaces);
 86        // visits source
 87        if (cf.sourceFile != null || cf.sourceDebug != null) {
 88            cv.visitSource(cf.sourceFile, cf.sourceDebug);
 89        }
 90        if (cf.nestHostClass != null)
 91            cv.visitNestHost(cf.nestHostClass);
 92        // visits outer class
 93        if (cf.outerClass != null) {
 94            cv.visitOuterClass(cf.outerClass, cf.outerMethod, cf.outerMethodDesc);
 95        }
 96        if (cf.nestMembers != null)
 97            for (String member : cf.nestMembers)
 98                cv.visitNestMember(member);
 99        // visits attributes and annotations
100        int i, n;
101        AnnotationNode an;
102        n = cf.visibleAnnotations == null ? 0 : cf.visibleAnnotations.size();
103        for (i = 0; i < n; ++i) {
104            an = (AnnotationNode) cf.visibleAnnotations.get(i);
105            an.accept(cv.visitAnnotation(an.desc, true));
106        }
107        n = cf.invisibleAnnotations == null ? 0
108                : cf.invisibleAnnotations.size();
109        for (i = 0; i < n; ++i) {
110            an = (AnnotationNode) cf.invisibleAnnotations.get(i);
111            an.accept(cv.visitAnnotation(an.desc, false));
112        }
113        
114        n = cf.attrs == null ? 0 : cf.attrs.size();
115        for (i = 0; i < n; ++i) {
116            cv.visitAttribute((Attribute) cf.attrs.get(i));
117        }
118        // visits inner classes
119        for (i = 0; i < cf.innerClasses.size(); ++i) {
120            ((InnerClassNode) cf.innerClasses.get(i)).accept(cv);
121        }
122        // visits fields
123        for (i = 0; i < cf.fields.size(); ++i) {
124            ((FieldNode) cf.fields.get(i)).accept(cv);
125        }
126        /*
127         * Mark this class as "processed" by adding a dummy field, so that
128         * we don't weave an already woven file
129         */
130        cv.visitField(ACC_PUBLIC | ACC_STATIC | ACC_FINAL, WOVEN_FIELD, "Z", "Z", Boolean.TRUE);
131        // visits methods
132        
133        MethodFlow sam = classFlow.getSAM(); 
134        for (i = 0; i < cf.methods.size(); ++i) {
135            MethodFlow m = (MethodFlow) cf.methods.get(i);
136            if (needsWeaving(m)) {
137                // For pausable methods, we generate two function signatures, one the original
138                // and the other with a fiber as the last param. For a non-abstract method, the 
139                // fiber'd version gets the original's body, and the original version throws
140                // an error saying "Not Woven", in case it is called at run time. This latter part
141                // is done in makeNotWovenMethod below.
142                
143                // However, if it is a single abstract method (SAM) of a functional interface,
144                // then we invert the arrangement. We generate two method bodies as before,
145                // but the fiber'd version gets the "not woven" message. This preserves the 
146                // original method as the SAM. However, the weaver (see CallWeaver and SAMWeaver)
147                // arranges it such that the invokedynamic instruction bridges the fiber'd version
148                // of the method with the body of the lambda expression. 
149                
150                boolean isSAM = (sam == m);
151                MethodWeaver mw = new MethodWeaver(this, context.detector, m, isSAM);
152                mw.accept(cv);
153                if (m.isPausable())
154                    mw.makeNotWovenMethod(cv, m, isSAM);
155            } else {
156                m.restoreNonInstructionNodes();
157                m.accept(cv);
158            }
159        }
160
161        // create shim methods (if any) to wrap SAM interface methods
162        for (SAMweaver sw: samWeavers) {
163            sw.accept(cv);
164        }
165        
166        // visits end
167        cv.visitEnd();
168    }
169
170    @SuppressWarnings(value = { "unchecked" })
171    static String[] toStringArray(List list) {
172        String[] array = new String[list.size()];
173        list.toArray(array);
174        return array;
175    }
176
177    void addClassInfo(ClassInfo ci) {
178        classInfoList.add(ci);
179    }
180    
181    public List<ClassInfo>  getClassInfos() {
182        return classInfoList;
183    }
184    
185    /*
186     * A method needs weaving ordinarily if it is marked pausable.
187     * However, if there exists another method with the same name
188     * and parameters and an additional Fiber parameter as the last
189     * one, then this method doesn't need weaving. Examples are
190     * kilim.Task.yield and kilim.Task.sleep
191     */
192    boolean needsWeaving(MethodFlow mf) {
193        if (!mf.needsWeaving() || mf.desc.endsWith(Constants.D_FIBER_LAST_ARG)) 
194            return false;
195        String fdesc = mf.desc.replace(")", Constants.D_FIBER_LAST_ARG);
196        for (MethodFlow omf: classFlow.getMethodFlows()) {
197            if (omf == mf) continue;
198            if (mf.name.equals(omf.name) && fdesc.equals(omf.desc)) {
199                return false;
200            }
201        }
202        return true;
203    }
204    
205    boolean needsWeaving() {
206        if (classFlow.isWoven) return false;
207        for (MethodFlow mf: classFlow.getMethodFlows()) {
208            if (needsWeaving(mf)) return true;
209        }
210        return false;
211    }
212    
213    /**
214     * Create a custom class (structure) to hold the state. The name of the
215     * state reflects the numbers of the various VMtypes in valInfoList. class
216     * kilim.SO2IJ3 reflects a class that stores two Objects one Integer and 3
217     * longs.
218     * 
219     * <pre>
220     *            class kilim.SO2IJ3 extends kilim.State {
221     *               public Object f1, f2;
222     *               public int f3;
223     *               public long f4, f5, f6;
224     *            } 
225     * </pre>
226     * 
227     * If there's no data to store, we use the kilim.State class directly to
228     * store the basic amount of data necessary to restore the stack.
229     */
230
231    String createStateClass(ValInfoList valInfoList) {
232        int numByType[] = { 0, 0, 0, 0, 0 };
233        for (ValInfo vi : valInfoList) {
234            numByType[vi.vmt]++;
235        }
236        String className = makeClassName(numByType);
237        ClassInfo classInfo= null;
238            classInfo= stateClasses_.get().get(className);
239            if (classInfo == null) {
240                ClassWriter cw = new kilim.analysis.ClassWriter(ClassWriter.COMPUTE_FRAMES, context.detector);
241                cw.visit(V1_1, ACC_PUBLIC | ACC_FINAL, className, null, "kilim/State", null);
242
243                // Create default constructor
244                // <init>() {
245                // super(); // call java/lang/Object.<init>()
246                // }
247                MethodVisitor mw = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
248                mw.visitVarInsn(ALOAD, 0);
249                mw.visitMethodInsn(INVOKESPECIAL, STATE_CLASS, "<init>", "()V", false);
250                mw.visitInsn(RETURN);
251                // this code uses a maximum of one stack element and one local variable
252                mw.visitMaxs(1, 1);
253                mw.visitEnd();
254                // create fields of the appropriate type.
255                for (ValInfo vi : valInfoList) {
256                    cw.visitField(ACC_PUBLIC, vi.fieldName, vi.fieldDesc(), null, null);
257                }
258                classInfo= new ClassInfo(className, cw.toByteArray());
259                stateClasses_.get().put(className, classInfo);
260            }
261        if (!classInfoList.contains(classInfo))
262          addClassInfo(classInfo);
263        return className;
264    }
265
266    private String makeClassName(int[] numByType) {
267        StringBuilder sb = new StringBuilder(30);
268        sb.append("kilim/S_");
269        for (int t = 0; t < 5; t++) {
270            int c = numByType[t];
271            if (c == 0)
272                continue;
273            sb.append(VMType.abbrev[t]);
274            if (c > 1) {
275                sb.append(c);
276            }
277        }
278        return sb.toString();
279    }
280
281    boolean isInterface() {
282        return classFlow.isInterface();
283    }
284    
285    ArrayList<SAMweaver> samWeavers = new ArrayList<SAMweaver>();
286    SAMweaver getSAMWeaver(String owner, String methodName, String desc, boolean itf) {
287        SAMweaver sw = new SAMweaver(context,owner, methodName, desc, itf);
288        // intern
289        for (SAMweaver s: samWeavers) {
290            if (s.equals(sw)) {
291                return s; 
292            }
293        }
294        samWeavers.add(sw);
295        sw.setIndex(samWeavers.size());
296        
297        return sw;
298    }
299
300    String getName() {
301        return classFlow.name;
302    }
303
304}
305
306