-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Custom ClassLoader for indexing classes #85
Conversation
@cal101 what I have realized is that using ASM (bytecode parser) is probably much faster than using reflection, specially to read the class definition. Now, the critical point is |
Reflectasm sounds cool but is reflection really a (measured?) problem? Since I got some strange results when profiling the class loader related code when I profiled that I used some simple timer based counting System.currentTime*. I found the results more reliable. Regarding the review I will try to find a slot. |
import java.io.File; | ||
import java.util.*; | ||
|
||
public class ClassPathTree { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The add method looks strange to me. I suggest writing a test that shows application of the class. Especially dealing with different depths ok key pathes.
I would call the keys parameter a keyPath.
I wonder if the outcome of adding
- A/B/C
- A/D/E
is correct according to the assumed expected behavior.
All fields seem to be candidates for "final".
Telling the syntax of the key would be nice: absolute file path? Relative file path?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in e36e5a8
} | ||
|
||
public IndexedURLClassPath(URL[] urls) { | ||
super(urls); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make copy of array?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do you suggest to copy this array?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Deep mistrust ;-) The array is writable and we are creating an alias. I don't want to check the control flow to make sure all users behave and don't modify. Immutable data types or defensive copy make it possible to keep it private/unmodifyable. Parallel execution plans make that even more important.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have marked it as final in 5e6fb89
The copy is created by who provides the array ;)
|
||
|
||
// Map from resource name to URLClassPath to delegate loading that resource to. | ||
private final ClassPathTree index = new ClassPathTree("/", "/", new HashMap<String, ClassPathTree>(), null); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I expected this declaration at beginning of class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (!rootPath.endsWith(File.separator)) { | ||
rootPath = rootPath + File.separator; | ||
} | ||
addFilesToIndex(rootPath.length(), root, delegate); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whats the urlclasspath definition for file archive?
In the past we had .zip accepted. Years ago but would check against the urlclassloader.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
URLClasspath belongs to the "misc" classes of the JVM to load resources.
I have excluded zips because I did not about this, but I can include them as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest checking how urlclassloader handles non-directories and do the same. If they check for suffixes we can do the same. But I expectvsomething different.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
URLConnection urlc = url.openConnection();
InputStream is = urlc.getInputStream();
if (urlc instanceof JarURLConnection) {
is what urclassloader does.
I suggest trying to open each non-directory as jar and ignore errors if it's not a jar.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not right at all. The URLClassLoader
is checking the permissions for the file with the approach that you pointed out.
http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/net/URLClassLoader.java#458
However, here: http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/sun/misc/URLClassPath.java#URLClassPath.JarLoader
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see your point/contradiction.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the JarLoader is not opening a connection, it is iterating the entries. Looking in detail in the code, I am starting to think that I should maintain and index in TypesLoaderVisitor
but using the current URLClassLoader
, which already is indexing the entries. Then, the pros are:
- I do not depend on
sun.misc
package, which is not part of the JDK in Java 10. - I reduce the
TypesLoader
part because the classloader is not needed anymore for the changes that you see in the next PR
The cons are:
- The indexation happens twice.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nevermind. It not indexes the classpath at the begining. Every request is really expensive in the URLClassLoader
class
maybeIndexResource(relPath, delegate); | ||
} | ||
File[] directoryEntries = f.listFiles(); | ||
assert(directoryEntries != null); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Throw ioexception instead of assert. See methods javadoc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ops it was because the code was initially copied from the pants project
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Callers may request the directory itself as a resource, and may | ||
// do so with or without trailing slashes. We do this in a while-loop | ||
// in case the classpath element has multiple superfluous trailing slashes. | ||
if (relPath.endsWith(File.separator)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move before outer if to store only without trailing slashes. Strip trailingvslashes on lookup, too if not already done.
File separator is always char and has constants for string and char.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
} | ||
|
||
private void addTypes(List<String> files, List<SymbolAction> actions, Node node) { | ||
Iterator<String> it = files.iterator(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here and other places: why not just use for loop instead of explicit iterator?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
} else { | ||
classLoader = new CachedClassLoader(new IndexedURLClassLoader(cl)); | ||
} | ||
SDKFiles = classLoader.getSDKContents("java.lang"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you care for avoiding statics at the moment or do you want deal with them later?
There is some kind of context missing that brackets an execution/run.
I am dreaming of parallel execution here...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Really good point about the parallel execution here :)
Well, I think that I cannot add more statics for now without overcomplicating the code. Indeed with visualVM now, the hotspot is the LoadStaticImportsAction
because of the reflection usage there asking for the import class.
What do you mean with a missing context?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you want to do parallel runs each run needs it's own set of mutable state.
Often ThreadLocals are used but I prefer explicit passing of a kind of "execution context" that stores data global for a run. For single threaded apps the "statics" provide that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cal, let's work in the parallel part in a different PR. I do not see how to do it easily with the current logic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure. I don't want to work on it now. I just wanted to point out not working too much against it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Check comments. Ignore where too picky... ;-)
@cal101 waiting for your approval ;) |
Will look at it after some sleep, garden work, shopping, working and driving. Maybe even some snack inbetween ;-) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor style stuff, see comments.
@@ -14,35 +14,52 @@ | |||
*/ | |||
package org.walkmod.javalang.compiler.types; | |||
|
|||
import java.util.HashMap; | |||
import java.util.Map; | |||
import java.util.*; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Style: wild or no wild?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It has been automatically performed by Intellij. I will removed it.
public class SymbolTypesClassLoader extends ClassLoader { | ||
public class CachedClassLoader extends ClassLoader { | ||
|
||
public static Map<String, Class<?>> PRIMITIVES = new HashMap<String, Class<?>>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Collections.unmodifyableMap?
|
||
/* The search path for classes and resources */ | ||
private IndexedURLClassPath ucp; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Style: Move to top of class?
import java.io.IOException; | ||
import java.net.MalformedURLException; | ||
import java.net.URL; | ||
import java.util.*; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wildcard?
|
||
private final URL[] urls; | ||
private int lastIndexed = 0; | ||
private static URL RT_JAR; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Final?
// static block to resolve java.lang package classes | ||
String[] bootPath = System.getProperties().get("sun.boot.class.path").toString() | ||
.split(Character.toString(File.pathSeparatorChar)); | ||
for (String lib : bootPath) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
File.pathSeparator
.split(Character.toString(File.pathSeparatorChar)); | ||
for (String lib : bootPath) { | ||
if (lib.endsWith("rt.jar")) { | ||
File f = new File(lib); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will that work for java 9?
Should we care now or later?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, I would care if rt is not part of java 9
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The internal files rt.jar, tools.jar, and dt.jar have been removed. The content of these files is now stored in a more efficient format in implementation-private files in the lib directory. Class and resource files previously in tools.jar and dt.jar are now always visible via the bootstrap or application class loaders in a JDK image.
😱
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am going to create a Issue, because it is a different discussion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cal101 could you review the last changes and then approve if you consider? I think that all your styling comments have been addressed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly style issues.
* @param relPath relative path | ||
* @param delegate value to insert | ||
*/ | ||
private void maybeIndexResource(String relPath, URL delegate) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What I meant was:
String cleanRelPath = stripTrailingFileSeparators();
if (!index.containsKey(cleanRelPath)) {
 index.put(cleanRelPath, delegate);
 }
And using the same strip-Method for lookup method of index, too.
Seems fishy for me to add multiples here but don't strip on lookup.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Theoretically, you could have multiple classes from different jars under the same package and the JDK uses the first that appears in the classpath.
|
||
|
||
import java.io.File; | ||
import java.util.*; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wild card.
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Set; | ||
import java.util.*; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wildcard.
public class IndexedURLClassLoader extends ClassLoader { | ||
|
||
/* The search path for classes and resources */ | ||
private IndexedURLClassPath ucp; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can be final.
@cal101 feel free to review it :)