diff options
author | cparsons <cparsons@google.com> | 2018-05-24 13:48:22 -0700 |
---|---|---|
committer | Copybara-Service <copybara-piper@google.com> | 2018-05-24 13:51:33 -0700 |
commit | 4dc97ffb3c7afb463678997d5549d9b7f959e394 (patch) | |
tree | 860f6cec29ac91ae42a53eccfaad0a39fd61b360 /src/main | |
parent | ee5ea6bde9c774623c8083914b84f6a53dc82288 (diff) |
Make @SkylarkModule detection (through superclasses/superinterfaces) well-defined.
Moving forward, if a class not already annotated with @SkylarkModule has SkylarkModule supertypes A and B, then it must be the case that A is a type of B, or B is a type of A. The skylark type of a given class is dictated by the *most specific* superclass annotated with @SkylarkModule.
RELNOTES: None.
PiperOrigin-RevId: 197946898
Diffstat (limited to 'src/main')
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/skylarkbuildapi/StructApi.java | 2 | ||||
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/skylarkinterface/SkylarkInterfaceUtils.java | 52 |
2 files changed, 46 insertions, 8 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/StructApi.java b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/StructApi.java index debb5bd1f1..5c0974975c 100644 --- a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/StructApi.java +++ b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/StructApi.java @@ -80,7 +80,7 @@ public interface StructApi extends SkylarkValue { * Callable Provider for new struct objects. */ @SkylarkModule(name = "Provider", documented = false, doc = "") - public interface StructProviderApi { + public interface StructProviderApi extends ProviderApi { @SkylarkCallable( name = "struct", diff --git a/src/main/java/com/google/devtools/build/lib/skylarkinterface/SkylarkInterfaceUtils.java b/src/main/java/com/google/devtools/build/lib/skylarkinterface/SkylarkInterfaceUtils.java index 81cd6aec06..3f25c32598 100644 --- a/src/main/java/com/google/devtools/build/lib/skylarkinterface/SkylarkInterfaceUtils.java +++ b/src/main/java/com/google/devtools/build/lib/skylarkinterface/SkylarkInterfaceUtils.java @@ -33,6 +33,46 @@ public class SkylarkInterfaceUtils { } } + /** + * Returns the more specific class of two classes. Class x is more specific than class y + * if x is assignable to y. For example, of Integer.class and Object.class, Integer.class is more + * specific. + * + * <p>If either class is null, returns the other class.</p> + * + * <p>If the classes are identical, returns the class.</p> + * + * @throws IllegalArgumentException if neither class is assignable to the other + */ + private static <T extends Annotation> ClassWithAnnotation<T> moreSpecificClass( + ClassWithAnnotation<T> x, ClassWithAnnotation<T> y) { + if (x == null) { + return y; + } else if (y == null) { + return x; + } + Class<?> xClass = x.klass; + Class<?> yClass = y.klass; + if (xClass.isAssignableFrom(yClass)) { + return y; + } else if (yClass.isAssignableFrom(xClass)) { + return x; + } else { + throw new IllegalArgumentException(String.format( + "Expected one of %s and %s to be assignable to each other", + xClass, yClass)); + } + } + + /** + * Searches a class or interface's class hierarchy for the given class annotation. + * + * <p>If the given class annotation appears multiple times within the class hierachy, this + * chooses the annotation on the most-specified class in the hierarchy.</p> + * + * @return a {@link ClassWithAnnotation} containing the best-fit annotation and the class + * it was declared on + */ @Nullable private static <T extends Annotation> ClassWithAnnotation<T> searchForClassAnnotation( Class<?> classObj, @@ -40,20 +80,18 @@ public class SkylarkInterfaceUtils { if (classObj.isAnnotationPresent(annotationClass)) { return new ClassWithAnnotation<T>(classObj, classObj.getAnnotation(annotationClass)); } + ClassWithAnnotation<T> bestCandidate = null; + Class<?> superclass = classObj.getSuperclass(); if (superclass != null) { ClassWithAnnotation<T> result = searchForClassAnnotation(superclass, annotationClass); - if (result != null) { - return result; - } + bestCandidate = moreSpecificClass(result, bestCandidate); } for (Class<?> interfaceObj : classObj.getInterfaces()) { ClassWithAnnotation<T> result = searchForClassAnnotation(interfaceObj, annotationClass); - if (result != null) { - return result; - } + bestCandidate = moreSpecificClass(result, bestCandidate); } - return null; + return bestCandidate; } /** |