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

co.paralleluniverse.fibers.instrument.OldSuspendablesScanner Maven / Gradle / Ivy

Go to download

The core library for Fibers on Java, compatible with Java 11-16. Forked from puniverse/quasar

There is a newer version: 10.0.6
Show newest version
/*
 * Quasar: lightweight threads and actors for the JVM.
 * Copyright (c) 2013-2014, Parallel Universe Software Co. All rights reserved.
 * 
 * This program and the accompanying materials are dual-licensed under
 * either the terms of the Eclipse Public License v1.0 as published by
 * the Eclipse Foundation
 *  
 *   or (per the licensee's choosing)
 *  
 * under the terms of the GNU Lesser General Public License version 3.0
 * as published by the Free Software Foundation.
 */
package co.paralleluniverse.fibers.instrument;

import co.paralleluniverse.common.reflection.ASMUtil;
import co.paralleluniverse.fibers.Suspendable;
import static co.paralleluniverse.common.reflection.ASMUtil.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.FileSet;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;

/**
 *
 * @author pron
 */
public class OldSuspendablesScanner extends Task {
    private static final boolean USE_REFLECTION = false;
    private static final String CLASSFILE_SUFFIX = ".class";
    private URLClassLoader cl;
    private final ArrayList filesets = new ArrayList();
    private final Set results = new HashSet();
    private String supersFile;
    private boolean append;
    private SimpleSuspendableClassifier ssc;
    private String suspendablesFile;

    public void addFileSet(FileSet fs) {
        filesets.add(fs);
    }

    public void setOutputFile(String outputFile) {
        this.supersFile = outputFile;
    }

    public void setAppend(boolean value) {
        this.append = value;
    }

    public void setSuspendablesFile(String suspendablesFile) {
        this.suspendablesFile = suspendablesFile;
    }

    public void run(String[] prefixes) throws Exception {
        for (String prefix : prefixes)
            collect(prefix);
        outputResults(supersFile, append, results);
    }

    public void nonAntExecute(String[] paths) throws Exception {
        readSuspandables();
        if (USE_REFLECTION)
            log("Using reflection", Project.MSG_INFO);
        List urls = new ArrayList<>();
        for (String path : paths)
            urls.add(new File(path).toURI().toURL());
        log("URLs: " + urls, Project.MSG_VERBOSE);

        cl = new URLClassLoader(urls.toArray(new URL[0]), getClass().getClassLoader());
        for (String path : paths) {
            for (File file : recursiveWalk(path)) {
                if (file.getName().endsWith(CLASSFILE_SUFFIX) && file.isFile())
                    scanClass(file);
            }
        }
        scanSuspendablesFile();
        outputResults(supersFile, append, results);
    }

    @Override
    public void execute() throws BuildException {
        readSuspandables();
        if (USE_REFLECTION)
            log("Using reflection", Project.MSG_INFO);
        try {
            List urls = new ArrayList<>();
            for (FileSet fs : filesets)
                urls.add(fs.getDir().toURI().toURL());
            cl = new URLClassLoader(urls.toArray(new URL[0]), getClass().getClassLoader());
            log("URLs: " + Arrays.toString(cl.getURLs()), Project.MSG_INFO);

            // scan classes in filesets
            for (FileSet fs : filesets) {
                try {
                    final DirectoryScanner ds = fs.getDirectoryScanner(getProject());
                    final String[] includedFiles = ds.getIncludedFiles();

                    for (String filename : includedFiles) {
                        if (filename.endsWith(CLASSFILE_SUFFIX)) {
                            File file = new File(fs.getDir(), filename);
                            if (file.isFile())
                                scanClass(file);
                            else
                                log("File not found: " + filename);
                        }
                    }
                } catch (BuildException ex) {
                    log(ex.getMessage(), ex, Project.MSG_WARN);
                }
            }

            scanSuspendablesFile();
            log("OUTPUT: " + supersFile, Project.MSG_INFO);
            outputResults(supersFile, append, results);
        } catch (Exception e) {
            log(e, Project.MSG_ERR);
            throw new BuildException(e);
        }
    }

    private void scanSuspendablesFile() throws Exception {
        // scan classes in suspendables file
        if (ssc != null) {
            Set classes = new HashSet<>();
            for (String susCls : ssc.getSuspendableClasses())
                classes.add(susCls);
            for (String susMethod : ssc.getSuspendables())
                classes.add(susMethod.substring(0, susMethod.indexOf('.')));
            for (String className : classes) {
                log("scanning suspendable class:" + className, Project.MSG_VERBOSE);
                scanClass(getClassNode(className, cl, true));
            }
        }
    }

    public void readSuspandables() {
        if (suspendablesFile != null) {
            if (!new File(suspendablesFile).isFile())
                log("suspendable file " + suspendablesFile + " not found", Project.MSG_INFO);
            ssc = new SimpleSuspendableClassifier(suspendablesFile);
            log("suspendablesFile: " + suspendablesFile, Project.MSG_INFO);
        }
    }

