kOptimizing
};
+ enum JniOptimizationFlags {
+ kNone,
+ kFastNative,
+ };
+
static Compiler* Create(CompilerDriver* driver, Kind kind);
virtual void Init() = 0;
virtual CompiledMethod* JniCompile(uint32_t access_flags,
uint32_t method_idx,
- const DexFile& dex_file) const = 0;
+ const DexFile& dex_file,
+ JniOptimizationFlags optimization_flags) const = 0;
virtual bool JitCompile(Thread* self ATTRIBUTE_UNUSED,
jit::JitCodeCache* code_cache ATTRIBUTE_UNUSED,
InstructionSetHasGenericJniStub(driver->GetInstructionSet())) {
// Leaving this empty will trigger the generic JNI version
} else {
- compiled_method = driver->GetCompiler()->JniCompile(access_flags, method_idx, dex_file);
+ // Look-up the ArtMethod associated with this code_item (if any)
+ // -- It is later used to lookup any [optimization] annotations for this method.
+ ScopedObjectAccess soa(self);
+ StackHandleScope<1> hs(soa.Self());
+ Handle<mirror::ClassLoader> class_loader_handle(hs.NewHandle(
+ soa.Decode<mirror::ClassLoader*>(class_loader)));
+
+ // TODO: Lookup annotation from DexFile directly without resolving method.
+ ArtMethod* method =
+ Runtime::Current()->GetClassLinker()->ResolveMethod<ClassLinker::kNoICCECheckForCache>(
+ dex_file,
+ method_idx,
+ dex_cache,
+ class_loader_handle,
+ /* referrer */ nullptr,
+ invoke_type);
+
+ bool fast_native = false;
+ if (LIKELY(method != nullptr)) {
+ fast_native = method->IsAnnotatedWithFastNative();
+ } else {
+ // Failed method resolutions happen very rarely, e.g. ancestor class cannot be resolved.
+ DCHECK(self->IsExceptionPending());
+ self->ClearException();
+ }
+
+ Compiler::JniOptimizationFlags optimization_flags =
+ fast_native ? Compiler::kFastNative : Compiler::kNone;
+ compiled_method = driver->GetCompiler()->JniCompile(access_flags,
+ method_idx,
+ dex_file,
+ optimization_flags);
CHECK(compiled_method != nullptr);
}
} else if ((access_flags & kAccAbstract) != 0) {
bool CompilerDriver::IsStringInit(uint32_t method_index, const DexFile* dex_file, int32_t* offset) {
DexFileMethodInliner* inliner = GetMethodInlinerMap()->GetMethodInliner(dex_file);
- PointerSize pointer_size = InstructionSetPointerSize(GetInstructionSet());
+ const PointerSize pointer_size = InstructionSetPointerSize(GetInstructionSet());
*offset = inliner->GetOffsetForStringInit(method_index, pointer_size);
return inliner->IsStringInitMethodIndex(method_index);
}
void StackArgsMixedImpl();
void StackArgsSignExtendedMips64Impl();
+ void NormalNativeImpl();
+ void FastNativeImpl();
+
JNIEnv* env_;
jstring library_search_path_;
jmethodID jmethod_;
JNI_TEST(StackArgsSignExtendedMips64)
+void Java_MyClassNatives_normalNative(JNIEnv*, jclass) {
+ // Intentionally left empty.
+}
+
+// Methods not annotated with anything are not considered "fast native"
+// -- Check that the annotation lookup does not find it.
+void JniCompilerTest::NormalNativeImpl() {
+ SetUpForTest(/* direct */ true,
+ "normalNative",
+ "()V",
+ reinterpret_cast<void*>(&Java_MyClassNatives_normalNative));
+
+ ScopedObjectAccess soa(Thread::Current());
+ ArtMethod* method = soa.DecodeMethod(jmethod_);
+ ASSERT_TRUE(method != nullptr);
+
+ EXPECT_FALSE(method->IsAnnotatedWithFastNative());
+}
+JNI_TEST(NormalNative)
+
+// Methods annotated with @FastNative are considered "fast native"
+// -- Check that the annotation lookup succeeds.
+void Java_MyClassNatives_fastNative(JNIEnv*, jclass) {
+ // Intentionally left empty.
+}
+
+void JniCompilerTest::FastNativeImpl() {
+ SetUpForTest(/* direct */ true,
+ "fastNative",
+ "()V",
+ reinterpret_cast<void*>(&Java_MyClassNatives_fastNative));
+
+ ScopedObjectAccess soa(Thread::Current());
+ ArtMethod* method = soa.DecodeMethod(jmethod_);
+ ASSERT_TRUE(method != nullptr);
+
+ EXPECT_TRUE(method->IsAnnotatedWithFastNative());
+}
+JNI_TEST(FastNative)
+
} // namespace art
#include "jni_compiler.h"
#include <algorithm>
+#include <ios>
#include <memory>
#include <vector>
#include <fstream>
#include "utils/mips/managed_register_mips.h"
#include "utils/mips64/managed_register_mips64.h"
#include "utils/x86/managed_register_x86.h"
+#include "utils.h"
#include "thread.h"
#define __ jni_asm->
namespace art {
+using JniOptimizationFlags = Compiler::JniOptimizationFlags;
+
template <PointerSize kPointerSize>
static void CopyParameter(JNIMacroAssembler<kPointerSize>* jni_asm,
ManagedRuntimeCallingConvention* mr_conv,
static CompiledMethod* ArtJniCompileMethodInternal(CompilerDriver* driver,
uint32_t access_flags,
uint32_t method_idx,
- const DexFile& dex_file) {
+ const DexFile& dex_file,
+ JniOptimizationFlags optimization_flags) {
const bool is_native = (access_flags & kAccNative) != 0;
CHECK(is_native);
const bool is_static = (access_flags & kAccStatic) != 0;
InstructionSet instruction_set = driver->GetInstructionSet();
const InstructionSetFeatures* instruction_set_features = driver->GetInstructionSetFeatures();
+ // i.e. if the method was annotated with @FastNative
+ const bool is_fast_native =
+ (static_cast<uint32_t>(optimization_flags) & Compiler::kFastNative) != 0;
+
+ VLOG(jni) << "JniCompile: Method :: "
+ << art::PrettyMethod(method_idx, dex_file, /* with signature */ true)
+ << " :: access_flags = " << std::hex << access_flags << std::dec;
+
+ if (UNLIKELY(is_fast_native)) {
+ VLOG(jni) << "JniCompile: Fast native method detected :: "
+ << art::PrettyMethod(method_idx, dex_file, /* with signature */ true);
+ }
+
ArenaPool pool;
ArenaAllocator arena(&pool);
ThreadOffset<kPointerSize> jni_start =
is_synchronized
? QUICK_ENTRYPOINT_OFFSET(kPointerSize, pJniMethodStartSynchronized)
- : QUICK_ENTRYPOINT_OFFSET(kPointerSize, pJniMethodStart);
+ : (is_fast_native
+ ? QUICK_ENTRYPOINT_OFFSET(kPointerSize, pJniMethodFastStart)
+ : QUICK_ENTRYPOINT_OFFSET(kPointerSize, pJniMethodStart));
+
main_jni_conv->ResetIterator(FrameOffset(main_out_arg_size));
FrameOffset locked_object_handle_scope_offset(0);
if (is_synchronized) {
}
// thread.
end_jni_conv->ResetIterator(FrameOffset(end_out_arg_size));
+
ThreadOffset<kPointerSize> jni_end(-1);
if (reference_return) {
// Pass result.
} else {
jni_end = is_synchronized
? QUICK_ENTRYPOINT_OFFSET(kPointerSize, pJniMethodEndSynchronized)
- : QUICK_ENTRYPOINT_OFFSET(kPointerSize, pJniMethodEnd);
+ : (is_fast_native
+ ? QUICK_ENTRYPOINT_OFFSET(kPointerSize, pJniMethodFastEnd)
+ : QUICK_ENTRYPOINT_OFFSET(kPointerSize, pJniMethodEnd));
}
// Pass saved local reference state.
if (end_jni_conv->IsCurrentParamOnStack()) {
}
}
-CompiledMethod* ArtQuickJniCompileMethod(CompilerDriver* compiler, uint32_t access_flags,
- uint32_t method_idx, const DexFile& dex_file) {
+CompiledMethod* ArtQuickJniCompileMethod(CompilerDriver* compiler,
+ uint32_t access_flags,
+ uint32_t method_idx,
+ const DexFile& dex_file,
+ Compiler::JniOptimizationFlags optimization_flags) {
if (Is64BitInstructionSet(compiler->GetInstructionSet())) {
return ArtJniCompileMethodInternal<PointerSize::k64>(
- compiler, access_flags, method_idx, dex_file);
+ compiler, access_flags, method_idx, dex_file, optimization_flags);
} else {
return ArtJniCompileMethodInternal<PointerSize::k32>(
- compiler, access_flags, method_idx, dex_file);
+ compiler, access_flags, method_idx, dex_file, optimization_flags);
}
}
#ifndef ART_COMPILER_JNI_QUICK_JNI_COMPILER_H_
#define ART_COMPILER_JNI_QUICK_JNI_COMPILER_H_
+#include "compiler.h"
#include "dex_file.h"
namespace art {
class CompilerDriver;
class CompiledMethod;
-CompiledMethod* ArtQuickJniCompileMethod(CompilerDriver* compiler, uint32_t access_flags,
- uint32_t method_idx, const DexFile& dex_file);
+CompiledMethod* ArtQuickJniCompileMethod(CompilerDriver* compiler,
+ uint32_t access_flags,
+ uint32_t method_idx,
+ const DexFile& dex_file,
+ Compiler::JniOptimizationFlags optimization_flags);
} // namespace art
EXPECT_EQ(72U, sizeof(OatHeader));
EXPECT_EQ(4U, sizeof(OatMethodOffsets));
EXPECT_EQ(20U, sizeof(OatQuickMethodHeader));
- EXPECT_EQ(162 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)),
+ EXPECT_EQ(164 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)),
sizeof(QuickEntryPoints));
}
CompiledMethod* JniCompile(uint32_t access_flags,
uint32_t method_idx,
- const DexFile& dex_file) const OVERRIDE {
- return ArtQuickJniCompileMethod(GetCompilerDriver(), access_flags, method_idx, dex_file);
+ const DexFile& dex_file,
+ JniOptimizationFlags optimization_flags) const OVERRIDE {
+ return ArtQuickJniCompileMethod(GetCompilerDriver(),
+ access_flags,
+ method_idx,
+ dex_file,
+ optimization_flags);
}
uintptr_t GetEntryPointOf(ArtMethod* method) const OVERRIDE
return GetDeclaringClass()->IsInterface();
}
+bool ArtMethod::IsAnnotatedWithFastNative() {
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+ StackHandleScope<1> shs(self);
+
+ const DexFile& dex_file = GetDeclaringClass()->GetDexFile();
+
+ mirror::Class* fast_native_annotation =
+ soa.Decode<mirror::Class*>(WellKnownClasses::dalvik_annotation_optimization_FastNative);
+ Handle<mirror::Class> fast_native_handle(shs.NewHandle(fast_native_annotation));
+
+ // Note: Resolves any method annotations' classes as a side-effect.
+ // -- This seems allowed by the spec since it says we can preload any classes
+ // referenced by another classes's constant pool table.
+ return dex_file.IsMethodAnnotationPresent(this, fast_native_handle, DexFile::kDexVisibilityBuild);
+}
+
bool ArtMethod::EqualParameters(Handle<mirror::ObjectArray<mirror::Class>> params) {
auto* dex_cache = GetDexCache();
auto* dex_file = dex_cache->GetDexFile();
return (GetAccessFlags() & kAccMustCountLocks) != 0;
}
+ // Checks to see if the method was annotated with @dalvik.annotation.optimization.FastNative
+ // -- Independent of kAccFastNative access flags.
+ bool IsAnnotatedWithFastNative();
+
// Returns true if this method could be overridden by a default method.
bool IsOverridableByDefaultMethod() SHARED_REQUIRES(Locks::mutator_lock_);
art::Thread::SelfOffset<POINTER_SIZE>().Int32Value())
// Offset of field Thread::tlsPtr_.thread_local_objects.
-#define THREAD_LOCAL_OBJECTS_OFFSET (THREAD_CARD_TABLE_OFFSET + 197 * __SIZEOF_POINTER__)
+#define THREAD_LOCAL_OBJECTS_OFFSET (THREAD_CARD_TABLE_OFFSET + 199 * __SIZEOF_POINTER__)
ADD_TEST_EQ(THREAD_LOCAL_OBJECTS_OFFSET,
art::Thread::ThreadLocalObjectsOffset<POINTER_SIZE>().Int32Value())
// Offset of field Thread::tlsPtr_.thread_local_pos.
return GetSignatureValue(method_class, annotation_set);
}
-bool DexFile::IsMethodAnnotationPresent(ArtMethod* method, Handle<mirror::Class> annotation_class)
+bool DexFile::IsMethodAnnotationPresent(ArtMethod* method,
+ Handle<mirror::Class> annotation_class,
+ uint32_t visibility /* = kDexVisibilityRuntime */)
const {
const AnnotationSetItem* annotation_set = FindAnnotationSetForMethod(method);
if (annotation_set == nullptr) {
}
StackHandleScope<1> hs(Thread::Current());
Handle<mirror::Class> method_class(hs.NewHandle(method->GetDeclaringClass()));
- const AnnotationItem* annotation_item = GetAnnotationItemFromAnnotationSet(
- method_class, annotation_set, kDexVisibilityRuntime, annotation_class);
+ const AnnotationItem* annotation_item = GetAnnotationItemFromAnnotationSet(method_class,
+ annotation_set,
+ visibility,
+ annotation_class);
return annotation_item != nullptr;
}
SHARED_REQUIRES(Locks::mutator_lock_);
mirror::ObjectArray<mirror::String>* GetSignatureAnnotationForMethod(ArtMethod* method) const
SHARED_REQUIRES(Locks::mutator_lock_);
- bool IsMethodAnnotationPresent(ArtMethod* method, Handle<mirror::Class> annotation_class) const
+ bool IsMethodAnnotationPresent(ArtMethod* method,
+ Handle<mirror::Class> annotation_class,
+ uint32_t visibility = kDexVisibilityRuntime) const
SHARED_REQUIRES(Locks::mutator_lock_);
const AnnotationSetItem* FindAnnotationSetForClass(Handle<mirror::Class> klass) const
// JNI
qpoints->pJniMethodStart = JniMethodStart;
+ qpoints->pJniMethodFastStart = JniMethodFastStart;
qpoints->pJniMethodStartSynchronized = JniMethodStartSynchronized;
qpoints->pJniMethodEnd = JniMethodEnd;
qpoints->pJniMethodEndSynchronized = JniMethodEndSynchronized;
qpoints->pJniMethodEndWithReference = JniMethodEndWithReference;
qpoints->pJniMethodEndWithReferenceSynchronized = JniMethodEndWithReferenceSynchronized;
+ qpoints->pJniMethodFastEnd = JniMethodFastEnd;
qpoints->pQuickGenericJniTrampoline = art_quick_generic_jni_trampoline;
// Locks
// JNI entrypoints.
// TODO: NO_THREAD_SAFETY_ANALYSIS due to different control paths depending on fast JNI.
extern uint32_t JniMethodStart(Thread* self) NO_THREAD_SAFETY_ANALYSIS HOT_ATTR;
+extern uint32_t JniMethodFastStart(Thread* self) NO_THREAD_SAFETY_ANALYSIS HOT_ATTR;
extern uint32_t JniMethodStartSynchronized(jobject to_lock, Thread* self)
NO_THREAD_SAFETY_ANALYSIS HOT_ATTR;
extern void JniMethodEnd(uint32_t saved_local_ref_cookie, Thread* self)
NO_THREAD_SAFETY_ANALYSIS HOT_ATTR;
+extern void JniMethodFastEnd(uint32_t saved_local_ref_cookie, Thread* self)
+ NO_THREAD_SAFETY_ANALYSIS HOT_ATTR;
extern void JniMethodEndSynchronized(uint32_t saved_local_ref_cookie, jobject locked,
Thread* self)
NO_THREAD_SAFETY_ANALYSIS HOT_ATTR;
V(HandleFillArrayData, void, void*, void*) \
\
V(JniMethodStart, uint32_t, Thread*) \
+ V(JniMethodFastStart, uint32_t, Thread*) \
V(JniMethodStartSynchronized, uint32_t, jobject, Thread*) \
V(JniMethodEnd, void, uint32_t, Thread*) \
+ V(JniMethodFastEnd, void, uint32_t, Thread*) \
V(JniMethodEndSynchronized, void, uint32_t, jobject, Thread*) \
V(JniMethodEndWithReference, mirror::Object*, jobject, uint32_t, Thread*) \
V(JniMethodEndWithReferenceSynchronized, mirror::Object*, jobject, uint32_t, jobject, Thread*) \
V(ReadBarrierMarkReg28, mirror::Object*, mirror::Object*) \
V(ReadBarrierMarkReg29, mirror::Object*, mirror::Object*) \
V(ReadBarrierSlow, mirror::Object*, mirror::Object*, mirror::Object*, uint32_t) \
- V(ReadBarrierForRootSlow, mirror::Object*, GcRoot<mirror::Object>*)
+ V(ReadBarrierForRootSlow, mirror::Object*, GcRoot<mirror::Object>*) \
+\
#endif // ART_RUNTIME_ENTRYPOINTS_QUICK_QUICK_ENTRYPOINTS_LIST_H_
#undef ART_RUNTIME_ENTRYPOINTS_QUICK_QUICK_ENTRYPOINTS_LIST_H_ // #define is only for lint.
handle_on_stack->Assign(to_ref);
}
+// Called on entry to fast JNI, push a new local reference table only.
+extern uint32_t JniMethodFastStart(Thread* self) {
+ JNIEnvExt* env = self->GetJniEnv();
+ DCHECK(env != nullptr);
+ uint32_t saved_local_ref_cookie = env->local_ref_cookie;
+ env->local_ref_cookie = env->locals.GetSegmentState();
+
+ if (kIsDebugBuild) {
+ ArtMethod* native_method = *self->GetManagedStack()->GetTopQuickFrame();
+ CHECK(native_method->IsAnnotatedWithFastNative()) << PrettyMethod(native_method);
+ }
+
+ return saved_local_ref_cookie;
+}
+
// Called on entry to JNI, transition out of Runnable and release share of mutator_lock_.
extern uint32_t JniMethodStart(Thread* self) {
JNIEnvExt* env = self->GetJniEnv();
self->PopHandleScope();
}
+// TODO: These should probably be templatized or macro-ized.
+// Otherwise there's just too much repetitive boilerplate.
+
extern void JniMethodEnd(uint32_t saved_local_ref_cookie, Thread* self) {
GoToRunnable(self);
PopLocalReferences(saved_local_ref_cookie, self);
}
+extern void JniMethodFastEnd(uint32_t saved_local_ref_cookie, Thread* self) {
+ // inlined fast version of GoToRunnable(self);
+
+ if (kIsDebugBuild) {
+ ArtMethod* native_method = *self->GetManagedStack()->GetTopQuickFrame();
+ CHECK(native_method->IsAnnotatedWithFastNative()) << PrettyMethod(native_method);
+ }
+
+ if (UNLIKELY(self->TestAllFlags())) {
+ // In fast JNI mode we never transitioned out of runnable. Perform a suspend check if there
+ // is a flag raised.
+ DCHECK(Locks::mutator_lock_->IsSharedHeld(self));
+ self->CheckSuspend();
+ }
+
+ PopLocalReferences(saved_local_ref_cookie, self);
+}
+
extern void JniMethodEndSynchronized(uint32_t saved_local_ref_cookie, jobject locked,
Thread* self) {
GoToRunnable(self);
PopLocalReferences(saved_local_ref_cookie, self);
}
+// TODO: JniMethodFastEndWithReference
+// (Probably don't need to have a synchronized variant since
+// it already has to do atomic operations)
+
// Common result handling for EndWithReference.
static mirror::Object* JniMethodEndWithReferenceHandleResult(jobject result,
uint32_t saved_local_ref_cookie,
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAputObjectWithBoundCheck, pAputObject, sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAputObject, pHandleFillArrayData, sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pHandleFillArrayData, pJniMethodStart, sizeof(void*));
- EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pJniMethodStart, pJniMethodStartSynchronized,
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pJniMethodStart, pJniMethodFastStart,
+ sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pJniMethodFastStart, pJniMethodStartSynchronized,
sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pJniMethodStartSynchronized, pJniMethodEnd,
sizeof(void*));
- EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pJniMethodEnd, pJniMethodEndSynchronized, sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pJniMethodEnd, pJniMethodFastEnd, sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pJniMethodFastEnd, pJniMethodEndSynchronized, sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pJniMethodEndSynchronized, pJniMethodEndWithReference,
sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pJniMethodEndWithReference,
VLOG(jni) << "[Registering JNI native method " << PrettyMethod(m) << "]";
+ is_fast = is_fast || m->IsFastNative(); // Merge with @FastNative state.
m->RegisterNative(fnPtr, is_fast);
}
return JNI_OK;
args.SetIfMissing(M::ParallelGCThreads, gc::Heap::kDefaultEnableParallelGC ?
static_cast<unsigned int>(sysconf(_SC_NPROCESSORS_CONF) - 1u) : 0u);
- // -Xverbose:
+ // -verbose:
{
LogVerbosity *log_verbosity = args.Get(M::Verbose);
if (log_verbosity != nullptr) {
QUICK_ENTRY_POINT_INFO(pReadBarrierMarkReg29)
QUICK_ENTRY_POINT_INFO(pReadBarrierSlow)
QUICK_ENTRY_POINT_INFO(pReadBarrierForRootSlow)
+
+ QUICK_ENTRY_POINT_INFO(pJniMethodFastStart)
+ QUICK_ENTRY_POINT_INFO(pJniMethodFastEnd)
#undef QUICK_ENTRY_POINT_INFO
os << offset;
namespace art {
jclass WellKnownClasses::com_android_dex_Dex;
+jclass WellKnownClasses::dalvik_annotation_optimization_FastNative;
jclass WellKnownClasses::dalvik_system_DexFile;
jclass WellKnownClasses::dalvik_system_DexPathList;
jclass WellKnownClasses::dalvik_system_DexPathList__Element;
void WellKnownClasses::Init(JNIEnv* env) {
com_android_dex_Dex = CacheClass(env, "com/android/dex/Dex");
+ dalvik_annotation_optimization_FastNative = CacheClass(env, "dalvik/annotation/optimization/FastNative");
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");
SHARED_REQUIRES(Locks::mutator_lock_);
static jclass com_android_dex_Dex;
+ static jclass dalvik_annotation_optimization_FastNative;
static jclass dalvik_system_DexFile;
static jclass dalvik_system_DexPathList;
static jclass dalvik_system_DexPathList__Element;
static JavaVM* jvm = nullptr;
+static jint Java_Main_intFastNativeMethod(JNIEnv*, jclass, jint a, jint b, jint c);
+
+static JNINativeMethod sMainMethods[] = {
+ {"intFastNativeMethod", "(III)I", reinterpret_cast<void*>(Java_Main_intFastNativeMethod) }
+};
+
extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void*) {
CHECK(vm != nullptr);
CHECK(jvm == nullptr);
jvm = vm;
std::cout << "JNI_OnLoad called" << std::endl;
+
return JNI_VERSION_1_6;
}
InvokeSpecificMethod(e, l, "sayHi");
}
+// Register on-demand because many tests share this JNI library and
+// we can't unconditionally register them.
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_registerNativesJniTest(JNIEnv* e, jclass kls) {
+ const size_t numMethods = sizeof(sMainMethods)/sizeof(JNINativeMethod);
+
+ if (e->RegisterNatives(kls, sMainMethods, numMethods) < 0) {
+ std::cerr << "RegisterNatives failed for 'Main'" << std::endl;
+ return JNI_FALSE;
+ }
+
+ return JNI_TRUE;
+}
+
+// Annotated with @FastNative in Java code. Doesn't need to be explicitly registered with "!".
+// NOTE: Has to be registered explicitly to avoid mutator lock check failures.
+static jint Java_Main_intFastNativeMethod(JNIEnv*, jclass, jint a, jint b, jint c) {
+ return a + b + c;
+}
+
} // namespace art
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
+import dalvik.annotation.optimization.FastNative;
+
public class Main {
public static void main(String[] args) {
System.loadLibrary(args[0]);
testInvokeLambdaMethod(() -> { System.out.println("hi-lambda: " + lambda); });
String def = "δ";
testInvokeLambdaDefaultMethod(() -> { System.out.println("hi-default " + def + lambda); });
+
+ registerNativesJniTest();
+ testFastNativeMethods();
}
+ private static native boolean registerNativesJniTest();
+
private static native void testCallDefaultMethods();
private static native void testFindClassOnAttachedNativeThread();
private static native void testInvokeLambdaMethod(LambdaInterface iface);
private static native void testInvokeLambdaDefaultMethod(LambdaInterface iface);
+
+ // Test invoking @FastNative methods works correctly.
+
+ // Return sum of a+b+c.
+ @FastNative
+ static native int intFastNativeMethod(int a, int b, int c);
+
+ private static void testFastNativeMethods() {
+ int returns[] = { 0, 3, 6, 9, 12 };
+ for (int i = 0; i < returns.length; i++) {
+ int result = intFastNativeMethod(i, i, i);
+ if (returns[i] != result) {
+ System.out.println("FastNative Int Run " + i + " with " + returns[i] + " vs " + result);
+ throw new AssertionError();
+ }
+ }
+ }
+
+
}
@FunctionalInterface
* limitations under the License.
*/
+import dalvik.annotation.optimization.FastNative;
+
class MyClassNatives {
native void throwException();
native void foo();
static native boolean returnTrue();
static native boolean returnFalse();
static native int returnInt();
+
+ // Check for @FastNative annotation presence [or lack of presence].
+ public static native void normalNative();
+ @FastNative
+ public static native void fastNative();
}