Merge "Fix darwin run-test problems"
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 1e64c00..9c2cc6c 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -2093,7 +2093,6 @@
// The boot class loader, search the boot class path.
ClassPathEntry pair = FindInClassPath(descriptor, boot_class_path_);
if (pair.second != nullptr) {
- StackHandleScope<1> hs(self);
return DefineClass(descriptor, NullHandle<mirror::ClassLoader>(), *pair.first, *pair.second);
} else {
// The boot class loader is searched ahead of the application class loader, failures are
@@ -2132,6 +2131,87 @@
}
} else {
ScopedObjectAccessUnchecked soa(self);
+ if (class_loader->GetClass() ==
+ soa.Decode<mirror::Class*>(WellKnownClasses::dalvik_system_PathClassLoader) &&
+ class_loader->GetParent()->GetClass() ==
+ soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_BootClassLoader)) {
+ ClassPathEntry pair = FindInClassPath(descriptor, boot_class_path_);
+ // Check if this would be found in the parent boot class loader.
+ if (pair.second != nullptr) {
+ mirror::Class* klass = LookupClass(descriptor, nullptr);
+ if (klass != nullptr) {
+ return klass;
+ }
+ klass = DefineClass(descriptor, NullHandle<mirror::ClassLoader>(), *pair.first,
+ *pair.second);
+ if (klass == nullptr) {
+ CHECK(self->IsExceptionPending()) << descriptor;
+ self->ClearException();
+ } else {
+ return klass;
+ }
+ } else {
+ // RegisterDexFile may allocate dex caches (and cause thread suspension).
+ StackHandleScope<3> hs(self);
+ // The class loader is a PathClassLoader which inherits from BaseDexClassLoader.
+ // We need to get the DexPathList and loop through it.
+ Handle<mirror::ArtField> cookie_field =
+ hs.NewHandle(soa.DecodeField(WellKnownClasses::dalvik_system_DexFile_cookie));
+ Handle<mirror::ArtField> dex_file_field =
+ hs.NewHandle(
+ soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList$Element_dexFile));
+ mirror::Object* dex_path_list =
+ soa.DecodeField(WellKnownClasses::dalvik_system_PathClassLoader_pathList)->
+ GetObject(class_loader.Get());
+ if (dex_path_list != nullptr && dex_file_field.Get() != nullptr &&
+ cookie_field.Get() != nullptr) {
+ // DexPathList has an array dexElements of Elements[] which each contain a dex file.
+ mirror::Object* dex_elements_obj =
+ soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList_dexElements)->
+ 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) {
+ 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) {
+ mirror::Object* element = dex_elements->GetWithoutChecks(i);
+ if (element == nullptr) {
+ // Should never happen, fall back to java code to throw a NPE.
+ break;
+ }
+ mirror::Object* dex_file = dex_file_field->GetObject(element);
+ if (dex_file != nullptr) {
+ const uint64_t cookie = cookie_field->GetLong(dex_file);
+ auto* dex_files =
+ reinterpret_cast<std::vector<const DexFile*>*>(static_cast<uintptr_t>(cookie));
+ if (dex_files == nullptr) {
+ // This should never happen so log a warning.
+ LOG(WARNING) << "Null DexFile::mCookie for " << descriptor;
+ break;
+ }
+ for (const DexFile* dex_file : *dex_files) {
+ const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor);
+ if (dex_class_def != nullptr) {
+ RegisterDexFile(*dex_file);
+ mirror::Class* klass =
+ DefineClass(descriptor, class_loader, *dex_file, *dex_class_def);
+ if (klass == nullptr) {
+ CHECK(self->IsExceptionPending()) << descriptor;
+ self->ClearException();
+ // Exit the loop to make the java code generate an exception.
+ i = dex_elements->GetLength();
+ break;
+ }
+ return klass;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
ScopedLocalRef<jobject> class_loader_object(soa.Env(),
soa.AddLocalReference<jobject>(class_loader.Get()));
std::string class_name_string(DescriptorToDot(descriptor));
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 494781a..2048160 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -120,7 +120,7 @@
desired_collector_type_(foreground_collector_type_),
heap_trim_request_lock_(nullptr),
last_trim_time_(0),
- last_heap_transition_time_(0),
+ heap_transition_or_trim_target_time_(0),
heap_trim_request_pending_(false),
parallel_gc_threads_(parallel_gc_threads),
conc_gc_threads_(conc_gc_threads),
@@ -916,6 +916,35 @@
}
void Heap::DoPendingTransitionOrTrim() {
+ Thread* self = Thread::Current();
+ CollectorType desired_collector_type;
+ // Wait until we reach the desired transition time.
+ while (true) {
+ uint64_t wait_time;
+ {
+ MutexLock mu(self, *heap_trim_request_lock_);
+ desired_collector_type = desired_collector_type_;
+ uint64_t current_time = NanoTime();
+ if (current_time >= heap_transition_or_trim_target_time_) {
+ break;
+ }
+ wait_time = heap_transition_or_trim_target_time_ - current_time;
+ }
+ ScopedThreadStateChange tsc(self, kSleeping);
+ usleep(wait_time / 1000); // Usleep takes microseconds.
+ }
+ // Launch homogeneous space compaction if it is desired.
+ if (desired_collector_type == kCollectorTypeHomogeneousSpaceCompact) {
+ if (!CareAboutPauseTimes()) {
+ PerformHomogeneousSpaceCompact();
+ }
+ // No need to Trim(). Homogeneous space compaction may free more virtual and physical memory.
+ desired_collector_type = collector_type_;
+ return;
+ }
+ // Transition the collector if the desired collector type is not the same as the current
+ // collector type.
+ TransitionCollector(desired_collector_type);
if (!CareAboutPauseTimes()) {
// Deflate the monitors, this can cause a pause but shouldn't matter since we don't care
// about pauses.
@@ -927,23 +956,7 @@
<< PrettyDuration(NanoTime() - start_time);
runtime->GetThreadList()->ResumeAll();
}
- if (NanoTime() - last_heap_transition_time_ > kCollectorTransitionWait) {
- // Launch homogeneous space compaction if it is desired.
- if (desired_collector_type_ == kCollectorTypeHomogeneousSpaceCompact) {
- if (!CareAboutPauseTimes()) {
- PerformHomogeneousSpaceCompact();
- last_heap_transition_time_ = NanoTime();
- }
- desired_collector_type_ = collector_type_;
- } else {
- // Transition the collector if the desired collector type is not the same as the current
- // collector type.
- TransitionCollector(desired_collector_type_);
- last_heap_transition_time_ = NanoTime();
- }
- }
- // Do a heap trim if it is needed. This is good to do even with hspace compaction since it may
- // trim the native heap and dlmalloc spaces.
+ // Do a heap trim if it is needed.
Trim();
}
@@ -2977,6 +2990,8 @@
if (desired_collector_type_ == desired_collector_type) {
return;
}
+ heap_transition_or_trim_target_time_ =
+ std::max(heap_transition_or_trim_target_time_, NanoTime() + delta_time);
desired_collector_type_ = desired_collector_type;
}
SignalHeapTrimDaemon(self);
@@ -2998,7 +3013,10 @@
Thread* self = Thread::Current();
Runtime* runtime = Runtime::Current();
- if (runtime == nullptr || !runtime->IsFinishedStarting() || runtime->IsShuttingDown(self)) {
+ if (runtime == nullptr || !runtime->IsFinishedStarting() || runtime->IsShuttingDown(self) ||
+ runtime->IsZygote()) {
+ // Ignore the request if we are the zygote to prevent app launching lag due to sleep in heap
+ // trimmer daemon. b/17310019
// Heap trimming isn't supported without a Java runtime or Daemons (such as at dex2oat time)
// Also: we do not wish to start a heap trim if the runtime is shutting down (a racy check
// as we don't hold the lock while requesting the trim).
@@ -3012,6 +3030,10 @@
return;
}
heap_trim_request_pending_ = true;
+ uint64_t current_time = NanoTime();
+ if (heap_transition_or_trim_target_time_ < current_time) {
+ heap_transition_or_trim_target_time_ = current_time + kHeapTrimWait;
+ }
}
// Notify the daemon thread which will actually do the heap trim.
SignalHeapTrimDaemon(self);
@@ -3064,8 +3086,6 @@
}
env->CallStaticVoidMethod(WellKnownClasses::java_lang_System,
WellKnownClasses::java_lang_System_runFinalization);
- env->CallStaticVoidMethod(WellKnownClasses::java_lang_System,
- WellKnownClasses::java_lang_System_runFinalization);
}
void Heap::RegisterNativeAllocation(JNIEnv* env, size_t bytes) {
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 3bfa748..9742277 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -830,8 +830,8 @@
Mutex* heap_trim_request_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
// When we want to perform the next heap trim (nano seconds).
uint64_t last_trim_time_ GUARDED_BY(heap_trim_request_lock_);
- // When we last performed a heap transition or hspace compact.
- uint64_t last_heap_transition_time_;
+ // When we want to perform the next heap transition (nano seconds) or heap trim.
+ uint64_t heap_transition_or_trim_target_time_ GUARDED_BY(heap_trim_request_lock_);
// If we have a heap trim request pending.
bool heap_trim_request_pending_ GUARDED_BY(heap_trim_request_lock_);
diff --git a/runtime/mirror/class_loader.h b/runtime/mirror/class_loader.h
index f3594e4..ff2ad89 100644
--- a/runtime/mirror/class_loader.h
+++ b/runtime/mirror/class_loader.h
@@ -32,6 +32,9 @@
static constexpr uint32_t InstanceSize() {
return sizeof(ClassLoader);
}
+ ClassLoader* GetParent() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return GetFieldObject<ClassLoader>(OFFSET_OF_OBJECT_MEMBER(ClassLoader, parent_));
+ }
private:
// Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 3a6a72b..7068a4d 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -26,7 +26,11 @@
namespace art {
jclass WellKnownClasses::com_android_dex_Dex;
+jclass WellKnownClasses::dalvik_system_DexFile;
+jclass WellKnownClasses::dalvik_system_DexPathList;
+jclass WellKnownClasses::dalvik_system_DexPathList$Element;
jclass WellKnownClasses::dalvik_system_PathClassLoader;
+jclass WellKnownClasses::java_lang_BootClassLoader;
jclass WellKnownClasses::java_lang_ClassLoader;
jclass WellKnownClasses::java_lang_ClassNotFoundException;
jclass WellKnownClasses::java_lang_Daemons;
@@ -79,6 +83,10 @@
jmethodID WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_broadcast;
jmethodID WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_dispatch;
+jfieldID WellKnownClasses::dalvik_system_DexFile_cookie;
+jfieldID WellKnownClasses::dalvik_system_PathClassLoader_pathList;
+jfieldID WellKnownClasses::dalvik_system_DexPathList_dexElements;
+jfieldID WellKnownClasses::dalvik_system_DexPathList$Element_dexFile;
jfieldID WellKnownClasses::java_lang_Thread_daemon;
jfieldID WellKnownClasses::java_lang_Thread_group;
jfieldID WellKnownClasses::java_lang_Thread_lock;
@@ -131,7 +139,11 @@
void WellKnownClasses::Init(JNIEnv* env) {
com_android_dex_Dex = CacheClass(env, "com/android/dex/Dex");
+ dalvik_system_DexFile = CacheClass(env, "dalvik/system/DexFile");
+ dalvik_system_DexPathList = CacheClass(env, "dalvik/system/DexPathList");
+ dalvik_system_DexPathList$Element = CacheClass(env, "dalvik/system/DexPathList$Element");
dalvik_system_PathClassLoader = CacheClass(env, "dalvik/system/PathClassLoader");
+ java_lang_BootClassLoader = CacheClass(env, "java/lang/BootClassLoader");
java_lang_ClassLoader = CacheClass(env, "java/lang/ClassLoader");
java_lang_ClassNotFoundException = CacheClass(env, "java/lang/ClassNotFoundException");
java_lang_Daemons = CacheClass(env, "java/lang/Daemons");
@@ -179,6 +191,10 @@
org_apache_harmony_dalvik_ddmc_DdmServer_broadcast = CacheMethod(env, org_apache_harmony_dalvik_ddmc_DdmServer, true, "broadcast", "(I)V");
org_apache_harmony_dalvik_ddmc_DdmServer_dispatch = CacheMethod(env, org_apache_harmony_dalvik_ddmc_DdmServer, true, "dispatch", "(I[BII)Lorg/apache/harmony/dalvik/ddmc/Chunk;");
+ dalvik_system_DexFile_cookie = CacheField(env, dalvik_system_DexFile, false, "mCookie", "J");
+ dalvik_system_PathClassLoader_pathList = CacheField(env, dalvik_system_PathClassLoader, false, "pathList", "Ldalvik/system/DexPathList;");
+ dalvik_system_DexPathList_dexElements = CacheField(env, dalvik_system_DexPathList, false, "dexElements", "[Ldalvik/system/DexPathList$Element;");
+ dalvik_system_DexPathList$Element_dexFile = CacheField(env, dalvik_system_DexPathList$Element, false, "dexFile", "Ldalvik/system/DexFile;");
java_lang_Thread_daemon = CacheField(env, java_lang_Thread, false, "daemon", "Z");
java_lang_Thread_group = CacheField(env, java_lang_Thread, false, "group", "Ljava/lang/ThreadGroup;");
java_lang_Thread_lock = CacheField(env, java_lang_Thread, false, "lock", "Ljava/lang/Object;");
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index 7639f50..b10106c 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -40,7 +40,11 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static jclass com_android_dex_Dex;
+ static jclass dalvik_system_DexFile;
+ static jclass dalvik_system_DexPathList;
+ static jclass dalvik_system_DexPathList$Element;
static jclass dalvik_system_PathClassLoader;
+ static jclass java_lang_BootClassLoader;
static jclass java_lang_ClassLoader;
static jclass java_lang_ClassNotFoundException;
static jclass java_lang_Daemons;
@@ -93,6 +97,10 @@
static jmethodID org_apache_harmony_dalvik_ddmc_DdmServer_broadcast;
static jmethodID org_apache_harmony_dalvik_ddmc_DdmServer_dispatch;
+ static jfieldID dalvik_system_DexFile_cookie;
+ static jfieldID dalvik_system_DexPathList_dexElements;
+ static jfieldID dalvik_system_DexPathList$Element_dexFile;
+ static jfieldID dalvik_system_PathClassLoader_pathList;
static jfieldID java_lang_reflect_AbstractMethod_artMethod;
static jfieldID java_lang_reflect_Field_artField;
static jfieldID java_lang_reflect_Proxy_h;
diff --git a/test/004-JniTest/expected.txt b/test/004-JniTest/expected.txt
index e69de29..49d9cc0 100644
--- a/test/004-JniTest/expected.txt
+++ b/test/004-JniTest/expected.txt
@@ -0,0 +1,29 @@
+Super.<init>
+Super.<init>
+Subclass.<init>
+Super.<init>
+Super.<init>
+Subclass.<init>
+Super.<init>
+RUNNING super object, super class, super nonstatic
+Super.nonstaticMethod
+PASSED super object, super class, super nonstatic
+Super.<init>
+RUNNING super object, sub class, super nonstatic
+Super.nonstaticMethod
+PASSED super object, sub class, super nonstatic
+Super.<init>
+Subclass.<init>
+RUNNING sub object, super class, super nonstatic
+Super.nonstaticMethod
+PASSED sub object, super class, super nonstatic
+Super.<init>
+Subclass.<init>
+RUNNING sub object, sub class, super nonstatic
+Super.nonstaticMethod
+PASSED sub object, sub class, super nonstatic
+Super.<init>
+Subclass.<init>
+RUNNING sub object, sub class, sub nonstatic
+Subclass.nonstaticMethod
+PASSED sub object, sub class, sub nonstatic
diff --git a/test/004-JniTest/jni_test.cc b/test/004-JniTest/jni_test.cc
index f5a1d65..6fc4484 100644
--- a/test/004-JniTest/jni_test.cc
+++ b/test/004-JniTest/jni_test.cc
@@ -353,3 +353,198 @@
extern "C" JNIEXPORT void JNICALL Java_Main_nativeTestShallowGetStackClass2(JNIEnv* env, jclass) {
PthreadHelper(&testShallowGetStackClass2);
}
+
+class JniCallNonvirtualVoidMethodTest {
+ public:
+ explicit JniCallNonvirtualVoidMethodTest(JNIEnv* env)
+ : env_(env),
+ check_jni_ri_(true),
+ check_jni_android_(true),
+ super_(GetClass("JniCallNonvirtualTest")),
+ sub_(GetClass("JniCallNonvirtualTestSubclass")),
+ super_constructor_(GetMethodID(super_, true, "<init>")),
+ super_static_(GetMethodID(super_, false, "staticMethod")),
+ super_nonstatic_(GetMethodID(super_, true, "nonstaticMethod")),
+ sub_constructor_(GetMethodID(sub_, true, "<init>")),
+ sub_static_(GetMethodID(sub_, false, "staticMethod")),
+ sub_nonstatic_(GetMethodID(sub_, true, "nonstaticMethod")),
+ super_field_(GetFieldID(super_, "nonstaticMethodSuperCalled")),
+ sub_field_(GetFieldID(super_, "nonstaticMethodSubCalled")) {}
+
+ void Test() {
+ TestStaticCallNonvirtualMethod();
+ TestNewObject();
+ TestnonstaticCallNonvirtualMethod();
+ }
+
+ JNIEnv* const env_;
+
+ bool const check_jni_ri_;
+ bool const check_jni_android_;
+
+ jclass const super_;
+ jclass const sub_;
+
+ jmethodID const super_constructor_;
+ jmethodID const super_static_;
+ jmethodID const super_nonstatic_;
+ jmethodID const sub_constructor_;
+ jmethodID const sub_static_;
+ jmethodID const sub_nonstatic_;
+
+ jfieldID const super_field_;
+ jfieldID const sub_field_;
+
+ private:
+ jclass GetClass(const char* class_name) {
+ jclass c = env_->FindClass(class_name);
+ if (env_->ExceptionCheck()) {
+ env_->ExceptionDescribe();
+ env_->FatalError(__FUNCTION__);
+ }
+ assert(!env_->ExceptionCheck());
+ assert(c != nullptr);
+ return c;
+ }
+
+ jmethodID GetMethodID(jclass c, bool nonstatic, const char* method_name) {
+ jmethodID m = ((nonstatic) ?
+ env_->GetMethodID(c, method_name, "()V") :
+ env_->GetStaticMethodID(c, method_name, "()V"));
+ if (env_->ExceptionCheck()) {
+ env_->ExceptionDescribe();
+ env_->FatalError(__FUNCTION__);
+ }
+ assert(m != nullptr);
+ return m;
+ }
+
+ jobject CallConstructor(jclass c, jmethodID m) {
+ jobject o = env_->NewObject(c, m);
+ if (env_->ExceptionCheck()) {
+ env_->ExceptionDescribe();
+ env_->FatalError(__FUNCTION__);
+ }
+ assert(o != nullptr);
+ return o;
+ }
+
+ void CallMethod(jobject o, jclass c, jmethodID m, bool nonstatic, const char* test_case) {
+ printf("RUNNING %s\n", test_case);
+ env_->CallNonvirtualVoidMethod(o, c, m);
+ bool exception_check = env_->ExceptionCheck();
+ if (c == nullptr || !nonstatic) {
+ if (!exception_check) {
+ printf("FAILED %s due to missing exception\n", test_case);
+ env_->FatalError("Expected NullPointerException with null jclass");
+ }
+ env_->ExceptionClear();
+ } else if (exception_check) {
+ printf("FAILED %s due to pending exception\n", test_case);
+ env_->ExceptionDescribe();
+ env_->FatalError(test_case);
+ }
+ printf("PASSED %s\n", test_case);
+ }
+
+ jfieldID GetFieldID(jclass c, const char* field_name) {
+ jfieldID m = env_->GetFieldID(c, field_name, "Z");
+ if (env_->ExceptionCheck()) {
+ env_->ExceptionDescribe();
+ env_->FatalError(__FUNCTION__);
+ }
+ assert(m != nullptr);
+ return m;
+ }
+
+ jboolean GetBooleanField(jobject o, jfieldID f) {
+ jboolean b = env_->GetBooleanField(o, f);
+ if (env_->ExceptionCheck()) {
+ env_->ExceptionDescribe();
+ env_->FatalError(__FUNCTION__);
+ }
+ return b;
+ }
+
+ void TestStaticCallNonvirtualMethod() {
+ if (!check_jni_ri_&& !check_jni_android_) {
+ CallMethod(nullptr, nullptr, super_static_, false, "null object, null class, super static");
+ }
+ if (!check_jni_android_) {
+ CallMethod(nullptr, super_, super_static_, false, "null object, super class, super static");
+ }
+ if (!check_jni_android_) {
+ CallMethod(nullptr, sub_, super_static_, false, "null object, sub class, super static");
+ }
+
+ if (!check_jni_ri_ && !check_jni_android_) {
+ CallMethod(nullptr, nullptr, sub_static_, false, "null object, null class, sub static");
+ }
+ if (!check_jni_android_) {
+ CallMethod(nullptr, sub_, sub_static_, false, "null object, super class, sub static");
+ }
+ if (!check_jni_android_) {
+ CallMethod(nullptr, super_, sub_static_, false, "null object, super class, sub static");
+ }
+ }
+
+ void TestNewObject() {
+ jobject super_super = CallConstructor(super_, super_constructor_);
+ jobject super_sub = CallConstructor(super_, sub_constructor_);
+ jobject sub_super = CallConstructor(sub_, super_constructor_);
+ jobject sub_sub = CallConstructor(sub_, sub_constructor_);
+
+ assert(env_->IsInstanceOf(super_super, super_));
+ assert(!env_->IsInstanceOf(super_super, sub_));
+
+ // Note that even though we called (and ran) the subclass
+ // constructor, we are not the subclass.
+ assert(env_->IsInstanceOf(super_sub, super_));
+ assert(!env_->IsInstanceOf(super_sub, sub_));
+
+ // Note that even though we called the superclass constructor, we
+ // are still the subclass.
+ assert(env_->IsInstanceOf(sub_super, super_));
+ assert(env_->IsInstanceOf(sub_super, sub_));
+
+ assert(env_->IsInstanceOf(sub_sub, super_));
+ assert(env_->IsInstanceOf(sub_sub, sub_));
+ }
+
+ void TestnonstaticCallNonvirtualMethod(bool super_object, bool super_class, bool super_method, const char* test_case) {
+ if (check_jni_android_) {
+ if (super_object && !super_method) {
+ return; // We don't allow a call with sub class method on the super class instance.
+ }
+ if (super_class && !super_method) {
+ return; // We don't allow a call with the sub class method with the super class argument.
+ }
+ }
+ jobject o = ((super_object) ?
+ CallConstructor(super_, super_constructor_) :
+ CallConstructor(sub_, sub_constructor_));
+ jclass c = (super_class) ? super_ : sub_;
+ jmethodID m = (super_method) ? super_nonstatic_ : sub_nonstatic_;
+ CallMethod(o, c, m, true, test_case);
+ jboolean super_field = GetBooleanField(o, super_field_);
+ jboolean sub_field = GetBooleanField(o, sub_field_);
+ assert(super_field == super_method);
+ assert(sub_field != super_method);
+ }
+
+ void TestnonstaticCallNonvirtualMethod() {
+ TestnonstaticCallNonvirtualMethod(true, true, true, "super object, super class, super nonstatic");
+ TestnonstaticCallNonvirtualMethod(true, false, true, "super object, sub class, super nonstatic");
+ TestnonstaticCallNonvirtualMethod(true, false, false, "super object, sub class, sub nonstatic");
+ TestnonstaticCallNonvirtualMethod(true, true, false, "super object, super class, sub nonstatic");
+
+ TestnonstaticCallNonvirtualMethod(false, true, true, "sub object, super class, super nonstatic");
+ TestnonstaticCallNonvirtualMethod(false, false, true, "sub object, sub class, super nonstatic");
+ TestnonstaticCallNonvirtualMethod(false, false, false, "sub object, sub class, sub nonstatic");
+ TestnonstaticCallNonvirtualMethod(false, true, false, "sub object, super class, sub nonstatic");
+ }
+};
+
+extern "C" void JNICALL Java_Main_testCallNonvirtual(JNIEnv* env, jclass) {
+ JniCallNonvirtualVoidMethodTest(env).Test();
+}
diff --git a/test/004-JniTest/src/Main.java b/test/004-JniTest/src/Main.java
index 5884bc0..8e92010 100644
--- a/test/004-JniTest/src/Main.java
+++ b/test/004-JniTest/src/Main.java
@@ -32,6 +32,7 @@
testIsAssignableFromOnPrimitiveTypes();
testShallowGetCallingClassLoader();
testShallowGetStackClass2();
+ testCallNonvirtual();
}
private static native void testFindClassOnAttachedNativeThread();
@@ -94,7 +95,7 @@
// Test sign-extension for values < 32b
- native static byte byteMethod(byte b1, byte b2, byte b3, byte b4, byte b5, byte b6, byte b7,
+ static native byte byteMethod(byte b1, byte b2, byte b3, byte b4, byte b5, byte b6, byte b7,
byte b8, byte b9, byte b10);
private static void testByteMethod() {
@@ -109,7 +110,7 @@
}
}
- native static short shortMethod(short s1, short s2, short s3, short s4, short s5, short s6, short s7,
+ private static native short shortMethod(short s1, short s2, short s3, short s4, short s5, short s6, short s7,
short s8, short s9, short s10);
private static void testShortMethod() {
@@ -126,7 +127,7 @@
// Test zero-extension for values < 32b
- native static boolean booleanMethod(boolean b1, boolean b2, boolean b3, boolean b4, boolean b5, boolean b6, boolean b7,
+ private static native boolean booleanMethod(boolean b1, boolean b2, boolean b3, boolean b4, boolean b5, boolean b6, boolean b7,
boolean b8, boolean b9, boolean b10);
private static void testBooleanMethod() {
@@ -139,7 +140,7 @@
}
}
- native static char charMethod(char c1, char c2, char c3, char c4, char c5, char c6, char c7,
+ private static native char charMethod(char c1, char c2, char c3, char c4, char c5, char c6, char c7,
char c8, char c9, char c10);
private static void testCharMethod() {
@@ -168,17 +169,55 @@
}
}
- native static boolean nativeIsAssignableFrom(Class<?> from, Class<?> to);
+ private static native boolean nativeIsAssignableFrom(Class<?> from, Class<?> to);
- static void testShallowGetCallingClassLoader() {
+ private static void testShallowGetCallingClassLoader() {
nativeTestShallowGetCallingClassLoader();
}
- native static void nativeTestShallowGetCallingClassLoader();
+ private native static void nativeTestShallowGetCallingClassLoader();
- static void testShallowGetStackClass2() {
+ private static void testShallowGetStackClass2() {
nativeTestShallowGetStackClass2();
}
- native static void nativeTestShallowGetStackClass2();
+ private static native void nativeTestShallowGetStackClass2();
+
+ private static native void testCallNonvirtual();
+}
+
+class JniCallNonvirtualTest {
+ public boolean nonstaticMethodSuperCalled = false;
+ public boolean nonstaticMethodSubCalled = false;
+
+ private static native void testCallNonvirtual();
+
+ public JniCallNonvirtualTest() {
+ System.out.println("Super.<init>");
+ }
+
+ public static void staticMethod() {
+ System.out.println("Super.staticMethod");
+ }
+
+ public void nonstaticMethod() {
+ System.out.println("Super.nonstaticMethod");
+ nonstaticMethodSuperCalled = true;
+ }
+}
+
+class JniCallNonvirtualTestSubclass extends JniCallNonvirtualTest {
+
+ public JniCallNonvirtualTestSubclass() {
+ System.out.println("Subclass.<init>");
+ }
+
+ public static void staticMethod() {
+ System.out.println("Subclass.staticMethod");
+ }
+
+ public void nonstaticMethod() {
+ System.out.println("Subclass.nonstaticMethod");
+ nonstaticMethodSubCalled = true;
+ }
}
diff --git a/test/120-hashcode/expected.txt b/test/120-hashcode/expected.txt
new file mode 100644
index 0000000..619c561
--- /dev/null
+++ b/test/120-hashcode/expected.txt
@@ -0,0 +1 @@
+Done.
diff --git a/test/120-hashcode/info.txt b/test/120-hashcode/info.txt
new file mode 100644
index 0000000..80f131d
--- /dev/null
+++ b/test/120-hashcode/info.txt
@@ -0,0 +1 @@
+Check that object hashCode and System.identityHashCode never cause the hash to change.
\ No newline at end of file
diff --git a/test/120-hashcode/src/Main.java b/test/120-hashcode/src/Main.java
new file mode 100644
index 0000000..d2435ce
--- /dev/null
+++ b/test/120-hashcode/src/Main.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2014 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) {
+ Object o = new Object();
+ // Generate a hashcode and put it in the lock word.
+ int hashOrig = o.hashCode();
+ int hashInflated = 0;
+ int hashSystemOrig = System.identityHashCode(o);
+ int hashSystemInflated = 0;
+ // Inflate the monitor to move the hash from the lock word to the Monitor.
+ synchronized (o) {
+ hashInflated = o.hashCode();
+ hashSystemInflated = System.identityHashCode(o);
+ }
+ // Make sure that all the hashes agree.
+ if (hashOrig != hashInflated || hashOrig != hashSystemOrig ||
+ hashSystemOrig != hashSystemInflated) {
+ System.err.println("hash codes dont match: " + hashOrig + " " + hashInflated + " " +
+ hashSystemOrig + " " + hashSystemInflated);
+ }
+ System.out.println("Done.");
+ }
+}
+
+