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

kilim.WeavingClassLoader 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 2016 nqzero, 2014 sriram srinivasan - offered under the terms of the MIT License

package kilim;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.CodeSigner;
import java.security.CodeSource;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.HashMap;

import kilim.analysis.ClassInfo;
import kilim.analysis.ClassWeaver;
import kilim.analysis.FileLister;
import kilim.analysis.KilimContext;
import kilim.mirrors.CachedClassMirrors;
import kilim.tools.Agent;
import kilim.tools.Weaver;

// TODO: this and related real-time-weaving classes could be moved to the analysis package
//       allowing the ant kilim-runtime.jar (see build.xml) to be smaller

/**
 * Classloader that loads classes from the classpath spec given by the system property
 * "kilim.class.path" and weaves them dynamically.
 */
public class WeavingClassLoader extends KilimClassLoader {
    public static final String KILIM_CLASSPATH = "kilim.class.path";
    Weaver weaver;
    
    URLClassLoader proxy;

    public static byte [] readFully(InputStream is) {
        try {
            ByteArrayOutputStream buffer = new ByteArrayOutputStream();
            int num;
            byte[] data = new byte[1 << 12];
            while ((num = is.read( data, 0, data.length )) != -1)
                buffer.write(data, 0, num);
            buffer.flush();
            return buffer.toByteArray();
        }
        catch (IOException ex) { return null; }
    }

    ClassLoader pcl;
    static ClassLoader platform;
    static {
        ClassLoader last, cl = WeavingClassLoader.class.getClassLoader();
        for (last = cl; cl != null; cl = cl.getParent())
            last = cl;
        platform = last;
    }

    public static URL [] getURLs(String [] classPaths) {
        ArrayList urls = new ArrayList();
        for (String name : classPaths) {
            name = name.trim();
            if (name.equals("")) continue;
            try { urls.add(new File(name).toURI().toURL()); }
            catch (IOException ioe) {
                // System.err.println( "'" + name + "' does not exist. See property " +
                // KILIM_CLASSPATH);
            }
        }

        URL [] paths = urls.toArray(new URL[0]);
        return paths;
    }

    
    public WeavingClassLoader() {
        this(null,getProxy());
    }
    private static URLClassLoader getProxy() {
        String classPath = System.getProperty(KILIM_CLASSPATH, "");
        String[] classPaths = classPath.split(":");
        URL [] paths = getURLs(classPaths);
        return paths.length > 0 ? new URLClassLoader(paths) : null;
    }
    public WeavingClassLoader(ClassLoader loader,URLClassLoader $proxy) {
        proxy = $proxy;
        pcl = proxy != null
                ? proxy
                : loader != null ? loader:getClass().getClassLoader();

        CachedClassMirrors ccm = new CachedClassMirrors(pcl);
        KilimContext kc = new KilimContext(ccm);
        weaver = new Weaver(kc);
    }
    /** run static method className.method(args) using reflection and this WeavingClassLoader */
    public void run(String className,String method,String ... args) throws Exception {
        Class mainClass = loadClass(className);
        Method mainMethod = mainClass.getMethod(method, new Class[]{String[].class});
        mainMethod.invoke(null,new Object[] {args});
    }

