Skip to content

Commit 5bf9ab2

Browse files
committed
ASM integration in TypesLoaderVisitor
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.
1 parent 67ea64a commit 5bf9ab2

File tree

3 files changed

+151
-33
lines changed

3 files changed

+151
-33
lines changed

pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,11 @@
119119
<artifactId>javalang</artifactId>
120120
<version>[4.1.0, 5.0.0)</version>
121121
</dependency>
122+
<dependency>
123+
<groupId>org.ow2.asm</groupId>
124+
<artifactId>asm-commons</artifactId>
125+
<version>6.1.1</version>
126+
</dependency>
122127
<dependency>
123128
<groupId>junit</groupId>
124129
<artifactId>junit</artifactId>
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package org.walkmod.javalang.compiler.types;
2+
3+
import org.objectweb.asm.Opcodes;
4+
import org.objectweb.asm.tree.ClassNode;
5+
6+
import java.io.File;
7+
8+
9+
public class ASMClass extends ClassNode {
10+
11+
12+
private String actualName;
13+
private Boolean anonymous;
14+
private boolean isPrivate = false;
15+
16+
17+
public ASMClass() {
18+
super(Opcodes.ASM5);
19+
}
20+
21+
@Override
22+
public void visit(int version, int access,
23+
String name, String signature, String superName, String[] interfaces) {
24+
super.visit(version, access, name, signature, superName, interfaces);
25+
actualName = name;
26+
isPrivate = access == Opcodes.ACC_PRIVATE;
27+
}
28+
29+
@Override
30+
public void visitInnerClass(String name, String outer, String innerName, int access) {
31+
super.visitInnerClass(name, outer, innerName, access);
32+
if (name.equals(actualName)) {
33+
anonymous = innerName == null;
34+
}
35+
}
36+
37+
public Boolean isAnonymous() {
38+
return anonymous != null && anonymous;
39+
}
40+
41+
public Boolean isMemberClass() {
42+
return anonymous != null && !anonymous;
43+
}
44+
45+
public String getActualName() {
46+
return actualName;
47+
}
48+
49+
public Boolean isPrivate() {
50+
return isPrivate;
51+
}
52+
53+
public String getPackage() {
54+
if (!actualName.contains(File.separator)){
55+
return null;
56+
}
57+
return getActualName().substring(0, getActualName().lastIndexOf(File.separator) + 1)
58+
.replaceAll(File.separator, "\\.");
59+
}
60+
61+
public String getSimpleName() {
62+
int index = getActualName().lastIndexOf(File.separator);
63+
if (index != -1){
64+
return actualName;
65+
} else {
66+
return getActualName().substring(index + 1);
67+
}
68+
}
69+
70+
public String getCanonicalName() {
71+
return getPackage() + "." + getSimpleName().replaceAll("$", "\\.");
72+
}
73+
}

src/main/java/org/walkmod/javalang/compiler/types/TypesLoaderVisitor.java

Lines changed: 73 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,17 @@
1414
*/
1515
package org.walkmod.javalang.compiler.types;
1616

17+
import java.io.ByteArrayOutputStream;
1718
import java.io.File;
1819
import java.io.IOException;
20+
import java.io.InputStream;
1921
import java.lang.reflect.Modifier;
2022
import java.net.URLClassLoader;
2123
import java.util.*;
22-
import java.util.jar.JarEntry;
23-
import java.util.jar.JarFile;
2424

