Merge "ART: Add OWNERS file"
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index c87abe5..b6ffcc5 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -30,6 +30,10 @@
ErroneousA \
ErroneousB \
ErroneousInit \
+ ForClassLoaderA \
+ ForClassLoaderB \
+ ForClassLoaderC \
+ ForClassLoaderD \
ExceptionHandle \
GetMethodSignature \
ImageLayoutA \
@@ -99,7 +103,7 @@
ART_GTEST_dex2oat_environment_tests_DEX_DEPS := Main MainStripped MultiDex MultiDexModifiedSecondary Nested
ART_GTEST_atomic_method_ref_map_test_DEX_DEPS := Interfaces
-ART_GTEST_class_linker_test_DEX_DEPS := AllFields ErroneousA ErroneousB ErroneousInit Interfaces MethodTypes MultiDex MyClass Nested Statics StaticsFromCode
+ART_GTEST_class_linker_test_DEX_DEPS := AllFields ErroneousA ErroneousB ErroneousInit ForClassLoaderA ForClassLoaderB ForClassLoaderC ForClassLoaderD Interfaces MethodTypes MultiDex MyClass Nested Statics StaticsFromCode
ART_GTEST_class_table_test_DEX_DEPS := XandY
ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod StaticLeafMethods ProfileTestMultiDex
ART_GTEST_dex_cache_test_DEX_DEPS := Main Packages MethodTypes
diff --git a/compiler/Android.bp b/compiler/Android.bp
index a1269dc..62226da 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -429,13 +429,20 @@
shared_libs: [
"libartd-compiler",
- "libartd-simulator",
"libvixld-arm",
"libvixld-arm64",
"libbacktrace",
"libnativeloader",
],
+
+ target: {
+ host: {
+ shared_libs: [
+ "libartd-simulator",
+ ],
+ },
+ },
}
art_cc_test {
diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h
index a04349e..db95bd6 100644
--- a/compiler/driver/compiler_driver-inl.h
+++ b/compiler/driver/compiler_driver-inl.h
@@ -103,16 +103,16 @@
}
inline ArtMethod* CompilerDriver::ResolveMethod(
- ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
- Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit,
- uint32_t method_idx, InvokeType invoke_type, bool check_incompatible_class_change) {
+ ScopedObjectAccess& soa,
+ Handle<mirror::DexCache> dex_cache,
+ Handle<mirror::ClassLoader> class_loader,
+ const DexCompilationUnit* mUnit,
+ uint32_t method_idx,
+ InvokeType invoke_type) {
DCHECK_EQ(class_loader.Get(), mUnit->GetClassLoader().Get());
ArtMethod* resolved_method =
- check_incompatible_class_change
- ? mUnit->GetClassLinker()->ResolveMethod<ClassLinker::kForceICCECheck>(
- *dex_cache->GetDexFile(), method_idx, dex_cache, class_loader, nullptr, invoke_type)
- : mUnit->GetClassLinker()->ResolveMethod<ClassLinker::kNoICCECheckForCache>(
- *dex_cache->GetDexFile(), method_idx, dex_cache, class_loader, nullptr, invoke_type);
+ mUnit->GetClassLinker()->ResolveMethod<ClassLinker::kForceICCECheck>(
+ *dex_cache->GetDexFile(), method_idx, dex_cache, class_loader, nullptr, invoke_type);
if (UNLIKELY(resolved_method == nullptr)) {
DCHECK(soa.Self()->IsExceptionPending());
// Clean up any exception left by type resolution.
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index f834f30..bb64755 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -87,6 +87,10 @@
// Print additional info during profile guided compilation.
static constexpr bool kDebugProfileGuidedCompilation = false;
+// Max encoded fields allowed for initializing app image. Hardcode the number for now
+// because 5000 should be large enough.
+static constexpr uint32_t kMaxEncodedFields = 5000;
+
static double Percentage(size_t x, size_t y) {
return 100.0 * (static_cast<double>(x)) / (static_cast<double>(x + y));
}
@@ -2273,11 +2277,17 @@
}
// Otherwise it's in app image but superclasses can't be initialized, no need to proceed.
old_status = klass->GetStatus();
+
+ bool too_many_encoded_fields = false;
+ if (!is_boot_image && klass->NumStaticFields() > kMaxEncodedFields) {
+ too_many_encoded_fields = true;
+ }
// If the class was not initialized, we can proceed to see if we can initialize static
- // fields.
+ // fields. Limit the max number of encoded fields.
if (!klass->IsInitialized() &&
(is_app_image || is_boot_image) &&
is_superclass_initialized &&
+ !too_many_encoded_fields &&
manager_->GetCompiler()->IsImageClass(descriptor)) {
bool can_init_static_fields = false;
if (is_boot_image) {
@@ -2415,30 +2425,6 @@
}
}
- bool NoPotentialInternStrings(Handle<mirror::Class> klass,
- Handle<mirror::ClassLoader>* class_loader)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- StackHandleScope<1> hs(Thread::Current());
- Handle<mirror::DexCache> h_dex_cache = hs.NewHandle(klass->GetDexCache());
- const DexFile* dex_file = h_dex_cache->GetDexFile();
- const DexFile::ClassDef* class_def = klass->GetClassDef();
- annotations::RuntimeEncodedStaticFieldValueIterator value_it(*dex_file,
- &h_dex_cache,
- class_loader,
- manager_->GetClassLinker(),
- *class_def);
-
- const auto jString = annotations::RuntimeEncodedStaticFieldValueIterator::kString;
- for ( ; value_it.HasNext(); value_it.Next()) {
- if (value_it.GetValueType() == jString) {
- // We don't want cache the static encoded strings which is a potential intern.
- return false;
- }
- }
-
- return true;
- }
-
bool ResolveTypesOfMethods(Thread* self, ArtMethod* m)
REQUIRES_SHARED(Locks::mutator_lock_) {
auto rtn_type = m->GetReturnType(true); // return value is discarded because resolve will be done internally.
@@ -2568,7 +2554,7 @@
}
}
- return NoPotentialInternStrings(klass, class_loader);
+ return true;
}
const ParallelCompilationManager* const manager_;
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index cd4f400..e9e7378 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -254,9 +254,12 @@
// Resolve a method. Returns null on failure, including incompatible class change.
ArtMethod* ResolveMethod(
- ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
- Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit,
- uint32_t method_idx, InvokeType invoke_type, bool check_incompatible_class_change = true)
+ ScopedObjectAccess& soa,
+ Handle<mirror::DexCache> dex_cache,
+ Handle<mirror::ClassLoader> class_loader,
+ const DexCompilationUnit* mUnit,
+ uint32_t method_idx,
+ InvokeType invoke_type)
REQUIRES_SHARED(Locks::mutator_lock_);
void ProcessedInstanceField(bool resolved);
diff --git a/dexlayout/dexlayout.h b/dexlayout/dexlayout.h
index 531bc98..ed011d6 100644
--- a/dexlayout/dexlayout.h
+++ b/dexlayout/dexlayout.h
@@ -58,7 +58,8 @@
bool show_section_headers_ = false;
bool show_section_statistics_ = false;
bool verbose_ = false;
- bool verify_output_ = false;
+ // TODO: Set verify_output_ back to false by default. Was set to true for debugging b/62840842.
+ bool verify_output_ = true;
bool visualize_pattern_ = false;
OutputFormat output_format_ = kOutputPlain;
const char* output_dex_directory_ = nullptr;
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 7aab9de..a19085f 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -55,6 +55,7 @@
#include "gc_root-inl.h"
#include "gc/accounting/card_table-inl.h"
#include "gc/accounting/heap_bitmap-inl.h"
+#include "gc/accounting/space_bitmap-inl.h"
#include "gc/heap.h"
#include "gc/scoped_gc_critical_section.h"
#include "gc/space/image_space.h"
@@ -88,6 +89,7 @@
#include "mirror/method_handles_lookup.h"
#include "mirror/object-inl.h"
#include "mirror/object_array-inl.h"
+#include "mirror/object-refvisitor-inl.h"
#include "mirror/proxy.h"
#include "mirror/reference-inl.h"
#include "mirror/stack_trace_element.h"
@@ -1193,6 +1195,63 @@
gc::accounting::HeapBitmap* const live_bitmap_;
};
+class FixupInternVisitor {
+ public:
+ ALWAYS_INLINE ObjPtr<mirror::Object> TryInsertIntern(mirror::Object* obj) const
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (obj != nullptr && obj->IsString()) {
+ const auto intern = Runtime::Current()->GetInternTable()->InternStrong(obj->AsString());
+ return intern;
+ }
+ return obj;
+ }
+
+ ALWAYS_INLINE void VisitRootIfNonNull(
+ mirror::CompressedReference<mirror::Object>* root) const
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (!root->IsNull()) {
+ VisitRoot(root);
+ }
+ }
+
+ ALWAYS_INLINE void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ root->Assign(TryInsertIntern(root->AsMirrorPtr()));
+ }
+
+ // Visit Class Fields
+ ALWAYS_INLINE void operator()(ObjPtr<mirror::Object> obj,
+ MemberOffset offset,
+ bool is_static ATTRIBUTE_UNUSED) const
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ // There could be overlap between ranges, we must avoid visiting the same reference twice.
+ // Avoid the class field since we already fixed it up in FixupClassVisitor.
+ if (offset.Uint32Value() != mirror::Object::ClassOffset().Uint32Value()) {
+ // Updating images, don't do a read barrier.
+ // Only string fields are fixed, don't do a verify.
+ mirror::Object* ref = obj->GetFieldObject<mirror::Object, kVerifyNone, kWithoutReadBarrier>(
+ offset);
+ obj->SetFieldObject<false, false>(offset, TryInsertIntern(ref));
+ }
+ }
+
+ void operator()(ObjPtr<mirror::Class> klass ATTRIBUTE_UNUSED,
+ ObjPtr<mirror::Reference> ref) const
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_) {
+ this->operator()(ref, mirror::Reference::ReferentOffset(), false);
+ }
+
+ void operator()(mirror::Object* obj) const
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (obj->IsDexCache()) {
+ obj->VisitReferences<true, kVerifyNone, kWithoutReadBarrier>(*this, *this);
+ } else {
+ // Don't visit native roots for non-dex-cache
+ obj->VisitReferences<false, kVerifyNone, kWithoutReadBarrier>(*this, *this);
+ }
+ }
+};
+
// Copies data from one array to another array at the same position
// if pred returns false. If there is a page of continuous data in
// the src array for which pred consistently returns true then
@@ -1285,6 +1344,7 @@
return false;
}
}
+
// Only add the classes to the class loader after the points where we can return false.
for (size_t i = 0; i < num_dex_caches; i++) {
ObjPtr<mirror::DexCache> dex_cache = dex_caches->Get(i);
@@ -1448,6 +1508,21 @@
}
}
}
+ {
+ // Fixup all the literal strings happens at app images which are supposed to be interned.
+ ScopedTrace timing("Fixup String Intern in image and dex_cache");
+ const auto& image_header = space->GetImageHeader();
+ const auto bitmap = space->GetMarkBitmap(); // bitmap of objects
+ const uint8_t* target_base = space->GetMemMap()->Begin();
+ const ImageSection& objects_section =
+ image_header.GetImageSection(ImageHeader::kSectionObjects);
+
+ uintptr_t objects_begin = reinterpret_cast<uintptr_t>(target_base + objects_section.Offset());
+ uintptr_t objects_end = reinterpret_cast<uintptr_t>(target_base + objects_section.End());
+
+ FixupInternVisitor fixup_intern_visitor;
+ bitmap->VisitMarkedRange(objects_begin, objects_end, fixup_intern_visitor);
+ }
if (*out_forward_dex_cache_array) {
ScopedTrace timing("Fixup ArtMethod dex cache arrays");
FixupArtMethodArrayVisitor visitor(header);
@@ -2410,74 +2485,121 @@
return ClassPathEntry(nullptr, nullptr);
}
+// Returns true if the given class loader is either a PathClassLoader or a DexClassLoader.
+// (they both have the same behaviour with respect to class lockup order)
+static bool IsPathOrDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
+ Handle<mirror::ClassLoader> class_loader)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ mirror::Class* class_loader_class = class_loader->GetClass();
+ return
+ (class_loader_class ==
+ soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader)) ||
+ (class_loader_class ==
+ soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DexClassLoader));
+}
+
+static bool IsDelegateLastClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
+ Handle<mirror::ClassLoader> class_loader)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ mirror::Class* class_loader_class = class_loader->GetClass();
+ return class_loader_class ==
+ soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DelegateLastClassLoader);
+}
+
bool ClassLinker::FindClassInBaseDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
Thread* self,
const char* descriptor,
size_t hash,
Handle<mirror::ClassLoader> class_loader,
ObjPtr<mirror::Class>* result) {
- // Termination case: boot class-loader.
+ // Termination case: boot class loader.
if (IsBootClassLoader(soa, class_loader.Get())) {
- // The boot class loader, search the boot class path.
- ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);
- if (pair.second != nullptr) {
- ObjPtr<mirror::Class> klass = LookupClass(self, descriptor, hash, nullptr);
- if (klass != nullptr) {
- *result = EnsureResolved(self, descriptor, klass);
- } else {
- *result = DefineClass(self,
- descriptor,
- hash,
- ScopedNullHandle<mirror::ClassLoader>(),
- *pair.first,
- *pair.second);
- }
- if (*result == nullptr) {
- CHECK(self->IsExceptionPending()) << descriptor;
- self->ClearException();
- }
+ *result = FindClassInBootClassLoaderClassPath(self, descriptor, hash);
+ return true;
+ }
+
+ if (IsPathOrDexClassLoader(soa, class_loader)) {
+ // For regular path or dex class loader the search order is:
+ // - parent
+ // - class loader dex files
+
+ // Handles as RegisterDexFile may allocate dex caches (and cause thread suspension).
+ StackHandleScope<1> hs(self);
+ Handle<mirror::ClassLoader> h_parent(hs.NewHandle(class_loader->GetParent()));
+ if (!FindClassInBaseDexClassLoader(soa, self, descriptor, hash, h_parent, result)) {
+ return false; // One of the parents is not supported.
+ }
+ if (*result != nullptr) {
+ return true; // Found the class up the chain.
+ }
+
+ // Search the current class loader classpath.
+ *result = FindClassInBaseDexClassLoaderClassPath(soa, descriptor, hash, class_loader);
+ return true;
+ }
+
+ if (IsDelegateLastClassLoader(soa, class_loader)) {
+ // For delegate last, the search order is:
+ // - boot class path
+ // - class loader dex files
+ // - parent
+ *result = FindClassInBootClassLoaderClassPath(self, descriptor, hash);
+ if (*result != nullptr) {
+ return true; // The class is part of the boot class path.
+ }
+
+ *result = FindClassInBaseDexClassLoaderClassPath(soa, descriptor, hash, class_loader);
+ if (*result != nullptr) {
+ return true; // Found the class in the current class loader
+ }
+
+ // Handles as RegisterDexFile may allocate dex caches (and cause thread suspension).
+ StackHandleScope<1> hs(self);
+ Handle<mirror::ClassLoader> h_parent(hs.NewHandle(class_loader->GetParent()));
+ return FindClassInBaseDexClassLoader(soa, self, descriptor, hash, h_parent, result);
+ }
+
+ // Unsupported class loader.
+ *result = nullptr;
+ return false;
+}
+
+// Finds the class in the boot class loader.
+// If the class is found the method returns the resolved class. Otherwise it returns null.
+ObjPtr<mirror::Class> ClassLinker::FindClassInBootClassLoaderClassPath(Thread* self,
+ const char* descriptor,
+ size_t hash) {
+ ObjPtr<mirror::Class> result = nullptr;
+ ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);
+ if (pair.second != nullptr) {
+ ObjPtr<mirror::Class> klass = LookupClass(self, descriptor, hash, nullptr);
+ if (klass != nullptr) {
+ result = EnsureResolved(self, descriptor, klass);
} else {
- *result = nullptr;
+ result = DefineClass(self,
+ descriptor,
+ hash,
+ ScopedNullHandle<mirror::ClassLoader>(),
+ *pair.first,
+ *pair.second);
}
- return true;
- }
-
- // Unsupported class-loader?
- if (soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader) !=
- class_loader->GetClass()) {
- // PathClassLoader is the most common case, so it's the one we check first. For secondary dex
- // files, we also check DexClassLoader here.
- if (soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DexClassLoader) !=
- class_loader->GetClass()) {
- *result = nullptr;
- return false;
+ if (result == nullptr) {
+ CHECK(self->IsExceptionPending()) << descriptor;
+ self->ClearException();
}
}
+ return result;
+}
- // Handles as RegisterDexFile may allocate dex caches (and cause thread suspension).
- StackHandleScope<4> hs(self);
- Handle<mirror::ClassLoader> h_parent(hs.NewHandle(class_loader->GetParent()));
- bool recursive_result = FindClassInBaseDexClassLoader(soa,
- self,
- descriptor,
- hash,
- h_parent,
- result);
+ObjPtr<mirror::Class> ClassLinker::FindClassInBaseDexClassLoaderClassPath(
+ ScopedObjectAccessAlreadyRunnable& soa,
+ const char* descriptor,
+ size_t hash,
+ Handle<mirror::ClassLoader> class_loader) {
+ CHECK(IsPathOrDexClassLoader(soa, class_loader) || IsDelegateLastClassLoader(soa, class_loader))
+ << "Unexpected class loader for descriptor " << descriptor;
- if (!recursive_result) {
- // Something wrong up the chain.
- return false;
- }
-
- if (*result != nullptr) {
- // Found the class up the chain.
- return true;
- }
-
- // Handle this step.
- // Handle as if this is the child PathClassLoader.
- // The class loader is a PathClassLoader which inherits from BaseDexClassLoader.
- // We need to get the DexPathList and loop through it.
+ Thread* self = soa.Self();
ArtField* const cookie_field =
jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie);
ArtField* const dex_file_field =
@@ -2489,10 +2611,11 @@
// DexPathList has an array dexElements of Elements[] which each contain a dex file.
ObjPtr<mirror::Object> dex_elements_obj =
jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList_dexElements)->
- GetObject(dex_path_list);
+ GetObject(dex_path_list);
// Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look
// at the mCookie which is a DexFile vector.
if (dex_elements_obj != nullptr) {
+ StackHandleScope<1> hs(self);
Handle<mirror::ObjectArray<mirror::Object>> dex_elements =
hs.NewHandle(dex_elements_obj->AsObjectArray<mirror::Object>());
for (int32_t i = 0; i < dex_elements->GetLength(); ++i) {
@@ -2518,19 +2641,18 @@
OatDexFile::FindClassDef(*cp_dex_file, descriptor, hash);
if (dex_class_def != nullptr) {
ObjPtr<mirror::Class> klass = DefineClass(self,
- descriptor,
- hash,
- class_loader,
- *cp_dex_file,
- *dex_class_def);
+ descriptor,
+ hash,
+ class_loader,
+ *cp_dex_file,
+ *dex_class_def);
if (klass == nullptr) {
CHECK(self->IsExceptionPending()) << descriptor;
self->ClearException();
// TODO: Is it really right to break here, and not check the other dex files?
- return true;
+ return nullptr;
}
- *result = klass;
- return true;
+ return klass;
}
}
}
@@ -2538,9 +2660,7 @@
}
self->AssertNoPendingException();
}
-
- // Result is still null from the parent call, no need to set it again...
- return true;
+ return nullptr;
}
mirror::Class* ClassLinker::FindClass(Thread* self,
@@ -8640,8 +8760,15 @@
return descriptor;
}
-jobject ClassLinker::CreatePathClassLoader(Thread* self,
- const std::vector<const DexFile*>& dex_files) {
+jobject ClassLinker::CreateWellKnownClassLoader(Thread* self,
+ const std::vector<const DexFile*>& dex_files,
+ jclass loader_class,
+ jobject parent_loader) {
+ CHECK(self->GetJniEnv()->IsSameObject(loader_class,
+ WellKnownClasses::dalvik_system_PathClassLoader) ||
+ self->GetJniEnv()->IsSameObject(loader_class,
+ WellKnownClasses::dalvik_system_DelegateLastClassLoader));
+
// SOAAlreadyRunnable is protected, and we need something to add a global reference.
// We could move the jobject to the callers, but all call-sites do this...
ScopedObjectAccessUnchecked soa(self);
@@ -8677,8 +8804,8 @@
for (const DexFile* dex_file : dex_files) {
StackHandleScope<4> hs2(self);
- // CreatePathClassLoader is only used by gtests. Index 0 of h_long_array is supposed to be the
- // oat file but we can leave it null.
+ // CreateWellKnownClassLoader is only used by gtests and compiler.
+ // Index 0 of h_long_array is supposed to be the oat file but we can leave it null.
Handle<mirror::LongArray> h_long_array = hs2.NewHandle(mirror::LongArray::Alloc(
self,
kDexFileIndexStart + 1));
@@ -8713,36 +8840,44 @@
// Set elements.
dex_elements_field->SetObject<false>(h_dex_path_list.Get(), h_dex_elements.Get());
- // Create PathClassLoader.
- Handle<mirror::Class> h_path_class_class = hs.NewHandle(
- soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader));
- Handle<mirror::Object> h_path_class_loader = hs.NewHandle(
- h_path_class_class->AllocObject(self));
- DCHECK(h_path_class_loader != nullptr);
+ // Create the class loader..
+ Handle<mirror::Class> h_loader_class = hs.NewHandle(soa.Decode<mirror::Class>(loader_class));
+ Handle<mirror::Object> h_class_loader = hs.NewHandle(h_loader_class->AllocObject(self));
+ DCHECK(h_class_loader != nullptr);
// Set DexPathList.
ArtField* path_list_field =
jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList);
DCHECK(path_list_field != nullptr);
- path_list_field->SetObject<false>(h_path_class_loader.Get(), h_dex_path_list.Get());
+ path_list_field->SetObject<false>(h_class_loader.Get(), h_dex_path_list.Get());
// Make a pretend boot-classpath.
// TODO: Should we scan the image?
ArtField* const parent_field =
mirror::Class::FindField(self,
- h_path_class_loader->GetClass(),
+ h_class_loader->GetClass(),
"parent",
"Ljava/lang/ClassLoader;");
DCHECK(parent_field != nullptr);
- ObjPtr<mirror::Object> boot_cl =
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader)->AllocObject(self);
- parent_field->SetObject<false>(h_path_class_loader.Get(), boot_cl);
+
+ ObjPtr<mirror::Object> parent = (parent_loader != nullptr)
+ ? soa.Decode<mirror::ClassLoader>(parent_loader)
+ : soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader)->AllocObject(self);
+ parent_field->SetObject<false>(h_class_loader.Get(), parent);
// Make it a global ref and return.
ScopedLocalRef<jobject> local_ref(
- soa.Env(), soa.Env()->AddLocalReference<jobject>(h_path_class_loader.Get()));
+ soa.Env(), soa.Env()->AddLocalReference<jobject>(h_class_loader.Get()));
return soa.Env()->NewGlobalRef(local_ref.get());
}
+jobject ClassLinker::CreatePathClassLoader(Thread* self,
+ const std::vector<const DexFile*>& dex_files) {
+ return CreateWellKnownClassLoader(self,
+ dex_files,
+ WellKnownClasses::dalvik_system_PathClassLoader,
+ nullptr);
+}
+
void ClassLinker::DropFindArrayClassCache() {
std::fill_n(find_array_class_cache_, kFindArrayCacheSize, GcRoot<mirror::Class>(nullptr));
find_array_class_cache_next_victim_ = 0;
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 1e8125e..de1fefd 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -553,8 +553,24 @@
REQUIRES(!Locks::classlinker_classes_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
- // Creates a GlobalRef PathClassLoader that can be used to load classes from the given dex files.
+ // Creates a GlobalRef PathClassLoader or DelegateLastClassLoader (specified by loader_class)
+ // that can be used to load classes from the given dex files. The parent of the class loader
+ // will be set to `parent_loader`. If `parent_loader` is null the parent will be
+ // the boot class loader.
+ // If class_loader points to a different class than PathClassLoader or DelegateLastClassLoader
+ // this method will abort.
// Note: the objects are not completely set up. Do not use this outside of tests and the compiler.
+ jobject CreateWellKnownClassLoader(Thread* self,
+ const std::vector<const DexFile*>& dex_files,
+ jclass loader_class,
+ jobject parent_loader)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!Locks::dex_lock_);
+
+ // Calls CreateWellKnownClassLoader(self,
+ // dex_files,
+ // WellKnownClasses::dalvik_system_PathClassLoader,
+ // nullptr)
jobject CreatePathClassLoader(Thread* self, const std::vector<const DexFile*>& dex_files)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::dex_lock_);
@@ -819,6 +835,27 @@
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::dex_lock_);
+ // Finds the class in the classpath of the given class loader. It only searches the class loader
+ // dex files and does not recurse into its parent.
+ // The method checks that the provided class loader is either a PathClassLoader or a
+ // DexClassLoader.
+ // If the class is found the method returns the resolved class. Otherwise it returns null.
+ ObjPtr<mirror::Class> FindClassInBaseDexClassLoaderClassPath(
+ ScopedObjectAccessAlreadyRunnable& soa,
+ const char* descriptor,
+ size_t hash,
+ Handle<mirror::ClassLoader> class_loader)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!Locks::dex_lock_);
+
+ // Finds the class in the boot class loader.
+ // If the class is found the method returns the resolved class. Otherwise it returns null.
+ ObjPtr<mirror::Class> FindClassInBootClassLoaderClassPath(Thread* self,
+ const char* descriptor,
+ size_t hash)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!Locks::dex_lock_);
+
// Finds a class by its descriptor, returning NULL if it isn't wasn't loaded
// by the given 'class_loader'. Uses the provided hash for the descriptor.
mirror::Class* LookupClass(Thread* self,
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 684a261..03cc6c5 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -1533,4 +1533,110 @@
ASSERT_TRUE(method1_type.Get() != method2_type.Get());
}
+// Verify that ClassLinker's CreateWellknownClassLoader works as expected
+// by creating a chain of class loaders with various dex files.
+TEST_F(ClassLinkerTest, CreateWellKnownClassLoader) {
+ // LoadDexIn*ClassLoader methods already assert that the parent loader is the expected one.
+ // No need to check again.
+ jobject class_loader_a = LoadDexInPathClassLoader("MyClass", nullptr);
+ jobject class_loader_b = LoadDexInDelegateLastClassLoader("Nested", class_loader_a);
+ jobject class_loader_c = LoadDexInPathClassLoader("MultiDex", class_loader_b);
+ LoadDexInDelegateLastClassLoader("Interfaces", class_loader_c);
+}
+
+class ClassLinkerClassLoaderTest : public ClassLinkerTest {
+ protected:
+ // Verifies that the class identified by the given descriptor is loaded with
+ // the expected_class_loader_obj when search from class_loader_to_search_obj.
+ // When expected_class_loader_obj is null the check will be done against BootClassLoader.
+ void VerifyClassResolution(const std::string& descriptor,
+ jobject class_loader_to_search_obj,
+ jobject expected_class_loader_obj,
+ bool should_find = true) {
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+ StackHandleScope<3> hs(self);
+ Handle<mirror::ClassLoader> class_loader_to_search(
+ hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader_to_search_obj)));
+
+ Handle<mirror::Class> klass = hs.NewHandle(
+ class_linker_->FindClass(soa.Self(), descriptor.c_str(), class_loader_to_search));
+
+ if (!should_find) {
+ if (self->IsExceptionPending()) {
+ self->ClearException();
+ }
+ ASSERT_TRUE(klass == nullptr);
+ } else if (expected_class_loader_obj == nullptr) {
+ ASSERT_TRUE(ClassLinker::IsBootClassLoader(soa, klass->GetClassLoader()));
+ } else {
+ ASSERT_TRUE(klass != nullptr) << descriptor;
+ Handle<mirror::ClassLoader> expected_class_loader(
+ hs.NewHandle(soa.Decode<mirror::ClassLoader>(expected_class_loader_obj)));
+ ASSERT_EQ(klass->GetClassLoader(), expected_class_loader.Get());
+ }
+ }
+};
+
+TEST_F(ClassLinkerClassLoaderTest, CreatePathClassLoader) {
+ jobject class_loader_a = LoadDexInPathClassLoader("ForClassLoaderA", nullptr);
+ VerifyClassResolution("LDefinedInA;", class_loader_a, class_loader_a);
+ VerifyClassResolution("Ljava/lang/String;", class_loader_a, nullptr);
+ VerifyClassResolution("LDefinedInB;", class_loader_a, nullptr, /*should_find*/ false);
+}
+
+TEST_F(ClassLinkerClassLoaderTest, CreateDelegateLastClassLoader) {
+ jobject class_loader_a = LoadDexInDelegateLastClassLoader("ForClassLoaderA", nullptr);
+ VerifyClassResolution("LDefinedInA;", class_loader_a, class_loader_a);
+ VerifyClassResolution("Ljava/lang/String;", class_loader_a, nullptr);
+ VerifyClassResolution("LDefinedInB;", class_loader_a, nullptr, /*should_find*/ false);
+}
+
+TEST_F(ClassLinkerClassLoaderTest, CreateClassLoaderChain) {
+ // The chain is
+ // ClassLoaderA (PathClassLoader, defines: A, AB, AC, AD)
+ // ^
+ // |
+ // ClassLoaderB (DelegateLastClassLoader, defines: B, AB, BC, BD)
+ // ^
+ // |
+ // ClassLoaderC (PathClassLoader, defines: C, AC, BC, CD)
+ // ^
+ // |
+ // ClassLoaderD (DelegateLastClassLoader, defines: D, AD, BD, CD)
+
+ jobject class_loader_a = LoadDexInPathClassLoader("ForClassLoaderA", nullptr);
+ jobject class_loader_b = LoadDexInDelegateLastClassLoader("ForClassLoaderB", class_loader_a);
+ jobject class_loader_c = LoadDexInPathClassLoader("ForClassLoaderC", class_loader_b);
+ jobject class_loader_d = LoadDexInDelegateLastClassLoader("ForClassLoaderD", class_loader_c);
+
+ // Verify exclusive classes (present in only one class loader).
+ VerifyClassResolution("LDefinedInD;", class_loader_d, class_loader_d);
+ VerifyClassResolution("LDefinedInC;", class_loader_d, class_loader_c);
+ VerifyClassResolution("LDefinedInB;", class_loader_d, class_loader_b);
+ VerifyClassResolution("LDefinedInA;", class_loader_d, class_loader_a);
+
+ // Verify classes that are defined in multiple classloader.
+
+ // Classes defined in B should be found in B even if they are defined in A or C because
+ // B is a DelegateLastClassLoader.
+ VerifyClassResolution("LDefinedInAB;", class_loader_d, class_loader_b);
+ VerifyClassResolution("LDefinedInABC;", class_loader_d, class_loader_b);
+ VerifyClassResolution("LDefinedInBC;", class_loader_d, class_loader_b);
+
+ // Classes defined in D should be found in D even if they are defined in parent class loaders
+ // as well because D is a DelegateLastClassLoader.
+ VerifyClassResolution("LDefinedInAD;", class_loader_d, class_loader_d);
+ VerifyClassResolution("LDefinedInBD;", class_loader_d, class_loader_d);
+ VerifyClassResolution("LDefinedInCD;", class_loader_d, class_loader_d);
+
+
+ // Classes not defined in the DelegateLastClassLoaders (i.e. D or B) should be found
+ // in the top parent.
+ VerifyClassResolution("LDefinedInAC;", class_loader_d, class_loader_a);
+
+ // Sanity check that we don't find an undefined class.
+ VerifyClassResolution("LNotDefined;", class_loader_d, nullptr, /*should_find*/ false);
+}
+
} // namespace art
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index 6441a44..659c7e4 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -680,19 +680,66 @@
}
jobject CommonRuntimeTestImpl::LoadDex(const char* dex_name) {
- std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles(dex_name);
+ jobject class_loader = LoadDexInPathClassLoader(dex_name, nullptr);
+ Thread::Current()->SetClassLoaderOverride(class_loader);
+ return class_loader;
+}
+
+jobject CommonRuntimeTestImpl::LoadDexInWellKnownClassLoader(const std::string& dex_name,
+ jclass loader_class,
+ jobject parent_loader) {
+ std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles(dex_name.c_str());
std::vector<const DexFile*> class_path;
CHECK_NE(0U, dex_files.size());
for (auto& dex_file : dex_files) {
class_path.push_back(dex_file.get());
loaded_dex_files_.push_back(std::move(dex_file));
}
-
Thread* self = Thread::Current();
- jobject class_loader = Runtime::Current()->GetClassLinker()->CreatePathClassLoader(self,
- class_path);
- self->SetClassLoaderOverride(class_loader);
- return class_loader;
+ ScopedObjectAccess soa(self);
+
+ jobject result = Runtime::Current()->GetClassLinker()->CreateWellKnownClassLoader(
+ self,
+ class_path,
+ loader_class,
+ parent_loader);
+
+ {
+ // Verify we build the correct chain.
+
+ ObjPtr<mirror::ClassLoader> actual_class_loader = soa.Decode<mirror::ClassLoader>(result);
+ // Verify that the result has the correct class.
+ CHECK_EQ(soa.Decode<mirror::Class>(loader_class), actual_class_loader->GetClass());
+ // Verify that the parent is not null. The boot class loader will be set up as a
+ // proper object.
+ ObjPtr<mirror::ClassLoader> actual_parent(actual_class_loader->GetParent());
+ CHECK(actual_parent != nullptr);
+
+ if (parent_loader != nullptr) {
+ // We were given a parent. Verify that it's what we expect.
+ ObjPtr<mirror::ClassLoader> expected_parent = soa.Decode<mirror::ClassLoader>(parent_loader);
+ CHECK_EQ(expected_parent, actual_parent);
+ } else {
+ // No parent given. The parent must be the BootClassLoader.
+ CHECK(Runtime::Current()->GetClassLinker()->IsBootClassLoader(soa, actual_parent));
+ }
+ }
+
+ return result;
+}
+
+jobject CommonRuntimeTestImpl::LoadDexInPathClassLoader(const std::string& dex_name,
+ jobject parent_loader) {
+ return LoadDexInWellKnownClassLoader(dex_name,
+ WellKnownClasses::dalvik_system_PathClassLoader,
+ parent_loader);
+}
+
+jobject CommonRuntimeTestImpl::LoadDexInDelegateLastClassLoader(const std::string& dex_name,
+ jobject parent_loader) {
+ return LoadDexInWellKnownClassLoader(dex_name,
+ WellKnownClasses::dalvik_system_DelegateLastClassLoader,
+ parent_loader);
}
std::string CommonRuntimeTestImpl::GetCoreFileLocation(const char* suffix) {
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index 3b3e6c5..5893573 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -134,10 +134,20 @@
std::unique_ptr<const DexFile> OpenTestDexFile(const char* name);
+ // Loads the test dex file identified by the given dex_name into a PathClassLoader.
+ // Returns the created class loader.
jobject LoadDex(const char* dex_name) REQUIRES_SHARED(Locks::mutator_lock_);
+ // Loads the test dex file identified by the given first_dex_name and second_dex_name
+ // into a PathClassLoader. Returns the created class loader.
jobject LoadMultiDex(const char* first_dex_name, const char* second_dex_name)
REQUIRES_SHARED(Locks::mutator_lock_);
+ jobject LoadDexInPathClassLoader(const std::string& dex_name, jobject parent_loader);
+ jobject LoadDexInDelegateLastClassLoader(const std::string& dex_name, jobject parent_loader);
+ jobject LoadDexInWellKnownClassLoader(const std::string& dex_name,
+ jclass loader_class,
+ jobject parent_loader);
+
std::string android_data_;
std::string dalvik_cache_;
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index 37734e8..6547299 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -376,6 +376,7 @@
mirror::Class* referring_class = referrer->GetDeclaringClass();
if (UNLIKELY(!referring_class->CheckResolvedFieldAccess(fields_class,
resolved_field,
+ referrer->GetDexCache(),
field_idx))) {
DCHECK(self->IsExceptionPending()); // Throw exception and unwind.
return nullptr; // Failure.
@@ -461,9 +462,11 @@
} else if (access_check) {
mirror::Class* methods_class = resolved_method->GetDeclaringClass();
bool can_access_resolved_method =
- referrer->GetDeclaringClass()->CheckResolvedMethodAccess<type>(methods_class,
- resolved_method,
- method_idx);
+ referrer->GetDeclaringClass()->CheckResolvedMethodAccess(methods_class,
+ resolved_method,
+ referrer->GetDexCache(),
+ method_idx,
+ type);
if (UNLIKELY(!can_access_resolved_method)) {
DCHECK(self->IsExceptionPending()); // Throw exception and unwind.
return nullptr; // Failure.
@@ -662,7 +665,7 @@
return nullptr;
}
}
- mirror::Class* referring_class = referrer->GetDeclaringClass();
+ ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass();
if (UNLIKELY(!referring_class->CanAccess(fields_class) ||
!referring_class->CanAccessMember(fields_class, resolved_field->GetAccessFlags()) ||
(is_set && resolved_field->IsFinal() && (fields_class != referring_class)))) {
@@ -677,18 +680,17 @@
}
// Fast path method resolution that can't throw exceptions.
+template <InvokeType type, bool access_check>
inline ArtMethod* FindMethodFast(uint32_t method_idx,
ObjPtr<mirror::Object> this_object,
- ArtMethod* referrer,
- bool access_check,
- InvokeType type) {
+ ArtMethod* referrer) {
ScopedAssertNoThreadSuspension ants(__FUNCTION__);
if (UNLIKELY(this_object == nullptr && type != kStatic)) {
return nullptr;
}
- mirror::Class* referring_class = referrer->GetDeclaringClass();
- ArtMethod* resolved_method =
- referrer->GetDexCache()->GetResolvedMethod(method_idx, kRuntimePointerSize);
+ ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass();
+ ObjPtr<mirror::DexCache> dex_cache = referrer->GetDexCache();
+ ArtMethod* resolved_method = dex_cache->GetResolvedMethod(method_idx, kRuntimePointerSize);
if (UNLIKELY(resolved_method == nullptr)) {
return nullptr;
}
@@ -698,7 +700,7 @@
if (UNLIKELY(icce)) {
return nullptr;
}
- mirror::Class* methods_class = resolved_method->GetDeclaringClass();
+ ObjPtr<mirror::Class> methods_class = resolved_method->GetDeclaringClass();
if (UNLIKELY(!referring_class->CanAccess(methods_class) ||
!referring_class->CanAccessMember(methods_class,
resolved_method->GetAccessFlags()))) {
@@ -713,7 +715,6 @@
return resolved_method;
} else if (type == kSuper) {
// TODO This lookup is rather slow.
- ObjPtr<mirror::DexCache> dex_cache = referrer->GetDexCache();
dex::TypeIndex method_type_idx = dex_cache->GetDexFile()->GetMethodId(method_idx).class_idx_;
ObjPtr<mirror::Class> method_reference_class = ClassLinker::LookupResolvedType(
method_type_idx, dex_cache, referrer->GetClassLoader());
@@ -727,7 +728,7 @@
if (!method_reference_class->IsAssignableFrom(referring_class)) {
return nullptr;
}
- mirror::Class* super_class = referring_class->GetSuperClass();
+ ObjPtr<mirror::Class> super_class = referring_class->GetSuperClass();
if (resolved_method->GetMethodIndex() >= super_class->GetVTableLength()) {
// The super class does not have the method.
return nullptr;
diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h
index eed08aa..fe85887 100644
--- a/runtime/entrypoints/entrypoint_utils.h
+++ b/runtime/entrypoints/entrypoint_utils.h
@@ -137,11 +137,10 @@
REQUIRES_SHARED(Locks::mutator_lock_);
// Fast path method resolution that can't throw exceptions.
+template <InvokeType type, bool access_check>
inline ArtMethod* FindMethodFast(uint32_t method_idx,
ObjPtr<mirror::Object> this_object,
- ArtMethod* referrer,
- bool access_check,
- InvokeType type)
+ ArtMethod* referrer)
REQUIRES_SHARED(Locks::mutator_lock_);
inline mirror::Class* ResolveVerifyAndClinit(dex::TypeIndex type_idx,
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 2c99aeb..36885d8 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -2363,7 +2363,7 @@
// It is valid to use this, as at the usage points here (returns from C functions) we are assuming
// to hold the mutator lock (see REQUIRES_SHARED(Locks::mutator_lock_) annotations).
-template<InvokeType type, bool access_check>
+template <InvokeType type, bool access_check>
static TwoWordReturn artInvokeCommon(uint32_t method_idx,
ObjPtr<mirror::Object> this_object,
Thread* self,
@@ -2371,7 +2371,7 @@
ScopedQuickEntrypointChecks sqec(self);
DCHECK_EQ(*sp, Runtime::Current()->GetCalleeSaveMethod(CalleeSaveType::kSaveRefsAndArgs));
ArtMethod* caller_method = QuickArgumentVisitor::GetCallingMethod(sp);
- ArtMethod* method = FindMethodFast(method_idx, this_object, caller_method, access_check, type);
+ ArtMethod* method = FindMethodFast<type, access_check>(method_idx, this_object, caller_method);
if (UNLIKELY(method == nullptr)) {
const DexFile* dex_file = caller_method->GetDeclaringClass()->GetDexCache()->GetDexFile();
uint32_t shorty_len;
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index b57e2b2..0687b75 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -951,13 +951,20 @@
// Test whether to use the interpreter or compiler entrypoint, and save that result to pass to
// PerformCall. A deoptimization could occur at any time, and we shouldn't change which
// entrypoint to use once we start building the shadow frame.
- bool use_interpreter_entrypoint = ClassLinker::ShouldUseInterpreterEntrypoint(
- called_method, called_method->GetEntryPointFromQuickCompiledCode());
+
+ // For unstarted runtimes, always use the interpreter entrypoint. This fixes the case where we are
+ // doing cross compilation. Note that GetEntryPointFromQuickCompiledCode doesn't use the image
+ // pointer size here and this may case an overflow if it is called from the compiler. b/62402160
+ const bool use_interpreter_entrypoint = !Runtime::Current()->IsStarted() ||
+ ClassLinker::ShouldUseInterpreterEntrypoint(
+ called_method,
+ called_method->GetEntryPointFromQuickCompiledCode());
if (LIKELY(code_item != nullptr)) {
// When transitioning to compiled code, space only needs to be reserved for the input registers.
// The rest of the frame gets discarded. This also prevents accessing the called method's code
// item, saving memory by keeping code items of compiled code untouched.
- if (Runtime::Current()->IsStarted() && !use_interpreter_entrypoint) {
+ if (!use_interpreter_entrypoint) {
+ DCHECK(!Runtime::Current()->IsAotCompiler()) << "Compiler should use interpreter entrypoint";
num_regs = number_of_inputs;
} else {
num_regs = code_item->registers_size_;
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 12baf38..419a4db 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -99,7 +99,7 @@
inline uint32_t Class::GetCopiedMethodsStartOffset() {
// Object::GetFieldShort returns an int16_t value, but
// Class::copied_methods_offset_ is an uint16_t value; cast the
- // latter to int16_t before returning it as an uint32_t value, so
+ // latter to uint16_t before returning it as an uint32_t value, so
// that uint16_t values between 2^15 and 2^16-1 are correctly
// handled.
return static_cast<uint16_t>(
@@ -113,7 +113,7 @@
inline uint32_t Class::GetVirtualMethodsStartOffset() {
// Object::GetFieldShort returns an int16_t value, but
// Class::virtual_method_offset_ is an uint16_t value; cast the
- // latter to int16_t before returning it as an uint32_t value, so
+ // latter to uint16_t before returning it as an uint32_t value, so
// that uint16_t values between 2^15 and 2^16-1 are correctly
// handled.
return static_cast<uint16_t>(
@@ -410,25 +410,24 @@
return IsArrayAssignableFromArray(src);
}
-template <bool throw_on_failure, bool use_referrers_cache>
+template <bool throw_on_failure>
inline bool Class::ResolvedFieldAccessTest(ObjPtr<Class> access_to,
ArtField* field,
- uint32_t field_idx,
- ObjPtr<DexCache> dex_cache) {
- DCHECK_EQ(use_referrers_cache, dex_cache == nullptr);
+ ObjPtr<DexCache> dex_cache,
+ uint32_t field_idx) {
+ DCHECK(dex_cache != nullptr);
if (UNLIKELY(!this->CanAccess(access_to))) {
// The referrer class can't access the field's declaring class but may still be able
// to access the field if the FieldId specifies an accessible subclass of the declaring
// class rather than the declaring class itself.
- ObjPtr<DexCache> referrer_dex_cache = use_referrers_cache ? this->GetDexCache() : dex_cache;
- dex::TypeIndex class_idx = referrer_dex_cache->GetDexFile()->GetFieldId(field_idx).class_idx_;
+ dex::TypeIndex class_idx = dex_cache->GetDexFile()->GetFieldId(field_idx).class_idx_;
// The referenced class has already been resolved with the field, but may not be in the dex
// cache. Use LookupResolveType here to search the class table if it is not in the dex cache.
// should be no thread suspension due to the class being resolved.
ObjPtr<Class> dex_access_to = Runtime::Current()->GetClassLinker()->LookupResolvedType(
- *referrer_dex_cache->GetDexFile(),
+ *dex_cache->GetDexFile(),
class_idx,
- referrer_dex_cache,
+ dex_cache,
access_to->GetClassLoader());
DCHECK(dex_access_to != nullptr);
if (UNLIKELY(!this->CanAccess(dex_access_to))) {
@@ -447,25 +446,25 @@
return false;
}
-template <bool throw_on_failure, bool use_referrers_cache, InvokeType throw_invoke_type>
+template <bool throw_on_failure>
inline bool Class::ResolvedMethodAccessTest(ObjPtr<Class> access_to,
ArtMethod* method,
+ ObjPtr<DexCache> dex_cache,
uint32_t method_idx,
- ObjPtr<DexCache> dex_cache) {
- static_assert(throw_on_failure || throw_invoke_type == kStatic, "Non-default throw invoke type");
- DCHECK_EQ(use_referrers_cache, dex_cache == nullptr);
+ InvokeType throw_invoke_type) {
+ DCHECK(throw_on_failure || throw_invoke_type == kStatic);
+ DCHECK(dex_cache != nullptr);
if (UNLIKELY(!this->CanAccess(access_to))) {
// The referrer class can't access the method's declaring class but may still be able
// to access the method if the MethodId specifies an accessible subclass of the declaring
// class rather than the declaring class itself.
- ObjPtr<DexCache> referrer_dex_cache = use_referrers_cache ? this->GetDexCache() : dex_cache;
- dex::TypeIndex class_idx = referrer_dex_cache->GetDexFile()->GetMethodId(method_idx).class_idx_;
+ dex::TypeIndex class_idx = dex_cache->GetDexFile()->GetMethodId(method_idx).class_idx_;
// The referenced class has already been resolved with the method, but may not be in the dex
// cache.
ObjPtr<Class> dex_access_to = Runtime::Current()->GetClassLinker()->LookupResolvedType(
- *referrer_dex_cache->GetDexFile(),
+ *dex_cache->GetDexFile(),
class_idx,
- referrer_dex_cache,
+ dex_cache,
access_to->GetClassLoader());
DCHECK(dex_access_to != nullptr);
if (UNLIKELY(!this->CanAccess(dex_access_to))) {
@@ -491,30 +490,30 @@
ArtField* field,
ObjPtr<DexCache> dex_cache,
uint32_t field_idx) {
- return ResolvedFieldAccessTest<false, false>(access_to, field, field_idx, dex_cache);
+ return ResolvedFieldAccessTest<false>(access_to, field, dex_cache, field_idx);
}
inline bool Class::CheckResolvedFieldAccess(ObjPtr<Class> access_to,
ArtField* field,
+ ObjPtr<DexCache> dex_cache,
uint32_t field_idx) {
- return ResolvedFieldAccessTest<true, true>(access_to, field, field_idx, nullptr);
+ return ResolvedFieldAccessTest<true>(access_to, field, dex_cache, field_idx);
}
inline bool Class::CanAccessResolvedMethod(ObjPtr<Class> access_to,
ArtMethod* method,
ObjPtr<DexCache> dex_cache,
uint32_t method_idx) {
- return ResolvedMethodAccessTest<false, false, kStatic>(access_to, method, method_idx, dex_cache);
+ return ResolvedMethodAccessTest<false>(access_to, method, dex_cache, method_idx, kStatic);
}
-template <InvokeType throw_invoke_type>
inline bool Class::CheckResolvedMethodAccess(ObjPtr<Class> access_to,
ArtMethod* method,
- uint32_t method_idx) {
- return ResolvedMethodAccessTest<true, true, throw_invoke_type>(access_to,
- method,
- method_idx,
- nullptr);
+ ObjPtr<DexCache> dex_cache,
+ uint32_t method_idx,
+ InvokeType throw_invoke_type) {
+ return ResolvedMethodAccessTest<true>(
+ access_to, method, dex_cache, method_idx, throw_invoke_type);
}
inline bool Class::IsSubClass(ObjPtr<Class> klass) {
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 61d6e05..00498bc 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -643,7 +643,10 @@
ObjPtr<DexCache> dex_cache,
uint32_t field_idx)
REQUIRES_SHARED(Locks::mutator_lock_);
- bool CheckResolvedFieldAccess(ObjPtr<Class> access_to, ArtField* field, uint32_t field_idx)
+ bool CheckResolvedFieldAccess(ObjPtr<Class> access_to,
+ ArtField* field,
+ ObjPtr<DexCache> dex_cache,
+ uint32_t field_idx)
REQUIRES_SHARED(Locks::mutator_lock_);
// Can this class access a resolved method?
@@ -654,10 +657,11 @@
ObjPtr<DexCache> dex_cache,
uint32_t method_idx)
REQUIRES_SHARED(Locks::mutator_lock_);
- template <InvokeType throw_invoke_type>
bool CheckResolvedMethodAccess(ObjPtr<Class> access_to,
ArtMethod* resolved_method,
- uint32_t method_idx)
+ ObjPtr<DexCache> dex_cache,
+ uint32_t method_idx,
+ InvokeType throw_invoke_type)
REQUIRES_SHARED(Locks::mutator_lock_);
bool IsSubClass(ObjPtr<Class> klass) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -1352,18 +1356,19 @@
uint32_t end_offset)
REQUIRES_SHARED(Locks::mutator_lock_);
- template <bool throw_on_failure, bool use_referrers_cache>
+ template <bool throw_on_failure>
bool ResolvedFieldAccessTest(ObjPtr<Class> access_to,
ArtField* field,
- uint32_t field_idx,
- ObjPtr<DexCache> dex_cache)
+ ObjPtr<DexCache> dex_cache,
+ uint32_t field_idx)
REQUIRES_SHARED(Locks::mutator_lock_);
- template <bool throw_on_failure, bool use_referrers_cache, InvokeType throw_invoke_type>
+ template <bool throw_on_failure>
bool ResolvedMethodAccessTest(ObjPtr<Class> access_to,
ArtMethod* resolved_method,
+ ObjPtr<DexCache> dex_cache,
uint32_t method_idx,
- ObjPtr<DexCache> dex_cache)
+ InvokeType throw_invoke_type)
REQUIRES_SHARED(Locks::mutator_lock_);
bool Implements(ObjPtr<Class> klass) REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/mirror/dex_cache_test.cc b/runtime/mirror/dex_cache_test.cc
index a110ed7..5b1ba8d 100644
--- a/runtime/mirror/dex_cache_test.cc
+++ b/runtime/mirror/dex_cache_test.cc
@@ -106,12 +106,12 @@
EXPECT_NE(klass1->NumStaticFields(), 0u);
for (ArtField& field : klass2->GetSFields()) {
- EXPECT_FALSE((
- klass1->ResolvedFieldAccessTest</*throw_on_failure*/ false,
- /*use_referrers_cache*/ false>(klass2.Get(),
- &field,
- field.GetDexFieldIndex(),
- klass1->GetDexCache())));
+ EXPECT_FALSE(
+ klass1->ResolvedFieldAccessTest</*throw_on_failure*/ false>(
+ klass2.Get(),
+ &field,
+ klass1->GetDexCache(),
+ field.GetDexFieldIndex()));
}
}
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index 21239fe..505e844 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -913,12 +913,12 @@
}
static jvmtiError GetBytecodes(jvmtiEnv* env,
- jmethodID method ATTRIBUTE_UNUSED,
- jint* bytecode_count_ptr ATTRIBUTE_UNUSED,
- unsigned char** bytecodes_ptr ATTRIBUTE_UNUSED) {
+ jmethodID method,
+ jint* bytecode_count_ptr,
+ unsigned char** bytecodes_ptr) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_get_bytecodes);
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::GetBytecodes(env, method, bytecode_count_ptr, bytecodes_ptr);
}
static jvmtiError IsMethodNative(jvmtiEnv* env, jmethodID method, jboolean* is_native_ptr) {
diff --git a/runtime/openjdkjvmti/art_jvmti.h b/runtime/openjdkjvmti/art_jvmti.h
index 2d5d527..c63e502 100644
--- a/runtime/openjdkjvmti/art_jvmti.h
+++ b/runtime/openjdkjvmti/art_jvmti.h
@@ -216,7 +216,7 @@
.can_tag_objects = 1,
.can_generate_field_modification_events = 1,
.can_generate_field_access_events = 1,
- .can_get_bytecodes = 0,
+ .can_get_bytecodes = 1,
.can_get_synthetic_attribute = 1,
.can_get_owned_monitor_info = 0,
.can_get_current_contended_monitor = 0,
diff --git a/runtime/openjdkjvmti/ti_method.cc b/runtime/openjdkjvmti/ti_method.cc
index beb639e..9b5b964 100644
--- a/runtime/openjdkjvmti/ti_method.cc
+++ b/runtime/openjdkjvmti/ti_method.cc
@@ -91,6 +91,40 @@
runtime->GetRuntimeCallbacks()->RemoveMethodCallback(&gMethodCallback);
}
+jvmtiError MethodUtil::GetBytecodes(jvmtiEnv* env,
+ jmethodID method,
+ jint* size_ptr,
+ unsigned char** bytecode_ptr) {
+ if (method == nullptr) {
+ return ERR(INVALID_METHODID);
+ }
+ art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
+
+ if (art_method->IsNative()) {
+ return ERR(NATIVE_METHOD);
+ }
+
+ if (size_ptr == nullptr || bytecode_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ const art::DexFile::CodeItem* code_item = art_method->GetCodeItem();
+ if (code_item == nullptr) {
+ *size_ptr = 0;
+ *bytecode_ptr = nullptr;
+ return OK;
+ }
+ // 2 bytes per instruction for dex code.
+ *size_ptr = code_item->insns_size_in_code_units_ * 2;
+ jvmtiError err = env->Allocate(*size_ptr, bytecode_ptr);
+ if (err != OK) {
+ return err;
+ }
+ memcpy(*bytecode_ptr, code_item->insns_, *size_ptr);
+ return OK;
+}
+
jvmtiError MethodUtil::GetArgumentsSize(jvmtiEnv* env ATTRIBUTE_UNUSED,
jmethodID method,
jint* size_ptr) {
diff --git a/runtime/openjdkjvmti/ti_method.h b/runtime/openjdkjvmti/ti_method.h
index cc161c8..d95a81b 100644
--- a/runtime/openjdkjvmti/ti_method.h
+++ b/runtime/openjdkjvmti/ti_method.h
@@ -44,6 +44,11 @@
static void Register(EventHandler* event_handler);
static void Unregister();
+ static jvmtiError GetBytecodes(jvmtiEnv* env,
+ jmethodID method,
+ jint* count_ptr,
+ unsigned char** bytecodes);
+
static jvmtiError GetArgumentsSize(jvmtiEnv* env, jmethodID method, jint* size_ptr);
static jvmtiError GetMaxLocals(jvmtiEnv* env, jmethodID method, jint* max_ptr);
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 4669e16..36ecd33 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -573,7 +573,14 @@
// Use a large local volatile array to ensure a large frame size. Do not use anything close
// to a full page for ASAN. It would be nice to ensure the frame size is at most a page, but
// there is no pragma support for this.
- volatile char space[kPageSize - 256];
+ // Note: for ASAN we need to shrink the array a bit, as there's other overhead.
+ constexpr size_t kAsanMultiplier =
+#ifdef ADDRESS_SANITIZER
+ 2u;
+#else
+ 1u;
+#endif
+ volatile char space[kPageSize - (kAsanMultiplier * 256)];
char sink ATTRIBUTE_UNUSED = space[zero];
if (reinterpret_cast<uintptr_t>(space) >= target + kPageSize) {
Touch(target);
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 24f194b..8d505e2 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -37,6 +37,7 @@
jclass WellKnownClasses::dalvik_annotation_optimization_CriticalNative;
jclass WellKnownClasses::dalvik_annotation_optimization_FastNative;
jclass WellKnownClasses::dalvik_system_BaseDexClassLoader;
+jclass WellKnownClasses::dalvik_system_DelegateLastClassLoader;
jclass WellKnownClasses::dalvik_system_DexClassLoader;
jclass WellKnownClasses::dalvik_system_DexFile;
jclass WellKnownClasses::dalvik_system_DexPathList;
@@ -270,6 +271,7 @@
CacheClass(env, "dalvik/annotation/optimization/CriticalNative");
dalvik_annotation_optimization_FastNative = CacheClass(env, "dalvik/annotation/optimization/FastNative");
dalvik_system_BaseDexClassLoader = CacheClass(env, "dalvik/system/BaseDexClassLoader");
+ dalvik_system_DelegateLastClassLoader = CacheClass(env, "dalvik/system/DelegateLastClassLoader");
dalvik_system_DexClassLoader = CacheClass(env, "dalvik/system/DexClassLoader");
dalvik_system_DexFile = CacheClass(env, "dalvik/system/DexFile");
dalvik_system_DexPathList = CacheClass(env, "dalvik/system/DexPathList");
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index c184731..c5a16c1 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -47,6 +47,7 @@
static jclass dalvik_annotation_optimization_CriticalNative;
static jclass dalvik_annotation_optimization_FastNative;
static jclass dalvik_system_BaseDexClassLoader;
+ static jclass dalvik_system_DelegateLastClassLoader;
static jclass dalvik_system_DexClassLoader;
static jclass dalvik_system_DexFile;
static jclass dalvik_system_DexPathList;
diff --git a/test/141-class-unload/src/Main.java b/test/141-class-unload/src/Main.java
index 9072c8b..3cfe006 100644
--- a/test/141-class-unload/src/Main.java
+++ b/test/141-class-unload/src/Main.java
@@ -65,7 +65,8 @@
String line;
int count = 0;
while ((line = reader.readLine()) != null) {
- if (line.contains("@141-class-unload-ex.jar")) {
+ if (line.contains("141-class-unload-ex.odex") ||
+ line.contains("141-class-unload-ex.vdex")) {
System.out.println(line);
++count;
}
diff --git a/test/1901-get-bytecodes/bytecodes.cc b/test/1901-get-bytecodes/bytecodes.cc
new file mode 100644
index 0000000..edcb734
--- /dev/null
+++ b/test/1901-get-bytecodes/bytecodes.cc
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#include <iostream>
+#include <pthread.h>
+#include <stdio.h>
+#include <vector>
+
+#include "android-base/logging.h"
+#include "jni.h"
+#include "scoped_local_ref.h"
+#include "scoped_primitive_array.h"
+
+#include "jvmti.h"
+
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "test_env.h"
+
+namespace art {
+namespace Test1901Bytecodes {
+
+extern "C" JNIEXPORT jbyteArray JNICALL Java_art_Test1901_getBytecodes(JNIEnv* env,
+ jclass,
+ jobject jmethod) {
+ jmethodID method = env->FromReflectedMethod(jmethod);
+ if (env->ExceptionCheck()) {
+ return nullptr;
+ }
+ unsigned char* bytecodes = nullptr;
+ jint bytecodes_size = 0;
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->GetBytecodes(method, &bytecodes_size, &bytecodes))) {
+ return nullptr;
+ }
+ jbyteArray out = env->NewByteArray(bytecodes_size);
+ if (env->ExceptionCheck()) {
+ return nullptr;
+ } else if (bytecodes_size == 0) {
+ return out;
+ }
+ jbyte* bytes = env->GetByteArrayElements(out, /* is_copy */ nullptr);
+ memcpy(bytes, bytecodes, bytecodes_size);
+ env->ReleaseByteArrayElements(out, bytes, 0);
+ return out;
+}
+
+} // namespace Test1901Bytecodes
+} // namespace art
diff --git a/test/1901-get-bytecodes/expected.txt b/test/1901-get-bytecodes/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/1901-get-bytecodes/expected.txt
diff --git a/test/1901-get-bytecodes/info.txt b/test/1901-get-bytecodes/info.txt
new file mode 100644
index 0000000..c8c9189
--- /dev/null
+++ b/test/1901-get-bytecodes/info.txt
@@ -0,0 +1,3 @@
+Tests basic functions in the jvmti plugin.
+
+Tests that the GetBytecodes function works as expected.
diff --git a/test/1901-get-bytecodes/run b/test/1901-get-bytecodes/run
new file mode 100755
index 0000000..c6e62ae
--- /dev/null
+++ b/test/1901-get-bytecodes/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+./default-run "$@" --jvmti
diff --git a/test/1901-get-bytecodes/src/Main.java b/test/1901-get-bytecodes/src/Main.java
new file mode 100644
index 0000000..d37fcdb
--- /dev/null
+++ b/test/1901-get-bytecodes/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ art.Test1901.run();
+ }
+}
diff --git a/test/1901-get-bytecodes/src/art/Test1901.java b/test/1901-get-bytecodes/src/art/Test1901.java
new file mode 100644
index 0000000..6940491
--- /dev/null
+++ b/test/1901-get-bytecodes/src/art/Test1901.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2016 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 art;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.Base64;
+
+public class Test1901 {
+ // Class & Dex file containing the following class.
+ // Using this representation to prevent any changes to the compiler or the file formats from
+ // changing the output of this test.
+ //
+ // package art;
+ // public class Target {
+ // public void doNothing() {
+ // return;
+ // }
+ //
+ // public static void staticNothing() {
+ // return;
+ // }
+ //
+ // public void sayHi() {
+ // System.out.println("hello");
+ // }
+ // }
+ public static byte[] CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADQAHgoABgAQCQARABIIABMKABQAFQcAFgcAFwEABjxpbml0PgEAAygpVgEABENvZGUB" +
+ "AA9MaW5lTnVtYmVyVGFibGUBAAlkb05vdGhpbmcBAA1zdGF0aWNOb3RoaW5nAQAFc2F5SGkBAApT" +
+ "b3VyY2VGaWxlAQALVGFyZ2V0LmphdmEMAAcACAcAGAwAGQAaAQAFaGVsbG8HABsMABwAHQEACmFy" +
+ "dC9UYXJnZXQBABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxq" +
+ "YXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExq" +
+ "YXZhL2xhbmcvU3RyaW5nOylWACEABQAGAAAAAAAEAAEABwAIAAEACQAAAB0AAQABAAAABSq3AAGx" +
+ "AAAAAQAKAAAABgABAAAAAgABAAsACAABAAkAAAAZAAAAAQAAAAGxAAAAAQAKAAAABgABAAAABAAJ" +
+ "AAwACAABAAkAAAAZAAAAAAAAAAGxAAAAAQAKAAAABgABAAAACAABAA0ACAABAAkAAAAlAAIAAQAA" +
+ "AAmyAAISA7YABLEAAAABAAoAAAAKAAIAAAAMAAgADQABAA4AAAACAA8=");
+ public static byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQAbYkxNjiZ8a+fNWF4smR2+uXbrq88/FNoYAwAAcAAAAHhWNBIAAAAAAAAAAHgCAAAP" +
+ "AAAAcAAAAAYAAACsAAAAAgAAAMQAAAABAAAA3AAAAAYAAADkAAAAAQAAABQBAADkAQAANAEAAJoB" +
+ "AACiAQAAsAEAAMcBAADbAQAA7wEAAAMCAAAQAgAAEwIAABcCAAAiAgAAKQIAAC4CAAA3AgAAPgIA" +
+ "AAEAAAACAAAAAwAAAAQAAAAFAAAABwAAAAcAAAAFAAAAAAAAAAgAAAAFAAAAlAEAAAQAAQALAAAA" +
+ "AAAAAAAAAAAAAAAACQAAAAAAAAANAAAAAAAAAA4AAAABAAEADAAAAAIAAAAAAAAAAAAAAAEAAAAC" +
+ "AAAAAAAAAAYAAAAAAAAAYgIAAAAAAAABAAEAAQAAAE0CAAAEAAAAcBAFAAAADgAAAAAAAAAAAFIC" +
+ "AAABAAAADgAAAAEAAQAAAAAAVwIAAAEAAAAOAAAAAwABAAIAAABcAgAACAAAAGIAAAAaAQoAbiAE" +
+ "ABAADgABAAAAAwAGPGluaXQ+AAxMYXJ0L1RhcmdldDsAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwAS" +
+ "TGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xhbmcvU3lzdGVt" +
+ "OwALVGFyZ2V0LmphdmEAAVYAAlZMAAlkb05vdGhpbmcABWhlbGxvAANvdXQAB3ByaW50bG4ABXNh" +
+ "eUhpAA1zdGF0aWNOb3RoaW5nAAIABw4ACAAHDgAEAAcOAAwABw54AAAAAgIAgYAEtAIDCcwCAQHg" +
+ "AgEB9AINAAAAAAAAAAEAAAAAAAAAAQAAAA8AAABwAAAAAgAAAAYAAACsAAAAAwAAAAIAAADEAAAA" +
+ "BAAAAAEAAADcAAAABQAAAAYAAADkAAAABgAAAAEAAAAUAQAAASAAAAQAAAA0AQAAARAAAAEAAACU" +
+ "AQAAAiAAAA8AAACaAQAAAyAAAAQAAABNAgAAACAAAAEAAABiAgAAABAAAAEAAAB4AgAA");
+
+ public static byte[][] DO_NOTHING_BYTECODES = new byte[][] {
+ // Dex Bytecodes for doNothing
+ // 0e00 |0000: return-void
+ new byte[] { 14, 0 },
+ // Java bytecodes
+ // 0: return
+ new byte[] { -79 },
+ };
+
+ public static byte[][] STATIC_NOTHING_BYTECODES = new byte[][] {
+ // Dex Bytecodes for staticNothing
+ // 0e00 |0000: return-void
+ new byte[] { 14, 0 },
+ // Java bytecodes
+ // 0: return
+ new byte[] { -79 },
+ };
+
+ public static byte[][] SAY_HI_NOTHING_BYTECODES = new byte[][] {
+ // Dex Bytecodes for sayHi
+ // 6200 0000 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0000
+ // 1a01 0a00 |0002: const-string v1, "hello" // string@000a
+ // 6e20 0400 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0004
+ // 0e00 |0007: return-void
+ new byte[] { 98, 0, 0, 0, 26, 1, 10, 0, 110, 32, 4, 0, 16, 0, 14, 0 },
+ // Java bytecodes
+ // 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
+ // 3: ldc #3 // String hello
+ // 5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
+ // 8: return
+ new byte[] { -78, 0, 2, 18, 3, -74, 0, 4, -79 },
+ };
+
+ public static ClassLoader getClassLoader() throws Exception {
+ try {
+ Class<?> class_loader_class = Class.forName("dalvik.system.InMemoryDexClassLoader");
+ Constructor<?> ctor = class_loader_class.getConstructor(ByteBuffer.class, ClassLoader.class);
+ // We are on art since we got the InMemoryDexClassLoader.
+ return (ClassLoader)ctor.newInstance(
+ ByteBuffer.wrap(DEX_BYTES), Test1901.class.getClassLoader());
+ } catch (ClassNotFoundException e) {
+ // Running on RI.
+ return new ClassLoader(Test1901.class.getClassLoader()) {
+ protected Class<?> findClass(String name) throws ClassNotFoundException {
+ if (name.equals("art.Target")) {
+ return defineClass(name, CLASS_BYTES, 0, CLASS_BYTES.length);
+ } else {
+ return super.findClass(name);
+ }
+ }
+ };
+ }
+ }
+
+ public static void CheckMethodBytes(Method m, byte[][] possible_bytecodes) {
+ byte[] real_codes = getBytecodes(m);
+ for (byte[] pos : possible_bytecodes) {
+ if (Arrays.equals(pos, real_codes)) {
+ return;
+ }
+ }
+ System.out.println("Unexpected bytecodes for " + m);
+ System.out.println("Received: " + Arrays.toString(real_codes));
+ System.out.println("Expected one of:");
+ for (byte[] pos : possible_bytecodes) {
+ System.out.println("\t" + Arrays.toString(pos));
+ }
+ }
+
+ public static void run() throws Exception {
+ Class<?> target = getClassLoader().loadClass("art.Target");
+ CheckMethodBytes(target.getDeclaredMethod("doNothing"), DO_NOTHING_BYTECODES);
+ CheckMethodBytes(target.getDeclaredMethod("staticNothing"), STATIC_NOTHING_BYTECODES);
+ CheckMethodBytes(target.getDeclaredMethod("sayHi"), SAY_HI_NOTHING_BYTECODES);
+ }
+
+ public static native byte[] getBytecodes(Method m);
+}
diff --git a/test/596-app-images/src/Main.java b/test/596-app-images/src/Main.java
index 8ee3c88..88d95f4 100644
--- a/test/596-app-images/src/Main.java
+++ b/test/596-app-images/src/Main.java
@@ -14,6 +14,10 @@
* limitations under the License.
*/
+import java.lang.reflect.Field;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
class Main {
static class Inner {
final public static int abc = 10;
@@ -46,13 +50,76 @@
if (!checkInitialized(StaticFieldsInit.class))
System.out.println("StaticFieldsInit class is not initialized!");
- if (checkInitialized(StaticInternString.class))
- System.out.println("StaticInternString class is initialized!");
+ if (!checkInitialized(StaticInternString.class))
+ System.out.println("StaticInternString class is not initialized!");
+
+ StringBuffer sb = new StringBuffer();
+ sb.append("java.");
+ sb.append("abc.");
+ sb.append("Action");
+
+ String tmp = sb.toString();
+ String intern = tmp.intern();
+
+ assertNotEqual(tmp, intern, "Dynamically constructed String, not interned.");
+ assertEqual(intern, StaticInternString.intent, "Static encoded literal String not interned.");
+ assertEqual(BootInternedString.boot, BootInternedString.boot.intern(),
+ "Static encoded literal String not moved back to runtime intern table.");
+
+ try {
+ Field f = StaticInternString.class.getDeclaredField("intent");
+ assertEqual(intern, f.get(null), "String Literals are not interned properly.");
+
+ } catch (Exception e) {
+ System.out.println("Exception");
+ }
+
+ assertEqual(StaticInternString.getIntent(), StaticInternString2.getIntent(),
+ "String Literals are not intenred properly, App image static strings duplicated.");
+
+ // reload the class StaticInternString, check whether static strings interned properly
+ final String DEX_FILE = System.getenv("DEX_LOCATION") + "/596-app-images.jar";
+ final String LIBRARY_SEARCH_PATH = System.getProperty("java.library.path");
+
+ try {
+ Class<?> pathClassLoader = Class.forName("dalvik.system.PathClassLoader");
+ if (pathClassLoader == null) {
+ throw new AssertionError("Counldn't find path class loader class");
+ }
+ Constructor<?> ctor =
+ pathClassLoader.getDeclaredConstructor(String.class, String.class, ClassLoader.class);
+ ClassLoader loader = (ClassLoader) ctor.newInstance(
+ DEX_FILE, LIBRARY_SEARCH_PATH, null);
+
+ Class<?> staticInternString = loader.loadClass("StaticInternString");
+
+ if (!checkAppImageContains(staticInternString)) {
+ System.out.println("Not loaded again.");
+ }
+ Method getIntent = staticInternString.getDeclaredMethod("getIntent");
+
+ assertEqual(StaticInternString.getIntent(), getIntent.invoke(staticInternString),
+ "Dynamically loaded app image's literal strings not interned properly.");
+ } catch (Exception e) {
+ e.printStackTrace(System.out);
+ }
+
}
public static native boolean checkAppImageLoaded();
public static native boolean checkAppImageContains(Class<?> klass);
public static native boolean checkInitialized(Class<?> klass);
+
+ public static void assertEqual(Object a, Object b, String msg) {
+ if (a != b)
+ System.out.println(msg);
+ }
+
+ public static void assertNotEqual(Object a, Object b, String msg) {
+ if (a == b)
+ System.out.println(msg);
+ }
+
}
class StaticFields{
@@ -68,6 +135,21 @@
}
class StaticInternString {
- final public static String intern = "java.abc.Action";
+ final public static String intent = "java.abc.Action";
+ static public String getIntent() {
+ return intent;
+ }
+}
+
+class BootInternedString {
+ final public static String boot = "double";
+}
+
+class StaticInternString2 {
+ final public static String intent = "java.abc.Action";
+
+ static String getIntent() {
+ return intent;
+ }
}
diff --git a/test/Android.bp b/test/Android.bp
index f893531..7d7afa5 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -286,6 +286,7 @@
"992-source-data/source_file.cc",
"993-breakpoints/breakpoints.cc",
"996-breakpoint-obsolete/obsolete_breakpoints.cc",
+ "1901-get-bytecodes/bytecodes.cc",
],
shared_libs: [
"libbase",
@@ -445,6 +446,7 @@
"art_debug_defaults",
"art_defaults",
],
+ header_libs: ["libnativebridge-dummy-headers"],
srcs: ["115-native-bridge/nativebridge.cc"],
target: {
android: {
diff --git a/test/ForClassLoaderA/Classes.java b/test/ForClassLoaderA/Classes.java
new file mode 100644
index 0000000..a65ef64
--- /dev/null
+++ b/test/ForClassLoaderA/Classes.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+class DefinedInA {
+}
+
+class DefinedInAB {
+}
+
+class DefinedInABC {
+}
+
+class DefinedInAC {
+}
+
+class DefinedInAD {
+}
+
diff --git a/test/ForClassLoaderB/Classes.java b/test/ForClassLoaderB/Classes.java
new file mode 100644
index 0000000..8c85ed5
--- /dev/null
+++ b/test/ForClassLoaderB/Classes.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+class DefinedInB {
+}
+
+class DefinedInAB {
+}
+
+class DefinedInABC {
+}
+
+class DefinedInBC {
+}
+
+class DefinedInBD {
+}
diff --git a/test/ForClassLoaderC/Classes.java b/test/ForClassLoaderC/Classes.java
new file mode 100644
index 0000000..7b9e83f
--- /dev/null
+++ b/test/ForClassLoaderC/Classes.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+class DefinedInC {
+}
+
+class DefinedInAC {
+}
+
+class DefinedInABC {
+}
+
+class DefinedInBC {
+}
+
+class DefinedInCD {
+}
diff --git a/test/ForClassLoaderD/Classes.java b/test/ForClassLoaderD/Classes.java
new file mode 100644
index 0000000..b34177f
--- /dev/null
+++ b/test/ForClassLoaderD/Classes.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+class DefinedInD {
+}
+
+class DefinedInAD {
+}
+
+class DefinedInBD {
+}
+
+class DefinedInCD {
+}
diff --git a/test/knownfailures.json b/test/knownfailures.json
index d3d92c6..7b801e2 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -645,6 +645,11 @@
"env_vars": {"SANITIZE_HOST": "address"}
},
{
+ "tests": "141-class-unload",
+ "description": "Idk why this fails",
+ "env_vars": {"SANITIZE_HOST": "address"}
+ },
+ {
"tests": ["988-method-trace"],
"variant": "redefine-stress | jvmti-stress",
"description": "Test disabled due to redefine-stress disabling intrinsics which changes the trace output slightly."