Fix bug in protected field access.

Change-Id: I38b094e54025d26950c0d8c8bb79d2de81d28428
diff --git a/src/compiler.cc b/src/compiler.cc
index 3980bac..7369535 100644
--- a/src/compiler.cc
+++ b/src/compiler.cc
@@ -628,17 +628,31 @@
   Field* resolved_field = ComputeReferrerField(mUnit, field_idx);
   if (resolved_field != NULL) {
     Class* referrer_class = ComputeReferrerClass(mUnit);
-    // Try to resolve referring class then access check, failure to pass the
-    Class* fields_class = resolved_field->GetDeclaringClass();
-    bool is_write_to_final_from_wrong_class = is_put && resolved_field->IsFinal() &&
-                                              fields_class != referrer_class;
-    if (referrer_class != NULL && referrer_class->CanAccess(fields_class) &&
-        referrer_class->CanAccessMember(fields_class, resolved_field->GetAccessFlags()) &&
-        !is_write_to_final_from_wrong_class) {
-      field_offset = resolved_field->GetOffset().Int32Value();
-      is_volatile = resolved_field->IsVolatile();
-      stats_->ResolvedInstanceField();
-      return true;  // Fast path.
+    if (referrer_class != NULL) {
+      Class* fields_class = resolved_field->GetDeclaringClass();
+      bool access_ok = referrer_class->CanAccess(fields_class) &&
+                       referrer_class->CanAccessMember(fields_class,
+                                                       resolved_field->GetAccessFlags());
+      if (!access_ok) {
+        // The referring class can't access the resolved field, this may occur as a result of a
+        // protected field being made public by a sub-class. Resort to the dex file to determine
+        // the correct class for the access check.
+        const DexFile& dex_file = mUnit->class_linker_->FindDexFile(referrer_class->GetDexCache());
+        Class* dex_fields_class = mUnit->class_linker_->ResolveType(dex_file,
+                                                         dex_file.GetFieldId(field_idx).class_idx_,
+                                                         referrer_class);
+        access_ok = referrer_class->CanAccess(dex_fields_class) &&
+                    referrer_class->CanAccessMember(dex_fields_class,
+                                                    resolved_field->GetAccessFlags());
+      }
+      bool is_write_to_final_from_wrong_class = is_put && resolved_field->IsFinal() &&
+          fields_class != referrer_class;
+      if (access_ok && !is_write_to_final_from_wrong_class) {
+        field_offset = resolved_field->GetOffset().Int32Value();
+        is_volatile = resolved_field->IsVolatile();
+        stats_->ResolvedInstanceField();
+        return true;  // Fast path.
+      }
     }
   }
   // Clean up any exception left by field/type resolution
@@ -672,10 +686,25 @@
         stats_->ResolvedLocalStaticField();
         return true;  // fast path
       } else {
+        bool access_ok = referrer_class->CanAccess(fields_class) &&
+                         referrer_class->CanAccessMember(fields_class,
+                                                         resolved_field->GetAccessFlags());
+        if (!access_ok) {
+          // The referring class can't access the resolved field, this may occur as a result of a
+          // protected field being made public by a sub-class. Resort to the dex file to determine
+          // the correct class for the access check. Don't change the field's class as that is
+          // used to identify the SSB.
+          const DexFile& dex_file = mUnit->class_linker_->FindDexFile(referrer_class->GetDexCache());
+          Class* dex_fields_class =
+              mUnit->class_linker_->ResolveType(dex_file,
+                                                dex_file.GetFieldId(field_idx).class_idx_,
+                                                referrer_class);
+          access_ok = referrer_class->CanAccess(dex_fields_class) &&
+                      referrer_class->CanAccessMember(dex_fields_class,
+                                                      resolved_field->GetAccessFlags());
+        }
         bool is_write_to_final_from_wrong_class = is_put && resolved_field->IsFinal();
-        if (referrer_class->CanAccess(fields_class) &&
-            referrer_class->CanAccessMember(fields_class, resolved_field->GetAccessFlags()) &&
-            !is_write_to_final_from_wrong_class) {
+        if (access_ok && !is_write_to_final_from_wrong_class) {
           // We have the resolved field, we must make it into a ssbIndex for the referrer
           // in its static storage base (which may fail if it doesn't have a slot for it)
           // TODO: for images we can elide the static storage base null check
@@ -689,7 +718,8 @@
             stats_->ResolvedStaticField();
             return true;
           }
-          // Search dex file for localized ssb index
+          // Search dex file for localized ssb index, may fail if field's class is a parent
+          // of the class mentioned in the dex file and there is no dex cache entry.
           std::string descriptor(FieldHelper(resolved_field).GetDeclaringClassDescriptor());
           const DexFile::StringId* string_id =
           mUnit->dex_file_->FindStringId(descriptor);