Remove field/method/class resolution metadata in vdex.
They can now be handled at runtime with access checks.
Bug: 112676029
Test: test.py
Change-Id: I08c838334fb0dc94e58fa24463f49633ef7989fc
diff --git a/dex2oat/driver/compiler_driver.cc b/dex2oat/driver/compiler_driver.cc
index c08227a..f03113f 100644
--- a/dex2oat/driver/compiler_driver.cc
+++ b/dex2oat/driver/compiler_driver.cc
@@ -1933,12 +1933,13 @@
ClassReference ref(dex_file, accessor.GetClassDefIndex());
const ClassStatus existing = ClassStatus::kNotReady;
ClassStateTable::InsertResult result =
- compiled_classes_.Insert(ref, existing, ClassStatus::kVerified);
+ compiled_classes_.Insert(ref, existing, ClassStatus::kVerifiedNeedsAccessChecks);
CHECK_EQ(result, ClassStateTable::kInsertResultSuccess) << ref.dex_file->GetLocation();
} else {
// Update the class status, so later compilation stages know they don't need to verify
// the class.
- LoadAndUpdateStatus(accessor, ClassStatus::kVerified, class_loader, soa.Self());
+ LoadAndUpdateStatus(
+ accessor, ClassStatus::kVerifiedNeedsAccessChecks, class_loader, soa.Self());
// Create `VerifiedMethod`s for each methods, the compiler expects one for
// quickening or compiling.
// Note that this means:
diff --git a/dex2oat/verifier_deps_test.cc b/dex2oat/verifier_deps_test.cc
index 79cd69a..0e0ce91 100644
--- a/dex2oat/verifier_deps_test.cc
+++ b/dex2oat/verifier_deps_test.cc
@@ -239,7 +239,7 @@
} else if (&cls->GetDexFile() != dex_file) {
// Ignore classes from different dex files.
} else if (verified_classes[i]) {
- ASSERT_EQ(cls->GetStatus(), ClassStatus::kVerified);
+ ASSERT_EQ(cls->GetStatus(), ClassStatus::kVerifiedNeedsAccessChecks);
} else {
ASSERT_LT(cls->GetStatus(), ClassStatus::kVerified);
}
@@ -291,141 +291,6 @@
return false;
}
- // Iterates over all class resolution records, finds an entry which matches
- // the given class descriptor and tests its properties.
- bool HasClass(const std::string& expected_klass,
- bool expected_resolved,
- const std::string& expected_access_flags = "") {
- for (auto& dex_dep : verifier_deps_->dex_deps_) {
- for (auto& entry : dex_dep.second->classes_) {
- if (expected_resolved != entry.IsResolved()) {
- continue;
- }
-
- std::string actual_klass = dex_dep.first->StringByTypeIdx(entry.GetDexTypeIndex());
- if (expected_klass != actual_klass) {
- continue;
- }
-
- if (expected_resolved) {
- // Test access flags. Note that PrettyJavaAccessFlags always appends
- // a space after the modifiers. Add it to the expected access flags.
- std::string actual_access_flags = PrettyJavaAccessFlags(entry.GetAccessFlags());
- if (expected_access_flags + " " != actual_access_flags) {
- continue;
- }
- }
-
- return true;
- }
- }
- return false;
- }
-
- // Iterates over all field resolution records, finds an entry which matches
- // the given field class+name+type and tests its properties.
- bool HasField(const std::string& expected_klass,
- const std::string& expected_name,
- const std::string& expected_type,
- bool expected_resolved,
- const std::string& expected_access_flags = "",
- const std::string& expected_decl_klass = "") {
- for (auto& dex_dep : verifier_deps_->dex_deps_) {
- for (auto& entry : dex_dep.second->fields_) {
- if (expected_resolved != entry.IsResolved()) {
- continue;
- }
-
- const dex::FieldId& field_id = dex_dep.first->GetFieldId(entry.GetDexFieldIndex());
-
- std::string actual_klass = dex_dep.first->StringByTypeIdx(field_id.class_idx_);
- if (expected_klass != actual_klass) {
- continue;
- }
-
- std::string actual_name = dex_dep.first->StringDataByIdx(field_id.name_idx_);
- if (expected_name != actual_name) {
- continue;
- }
-
- std::string actual_type = dex_dep.first->StringByTypeIdx(field_id.type_idx_);
- if (expected_type != actual_type) {
- continue;
- }
-
- if (expected_resolved) {
- // Test access flags. Note that PrettyJavaAccessFlags always appends
- // a space after the modifiers. Add it to the expected access flags.
- std::string actual_access_flags = PrettyJavaAccessFlags(entry.GetAccessFlags());
- if (expected_access_flags + " " != actual_access_flags) {
- continue;
- }
-
- std::string actual_decl_klass = verifier_deps_->GetStringFromId(
- *dex_dep.first, entry.GetDeclaringClassIndex());
- if (expected_decl_klass != actual_decl_klass) {
- continue;
- }
- }
-
- return true;
- }
- }
- return false;
- }
-
- // Iterates over all method resolution records, finds an entry which matches
- // the given field kind+class+name+signature and tests its properties.
- bool HasMethod(const std::string& expected_klass,
- const std::string& expected_name,
- const std::string& expected_signature,
- bool expect_resolved,
- const std::string& expected_access_flags = "",
- const std::string& expected_decl_klass = "") {
- for (auto& dex_dep : verifier_deps_->dex_deps_) {
- for (const VerifierDeps::MethodResolution& entry : dex_dep.second->methods_) {
- if (expect_resolved != entry.IsResolved()) {
- continue;
- }
-
- const dex::MethodId& method_id = dex_dep.first->GetMethodId(entry.GetDexMethodIndex());
-
- std::string actual_klass = dex_dep.first->StringByTypeIdx(method_id.class_idx_);
- if (expected_klass != actual_klass) {
- continue;
- }
-
- std::string actual_name = dex_dep.first->StringDataByIdx(method_id.name_idx_);
- if (expected_name != actual_name) {
- continue;
- }
-
- std::string actual_signature = dex_dep.first->GetMethodSignature(method_id).ToString();
- if (expected_signature != actual_signature) {
- continue;
- }
-
- if (expect_resolved) {
- // Test access flags. Note that PrettyJavaAccessFlags always appends
- // a space after the modifiers. Add it to the expected access flags.
- std::string actual_access_flags = PrettyJavaAccessFlags(entry.GetAccessFlags());
- if (expected_access_flags + " " != actual_access_flags) {
- continue;
- }
-
- std::string actual_decl_klass = verifier_deps_->GetStringFromId(
- *dex_dep.first, entry.GetDeclaringClassIndex());
- if (expected_decl_klass != actual_decl_klass) {
- continue;
- }
- }
-
- return true;
- }
- }
- return false;
- }
-
size_t NumberOfCompiledDexFiles() {
return verifier_deps_->dex_deps_.size();
}
@@ -437,9 +302,6 @@
bool HasEachKindOfRecord() {
bool has_strings = false;
bool has_assignability = false;
- bool has_classes = false;
- bool has_fields = false;
- bool has_methods = false;
bool has_verified_classes = false;
bool has_unverified_classes = false;
bool has_redefined_classes = false;
@@ -449,9 +311,6 @@
has_strings |= !entry.second->strings_.empty();
has_assignability |= !entry.second->assignable_types_.empty();
has_assignability |= !entry.second->unassignable_types_.empty();
- has_classes |= !entry.second->classes_.empty();
- has_fields |= !entry.second->fields_.empty();
- has_methods |= !entry.second->methods_.empty();
has_verified_classes |= HasBoolValue(entry.second->verified_classes_, true);
has_unverified_classes |= HasBoolValue(entry.second->verified_classes_, false);
has_redefined_classes |= HasBoolValue(entry.second->redefined_classes_, true);
@@ -460,9 +319,6 @@
return has_strings &&
has_assignability &&
- has_classes &&
- has_fields &&
- has_methods &&
has_verified_classes &&
has_unverified_classes &&
has_redefined_classes &&
@@ -605,21 +461,6 @@
ASSERT_TRUE(HasAssignable("Ljava/lang/Exception;", "Ljava/util/SimpleTimeZone;", false));
}
-TEST_F(VerifierDepsTest, ArgumentType_ResolvedClass) {
- ASSERT_TRUE(VerifyMethod("ArgumentType_ResolvedClass"));
- ASSERT_TRUE(HasClass("Ljava/lang/Thread;", true, "public"));
-}
-
-TEST_F(VerifierDepsTest, ArgumentType_UnresolvedClass) {
- ASSERT_TRUE(VerifyMethod("ArgumentType_UnresolvedClass"));
- ASSERT_TRUE(HasClass("LUnresolvedClass;", false));
-}
-
-TEST_F(VerifierDepsTest, ArgumentType_UnresolvedSuper) {
- ASSERT_TRUE(VerifyMethod("ArgumentType_UnresolvedSuper"));
- ASSERT_TRUE(HasClass("LMySetWithUnresolvedSuper;", false));
-}
-
TEST_F(VerifierDepsTest, ReturnType_Reference) {
ASSERT_TRUE(VerifyMethod("ReturnType_Reference"));
ASSERT_TRUE(HasAssignable("Ljava/lang/Throwable;", "Ljava/lang/IllegalStateException;", true));
@@ -632,14 +473,6 @@
TEST_F(VerifierDepsTest, InvokeArgumentType) {
ASSERT_TRUE(VerifyMethod("InvokeArgumentType"));
- ASSERT_TRUE(HasClass("Ljava/text/SimpleDateFormat;", true, "public"));
- ASSERT_TRUE(HasClass("Ljava/util/SimpleTimeZone;", true, "public"));
- ASSERT_TRUE(HasMethod("Ljava/text/SimpleDateFormat;",
- "setTimeZone",
- "(Ljava/util/TimeZone;)V",
- /* expect_resolved= */ true,
- "public",
- "Ljava/text/DateFormat;"));
ASSERT_TRUE(HasAssignable("Ljava/util/TimeZone;", "Ljava/util/SimpleTimeZone;", true));
}
@@ -665,51 +498,6 @@
"Ljava/lang/Exception;", "Ljava/util/concurrent/TimeoutException;", true));
}
-TEST_F(VerifierDepsTest, ConstClass_Resolved) {
- ASSERT_TRUE(VerifyMethod("ConstClass_Resolved"));
- ASSERT_TRUE(HasClass("Ljava/lang/IllegalStateException;", true, "public"));
-}
-
-TEST_F(VerifierDepsTest, ConstClass_Unresolved) {
- ASSERT_FALSE(VerifyMethod("ConstClass_Unresolved"));
- ASSERT_TRUE(HasClass("LUnresolvedClass;", false));
-}
-
-TEST_F(VerifierDepsTest, CheckCast_Resolved) {
- ASSERT_TRUE(VerifyMethod("CheckCast_Resolved"));
- ASSERT_TRUE(HasClass("Ljava/lang/IllegalStateException;", true, "public"));
-}
-
-TEST_F(VerifierDepsTest, CheckCast_Unresolved) {
- ASSERT_FALSE(VerifyMethod("CheckCast_Unresolved"));
- ASSERT_TRUE(HasClass("LUnresolvedClass;", false));
-}
-
-TEST_F(VerifierDepsTest, InstanceOf_Resolved) {
- ASSERT_TRUE(VerifyMethod("InstanceOf_Resolved"));
- ASSERT_TRUE(HasClass("Ljava/lang/IllegalStateException;", true, "public"));
-}
-
-TEST_F(VerifierDepsTest, InstanceOf_Unresolved) {
- ASSERT_FALSE(VerifyMethod("InstanceOf_Unresolved"));
- ASSERT_TRUE(HasClass("LUnresolvedClass;", false));
-}
-
-TEST_F(VerifierDepsTest, NewInstance_Resolved) {
- ASSERT_TRUE(VerifyMethod("NewInstance_Resolved"));
- ASSERT_TRUE(HasClass("Ljava/lang/IllegalStateException;", true, "public"));
-}
-
-TEST_F(VerifierDepsTest, NewInstance_Unresolved) {
- ASSERT_FALSE(VerifyMethod("NewInstance_Unresolved"));
- ASSERT_TRUE(HasClass("LUnresolvedClass;", false));
-}
-
-TEST_F(VerifierDepsTest, NewArray_Unresolved) {
- ASSERT_FALSE(VerifyMethod("NewArray_Unresolved"));
- ASSERT_TRUE(HasClass("[LUnresolvedClass;", false));
-}
-
TEST_F(VerifierDepsTest, Throw) {
ASSERT_TRUE(VerifyMethod("Throw"));
ASSERT_TRUE(HasAssignable("Ljava/lang/Throwable;", "Ljava/lang/IllegalStateException;", true));
@@ -717,9 +505,6 @@
TEST_F(VerifierDepsTest, MoveException_Resolved) {
ASSERT_TRUE(VerifyMethod("MoveException_Resolved"));
- ASSERT_TRUE(HasClass("Ljava/io/InterruptedIOException;", true, "public"));
- ASSERT_TRUE(HasClass("Ljava/net/SocketTimeoutException;", true, "public"));
- ASSERT_TRUE(HasClass("Ljava/util/zip/ZipException;", true, "public"));
// Testing that all exception types are assignable to Throwable.
ASSERT_TRUE(HasAssignable("Ljava/lang/Throwable;", "Ljava/io/InterruptedIOException;", true));
@@ -736,404 +521,44 @@
"Ljava/io/InterruptedIOException;", "Ljava/net/SocketTimeoutException;", true));
}
-TEST_F(VerifierDepsTest, MoveException_Unresolved) {
- ASSERT_FALSE(VerifyMethod("MoveException_Unresolved"));
- ASSERT_TRUE(HasClass("LUnresolvedException;", false));
-}
-
-TEST_F(VerifierDepsTest, StaticField_Resolved_DeclaredInReferenced) {
- ASSERT_TRUE(VerifyMethod("StaticField_Resolved_DeclaredInReferenced"));
- ASSERT_TRUE(HasClass("Ljava/lang/System;", true, "public"));
- ASSERT_TRUE(HasField("Ljava/lang/System;",
- "out",
- "Ljava/io/PrintStream;",
- true,
- "public static",
- "Ljava/lang/System;"));
-}
-
-TEST_F(VerifierDepsTest, StaticField_Resolved_DeclaredInSuperclass1) {
- ASSERT_TRUE(VerifyMethod("StaticField_Resolved_DeclaredInSuperclass1"));
- ASSERT_TRUE(HasClass("Ljava/util/SimpleTimeZone;", true, "public"));
- ASSERT_TRUE(HasField(
- "Ljava/util/SimpleTimeZone;", "LONG", "I", true, "public static", "Ljava/util/TimeZone;"));
-}
-
-TEST_F(VerifierDepsTest, StaticField_Resolved_DeclaredInSuperclass2) {
- ASSERT_TRUE(VerifyMethod("StaticField_Resolved_DeclaredInSuperclass2"));
- ASSERT_TRUE(HasField(
- "LMySimpleTimeZone;", "SHORT", "I", true, "public static", "Ljava/util/TimeZone;"));
-}
-
-TEST_F(VerifierDepsTest, StaticField_Resolved_DeclaredInInterface1) {
- ASSERT_TRUE(VerifyMethod("StaticField_Resolved_DeclaredInInterface1"));
- ASSERT_TRUE(HasClass("Ljavax/xml/transform/dom/DOMResult;", true, "public"));
- ASSERT_TRUE(HasField("Ljavax/xml/transform/dom/DOMResult;",
- "PI_ENABLE_OUTPUT_ESCAPING",
- "Ljava/lang/String;",
- true,
- "public static",
- "Ljavax/xml/transform/Result;"));
-}
-
-TEST_F(VerifierDepsTest, StaticField_Resolved_DeclaredInInterface2) {
- ASSERT_TRUE(VerifyMethod("StaticField_Resolved_DeclaredInInterface2"));
- ASSERT_TRUE(HasField("LMyDOMResult;",
- "PI_ENABLE_OUTPUT_ESCAPING",
- "Ljava/lang/String;",
- true,
- "public static",
- "Ljavax/xml/transform/Result;"));
-}
-
-TEST_F(VerifierDepsTest, StaticField_Resolved_DeclaredInInterface3) {
- ASSERT_TRUE(VerifyMethod("StaticField_Resolved_DeclaredInInterface3"));
- ASSERT_TRUE(HasField("LMyResult;",
- "PI_ENABLE_OUTPUT_ESCAPING",
- "Ljava/lang/String;",
- true,
- "public static",
- "Ljavax/xml/transform/Result;"));
-}
-
-TEST_F(VerifierDepsTest, StaticField_Resolved_DeclaredInInterface4) {
- ASSERT_TRUE(VerifyMethod("StaticField_Resolved_DeclaredInInterface4"));
- ASSERT_TRUE(HasField("LMyDocument;",
- "ELEMENT_NODE",
- "S",
- true,
- "public static",
- "Lorg/w3c/dom/Node;"));
-}
-
-TEST_F(VerifierDepsTest, StaticField_Unresolved_ReferrerInBoot) {
- ASSERT_TRUE(VerifyMethod("StaticField_Unresolved_ReferrerInBoot"));
- ASSERT_TRUE(HasClass("Ljava/util/TimeZone;", true, "public"));
- ASSERT_TRUE(HasField("Ljava/util/TimeZone;", "x", "I", false));
-}
-
-TEST_F(VerifierDepsTest, StaticField_Unresolved_ReferrerInDex) {
- ASSERT_TRUE(VerifyMethod("StaticField_Unresolved_ReferrerInDex"));
- ASSERT_TRUE(HasField("LMyThreadSet;", "x", "I", false));
-}
-
TEST_F(VerifierDepsTest, InstanceField_Resolved_DeclaredInReferenced) {
ASSERT_TRUE(VerifyMethod("InstanceField_Resolved_DeclaredInReferenced"));
- ASSERT_TRUE(HasClass("Ljava/io/InterruptedIOException;", true, "public"));
- ASSERT_TRUE(HasField("Ljava/io/InterruptedIOException;",
- "bytesTransferred",
- "I",
- true,
- "public",
- "Ljava/io/InterruptedIOException;"));
ASSERT_TRUE(HasAssignable(
"Ljava/io/InterruptedIOException;", "Ljava/net/SocketTimeoutException;", true));
}
TEST_F(VerifierDepsTest, InstanceField_Resolved_DeclaredInSuperclass1) {
ASSERT_TRUE(VerifyMethod("InstanceField_Resolved_DeclaredInSuperclass1"));
- ASSERT_TRUE(HasClass("Ljava/net/SocketTimeoutException;", true, "public"));
- ASSERT_TRUE(HasField("Ljava/net/SocketTimeoutException;",
- "bytesTransferred",
- "I",
- true,
- "public",
- "Ljava/io/InterruptedIOException;"));
ASSERT_TRUE(HasAssignable(
"Ljava/io/InterruptedIOException;", "Ljava/net/SocketTimeoutException;", true));
}
TEST_F(VerifierDepsTest, InstanceField_Resolved_DeclaredInSuperclass2) {
ASSERT_TRUE(VerifyMethod("InstanceField_Resolved_DeclaredInSuperclass2"));
- ASSERT_TRUE(HasField("LMySocketTimeoutException;",
- "bytesTransferred",
- "I",
- true,
- "public",
- "Ljava/io/InterruptedIOException;"));
ASSERT_TRUE(HasAssignable(
"Ljava/io/InterruptedIOException;", "Ljava/net/SocketTimeoutException;", true));
}
-TEST_F(VerifierDepsTest, InstanceField_Unresolved_ReferrerInBoot) {
- ASSERT_TRUE(VerifyMethod("InstanceField_Unresolved_ReferrerInBoot"));
- ASSERT_TRUE(HasClass("Ljava/io/InterruptedIOException;", true, "public"));
- ASSERT_TRUE(HasField("Ljava/io/InterruptedIOException;", "x", "I", false));
-}
-
-TEST_F(VerifierDepsTest, InstanceField_Unresolved_ReferrerInDex) {
- ASSERT_TRUE(VerifyMethod("InstanceField_Unresolved_ReferrerInDex"));
- ASSERT_TRUE(HasField("LMyThreadSet;", "x", "I", false));
-}
-
-TEST_F(VerifierDepsTest, InvokeStatic_Resolved_DeclaredInReferenced) {
- ASSERT_TRUE(VerifyMethod("InvokeStatic_Resolved_DeclaredInReferenced"));
- ASSERT_TRUE(HasClass("Ljava/net/Socket;", true, "public"));
- ASSERT_TRUE(HasMethod("Ljava/net/Socket;",
- "setSocketImplFactory",
- "(Ljava/net/SocketImplFactory;)V",
- /* expect_resolved= */ true,
- "public static",
- "Ljava/net/Socket;"));
-}
-
-TEST_F(VerifierDepsTest, InvokeStatic_Resolved_DeclaredInSuperclass1) {
- ASSERT_TRUE(VerifyMethod("InvokeStatic_Resolved_DeclaredInSuperclass1"));
- ASSERT_TRUE(HasClass("Ljavax/net/ssl/SSLSocket;", true, "public"));
- ASSERT_TRUE(HasMethod("Ljavax/net/ssl/SSLSocket;",
- "setSocketImplFactory",
- "(Ljava/net/SocketImplFactory;)V",
- /* expect_resolved= */ true,
- "public static",
- "Ljava/net/Socket;"));
-}
-
-TEST_F(VerifierDepsTest, InvokeStatic_Resolved_DeclaredInSuperclass2) {
- ASSERT_TRUE(VerifyMethod("InvokeStatic_Resolved_DeclaredInSuperclass2"));
- ASSERT_TRUE(HasMethod("LMySSLSocket;",
- "setSocketImplFactory",
- "(Ljava/net/SocketImplFactory;)V",
- /* expect_resolved= */ true,
- "public static",
- "Ljava/net/Socket;"));
-}
-
-TEST_F(VerifierDepsTest, InvokeStatic_DeclaredInInterface1) {
- ASSERT_TRUE(VerifyMethod("InvokeStatic_DeclaredInInterface1"));
- ASSERT_TRUE(HasClass("Ljava/util/Map$Entry;", true, "public interface"));
- ASSERT_TRUE(HasMethod("Ljava/util/Map$Entry;",
- "comparingByKey",
- "()Ljava/util/Comparator;",
- /* expect_resolved= */ true,
- "public static",
- "Ljava/util/Map$Entry;"));
-}
-
-TEST_F(VerifierDepsTest, InvokeStatic_DeclaredInInterface2) {
- ASSERT_FALSE(VerifyMethod("InvokeStatic_DeclaredInInterface2"));
- ASSERT_TRUE(HasClass("Ljava/util/AbstractMap$SimpleEntry;", true, "public"));
- ASSERT_TRUE(HasMethod("Ljava/util/AbstractMap$SimpleEntry;",
- "comparingByKey",
- "()Ljava/util/Comparator;",
- /* expect_resolved= */ false));
-}
-
-TEST_F(VerifierDepsTest, InvokeStatic_Unresolved1) {
- ASSERT_FALSE(VerifyMethod("InvokeStatic_Unresolved1"));
- ASSERT_TRUE(HasClass("Ljavax/net/ssl/SSLSocket;", true, "public"));
- ASSERT_TRUE(HasMethod("Ljavax/net/ssl/SSLSocket;",
- "x",
- "()V",
- /* expect_resolved= */ false));
-}
-
-TEST_F(VerifierDepsTest, InvokeStatic_Unresolved2) {
- ASSERT_FALSE(VerifyMethod("InvokeStatic_Unresolved2"));
- ASSERT_TRUE(HasMethod("LMySSLSocket;",
- "x",
- "()V",
- /* expect_resolved= */ false));
-}
-
-TEST_F(VerifierDepsTest, InvokeDirect_Resolved_DeclaredInReferenced) {
- ASSERT_TRUE(VerifyMethod("InvokeDirect_Resolved_DeclaredInReferenced"));
- ASSERT_TRUE(HasClass("Ljava/net/Socket;", true, "public"));
- ASSERT_TRUE(HasMethod("Ljava/net/Socket;",
- "<init>",
- "()V",
- /* expect_resolved= */ true,
- "public",
- "Ljava/net/Socket;"));
-}
-
-TEST_F(VerifierDepsTest, InvokeDirect_Resolved_DeclaredInSuperclass1) {
- ASSERT_FALSE(VerifyMethod("InvokeDirect_Resolved_DeclaredInSuperclass1"));
- ASSERT_TRUE(HasClass("Ljavax/net/ssl/SSLSocket;", true, "public"));
- ASSERT_TRUE(HasMethod("Ljavax/net/ssl/SSLSocket;",
- "checkOldImpl",
- "()V",
- /* expect_resolved= */ true,
- "private",
- "Ljava/net/Socket;"));
-}
-
-TEST_F(VerifierDepsTest, InvokeDirect_Resolved_DeclaredInSuperclass2) {
- ASSERT_FALSE(VerifyMethod("InvokeDirect_Resolved_DeclaredInSuperclass2"));
- ASSERT_TRUE(HasMethod("LMySSLSocket;",
- "checkOldImpl",
- "()V",
- /* expect_resolved= */ true,
- "private",
- "Ljava/net/Socket;"));
-}
-
-TEST_F(VerifierDepsTest, InvokeDirect_Unresolved1) {
- ASSERT_FALSE(VerifyMethod("InvokeDirect_Unresolved1"));
- ASSERT_TRUE(HasClass("Ljavax/net/ssl/SSLSocket;", true, "public"));
- ASSERT_TRUE(HasMethod("Ljavax/net/ssl/SSLSocket;",
- "x",
- "()V",
- /* expect_resolved= */ false));
-}
-
-TEST_F(VerifierDepsTest, InvokeDirect_Unresolved2) {
- ASSERT_FALSE(VerifyMethod("InvokeDirect_Unresolved2"));
- ASSERT_TRUE(HasMethod("LMySSLSocket;",
- "x",
- "()V",
- /* expect_resolved= */ false));
-}
-
TEST_F(VerifierDepsTest, InvokeVirtual_Resolved_DeclaredInReferenced) {
ASSERT_TRUE(VerifyMethod("InvokeVirtual_Resolved_DeclaredInReferenced"));
- ASSERT_TRUE(HasClass("Ljava/lang/Throwable;", true, "public"));
- ASSERT_TRUE(HasMethod("Ljava/lang/Throwable;",
- "getMessage",
- "()Ljava/lang/String;",
- /* expect_resolved= */ true,
- "public",
- "Ljava/lang/Throwable;"));
// Type dependency on `this` argument.
ASSERT_TRUE(HasAssignable("Ljava/lang/Throwable;", "Ljava/net/SocketTimeoutException;", true));
}
TEST_F(VerifierDepsTest, InvokeVirtual_Resolved_DeclaredInSuperclass1) {
ASSERT_TRUE(VerifyMethod("InvokeVirtual_Resolved_DeclaredInSuperclass1"));
- ASSERT_TRUE(HasClass("Ljava/io/InterruptedIOException;", true, "public"));
- ASSERT_TRUE(HasMethod("Ljava/io/InterruptedIOException;",
- "getMessage",
- "()Ljava/lang/String;",
- /* expect_resolved= */ true,
- "public",
- "Ljava/lang/Throwable;"));
// Type dependency on `this` argument.
ASSERT_TRUE(HasAssignable("Ljava/lang/Throwable;", "Ljava/net/SocketTimeoutException;", true));
}
-TEST_F(VerifierDepsTest, InvokeVirtual_Resolved_DeclaredInSuperclass2) {
- ASSERT_TRUE(VerifyMethod("InvokeVirtual_Resolved_DeclaredInSuperclass2"));
- ASSERT_TRUE(HasMethod("LMySocketTimeoutException;",
- "getMessage",
- "()Ljava/lang/String;",
- /* expect_resolved= */ true,
- "public",
- "Ljava/lang/Throwable;"));
-}
-
-TEST_F(VerifierDepsTest, InvokeVirtual_Resolved_DeclaredInSuperinterface) {
- ASSERT_TRUE(VerifyMethod("InvokeVirtual_Resolved_DeclaredInSuperinterface"));
- ASSERT_TRUE(HasMethod("LMyThreadSet;",
- "size",
- "()I",
- /* expect_resolved= */ true,
- "public",
- "Ljava/util/Set;"));
-}
-
-TEST_F(VerifierDepsTest, InvokeVirtual_Unresolved1) {
- ASSERT_FALSE(VerifyMethod("InvokeVirtual_Unresolved1"));
- ASSERT_TRUE(HasClass("Ljava/io/InterruptedIOException;", true, "public"));
- ASSERT_TRUE(HasMethod("Ljava/io/InterruptedIOException;",
- "x",
- "()V",
- /* expect_resolved= */ false));
-}
-
-TEST_F(VerifierDepsTest, InvokeVirtual_Unresolved2) {
- ASSERT_FALSE(VerifyMethod("InvokeVirtual_Unresolved2"));
- ASSERT_TRUE(HasMethod("LMySocketTimeoutException;",
- "x",
- "()V",
- /* expect_resolved= */ false));
-}
-
-TEST_F(VerifierDepsTest, InvokeInterface_Resolved_DeclaredInReferenced) {
- ASSERT_TRUE(VerifyMethod("InvokeInterface_Resolved_DeclaredInReferenced"));
- ASSERT_TRUE(HasClass("Ljava/lang/Runnable;", true, "public interface"));
- ASSERT_TRUE(HasMethod("Ljava/lang/Runnable;",
- "run",
- "()V",
- /* expect_resolved= */ true,
- "public",
- "Ljava/lang/Runnable;"));
-}
-
-TEST_F(VerifierDepsTest, InvokeInterface_Resolved_DeclaredInSuperclass) {
- ASSERT_FALSE(VerifyMethod("InvokeInterface_Resolved_DeclaredInSuperclass"));
- // TODO: Maybe we should not record dependency if the invoke type does not match the lookup type.
- ASSERT_TRUE(HasMethod("LMyThread;",
- "join",
- "()V",
- /* expect_resolved= */ true,
- "public",
- "Ljava/lang/Thread;"));
-}
-
-TEST_F(VerifierDepsTest, InvokeInterface_Resolved_DeclaredInSuperinterface1) {
- ASSERT_FALSE(VerifyMethod("InvokeInterface_Resolved_DeclaredInSuperinterface1"));
- // TODO: Maybe we should not record dependency if the invoke type does not match the lookup type.
- ASSERT_TRUE(HasMethod("LMyThreadSet;",
- "run",
- "()V",
- /* expect_resolved= */ true,
- "public",
- "Ljava/lang/Thread;"));
-}
-
-TEST_F(VerifierDepsTest, InvokeInterface_Resolved_DeclaredInSuperinterface2) {
- ASSERT_FALSE(VerifyMethod("InvokeInterface_Resolved_DeclaredInSuperinterface2"));
- ASSERT_TRUE(HasMethod("LMyThreadSet;",
- "isEmpty",
- "()Z",
- /* expect_resolved= */ true,
- "public",
- "Ljava/util/Set;"));
-}
-
-TEST_F(VerifierDepsTest, InvokeInterface_Unresolved1) {
- ASSERT_FALSE(VerifyMethod("InvokeInterface_Unresolved1"));
- ASSERT_TRUE(HasClass("Ljava/lang/Runnable;", true, "public interface"));
- ASSERT_TRUE(HasMethod("Ljava/lang/Runnable;",
- "x",
- "()V",
- /* expect_resolved= */ false));
-}
-
-TEST_F(VerifierDepsTest, InvokeInterface_Unresolved2) {
- ASSERT_FALSE(VerifyMethod("InvokeInterface_Unresolved2"));
- ASSERT_TRUE(HasMethod("LMyThreadSet;", "x", "()V", /* expect_resolved= */ false));
-}
-
TEST_F(VerifierDepsTest, InvokeSuper_ThisAssignable) {
ASSERT_TRUE(VerifyMethod("InvokeSuper_ThisAssignable"));
- ASSERT_TRUE(HasClass("Ljava/lang/Runnable;", true, "public interface"));
ASSERT_TRUE(HasAssignable("Ljava/lang/Runnable;", "Ljava/lang/Thread;", true));
- ASSERT_TRUE(HasMethod("Ljava/lang/Runnable;",
- "run",
- "()V",
- /* expect_resolved= */ true,
- "public",
- "Ljava/lang/Runnable;"));
}
TEST_F(VerifierDepsTest, InvokeSuper_ThisNotAssignable) {
ASSERT_FALSE(VerifyMethod("InvokeSuper_ThisNotAssignable"));
- ASSERT_TRUE(HasClass("Ljava/lang/Integer;", true, "public"));
ASSERT_TRUE(HasAssignable("Ljava/lang/Integer;", "Ljava/lang/Thread;", false));
- ASSERT_TRUE(HasMethod("Ljava/lang/Integer;",
- "intValue", "()I",
- /* expect_resolved= */ true,
- "public", "Ljava/lang/Integer;"));
-}
-
-TEST_F(VerifierDepsTest, ArgumentType_ResolvedReferenceArray) {
- ASSERT_TRUE(VerifyMethod("ArgumentType_ResolvedReferenceArray"));
- ASSERT_TRUE(HasClass("[Ljava/lang/Thread;", true, "public"));
-}
-
-TEST_F(VerifierDepsTest, NewArray_Resolved) {
- ASSERT_TRUE(VerifyMethod("NewArray_Resolved"));
- ASSERT_TRUE(HasClass("[Ljava/lang/IllegalStateException;", true, "public"));
}
TEST_F(VerifierDepsTest, EncodeDecode) {
@@ -1199,14 +624,6 @@
VerifyDexFile();
// Test that a class which redefines a boot classpath class has dependencies recorded.
ASSERT_TRUE(HasRedefinedClass("Ljava/net/SocketTimeoutException;"));
- // These come from test case InstanceField_Resolved_DeclaredInSuperclass1.
- ASSERT_TRUE(HasClass("Ljava/net/SocketTimeoutException;", true, "public"));
- ASSERT_TRUE(HasField("Ljava/net/SocketTimeoutException;",
- "bytesTransferred",
- "I",
- true,
- "public",
- "Ljava/io/InterruptedIOException;"));
}
TEST_F(VerifierDepsTest, UnverifiedOrder) {
@@ -1270,149 +687,6 @@
ASSERT_FALSE(RunValidation([](VerifierDeps::DexFileDeps& deps) {
deps.unassignable_types_.insert(*deps.assignable_types_.begin());
}, buffer, &error_msg));
-
- // Mess with classes.
- ASSERT_FALSE(RunValidation([](VerifierDeps::DexFileDeps& deps) {
- for (const auto& entry : deps.classes_) {
- if (entry.IsResolved()) {
- deps.classes_.insert(VerifierDeps::ClassResolution(
- entry.GetDexTypeIndex(), VerifierDeps::kUnresolvedMarker));
- return;
- }
- }
- LOG(FATAL) << "Could not find any resolved classes";
- UNREACHABLE();
- }, buffer, &error_msg));
- ASSERT_FALSE(RunValidation([](VerifierDeps::DexFileDeps& deps) {
- for (const auto& entry : deps.classes_) {
- if (!entry.IsResolved()) {
- deps.classes_.insert(VerifierDeps::ClassResolution(
- entry.GetDexTypeIndex(), VerifierDeps::kUnresolvedMarker - 1));
- return;
- }
- }
- LOG(FATAL) << "Could not find any unresolved classes";
- UNREACHABLE();
- }, buffer, &error_msg));
- ASSERT_FALSE(RunValidation([](VerifierDeps::DexFileDeps& deps) {
- for (const auto& entry : deps.classes_) {
- if (entry.IsResolved()) {
- deps.classes_.insert(VerifierDeps::ClassResolution(
- entry.GetDexTypeIndex(), entry.GetAccessFlags() - 1));
- return;
- }
- }
- LOG(FATAL) << "Could not find any resolved classes";
- UNREACHABLE();
- }, buffer, &error_msg));
-
- // Mess with fields.
- ASSERT_FALSE(RunValidation([](VerifierDeps::DexFileDeps& deps) {
- for (const auto& entry : deps.fields_) {
- if (entry.IsResolved()) {
- deps.fields_.insert(VerifierDeps::FieldResolution(entry.GetDexFieldIndex(),
- VerifierDeps::kUnresolvedMarker,
- entry.GetDeclaringClassIndex()));
- return;
- }
- }
- LOG(FATAL) << "Could not find any resolved fields";
- UNREACHABLE();
- }, buffer, &error_msg));
- ASSERT_FALSE(RunValidation([](VerifierDeps::DexFileDeps& deps) {
- for (const auto& entry : deps.fields_) {
- if (!entry.IsResolved()) {
- constexpr dex::StringIndex kStringIndexZero(0); // We know there is a class there.
- deps.fields_.insert(VerifierDeps::FieldResolution(0 /* we know there is a field there */,
- VerifierDeps::kUnresolvedMarker - 1,
- kStringIndexZero));
- return;
- }
- }
- LOG(FATAL) << "Could not find any unresolved fields";
- UNREACHABLE();
- }, buffer, &error_msg));
- ASSERT_FALSE(RunValidation([](VerifierDeps::DexFileDeps& deps) {
- for (const auto& entry : deps.fields_) {
- if (entry.IsResolved()) {
- deps.fields_.insert(VerifierDeps::FieldResolution(entry.GetDexFieldIndex(),
- entry.GetAccessFlags() - 1,
- entry.GetDeclaringClassIndex()));
- return;
- }
- }
- LOG(FATAL) << "Could not find any resolved fields";
- UNREACHABLE();
- }, buffer, &error_msg));
- ASSERT_FALSE(RunValidation([](VerifierDeps::DexFileDeps& deps) {
- for (const auto& entry : deps.fields_) {
- constexpr dex::StringIndex kNewTypeIndex(0);
- if (entry.GetDeclaringClassIndex() != kNewTypeIndex) {
- deps.fields_.insert(VerifierDeps::FieldResolution(entry.GetDexFieldIndex(),
- entry.GetAccessFlags(),
- kNewTypeIndex));
- return;
- }
- }
- LOG(FATAL) << "Could not find any suitable fields";
- UNREACHABLE();
- }, buffer, &error_msg));
-
- // Mess with methods.
- ASSERT_FALSE(RunValidation([](VerifierDeps::DexFileDeps& deps) {
- std::set<VerifierDeps::MethodResolution>* methods = &deps.methods_;
- for (const auto& entry : *methods) {
- if (entry.IsResolved()) {
- methods->insert(VerifierDeps::MethodResolution(entry.GetDexMethodIndex(),
- VerifierDeps::kUnresolvedMarker,
- entry.GetDeclaringClassIndex()));
- return;
- }
- }
- LOG(FATAL) << "Could not find any resolved methods";
- UNREACHABLE();
- }, buffer, &error_msg));
- ASSERT_FALSE(RunValidation([](VerifierDeps::DexFileDeps& deps) {
- std::set<VerifierDeps::MethodResolution>* methods = &deps.methods_;
- for (const auto& entry : *methods) {
- if (!entry.IsResolved()) {
- constexpr dex::StringIndex kStringIndexZero(0); // We know there is a class there.
- methods->insert(VerifierDeps::MethodResolution(0 /* we know there is a method there */,
- VerifierDeps::kUnresolvedMarker - 1,
- kStringIndexZero));
- return;
- }
- }
- LOG(FATAL) << "Could not find any unresolved methods";
- UNREACHABLE();
- }, buffer, &error_msg));
- ASSERT_FALSE(RunValidation([](VerifierDeps::DexFileDeps& deps) {
- std::set<VerifierDeps::MethodResolution>* methods = &deps.methods_;
- for (const auto& entry : *methods) {
- if (entry.IsResolved()) {
- methods->insert(VerifierDeps::MethodResolution(entry.GetDexMethodIndex(),
- entry.GetAccessFlags() - 1,
- entry.GetDeclaringClassIndex()));
- return;
- }
- }
- LOG(FATAL) << "Could not find any resolved methods";
- UNREACHABLE();
- }, buffer, &error_msg));
- ASSERT_FALSE(RunValidation([](VerifierDeps::DexFileDeps& deps) {
- std::set<VerifierDeps::MethodResolution>* methods = &deps.methods_;
- for (const auto& entry : *methods) {
- constexpr dex::StringIndex kNewTypeIndex(0);
- if (entry.IsResolved() && entry.GetDeclaringClassIndex() != kNewTypeIndex) {
- methods->insert(VerifierDeps::MethodResolution(entry.GetDexMethodIndex(),
- entry.GetAccessFlags(),
- kNewTypeIndex));
- return;
- }
- }
- LOG(FATAL) << "Could not find any suitable methods";
- UNREACHABLE();
- }, buffer, &error_msg));
}
TEST_F(VerifierDepsTest, CompilerDriver) {
@@ -1439,20 +713,6 @@
VerifierDeps decoded_deps(dex_files_, /*output_only=*/ false);
bool parsed = decoded_deps.ParseStoredData(dex_files_, ArrayRef<const uint8_t>(buffer));
ASSERT_TRUE(parsed);
- if (verify_failure) {
- // Just taint the decoded VerifierDeps with one invalid entry.
- VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
- bool found = false;
- for (const auto& entry : deps->classes_) {
- if (entry.IsResolved()) {
- deps->classes_.insert(VerifierDeps::ClassResolution(
- entry.GetDexTypeIndex(), VerifierDeps::kUnresolvedMarker));
- found = true;
- break;
- }
- }
- ASSERT_TRUE(found);
- }
VerifyWithCompilerDriver(&decoded_deps);
if (verify_failure) {
diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h
index d205904..98efaa1 100644
--- a/runtime/vdex_file.h
+++ b/runtime/vdex_file.h
@@ -114,8 +114,8 @@
static constexpr uint8_t kVdexMagic[] = { 'v', 'd', 'e', 'x' };
// The format version of the verifier deps header and the verifier deps.
- // Last update: Add boot checksum, class loader context.
- static constexpr uint8_t kVerifierDepsVersion[] = { '0', '2', '1', '\0' };
+ // Last update: Remove class/field/method resolution.
+ static constexpr uint8_t kVerifierDepsVersion[] = { '0', '2', '2', '\0' };
// The format version of the dex section header and the dex section, containing
// both the dex code and the quickening data.
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index c33e646..fea34ef 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -3756,9 +3756,6 @@
return *result;
}
- // Record result of class resolution attempt.
- VerifierDeps::MaybeRecordClassResolution(*dex_file_, class_idx, klass);
-
// If requested, check if access is allowed. Unresolved types are included in this check, as the
// interpreter only tests whether access is allowed when a class is not pre-verified and runs in
// the access-checks interpreter. If result is primitive, skip the access check.
@@ -3894,11 +3891,6 @@
klass, dex_cache_.Get(), class_loader_.Get(), dex_method_idx);
}
- // Record result of method resolution attempt. The klass resolution has recorded whether
- // the class is an interface or not and therefore the type of the lookup performed above.
- // TODO: Maybe we should not record dependency if the invoke type does not match the lookup type.
- VerifierDeps::MaybeRecordMethodResolution(*dex_file_, dex_method_idx, res_method);
-
bool must_fail = false;
// This is traditional and helps with screwy bytecode. It will tell you that, yes, a method
// exists, but that it's called incorrectly. This significantly helps debugging, as locally it's
@@ -4674,9 +4666,6 @@
ClassLinker* class_linker = GetClassLinker();
ArtField* field = class_linker->ResolveFieldJLS(field_idx, dex_cache_, class_loader_);
- // Record result of the field resolution attempt.
- VerifierDeps::MaybeRecordFieldResolution(*dex_file_, field_idx, field);
-
if (field == nullptr) {
VLOG(verifier) << "Unable to resolve static field " << field_idx << " ("
<< dex_file_->GetFieldName(field_id) << ") in "
@@ -4724,9 +4713,6 @@
ClassLinker* class_linker = GetClassLinker();
ArtField* field = class_linker->ResolveFieldJLS(field_idx, dex_cache_, class_loader_);
- // Record result of the field resolution attempt.
- VerifierDeps::MaybeRecordFieldResolution(*dex_file_, field_idx, field);
-
if (field == nullptr) {
VLOG(verifier) << "Unable to resolve instance field " << field_idx << " ("
<< dex_file_->GetFieldName(field_id) << ") in "
diff --git a/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc
index d06e3d4..dd68416 100644
--- a/runtime/verifier/verifier_deps.cc
+++ b/runtime/verifier/verifier_deps.cc
@@ -69,9 +69,6 @@
DCHECK(other_deps.strings_.empty());
my_deps->assignable_types_.merge(other_deps.assignable_types_);
my_deps->unassignable_types_.merge(other_deps.unassignable_types_);
- my_deps->classes_.merge(other_deps.classes_);
- my_deps->fields_.merge(other_deps.fields_);
- my_deps->methods_.merge(other_deps.methods_);
BitVectorOr(my_deps->verified_classes_, other_deps.verified_classes_);
BitVectorOr(my_deps->redefined_classes_, other_deps.redefined_classes_);
}
@@ -87,22 +84,6 @@
return (it == dex_deps_.end()) ? nullptr : it->second.get();
}
-// Access flags that impact vdex verification.
-static constexpr uint32_t kAccVdexAccessFlags =
- kAccPublic | kAccPrivate | kAccProtected | kAccStatic | kAccInterface;
-
-template <typename Ptr>
-uint16_t VerifierDeps::GetAccessFlags(Ptr element) {
- static_assert(kAccJavaFlagsMask == 0xFFFF, "Unexpected value of a constant");
- if (element == nullptr) {
- return VerifierDeps::kUnresolvedMarker;
- } else {
- uint16_t access_flags = Low16Bits(element->GetAccessFlags()) & kAccVdexAccessFlags;
- CHECK_NE(access_flags, VerifierDeps::kUnresolvedMarker);
- return access_flags;
- }
-}
-
dex::StringIndex VerifierDeps::GetClassDescriptorStringId(const DexFile& dex_file,
ObjPtr<mirror::Class> klass) {
DCHECK(klass != nullptr);
@@ -126,61 +107,6 @@
return GetIdFromString(dex_file, klass->GetDescriptor(&temp));
}
-// Try to find the string descriptor of the class. type_idx is a best guess of a matching string id.
-static dex::StringIndex TryGetClassDescriptorStringId(const DexFile& dex_file,
- dex::TypeIndex type_idx,
- ObjPtr<mirror::Class> klass)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- if (!klass->IsArrayClass()) {
- const dex::TypeId& type_id = dex_file.GetTypeId(type_idx);
- const DexFile& klass_dex = klass->GetDexFile();
- const dex::TypeId& klass_type_id = klass_dex.GetTypeId(klass->GetClassDef()->class_idx_);
- if (strcmp(dex_file.GetTypeDescriptor(type_id),
- klass_dex.GetTypeDescriptor(klass_type_id)) == 0) {
- return type_id.descriptor_idx_;
- }
- }
- return dex::StringIndex::Invalid();
-}
-
-dex::StringIndex VerifierDeps::GetMethodDeclaringClassStringId(const DexFile& dex_file,
- uint32_t dex_method_index,
- ArtMethod* method) {
- static_assert(kAccJavaFlagsMask == 0xFFFF, "Unexpected value of a constant");
- if (method == nullptr) {
- return dex::StringIndex(VerifierDeps::kUnresolvedMarker);
- }
- const dex::StringIndex string_id = TryGetClassDescriptorStringId(
- dex_file,
- dex_file.GetMethodId(dex_method_index).class_idx_,
- method->GetDeclaringClass());
- if (string_id.IsValid()) {
- // Got lucky using the original dex file, return based on the input dex file.
- DCHECK_EQ(GetClassDescriptorStringId(dex_file, method->GetDeclaringClass()), string_id);
- return string_id;
- }
- return GetClassDescriptorStringId(dex_file, method->GetDeclaringClass());
-}
-
-dex::StringIndex VerifierDeps::GetFieldDeclaringClassStringId(const DexFile& dex_file,
- uint32_t dex_field_idx,
- ArtField* field) {
- static_assert(kAccJavaFlagsMask == 0xFFFF, "Unexpected value of a constant");
- if (field == nullptr) {
- return dex::StringIndex(VerifierDeps::kUnresolvedMarker);
- }
- const dex::StringIndex string_id = TryGetClassDescriptorStringId(
- dex_file,
- dex_file.GetFieldId(dex_field_idx).class_idx_,
- field->GetDeclaringClass());
- if (string_id.IsValid()) {
- // Got lucky using the original dex file, return based on the input dex file.
- DCHECK_EQ(GetClassDescriptorStringId(dex_file, field->GetDeclaringClass()), string_id);
- return string_id;
- }
- return GetClassDescriptorStringId(dex_file, field->GetDeclaringClass());
-}
-
static inline VerifierDeps* GetMainVerifierDeps() {
// The main VerifierDeps is the one set in the compiler callbacks, which at the
// end of verification will have all the per-thread VerifierDeps merged into it.
@@ -289,67 +215,6 @@
return (GetDexFileDeps(*dex_file) == nullptr);
}
-void VerifierDeps::AddClassResolution(const DexFile& dex_file,
- dex::TypeIndex type_idx,
- ObjPtr<mirror::Class> klass) {
- DexFileDeps* dex_deps = GetDexFileDeps(dex_file);
- if (dex_deps == nullptr) {
- // This invocation is from verification of a dex file which is not being compiled.
- return;
- }
-
- if (klass != nullptr && !IsInClassPath(klass)) {
- // Class resolved into one of the DEX files which are being compiled.
- // This is not a classpath dependency.
- return;
- }
-
- dex_deps->classes_.emplace(ClassResolution(type_idx, GetAccessFlags(klass)));
-}
-
-void VerifierDeps::AddFieldResolution(const DexFile& dex_file,
- uint32_t field_idx,
- ArtField* field) {
- DexFileDeps* dex_deps = GetDexFileDeps(dex_file);
- if (dex_deps == nullptr) {
- // This invocation is from verification of a dex file which is not being compiled.
- return;
- }
-
- if (field != nullptr && !IsInClassPath(field->GetDeclaringClass())) {
- // Field resolved into one of the DEX files which are being compiled.
- // This is not a classpath dependency.
- return;
- }
-
- dex_deps->fields_.emplace(FieldResolution(field_idx,
- GetAccessFlags(field),
- GetFieldDeclaringClassStringId(dex_file,
- field_idx,
- field)));
-}
-
-void VerifierDeps::AddMethodResolution(const DexFile& dex_file,
- uint32_t method_idx,
- ArtMethod* method) {
- DexFileDeps* dex_deps = GetDexFileDeps(dex_file);
- if (dex_deps == nullptr) {
- // This invocation is from verification of a dex file which is not being compiled.
- return;
- }
-
- if (method != nullptr && !IsInClassPath(method->GetDeclaringClass())) {
- // Method resolved into one of the DEX files which are being compiled.
- // This is not a classpath dependency.
- return;
- }
-
- MethodResolution method_tuple(method_idx,
- GetAccessFlags(method),
- GetMethodDeclaringClassStringId(dex_file, method_idx, method));
- dex_deps->methods_.insert(method_tuple);
-}
-
ObjPtr<mirror::Class> VerifierDeps::FindOneClassPathBoundaryForInterface(
ObjPtr<mirror::Class> destination,
ObjPtr<mirror::Class> source) const {
@@ -538,33 +403,6 @@
dex_deps->verified_classes_[dex_file.GetIndexForClassDef(class_def)] = true;
}
-void VerifierDeps::MaybeRecordClassResolution(const DexFile& dex_file,
- dex::TypeIndex type_idx,
- ObjPtr<mirror::Class> klass) {
- VerifierDeps* thread_deps = GetThreadLocalVerifierDeps();
- if (thread_deps != nullptr) {
- thread_deps->AddClassResolution(dex_file, type_idx, klass);
- }
-}
-
-void VerifierDeps::MaybeRecordFieldResolution(const DexFile& dex_file,
- uint32_t field_idx,
- ArtField* field) {
- VerifierDeps* thread_deps = GetThreadLocalVerifierDeps();
- if (thread_deps != nullptr) {
- thread_deps->AddFieldResolution(dex_file, field_idx, field);
- }
-}
-
-void VerifierDeps::MaybeRecordMethodResolution(const DexFile& dex_file,
- uint32_t method_idx,
- ArtMethod* method) {
- VerifierDeps* thread_deps = GetThreadLocalVerifierDeps();
- if (thread_deps != nullptr) {
- thread_deps->AddMethodResolution(dex_file, method_idx, method);
- }
-}
-
void VerifierDeps::MaybeRecordAssignability(const DexFile& dex_file,
ObjPtr<mirror::Class> destination,
ObjPtr<mirror::Class> source,
@@ -583,27 +421,12 @@
template<> inline uint32_t Encode<uint16_t>(uint16_t in) {
return in;
}
-template<> inline uint32_t Encode<uint32_t>(uint32_t in) {
- return in;
-}
-template<> inline uint32_t Encode<dex::TypeIndex>(dex::TypeIndex in) {
- return in.index_;
-}
template<> inline uint32_t Encode<dex::StringIndex>(dex::StringIndex in) {
return in.index_;
}
template<typename T> inline T Decode(uint32_t in);
-template<> inline uint16_t Decode<uint16_t>(uint32_t in) {
- return dchecked_integral_cast<uint16_t>(in);
-}
-template<> inline uint32_t Decode<uint32_t>(uint32_t in) {
- return in;
-}
-template<> inline dex::TypeIndex Decode<dex::TypeIndex>(uint32_t in) {
- return dex::TypeIndex(in);
-}
template<> inline dex::StringIndex Decode<dex::StringIndex>(uint32_t in) {
return dex::StringIndex(in);
}
@@ -748,12 +571,6 @@
return true;
}
-static inline std::string ToHex(uint32_t value) {
- std::stringstream ss;
- ss << std::hex << value << std::dec;
- return ss.str();
-}
-
} // namespace
void VerifierDeps::Encode(const std::vector<const DexFile*>& dex_files,
@@ -763,9 +580,6 @@
EncodeStringVector(buffer, deps.strings_);
EncodeSet(buffer, deps.assignable_types_);
EncodeSet(buffer, deps.unassignable_types_);
- EncodeSet(buffer, deps.classes_);
- EncodeSet(buffer, deps.fields_);
- EncodeSet(buffer, deps.methods_);
EncodeUint16SparseBitVector(buffer, deps.verified_classes_, /* sparse_value= */ false);
EncodeUint16SparseBitVector(buffer, deps.redefined_classes_, /* sparse_value= */ true);
}
@@ -783,9 +597,6 @@
data_start, data_end, &deps.assignable_types_) &&
DecodeSet</*kFillSet=*/ !kOnlyVerifiedClasses>(
data_start, data_end, &deps.unassignable_types_) &&
- DecodeSet</*kFillSet=*/ !kOnlyVerifiedClasses>(data_start, data_end, &deps.classes_) &&
- DecodeSet</*kFillSet=*/ !kOnlyVerifiedClasses>(data_start, data_end, &deps.fields_) &&
- DecodeSet</*kFillSet=*/ !kOnlyVerifiedClasses>(data_start, data_end, &deps.methods_) &&
DecodeUint16SparseBitVector</*kFillVector=*/ true>(
data_start, data_end, num_class_defs, /*sparse_value=*/ false, &deps.verified_classes_) &&
DecodeUint16SparseBitVector</*kFillVector=*/ !kOnlyVerifiedClasses>(
@@ -876,9 +687,6 @@
return (strings_ == rhs.strings_) &&
(assignable_types_ == rhs.assignable_types_) &&
(unassignable_types_ == rhs.unassignable_types_) &&
- (classes_ == rhs.classes_) &&
- (fields_ == rhs.fields_) &&
- (methods_ == rhs.methods_) &&
(verified_classes_ == rhs.verified_classes_);
}
@@ -925,50 +733,6 @@
<< "\n";
}
- for (const ClassResolution& entry : dep.second->classes_) {
- vios->Stream()
- << dex_file.StringByTypeIdx(entry.GetDexTypeIndex())
- << (entry.IsResolved() ? " must be resolved " : "must not be resolved ")
- << " with access flags " << std::hex << entry.GetAccessFlags() << std::dec
- << "\n";
- }
-
- for (const FieldResolution& entry : dep.second->fields_) {
- const dex::FieldId& field_id = dex_file.GetFieldId(entry.GetDexFieldIndex());
- vios->Stream()
- << dex_file.GetFieldDeclaringClassDescriptor(field_id) << "->"
- << dex_file.GetFieldName(field_id) << ":"
- << dex_file.GetFieldTypeDescriptor(field_id)
- << " is expected to be ";
- if (!entry.IsResolved()) {
- vios->Stream() << "unresolved\n";
- } else {
- vios->Stream()
- << "in class "
- << GetStringFromId(dex_file, entry.GetDeclaringClassIndex())
- << ", and have the access flags " << std::hex << entry.GetAccessFlags() << std::dec
- << "\n";
- }
- }
-
- for (const MethodResolution& method : dep.second->methods_) {
- const dex::MethodId& method_id = dex_file.GetMethodId(method.GetDexMethodIndex());
- vios->Stream()
- << dex_file.GetMethodDeclaringClassDescriptor(method_id) << "->"
- << dex_file.GetMethodName(method_id)
- << dex_file.GetMethodSignature(method_id).ToString()
- << " is expected to be ";
- if (!method.IsResolved()) {
- vios->Stream() << "unresolved\n";
- } else {
- vios->Stream()
- << "in class "
- << GetStringFromId(dex_file, method.GetDeclaringClassIndex())
- << ", have the access flags " << std::hex << method.GetAccessFlags() << std::dec
- << "\n";
- }
- }
-
for (size_t idx = 0; idx < dep.second->verified_classes_.size(); idx++) {
if (!dep.second->verified_classes_[idx]) {
vios->Stream()
@@ -1045,169 +809,6 @@
return true;
}
-bool VerifierDeps::VerifyClasses(Handle<mirror::ClassLoader> class_loader,
- const DexFile& dex_file,
- const std::set<ClassResolution>& classes,
- Thread* self,
- /* out */ std::string* error_msg) const {
- StackHandleScope<1> hs(self);
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- MutableHandle<mirror::Class> cls(hs.NewHandle<mirror::Class>(nullptr));
- for (const auto& entry : classes) {
- std::string descriptor = dex_file.StringByTypeIdx(entry.GetDexTypeIndex());
- cls.Assign(FindClassAndClearException(class_linker, self, descriptor, class_loader));
-
- if (entry.IsResolved()) {
- if (cls == nullptr) {
- *error_msg = "Could not resolve class " + descriptor;
- return false;
- } else if (entry.GetAccessFlags() != GetAccessFlags(cls.Get())) {
- *error_msg = "Unexpected access flags on class " + descriptor
- + " (expected=" + ToHex(entry.GetAccessFlags())
- + ", actual=" + ToHex(GetAccessFlags(cls.Get())) + ")";
- return false;
- }
- } else if (cls != nullptr) {
- *error_msg = "Unexpected successful resolution of class " + descriptor;
- return false;
- }
- }
- return true;
-}
-
-static std::string GetFieldDescription(const DexFile& dex_file, uint32_t index) {
- const dex::FieldId& field_id = dex_file.GetFieldId(index);
- return std::string(dex_file.GetFieldDeclaringClassDescriptor(field_id))
- + "->"
- + dex_file.GetFieldName(field_id)
- + ":"
- + dex_file.GetFieldTypeDescriptor(field_id);
-}
-
-bool VerifierDeps::VerifyFields(Handle<mirror::ClassLoader> class_loader,
- const DexFile& dex_file,
- const std::set<FieldResolution>& fields,
- Thread* self,
- /* out */ std::string* error_msg) const {
- // Check recorded fields are resolved the same way, have the same recorded class,
- // and have the same recorded flags.
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- for (const auto& entry : fields) {
- const dex::FieldId& field_id = dex_file.GetFieldId(entry.GetDexFieldIndex());
- std::string_view name(dex_file.StringDataByIdx(field_id.name_idx_));
- std::string_view type(
- dex_file.StringDataByIdx(dex_file.GetTypeId(field_id.type_idx_).descriptor_idx_));
- // Only use field_id.class_idx_ when the entry is unresolved, which is rare.
- // Otherwise, we might end up resolving an application class, which is expensive.
- std::string expected_decl_klass = entry.IsResolved()
- ? GetStringFromId(dex_file, entry.GetDeclaringClassIndex())
- : dex_file.StringByTypeIdx(field_id.class_idx_);
- ObjPtr<mirror::Class> cls = FindClassAndClearException(
- class_linker, self, expected_decl_klass.c_str(), class_loader);
- if (cls == nullptr) {
- *error_msg = "Could not resolve class " + expected_decl_klass;
- return false;
- }
- DCHECK(cls->IsResolved());
-
- ArtField* field = mirror::Class::FindField(self, cls, name, type);
- if (entry.IsResolved()) {
- std::string temp;
- if (field == nullptr) {
- *error_msg = "Could not resolve field " +
- GetFieldDescription(dex_file, entry.GetDexFieldIndex());
- return false;
- } else if (expected_decl_klass != field->GetDeclaringClass()->GetDescriptor(&temp)) {
- *error_msg = "Unexpected declaring class for field resolution "
- + GetFieldDescription(dex_file, entry.GetDexFieldIndex())
- + " (expected=" + expected_decl_klass
- + ", actual=" + field->GetDeclaringClass()->GetDescriptor(&temp) + ")";
- return false;
- } else if (entry.GetAccessFlags() != GetAccessFlags(field)) {
- *error_msg = "Unexpected access flags for resolved field "
- + GetFieldDescription(dex_file, entry.GetDexFieldIndex())
- + " (expected=" + ToHex(entry.GetAccessFlags())
- + ", actual=" + ToHex(GetAccessFlags(field)) + ")";
- return false;
- }
- } else if (field != nullptr) {
- *error_msg = "Unexpected successful resolution of field "
- + GetFieldDescription(dex_file, entry.GetDexFieldIndex());
- return false;
- }
- }
- return true;
-}
-
-static std::string GetMethodDescription(const DexFile& dex_file, uint32_t index) {
- const dex::MethodId& method_id = dex_file.GetMethodId(index);
- return std::string(dex_file.GetMethodDeclaringClassDescriptor(method_id))
- + "->"
- + dex_file.GetMethodName(method_id)
- + dex_file.GetMethodSignature(method_id).ToString();
-}
-
-bool VerifierDeps::VerifyMethods(Handle<mirror::ClassLoader> class_loader,
- const DexFile& dex_file,
- const std::set<MethodResolution>& methods,
- Thread* self,
- /* out */ std::string* error_msg) const {
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- PointerSize pointer_size = class_linker->GetImagePointerSize();
-
- for (const auto& entry : methods) {
- const dex::MethodId& method_id = dex_file.GetMethodId(entry.GetDexMethodIndex());
-
- const char* name = dex_file.GetMethodName(method_id);
- const Signature signature = dex_file.GetMethodSignature(method_id);
- // Only use method_id.class_idx_ when the entry is unresolved, which is rare.
- // Otherwise, we might end up resolving an application class, which is expensive.
- std::string expected_decl_klass = entry.IsResolved()
- ? GetStringFromId(dex_file, entry.GetDeclaringClassIndex())
- : dex_file.StringByTypeIdx(method_id.class_idx_);
-
- ObjPtr<mirror::Class> cls = FindClassAndClearException(
- class_linker, self, expected_decl_klass.c_str(), class_loader);
- if (cls == nullptr) {
- *error_msg = "Could not resolve class " + expected_decl_klass;
- return false;
- }
- DCHECK(cls->IsResolved());
- ArtMethod* method = nullptr;
- if (cls->IsInterface()) {
- method = cls->FindInterfaceMethod(name, signature, pointer_size);
- } else {
- method = cls->FindClassMethod(name, signature, pointer_size);
- }
-
- if (entry.IsResolved()) {
- std::string temp;
- if (method == nullptr) {
- *error_msg = "Could not resolve method "
- + GetMethodDescription(dex_file, entry.GetDexMethodIndex());
- return false;
- } else if (expected_decl_klass != method->GetDeclaringClass()->GetDescriptor(&temp)) {
- *error_msg = "Unexpected declaring class for method resolution "
- + GetMethodDescription(dex_file, entry.GetDexMethodIndex())
- + " (expected=" + expected_decl_klass
- + ", actual=" + method->GetDeclaringClass()->GetDescriptor(&temp) + ")";
- return false;
- } else if (entry.GetAccessFlags() != GetAccessFlags(method)) {
- *error_msg = "Unexpected access flags for resolved method resolution "
- + GetMethodDescription(dex_file, entry.GetDexMethodIndex())
- + " (expected=" + ToHex(entry.GetAccessFlags())
- + ", actual=" + ToHex(GetAccessFlags(method)) + ")";
- return false;
- }
- } else if (method != nullptr) {
- *error_msg = "Unexpected successful resolution of method "
- + GetMethodDescription(dex_file, entry.GetDexMethodIndex());
- return false;
- }
- }
- return true;
-}
-
bool VerifierDeps::IsInDexFiles(const char* descriptor,
size_t hash,
const std::vector<const DexFile*>& dex_files,
@@ -1283,10 +884,7 @@
deps.unassignable_types_,
/* expected_assignability= */ false,
self,
- error_msg) &&
- VerifyClasses(class_loader, dex_file, deps.classes_, self, error_msg) &&
- VerifyFields(class_loader, dex_file, deps.fields_, self, error_msg) &&
- VerifyMethods(class_loader, dex_file, deps.methods_, self, error_msg);
+ error_msg);
}
} // namespace verifier
diff --git a/runtime/verifier/verifier_deps.h b/runtime/verifier/verifier_deps.h
index 326ee53..15fb4a5 100644
--- a/runtime/verifier/verifier_deps.h
+++ b/runtime/verifier/verifier_deps.h
@@ -84,30 +84,6 @@
static void MaybeRecordClassRedefinition(const DexFile& dex_file, const dex::ClassDef& class_def)
REQUIRES(!Locks::verifier_deps_lock_);
- // Record the outcome `klass` of resolving type `type_idx` from `dex_file`.
- // If `klass` is null, the class is assumed unresolved.
- static void MaybeRecordClassResolution(const DexFile& dex_file,
- dex::TypeIndex type_idx,
- ObjPtr<mirror::Class> klass)
- REQUIRES_SHARED(Locks::mutator_lock_)
- REQUIRES(!Locks::verifier_deps_lock_);
-
- // Record the outcome `field` of resolving field `field_idx` from `dex_file`.
- // If `field` is null, the field is assumed unresolved.
- static void MaybeRecordFieldResolution(const DexFile& dex_file,
- uint32_t field_idx,
- ArtField* field)
- REQUIRES_SHARED(Locks::mutator_lock_)
- REQUIRES(!Locks::verifier_deps_lock_);
-
- // Record the outcome `method` of resolving method `method_idx` from `dex_file`.
- // If `method` is null, the method is assumed unresolved.
- static void MaybeRecordMethodResolution(const DexFile& dex_file,
- uint32_t method_idx,
- ArtMethod* method)
- REQUIRES_SHARED(Locks::mutator_lock_)
- REQUIRES(!Locks::verifier_deps_lock_);
-
// Record the outcome `is_assignable` of type assignability test from `source`
// to `destination` as defined by RegType::AssignableFrom. `dex_file` is the
// owner of the method for which MethodVerifier performed the assignability test.
@@ -154,48 +130,6 @@
/*out*/std::vector<std::vector<bool>>* verified_classes_per_dex);
private:
- static constexpr uint16_t kUnresolvedMarker = static_cast<uint16_t>(-1);
-
- using ClassResolutionBase = std::tuple<dex::TypeIndex, uint16_t>;
- struct ClassResolution : public ClassResolutionBase {
- ClassResolution() = default;
- ClassResolution(const ClassResolution&) = default;
- ClassResolution(dex::TypeIndex type_idx, uint16_t access_flags)
- : ClassResolutionBase(type_idx, access_flags) {}
-
- bool IsResolved() const { return GetAccessFlags() != kUnresolvedMarker; }
- dex::TypeIndex GetDexTypeIndex() const { return std::get<0>(*this); }
- uint16_t GetAccessFlags() const { return std::get<1>(*this); }
- };
-
- using FieldResolutionBase = std::tuple<uint32_t, uint16_t, dex::StringIndex>;
- struct FieldResolution : public FieldResolutionBase {
- FieldResolution() = default;
- FieldResolution(const FieldResolution&) = default;
- FieldResolution(uint32_t field_idx, uint16_t access_flags, dex::StringIndex declaring_class_idx)
- : FieldResolutionBase(field_idx, access_flags, declaring_class_idx) {}
-
- bool IsResolved() const { return GetAccessFlags() != kUnresolvedMarker; }
- uint32_t GetDexFieldIndex() const { return std::get<0>(*this); }
- uint16_t GetAccessFlags() const { return std::get<1>(*this); }
- dex::StringIndex GetDeclaringClassIndex() const { return std::get<2>(*this); }
- };
-
- using MethodResolutionBase = std::tuple<uint32_t, uint16_t, dex::StringIndex>;
- struct MethodResolution : public MethodResolutionBase {
- MethodResolution() = default;
- MethodResolution(const MethodResolution&) = default;
- MethodResolution(uint32_t method_idx,
- uint16_t access_flags,
- dex::StringIndex declaring_class_idx)
- : MethodResolutionBase(method_idx, access_flags, declaring_class_idx) {}
-
- bool IsResolved() const { return GetAccessFlags() != kUnresolvedMarker; }
- uint32_t GetDexMethodIndex() const { return std::get<0>(*this); }
- uint16_t GetAccessFlags() const { return std::get<1>(*this); }
- dex::StringIndex GetDeclaringClassIndex() const { return std::get<2>(*this); }
- };
-
using TypeAssignabilityBase = std::tuple<dex::StringIndex, dex::StringIndex>;
struct TypeAssignability : public TypeAssignabilityBase {
TypeAssignability() = default;
@@ -223,11 +157,6 @@
std::set<TypeAssignability> assignable_types_;
std::set<TypeAssignability> unassignable_types_;
- // Sets of recorded class/field/method resolutions.
- std::set<ClassResolution> classes_;
- std::set<FieldResolution> fields_;
- std::set<MethodResolution> methods_;
-
// Bit vector indexed by class def indices indicating whether the corresponding
// class was successfully verified.
std::vector<bool> verified_classes_;
@@ -277,46 +206,11 @@
// Returns the string represented by `id`.
std::string GetStringFromId(const DexFile& dex_file, dex::StringIndex string_id) const;
- // Returns the bytecode access flags of `element` (bottom 16 bits), or
- // `kUnresolvedMarker` if `element` is null.
- template <typename Ptr>
- static uint16_t GetAccessFlags(Ptr element)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
- // Returns a string ID of the descriptor of the declaring class of `element`,
- // or `kUnresolvedMarker` if `element` is null.
- dex::StringIndex GetMethodDeclaringClassStringId(const DexFile& dex_file,
- uint32_t dex_method_idx,
- ArtMethod* method)
- REQUIRES_SHARED(Locks::mutator_lock_);
- dex::StringIndex GetFieldDeclaringClassStringId(const DexFile& dex_file,
- uint32_t dex_field_idx,
- ArtField* field)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
// Returns a string ID of the descriptor of the class.
dex::StringIndex GetClassDescriptorStringId(const DexFile& dex_file, ObjPtr<mirror::Class> klass)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::verifier_deps_lock_);
- void AddClassResolution(const DexFile& dex_file,
- dex::TypeIndex type_idx,
- ObjPtr<mirror::Class> klass)
- REQUIRES_SHARED(Locks::mutator_lock_)
- REQUIRES(!Locks::verifier_deps_lock_);
-
- void AddFieldResolution(const DexFile& dex_file,
- uint32_t field_idx,
- ArtField* field)
- REQUIRES_SHARED(Locks::mutator_lock_)
- REQUIRES(!Locks::verifier_deps_lock_);
-
- void AddMethodResolution(const DexFile& dex_file,
- uint32_t method_idx,
- ArtMethod* method)
- REQUIRES_SHARED(Locks::mutator_lock_)
- REQUIRES(!Locks::verifier_deps_lock_);
-
void AddAssignability(const DexFile& dex_file,
ObjPtr<mirror::Class> destination,
ObjPtr<mirror::Class> source,
@@ -366,36 +260,6 @@
/* out */ std::string* error_msg) const
REQUIRES_SHARED(Locks::mutator_lock_);
- // Verify that the set of resolved classes at the point of creation
- // of this `VerifierDeps` is still the same.
- bool VerifyClasses(Handle<mirror::ClassLoader> class_loader,
- const DexFile& dex_file,
- const std::set<ClassResolution>& classes,
- Thread* self,
- /* out */ std::string* error_msg) const
- REQUIRES_SHARED(Locks::mutator_lock_);
-
- // Verify that the set of resolved fields at the point of creation
- // of this `VerifierDeps` is still the same, and each field resolves to the
- // same field holder and access flags.
- bool VerifyFields(Handle<mirror::ClassLoader> class_loader,
- const DexFile& dex_file,
- const std::set<FieldResolution>& classes,
- Thread* self,
- /* out */ std::string* error_msg) const
- REQUIRES_SHARED(Locks::mutator_lock_)
- REQUIRES(!Locks::verifier_deps_lock_);
-
- // Verify that the set of resolved methods at the point of creation
- // of this `VerifierDeps` is still the same, and each method resolves to the
- // same method holder, access flags, and invocation kind.
- bool VerifyMethods(Handle<mirror::ClassLoader> class_loader,
- const DexFile& dex_file,
- const std::set<MethodResolution>& methods,
- Thread* self,
- /* out */ std::string* error_msg) const
- REQUIRES_SHARED(Locks::mutator_lock_);
-
// Map from DexFiles into dependencies collected from verification of their methods.
std::map<const DexFile*, std::unique_ptr<DexFileDeps>> dex_deps_;