Merge "Fix ComputeModifiedUtf8Hash()."
diff --git a/compiler/optimizing/code_generator_vector_arm64.cc b/compiler/optimizing/code_generator_vector_arm64.cc
index 5a18c1f..9506ff8 100644
--- a/compiler/optimizing/code_generator_vector_arm64.cc
+++ b/compiler/optimizing/code_generator_vector_arm64.cc
@@ -16,6 +16,7 @@
#include "code_generator_arm64.h"
+#include "arch/arm64/instruction_set_features_arm64.h"
#include "mirror/array-inl.h"
#include "mirror/string.h"
@@ -37,6 +38,14 @@
#define __ GetVIXLAssembler()->
+// Build-time switch for Armv8.4-a dot product instructions.
+static constexpr bool kArm64EmitDotProdInstructions = true;
+
+// Returns whether dot product instructions should be emitted.
+static bool ShouldEmitDotProductInstructions(const CodeGeneratorARM64* codegen_) {
+ return kArm64EmitDotProdInstructions && codegen_->GetInstructionSetFeatures().HasDotProd();
+}
+
void LocationsBuilderARM64::VisitVecReplicateScalar(HVecReplicateScalar* instruction) {
LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
HInstruction* input = instruction->InputAt(0);
@@ -1285,8 +1294,9 @@
locations->SetInAt(2, Location::RequiresFpuRegister());
locations->SetOut(Location::SameAsFirstInput());
- // For Int8 and Uint8 we need a temp register.
- if (DataType::Size(instruction->InputAt(1)->AsVecOperation()->GetPackedType()) == 1) {
+ // For Int8 and Uint8 general case we need a temp register.
+ if ((DataType::Size(instruction->InputAt(1)->AsVecOperation()->GetPackedType()) == 1) &&
+ !ShouldEmitDotProductInstructions(codegen_)) {
locations->AddTemp(Location::RequiresFpuRegister());
}
}
@@ -1308,25 +1318,32 @@
switch (inputs_data_size) {
case 1u: {
DCHECK_EQ(16u, a->GetVectorLength());
- VRegister tmp = VRegisterFrom(locations->GetTemp(0));
if (instruction->IsZeroExtending()) {
- // TODO: Use Armv8.4-A UDOT instruction when it is available.
- __ Umull(tmp.V8H(), left.V8B(), right.V8B());
- __ Uaddw(acc.V4S(), acc.V4S(), tmp.V4H());
- __ Uaddw2(acc.V4S(), acc.V4S(), tmp.V8H());
+ if (ShouldEmitDotProductInstructions(codegen_)) {
+ __ Udot(acc.V4S(), left.V16B(), right.V16B());
+ } else {
+ VRegister tmp = VRegisterFrom(locations->GetTemp(0));
+ __ Umull(tmp.V8H(), left.V8B(), right.V8B());
+ __ Uaddw(acc.V4S(), acc.V4S(), tmp.V4H());
+ __ Uaddw2(acc.V4S(), acc.V4S(), tmp.V8H());
- __ Umull2(tmp.V8H(), left.V16B(), right.V16B());
- __ Uaddw(acc.V4S(), acc.V4S(), tmp.V4H());
- __ Uaddw2(acc.V4S(), acc.V4S(), tmp.V8H());
+ __ Umull2(tmp.V8H(), left.V16B(), right.V16B());
+ __ Uaddw(acc.V4S(), acc.V4S(), tmp.V4H());
+ __ Uaddw2(acc.V4S(), acc.V4S(), tmp.V8H());
+ }
} else {
- // TODO: Use Armv8.4-A SDOT instruction when it is available.
- __ Smull(tmp.V8H(), left.V8B(), right.V8B());
- __ Saddw(acc.V4S(), acc.V4S(), tmp.V4H());
- __ Saddw2(acc.V4S(), acc.V4S(), tmp.V8H());
+ if (ShouldEmitDotProductInstructions(codegen_)) {
+ __ Sdot(acc.V4S(), left.V16B(), right.V16B());
+ } else {
+ VRegister tmp = VRegisterFrom(locations->GetTemp(0));
+ __ Smull(tmp.V8H(), left.V8B(), right.V8B());
+ __ Saddw(acc.V4S(), acc.V4S(), tmp.V4H());
+ __ Saddw2(acc.V4S(), acc.V4S(), tmp.V8H());
- __ Smull2(tmp.V8H(), left.V16B(), right.V16B());
- __ Saddw(acc.V4S(), acc.V4S(), tmp.V4H());
- __ Saddw2(acc.V4S(), acc.V4S(), tmp.V8H());
+ __ Smull2(tmp.V8H(), left.V16B(), right.V16B());
+ __ Saddw(acc.V4S(), acc.V4S(), tmp.V4H());
+ __ Saddw2(acc.V4S(), acc.V4S(), tmp.V8H());
+ }
}
break;
}
diff --git a/tools/class2greylist/src/com/android/class2greylist/AnnotatedClassContext.java b/tools/class2greylist/src/com/android/class2greylist/AnnotatedClassContext.java
new file mode 100644
index 0000000..1dd74dd
--- /dev/null
+++ b/tools/class2greylist/src/com/android/class2greylist/AnnotatedClassContext.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * 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.android.class2greylist;
+
+import java.util.Formatter;
+import java.util.Locale;
+import org.apache.bcel.Const;
+import org.apache.bcel.classfile.FieldOrMethod;
+import org.apache.bcel.classfile.JavaClass;
+
+/**
+ * Encapsulates context for a single annotation on a class.
+ */
+public class AnnotatedClassContext extends AnnotationContext {
+
+ public final String signatureFormatString;
+
+ public AnnotatedClassContext(
+ Status status,
+ JavaClass definingClass,
+ String signatureFormatString) {
+ super(status, definingClass);
+ this.signatureFormatString = signatureFormatString;
+ }
+
+ @Override
+ public String getMemberDescriptor() {
+ return String.format(Locale.US, signatureFormatString, getClassDescriptor());
+ }
+
+ @Override
+ public void reportError(String message, Object... args) {
+ Formatter error = new Formatter();
+ error
+ .format("%s: %s: ", definingClass.getSourceFileName(), definingClass.getClassName())
+ .format(Locale.US, message, args);
+
+ status.error(error.toString());
+ }
+
+}
diff --git a/tools/class2greylist/src/com/android/class2greylist/AnnotatedMemberContext.java b/tools/class2greylist/src/com/android/class2greylist/AnnotatedMemberContext.java
new file mode 100644
index 0000000..4802788
--- /dev/null
+++ b/tools/class2greylist/src/com/android/class2greylist/AnnotatedMemberContext.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * 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.android.class2greylist;
+
+import java.util.Formatter;
+import org.apache.bcel.Const;
+import org.apache.bcel.classfile.FieldOrMethod;
+import org.apache.bcel.classfile.JavaClass;
+
+import java.util.Locale;
+
+/**
+ * Encapsulates context for a single annotation on a class member.
+ */
+public class AnnotatedMemberContext extends AnnotationContext {
+
+ public final FieldOrMethod member;
+ public final String signatureFormatString;
+
+ public AnnotatedMemberContext(
+ Status status,
+ JavaClass definingClass,
+ FieldOrMethod member,
+ String signatureFormatString) {
+ super(status, definingClass);
+ this.member = member;
+ this.signatureFormatString = signatureFormatString;
+ }
+
+ @Override
+ public String getMemberDescriptor() {
+ return String.format(Locale.US, signatureFormatString,
+ getClassDescriptor(), member.getName(), member.getSignature());
+ }
+
+ @Override
+ public void reportError(String message, Object... args) {
+ Formatter error = new Formatter();
+ error
+ .format("%s: %s.%s: ", definingClass.getSourceFileName(),
+ definingClass.getClassName(), member.getName())
+ .format(Locale.US, message, args);
+
+ status.error(error.toString());
+ }
+}
diff --git a/tools/class2greylist/src/com/android/class2greylist/AnnotationContext.java b/tools/class2greylist/src/com/android/class2greylist/AnnotationContext.java
index eb54a33..73b74a9 100644
--- a/tools/class2greylist/src/com/android/class2greylist/AnnotationContext.java
+++ b/tools/class2greylist/src/com/android/class2greylist/AnnotationContext.java
@@ -1,66 +1,51 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * 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.android.class2greylist;
import org.apache.bcel.Const;
-import org.apache.bcel.classfile.FieldOrMethod;
import org.apache.bcel.classfile.JavaClass;
-import java.util.Locale;
-
/**
- * Encapsulates context for a single annotation on a class member.
*/
-public class AnnotationContext {
+public abstract class AnnotationContext {
- public final Status status;
- public final FieldOrMethod member;
- public final JavaClass definingClass;
- public final String signatureFormatString;
+ public final Status status;
+ public final JavaClass definingClass;
- public AnnotationContext(
- Status status,
- FieldOrMethod member,
- JavaClass definingClass,
- String signatureFormatString) {
- this.status = status;
- this.member = member;
- this.definingClass = definingClass;
- this.signatureFormatString = signatureFormatString;
- }
+ public AnnotationContext(Status status, JavaClass definingClass) {
+ this.status = status;
+ this.definingClass = definingClass;
+ }
- /**
- * @return the full descriptor of enclosing class.
- */
- public String getClassDescriptor() {
- // JavaClass.getName() returns the Java-style name (with . not /), so we must fetch
- // the original class name from the constant pool.
- return definingClass.getConstantPool().getConstantString(
- definingClass.getClassNameIndex(), Const.CONSTANT_Class);
- }
+ public String getClassDescriptor() {
+ // JavaClass.getName() returns the Java-style name (with . not /), so we must fetch
+ // the original class name from the constant pool.
+ return definingClass.getConstantPool().getConstantString(
+ definingClass.getClassNameIndex(), Const.CONSTANT_Class);
+ }
- /**
- * @return the full descriptor of this member, in the format expected in
- * the greylist.
- */
- public String getMemberDescriptor() {
- return String.format(Locale.US, signatureFormatString,
- getClassDescriptor(), member.getName(), member.getSignature());
- }
+ /**
+ * @return the full descriptor of this member, in the format expected in
+ * the greylist.
+ */
+ public abstract String getMemberDescriptor();
- /**
- * Report an error in this context. The final error message will include
- * the class and member names, and the source file name.
- */
- public void reportError(String message, Object... args) {
- StringBuilder error = new StringBuilder();
- error.append(definingClass.getSourceFileName())
- .append(": ")
- .append(definingClass.getClassName())
- .append(".")
- .append(member.getName())
- .append(": ")
- .append(String.format(Locale.US, message, args));
-
- status.error(error.toString());
- }
-
+ /**
+ * Report an error in this context. The final error message will include
+ * the class and member names, and the source file name.
+ */
+ public abstract void reportError(String message, Object... args);
}
diff --git a/tools/class2greylist/src/com/android/class2greylist/AnnotationVisitor.java b/tools/class2greylist/src/com/android/class2greylist/AnnotationVisitor.java
index 57ccbdc..3a58cf1 100644
--- a/tools/class2greylist/src/com/android/class2greylist/AnnotationVisitor.java
+++ b/tools/class2greylist/src/com/android/class2greylist/AnnotationVisitor.java
@@ -55,6 +55,10 @@
public void visit() {
mStatus.debug("Visit class %s", mClass.getClassName());
+ AnnotationContext context = new AnnotatedClassContext(mStatus, mClass, "L%s;");
+ AnnotationEntry[] annotationEntries = mClass.getAnnotationEntries();
+ handleAnnotations(context, annotationEntries);
+
mDescendingVisitor.visit();
}
@@ -70,9 +74,15 @@
private void visitMember(FieldOrMethod member, String signatureFormatString) {
mStatus.debug("Visit member %s : %s", member.getName(), member.getSignature());
- AnnotationContext context = new AnnotationContext(mStatus, member,
- (JavaClass) mDescendingVisitor.predecessor(), signatureFormatString);
- for (AnnotationEntry a : member.getAnnotationEntries()) {
+ AnnotationContext context = new AnnotatedMemberContext(mStatus,
+ (JavaClass) mDescendingVisitor.predecessor(), member,
+ signatureFormatString);
+ AnnotationEntry[] annotationEntries = member.getAnnotationEntries();
+ handleAnnotations(context, annotationEntries);
+ }
+
+ private void handleAnnotations(AnnotationContext context, AnnotationEntry[] annotationEntries) {
+ for (AnnotationEntry a : annotationEntries) {
if (mAnnotationHandlers.containsKey(a.getAnnotationType())) {
mStatus.debug("Member has annotation %s for which we have a handler",
a.getAnnotationType());
diff --git a/tools/class2greylist/src/com/android/class2greylist/Class2Greylist.java b/tools/class2greylist/src/com/android/class2greylist/Class2Greylist.java
index 433c2c7..1da848b 100644
--- a/tools/class2greylist/src/com/android/class2greylist/Class2Greylist.java
+++ b/tools/class2greylist/src/com/android/class2greylist/Class2Greylist.java
@@ -47,8 +47,8 @@
private static final Set<String> GREYLIST_ANNOTATIONS =
ImmutableSet.of(
- "Landroid/annotation/UnsupportedAppUsage;",
- "Ldalvik/annotation/compat/UnsupportedAppUsage;");
+ "android.annotation.UnsupportedAppUsage",
+ "dalvik.annotation.compat.UnsupportedAppUsage");
private static final Set<String> WHITELIST_ANNOTATIONS = ImmutableSet.of();
public static final String FLAG_WHITELIST = "whitelist";
@@ -185,7 +185,12 @@
UnsupportedAppUsageAnnotationHandler greylistAnnotationHandler =
new UnsupportedAppUsageAnnotationHandler(
mStatus, mOutput, mPublicApis, TARGET_SDK_TO_LIST_MAP);
- GREYLIST_ANNOTATIONS.forEach(a -> builder.put(a, greylistAnnotationHandler));
+ GREYLIST_ANNOTATIONS
+ .forEach(a -> addRepeatedAnnotationHandlers(
+ builder,
+ classNameToSignature(a),
+ classNameToSignature(a + "$Container"),
+ greylistAnnotationHandler));
CovariantReturnTypeHandler covariantReturnTypeHandler = new CovariantReturnTypeHandler(
mOutput, mPublicApis, FLAG_WHITELIST);
@@ -195,6 +200,10 @@
.build();
}
+ private String classNameToSignature(String a) {
+ return "L" + a.replace('.', '/') + ";";
+ }
+
/**
* Add a handler for an annotation as well as an handler for the container annotation that is
* used when the annotation is repeated.
diff --git a/tools/class2greylist/src/com/android/class2greylist/CovariantReturnTypeHandler.java b/tools/class2greylist/src/com/android/class2greylist/CovariantReturnTypeHandler.java
index 64d8997..eb2e42d 100644
--- a/tools/class2greylist/src/com/android/class2greylist/CovariantReturnTypeHandler.java
+++ b/tools/class2greylist/src/com/android/class2greylist/CovariantReturnTypeHandler.java
@@ -4,15 +4,10 @@
import com.google.common.collect.ImmutableSet;
import org.apache.bcel.classfile.AnnotationEntry;
-import org.apache.bcel.classfile.Constant;
-import org.apache.bcel.classfile.ConstantPool;
-import org.apache.bcel.classfile.ElementValue;
import org.apache.bcel.classfile.ElementValuePair;
import org.apache.bcel.classfile.Method;
-import java.util.List;
import java.util.Locale;
-import java.util.Map;
import java.util.Set;
/**
@@ -48,6 +43,13 @@
@Override
public void handleAnnotation(AnnotationEntry annotation, AnnotationContext context) {
+ if (context instanceof AnnotatedClassContext) {
+ return;
+ }
+ handleAnnotation(annotation, (AnnotatedMemberContext) context);
+ }
+
+ private void handleAnnotation(AnnotationEntry annotation, AnnotatedMemberContext context) {
// Verify that the annotation has been applied to what we expect, and
// has the right form. Note, this should not strictly be necessary, as
// the annotation has a target of just 'method' and the property
diff --git a/tools/class2greylist/src/com/android/class2greylist/MemberDumpingVisitor.java b/tools/class2greylist/src/com/android/class2greylist/MemberDumpingVisitor.java
index 6677a3f..89c8bd7 100644
--- a/tools/class2greylist/src/com/android/class2greylist/MemberDumpingVisitor.java
+++ b/tools/class2greylist/src/com/android/class2greylist/MemberDumpingVisitor.java
@@ -40,8 +40,9 @@
}
private void visitMember(FieldOrMethod member, String signatureFormatString) {
- AnnotationContext context = new AnnotationContext(mStatus, member,
- (JavaClass) mDescendingVisitor.predecessor(), signatureFormatString);
+ AnnotationContext context = new AnnotatedMemberContext(mStatus,
+ (JavaClass) mDescendingVisitor.predecessor(), member,
+ signatureFormatString);
System.out.println(context.getMemberDescriptor());
}
}
diff --git a/tools/class2greylist/src/com/android/class2greylist/UnsupportedAppUsageAnnotationHandler.java b/tools/class2greylist/src/com/android/class2greylist/UnsupportedAppUsageAnnotationHandler.java
index d1f3e77..b45e1b3 100644
--- a/tools/class2greylist/src/com/android/class2greylist/UnsupportedAppUsageAnnotationHandler.java
+++ b/tools/class2greylist/src/com/android/class2greylist/UnsupportedAppUsageAnnotationHandler.java
@@ -1,7 +1,6 @@
package com.android.class2greylist;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableSet;
import org.apache.bcel.Const;
@@ -20,11 +19,11 @@
* Processes {@code UnsupportedAppUsage} annotations to generate greylist
* entries.
*
- * Any annotations with a {@link #EXPECTED_SIGNATURE} property will have their
+ * Any annotations with a {@link #EXPECTED_SIGNATURE_PROPERTY} property will have their
* generated signature verified against this, and an error will be reported if
* it does not match. Exclusions are made for bridge methods.
*
- * Any {@link #MAX_TARGET_SDK} properties will be validated against the given
+ * Any {@link #MAX_TARGET_SDK_PROPERTY} properties will be validated against the given
* set of valid values, then passed through to the greylist consumer.
*/
public class UnsupportedAppUsageAnnotationHandler extends AnnotationHandler {
@@ -32,6 +31,7 @@
// properties of greylist annotations:
private static final String EXPECTED_SIGNATURE_PROPERTY = "expectedSignature";
private static final String MAX_TARGET_SDK_PROPERTY = "maxTargetSdk";
+ private static final String IMPLICIT_MEMBER_PROPERTY = "implicitMember";
private final Status mStatus;
private final Predicate<ClassMember> mClassMemberFilter;
@@ -80,16 +80,20 @@
@Override
public void handleAnnotation(AnnotationEntry annotation, AnnotationContext context) {
- FieldOrMethod member = context.member;
-
- boolean isBridgeMethod = (member instanceof Method) &&
+ boolean isBridgeMethod = false;
+ if (context instanceof AnnotatedMemberContext) {
+ AnnotatedMemberContext memberContext = (AnnotatedMemberContext) context;
+ FieldOrMethod member = memberContext.member;
+ isBridgeMethod = (member instanceof Method) &&
(member.getAccessFlags() & Const.ACC_BRIDGE) != 0;
- if (isBridgeMethod) {
- mStatus.debug("Member is a bridge method");
+ if (isBridgeMethod) {
+ mStatus.debug("Member is a bridge method");
+ }
}
String signature = context.getMemberDescriptor();
Integer maxTargetSdk = null;
+ String implicitMemberSignature = null;
for (ElementValuePair property : annotation.getElementValuePairs()) {
switch (property.getNameString()) {
@@ -113,9 +117,30 @@
maxTargetSdk = ((SimpleElementValue) property.getValue()).getValueInt();
break;
+ case IMPLICIT_MEMBER_PROPERTY:
+ implicitMemberSignature = property.getValue().stringifyValue();
+ if (context instanceof AnnotatedClassContext) {
+ signature = String.format("L%s;->%s",
+ context.getClassDescriptor(), implicitMemberSignature);
+ } else {
+ context.reportError(
+ "Expected annotation with an %s property to be on a class but is on %s",
+ IMPLICIT_MEMBER_PROPERTY,
+ signature);
+ return;
+ }
+ break;
}
}
+ if (context instanceof AnnotatedClassContext && implicitMemberSignature == null) {
+ context.reportError(
+ "Missing property %s on annotation on class %s",
+ IMPLICIT_MEMBER_PROPERTY,
+ signature);
+ return;
+ }
+
// Verify that maxTargetSdk is valid.
if (!mSdkVersionToFlagMap.containsKey(maxTargetSdk)) {
context.reportError("Invalid value for %s: got %d, expected one of [%s]",
diff --git a/tools/class2greylist/test/src/com/android/class2greylist/UnsupportedAppUsageAnnotationHandlerTest.java b/tools/class2greylist/test/src/com/android/class2greylist/UnsupportedAppUsageAnnotationHandlerTest.java
index dc767fe..a6d6a16 100644
--- a/tools/class2greylist/test/src/com/android/class2greylist/UnsupportedAppUsageAnnotationHandlerTest.java
+++ b/tools/class2greylist/test/src/com/android/class2greylist/UnsupportedAppUsageAnnotationHandlerTest.java
@@ -59,10 +59,17 @@
"package annotation;",
"import static java.lang.annotation.RetentionPolicy.CLASS;",
"import java.lang.annotation.Retention;",
+ "import java.lang.annotation.Repeatable;",
"@Retention(CLASS)",
+ "@Repeatable(Anno.Container.class)",
"public @interface Anno {",
" String expectedSignature() default \"\";",
" int maxTargetSdk() default Integer.MAX_VALUE;",
+ " String implicitMember() default \"\";",
+ " @Retention(CLASS)",
+ " public @interface Container {",
+ " Anno[] value();",
+ " }",
"}"));
}
@@ -137,6 +144,70 @@
}
@Test
+ public void testGreylistImplicit() throws IOException {
+ mJavac.addSource("a.b.EnumClass", Joiner.on('\n').join(
+ "package a.b;",
+ "import annotation.Anno;",
+ "@Anno(implicitMember=\"values()[La/b/EnumClass;\")",
+ "public enum EnumClass {",
+ " VALUE",
+ "}"));
+ mJavac.compile();
+
+ new AnnotationVisitor(mJavac.getCompiledClass("a.b.EnumClass"), mStatus,
+ ImmutableMap.of(ANNOTATION, createGreylistHandler(x -> true, NULL_SDK_MAP))
+ ).visit();
+
+ assertNoErrors();
+ ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class);
+ verify(mConsumer, times(1)).consume(greylist.capture(), any(), any());
+ assertThat(greylist.getValue()).isEqualTo("La/b/EnumClass;->values()[La/b/EnumClass;");
+ }
+
+ @Test
+ public void testGreylistImplicit_Invalid_MissingOnClass() throws IOException {
+ mJavac.addSource("a.b.EnumClass", Joiner.on('\n').join(
+ "package a.b;",
+ "import annotation.Anno;",
+ "@Anno",
+ "public enum EnumClass {",
+ " VALUE",
+ "}"));
+ mJavac.compile();
+
+ new AnnotationVisitor(mJavac.getCompiledClass("a.b.EnumClass"), mStatus,
+ ImmutableMap.of(ANNOTATION, createGreylistHandler(x -> true, NULL_SDK_MAP))
+ ).visit();
+
+ ArgumentCaptor<String> format = ArgumentCaptor.forClass(String.class);
+ verify(mStatus, times(1)).error(format.capture(), any());
+ // Ensure that the correct error is reported.
+ assertThat(format.getValue())
+ .contains("Missing property implicitMember on annotation on class");
+ }
+
+ @Test
+ public void testGreylistImplicit_Invalid_PresentOnMember() throws IOException {
+ mJavac.addSource("a.b.EnumClass", Joiner.on('\n').join(
+ "package a.b;",
+ "import annotation.Anno;",
+ "public enum EnumClass {",
+ " @Anno(implicitMember=\"values()[La/b/EnumClass;\")",
+ " VALUE",
+ "}"));
+ mJavac.compile();
+
+ new AnnotationVisitor(mJavac.getCompiledClass("a.b.EnumClass"), mStatus,
+ ImmutableMap.of(ANNOTATION, createGreylistHandler(x -> true, NULL_SDK_MAP))
+ ).visit();
+
+ ArgumentCaptor<String> format = ArgumentCaptor.forClass(String.class);
+ verify(mStatus, times(1)).error(format.capture(), any());
+ assertThat(format.getValue())
+ .contains("Expected annotation with an implicitMember property to be on a class");
+ }
+
+ @Test
public void testGreylistMethodExpectedSignature() throws IOException {
mJavac.addSource("a.b.Class", Joiner.on('\n').join(
"package a.b;",