PageRenderTime 29ms CodeModel.GetById 2ms app.highlight 22ms RepoModel.GetById 1ms app.codeStats 0ms

/src/kilim/analysis/ClassFlow.java

http://github.com/kilim/kilim
Java | 187 lines | 138 code | 24 blank | 25 comment | 26 complexity | 72ee80527de0433fcaf6eae597ceeef4 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 */
  6package kilim.analysis;
  7
  8import kilim.*;
  9import kilim.mirrors.Detector;
 10
 11import java.io.IOException;
 12import java.io.InputStream;
 13import java.util.ArrayList;
 14
 15import org.objectweb.asm.ClassReader;
 16import org.objectweb.asm.MethodVisitor;
 17import org.objectweb.asm.Opcodes;
 18import org.objectweb.asm.tree.ClassNode;
 19import org.objectweb.asm.tree.FieldNode;
 20
 21/**
 22 * This class reads a .class file (or stream), wraps each method with a MethodFlow object and optionally analyzes it.
 23 * 
 24 */
 25public class ClassFlow extends ClassNode {
 26    ArrayList<MethodFlow> methodFlows;
 27    public ClassReader           cr;
 28    String                classDesc;
 29    /**
 30     * true if any of the methods contained in the class file is pausable. ClassWeaver uses it later to avoid weaving if
 31     * isPausable isn't true.
 32     */
 33    private boolean       isPausable;
 34
 35    /**
 36     * true if the .class being read is already woven.
 37     */
 38    public boolean        isWoven = false;
 39    public KilimContext context;
 40
 41    /** the original bytecode associated with the class */
 42    public byte [] code;
 43
 44    public ClassFlow(KilimContext context,InputStream is) throws IOException {
 45        super(Constants.KILIM_ASM);
 46        this.context = context;
 47        cr = new ClassReader(is);
 48        code = cr.b;
 49    }
 50
 51    public ClassFlow(KilimContext context,String aClassName) throws IOException {
 52        super(Constants.KILIM_ASM);
 53        this.context = context;
 54        cr = new ClassReader(aClassName);
 55        code = cr.b;
 56    }
 57
 58
 59
 60    @Override
 61    @SuppressWarnings( { "unchecked" })
 62    public MethodVisitor visitMethod(
 63            final int access,
 64            final String name,
 65            final String desc,
 66            final String signature,
 67            final String[] exceptions)
 68    {
 69        MethodFlow mn = new MethodFlow( this, access, name,  desc, signature,
 70                exceptions, context.detector);
 71        super.methods.add(mn);
 72        return mn;
 73    }
 74
 75    public ArrayList<MethodFlow> getMethodFlows() {
 76        assert (methodFlows != null) : "ClassFlow.analyze not called";
 77        return methodFlows;
 78    }
 79
 80    public ArrayList<MethodFlow> analyze(boolean forceAnalysis) throws KilimException {
 81        // cr.accept(this, ClassReader.SKIP_DEBUG);
 82
 83        try {
 84            cr.accept(this, /*flags*/ClassReader.SKIP_FRAMES);
 85            for (Object o : this.fields) {
 86                FieldNode fn = (FieldNode) o;
 87                if (fn.name.equals(Constants.WOVEN_FIELD)) {
 88                    isWoven = true;
 89                    break;
 90                }
 91            }
 92            if (isWoven && !forceAnalysis) 
 93                return new ArrayList<MethodFlow>(); // This is a hack. 
 94
 95
 96            cr = null; // We don't need this any more.
 97            classDesc = TypeDesc.getInterned("L" + name + ';');
 98            ArrayList<MethodFlow> flows = new ArrayList<MethodFlow>(methods.size());
 99            String msg = "";
100            for (Object o : methods) {
101                try {
102                    MethodFlow mf = (MethodFlow) o;
103                    if (mf.isBridge()) {
104                        MethodFlow mmf = getOrigWithSameSig(mf);
105                        if (mmf != null)
106                            mf.setPausable(mmf.isPausable());
107                    }
108                    mf.verifyPausables();
109                    if (mf.isPausable())
110                        isPausable = true;
111                    if ((mf.needsWeaving() || forceAnalysis) && (!mf.isAbstract())) {
112                        mf.analyze();
113                    }
114                    flows.add(mf);
115                } catch (KilimException ke) {
116                    msg = msg + ke.getMessage() + "\n-------------------------------------------------\n";
117                }
118            }
119            if (msg.length() > 0) {
120                throw new KilimException(msg);
121            }
122            methodFlows = flows;
123            return flows;
124
125        } finally {
126        }
127    }
128
129    private MethodFlow getOrigWithSameSig(MethodFlow bridgeMethod) {
130        for (Object o : methods) {
131            MethodFlow mf = (MethodFlow) o;
132            if (mf == bridgeMethod)
133                continue;
134            if (mf.name.equals(bridgeMethod.name)) {
135                String mfArgs = mf.desc.substring(0, mf.desc.indexOf(')'));
136                String bmArgs = bridgeMethod.desc.substring(0, bridgeMethod.desc.indexOf(')'));
137                if (mfArgs.equals(bmArgs))
138                    return mf;
139            }
140        }
141        return null;
142        // throw new AssertionError("Bridge method found, but original method does not exist\nBridge method:" +
143        // this.name + "::" + bridgeMethod.name + bridgeMethod.desc);
144    }
145
146    public String getClassDescriptor() {
147        return classDesc;
148    }
149
150    public String getClassName() {
151        return super.name.replace('/', '.');
152    }
153
154    public boolean isPausable() {
155        getMethodFlows(); // check analyze has been run.
156        return isPausable;
157    }
158
159    boolean isInterface() {
160        return (this.access & Opcodes.ACC_INTERFACE) != 0;
161    }
162
163    boolean isJava7() {
164        return (version & 0x00FF) < 52;
165    }
166    
167    /*
168     * If this class is a functional interface, return the one "Single Abstract
169     * Method". If there is more than one abstract method, return null.
170     * SAM methods are given special treatment 
171     */
172    public MethodFlow getSAM() {
173        if (!isInterface() || isJava7()) {
174            return null;
175        }
176        MethodFlow sam = null;
177        for (MethodFlow mf: methodFlows) {
178            if (mf.isAbstract()) {
179                if (sam != null) {
180                    return null;
181                }
182                sam = mf;
183            }
184        }
185        return sam;
186    }
187}