Skip to content

Commit

Permalink
Fix unnecessary scan for absent and bytecode only classes.
Browse files Browse the repository at this point in the history
During compilation class references are resolved by JDT using
compiled bytecode. If the class is not part of the current compilation,
JDT will query a NameEnvironment for the bytocode of the class which
GWT might provide from its caches. In the case that is not in any cache
GWT falls back to loading from the classpath (which is actually only
allowed for annotations). In such cases the result is not cached forcing
resource scans every time (even if the class is not found at all).

This process in noticeable when resolving unqualified JSNI references.

This patch solves the issue by caching the results of class patch
queries.

Bug: issue 9006.
Change-Id: I92e78da8e39fb4f4a2321cac45d43ee6e69467b4
  • Loading branch information
rluble authored and Gerrit Code Review committed Mar 10, 2015
1 parent 2003568 commit 8b584f4
Showing 1 changed file with 42 additions and 21 deletions.
63 changes: 42 additions & 21 deletions dev/core/src/com/google/gwt/dev/javac/JdtCompiler.java
Expand Up @@ -29,6 +29,7 @@
import com.google.gwt.thirdparty.guava.common.collect.ArrayListMultimap;
import com.google.gwt.thirdparty.guava.common.collect.ImmutableMap;
import com.google.gwt.thirdparty.guava.common.collect.ListMultimap;
import com.google.gwt.thirdparty.guava.common.collect.Maps;
import com.google.gwt.thirdparty.guava.common.io.BaseEncoding;
import com.google.gwt.util.tools.Utility;

Expand Down Expand Up @@ -452,33 +453,18 @@ public NameEnvironmentAnswer findType(char[][] compoundTypeName) {
return additionalProviderAnswer;
}

// TODO(stalcup): Add verification that all classpath bytecode is for Annotations.
NameEnvironmentAnswer classPathAnswer = findTypeInClassPath(internalName);

if (classPathAnswer != null) {
return classPathAnswer;
}

// LambdaMetafactory is byte-code side artifact of JDT compile and actually not referenced by
// our AST. However, this class is only available in JDK8+ so JdtCompiler fails to validate
// the classes that are referencing it. We tackle that by providing a stub version if it is
// not found in the class path.
if (internalName.equals("java/lang/invoke/LambdaMetafactory")) {
try {
ClassFileReader cfr = new ClassFileReader(getLambdaMetafactoryBytes(),
"synthtetic:java/lang/invoke/LambdaMetafactory".toCharArray(), true);
return new NameEnvironmentAnswer(cfr, null);
} catch (ClassFormatException e) {
e.printStackTrace();
}
}

return null;
}

/*
Generated from:
public class LambdaMetafactory {
public class LambdaMetafactory {
public static CallSite metafactory(MethodHandles.Lookup caller, String invokedName,
MethodType invokedType, MethodType samMethodType, MethodHandle implMethod,
MethodType instantiatedMethodType) {
Expand Down Expand Up @@ -536,6 +522,19 @@ private NameEnvironmentAnswer findTypeInAdditionalProvider(String internalName)
}

private NameEnvironmentAnswer findTypeInClassPath(String internalName) {

// If the class was previously queried here return the cached result.
if (cachedClassPathAnswerByInternalName.containsKey(internalName)) {
// The return might be null if the class was not found at all.
return cachedClassPathAnswerByInternalName.get(internalName);
}
NameEnvironmentAnswer answer = doFindTypeInClassPath(internalName);
// Here we cache the answer even if it is null to denote that it was not found.
cachedClassPathAnswerByInternalName.put(internalName, answer);
return answer;
}

private NameEnvironmentAnswer doFindTypeInClassPath(String internalName) {
URL resource = getClassLoader().getResource(internalName + ".class");
if (resource == null) {
return null;
Expand All @@ -548,15 +547,30 @@ private NameEnvironmentAnswer findTypeInClassPath(String internalName) {
ClassFileReader.read(openStream, resource.toExternalForm(), true);
// In case insensitive file systems we might have found a resource whose name is different
// in case and should not be returned as an answer.
return internalName.equals(CharOperation.charToString(classFileReader.getName())) ?
new NameEnvironmentAnswer(classFileReader, null) : null;
if (internalName.equals(CharOperation.charToString(classFileReader.getName()))) {
return new NameEnvironmentAnswer(classFileReader, null);
}
} catch (IOException e) {
return null;
// returns null indicating a failure.
} catch (ClassFormatException e) {
return null;
// returns null indicating a failure.
} finally {
Utility.close(openStream);
}
// LambdaMetafactory is byte-code side artifact of JDT compile and actually not referenced by
// our AST. However, this class is only available in JDK8+ so JdtCompiler fails to validate
// the classes that are referencing it. We tackle that by providing a stub version if it is
// not found in the class path.
if (internalName.equals("java/lang/invoke/LambdaMetafactory")) {
try {
ClassFileReader cfr = new ClassFileReader(getLambdaMetafactoryBytes(),
"synthetic:java/lang/invoke/LambdaMetafactory".toCharArray(), true);
return new NameEnvironmentAnswer(cfr, null);
} catch (ClassFormatException e) {
e.printStackTrace();
}
}
return null;
}

@Override
Expand Down Expand Up @@ -746,6 +760,13 @@ private static void resolveRecursive(ReferenceBinding outerType) {
*/
private final Map<String, CompiledClass> internalTypes = new HashMap<String, CompiledClass>();

/**
* Remembers types that have been found in the classpath or not found at all to avoid unnecessary
* resource scanning.
*/
private final Map<String, NameEnvironmentAnswer> cachedClassPathAnswerByInternalName =
Maps.newHashMap();

/**
* Only active during a compile.
*/
Expand Down

0 comments on commit 8b584f4

Please sign in to comment.