diff options
| -rw-r--r-- | runtime/dex_file.cc | 18 | ||||
| -rw-r--r-- | test/005-annotations/src/android/test/anno/TestAnnotations.java | 68 | 
2 files changed, 83 insertions, 3 deletions
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc index af12abfc08..05c95e069e 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -1163,6 +1163,18 @@ static uint64_t ReadUnsignedLong(const uint8_t* ptr, int zwidth, bool fill_on_ri    return val;  } +// Checks that visibility is as expected. Includes special behavior for M and +// before to allow runtime and build visibility when expecting runtime. +static bool IsVisibilityCompatible(uint32_t actual, uint32_t expected) { +  if (expected == DexFile::kDexVisibilityRuntime) { +    int32_t sdk_version = Runtime::Current()->GetTargetSdkVersion(); +    if (sdk_version > 0 && sdk_version <= 23) { +      return actual == DexFile::kDexVisibilityRuntime || actual == DexFile::kDexVisibilityBuild; +    } +  } +  return actual == expected; +} +  const DexFile::AnnotationSetItem* DexFile::FindAnnotationSetForField(ArtField* field) const {    mirror::Class* klass = field->GetDeclaringClass();    const AnnotationsDirectoryItem* annotations_dir = GetAnnotationsDirectory(*klass->GetClassDef()); @@ -1640,7 +1652,7 @@ const DexFile::AnnotationItem* DexFile::GetAnnotationItemFromAnnotationSet(      Handle<mirror::Class> annotation_class) const {    for (uint32_t i = 0; i < annotation_set->size_; ++i) {      const AnnotationItem* annotation_item = GetAnnotationItem(annotation_set, i); -    if (annotation_item->visibility_ != visibility) { +    if (!IsVisibilityCompatible(annotation_item->visibility_, visibility)) {        continue;      }      const uint8_t* annotation = annotation_item->annotation_; @@ -1758,6 +1770,8 @@ mirror::ObjectArray<mirror::Object>* DexFile::ProcessAnnotationSet(Handle<mirror    uint32_t dest_index = 0;    for (uint32_t i = 0; i < size; ++i) {      const AnnotationItem* annotation_item = GetAnnotationItem(annotation_set, i); +    // Note that we do not use IsVisibilityCompatible here because older code +    // was correct for this case.      if (annotation_item->visibility_ != visibility) {        continue;      } @@ -2146,7 +2160,7 @@ const DexFile::AnnotationItem* DexFile::SearchAnnotationSet(const AnnotationSetI    const AnnotationItem* result = nullptr;    for (uint32_t i = 0; i < annotation_set->size_; ++i) {      const AnnotationItem* annotation_item = GetAnnotationItem(annotation_set, i); -    if (annotation_item->visibility_ != visibility) { +    if (!IsVisibilityCompatible(annotation_item->visibility_, visibility)) {        continue;      }      const uint8_t* annotation = annotation_item->annotation_; diff --git a/test/005-annotations/src/android/test/anno/TestAnnotations.java b/test/005-annotations/src/android/test/anno/TestAnnotations.java index d36d43e1ba..51254b4220 100644 --- a/test/005-annotations/src/android/test/anno/TestAnnotations.java +++ b/test/005-annotations/src/android/test/anno/TestAnnotations.java @@ -159,7 +159,23 @@ public class TestAnnotations {          System.out.println("");      } - +    public static void testVisibilityCompatibility() throws Exception { +        if (!VMRuntime.isAndroid()) { +            return; +        } +        Object runtime = VMRuntime.getRuntime(); +        int currentSdkVersion = VMRuntime.getTargetSdkVersion(runtime); +        // SDK version 23 is M. +        int oldSdkVersion = 23; +        VMRuntime.setTargetSdkVersion(runtime, oldSdkVersion); +        // This annotation has CLASS retention, but is visible to the runtime in M and earlier. +        Annotation anno = SimplyNoted.class.getAnnotation(AnnoSimpleTypeInvis.class); +        if (anno == null) { +            System.out.println("testVisibilityCompatibility failed: " + +                    "SimplyNoted.get(AnnoSimpleTypeInvis) should not be null"); +        } +        VMRuntime.setTargetSdkVersion(runtime, currentSdkVersion); +    }      public static void main(String[] args) {          System.out.println("TestAnnotations..."); @@ -229,5 +245,55 @@ public class TestAnnotations {          } catch (NoSuchFieldError expected) {              System.out.println("Got expected NoSuchFieldError");          } + +        // Test if annotations marked VISIBILITY_BUILD are visible to runtime in M and earlier. +        try { +            testVisibilityCompatibility(); +        } catch (Exception e) { +            System.out.println("testVisibilityCompatibility failed: " + e); +        } +    } + +    private static class VMRuntime { +        private static Class vmRuntimeClass; +        private static Method getRuntimeMethod; +        private static Method getTargetSdkVersionMethod; +        private static Method setTargetSdkVersionMethod; +        static { +            init(); +        } + +        private static void init() { +            try { +                vmRuntimeClass = Class.forName("dalvik.system.VMRuntime"); +            } catch (Exception e) { +                return; +            } +            try { +                getRuntimeMethod = vmRuntimeClass.getDeclaredMethod("getRuntime"); +                getTargetSdkVersionMethod = +                        vmRuntimeClass.getDeclaredMethod("getTargetSdkVersion"); +                setTargetSdkVersionMethod = +                        vmRuntimeClass.getDeclaredMethod("setTargetSdkVersion", Integer.TYPE); +            } catch (Exception e) { +                throw new RuntimeException(e); +            } +        } + +        public static boolean isAndroid() { +            return vmRuntimeClass != null; +        } + +        public static Object getRuntime() throws Exception { +            return getRuntimeMethod.invoke(null); +        } + +        public static int getTargetSdkVersion(Object runtime) throws Exception { +            return (int) getTargetSdkVersionMethod.invoke(runtime); +        } + +        public static void setTargetSdkVersion(Object runtime, int version) throws Exception { +            setTargetSdkVersionMethod.invoke(runtime, version); +        }      }  }  |