From 42e63a757db0f9d29d9d9b31fdb1a74c4c160656 Mon Sep 17 00:00:00 2001 From: zgao Date: Tue, 3 Jan 2017 08:03:19 -0800 Subject: [PATCH] Add ReflectionSupport level to PackageData Change on 2017/01/03 by zgao ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=143445068 --- .../j2objc/util/PackageInfoLookup.java | 59 +++++++++++++++++++ .../google/devtools/j2objc/SmallTests.java | 2 + .../j2objc/util/PackageInfoLookupTest.java | 50 ++++++++++++++++ 3 files changed, 111 insertions(+) create mode 100644 translator/src/test/java/com/google/devtools/j2objc/util/PackageInfoLookupTest.java diff --git a/translator/src/main/java/com/google/devtools/j2objc/util/PackageInfoLookup.java b/translator/src/main/java/com/google/devtools/j2objc/util/PackageInfoLookup.java index a16b1aec30..4d0bb395a4 100644 --- a/translator/src/main/java/com/google/devtools/j2objc/util/PackageInfoLookup.java +++ b/translator/src/main/java/com/google/devtools/j2objc/util/PackageInfoLookup.java @@ -15,9 +15,12 @@ package com.google.devtools.j2objc.util; import com.google.devtools.j2objc.file.InputFile; +import com.google.j2objc.annotations.ReflectionSupport; import java.io.IOException; import java.util.HashMap; import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; @@ -31,6 +34,9 @@ public class PackageInfoLookup { private final Map map = new HashMap<>(); private final FileUtil fileUtil; + private static final String REFLECTION_SUPPORT_REGEX = + "@(?:com\\.google\\.j2objc\\.annotations\\.)?ReflectionSupport\\s*" + + "\\([^\\)]*(FULL|NATIVE_ONLY)\\s*\\)"; // Avoid allocating a new PackageData instance for packages with no attributes. private static final PackageData EMPTY_DATA = new PackageData(new PackageDataBuilder()); @@ -42,10 +48,12 @@ private static class PackageData { private final String objectiveCName; private final boolean parametersAreNonnullByDefault; + private final ReflectionSupport.Level reflectionSupportLevel; private PackageData(PackageDataBuilder builder) { this.objectiveCName = builder.objectiveCName; this.parametersAreNonnullByDefault = builder.parametersAreNonnullByDefault; + this.reflectionSupportLevel = builder.reflectionSupportLevel; } } @@ -54,6 +62,7 @@ private static class PackageDataBuilder { private boolean isEmpty = true; private String objectiveCName = null; private boolean parametersAreNonnullByDefault = false; + private ReflectionSupport.Level reflectionSupportLevel; private void setObjectiveCName(String objectiveCName) { this.objectiveCName = objectiveCName; @@ -65,6 +74,11 @@ private void setParametersAreNonnullByDefault() { isEmpty = false; } + private void setReflectionSupportLevel(ReflectionSupport.Level level) { + this.reflectionSupportLevel = level; + isEmpty = false; + } + private PackageData build() { return isEmpty ? EMPTY_DATA : new PackageData(this); } @@ -78,6 +92,10 @@ public boolean hasParametersAreNonnullByDefault(String packageName) { return getPackageData(packageName).parametersAreNonnullByDefault; } + public ReflectionSupport.Level getReflectionSupportLevel(String packageName) { + return getPackageData(packageName).reflectionSupportLevel; + } + private PackageData getPackageData(String packageName) { PackageData result = map.get(packageName); if (result == null) { @@ -106,6 +124,34 @@ private PackageData findPackageData(String packageName) { return EMPTY_DATA; } + /** + * Return true if pkgInfo has the specified annotation. + * + * @param pkgInfo package-info source code + * @param annotation fully qualified name of the annotation + */ + private static boolean hasAnnotation(String pkgInfo, String annotation) { + if (!annotation.contains(".")) { + ErrorUtil.warning(annotation + " is not a fully qualified name"); + } + if (pkgInfo.contains("@" + annotation)) { + return true; + } + int idx = annotation.lastIndexOf("."); + String annotationPackageName = annotation.substring(0, idx); + String annotationSimpleName = annotation.substring(idx + 1); + if (pkgInfo.contains("@" + annotationSimpleName)) { + String importRegex = + "import\\s*" + annotationPackageName + "(\\.\\*|\\." + annotationSimpleName + ")"; + Pattern p = Pattern.compile(importRegex); + Matcher m = p.matcher(pkgInfo); + if (m.find()) { + return true; + } + } + return false; + } + private PackageData parseDataFromSourceFile(InputFile file) throws IOException { PackageDataBuilder builder = new PackageDataBuilder(); String pkgInfo = fileUtil.readFile(file); @@ -131,9 +177,22 @@ private PackageData parseDataFromSourceFile(InputFile file) throws IOException { || pkgInfo.contains("@javax.annotation.ParametersAreNonnullByDefault")) { builder.setParametersAreNonnullByDefault(); } + + // @ReflectionSupportLevel + if (hasAnnotation(pkgInfo, "com.google.j2objc.annotations.ReflectionSupport")) { + Pattern p = Pattern.compile(REFLECTION_SUPPORT_REGEX); + Matcher m = p.matcher(pkgInfo); + if (m.find()) { + String level = m.group(1); + builder.setReflectionSupportLevel(ReflectionSupport.Level.valueOf(level)); + } else { + ErrorUtil.warning("Invalid ReflectionSupport Level in " + file.getUnitName()); + } + } return builder.build(); } + // TODO(user): Support parsing ReflectionSupport annotation from .class files private PackageData parseDataFromClassFile(InputFile file) throws IOException { PackageDataBuilder builder = new PackageDataBuilder(); ClassReader classReader = new ClassReader(file.getInputStream()); diff --git a/translator/src/test/java/com/google/devtools/j2objc/SmallTests.java b/translator/src/test/java/com/google/devtools/j2objc/SmallTests.java index 176a72d309..177291ab6f 100644 --- a/translator/src/test/java/com/google/devtools/j2objc/SmallTests.java +++ b/translator/src/test/java/com/google/devtools/j2objc/SmallTests.java @@ -79,6 +79,7 @@ import com.google.devtools.j2objc.util.ErrorUtilTest; import com.google.devtools.j2objc.util.FileUtilTest; import com.google.devtools.j2objc.util.NameTableTest; +import com.google.devtools.j2objc.util.PackageInfoLookupTest; import com.google.devtools.j2objc.util.PackagePrefixesTest; import com.google.devtools.j2objc.util.ProGuardUsageParserTest; import com.google.devtools.j2objc.util.TranslationUtilTest; @@ -140,6 +141,7 @@ public class SmallTests { OptionsTest.class, OuterReferenceFixerTest.class, OuterReferenceResolverTest.class, + PackageInfoLookupTest.class, PackageInfoRewriterTest.class, PackagePrefixesTest.class, PrimitiveArrayTest.class, diff --git a/translator/src/test/java/com/google/devtools/j2objc/util/PackageInfoLookupTest.java b/translator/src/test/java/com/google/devtools/j2objc/util/PackageInfoLookupTest.java new file mode 100644 index 0000000000..c935949ba1 --- /dev/null +++ b/translator/src/test/java/com/google/devtools/j2objc/util/PackageInfoLookupTest.java @@ -0,0 +1,50 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.devtools.j2objc.util; + +import com.google.devtools.j2objc.GenerationTest; +import com.google.devtools.j2objc.ast.CompilationUnit; +import com.google.j2objc.annotations.ReflectionSupport; +import java.io.IOException; + +/** + * Unit tests for the {@link PackageInfoLookup}. + * + * @author Tim Gao + */ +public class PackageInfoLookupTest extends GenerationTest { + + public void testReflectionSupportAnnotation() throws IOException { + addSourceFile("@ReflectionSupport(value = ReflectionSupport.Level.FULL) package foo;" + + "import com.google.j2objc.annotations.ReflectionSupport;", "foo/package-info.java"); + CompilationUnit unit = translateType("foo.A", "package foo; public class A {}"); + PackageInfoLookup packageInfoLookup = unit.getEnv().options().getPackageInfoLookup(); + assert packageInfoLookup.getReflectionSupportLevel("foo") == ReflectionSupport.Level.FULL; + + addSourceFile("@ReflectionSupport(ReflectionSupport.Level.FULL) package bar;" + + "import com.google.j2objc.annotations.*;", "bar/package-info.java"); + unit = translateType("bar.A", "package bar; public class A {}"); + packageInfoLookup = unit.getEnv().options().getPackageInfoLookup(); + assert packageInfoLookup.getReflectionSupportLevel("bar") == ReflectionSupport.Level.FULL; + + addSourceFile("@com.google.j2objc.annotations.ReflectionSupport" + + "(com.google.j2objc.annotations.ReflectionSupport.Level.NATIVE_ONLY) package baz;", + "baz/package-info.java"); + unit = translateType("baz.A", "package baz; public class A {}"); + packageInfoLookup = unit.getEnv().options().getPackageInfoLookup(); + assert + packageInfoLookup.getReflectionSupportLevel("baz") == ReflectionSupport.Level.NATIVE_ONLY; + } +}