25+
import org.objectweb.asm.ClassReader;
26+
import org.objectweb.asm.Opcodes;
27+
import org.objectweb.asm.tree.InnerClassNode;
2528
import org.walkmod.javalang.ast.CompilationUnit;
2629
import org.walkmod.javalang.ast.ImportDeclaration;
2730
import org.walkmod.javalang.ast.Node;
@@ -34,10 +37,8 @@
3437
import org.walkmod.javalang.ast.body.TypeDeclaration;
3538
import org.walkmod.javalang.ast.expr.ObjectCreationExpr;
3639
import org.walkmod.javalang.ast.expr.QualifiedNameExpr;
37-
import org.walkmod.javalang.ast.type.Type;
3840
import org.walkmod.javalang.compiler.actions.LoadStaticImportsAction;
3941
import org.walkmod.javalang.compiler.providers.SymbolActionProvider;
40-
import org.walkmod.javalang.compiler.symbols.ASTSymbolTypeResolver;
4142
import org.walkmod.javalang.compiler.symbols.ReferenceType;
4243
import org.walkmod.javalang.compiler.symbols.Scope;
4344
import org.walkmod.javalang.compiler.symbols.Symbol;
@@ -450,13 +451,15 @@ public void visit(ImportDeclaration id, T context) {
450451
/**
451452
* @param importedInner {@link @see #resolveSymbolName}
452453
*/
453-
private void loadNestedClasses(Class<?> clazz, boolean imported, Node node, final boolean importedInner) {
454-
Class<?>[] innerClasses = clazz.getDeclaredClasses();
455-
if (innerClasses != null) {
456-
for (int i = 0; i < innerClasses.length; i++) {
457-
if (!Modifier.isPrivate(innerClasses[i].getModifiers())) {
458-
String fullName = innerClasses[i].getName();
459-
SymbolType st = new SymbolType(innerClasses[i]);
454+
private void loadNestedClasses(ASMClass clazz, boolean imported, Node node, final boolean importedInner) {
455+
456+
if (clazz.innerClasses != null) {
457+
Iterator<InnerClassNode> it = clazz.innerClasses.iterator();
458+
while(it.hasNext()) {
459+
InnerClassNode innerClass = it.next();
460+
if (innerClass.access != Opcodes.ACC_PRIVATE) {
461+
String fullName = innerClass.name;
462+
SymbolType st = new SymbolType(fullName);
460463
symbolTable.pushSymbol(resolveSymbolName(fullName, imported, importedInner), ReferenceType.TYPE, st,
461464
node, true);
462465
}
@@ -465,43 +468,80 @@ private void loadNestedClasses(Class<?> clazz, boolean imported, Node node, fina
465468
}
466469
}
467470

471+
private static byte[] readStream(InputStream inputStream, boolean close) throws IOException {
472+
if(inputStream == null) {
473+
throw new IOException("Class not found");
474+
} else {
475+
try {
476+
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
477+
byte[] data = new byte[4096];
478+
479+
int bytesRead;
480+
while((bytesRead = inputStream.read(data, 0, data.length)) != -1) {
481+
outputStream.write(data, 0, bytesRead);
482+
}
483+
484+
outputStream.flush();
485+
byte[] var5 = outputStream.toByteArray();
486+
return var5;
487+
} finally {
488+
if(close) {
489+
inputStream.close();
490+
}
491+
492+
}
493+
}
494+
}
495+
496+
private ASMClass getASMClass(String name) throws IOException {
497+
ASMClass visitor = new ASMClass();
498+
ClassReader reader = new ClassReader(readStream(
499+
classLoader.getResourceAsStream(name.replace('.', '/') + ".class"),
500+
true));
501+
reader.accept(visitor, 1);
502+
return visitor;
503+
}
504+
468505
private void addType(final String name, boolean imported, Node node, List<SymbolAction> actions) {
469506
if (classLoader != null && name != null) {
507+
508+
//check if the file exists in the classpath
470509
try {
471-
Class<?> clazz = Class.forName(name, false, classLoader);
472-
if (!Modifier.isPrivate(clazz.getModifiers()) && !clazz.isAnonymousClass()) {
510+
511+
ASMClass asmClass = getASMClass(name);
512+
513+
if (!asmClass.isPrivate() && !asmClass.isAnonymous()){ //anonymous?
473514

474515
boolean overrideSimpleName = true;
475516

476-
SymbolType st = new SymbolType(clazz);
517+
SymbolType st = new SymbolType(name);
477518

478519
if (node instanceof ImportDeclaration) {
479520
ImportDeclaration id = (ImportDeclaration) node;
480521
if (id.isAsterisk()) {
481522
overrideSimpleName = false;
482523
}
483524
}
484-
symbolTable.pushSymbol(resolveSymbolName(name, imported, false), ReferenceType.TYPE, st, node,
485-
actions, overrideSimpleName);
525+
symbolTable.pushSymbol(resolveSymbolName(name, imported, false),
526+
ReferenceType.TYPE, st, node, actions, overrideSimpleName);
486527

487-
if (clazz.isMemberClass()) {
488-
String cname = clazz.getCanonicalName();
489-
if (cname != null) {
490-
symbolTable.pushSymbol(cname, ReferenceType.TYPE, st, node, actions, true);
528+
if (asmClass.isMemberClass()) {
491529

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

496-
symbolTable.pushSymbol(clazz.getSimpleName(), ReferenceType.TYPE, st, node, actions,
497-
true);
498-
}
532+
String pkg = asmClass.getPackage();
533+
if (pkg != null) {
534+
if (pkg.equals(packageName) && node != null) {
535+
536+
symbolTable.pushSymbol(asmClass.getSimpleName(), ReferenceType.TYPE, st, node, actions,
537+
true);
499538
}
500539
}
540+
501541
}
502-
loadNestedClasses(clazz, imported, node, true);
542+
loadNestedClasses(asmClass, imported, node, true);
503543
}
504-
} catch (ClassNotFoundException e) {
544+
} catch (IOException e) {
505545
loadInnerClass(name, imported, node, actions);
506546
} catch (IncompatibleClassChangeError e2) {
507547
int index = name.lastIndexOf("$");
@@ -525,19 +565,19 @@ private void loadInnerClass(String name, boolean imported, Node node, List<Symbo
525565
String internalName = preffix + "$" + suffix;
526566

527567
try {
528-
Class<?> clazz = Class.forName(internalName, false, classLoader);
568+
ASMClass asmClass = getASMClass(internalName);
529569

530-
if (!Modifier.isPrivate(clazz.getModifiers())) {
570+
if (!asmClass.isPrivate()) {
531571
String keyName = resolveSymbolName(internalName, imported, false);
532-
SymbolType st = new SymbolType(clazz);
572+
SymbolType st = new SymbolType(internalName);
533573
Symbol<?> pushedSymbol =
534574
symbolTable.pushSymbol(keyName, ReferenceType.TYPE, st, node, actions, true);
535575
if (pushedSymbol != null) {
536-
loadNestedClasses(clazz, imported, node, false);
576+
loadNestedClasses(asmClass, imported, node, false);
537577
}
538578

539579
}
540-
} catch (ClassNotFoundException e1) {
580+
} catch (IOException e1) {
541581
int indexDot = internalName.indexOf(".");
542582
if (indexDot == -1) {
543583
throw new RuntimeException("The referenced class " + internalName + " does not exists");

0 commit comments

Comments
 (0)