aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar cparsons <cparsons@google.com>2018-07-25 15:16:12 -0700
committerGravatar Copybara-Service <copybara-piper@google.com>2018-07-25 15:17:45 -0700
commitdc6f1cbe811fbd7f24dfa355e198a6bf8d173327 (patch)
tree74351c00f17ec09a2f7de52f680f2baef9a5e947
parent8e6a1b93a933300f1dd5d97184424f3b3062e603 (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.java99
-rw-r--r--src/main/java/com/google/devtools/build/lib/syntax/SkylarkSignatureProcessor.java36
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",