diff options
| author | 2016-05-26 18:39:17 -0700 | |
|---|---|---|
| committer | 2016-05-27 17:27:28 -0700 | |
| commit | 3d0808655cd8cfbc867bf330bcc6f954c59beb9d (patch) | |
| tree | e7e6e6c62562b6f19e0dbf7b2249a900a1938afa | |
| parent | d355b75ca09d83c47e596daeb1a434942de83bee (diff) | |
Relax annotation visibility so runtime includes build.
Previous behavior in M and earlier would allow annotations marked
VISIBILITY_BUILD to be visible to the runtime when they should not
have been. When targeting older sdks, revert to this behavior.
Bug: 28826610
(cherry-picked from commit 7e50a7a5a1ad947e84c425efb2e97c442f91b155)
Change-Id: I43d2328be41ec9f4e679b3151f586c0b87b38c7c
| -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); + } } } |