Skip to content

Commit

Permalink
ASM integration in TypesLoaderVisitor
Browse files Browse the repository at this point in the history
This patch replaces reflection by ASM and increases
the performace almost a 20%. ASM parses binary files and
allows to extract the minimum amount of information that
is required to analyze.
  • Loading branch information
rpau committed Apr 12, 2018
1 parent 67ea64a commit 5bf9ab2
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 33 deletions.
5 changes: 5 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,11 @@
<artifactId>javalang</artifactId>
<version>[4.1.0, 5.0.0)</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-commons</artifactId>
<version>6.1.1</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
Expand Down
73 changes: 73 additions & 0 deletions src/main/java/org/walkmod/javalang/compiler/types/ASMClass.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package org.walkmod.javalang.compiler.types;

import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.ClassNode;

import java.io.File;


public class ASMClass extends ClassNode {


private String actualName;
private Boolean anonymous;
private boolean isPrivate = false;


public ASMClass() {
super(Opcodes.ASM5);
}

@Override
public void visit(int version, int access,
String name, String signature, String superName, String[] interfaces) {
super.visit(version, access, name, signature, superName, interfaces);
actualName = name;
isPrivate = access == Opcodes.ACC_PRIVATE;
}

@Override
public void visitInnerClass(String name, String outer, String innerName, int access) {
super.visitInnerClass(name, outer, innerName, access);
if (name.equals(actualName)) {
anonymous = innerName == null;
}
}

public Boolean isAnonymous() {
return anonymous != null && anonymous;
}

public Boolean isMemberClass() {
return anonymous != null && !anonymous;
}

public String getActualName() {
return actualName;
}

public Boolean isPrivate() {
return isPrivate;
}

public String getPackage() {
if (!actualName.contains(File.separator)){
return null;
}
return getActualName().substring(0, getActualName().lastIndexOf(File.separator) + 1)
.replaceAll(File.separator, "\\.");
}

public String getSimpleName() {
int index = getActualName().lastIndexOf(File.separator);
if (index != -1){
return actualName;
} else {
return getActualName().substring(index + 1);
}
}

public String getCanonicalName() {
return getPackage() + "." + getSimpleName().replaceAll("$", "\\.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,17 @@
*/
package org.walkmod.javalang.compiler.types;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Modifier;
import java.net.URLClassLoader;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.InnerClassNode;
import org.walkmod.javalang.ast.CompilationUnit;
import org.walkmod.javalang.ast.ImportDeclaration;
import org.walkmod.javalang.ast.Node;
Expand All @@ -34,10 +37,8 @@
import org.walkmod.javalang.ast.body.TypeDeclaration;
import org.walkmod.javalang.ast.expr.ObjectCreationExpr;
import org.walkmod.javalang.ast.expr.QualifiedNameExpr;
import org.walkmod.javalang.ast.type.Type;
import org.walkmod.javalang.compiler.actions.LoadStaticImportsAction;
import org.walkmod.javalang.compiler.providers.SymbolActionProvider;
import org.walkmod.javalang.compiler.symbols.ASTSymbolTypeResolver;
import org.walkmod.javalang.compiler.symbols.ReferenceType;
import org.walkmod.javalang.compiler.symbols.Scope;
import org.walkmod.javalang.compiler.symbols.Symbol;
Expand Down Expand Up @@ -450,13 +451,15 @@ public void visit(ImportDeclaration id, T context) {
/**
* @param importedInner {@link @see #resolveSymbolName}
*/
private void loadNestedClasses(Class<?> clazz, boolean imported, Node node, final boolean importedInner) {
Class<?>[] innerClasses = clazz.getDeclaredClasses();
if (innerClasses != null) {
for (int i = 0; i < innerClasses.length; i++) {
if (!Modifier.isPrivate(innerClasses[i].getModifiers())) {
String fullName = innerClasses[i].getName();
SymbolType st = new SymbolType(innerClasses[i]);
private void loadNestedClasses(ASMClass clazz, boolean imported, Node node, final boolean importedInner) {

if (clazz.innerClasses != null) {
Iterator<InnerClassNode> it = clazz.innerClasses.iterator();
while(it.hasNext()) {
InnerClassNode innerClass = it.next();
if (innerClass.access != Opcodes.ACC_PRIVATE) {
String fullName = innerClass.name;
SymbolType st = new SymbolType(fullName);
symbolTable.pushSymbol(resolveSymbolName(fullName, imported, importedInner), ReferenceType.TYPE, st,
node, true);
}
Expand All @@ -465,43 +468,80 @@ private void loadNestedClasses(Class<?> clazz, boolean imported, Node node, fina
}
}

private static byte[] readStream(InputStream inputStream, boolean close) throws IOException {
if(inputStream == null) {
throw new IOException("Class not found");
} else {
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
byte[] data = new byte[4096];

int bytesRead;
while((bytesRead = inputStream.read(data, 0, data.length)) != -1) {
outputStream.write(data, 0, bytesRead);
}

outputStream.flush();
byte[] var5 = outputStream.toByteArray();
return var5;
} finally {
if(close) {
inputStream.close();
}

}
}
}

private ASMClass getASMClass(String name) throws IOException {
ASMClass visitor = new ASMClass();
ClassReader reader = new ClassReader(readStream(
classLoader.getResourceAsStream(name.replace('.', '/') + ".class"),
true));
reader.accept(visitor, 1);
return visitor;
}

private void addType(final String name, boolean imported, Node node, List<SymbolAction> actions) {
if (classLoader != null && name != null) {

//check if the file exists in the classpath
try {
Class<?> clazz = Class.forName(name, false, classLoader);
if (!Modifier.isPrivate(clazz.getModifiers()) && !clazz.isAnonymousClass()) {

ASMClass asmClass = getASMClass(name);

if (!asmClass.isPrivate() && !asmClass.isAnonymous()){ //anonymous?

boolean overrideSimpleName = true;

SymbolType st = new SymbolType(clazz);
SymbolType st = new SymbolType(name);

if (node instanceof ImportDeclaration) {
ImportDeclaration id = (ImportDeclaration) node;
if (id.isAsterisk()) {
overrideSimpleName = false;
}
}
symbolTable.pushSymbol(resolveSymbolName(name, imported, false), ReferenceType.TYPE, st, node,
actions, overrideSimpleName);
symbolTable.pushSymbol(resolveSymbolName(name, imported, false),
ReferenceType.TYPE, st, node, actions, overrideSimpleName);

if (clazz.isMemberClass()) {
String cname = clazz.getCanonicalName();
if (cname != null) {
symbolTable.pushSymbol(cname, ReferenceType.TYPE, st, node, actions, true);
if (asmClass.isMemberClass()) {

Package pkg = clazz.getPackage();
if (pkg != null) {
if (pkg.getName().equals(packageName) && node != null) {
symbolTable.pushSymbol(asmClass.getCanonicalName(), ReferenceType.TYPE, st, node, actions, true);

symbolTable.pushSymbol(clazz.getSimpleName(), ReferenceType.TYPE, st, node, actions,
true);
}
String pkg = asmClass.getPackage();
if (pkg != null) {
if (pkg.equals(packageName) && node != null) {

symbolTable.pushSymbol(asmClass.getSimpleName(), ReferenceType.TYPE, st, node, actions,
true);
}
}

}
loadNestedClasses(clazz, imported, node, true);
loadNestedClasses(asmClass, imported, node, true);
}
} catch (ClassNotFoundException e) {
} catch (IOException e) {
loadInnerClass(name, imported, node, actions);
} catch (IncompatibleClassChangeError e2) {
int index = name.lastIndexOf("$");
Expand All @@ -525,19 +565,19 @@ private void loadInnerClass(String name, boolean imported, Node node, List<Symbo
String internalName = preffix + "$" + suffix;

try {
Class<?> clazz = Class.forName(internalName, false, classLoader);
ASMClass asmClass = getASMClass(internalName);

if (!Modifier.isPrivate(clazz.getModifiers())) {
if (!asmClass.isPrivate()) {
String keyName = resolveSymbolName(internalName, imported, false);
SymbolType st = new SymbolType(clazz);
SymbolType st = new SymbolType(internalName);
Symbol<?> pushedSymbol =
symbolTable.pushSymbol(keyName, ReferenceType.TYPE, st, node, actions, true);
if (pushedSymbol != null) {
loadNestedClasses(clazz, imported, node, false);
loadNestedClasses(asmClass, imported, node, false);
}

}
} catch (ClassNotFoundException e1) {
} catch (IOException e1) {
int indexDot = internalName.indexOf(".");
if (indexDot == -1) {
throw new RuntimeException("The referenced class " + internalName + " does not exists");
Expand Down

0 comments on commit 5bf9ab2

Please sign in to comment.