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;",