diff options
author | cparsons <cparsons@google.com> | 2018-07-25 15:16:12 -0700 |
---|---|---|
committer | Copybara-Service <copybara-piper@google.com> | 2018-07-25 15:17:45 -0700 |
commit | dc6f1cbe811fbd7f24dfa355e198a6bf8d173327 (patch) | |
tree | 74351c00f17ec09a2f7de52f680f2baef9a5e947 | |
parent | 8e6a1b93a933300f1dd5d97184424f3b3062e603 (diff) |
Cache default parameter values for skylark methods, and compute types of Param annotations only once.
This change has been manually verified to greatly reduce analysis time (~50%) on very large builds, as it mitigates a previous regression brought on by the migration of @SkylarkSignature to @SkylarkCallable.
RELNOTES: None.
PiperOrigin-RevId: 206063684
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/syntax/FuncallExpression.java | 99 | ||||
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/syntax/SkylarkSignatureProcessor.java | 36 |
2 files changed, 97 insertions, 38 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/FuncallExpression.java b/src/main/java/com/google/devtools/build/lib/syntax/FuncallExpression.java index 53b892dc37..fefcf70355 100644 --- a/src/main/java/com/google/devtools/build/lib/syntax/FuncallExpression.java +++ b/src/main/java/com/google/devtools/build/lib/syntax/FuncallExpression.java @@ -58,18 +58,77 @@ import javax.annotation.Nullable; public final class FuncallExpression extends Expression { /** - * A value class to store Methods with their corresponding SkylarkCallable annotations. - * This is needed because the annotation is sometimes in a superclass. + * A tuple of Param annotation to the skylark type it represents. While the type can be + * inferred completely by the Param annotation, this tuple allows for the type of a given + * parameter to be determined only once, as it is an expensive operation. + */ + private static final class ParamInfo { + private final Param param; + private final SkylarkType type; + + public ParamInfo(Param param) { + this.param = param; + this.type = getSkylarkType(param); + } + + private static SkylarkType getSkylarkType(Param param) { + SkylarkType result = SkylarkType.BOTTOM; + if (param.allowedTypes().length > 0) { + Preconditions.checkState(Object.class.equals(param.type())); + for (ParamType paramType : param.allowedTypes()) { + SkylarkType t = + paramType.generic1() != Object.class + ? SkylarkType.of(paramType.type(), paramType.generic1()) + : SkylarkType.of(paramType.type()); + result = SkylarkType.Union.of(result, t); + } + } else { + result = + param.generic1() != Object.class + ? SkylarkType.of(param.type(), param.generic1()) + : SkylarkType.of(param.type()); + } + + if (param.noneable()) { + result = SkylarkType.Union.of(result, SkylarkType.NONE); + } + return result; + } + + public Param getParam() { + return param; + } + + public SkylarkType getType() { + return type; + } + } + + /** + * A value class to store Methods with their corresponding SkylarkCallable annotations and some + * information about the method. */ public static final class MethodDescriptor { + private final Method method; + private final ParamInfo[] methodParams; private final SkylarkCallable annotation; private MethodDescriptor(Method method, SkylarkCallable annotation) { this.method = method; + this.methodParams = methodParams(annotation); this.annotation = annotation; } + private static ParamInfo[] methodParams(SkylarkCallable annotation) { + Param[] annotationParameters = annotation.parameters(); + ParamInfo[] paramInfoArr = new ParamInfo[annotationParameters.length]; + for (int i = 0; i < paramInfoArr.length; i++) { + paramInfoArr[i] = new ParamInfo(annotationParameters[i]); + } + return paramInfoArr; + } + Method getMethod() { return method; } @@ -80,6 +139,13 @@ public final class FuncallExpression extends Expression { public SkylarkCallable getAnnotation() { return annotation; } + + /** + * Returns an array of objects describing the parameters of this method. + */ + public ParamInfo[] getMethodParams() { + return methodParams; + } } private static final LoadingCache<Class<?>, Optional<MethodDescriptor>> selfCallCache = @@ -541,30 +607,6 @@ public final class FuncallExpression extends Expression { return matchingMethod; } - private static SkylarkType getType(Param param) { - SkylarkType result = SkylarkType.BOTTOM; - if (param.allowedTypes().length > 0) { - Preconditions.checkState(Object.class.equals(param.type())); - for (ParamType paramType : param.allowedTypes()) { - SkylarkType t = - paramType.generic1() != Object.class - ? SkylarkType.of(paramType.type(), paramType.generic1()) - : SkylarkType.of(paramType.type()); - result = SkylarkType.Union.of(result, t); - } - } else { - result = - param.generic1() != Object.class - ? SkylarkType.of(param.type(), param.generic1()) - : SkylarkType.of(param.type()); - } - - if (param.noneable()) { - result = SkylarkType.Union.of(result, SkylarkType.NONE); - } - return result; - } - private static boolean isParamNamed(Param param) { return param.named() || param.legacyNamed(); } @@ -622,8 +664,9 @@ public final class FuncallExpression extends Expression { // Positional parameters are always enumerated before non-positional parameters, // And default-valued positional parameters are always enumerated after other positional // parameters. These invariants are validated by the SkylarkCallable annotation processor. - for (Param param : callable.parameters()) { - SkylarkType type = getType(param); + for (ParamInfo paramInfo : method.getMethodParams()) { + SkylarkType type = paramInfo.getType(); + Param param = paramInfo.getParam(); Object value = null; if (argIndex < args.size() && param.positional()) { // Positional args and params remain. diff --git a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkSignatureProcessor.java b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkSignatureProcessor.java index 6cdb889105..009bdedfd3 100644 --- a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkSignatureProcessor.java +++ b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkSignatureProcessor.java @@ -14,6 +14,8 @@ package com.google.devtools.build.lib.syntax; import com.google.common.base.Preconditions; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; import com.google.common.primitives.Booleans; import com.google.devtools.build.lib.skylarkinterface.Param; import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable; @@ -34,6 +36,12 @@ import javax.annotation.Nullable; */ public class SkylarkSignatureProcessor { + // A cache mapping string representation of a skylark parameter default value to the object + // represented by that string. For example, "None" -> Runtime.NONE. This cache is manually + // maintained (instead of using, for example, a LoadingCache), as default values may sometimes + // be recursively requested. + private static final Cache<String, Object> defaultValueCache = CacheBuilder.newBuilder().build(); + /** * Extracts a {@code FunctionSignature.WithValues<Object, SkylarkType>} from a * {@link SkylarkCallable}-annotated method. @@ -230,16 +238,24 @@ public class SkylarkSignatureProcessor { } else if (param.defaultValue().isEmpty()) { return Runtime.NONE; } else { - try (Mutability mutability = Mutability.create("initialization")) { - // Note that this Skylark environment ignores command line flags. - Environment env = - Environment.builder(mutability) - .useDefaultSemantics() - .setGlobals(Environment.CONSTANTS_ONLY) - .setEventHandler(Environment.FAIL_FAST_HANDLER) - .build() - .update("unbound", Runtime.UNBOUND); - return BuildFileAST.eval(env, param.defaultValue()); + try { + Object defaultValue = defaultValueCache.getIfPresent(param.defaultValue()); + if (defaultValue != null) { + return defaultValue; + } + try (Mutability mutability = Mutability.create("initialization")) { + // Note that this Skylark environment ignores command line flags. + Environment env = + Environment.builder(mutability) + .useDefaultSemantics() + .setGlobals(Environment.CONSTANTS_ONLY) + .setEventHandler(Environment.FAIL_FAST_HANDLER) + .build() + .update("unbound", Runtime.UNBOUND); + defaultValue = BuildFileAST.eval(env, param.defaultValue()); + defaultValueCache.put(param.defaultValue(), defaultValue); + return defaultValue; + } } catch (Exception e) { throw new RuntimeException(String.format( "Exception while processing @SkylarkSignature.Param %s, default value %s", |