diff options
author | 2016-10-31 14:09:41 +0000 | |
---|---|---|
committer | 2016-10-31 15:05:25 +0000 | |
commit | a5b1674d7b237965694217b82c907ac5d64ce053 (patch) | |
tree | 74ccca5a4f598d562c48c9bc1a7a10b8f0adabbf /src/main/java/com/google/devtools | |
parent | 6a922aab3dbefc45e16b2bd7a16613483dd73caf (diff) |
Allow calling attributes of built-in objects.
If `f` is an object with an attribute `x` which is callable, it's valid now to
call `f.x()` directly (caused a "no function called x" error before because .x
is a attribute, not a method).
--
MOS_MIGRATED_REVID=137698425
Diffstat (limited to 'src/main/java/com/google/devtools')
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/syntax/FuncallExpression.java | 36 |
1 files changed, 31 insertions, 5 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 4b233502d8..5748f63bdb 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 @@ -361,7 +361,9 @@ public final class FuncallExpression extends Expression { ArgumentListConversionResult argumentListConversionResult = null; if (methods != null) { for (MethodDescriptor method : methods) { - if (!method.getAnnotation().structField()) { + if (method.getAnnotation().structField()) { + return new Pair<>(method, null); + } else { argumentListConversionResult = convertArgumentList(args, kwargs, method); if (argumentListConversionResult.getArguments() != null) { if (matchingMethod == null) { @@ -410,8 +412,7 @@ public final class FuncallExpression extends Expression { /** * Constructs the parameters list to actually pass to the method, filling with default values if - * any. If the type does not match, returns null and fills in methodTypeMatchError with the - * appropriate message. + * any. If there is a type or argument mismatch, returns a result containing an error message. */ private ArgumentListConversionResult convertArgumentList( List<Object> args, Map<String, Object> kwargs, MethodDescriptor method) { @@ -666,7 +667,7 @@ public final class FuncallExpression extends Expression { * @param positionals The first object is expected to be the object the method is called on. * @param call the original expression that caused this call, needed for rules especially */ - public static Object invokeObjectMethod( + public Object invokeObjectMethod( String method, ImmutableList<Object> positionals, ImmutableMap<String, Object> keyWordArgs, @@ -711,6 +712,23 @@ public final class FuncallExpression extends Expression { } Pair<MethodDescriptor, List<Object>> javaMethod = call.findJavaMethod(objClass, method, positionalArgs, keyWordArgs); + if (javaMethod.first.getAnnotation().structField()) { + // Not a method but a callable attribute + try { + return callFunction(javaMethod.first.getMethod().invoke(obj), env); + } catch (IllegalAccessException e) { + throw new EvalException(getLocation(), "Method invocation failed: " + e); + } catch (InvocationTargetException e) { + if (e.getCause() instanceof FuncallException) { + throw new EvalException(getLocation(), e.getCause().getMessage()); + } else if (e.getCause() != null) { + throw new EvalExceptionWithJavaCause(getLocation(), e.getCause()); + } else { + // This is unlikely to happen + throw new EvalException(getLocation(), "Method invocation failed: " + e); + } + } + } return callMethod(javaMethod.first, method, obj, javaMethod.second.toArray(), location, env); } } @@ -772,13 +790,21 @@ public final class FuncallExpression extends Expression { */ private Object invokeGlobalFunction(Environment env) throws EvalException, InterruptedException { Object funcValue = func.eval(env); + return callFunction(funcValue, env); + } + + /** + * Calls a function object + */ + private Object callFunction(Object funcValue, Environment env) + throws EvalException, InterruptedException { ImmutableList.Builder<Object> posargs = new ImmutableList.Builder<>(); // We copy this into an ImmutableMap in the end, but we can't use an ImmutableMap.Builder, or // we'd still have to have a HashMap on the side for the sake of properly handling duplicates. Map<String, Object> kwargs = new HashMap<>(); BaseFunction function = checkCallable(funcValue, getLocation()); evalArguments(posargs, kwargs, env); - return function.call(posargs.build(), ImmutableMap.<String, Object>copyOf(kwargs), this, env); + return function.call(posargs.build(), ImmutableMap.copyOf(kwargs), this, env); } /** |