All Downloads are FREE. Search and download functionalities are using the official Maven repository.

kilim.mirrors.Detector Maven / Gradle / Ivy

Go to download

Coroutines, continuations, fibers, actors and message passing for the JVM

There is a newer version: 2.0.2-jdk7
Show newest version
/* Copyright (c) 2006, Sriram Srinivasan
 *
 * You may distribute this software under the terms of the license 
 * specified in the file "License"
 */
package kilim.mirrors;

import static kilim.Constants.D_OBJECT;

import java.util.ArrayList;

import kilim.NotPausable;
import kilim.Pausable;
import kilim.analysis.AsmDetector;
import kilim.mirrors.CachedClassMirrors.ClassMirror;
import kilim.mirrors.CachedClassMirrors.MethodMirror;

/**
 * Utility class to check if a method has been marked pausable
 * 
 */
public class Detector {
    public static final int METHOD_NOT_FOUND_OR_PAUSABLE = 0; // either not found, or not pausable if found.
    public static final int PAUSABLE_METHOD_FOUND = 1; // known to be pausable
    public static final int METHOD_NOT_PAUSABLE = 2; // known to be not pausable
    

    // Note that we don't have the kilim package itself in the following list.
    static final String[] STANDARD_DONT_CHECK_LIST = { "java.", "javax." };

    public final CachedClassMirrors mirrors;

    public Detector(CachedClassMirrors mirrors) {
        this.mirrors = mirrors;

        NOT_PAUSABLE = mirrors.mirror(NotPausable.class);
        PAUSABLE = mirrors.mirror(Pausable.class);
        OBJECT = mirrors.mirror(Object.class);

    }

    ClassMirror NOT_PAUSABLE, PAUSABLE, OBJECT;

    public boolean isPausable(String className, String methodName, String desc) {
        return getPausableStatus(className, methodName, desc) == PAUSABLE_METHOD_FOUND;
    }

    /**
     * @return one of METHOD_NOT_FOUND, PAUSABLE_METHOD_FOUND, METHOD_NOT_PAUSABLE
     */

    static boolean isNonPausableClass(String className) {
        return className == null || className.charAt(0) == '[' || 
           className.startsWith("java.") || className.startsWith("javax.");
    }
    
    static boolean isNonPausableMethod(String methodName) {
        return methodName.endsWith("init>");
    }

    
    public int getPausableStatus(String className, String methodName, String desc) {
        int ret = METHOD_NOT_FOUND_OR_PAUSABLE;
        // array methods (essentially methods deferred to Object (clone, wait etc)
        // and constructor methods are not pausable
        if (isNonPausableClass(className) || isNonPausableMethod(methodName)) {
            return METHOD_NOT_FOUND_OR_PAUSABLE; 
        }
        className = className.replace('/', '.');
        try {
            MethodMirror m = findPausableMethod(className, methodName, desc);
            if (m != null) {
                for (String ex : m.getExceptionTypes()) {
                    if (isNonPausableClass(ex)) continue;
                    ClassMirror c = classForName(ex);
                    if (NOT_PAUSABLE.isAssignableFrom(c)) {
                        return METHOD_NOT_PAUSABLE;
                    }
                    if (PAUSABLE.isAssignableFrom(c)) {
                        return PAUSABLE_METHOD_FOUND;
                    }
                }
                return METHOD_NOT_PAUSABLE;
            }
        } catch (ClassMirrorNotFoundException ignore) {

        } catch (VerifyError ve) {
            return AsmDetector.getPausableStatus(className, methodName, desc, this);
        }
        return ret;
    }

    public ClassMirror classForName(String className) throws ClassMirrorNotFoundException {
        className = className.replace('/', '.');
        return mirrors.classForName(className);
    }

    public ClassMirror[] classForNames(String[] classNames) throws ClassMirrorNotFoundException {
        if (classNames == null) {
            return new ClassMirror[0];
        }
        ClassMirror[] ret = new ClassMirror[classNames.length];
        int i = 0;
        for (String cn : classNames) {
            ret[i++] = classForName(cn);
        }
        return ret;
    }

    private MethodMirror findPausableMethod(String className, String methodName, String desc)
            throws ClassMirrorNotFoundException {
        
        if (isNonPausableClass(className) || isNonPausableMethod(methodName)) 
            return null;

        ClassMirror cl = classForName(className);
        if (cl == null) return null;
        
        for (MethodMirror om : cl.getDeclaredMethods()) {
            if (om.getName().equals(methodName)) {
                // when comparing descriptors only compare arguments, not return types
                String omDesc= om.getMethodDescriptor();
            
                if (omDesc.substring(0,omDesc.indexOf(")")).equals(desc.substring(0,desc.indexOf(")")))) {
                    if (om.isBridge())  continue;
                    return om;
                }
            }
        }

        if (OBJECT.equals(cl))
            return null;

        MethodMirror m = findPausableMethod(cl.getSuperclass(), methodName, desc);
        if (m != null)
            return m;
        
        for (String ifname : cl.getInterfaces()) {
            if (isNonPausableClass(ifname)) continue;
            m = findPausableMethod(ifname, methodName, desc);
            if (m != null)
                return m;
        }
        return null;
    }

    @SuppressWarnings("unused")
    private static String statusToStr(int st) {
        switch (st) {
        case METHOD_NOT_FOUND_OR_PAUSABLE:
            return "not found or pausable";
        case PAUSABLE_METHOD_FOUND:
            return "pausable";
        case METHOD_NOT_PAUSABLE:
            return "not pausable";
        default:
            throw new AssertionError("Unknown status");
        }
    }


    public String commonSuperType(String oa, String ob) throws ClassMirrorNotFoundException {
        String a = toClassName(oa);
        String b = toClassName(ob);

        try {
            ClassMirror ca = classForName(a);
            ClassMirror cb = classForName(b);
            if (ca.isAssignableFrom(cb))
                return oa;
            if (cb.isAssignableFrom(ca))
                return ob;
            if (ca.isInterface() && cb.isInterface()) {
                return "java/lang/Object"; // This is what the java bytecode verifier does
            }
        } catch (ClassMirrorNotFoundException e) {
            // try to see if the below works...
        }

        if (a.equals(b)) {
        	return oa;
        }
        
        ArrayList sca = getSuperClasses(a);
        ArrayList scb = getSuperClasses(b);
        int lasta = sca.size() - 1;
        int lastb = scb.size() - 1;
        do {
            if (sca.get(lasta).equals(scb.get(lastb))) {
                lasta--;
                lastb--;
            } else {
                break;
            }
        } while (lasta >= 0 && lastb >= 0);
        
        if (sca.size() == lasta+1) {
        	return "java/lang/Object";
        }
        
        return sca.get(lasta + 1).replace('.', '/');
    }

    final private static ArrayList EMPTY_STRINGS = new ArrayList(0);
    public ArrayList getSuperClasses(String name) throws ClassMirrorNotFoundException {
        if (name == null) {
            return EMPTY_STRINGS;
        }
        ArrayList ret = new ArrayList(3);
        while (name != null) {
            ret.add(name);
            ClassMirror c = classForName(name);
            name = c.getSuperclass();
        }
        return ret;

    }

    private static String toDesc(String name) {
        return (name.equals(JAVA_LANG_OBJECT)) ? D_OBJECT : "L" + name.replace('.', '/') + ';';
    }

    private static String toClassName(String s) {
    	if (s.endsWith(";"))
    		return s.replace('/', '.').substring(1, s.length() - 1);
    	else
    		return s.replace('/', '.');
    }

    static String JAVA_LANG_OBJECT = "java.lang.Object";

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy