![JAR search and dependency download from the Maven repository](/logo.png)
co.paralleluniverse.fibers.instrument.SuspendablesScanner Maven / Gradle / Ivy
/*
* Quasar: lightweight threads and actors for the JVM.
* Copyright (c) 2013-2015, 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.ClassLoaderUtil;
import static co.paralleluniverse.common.reflection.ClassLoaderUtil.isClassFile;
import static co.paralleluniverse.common.reflection.ClassLoaderUtil.classToResource;
import static co.paralleluniverse.fibers.instrument.QuasarInstrumentor.ASMAPI;
import static co.paralleluniverse.fibers.instrument.Classes.SUSPENDABLE_DESC;
import static co.paralleluniverse.fibers.instrument.Classes.DONT_INSTRUMENT_DESC;
import static co.paralleluniverse.fibers.instrument.Classes.SUSPEND_EXECUTION_NAME;
import co.paralleluniverse.fibers.instrument.MethodDatabase.SuspendableType;
import com.google.common.base.Function;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLConnection;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.AbstractCollection;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import org.apache.tools.ant.AntClassLoader;
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.AnnotationVisitor;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Handle;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
public class SuspendablesScanner extends Task {
private final Map methods = new HashMap<>();
private final Map classes = new HashMap<>();
private final Set knownSuspendablesOrSupers = new HashSet<>();
private final boolean ant;
private URLClassLoader cl;
private final ArrayList filesets = new ArrayList<>();
private final Path projectDir;
private URL[] urls;
private SimpleSuspendableClassifier ssc;
private boolean auto = true;
private boolean append = false;
private String supersFile;
private String suspendablesFile;
private boolean quiet;
public SuspendablesScanner() {
this.ant = getClass().getClassLoader() instanceof AntClassLoader;
this.projectDir = null;
this.quiet = true;
}
public SuspendablesScanner(Path projectDir) {
this.quiet = true;
this.ant = getClass().getClassLoader() instanceof AntClassLoader;
this.projectDir = projectDir;
assert !ant;
}
public void addFileSet(FileSet fs) {
filesets.add(fs);
}
public void setSuspendablesFile(String outputFile) {
this.suspendablesFile = outputFile;
}
public void setSupersFile(String outputFile) {
this.supersFile = outputFile;
}
/**
* Whether suspendables should be found based on the method call-graph.
* If false, only suspendable-supers of known suspendables will be found.
*/
public void setAuto(boolean value) {
this.auto = value;
}
/**
* Whether the found methods should be appended to the output files(s) or replace them.
*/
public void setAppend(boolean value) {
this.append = value;
}
void setURLs(List urls) {
this.urls = unique(urls).toArray(new URL[0]);
this.cl = new URLClassLoader(this.urls);
this.ssc = new SimpleSuspendableClassifier(cl);
}
private static List unique(List list) {
return new ArrayList<>(new LinkedHashSet<>(list));
}
@Override
public void execute() throws BuildException {
try {
run();
log("OUTPUT: " + supersFile, Project.MSG_INFO);
log("OUTPUT: " + suspendablesFile, Project.MSG_INFO);
// output results
final ArrayList suspendables = suspendablesFile != null ? new ArrayList() : null;
final ArrayList suspendableSupers = supersFile != null ? new ArrayList() : null;
putSuspendablesAndSupers(suspendables, suspendableSupers);
if (suspendablesFile != null) {
Collections.sort(suspendables);
outputResults(suspendablesFile, append, suspendables);
}
if (supersFile != null) {
Collections.sort(suspendableSupers);
outputResults(supersFile, append, suspendableSupers);
}
} catch (Exception e) {
log(e, Project.MSG_ERR);
throw new BuildException(e);
}
}
public void run() {
try {
// System.err.println("QQQQQQQQ: " + new SimpleSuspendableClassifier(supersFile).getSuspendableClasses());
final List us = new ArrayList<>();
if (ant) {
final AntClassLoader acl = (AntClassLoader) getClass().getClassLoader();
classpathToUrls(acl.getClasspath().split(System.getProperty("path.separator")), us);
for (FileSet fs : filesets)
us.add(fs.getDir().toURI().toURL());
} else {
// final URLClassLoader ucl = (URLClassLoader) getClass().getClassLoader();
// us.addAll(Arrays.asList(ucl.getURLs()));
}
if (this.urls != null) // only in tests
us.addAll(Arrays.asList(this.urls));
setURLs(us);
log("Classpath URLs: " + Arrays.toString(this.urls), Project.MSG_INFO);
List pus = new ArrayList<>();
if (ant) {
for (FileSet fs : filesets)
pus.add(fs.getDir().toURI().toURL());
} else
pus.add(projectDir.toUri().toURL());
log("Project URLs: " + pus, Project.MSG_INFO);
final long tStart = System.nanoTime();
scanExternalSuspendables();
final long tScanExternal = System.nanoTime();
if (auto)
log("Scanned external suspendables in " + (tScanExternal - tStart) / 1000000 + " ms", Project.MSG_INFO);
// scan classes in filesets
Function fileVisitor = new Function() {
@Override
public Void apply(InputStream is1) {
try (InputStream is = is1) {
createGraph(is);
return null;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
};
if (ant)
visitAntProject(fileVisitor);
else
visitProjectDir(fileVisitor);
scanSuspendablesFile(fileVisitor);
final long tBuildGraph = System.nanoTime();
log("Built method graph in " + (tBuildGraph - tScanExternal) / 1000000 + " ms", Project.MSG_INFO);
walkGraph();
final long tWalkGraph = System.nanoTime();
log("Walked method graph in " + (tWalkGraph - tBuildGraph) / 1000000 + " ms", Project.MSG_INFO);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void log(Throwable t, int msgLevel) {
if (quiet) {
return;
}
super.log(t, msgLevel);
}
@Override
public void log(String msg, Throwable t, int msgLevel) {
if (quiet) {
return;
}
super.log(msg, t, msgLevel);
}
@Override
public void log(String msg, int msgLevel) {
if (quiet) {
return;
}
super.log(msg, msgLevel);
}
@Override
public void log(String msg) {
if (quiet) {
return;
}
super.log(msg);
}
private void scanExternalSuspendables() throws IOException {
ClassLoaderUtil.accept(cl, new ClassLoaderUtil.Visitor() {
@Override
public void visit(String resource, URL url, ClassLoader cl) throws IOException {
if (resource.startsWith("java/util") || resource.startsWith("java/lang") || resource.startsWith("co/paralleluniverse/asm"))
return;
if (isClassFile(url.getFile())) {
URLConnection uc = url.openConnection();
uc.setUseCaches(false);
try (InputStream is = uc.getInputStream()) {
if (is == null)
throw new IOException("Resource " + resource + " not found (" + url + ")");
new ClassReader(is)
.accept(new SuspendableClassifier(false, ASMAPI, null), ClassReader.SKIP_DEBUG | ClassReader.SKIP_CODE);
} catch (Exception e) {
throw new RuntimeException("Exception thrown during processing (aaa) of " + resource + /*"(classloader: " + Arrays.toString(SuspendablesScanner.this.cl.getURLs()) + ")" +*/ " at " + url, e);
}
}
}
});
}
private void visitAntProject(Function classFileVisitor) throws IOException {
for (FileSet fs : filesets) {
try {
final DirectoryScanner ds = fs.getDirectoryScanner(getProject());
final String[] includedFiles = ds.getIncludedFiles();
for (String filename : includedFiles) {
if (isClassFile(filename)) {
try {
File file = new File(fs.getDir(), filename);
if (file.isFile())
classFileVisitor.apply(new FileInputStream(file));
else
log("File not found: " + filename);
} catch (Exception e) {
throw new RuntimeException("Exception while processing " + filename, e);
}
}
}
} catch (BuildException ex) {
log(ex.getMessage(), ex, Project.MSG_WARN);
}
}
}
private void visitProjectDir(final Function classFileVisitor) throws IOException {
Files.walkFileTree(projectDir, new SimpleFileVisitor() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
try {
if (isClassFile(file.getFileName().toString()))
classFileVisitor.apply(Files.newInputStream(file));
return FileVisitResult.CONTINUE;
} catch (Exception e) {
throw new RuntimeException("Exception while processing " + file, e);
}
}
});
}
/**
* Visits classes whose methods are found in the suspendables file, as if they were part of the project
*/
private void scanSuspendablesFile(Function classFileVisitor) {
// scan classes in suspendables file
if (suspendablesFile != null) {
SimpleSuspendableClassifier tssc = new SimpleSuspendableClassifier(suspendablesFile);
final Set cs = new HashSet<>();
cs.addAll(tssc.getSuspendableClasses());
for (String susMethod : tssc.getSuspendables())
cs.add(susMethod.substring(0, susMethod.indexOf('.')));
for (String className : cs) {
try {
log("Scanning suspendable class:" + className, Project.MSG_VERBOSE);
classFileVisitor.apply(cl.getResourceAsStream(classToResource(className)));
} catch (Exception e) {
throw new RuntimeException("Exception while processing " + className, e);
}
}
}
}
private class SuspendableClassifier extends ClassVisitor {
private final boolean inProject;
private String className;
private boolean suspendableClass;
public SuspendableClassifier(boolean inProject, int api, ClassVisitor cv) {
super(api, cv);
this.inProject = inProject;
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
super.visit(version, access, name, signature, superName, interfaces);
this.className = name.intern();
log("Searching suspendables in " + className, Project.MSG_DEBUG);
}
@Override
public AnnotationVisitor visitAnnotation(String adesc, boolean visible) {
final AnnotationVisitor av = super.visitAnnotation(adesc, visible);
if (adesc.equals(SUSPENDABLE_DESC))
suspendableClass = true;
return av;
}
@Override
public MethodVisitor visitMethod(int access, final String methodname, final String desc, String signature, String[] exceptions) {
final MethodVisitor mv = super.visitMethod(access, methodname, desc, signature, exceptions);
SuspendableType suspendable = SuspendableType.NON_SUSPENDABLE;
final boolean noImpl = (access & (Opcodes.ACC_ABSTRACT | Opcodes.ACC_NATIVE)) != 0;
if (suspendableClass)
suspendable = noImpl ? SuspendableType.SUSPENDABLE_SUPER : SuspendableType.SUSPENDABLE;
if (suspendable != SuspendableType.SUSPENDABLE && checkExceptions(exceptions))
suspendable = noImpl ? SuspendableType.SUSPENDABLE_SUPER : SuspendableType.SUSPENDABLE;
if (suspendable != SuspendableType.SUSPENDABLE && ssc.isSuperSuspendable(className, methodname, desc))
suspendable = max(suspendable, SuspendableType.SUSPENDABLE_SUPER);
if (suspendable != SuspendableType.SUSPENDABLE && ssc.isSuspendable(className, methodname, desc))
suspendable = max(suspendable, SuspendableType.SUSPENDABLE);
final SuspendableType suspendable1 = suspendable;
return new MethodVisitor(api, mv) {
private SuspendableType susp = suspendable1 != SuspendableType.NON_SUSPENDABLE ? suspendable1 : null;
@Override
public AnnotationVisitor visitAnnotation(String adesc, boolean visible) {
final AnnotationVisitor av = super.visitAnnotation(desc, visible);
if (SUSPENDABLE_DESC.equals(adesc))
susp = noImpl ? SuspendableType.SUSPENDABLE_SUPER : SuspendableType.SUSPENDABLE;
else if (DONT_INSTRUMENT_DESC.equals(adesc))
susp = SuspendableType.NON_SUSPENDABLE;
return av;
}
@Override
public void visitEnd() {
super.visitEnd();
if (susp != null)
markKnownSuspendable(methodname, desc, susp);
}
};
}
private void markKnownSuspendable(String methodname, String desc, SuspendableType sus) {
final MethodNode method = getOrCreateMethodNode(className + '.' + methodname + desc);
method.owner = className;
method.inProject |= inProject;
method.setSuspendType(max(method.suspendType, sus));
method.known = true;
if (auto || inProject)
knownSuspendablesOrSupers.add(method);
log("Known suspendable " + className + '.' + methodname + desc, Project.MSG_VERBOSE);
}
private boolean checkExceptions(String[] exceptions) {
if (exceptions != null) {
for (String ex : exceptions) {
if (ex.equals(SUSPEND_EXECUTION_NAME))
return true;
}
}
return false;
}
private SuspendableType max(SuspendableType a, SuspendableType b) {
if (a == null)
return b;
if (b == null)
return a;
return b.compareTo(a) > 0 ? b : a;
}
}
private class ClassNodeVisitor extends ClassVisitor {
private final boolean inProject;
private String className;
private final List methods = new ArrayList<>();
private ClassNode cn;
public ClassNodeVisitor(boolean inProject, int api, ClassVisitor cv) {
super(api, cv);
this.inProject = inProject;
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
super.visit(version, access, name, signature, superName, interfaces);
this.className = name;
log("Loading and analyzing class " + className, Project.MSG_DEBUG);
cn = getOrCreateClassNode(className);
cn.inProject |= inProject;
cn.setSupers(superName, interfaces);
}
@Override
public MethodVisitor visitMethod(int access, String methodname, String desc, String signature, String[] exceptions) {
final MethodVisitor mv = super.visitMethod(access, methodname, desc, signature, exceptions);
methods.add((methodname + desc).intern());
return mv;
}
@Override
public void visitEnd() {
super.visitEnd();
cn.setMethods(methods);
}
}
private class CallGraphVisitor extends ClassVisitor {
private final boolean inProject;
private String className;
public CallGraphVisitor(boolean inProject, int api, ClassVisitor cv) {
super(api, cv);
this.inProject = inProject;
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
super.visit(version, access, name, signature, superName, interfaces);
this.className = name;
}
@Override
public MethodVisitor visitMethod(int access, final String methodname, final String desc, String signature, String[] exceptions) {
final MethodVisitor mv = super.visitMethod(access, methodname, desc, signature, exceptions);
final MethodNode caller = getOrCreateMethodNode(className + '.' + methodname + desc);
caller.inProject |= inProject;
return new MethodVisitor(api, mv) {
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
super.visitMethodInsn(opcode, owner, name, desc, itf);
if (isReflectInvocation(owner, name))
log("NOTE: Reflective invocation in " + methodToString(), Project.MSG_WARN);
else if (isInvocationHandlerInvocation(owner, name))
log("NOTE: Invocation handler invocation in " + methodToString(), Project.MSG_WARN);
else if (isMethodHandleInvocation(owner, name))
log("NOTE: Method handle invocation in " + methodToString(), Project.MSG_WARN);
else {
final MethodNode callee = getOrCreateMethodNode(owner + '.' + name + desc);
log("Adding caller " + caller + " to " + callee, Project.MSG_DEBUG);
callee.addCaller(caller);
}
}
@Override
public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
super.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
log("NOTE: InvokeDynamic invocation in " + methodToString(), Project.MSG_WARN);
}
private String methodToString() {
return (className + '.' + methodname + "(" + Arrays.toString(Type.getArgumentTypes(desc)) + ") - " + className + '.' + methodname + desc);
}
};
}
}
private void createGraph(InputStream classStream) throws IOException {
final ClassReader cr = new ClassReader(classStream);
ClassVisitor cv = null;
cv = new SuspendableClassifier(true, ASMAPI, cv);
cv = new ClassNodeVisitor(true, ASMAPI, cv);
if (auto)
cv = new CallGraphVisitor(true, ASMAPI, cv);
cr.accept(cv, ClassReader.SKIP_DEBUG | (auto ? 0 : ClassReader.SKIP_CODE));
}
private void walkGraph() {
final Queue q = new ArrayDeque<>();
q.addAll(knownSuspendablesOrSupers); // start the bfs from the manualSusp (all classpath)
while (!q.isEmpty()) {
final MethodNode m = q.poll();
if (m.inProject) {
followBridges(q, getClassNode(m), m);
followSupers(q, getClassNode(m), m);
}
followNonOverriddenSubs(q, getClassNode(m), m);
followCallers(q, m);
}
}
private void followBridges(Queue q, ClassNode cls, MethodNode method) {
log("followBridges " + method + " " + cls, Project.MSG_DEBUG);
if (cls == null)
return;
if (method.suspendType == SuspendableType.NON_SUSPENDABLE)
return;
final List bridges = cls.getMethodWithDifferentReturn(method.name);
for (String m1 : bridges) {
if (!method.name.equals(m1)) {
MethodNode m = getOrCreateMethodNode(cls.name + '.' + m1);
if (m.suspendType != SuspendableType.SUSPENDABLE && m.suspendType != SuspendableType.SUSPENDABLE_SUPER) {
m.setSuspendType(SuspendableType.SUSPENDABLE_SUPER);
m.inProject = method.inProject;
q.add(m);
}
}
}
}
private boolean followSupers(Queue q, ClassNode cls, MethodNode method) {
log("followSupers " + method + " " + cls, Project.MSG_DEBUG);
if (cls == null)
return false;
if (method.suspendType == SuspendableType.NON_SUSPENDABLE || method.setBySuper)
return false;
boolean foundMethod = false;
if (cls.hasMethod(method.name) && method.classNode != cls) {
final MethodNode m1 = methods.get((cls.name + '.' + method.name).intern());
if (m1 != null && m1.suspendType == SuspendableType.NON_SUSPENDABLE)
return false;
if (m1 == null || m1.suspendType == null) {
log("Found parent of suspendable method: " + method.owner + '.' + method.name + " in " + cls.name
+ (cls.inProject ? "" : " NOT IN PROJECT"), cls.inProject ? Project.MSG_VERBOSE : Project.MSG_WARN);
final MethodNode m = getOrCreateMethodNode(cls.name + '.' + method.name);
m.setSuspendType(SuspendableType.SUSPENDABLE_SUPER);
q.add(m);
foundMethod = true;
}
}
// recursively look in superclass and interfaces
boolean methodInParent = false;
for (ClassNode s : cls.supers)
methodInParent |= followSupers(q, fill(s), method);
if (!foundMethod && methodInParent)
log("Found parent of suspendable method in a parent of: " + method.owner + '.' + method.name + " in " + cls.name
+ (cls.inProject ? "" : " NOT IN PROJECT"), cls.inProject ? Project.MSG_VERBOSE : Project.MSG_WARN);
return foundMethod || methodInParent;
}
private void followNonOverriddenSubs(Queue q, ClassNode cls, MethodNode method) {
log("followNonOverriddenSubs " + method + " " + cls, Project.MSG_DEBUG);
if (cls == null)
return;
if (method.suspendType == SuspendableType.NON_SUSPENDABLE)
return;
if (cls.subs != null) {
for (ClassNode s : cls.subs) {
if (s != null && !s.hasMethod(method.name) && s.inProject) {
MethodNode sm = getOrCreateMethodNode(s.name + '.' + method.name);
sm.inProject = true;
sm.refersToSuper = true;
if (sm.inProject && sm.suspendType == null) {
sm.setSuspendType(method.suspendType);
sm.setBySuper = true;
q.add(sm);
followNonOverriddenSubs(q, s, sm);
}
}
}
}
}
private void followCallers(Queue q, MethodNode method) {
// mark as suspendables methods from the project which are calling of the given bfs node (which is superSuspenable or suspendable)
for (MethodNode caller : method.getCallers()) {
if (caller.suspendType == null) { // not yet visited
q.add(caller);
log("Marking " + caller + " suspendable because it calls " + method, Project.MSG_VERBOSE);
caller.setSuspendType(SuspendableType.SUSPENDABLE);
}
}
}
public void putSuspendablesAndSupers(Collection suspendables, Collection suspendableSupers) {
for (MethodNode method : methods.values()) {
if (!method.known) {
if (method.suspendType == SuspendableType.SUSPENDABLE && !method.refersToSuper && suspendables != null)
suspendables.add(output(method));
else if (method.suspendType == SuspendableType.SUSPENDABLE_SUPER && !method.refersToSuper && suspendableSupers != null)
suspendableSupers.add(output(method));
// we don't output refersToSuper methods because the instrumentor's MethodDatabase.isMethodSuspendable0 does that traversal again.
}
}
}
private static String output(MethodNode method) {
return method.owner.replace('/', '.') + '.' + method.name;
}
private static void outputResults(String outputFile, boolean append, Collection results) throws Exception {
try (PrintStream out = getOutputStream(outputFile, append)) {
for (String s : results)
out.println(s);
}
}
private MethodNode getOrCreateMethodNode(String methodName) {
methodName = methodName.intern();
MethodNode entry = methods.get(methodName);
if (entry == null) {
entry = new MethodNode(getClassName(methodName), getMethodWithDesc(methodName));
methods.put(methodName, entry);
}
return entry;
}
private ClassNode getOrCreateClassNode(String className) {
className = className.intern();
ClassNode node = classes.get(className);
if (node == null) {
node = new ClassNode(className);
classes.put(className, node);
}
return node;
}
private ClassNode getOrLoadClassNode(String className) {
return fill(getOrCreateClassNode(className.intern()));
}
private ClassNode fill(ClassNode node) {
if (node.supers == null) {
try (InputStream is = ClassLoaderUtil.getResourceAsStream(cl, classToResource(node.name))) {
final ClassReader cr = new ClassReader(is);
cr.accept(new ClassNodeVisitor(false, ASMAPI, null), ClassReader.SKIP_DEBUG | ClassReader.SKIP_CODE);
assert node.supers != null;
} catch (IOException e) {
throw new RuntimeException("during processing of " + node.name, e);
}
}
return node;
}
private ClassNode getClassNode(MethodNode m) {
ClassNode c = m.classNode;
if (c == null) {
c = getOrLoadClassNode(m.owner);
m.classNode = c;
}
return c;
}
private class ClassNode {
boolean inProject;
final String name;
ClassNode[] supers; // super and interfaces classnames
private ClassNode[] subs; // sub classnames
private int numSubs;
private String[] methods;
public ClassNode(String name) {
this.name = name;
}
void setSupers(String superName, String[] interfaces) {
this.supers = new ClassNode[(superName != null ? 1 : 0) + (interfaces != null ? interfaces.length : 0)];
int i = 0;
if (superName != null) {
supers[i] = getOrCreateClassNode(superName);
supers[i].addSub(this);
i++;
}
if (interfaces != null) {
for (String iface : interfaces) {
supers[i] = getOrCreateClassNode(iface);
supers[i].addSub(this);
i++;
}
}
}
void setMethods(Collection ms) {
this.methods = ms.toArray(new String[ms.size()]);
for (int i = 0; i < methods.length; i++)
methods[i] = methods[i].intern();
}
boolean hasMethod(String method) {
for (String m : methods) {
if (method.equals(m))
return true;
}
return false;
}
List getMethodWithDifferentReturn(String method) {
method = getMethodWithoutReturn(method);
List ms = new ArrayList<>();
for (String m : methods) {
if (m.startsWith(method))
ms.add(m);
}
return ms;
}
@Override
public String toString() {
return "ClassNode{" + name + " inProject: " + inProject + '}';
}
private void addSub(ClassNode cn) {
if (subs == null)
subs = new ClassNode[4];
if (numSubs + 1 >= subs.length)
this.subs = Arrays.copyOf(subs, subs.length * 2);
subs[numSubs] = cn;
numSubs++;
}
}
private static class MethodNode {
String owner;
ClassNode classNode;
final String name; // methodname+desc
//int acc;
boolean inProject;
boolean known;
SuspendableType suspendType;
boolean refersToSuper;
boolean setBySuper;
private MethodNode[] callers;
private int numCallers;
public MethodNode(String owner, String nameAndDesc) {
this.owner = owner.intern();
this.name = nameAndDesc.intern();
}
public void addCaller(MethodNode caller) {
if (callers == null)
callers = new MethodNode[4];
if (numCallers + 1 >= callers.length)
this.callers = Arrays.copyOf(callers, callers.length * 2);
callers[numCallers] = caller;
numCallers++;
}
@SuppressWarnings("override")
public Collection getCallers() {
if (callers == null)
return Collections.emptyList();
return new AbstractCollection() {
public int size() {
return numCallers;
}
public Iterator iterator() {
return new Iterator() {
private int i;
public boolean hasNext() {
return i < numCallers;
}
public MethodNode next() {
return callers[i++];
}
public void remove() {
throw new UnsupportedOperationException("remove");
}
};
}
};
}
@Override
public String toString() {
return "MethodNode{" + owner + '.' + name + " inProject: " + inProject + " suspendType: " + suspendType + '}';
}
public void setSuspendType(SuspendableType suspendType) {
// if ("co/paralleluniverse/strands/Strand".equals(owner) && "join()V".equals(name)) {
// System.err.println("XXXX:" + owner + "." + name + " " + suspendType);
// Thread.dumpStack();
// }
this.suspendType = suspendType;
this.setBySuper = false;
}
}
private static String getClassName(String fullMethodWithDesc) {
return fullMethodWithDesc.substring(0, fullMethodWithDesc.lastIndexOf('.'));
}
private static String getMethodWithDesc(String fullMethodWithDesc) {
return fullMethodWithDesc.substring(fullMethodWithDesc.lastIndexOf('.') + 1);
}
private static String getMethodWithoutReturn(String fullMethodWithDesc) {
String m = getMethodWithDesc(fullMethodWithDesc);
return m.substring(0, m.lastIndexOf(')') + 1);
}
private static boolean isReflectInvocation(String className, String methodName) {
return className.equals("java/lang/reflect/Method") && methodName.equals("invoke");
}
private static boolean isInvocationHandlerInvocation(String className, String methodName) {
return className.equals("java/lang/reflect/InvocationHandler") && methodName.equals("invoke");
}
private static boolean isMethodHandleInvocation(String className, String methodName) {
return className.equals("java/lang/invoke/MethodHandle") && methodName.startsWith("invoke");
}
private static void classpathToUrls(String[] classPath, List urls) throws RuntimeException {
try {
for (String cp : classPath)
urls.add(new File(cp).toURI().toURL());
} catch (MalformedURLException ex) {
throw new AssertionError(ex);
}
}
private static PrintStream getOutputStream(String outputFile, boolean append) 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, append));
} else
return System.out;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy