/* Copyright (c) 2006, Sriram Srinivasan * * You may distribute this software under the terms of the license * specified in the file "License" */ package kilim.analysis; import kilim.*; import kilim.mirrors.Detector; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import org.objectweb.asm.ClassReader; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.FieldNode; /** * This class reads a .class file (or stream), wraps each method with a MethodFlow object and optionally analyzes it. * */ public class ClassFlow extends ClassNode { ArrayList<MethodFlow> methodFlows; ClassReader cr; String classDesc; /** * true if any of the methods contained in the class file is pausable. ClassWeaver uses it later to avoid weaving if * isPausable isn't true. */ private boolean isPausable; /** * true if the .class being read is already woven. */ public boolean isWoven = false; private Detector detector; public ClassFlow(InputStream is, Detector detector) throws IOException { cr = new ClassReader(is); this.detector = detector; } public ClassFlow(String aClassName, Detector detector) throws IOException { cr = new ClassReader(aClassName); this.detector = detector; } public ClassFlow(byte[] data, Detector detector) { cr = new ClassReader(data); this.detector = detector; } @Override @SuppressWarnings( { "unchecked" }) public MethodVisitor visitMethod( final int access, final String name, final String desc, final String signature, final String[] exceptions) { MethodFlow mn = new MethodFlow( this, access, name, desc, signature, exceptions, detector); super.methods.add(mn); return mn; } public ArrayList<MethodFlow> getMethodFlows() { assert (methodFlows != null) : "ClassFlow.analyze not called"; return methodFlows; } public ArrayList<MethodFlow> analyze(boolean forceAnalysis) throws KilimException { // cr.accept(this, ClassReader.SKIP_DEBUG); Detector save = Detector.setDetector(detector); try { cr.accept(this, /*flags*/ClassReader.SKIP_FRAMES); for (Object o : this.fields) { FieldNode fn = (FieldNode) o; if (fn.name.equals(Constants.WOVEN_FIELD)) { isWoven = true; break; } } if (isWoven && !forceAnalysis) return new ArrayList<MethodFlow>(); // This is a hack. cr = null; // We don't need this any more. classDesc = TypeDesc.getInterned("L" + name + ';'); ArrayList<MethodFlow> flows = new ArrayList<MethodFlow>(methods.size()); String msg = ""; for (Object o : methods) { try { MethodFlow mf = (MethodFlow) o; if (mf.isBridge()) { MethodFlow mmf = getOrigWithSameSig(mf); if (mmf != null) mf.setPausable(mmf.isPausable()); } mf.verifyPausables(); if (mf.isPausable()) isPausable = true; if ((mf.isPausable() || forceAnalysis) && (!mf.isAbstract())) { mf.analyze(); } flows.add(mf); } catch (KilimException ke) { msg = msg + ke.getMessage() + "\n-------------------------------------------------\n"; } } if (msg.length() > 0) { throw new KilimException(msg); } methodFlows = flows; return flows; } finally { Detector.setDetector(save); } } private MethodFlow getOrigWithSameSig(MethodFlow bridgeMethod) { for (Object o : methods) { MethodFlow mf = (MethodFlow) o; if (mf == bridgeMethod) continue; if (mf.name.equals(bridgeMethod.name)) { String mfArgs = mf.desc.substring(0, mf.desc.indexOf(')')); String bmArgs = bridgeMethod.desc.substring(0, bridgeMethod.desc.indexOf(')')); if (mfArgs.equals(bmArgs)) return mf; } } return null; // throw new AssertionError("Bridge method found, but original method does not exist\nBridge method:" + // this.name + "::" + bridgeMethod.name + bridgeMethod.desc); } public String getClassDescriptor() { return classDesc; } public String getClassName() { return super.name.replace('/', '.'); } public boolean isPausable() { getMethodFlows(); // check analyze has been run. return isPausable; } boolean isInterface() { return (this.access & Opcodes.ACC_INTERFACE) != 0; } public Detector detector() { return detector; } }