#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"
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() {
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_);
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_
// 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.
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);
}
}
}
#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,
}
// 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.
// 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;
}
ThreadUtil::Register(&gEventHandler);
ClassUtil::Register(&gEventHandler);
DumpUtil::Register(&gEventHandler);
+ MethodUtil::Register(&gEventHandler);
SearchUtil::Register();
HeapUtil::Register();
ThreadUtil::Unregister();
ClassUtil::Unregister();
DumpUtil::Unregister();
+ MethodUtil::Unregister();
SearchUtil::Unregister();
HeapUtil::Unregister();
.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,
}
}
+// 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.
#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) {
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);
#include <algorithm>
+#include "art_method.h"
#include "base/macros.h"
#include "class_linker.h"
#include "thread.h"
}
}
+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
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
/*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_);
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
--- /dev/null
+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
--- /dev/null
+Tests native-method-bind callback and native method replacement.
--- /dev/null
+/*
+ * 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
--- /dev/null
+#!/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
--- /dev/null
+/*
+ * 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();
+ }
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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();
+}
"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",