From 8b584f412d055ff39949d8b28a8e78d6b0bfe9b6 Mon Sep 17 00:00:00 2001 From: Roberto Lublinerman Date: Fri, 23 Jan 2015 14:19:02 -0800 Subject: [PATCH] Fix unnecessary scan for absent and bytecode only classes. 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 --- .../com/google/gwt/dev/javac/JdtCompiler.java | 63 ++++++++++++------- 1 file changed, 42 insertions(+), 21 deletions(-) diff --git a/dev/core/src/com/google/gwt/dev/javac/JdtCompiler.java b/dev/core/src/com/google/gwt/dev/javac/JdtCompiler.java index 139da4c3210..b74640cd748 100644 --- a/dev/core/src/com/google/gwt/dev/javac/JdtCompiler.java +++ b/dev/core/src/com/google/gwt/dev/javac/JdtCompiler.java @@ -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; @@ -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) { @@ -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; @@ -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 @@ -746,6 +760,13 @@ private static void resolveRecursive(ReferenceBinding outerType) { */ private final Map internalTypes = new HashMap(); + /** + * Remembers types that have been found in the classpath or not found at all to avoid unnecessary + * resource scanning. + */ + private final Map cachedClassPathAnswerByInternalName = + Maps.newHashMap(); + /** * Only active during a compile. */