    private Set collect(String prefix) throws Exception {
        prefix = prefix.trim();
        prefix = prefix.replace('.', '/');
        for (Enumeration urls = ClassLoader.getSystemResources(prefix); urls.hasMoreElements();) {
            URL url = urls.nextElement();
            File file = new File(url.getFile());
            if (file.isDirectory())
                scanClasses(file);
        }
        return results;
    }

    private void scanClasses(File file) throws Exception {
        if (file.isDirectory()) {
            System.out.println("Scanning dir: " + file.getPath());
            for (File f : file.listFiles())
                scanClasses(f);
        } else
            scanClass(file);
    }

    private void scanClass(File file) throws Exception {
        log("Scanning " + file, Project.MSG_VERBOSE);
        if (file != null) {
            if (USE_REFLECTION)
                scanClass(Class.forName(extractClassName(file)));
            else
                scanClass(getClassNode(new FileInputStream(file), true));
        }
    }

    private static void outputResults(String outputFile, boolean append1, Collection results) throws Exception {
        try (PrintStream out = getOutputStream(outputFile, append1)) {
            List sorted = new ArrayList<>(results);
            Collections.sort(sorted);
            for (String s : sorted) {
                // if (out != System.out)
                //    System.out.println(s);
                out.println(s);
            }
        }
    }

    private static PrintStream getOutputStream(String outputFile, boolean append1) throws Exception {
        if (outputFile != null) {
            outputFile = outputFile.trim();
            if (outputFile.isEmpty())
                outputFile = null;
        }
        if (outputFile != null) {
            File file = new File(outputFile);
            if (file.getParent() != null && !file.getParentFile().exists())
                file.getParentFile().mkdirs();
            return new PrintStream(new FileOutputStream(file, append1));
        } else
            return System.out;
    }

    boolean isSuspendable(ClassNode cls, MethodNode m) {
        return hasAnnotation(Suspendable.class, m)
                || (ssc != null && ssc.isSuspendable(cls.name, m.name, m.desc));
    }

    /////////// ASM
    public void scanClass(ClassNode cls) throws Exception {
        List methods = cls.methods;
        for (MethodNode m : methods) {
            if (isSuspendable(cls, m)) {
                log("Found annotated method: " + cls.name + "." + m.name + m.signature, Project.MSG_VERBOSE);
                findSuperDeclarations(cls, cls, m);
            }
        }
    }

    private boolean findSuperDeclarations(ClassNode cls, ClassNode declaringClass, MethodNode method) throws IOException {
        if (cls == null)
            return false;

        boolean foundMethod = false;
        MethodNode m;
        if ((m = getMethod(method, cls)) != null) {
            foundMethod = true;
            if (!ASMUtil.equals(cls, declaringClass) && !isSuspendable(cls, m)) {
                log("Found parent of annotated method: " + declaringClass.name + "." + method.name + method.signature + " in " + cls.name, Project.MSG_VERBOSE);
                results.add(cls.name.replace('/', '.') + '.' + method.name);
            }
        }

        // recursively look in superclass and interfaces
        boolean methodInParent = false;
        methodInParent |= findSuperDeclarations(getClassNode(cls.superName, cl, true), declaringClass, method);
        for (String iface : (List) cls.interfaces)
            methodInParent |= findSuperDeclarations(getClassNode(iface, cl, true), declaringClass, method);
        if (!foundMethod && methodInParent) {
            log("Found parent of annotated method in a parent of: " + declaringClass.name + "." + method.name + method.signature + " in " + cls.name, Project.MSG_VERBOSE);
            results.add(cls.name.replace('/', '.') + '.' + method.name);
        }

        return foundMethod | methodInParent;
    }

    ///////// REFLECTION
    private String extractClassName(File file) {
        String fileName = file.getPath();
        URL[] urls = ((URLClassLoader) cl).getURLs();
        for (URL url : urls) {
            if (fileName.startsWith(url.getPath())) {
                String className = fileName.substring(url.getPath().length(),
                        fileName.length() - CLASSFILE_SUFFIX.length()).replace('/', '.');
                return className;
            }
        }
        throw new RuntimeException();
    }

    private void scanClass(Class cls) throws Exception {
        Method[] methods = cls.getDeclaredMethods();
        for (Method m : methods) {
            if (m.isAnnotationPresent(Suspendable.class))
                findSuperDeclarations(cls, m);
        }
    }

    private void findSuperDeclarations(Class cls, Method method) {
        if (cls == null)
            return;

        if (!cls.equals(method.getDeclaringClass())) {
            try {
                cls.getDeclaredMethod(method.getName(), method.getParameterTypes());
                results.add(cls.getName() + '.' + method.getName());
            } catch (NoSuchMethodException e) {
            }
        }

        // recursively look in superclass and interfaces
        findSuperDeclarations(cls.getSuperclass(), method);
        for (Class iface : cls.getInterfaces())
            findSuperDeclarations(iface, method);
    }

    private List recursiveWalk(String path) {
        File[] list = new File(path).listFiles();
        List result = new ArrayList<>();
        if (list == null)
            return result;
        for (File f : list)
            if (f.isDirectory())
                result.addAll(recursiveWalk(f.getAbsolutePath()));
            else
                result.add(f);
        return result;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy