aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com')
-rw-r--r--src/main/java/com/google/devtools/build/lib/packages/StructProvider.java10
-rw-r--r--src/main/java/com/google/devtools/build/lib/skylarkinterface/processor/SkylarkCallableProcessor.java4
-rw-r--r--src/main/java/com/google/devtools/build/lib/syntax/DotExpression.java42
-rw-r--r--src/main/java/com/google/devtools/build/lib/syntax/FuncallExpression.java61
4 files changed, 81 insertions, 36 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/packages/StructProvider.java b/src/main/java/com/google/devtools/build/lib/packages/StructProvider.java
index 21f847528b..4396b43a15 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/StructProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/StructProvider.java
@@ -38,8 +38,14 @@ public final class StructProvider extends BuiltinProvider<Info>
@Override
public Info createStruct(SkylarkDict<?, ?> kwargs, Location loc) throws EvalException {
- return SkylarkInfo.createSchemaless(
- this, kwargs.getContents(String.class, Object.class, "kwargs"), loc);
+ Map<String, Object> kwargsMap = kwargs.getContents(String.class, Object.class, "kwargs");
+ if (kwargsMap.containsKey("to_json")) {
+ throw new EvalException(loc, "cannot override built-in struct function 'to_json'");
+ }
+ if (kwargsMap.containsKey("to_proto")) {
+ throw new EvalException(loc, "cannot override built-in struct function 'to_proto'");
+ }
+ return SkylarkInfo.createSchemaless(this, kwargsMap, loc);
}
/**
diff --git a/src/main/java/com/google/devtools/build/lib/skylarkinterface/processor/SkylarkCallableProcessor.java b/src/main/java/com/google/devtools/build/lib/skylarkinterface/processor/SkylarkCallableProcessor.java
index 71df625cba..2ccc6ccb49 100644
--- a/src/main/java/com/google/devtools/build/lib/skylarkinterface/processor/SkylarkCallableProcessor.java
+++ b/src/main/java/com/google/devtools/build/lib/skylarkinterface/processor/SkylarkCallableProcessor.java
@@ -158,14 +158,12 @@ public final class SkylarkCallableProcessor extends AbstractProcessor {
throws SkylarkCallableProcessorException {
if (annotation.structField()) {
if (annotation.useAst()
- || annotation.useEnvironment()
- || annotation.useAst()
|| !annotation.extraPositionals().name().isEmpty()
|| !annotation.extraKeywords().name().isEmpty()) {
throw new SkylarkCallableProcessorException(
methodElement,
"@SkylarkCallable-annotated methods with structField=true may not also specify "
- + "useAst, useEnvironment, useLocation, extraPositionals, or extraKeywords");
+ + "useAst, extraPositionals, or extraKeywords");
}
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/DotExpression.java b/src/main/java/com/google/devtools/build/lib/syntax/DotExpression.java
index ed45d6079f..b41f274c37 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/DotExpression.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/DotExpression.java
@@ -118,6 +118,32 @@ public final class DotExpression extends Expression {
*/
public static Object eval(Object objValue, String name,
Location loc, Environment env) throws EvalException, InterruptedException {
+
+ Iterable<MethodDescriptor> methods =
+ objValue instanceof Class<?>
+ ? FuncallExpression.getMethods((Class<?>) objValue, name)
+ : FuncallExpression.getMethods(objValue.getClass(), name);
+
+ if (methods != null) {
+ Optional<MethodDescriptor> method =
+ Streams.stream(methods)
+ .filter(methodDescriptor -> methodDescriptor.getAnnotation().structField())
+ .findFirst();
+ if (method.isPresent() && method.get().getAnnotation().structField()) {
+ return FuncallExpression.callMethod(
+ method.get(),
+ name,
+ objValue,
+ FuncallExpression.extraInterpreterArgs(
+ method.get().getAnnotation(),
+ /* ast = */ null,
+ loc,
+ env).toArray(),
+ loc,
+ env);
+ }
+ }
+
if (objValue instanceof SkylarkClassObject) {
try {
return ((SkylarkClassObject) objValue).getValue(name);
@@ -142,22 +168,6 @@ public final class DotExpression extends Expression {
}
}
- Iterable<MethodDescriptor> methods =
- objValue instanceof Class<?>
- ? FuncallExpression.getMethods((Class<?>) objValue, name)
- : FuncallExpression.getMethods(objValue.getClass(), name);
-
- if (methods != null) {
- Optional<MethodDescriptor> method =
- Streams.stream(methods)
- .filter(methodDescriptor -> methodDescriptor.getAnnotation().structField())
- .findFirst();
- if (method.isPresent() && method.get().getAnnotation().structField()) {
- return FuncallExpression.callMethod(
- method.get(), name, objValue, new Object[] {}, loc, env);
- }
- }
-
return null;
}
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 236bcc2576..bf43266b21 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
@@ -411,6 +411,9 @@ public final class FuncallExpression extends Expression {
/**
* Invokes the given structField=true method and returns the result.
*
+ * <p>The given method must <b>not</b> require extra-interpreter parameters, such as
+ * {@link Environment}. This method throws {@link IllegalArgumentException} for violations.</p>
+ *
* @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
@@ -420,7 +423,12 @@ public final class FuncallExpression extends Expression {
public static Object invokeStructField(
MethodDescriptor methodDescriptor, String fieldName, Object obj)
throws EvalException, InterruptedException {
- Preconditions.checkArgument(methodDescriptor.getAnnotation().structField());
+ Preconditions.checkArgument(methodDescriptor.getAnnotation().structField(),
+ "Can only be invoked on structField callables");
+ Preconditions.checkArgument(!methodDescriptor.getAnnotation().useEnvironment()
+ || !methodDescriptor.getAnnotation().useSkylarkSemantics()
+ || !methodDescriptor.getAnnotation().useLocation(),
+ "Cannot be invoked on structField callables with extra interpreter params");
return callMethod(methodDescriptor, fieldName, obj, new Object[0], Location.BUILTIN, null);
}
@@ -494,8 +502,12 @@ public final class FuncallExpression extends Expression {
if (methods != null) {
for (MethodDescriptor method : methods) {
if (method.getAnnotation().structField()) {
- // TODO(cparsons): Allow structField methods to accept interpreter-supplied arguments.
- return new Pair<>(method, null);
+ // This indicates a built-in structField which returns a function which may have
+ // one or more arguments itself. For example, foo.bar('baz'), where foo.bar is a
+ // structField returning a function. Calling the "bar" callable of foo should
+ // not have 'baz' propagated, though extra interpreter arguments should be supplied.
+ return new Pair<>(method,
+ extraInterpreterArgs(method.getAnnotation(), null, getLocation(), environment));
} else {
argumentListConversionResult = convertArgumentList(args, kwargs, method, environment);
if (argumentListConversionResult.getArguments() != null) {
@@ -567,6 +579,36 @@ public final class FuncallExpression extends Expression {
}
/**
+ * Returns the extra interpreter arguments for the given {@link SkylarkCallable}, to be added
+ * at the end of the argument list for the callable.
+ *
+ * <p>This method accepts null {@code ast} only if {@code callable.useAst()} is false. It is
+ * up to the caller to validate this invariant.</p>
+ */
+ public static List<Object> extraInterpreterArgs(SkylarkCallable callable,
+ @Nullable FuncallExpression ast, Location loc, Environment env) {
+
+ ImmutableList.Builder<Object> builder = ImmutableList.builder();
+
+ if (callable.useLocation()) {
+ builder.add(loc);
+ }
+ if (callable.useAst()) {
+ if (ast == null) {
+ throw new IllegalArgumentException("Callable expects to receive ast: " + callable.name());
+ }
+ builder.add(ast);
+ }
+ if (callable.useEnvironment()) {
+ builder.add(env);
+ }
+ if (callable.useSkylarkSemantics()) {
+ builder.add(env.getSemantics());
+ }
+ return builder.build();
+ }
+
+ /**
* Constructs the parameters list to actually pass to the method, filling with default values if
* any. If there is a type or argument mismatch, returns a result containing an error message.
*/
@@ -702,18 +744,7 @@ public final class FuncallExpression extends Expression {
if (acceptsExtraKwargs) {
builder.add(SkylarkDict.copyOf(environment, extraKwargsBuilder.build()));
}
- if (callable.useLocation()) {
- builder.add(getLocation());
- }
- if (callable.useAst()) {
- builder.add(this);
- }
- if (callable.useEnvironment()) {
- builder.add(environment);
- }
- if (callable.useSkylarkSemantics()) {
- builder.add(environment.getSemantics());
- }
+ builder.addAll(extraInterpreterArgs(callable, this, getLocation(), environment));
return ArgumentListConversionResult.fromArgumentList(builder.build());
}