/src/kilim/analysis/ClassWeaver.java
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