diff options
author | cparsons <cparsons@google.com> | 2018-02-05 02:01:28 -0800 |
---|---|---|
committer | Copybara-Service <copybara-piper@google.com> | 2018-02-05 02:02:56 -0800 |
commit | 6c1c0660dd51420160eb1c0f5eac7e67917a231c (patch) | |
tree | 756d35b5aad7d6f136d44df2450df63e1884733b /src/main/java/com/google/devtools/build/lib/syntax/FuncallExpression.java | |
parent | 152b22dd9d817e7f01f88cd8b62928052f0a32df (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.java | 86 |
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 { |