    /** 
     * replace the exclude filter which determines whether a named class should be defined
     * definitively by this loader or instead delegated to the parent loader.
     * this is sometimes necessary to work with classes that may have been previously loaded,
     * eg because they were used as a java agent
     * @param exclude the filter to run
     * @return 
     */
    public WeavingClassLoader exclude(Excludable exclude) { checker = exclude; return this; }
    public static interface Excludable {
        /**
         * check whether the named class should be loaded by the parent loader
         * @param name the name of the class to check
         * @return true if this class loader should delegate to the parent loader
         */
        boolean check(String name);
    }
    private Excludable checker;
    
    
    public Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
        Class klass = null;
        try { klass = platform.loadClass(name); } catch (ClassNotFoundException ex) {}
        if (klass==null)
            klass = findLoadedClass(name);
        if (klass==null & (checker != null && checker.check(name)))
            klass = pcl.loadClass(name);
        if (klass==null) synchronized (this) {
            klass = findLoadedClass(name);
            if (klass==null)
                klass = findClass( name );
        }
        if (resolve) resolveClass( klass );
        return klass;
    }
    private boolean proxy(String cname) { 
        return proxy != null && proxy.findResource(cname) == null;
    }
    
    private Class defineAll(String name,ClassWeaver cw) {
        Class ret = null;
        for (ClassInfo ci : cw.getClassInfos()) {
            if (findLoadedClass(ci.className)==null) {
                Class c = define(ci.className, ci.bytes);
                if      (ci.className.equals(name))          ret = c;
                else if (ci.className.startsWith("kilim.S")) resolveClass(c);
            }
        }
        return ret;
    }

    public static InputStream getByteStream(ClassLoader cl,String name,String cname) {
        InputStream is = cl.getResourceAsStream( cname );
        if (is==null) is = ClassLoader.getSystemResourceAsStream( cname );
        if (is==null & Agent.map != null) {
            // force the nominal class loader to load the bytecode so that the agent sees it
            try { cl.loadClass(name); }
            catch (Exception ex) {}
            byte [] bytes = Agent.map.get(cname);
            if (bytes != null) is = new ByteArrayInputStream(bytes);
        }
        return is;
    }
    
    /**
     * load the bytecode for a class of a given name from the classpath and weave it
     * @param name the fully qualified class name
     * @return the weaver
     */    
    public ClassWeaver weaveClass(String name) {
        String cname = makeResourceName(name);
        InputStream is = getByteStream(pcl,name,cname);
        ClassWeaver cw = weaver.weave(is);
        return cw;
    }
    protected Class findClass(String name) throws ClassNotFoundException {
        String cname = makeResourceName(name);
        
        InputStream is = getByteStream(pcl,name,cname);
        ClassWeaver cw;
        byte [] code;

        if (is==null) {}
        else if (proxy(cname)) {
            if ((code=readFully(is)) != null)
                return define(name,code);
        }
        else if ((cw = weaver.weave(is)) != null) {
            Class ret = defineAll(name,cw);
            return ret==null ? define(name, cw.classFlow.code) : ret;
        }
        throw new ClassNotFoundException();
    }

    private final HashMap cache = new HashMap();
    private ProtectionDomain get(String name) {
        URL url = url(name);
        if (url==null) return null;

        ProtectionDomain pd = null;
        synchronized (cache) {
            pd = cache.get(url);
            if (pd == null) {
                CodeSource cs = new CodeSource(url,(CodeSigner []) null);
                pd = new ProtectionDomain(cs, null, this, null);
                cache.put(url, pd);
            }
        }
        return pd;
    }
    
    public Class define(String name,byte [] code) {
        CachedClassMirrors.ClassMirror cm = null;
        return defineClass(name, code, 0, code.length, get(name));
    }

    /**
     * convert a fully qualified class name to a resource name. Note: the Class and ClassLoader
     * javadocs don't explicitly specify the string formats used for the various methods, so
     * this conversion is potentially fragile
     * @param name as returned by Class.getName
     * @return the name in a format suitable for use with the various ClassLoader.getResource methods
     */
    // https://docs.oracle.com/javase/8/docs/technotes/guides/lang/resources.html#res_names
    public static String makeResourceName(String name) { return name.replace('.','/') + ".class"; }
    
    /** 
     * read bytecode for the named class from a source classloader
     * @param loader the classloader to get the bytecode from, or null for the current classloader
     * @param name the internal name for the class as would be passed to loadClass
     * @return a new instance, or null if the bytecode is not found
     */
    public static byte [] findCode(ClassLoader loader,String name) {
        if (loader==null) loader = WeavingClassLoader.class.getClassLoader();
        String cname = makeResourceName(name);
        InputStream is = getByteStream(loader,name,cname);
        if (is != null)
            return readFully(is);
        return null;
    }
    public URL url(String name) {
        String cname = makeResourceName(name);
        URL url = pcl.getResource( cname ), ret = null;
        if (url==null) url = ClassLoader.getSystemResource( cname );
        if (url==null) return null;
        String path = url.getPath();

        boolean matches = path.endsWith(cname);
        assert matches : "url code source doesn't match expectation: " + name + ", " + path;
        if (! matches) return null;
        
        try {
            ret = new URL(url,path.replace(cname,""));
        }
        catch (Exception ex) {}
        return ret;
    }


    
    public static byte[] readFully(FileLister.Entry fe) throws IOException {
        DataInputStream in = new DataInputStream(fe.getInputStream());
        byte[] contents = new byte[(int)fe.getSize()];
        in.readFully(contents);
        in.close();
        return contents;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy