PageRenderTime 38ms CodeModel.GetById 16ms app.highlight 18ms RepoModel.GetById 1ms app.codeStats 1ms

/src/kilim/mirrors/Detector.java

http://github.com/kilim/kilim
Java | 228 lines | 170 code | 41 blank | 17 comment | 49 complexity | a70b1d5f55ec4fcb2e7c9396a5706f5e 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.mirrors;
  7
  8import static kilim.Constants.D_OBJECT;
  9
 10import java.util.ArrayList;
 11
 12import kilim.NotPausable;
 13import kilim.Pausable;
 14import kilim.mirrors.CachedClassMirrors.ClassMirror;
 15import kilim.mirrors.CachedClassMirrors.MethodMirror;
 16
 17/**
 18 * Utility class to check if a method has been marked pausable
 19 * 
 20 */
 21public class Detector {
 22    public static final int METHOD_NOT_FOUND_OR_PAUSABLE = 0; // either not found, or not pausable if found.
 23    public static final int PAUSABLE_METHOD_FOUND = 1; // known to be pausable
 24    public static final int METHOD_NOT_PAUSABLE = 2; // known to be not pausable
 25    
 26
 27    // Note that we don't have the kilim package itself in the following list.
 28    static final String[] STANDARD_DONT_CHECK_LIST = { "java.", "javax." };
 29
 30    public final CachedClassMirrors mirrors;
 31
 32    public Detector(CachedClassMirrors mirrors) {
 33        this.mirrors = mirrors;
 34
 35        NOT_PAUSABLE = mirrors.mirror(NotPausable.class);
 36        PAUSABLE = mirrors.mirror(Pausable.class);
 37        OBJECT = mirrors.mirror(Object.class);
 38
 39    }
 40
 41    ClassMirror NOT_PAUSABLE, PAUSABLE, OBJECT;
 42
 43    public boolean isPausable(String className, String methodName, String desc) {
 44        return getPausableStatus(className, methodName, desc) == PAUSABLE_METHOD_FOUND;
 45    }
 46
 47    /**
 48     * @return one of METHOD_NOT_FOUND, PAUSABLE_METHOD_FOUND, METHOD_NOT_PAUSABLE
 49     */
 50
 51    static boolean isNonPausableClass(String className) {
 52        return className == null || className.charAt(0) == '[' || 
 53           className.startsWith("java.") || className.startsWith("javax.");
 54    }
 55    
 56    static boolean isNonPausableMethod(String methodName) {
 57        return methodName.endsWith("init>");
 58    }
 59
 60    
 61    public int getPausableStatus(String className, String methodName, String desc) {
 62        int ret = METHOD_NOT_FOUND_OR_PAUSABLE;
 63        // array methods (essentially methods deferred to Object (clone, wait etc)
 64        // and constructor methods are not pausable
 65        if (isNonPausableClass(className) || isNonPausableMethod(methodName)) {
 66            return METHOD_NOT_FOUND_OR_PAUSABLE; 
 67        }
 68        className = className.replace('/', '.');
 69        try {
 70            MethodMirror m = findPausableMethod(className, methodName, desc);
 71            if (m != null) {
 72                for (String ex : m.getExceptionTypes()) {
 73                    if (isNonPausableClass(ex)) continue;
 74                    ClassMirror c = classForName(ex);
 75                    if (NOT_PAUSABLE.isAssignableFrom(c)) {
 76                        return METHOD_NOT_PAUSABLE;
 77                    }
 78                    if (PAUSABLE.isAssignableFrom(c)) {
 79                        return PAUSABLE_METHOD_FOUND;
 80                    }
 81                }
 82                return METHOD_NOT_PAUSABLE;
 83            }
 84        } catch (ClassMirrorNotFoundException ignore) {
 85        }
 86        return ret;
 87    }
 88
 89    public ClassMirror classForName(String className) throws ClassMirrorNotFoundException {
 90        className = className.replace('/', '.');
 91        return mirrors.classForName(className);
 92    }
 93
 94    public ClassMirror[] classForNames(String[] classNames) throws ClassMirrorNotFoundException {
 95        if (classNames == null) {
 96            return new ClassMirror[0];
 97        }
 98        ClassMirror[] ret = new ClassMirror[classNames.length];
 99        int i = 0;
100        for (String cn : classNames) {
101            ret[i++] = classForName(cn);
102        }
103        return ret;
104    }
105
106    private MethodMirror findPausableMethod(String className, String methodName, String desc)
107            throws ClassMirrorNotFoundException {
108        
109        if (isNonPausableClass(className) || isNonPausableMethod(methodName)) 
110            return null;
111
112        ClassMirror cl = classForName(className);
113        if (cl == null) return null;
114        
115        for (MethodMirror om : cl.getDeclaredMethods()) {
116            if (om.getName().equals(methodName)) {
117                // when comparing descriptors only compare arguments, not return types
118                String omDesc= om.getMethodDescriptor();
119            
120                if (omDesc.substring(0,omDesc.indexOf(")")).equals(desc.substring(0,desc.indexOf(")")))) {
121                    if (om.isBridge())  continue;
122                    return om;
123                }
124            }
125        }
126
127        if (OBJECT.equals(cl))
128            return null;
129
130        MethodMirror m = findPausableMethod(cl.getSuperclass(), methodName, desc);
131        if (m != null)
132            return m;
133        
134        for (String ifname : cl.getInterfaces()) {
135            if (isNonPausableClass(ifname)) continue;
136            m = findPausableMethod(ifname, methodName, desc);
137            if (m != null)
138                return m;
139        }
140        return null;
141    }
142
143    @SuppressWarnings("unused")
144    private static String statusToStr(int st) {
145        switch (st) {
146        case METHOD_NOT_FOUND_OR_PAUSABLE:
147            return "not found or pausable";
148        case PAUSABLE_METHOD_FOUND:
149            return "pausable";
150        case METHOD_NOT_PAUSABLE:
151            return "not pausable";
152        default:
153            throw new AssertionError("Unknown status");
154        }
155    }
156
157
158    public String commonSuperType(String oa, String ob) throws ClassMirrorNotFoundException {
159        String a = toClassName(oa);
160        String b = toClassName(ob);
161
162        try {
163            ClassMirror ca = classForName(a);
164            ClassMirror cb = classForName(b);
165            if (ca.isAssignableFrom(cb))
166                return oa;
167            if (cb.isAssignableFrom(ca))
168                return ob;
169            if (ca.isInterface() && cb.isInterface()) {
170                return "java/lang/Object"; // This is what the java bytecode verifier does
171            }
172        } catch (ClassMirrorNotFoundException e) {
173            // try to see if the below works...
174        }
175
176        if (a.equals(b)) {
177        	return oa;
178        }
179        
180        ArrayList<String> sca = getSuperClasses(a);
181        ArrayList<String> scb = getSuperClasses(b);
182        int lasta = sca.size() - 1;
183        int lastb = scb.size() - 1;
184        do {
185            if (sca.get(lasta).equals(scb.get(lastb))) {
186                lasta--;
187                lastb--;
188            } else {
189                break;
190            }
191        } while (lasta >= 0 && lastb >= 0);
192        
193        if (sca.size() == lasta+1) {
194        	return "java/lang/Object";
195        }
196        
197        return sca.get(lasta + 1).replace('.', '/');
198    }
199
200    final private static ArrayList<String> EMPTY_STRINGS = new ArrayList<String>(0);
201    public ArrayList<String> getSuperClasses(String name) throws ClassMirrorNotFoundException {
202        if (name == null) {
203            return EMPTY_STRINGS;
204        }
205        ArrayList<String> ret = new ArrayList<String>(3);
206        while (name != null) {
207            ret.add(name);
208            ClassMirror c = classForName(name);
209            name = c.getSuperclass();
210        }
211        return ret;
212
213    }
214
215    private static String toDesc(String name) {
216        return (name.equals(JAVA_LANG_OBJECT)) ? D_OBJECT : "L" + name.replace('.', '/') + ';';
217    }
218
219    private static String toClassName(String s) {
220    	if (s.endsWith(";"))
221    		return s.replace('/', '.').substring(1, s.length() - 1);
222    	else
223    		return s.replace('/', '.');
224    }
225
226    static String JAVA_LANG_OBJECT = "java.lang.Object";
227
228}