Skip to content

Commit 67ea64a

Browse files
committed
Custom ClassLoader for indexing classes
1 parent 4788033 commit 67ea64a

File tree

5 files changed

+477
-257
lines changed

5 files changed

+477
-257
lines changed

src/main/java/org/walkmod/javalang/compiler/types/SymbolTypesClassLoader.java renamed to src/main/java/org/walkmod/javalang/compiler/types/CachedClassLoader.java

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

17-
import java.util.HashMap;
18-
import java.util.Map;
17+
import java.util.*;
1918

19+
import org.walkmod.javalang.ast.SymbolData;
2020
import org.walkmod.javalang.ast.type.Type;
2121
import org.walkmod.javalang.compiler.symbols.ASTSymbolTypeResolver;
22-
import org.walkmod.javalang.compiler.symbols.SymbolType;
2322

24-
public class SymbolTypesClassLoader extends ClassLoader {
23+
public class CachedClassLoader extends ClassLoader {
24+
25+
public static Map<String, Class<?>> PRIMITIVES = new HashMap<String, Class<?>>();
26+
27+
static {
28+
// static block to resolve primitive classes
29+
PRIMITIVES.put("boolean", boolean.class);
30+
PRIMITIVES.put("int", int.class);
31+
PRIMITIVES.put("long", long.class);
32+
PRIMITIVES.put("double", double.class);
33+
PRIMITIVES.put("char", char.class);
34+
PRIMITIVES.put("float", float.class);
35+
PRIMITIVES.put("short", short.class);
36+
PRIMITIVES.put("byte", byte.class);
37+
PRIMITIVES.put("void", void.class);
38+
}
39+
2540
private Map<String, Class<?>> cache = new HashMap<String, Class<?>>();
2641

27-
public SymbolTypesClassLoader(ClassLoader parent) {
42+
public CachedClassLoader(IndexedURLClassLoader parent) {
2843
super(parent);
29-
cache.put("boolean", boolean.class);
30-
cache.put("int", int.class);
31-
cache.put("long", long.class);
32-
cache.put("double", double.class);
33-
cache.put("char", char.class);
34-
cache.put("float", float.class);
35-
cache.put("short", short.class);
36-
cache.put("byte", byte.class);
37-
cache.put("void", void.class);
44+
45+
cache.putAll(PRIMITIVES);
46+
3847
}
3948

4049
public Class<?> loadClass(Type t) throws ClassNotFoundException {
41-
4250
return ASTSymbolTypeResolver.getInstance().valueOf(t).getClazz();
4351
}
4452

45-
public Class<?> loadClass(SymbolType t) throws ClassNotFoundException {
53+
54+
public List<String> getPackageContents(String packageName) {
55+
return ((IndexedURLClassLoader)getParent()).getPackageClasses(packageName);
56+
}
57+
58+
public List<String> getSDKContents(String packageName) {
59+
return ((IndexedURLClassLoader)getParent()).getSDKContents(packageName);
60+
}
61+
62+
public Class<?> loadClass(SymbolData t) throws ClassNotFoundException {
4663
String name = t.getName();
4764
Class<?> cachedClass = cache.get(name);
4865
if (cachedClass == null) {
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package org.walkmod.javalang.compiler.types;
2+
3+
4+
import sun.misc.URLClassPath;
5+
6+
import java.io.File;
7+
import java.util.*;
8+
9+
public class ClassPathTree {
10+
private String key;
11+
private Map<String, ClassPathTree> children;
12+
private TreeValue value;
13+
14+
public ClassPathTree(String key, String path, Map<String, ClassPathTree> children, URLClassPath value) {
15+
this.key = key;
16+
this.children = children;
17+
this.value = new TreeValue(path, value);
18+
}
19+
20+
public void put(String key, URLClassPath value) {
21+
insertTree(key, Arrays.asList(key.split(File.separator)), value);
22+
}
23+
24+
private void insertTree(String path, List<String> keys, URLClassPath value) {
25+
if (keys.isEmpty()) {
26+
return;
27+
}
28+
String currentKey = keys.get(0);
29+
ClassPathTree tree = children.get(currentKey);
30+
if (tree != null) {
31+
tree.insertTree(path, keys.subList(1, keys.size()), value);
32+
} else {
33+
if (children.isEmpty()) {
34+
String originalKey = this.value.path.substring(this.value.path.lastIndexOf("/") + 1);
35+
36+
children.put(originalKey, new ClassPathTree(
37+
originalKey,
38+
this.value.path,
39+
new HashMap<String, ClassPathTree>(),
40+
this.value.delegate));
41+
this.value = null;
42+
}
43+
children.put(currentKey, new ClassPathTree(currentKey, path, new HashMap<String, ClassPathTree>(), value));
44+
}
45+
}
46+
47+
public List<String> list(String key) {
48+
if ("".equals(key)) {
49+
return findAll(Collections.<String>emptyList());
50+
}
51+
return findAll(Arrays.asList(key.split(File.separator)));
52+
}
53+
54+
private List<String> findAll(List<String> keys) {
55+
List<String> list = new LinkedList<String>();
56+
if (keys.isEmpty()) {
57+
if (value != null) {
58+
list.add(value.path);
59+
}
60+
Iterator<ClassPathTree> it = children.values().iterator();
61+
while (it.hasNext()) {
62+
ClassPathTree tree = it.next();
63+
if (tree.value != null) {
64+
list.add(tree.value.path);
65+
}
66+
}
67+
} else {
68+
String next = keys.get(0);
69+
ClassPathTree tree = children.get(next);
70+
if (tree != null) {
71+
return tree.findAll(keys.subList(1, keys.size()));
72+
}
73+
}
74+
return list;
75+
}
76+
77+
public URLClassPath get(String key) {
78+
return findOne(Arrays.asList(key.split(File.separator)));
79+
}
80+
81+
public boolean containsKey(String key) {
82+
return get(key) != null;
83+
}
84+
85+
private URLClassPath findOne(List<String> keys) {
86+
if (keys.isEmpty()) {
87+
if (value != null) {
88+
return value.delegate;
89+
}
90+
return null;
91+
}
92+
String next = keys.get(0);
93+
ClassPathTree tree = children.get(next);
94+
if (tree != null) {
95+
return tree.findOne(keys.subList(1, keys.size()));
96+
}
97+
return null;
98+
}
99+
100+
class TreeValue {
101+
String path;
102+
URLClassPath delegate;
103+
104+
public TreeValue(String path, URLClassPath delegate) {
105+
this.path = path;
106+
this.delegate = delegate;
107+
}
108+
}
109+
}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
package org.walkmod.javalang.compiler.types;
2+
3+
import java.io.File;
4+
import java.io.IOException;
5+
import java.lang.management.ManagementFactory;
6+
import java.net.MalformedURLException;
7+
import java.net.URL;
8+
import java.security.AllPermission;
9+
import java.security.CodeSource;
10+
import java.security.PermissionCollection;
11+
import java.security.Permissions;
12+
import java.security.ProtectionDomain;
13+
import java.security.cert.Certificate;
14+
import java.util.Enumeration;
15+
import java.util.List;
16+
17+
import sun.misc.Resource;
18+
19+
20+
/**
21+
* A custom ClassLoader that indexes the contents of classpath elements, for faster class locating.
22+
*
23+
* The standard URLClassLoader does a linear scan of the classpath for each class or resource, which
24+
* becomes prohibitively expensive for classpaths with many elements.
25+
*/
26+
public class IndexedURLClassLoader extends ClassLoader {
27+
28+
public IndexedURLClassLoader(ClassLoader parent) {
29+
// parent is the default system classloader, which we want to bypass entirely in
30+
// the delegation hierarchy, so we make our parent that thing's parent instead.
31+
super(parent.getParent());
32+
this.ucp = new IndexedURLClassPath(getClassPathURLs());
33+
}
34+
35+
public URL[] getURLs() {
36+
return this.ucp.getURLs();
37+
}
38+
39+
public IndexedURLClassLoader(URL[] urls, ClassLoader parent) {
40+
super(parent);
41+
this.ucp = new IndexedURLClassPath(urls);
42+
}
43+
44+
public List<String> getPackageClasses(String packageName) {
45+
return ucp.listPackageContents(packageName,false);
46+
}
47+
48+
public List<String> getSDKContents(String packageName) {
49+
return ucp.listSDKContents(packageName,false);
50+
}
51+
52+
private static URL[] getClassPathURLs() {
53+
try {
54+
String[] paths = System.getProperties().getProperty("java.class.path").split(File.pathSeparator);
55+
URL[] urls = new URL[paths.length];
56+
for (int i = 0; i < paths.length; ++i) {
57+
urls[i] = new File(paths[i]).toURI().toURL();
58+
}
59+
return urls;
60+
} catch (MalformedURLException e) {
61+
throw new RuntimeException(e);
62+
}
63+
}
64+
65+
/* The search path for classes and resources */
66+
private IndexedURLClassPath ucp;
67+
68+
@Override
69+
public URL findResource(String name) {
70+
return ucp.findResource(name, false);
71+
}
72+
73+
@Override
74+
protected Class<?> findClass(String name) throws ClassNotFoundException {
75+
try {
76+
String path = name.replace('.', '/').concat(".class");
77+
Resource res = ucp.getResource(path);
78+
if (res != null) {
79+
int i = name.lastIndexOf('.');
80+
if (i != -1) {
81+
String pkgname = name.substring(0, i);
82+
// Check if package already loaded.
83+
Package pkg = getPackage(pkgname);
84+
if (pkg == null) {
85+
definePackage(pkgname, null, null, null, null, null, null, null);
86+
}
87+
}
88+
byte[] data = res.getBytes();
89+
// Add a CodeSource via a ProtectionDomain, as code may use this to find its own jars.
90+
CodeSource cs = new CodeSource(res.getCodeSourceURL(), (Certificate[])null);
91+
PermissionCollection pc = new Permissions();
92+
pc.add(new AllPermission());
93+
ProtectionDomain pd = new ProtectionDomain(cs, pc);
94+
return defineClass(name, data, 0, data.length, pd);
95+
} else {
96+
throw new ClassNotFoundException(String.format("IndexedURLClassLoader can't find class %s", name));
97+
}
98+
} catch (IOException e) {
99+
throw new ClassNotFoundException(String.format("IndexedURLClassLoader failed to read class %s", name), e);
100+
}
101+
}
102+
103+
@Override
104+
public Enumeration<URL> findResources(final String name) throws IOException {
105+
final Enumeration e = ucp.findResources(name, true);
106+
107+
return new Enumeration<URL>() {
108+
public URL nextElement() {
109+
return (URL)e.nextElement();
110+
}
111+
112+
public boolean hasMoreElements() {
113+
return e.hasMoreElements();
114+
}
115+
};
116+
}
117+
118+
119+
}

0 commit comments

Comments
 (0)