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-04-24 08:07:02 -0700
committerGravatar Copybara-Service <copybara-piper@google.com>2018-04-24 08:09:17 -0700
commit0e86686e3b1769213f70fef08047946d3d29db29 (patch)
tree9720596348f871070b9c7e651d4bd9bcc78c19f7 /src/main/java/com/google/devtools/build/lib/syntax/FuncallExpression.java
parent451d2c9cd67d5d29e85784f403053f10b6e119ef (diff)
Introduce @SkylarkCallable.selfCall, to signify the containing class should be treated as a callable skylark object.
This will allow Skylark Provider objects to be better specified. For example, "JavaInfo" can have a fully-documented, fully-specified @SkylarkCallable method with selfCall=true to represent the method JavaInfo(), instead of being a subclass of BaseFunction and requiring a @SkylarkSignature annotation. There are no usages of this pattern introduced in this CL, and also no updates to docgen to support the new pattern. These will be introduced in another CL. RELNOTES: None. PiperOrigin-RevId: 194088227
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.java72
1 files changed, 70 insertions, 2 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 dba8ffc92c..236bcc2576 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
@@ -49,6 +49,7 @@ import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
@@ -82,6 +83,33 @@ public final class FuncallExpression extends Expression {
}
}
+ private static final LoadingCache<Class<?>, Optional<MethodDescriptor>> selfCallCache =
+ CacheBuilder.newBuilder()
+ .build(
+ new CacheLoader<Class<?>, Optional<MethodDescriptor>>() {
+ @Override
+ public Optional<MethodDescriptor> load(Class<?> key) throws Exception {
+ MethodDescriptor returnValue = null;
+ for (Method method : key.getMethods()) {
+ // Synthetic methods lead to false multiple matches
+ if (method.isSynthetic()) {
+ continue;
+ }
+ SkylarkCallable callable = SkylarkInterfaceUtils.getSkylarkCallable(method);
+ if (callable != null && callable.selfCall()) {
+ if (returnValue != null) {
+ throw new IllegalArgumentException(
+ String.format(
+ "Class %s has two selfCall methods defined",
+ key.getName()));
+ }
+ returnValue = new MethodDescriptor(method, callable);
+ }
+ }
+ return Optional.ofNullable(returnValue);
+ }
+ });
+
private static final LoadingCache<Class<?>, Map<String, List<MethodDescriptor>>> methodCache =
CacheBuilder.newBuilder()
.build(
@@ -99,6 +127,10 @@ public final class FuncallExpression extends Expression {
if (callable == null) {
continue;
}
+ if (callable.selfCall()) {
+ // Self-call java methods are not treated as methods of the skylark value.
+ continue;
+ }
String name = callable.name();
if (name.isEmpty()) {
name = StringUtilities.toPythonStyleFunctionName(method.getName());
@@ -330,6 +362,38 @@ public final class FuncallExpression extends Expression {
}
/**
+ * Returns true if the given class has a method annotated with {@link SkylarkCallable}
+ * with {@link SkylarkCallable#selfCall()} set to true.
+ */
+ public static boolean hasSelfCallMethod(Class<?> objClass) {
+ try {
+ return selfCallCache.get(objClass).isPresent();
+ } catch (ExecutionException e) {
+ throw new IllegalStateException("Method loading failed: " + e);
+ }
+ }
+
+ /**
+ * Returns a {@link BuiltinCallable} object representing a function which calls the selfCall
+ * java method of the given object (the {@link SkylarkCallable} method with
+ * {@link SkylarkCallable#selfCall()} set to true).
+ *
+ * @throws IllegalStateException if no such method exists for the object
+ */
+ public static BuiltinCallable getSelfCallMethod(Object obj) {
+ try {
+ Optional<MethodDescriptor> selfCallDescriptor = selfCallCache.get(obj.getClass());
+ if (!selfCallDescriptor.isPresent()) {
+ throw new IllegalStateException("Class " + obj.getClass() + " has no selfCall method");
+ }
+ MethodDescriptor descriptor = selfCallDescriptor.get();
+ return new BuiltinCallable(descriptor.getAnnotation().name(), obj, descriptor);
+ } catch (ExecutionException e) {
+ throw new IllegalStateException("Method loading failed: " + e);
+ }
+ }
+
+ /**
* Returns a {@link BuiltinCallable} representing a {@link SkylarkCallable}-annotated instance
* method of a given object with the given method name.
*/
@@ -728,14 +792,18 @@ public final class FuncallExpression extends Expression {
}
/**
- * Checks whether the given object is a {@link BaseFunction}.
+ * Checks whether the given object is callable, either by being a {@link BaseFunction} or having
+ * a {@link SkylarkCallable}-annotated method with selfCall = true.
*
- * @throws EvalException If not a BaseFunction.
+ * @return a BaseFunction object representing the callable function this object represents
+ * @throws EvalException if the object is not callable.
*/
private static BaseFunction checkCallable(Object functionValue, Location location)
throws EvalException {
if (functionValue instanceof BaseFunction) {
return (BaseFunction) functionValue;
+ } else if (hasSelfCallMethod(functionValue.getClass())) {
+ return getSelfCallMethod(functionValue);
} else {
throw new EvalException(
location, "'" + EvalUtils.getDataTypeName(functionValue) + "' object is not callable");