aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/syntax/FuncallExpression.java
diff options
context:
space:
mode:
authorGravatar cparsons <cparsons@google.com>2018-02-05 02:01:28 -0800
committerGravatar Copybara-Service <copybara-piper@google.com>2018-02-05 02:02:56 -0800
commit6c1c0660dd51420160eb1c0f5eac7e67917a231c (patch)
tree756d35b5aad7d6f136d44df2450df63e1884733b /src/main/java/com/google/devtools/build/lib/syntax/FuncallExpression.java
parent152b22dd9d817e7f01f88cd8b62928052f0a32df (diff)
Expose structField callable methods of skylark objects to dir() and str() calls
RELNOTES: None. PiperOrigin-RevId: 184498836
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/syntax/FuncallExpression.java')
-rw-r--r--src/main/java/com/google/devtools/build/lib/syntax/FuncallExpression.java86
1 files changed, 80 insertions, 6 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 833b20a7fb..241789d268 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
@@ -42,12 +42,14 @@ import java.lang.reflect.Modifier;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
+import java.util.stream.Collectors;
import javax.annotation.Nullable;
/**
@@ -134,6 +136,48 @@ public final class FuncallExpression extends Expression {
}
});
+ private static final LoadingCache<Class<?>, Map<String, MethodDescriptor>> fieldCache =
+ CacheBuilder.newBuilder()
+ .initialCapacity(10)
+ .maximumSize(100)
+ .build(
+ new CacheLoader<Class<?>, Map<String, MethodDescriptor>>() {
+
+ @Override
+ public Map<String, MethodDescriptor> load(Class<?> key) throws Exception {
+ ImmutableMap.Builder<String, MethodDescriptor> fieldMap = ImmutableMap.builder();
+ HashSet<String> fieldNamesForCollisions = new HashSet<>();
+ List<MethodDescriptor> fieldMethods =
+ methodCache
+ .get(key)
+ .values()
+ .stream()
+ .flatMap(List::stream)
+ .filter(
+ methodDescriptor -> methodDescriptor.getAnnotation().structField())
+ .collect(Collectors.toList());
+
+ for (MethodDescriptor fieldMethod : fieldMethods) {
+ SkylarkCallable callable = fieldMethod.getAnnotation();
+ String name = callable.name();
+ if (name.isEmpty()) {
+ name =
+ StringUtilities.toPythonStyleFunctionName(
+ fieldMethod.getMethod().getName());
+ }
+ // TODO(b/72113542): Validate with annotation processor instead of at runtime.
+ if (!fieldNamesForCollisions.add(name)) {
+ throw new IllegalArgumentException(
+ String.format(
+ "Class %s has two structField methods named %s defined",
+ key.getName(), name));
+ }
+ fieldMap.put(name, fieldMethod);
+ }
+ return fieldMap.build();
+ }
+ });
+
/**
* Returns a map of methods and corresponding SkylarkCallable annotations of the methods of the
* classObj class reachable from Skylark.
@@ -260,15 +304,30 @@ public final class FuncallExpression extends Expression {
return printer.toString();
}
- /**
- * Returns the list of Skylark callable Methods of objClass with the given name and argument
- * number.
- */
+ /** Returns the Skylark callable Method of objClass with structField=true and the given name. */
+ public static MethodDescriptor getStructField(Class<?> objClass, String methodName) {
+ try {
+ return fieldCache.get(objClass).get(methodName);
+ } catch (ExecutionException e) {
+ throw new IllegalStateException("Method loading failed: " + e);
+ }
+ }
+
+ /** Returns the list of names of Skylark callable Methods of objClass with structField=true. */
+ public static Set<String> getStructFieldNames(Class<?> objClass) {
+ try {
+ return fieldCache.get(objClass).keySet();
+ } catch (ExecutionException e) {
+ throw new IllegalStateException("Method loading failed: " + e);
+ }
+ }
+
+ /** Returns the list of Skylark callable Methods of objClass with the given name. */
public static List<MethodDescriptor> getMethods(Class<?> objClass, String methodName) {
try {
return methodCache.get(objClass).get(methodName);
} catch (ExecutionException e) {
- throw new IllegalStateException("method invocation failed: " + e);
+ throw new IllegalStateException("Method loading failed: " + e);
}
}
@@ -280,10 +339,25 @@ public final class FuncallExpression extends Expression {
try {
return methodCache.get(objClass).keySet();
} catch (ExecutionException e) {
- throw new IllegalStateException("method invocation failed: " + e);
+ throw new IllegalStateException("Method loading failed: " + e);
}
}
+ /**
+ * Invokes the given structField=true method and returns the result.
+ *
+ * @param methodDescriptor the descriptor of the method to invoke
+ * @param fieldName the name of the struct field
+ * @param obj the object on which to invoke the method
+ * @return the method return value
+ * @throws EvalException if there was an issue evaluating the method
+ */
+ public static Object invokeStructField(
+ MethodDescriptor methodDescriptor, String fieldName, Object obj) throws EvalException {
+ Preconditions.checkArgument(methodDescriptor.getAnnotation().structField());
+ return callMethod(methodDescriptor, fieldName, obj, new Object[0], Location.BUILTIN, null);
+ }
+
static Object callMethod(MethodDescriptor methodDescriptor, String methodName, Object obj,
Object[] args, Location loc, Environment env) throws EvalException {
try {