Access checks may fail verification due to class loaders

At compile time we have incomplete class loader information. We need to
be tolerant and make this a runtime issue.

Change-Id: I9e63b804ae68434753f0aea7a855014030a1542d
diff --git a/src/compiler.h b/src/compiler.h
index a8c596a..b43d5f6 100644
--- a/src/compiler.h
+++ b/src/compiler.h
@@ -94,16 +94,17 @@
   bool CanAccessTypeWithoutChecks(uint32_t referrer_idx, const DexCache* dex_cache,
                                   const DexFile& dex_file, uint32_t type_idx) const {
     Class* resolved_class = dex_cache->GetResolvedType(type_idx);
-    // We should never ask whether a type needs access checks to raise a verification error,
-    // all other cases where this following test could fail should have been rewritten by the
-    // verifier to verification errors. Also need to handle a lack of knowledge at compile time.
-#ifndef NDEBUG
+    if (resolved_class == NULL) {
+      return false;  // Unknown class needs access checks.
+    }
     const DexFile::MethodId& method_id = dex_file.GetMethodId(referrer_idx);
     Class* referrer_class = dex_cache->GetResolvedType(method_id.class_idx_);
-    DCHECK(resolved_class == NULL || referrer_class == NULL ||
-           referrer_class->CanAccess(resolved_class));
-#endif
-    return resolved_class != NULL;
+    if (referrer_class == NULL) {
+      return false;  // Incomplete referrer knowledge needs access check.
+    }
+    // Perform access check, will return true if access is ok or false if we're going to have to
+    // check this at runtime (for example for class loaders).
+    return referrer_class->CanAccess(resolved_class);
   }
 
  private:
diff --git a/src/dex_verifier.cc b/src/dex_verifier.cc
index adf0288..2fb6492 100644
--- a/src/dex_verifier.cc
+++ b/src/dex_verifier.cc
@@ -1032,13 +1032,18 @@
 
 std::ostream& DexVerifier::Fail(VerifyError error) {
   CHECK_EQ(failure_, VERIFY_ERROR_NONE);
-  // If we're optimistically running verification at compile time, turn NO_xxx errors into generic
-  // errors so that we reverify at runtime
+  // If we're optimistically running verification at compile time, turn NO_xxx and ACCESS_xxx
+  // errors into generic errors so that we re-verify at runtime. We may fail to find or to agree
+  // on access because of not yet available class loaders, or class loaders that will differ at
+  // runtime.
   if (Runtime::Current()->IsCompiler()) {
     switch(error) {
       case VERIFY_ERROR_NO_CLASS:
       case VERIFY_ERROR_NO_FIELD:
       case VERIFY_ERROR_NO_METHOD:
+      case VERIFY_ERROR_ACCESS_CLASS:
+      case VERIFY_ERROR_ACCESS_FIELD:
+      case VERIFY_ERROR_ACCESS_METHOD:
         error = VERIFY_ERROR_GENERIC;
         break;
       default:
@@ -2156,30 +2161,38 @@
     }
     case Instruction::NEW_ARRAY: {
       const RegType& res_type = ResolveClassAndCheckAccess(dec_insn.vC_);
-      // TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved
-      if (!res_type.IsArrayTypes()) {
-        Fail(VERIFY_ERROR_GENERIC) << "new-array on non-array class " << res_type;
+      if (res_type.IsUnknown()) {
+        CHECK_NE(failure_, VERIFY_ERROR_NONE);
       } else {
-        /* make sure "size" register is valid type */
-        work_line_->VerifyRegisterType(dec_insn.vB_, reg_types_.Integer());
-        /* set register type to array class */
-        work_line_->SetRegisterType(dec_insn.vA_, res_type);
+        // TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved
+        if (!res_type.IsArrayTypes()) {
+          Fail(VERIFY_ERROR_GENERIC) << "new-array on non-array class " << res_type;
+        } else {
+          /* make sure "size" register is valid type */
+          work_line_->VerifyRegisterType(dec_insn.vB_, reg_types_.Integer());
+          /* set register type to array class */
+          work_line_->SetRegisterType(dec_insn.vA_, res_type);
+        }
       }
       break;
     }
     case Instruction::FILLED_NEW_ARRAY:
     case Instruction::FILLED_NEW_ARRAY_RANGE: {
       const RegType& res_type = ResolveClassAndCheckAccess(dec_insn.vB_);
-      // TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved
-      if (!res_type.IsArrayTypes()) {
-        Fail(VERIFY_ERROR_GENERIC) << "filled-new-array on non-array class";
+      if (res_type.IsUnknown()) {
+        CHECK_NE(failure_, VERIFY_ERROR_NONE);
       } else {
-        bool is_range = (dec_insn.opcode_ == Instruction::FILLED_NEW_ARRAY_RANGE);
-        /* check the arguments to the instruction */
-        VerifyFilledNewArrayRegs(dec_insn, res_type, is_range);
-        /* filled-array result goes into "result" register */
-        work_line_->SetResultRegisterType(res_type);
-        just_set_result = true;
+        // TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved
+        if (!res_type.IsArrayTypes()) {
+          Fail(VERIFY_ERROR_GENERIC) << "filled-new-array on non-array class";
+        } else {
+          bool is_range = (dec_insn.opcode_ == Instruction::FILLED_NEW_ARRAY_RANGE);
+          /* check the arguments to the instruction */
+          VerifyFilledNewArrayRegs(dec_insn, res_type, is_range);
+          /* filled-array result goes into "result" register */
+          work_line_->SetResultRegisterType(res_type);
+          just_set_result = true;
+        }
       }
       break;
     }
diff --git a/src/dex_verifier.h b/src/dex_verifier.h
index cbf26d6..d8792cf 100644
--- a/src/dex_verifier.h
+++ b/src/dex_verifier.h
@@ -216,7 +216,7 @@
   bool IsArrayTypes() const {
     if (IsUnresolvedTypes()) {
       return GetDescriptor()->CharAt(0) == '[';
-    } else if (!IsConstant()) {
+    } else if (IsReference()) {
       return GetClass()->IsArrayClass();
     } else {
       return false;
@@ -228,7 +228,7 @@
       // Primitive arrays will always resolve
       DCHECK(GetDescriptor()->CharAt(1) == 'L' || GetDescriptor()->CharAt(1) == '[');
       return GetDescriptor()->CharAt(0) == '[';
-    } else if (!IsConstant()) {
+    } else if (IsReference()) {
       Class* type = GetClass();
       return type->IsArrayClass() && !type->GetComponentType()->IsPrimitive();
     } else {