summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--runtime/art_method.cc12
-rw-r--r--runtime/art_method.h16
-rw-r--r--runtime/entrypoints/jni/jni_entrypoints.cc7
-rw-r--r--runtime/entrypoints/quick/quick_trampoline_entrypoints.cc6
-rw-r--r--runtime/jni_internal.cc3
-rw-r--r--runtime/openjdkjvmti/OpenjdkJvmTi.cc2
-rw-r--r--runtime/openjdkjvmti/art_jvmti.h2
-rw-r--r--runtime/openjdkjvmti/events-inl.h21
-rw-r--r--runtime/openjdkjvmti/ti_method.cc45
-rw-r--r--runtime/openjdkjvmti/ti_method.h5
-rw-r--r--runtime/runtime_callbacks.cc22
-rw-r--r--runtime/runtime_callbacks.h14
-rw-r--r--test/986-native-method-bind/expected.txt8
-rw-r--r--test/986-native-method-bind/info.txt1
-rw-r--r--test/986-native-method-bind/native_bind.cc110
-rwxr-xr-xtest/986-native-method-bind/run17
-rw-r--r--test/986-native-method-bind/src/Main.java21
-rw-r--r--test/986-native-method-bind/src/art/Main.java28
-rw-r--r--test/986-native-method-bind/src/art/Redefinition.java96
-rw-r--r--test/986-native-method-bind/src/art/Test986.java82
-rw-r--r--test/Android.bp1
21 files changed, 504 insertions, 15 deletions
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index 5a71be6eb9..76fdd43992 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -43,6 +43,7 @@
#include "mirror/object-inl.h"
#include "mirror/string.h"
#include "oat_file-inl.h"
+#include "runtime_callbacks.h"
#include "scoped_thread_state_change-inl.h"
#include "well_known_classes.h"
@@ -372,20 +373,25 @@ void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue*
self->PopManagedStackFragment(fragment);
}
-void ArtMethod::RegisterNative(const void* native_method, bool is_fast) {
+const void* ArtMethod::RegisterNative(const void* native_method, bool is_fast) {
CHECK(IsNative()) << PrettyMethod();
CHECK(!IsFastNative()) << PrettyMethod();
CHECK(native_method != nullptr) << PrettyMethod();
if (is_fast) {
AddAccessFlags(kAccFastNative);
}
- SetEntryPointFromJni(native_method);
+ void* new_native_method = nullptr;
+ Runtime::Current()->GetRuntimeCallbacks()->RegisterNativeMethod(this,
+ native_method,
+ /*out*/&new_native_method);
+ SetEntryPointFromJni(new_native_method);
+ return new_native_method;
}
void ArtMethod::UnregisterNative() {
CHECK(IsNative() && !IsFastNative()) << PrettyMethod();
// restore stub to lookup native pointer via dlsym
- RegisterNative(GetJniDlsymLookupStub(), false);
+ SetEntryPointFromJni(GetJniDlsymLookupStub());
}
bool ArtMethod::IsOverridableByDefaultMethod() {
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 51b65760a1..b01b344bda 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -398,8 +398,10 @@ class ArtMethod FINAL {
pointer_size);
}
- void RegisterNative(const void* native_method, bool is_fast)
- REQUIRES_SHARED(Locks::mutator_lock_);
+ // Registers the native method and returns the new entry point. NB The returned entry point might
+ // be different from the native_method argument if some MethodCallback modifies it.
+ const void* RegisterNative(const void* native_method, bool is_fast)
+ REQUIRES_SHARED(Locks::mutator_lock_) WARN_UNUSED;
void UnregisterNative() REQUIRES_SHARED(Locks::mutator_lock_);
@@ -744,6 +746,16 @@ class ArtMethod FINAL {
DISALLOW_COPY_AND_ASSIGN(ArtMethod); // Need to use CopyFrom to deal with 32 vs 64 bits.
};
+class MethodCallback {
+ public:
+ virtual ~MethodCallback() {}
+
+ virtual void RegisterNativeMethod(ArtMethod* method,
+ const void* original_implementation,
+ /*out*/void** new_implementation)
+ REQUIRES_SHARED(Locks::mutator_lock_) = 0;
+};
+
} // namespace art
#endif // ART_RUNTIME_ART_METHOD_H_
diff --git a/runtime/entrypoints/jni/jni_entrypoints.cc b/runtime/entrypoints/jni/jni_entrypoints.cc
index fd23ced7f7..546e59d223 100644
--- a/runtime/entrypoints/jni/jni_entrypoints.cc
+++ b/runtime/entrypoints/jni/jni_entrypoints.cc
@@ -25,10 +25,10 @@ namespace art {
// Used by the JNI dlsym stub to find the native method to invoke if none is registered.
#if defined(__arm__) || defined(__aarch64__)
-extern "C" void* artFindNativeMethod() {
+extern "C" const void* artFindNativeMethod() {
Thread* self = Thread::Current();
#else
-extern "C" void* artFindNativeMethod(Thread* self) {
+extern "C" const void* artFindNativeMethod(Thread* self) {
DCHECK_EQ(self, Thread::Current());
#endif
Locks::mutator_lock_->AssertNotHeld(self); // We come here as Native.
@@ -45,8 +45,7 @@ extern "C" void* artFindNativeMethod(Thread* self) {
return nullptr;
} else {
// Register so that future calls don't come here
- method->RegisterNative(native_code, false);
- return native_code;
+ return method->RegisterNative(native_code, false);
}
}
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 354ae205e8..2b349e39a0 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -2025,9 +2025,9 @@ void BuildGenericJniFrameVisitor::FinalizeHandleScope(Thread* self) {
}
#if defined(__arm__) || defined(__aarch64__)
-extern "C" void* artFindNativeMethod();
+extern "C" const void* artFindNativeMethod();
#else
-extern "C" void* artFindNativeMethod(Thread* self);
+extern "C" const void* artFindNativeMethod(Thread* self);
#endif
static uint64_t artQuickGenericJniEndJNIRef(Thread* self,
@@ -2126,7 +2126,7 @@ extern "C" TwoWordReturn artQuickGenericJniTrampoline(Thread* self, ArtMethod**
}
// Retrieve the stored native code.
- void* nativeCode = called->GetEntryPointFromJni();
+ void const* nativeCode = called->GetEntryPointFromJni();
// There are two cases for the content of nativeCode:
// 1) Pointer to the native function.
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index 5418d3569e..b146b51033 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -2277,7 +2277,8 @@ class JNI {
// TODO: make this a hard register error in the future.
}
- m->RegisterNative(fnPtr, is_fast);
+ const void* final_function_ptr = m->RegisterNative(fnPtr, is_fast);
+ UNUSED(final_function_ptr);
}
return JNI_OK;
}
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index 39e603e1e7..c3a94b93a0 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -1556,6 +1556,7 @@ extern "C" bool ArtPlugin_Initialize() {
ThreadUtil::Register(&gEventHandler);
ClassUtil::Register(&gEventHandler);
DumpUtil::Register(&gEventHandler);
+ MethodUtil::Register(&gEventHandler);
SearchUtil::Register();
HeapUtil::Register();
@@ -1569,6 +1570,7 @@ extern "C" bool ArtPlugin_Deinitialize() {
ThreadUtil::Unregister();
ClassUtil::Unregister();
DumpUtil::Unregister();
+ MethodUtil::Unregister();
SearchUtil::Unregister();
HeapUtil::Unregister();
diff --git a/runtime/openjdkjvmti/art_jvmti.h b/runtime/openjdkjvmti/art_jvmti.h
index 2ff3a478c4..2a2aa4c199 100644
--- a/runtime/openjdkjvmti/art_jvmti.h
+++ b/runtime/openjdkjvmti/art_jvmti.h
@@ -223,7 +223,7 @@ const jvmtiCapabilities kPotentialCapabilities = {
.can_generate_compiled_method_load_events = 0,
.can_generate_monitor_events = 0,
.can_generate_vm_object_alloc_events = 1,
- .can_generate_native_method_bind_events = 0,
+ .can_generate_native_method_bind_events = 1,
.can_generate_garbage_collection_events = 1,
.can_generate_object_free_events = 1,
.can_force_early_return = 0,
diff --git a/runtime/openjdkjvmti/events-inl.h b/runtime/openjdkjvmti/events-inl.h
index 233b45cda8..57abf3142d 100644
--- a/runtime/openjdkjvmti/events-inl.h
+++ b/runtime/openjdkjvmti/events-inl.h
@@ -191,6 +191,27 @@ inline void EventHandler::DispatchEvent(ArtJvmTiEnv* env, art::Thread* thread, A
}
}
+// Need to give a custom specialization for NativeMethodBind since it has to deal with an out
+// variable.
+template <>
+inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kNativeMethodBind>(art::Thread* thread,
+ JNIEnv* jnienv,
+ jthread jni_thread,
+ jmethodID method,
+ void* cur_method,
+ void** new_method) const {
+ *new_method = cur_method;
+ for (ArtJvmTiEnv* env : envs) {
+ if (env != nullptr && ShouldDispatch<ArtJvmtiEvent::kNativeMethodBind>(env, thread)) {
+ auto callback = impl::GetCallback<ArtJvmtiEvent::kNativeMethodBind>(env);
+ (*callback)(env, jnienv, jni_thread, method, cur_method, new_method);
+ if (*new_method != nullptr) {
+ cur_method = *new_method;
+ }
+ }
+ }
+}
+
// C++ does not allow partial template function specialization. The dispatch for our separated
// ClassFileLoadHook event types is the same, and in the DispatchClassFileLoadHookEvent helper.
// The following two DispatchEvent specializations dispatch to it.
diff --git a/runtime/openjdkjvmti/ti_method.cc b/runtime/openjdkjvmti/ti_method.cc
index 01bf21d53e..2adabbaff4 100644
--- a/runtime/openjdkjvmti/ti_method.cc
+++ b/runtime/openjdkjvmti/ti_method.cc
@@ -35,14 +35,59 @@
#include "art_method-inl.h"
#include "base/enums.h"
#include "dex_file_annotations.h"
+#include "events-inl.h"
#include "jni_internal.h"
#include "mirror/object_array-inl.h"
#include "modifiers.h"
+#include "runtime_callbacks.h"
#include "scoped_thread_state_change-inl.h"
+#include "ScopedLocalRef.h"
#include "thread-inl.h"
+#include "thread_list.h"
namespace openjdkjvmti {
+struct TiMethodCallback : public art::MethodCallback {
+ void RegisterNativeMethod(art::ArtMethod* method,
+ const void* cur_method,
+ /*out*/void** new_method)
+ OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ if (event_handler->IsEventEnabledAnywhere(ArtJvmtiEvent::kNativeMethodBind)) {
+ art::Thread* thread = art::Thread::Current();
+ ScopedLocalRef<jthread> thread_jni(
+ thread->GetJniEnv(), thread->GetJniEnv()->AddLocalReference<jthread>(thread->GetPeer()));
+ art::ScopedThreadSuspension sts(thread, art::ThreadState::kNative);
+ event_handler->DispatchEvent<ArtJvmtiEvent::kNativeMethodBind>(
+ thread,
+ static_cast<JNIEnv*>(thread->GetJniEnv()),
+ thread_jni.get(),
+ art::jni::EncodeArtMethod(method),
+ const_cast<void*>(cur_method),
+ new_method);
+ }
+ }
+
+ EventHandler* event_handler = nullptr;
+};
+
+TiMethodCallback gMethodCallback;
+
+void MethodUtil::Register(EventHandler* handler) {
+ gMethodCallback.event_handler = handler;
+ art::ScopedThreadStateChange stsc(art::Thread::Current(),
+ art::ThreadState::kWaitingForDebuggerToAttach);
+ art::ScopedSuspendAll ssa("Add method callback");
+ art::Runtime::Current()->GetRuntimeCallbacks()->AddMethodCallback(&gMethodCallback);
+}
+
+void MethodUtil::Unregister() {
+ art::ScopedThreadStateChange stsc(art::Thread::Current(),
+ art::ThreadState::kWaitingForDebuggerToAttach);
+ art::ScopedSuspendAll ssa("Remove method callback");
+ art::Runtime* runtime = art::Runtime::Current();
+ runtime->GetRuntimeCallbacks()->RemoveMethodCallback(&gMethodCallback);
+}
+
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 e5c1705ada..cc161c8fed 100644
--- a/runtime/openjdkjvmti/ti_method.h
+++ b/runtime/openjdkjvmti/ti_method.h
@@ -37,8 +37,13 @@
namespace openjdkjvmti {
+class EventHandler;
+
class MethodUtil {
public:
+ static void Register(EventHandler* event_handler);
+ static void Unregister();
+
static jvmtiError GetArgumentsSize(jvmtiEnv* env, jmethodID method, jint* size_ptr);
static jvmtiError GetMaxLocals(jvmtiEnv* env, jmethodID method, jint* max_ptr);
diff --git a/runtime/runtime_callbacks.cc b/runtime/runtime_callbacks.cc
index 25324b52d1..16d6c13722 100644
--- a/runtime/runtime_callbacks.cc
+++ b/runtime/runtime_callbacks.cc
@@ -18,6 +18,7 @@
#include <algorithm>
+#include "art_method.h"
#include "base/macros.h"
#include "class_linker.h"
#include "thread.h"
@@ -131,4 +132,25 @@ void RuntimeCallbacks::NextRuntimePhase(RuntimePhaseCallback::RuntimePhase phase
}
}
+void RuntimeCallbacks::AddMethodCallback(MethodCallback* cb) {
+ method_callbacks_.push_back(cb);
+}
+
+void RuntimeCallbacks::RemoveMethodCallback(MethodCallback* cb) {
+ Remove(cb, &method_callbacks_);
+}
+
+void RuntimeCallbacks::RegisterNativeMethod(ArtMethod* method,
+ const void* in_cur_method,
+ /*out*/void** new_method) {
+ void* cur_method = const_cast<void*>(in_cur_method);
+ *new_method = cur_method;
+ for (MethodCallback* cb : method_callbacks_) {
+ cb->RegisterNativeMethod(method, cur_method, new_method);
+ if (*new_method != nullptr) {
+ cur_method = *new_method;
+ }
+ }
+}
+
} // namespace art
diff --git a/runtime/runtime_callbacks.h b/runtime/runtime_callbacks.h
index d321254e17..e8f1824262 100644
--- a/runtime/runtime_callbacks.h
+++ b/runtime/runtime_callbacks.h
@@ -31,8 +31,10 @@ class Class;
class ClassLoader;
} // namespace mirror
+class ArtMethod;
class ClassLoadCallback;
class Thread;
+class MethodCallback;
class ThreadLifecycleCallback;
// Note: RuntimeCallbacks uses the mutator lock to synchronize the callback lists. A thread must
@@ -110,6 +112,14 @@ class RuntimeCallbacks {
/*out*/DexFile::ClassDef const** final_class_def)
REQUIRES_SHARED(Locks::mutator_lock_);
+ void AddMethodCallback(MethodCallback* cb) REQUIRES(Locks::mutator_lock_);
+ void RemoveMethodCallback(MethodCallback* cb) REQUIRES(Locks::mutator_lock_);
+
+ void RegisterNativeMethod(ArtMethod* method,
+ const void* original_implementation,
+ /*out*/void** new_implementation)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
private:
std::vector<ThreadLifecycleCallback*> thread_callbacks_
GUARDED_BY(Locks::mutator_lock_);
@@ -118,7 +128,9 @@ class RuntimeCallbacks {
std::vector<RuntimeSigQuitCallback*> sigquit_callbacks_
GUARDED_BY(Locks::mutator_lock_);
std::vector<RuntimePhaseCallback*> phase_callbacks_
- GUARDED_BY(Locks::mutator_lock_);
+ GUARDED_BY(Locks::mutator_lock_);
+ std::vector<MethodCallback*> method_callbacks_
+ GUARDED_BY(Locks::mutator_lock_);
};
} // namespace art
diff --git a/test/986-native-method-bind/expected.txt b/test/986-native-method-bind/expected.txt
new file mode 100644
index 0000000000..189217d761
--- /dev/null
+++ b/test/986-native-method-bind/expected.txt
@@ -0,0 +1,8 @@
+private static native void art.Test986$Transform.sayHi() = Java_art_Test986_00024Transform_sayHi -> Java_art_Test986_00024Transform_sayHi
+Hello
+private static native void art.Test986$Transform.sayHi() = Java_art_Test986_00024Transform_sayHi -> NoReallySayGoodbye
+Bye
+public static native void art.Main.bindAgentJNI(java.lang.String,java.lang.ClassLoader) = Java_art_Main_bindAgentJNI -> Java_art_Main_bindAgentJNI
+public static native void art.Main.bindAgentJNIForClass(java.lang.Class) = Java_art_Main_bindAgentJNIForClass -> Java_art_Main_bindAgentJNIForClass
+private static native void art.Test986.setNativeBindNotify(boolean) = Java_art_Test986_setNativeBindNotify -> Java_art_Test986_setNativeBindNotify
+private static native void art.Test986.setupNativeBindNotify() = Java_art_Test986_setupNativeBindNotify -> Java_art_Test986_setupNativeBindNotify
diff --git a/test/986-native-method-bind/info.txt b/test/986-native-method-bind/info.txt
new file mode 100644
index 0000000000..1939936ca9
--- /dev/null
+++ b/test/986-native-method-bind/info.txt
@@ -0,0 +1 @@
+Tests native-method-bind callback and native method replacement.
diff --git a/test/986-native-method-bind/native_bind.cc b/test/986-native-method-bind/native_bind.cc
new file mode 100644
index 0000000000..4f93f87dfe
--- /dev/null
+++ b/test/986-native-method-bind/native_bind.cc
@@ -0,0 +1,110 @@
+/*
+ * 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.
+ */
+
+#include <inttypes.h>
+#include <memory>
+#include <stdio.h>
+#include <dlfcn.h>
+
+#include "android-base/stringprintf.h"
+#include "jni.h"
+#include "jvmti.h"
+
+// Test infrastructure
+#include "jni_binder.h"
+#include "jvmti_helper.h"
+#include "test_env.h"
+#include "scoped_local_ref.h"
+
+namespace art {
+namespace Test986NativeBind {
+
+static void doUpPrintCall(JNIEnv* env, const char* function) {
+ ScopedLocalRef<jclass> klass(env, env->FindClass("art/Test986"));
+ jmethodID targetMethod = env->GetStaticMethodID(klass.get(), function, "()V");
+ env->CallStaticVoidMethod(klass.get(), targetMethod);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test986_00024Transform_sayHi(
+ JNIEnv* env, jclass klass ATTRIBUTE_UNUSED) {
+ doUpPrintCall(env, "doSayHi");
+}
+
+extern "C" JNIEXPORT void JNICALL NoReallySayGoodbye(JNIEnv* env, jclass klass ATTRIBUTE_UNUSED) {
+ doUpPrintCall(env, "doSayBye");
+}
+
+static void doJvmtiMethodBind(jvmtiEnv* jvmtienv ATTRIBUTE_UNUSED,
+ JNIEnv* env,
+ jthread thread ATTRIBUTE_UNUSED,
+ jmethodID m,
+ void* address,
+ /*out*/void** out_address) {
+ ScopedLocalRef<jclass> method_class(env, env->FindClass("java/lang/reflect/Method"));
+ ScopedLocalRef<jobject> method_obj(env, env->ToReflectedMethod(method_class.get(), m, false));
+ Dl_info addr_info;
+ if (dladdr(address, &addr_info) == 0 || addr_info.dli_sname == nullptr) {
+ ScopedLocalRef<jclass> exception_class(env, env->FindClass("java/lang/Exception"));
+ env->ThrowNew(exception_class.get(), "dladdr failure!");
+ return;
+ }
+ ScopedLocalRef<jstring> sym_name(env, env->NewStringUTF(addr_info.dli_sname));
+ ScopedLocalRef<jclass> klass(env, env->FindClass("art/Test986"));
+ jmethodID upcallMethod = env->GetStaticMethodID(
+ klass.get(),
+ "doNativeMethodBind",
+ "(Ljava/lang/reflect/Method;Ljava/lang/String;)Ljava/lang/String;");
+ if (env->ExceptionCheck()) {
+ return;
+ }
+ ScopedLocalRef<jstring> new_symbol(env,
+ reinterpret_cast<jstring>(
+ env->CallStaticObjectMethod(klass.get(),
+ upcallMethod,
+ method_obj.get(),
+ sym_name.get())));
+ const char* new_symbol_chars = env->GetStringUTFChars(new_symbol.get(), nullptr);
+ if (strcmp(new_symbol_chars, addr_info.dli_sname) != 0) {
+ *out_address = dlsym(RTLD_DEFAULT, new_symbol_chars);
+ if (*out_address == nullptr) {
+ ScopedLocalRef<jclass> exception_class(env, env->FindClass("java/lang/Exception"));
+ env->ThrowNew(exception_class.get(), "dlsym failure!");
+ return;
+ }
+ }
+ env->ReleaseStringUTFChars(new_symbol.get(), new_symbol_chars);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test986_setupNativeBindNotify(
+ JNIEnv* env ATTRIBUTE_UNUSED, jclass klass ATTRIBUTE_UNUSED) {
+ jvmtiEventCallbacks cb;
+ memset(&cb, 0, sizeof(cb));
+ cb.NativeMethodBind = doJvmtiMethodBind;
+ jvmti_env->SetEventCallbacks(&cb, sizeof(cb));
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test986_setNativeBindNotify(
+ JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jboolean enable) {
+ jvmtiError res = jvmti_env->SetEventNotificationMode(enable ? JVMTI_ENABLE : JVMTI_DISABLE,
+ JVMTI_EVENT_NATIVE_METHOD_BIND,
+ nullptr);
+ if (res != JVMTI_ERROR_NONE) {
+ JvmtiErrorToException(env, jvmti_env, res);
+ }
+}
+
+} // namespace Test986NativeBind
+} // namespace art
diff --git a/test/986-native-method-bind/run b/test/986-native-method-bind/run
new file mode 100755
index 0000000000..e92b873956
--- /dev/null
+++ b/test/986-native-method-bind/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+./default-run "$@" --jvmti
diff --git a/test/986-native-method-bind/src/Main.java b/test/986-native-method-bind/src/Main.java
new file mode 100644
index 0000000000..fac9d8e2a9
--- /dev/null
+++ b/test/986-native-method-bind/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.Test986.run();
+ }
+}
diff --git a/test/986-native-method-bind/src/art/Main.java b/test/986-native-method-bind/src/art/Main.java
new file mode 100644
index 0000000000..8b01920638
--- /dev/null
+++ b/test/986-native-method-bind/src/art/Main.java
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+package art;
+
+// Binder class so the agent's C code has something that can be bound and exposed to tests.
+// In a package to separate cleanly and work around CTS reference issues (though this class
+// should be replaced in the CTS version).
+public class Main {
+ // Load the given class with the given classloader, and bind all native methods to corresponding
+ // C methods in the agent. Will abort if any of the steps fail.
+ public static native void bindAgentJNI(String className, ClassLoader classLoader);
+ // Same as above, giving the class directly.
+ public static native void bindAgentJNIForClass(Class<?> klass);
+}
diff --git a/test/986-native-method-bind/src/art/Redefinition.java b/test/986-native-method-bind/src/art/Redefinition.java
new file mode 100644
index 0000000000..0350ab42ad
--- /dev/null
+++ b/test/986-native-method-bind/src/art/Redefinition.java
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+
+package art;
+
+import java.util.ArrayList;
+// Common Redefinition functions. Placed here for use by CTS
+public class Redefinition {
+ // Bind native functions.
+ static {
+ Main.bindAgentJNIForClass(Redefinition.class);
+ }
+
+ public static final class CommonClassDefinition {
+ public final Class<?> target;
+ public final byte[] class_file_bytes;
+ public final byte[] dex_file_bytes;
+
+ public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
+ this.target = target;
+ this.class_file_bytes = class_file_bytes;
+ this.dex_file_bytes = dex_file_bytes;
+ }
+ }
+
+ // A set of possible test configurations. Test should set this if they need to.
+ // This must be kept in sync with the defines in ti-agent/common_helper.cc
+ public static enum Config {
+ COMMON_REDEFINE(0),
+ COMMON_RETRANSFORM(1),
+ COMMON_TRANSFORM(2);
+
+ private final int val;
+ private Config(int val) {
+ this.val = val;
+ }
+ }
+
+ public static void setTestConfiguration(Config type) {
+ nativeSetTestConfiguration(type.val);
+ }
+
+ private static native void nativeSetTestConfiguration(int type);
+
+ // Transforms the class
+ public static native void doCommonClassRedefinition(Class<?> target,
+ byte[] classfile,
+ byte[] dexfile);
+
+ public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
+ ArrayList<Class<?>> classes = new ArrayList<>();
+ ArrayList<byte[]> class_files = new ArrayList<>();
+ ArrayList<byte[]> dex_files = new ArrayList<>();
+
+ for (CommonClassDefinition d : defs) {
+ classes.add(d.target);
+ class_files.add(d.class_file_bytes);
+ dex_files.add(d.dex_file_bytes);
+ }
+ doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
+ class_files.toArray(new byte[0][]),
+ dex_files.toArray(new byte[0][]));
+ }
+
+ public static void addMultiTransformationResults(CommonClassDefinition... defs) {
+ for (CommonClassDefinition d : defs) {
+ addCommonTransformationResult(d.target.getCanonicalName(),
+ d.class_file_bytes,
+ d.dex_file_bytes);
+ }
+ }
+
+ public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
+ byte[][] classfiles,
+ byte[][] dexfiles);
+ public static native void doCommonClassRetransformation(Class<?>... target);
+ public static native void setPopRetransformations(boolean pop);
+ public static native void popTransformationFor(String name);
+ public static native void enableCommonRetransformation(boolean enable);
+ public static native void addCommonTransformationResult(String target_name,
+ byte[] class_bytes,
+ byte[] dex_bytes);
+}
diff --git a/test/986-native-method-bind/src/art/Test986.java b/test/986-native-method-bind/src/art/Test986.java
new file mode 100644
index 0000000000..edd2e9d79f
--- /dev/null
+++ b/test/986-native-method-bind/src/art/Test986.java
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+package art;
+
+import java.lang.reflect.Method;
+import java.util.HashMap;
+
+public class Test986 {
+ static {
+ // NB This is called before any setup is done so we don't need to worry about getting bind
+ // events.
+ Main.bindAgentJNIForClass(Test986.class);
+ }
+
+
+ private static final HashMap<Method, String> SymbolMap = new HashMap<>();
+
+ // A class with a native method we can play with.
+ static class Transform {
+ private static native void sayHi();
+ }
+
+ public static void run() throws Exception {
+ setupNativeBindNotify();
+ setNativeBindNotify(true);
+ doTest();
+ }
+
+ private static void setNativeTransform(Method method, String dest) {
+ SymbolMap.put(method, dest);
+ }
+
+ /**
+ * Notifies java that a native method bind has occurred and requests the new symbol to bind to.
+ */
+ public static String doNativeMethodBind(Method method, String nativeSym) {
+ // Disable native bind notify for now to avoid infinite loops.
+ setNativeBindNotify(false);
+ String transSym = SymbolMap.getOrDefault(method, nativeSym);
+ System.out.println(method + " = " + nativeSym + " -> " + transSym);
+ setNativeBindNotify(true);
+ return transSym;
+ }
+
+ public static void doTest() throws Exception {
+ Method say_hi_method = Transform.class.getDeclaredMethod("sayHi");
+ // TODO We should test auto-binding but due to the way this is run that will be annoying.
+ Main.bindAgentJNIForClass(Transform.class);
+ Transform.sayHi();
+ setNativeTransform(say_hi_method, "NoReallySayGoodbye");
+ Main.bindAgentJNIForClass(Transform.class);
+ Transform.sayHi();
+ Main.bindAgentJNIForClass(Main.class);
+ Main.bindAgentJNIForClass(Test986.class);
+ }
+
+ // Functions called from native code.
+ public static void doSayHi() {
+ System.out.println("Hello");
+ }
+
+ public static void doSayBye() {
+ System.out.println("Bye");
+ }
+
+ private static native void setNativeBindNotify(boolean enable);
+ private static native void setupNativeBindNotify();
+}
diff --git a/test/Android.bp b/test/Android.bp
index c5d96da20c..15b3f8a438 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -275,6 +275,7 @@ art_cc_defaults {
"933-misc-events/misc_events.cc",
"945-obsolete-native/obsolete_native.cc",
"984-obsolete-invoke/obsolete_invoke.cc",
+ "986-native-method-bind/native_bind.cc",
],
shared_libs: [
"libbase",