Enable SkylarkCallable methods to be used on ClassObjects. Previously, ClassObjects' SkylarkCallable struct fields worked as expected (with values of the struct taking precedence), but calling methods annotated with SkylarkCallable did not work at all; methods were treated as if they were not present in the struct. This adds some tests for the various ways of looking up fields and methods, and rearranges the logic in FuncallExpression to allow for callable methods on SkylarkClassObject's descendants. -- PiperOrigin-RevId: 150876181 MOS_MIGRATED_REVID=150876181
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 c301061..2c7a2c8 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
@@ -370,7 +370,10 @@ } if (matchingMethod == null) { String errorMessage; - if (argumentListConversionResult == null || argumentListConversionResult.getError() == null) { + if (ClassObject.class.isAssignableFrom(objClass)) { + errorMessage = String.format("struct has no method '%s'", methodName); + } else if (argumentListConversionResult == null + || argumentListConversionResult.getError() == null) { errorMessage = String.format( "type '%s' has no method %s", @@ -606,6 +609,8 @@ Object value = positionals.get(0); ImmutableList<Object> positionalArgs = positionals.subList(1, positionals.size()); BaseFunction function = Runtime.getFunction(EvalUtils.getSkylarkType(value.getClass()), method); + Object fieldValue = + (value instanceof ClassObject) ? ((ClassObject) value).getValue(method) : null; if (function != null) { if (!isNamespace(value.getClass())) { // Use self as an implicit parameter in front. @@ -613,11 +618,7 @@ } return function.call( positionalArgs, ImmutableMap.<String, Object>copyOf(keyWordArgs), call, env); - } else if (value instanceof ClassObject) { - Object fieldValue = ((ClassObject) value).getValue(method); - if (fieldValue == null) { - throw new EvalException(location, String.format("struct has no method '%s'", method)); - } + } else if (fieldValue != null) { if (!(fieldValue instanceof BaseFunction)) { throw new EvalException( location, String.format("struct field '%s' is not a function", method));