static jint RegisterNatives(JNIEnv*
env
,
2460 jclass java_class,
2461 const JNINativeMethod* methods,
2462 jint method_count) {
2463
if
(UNLIKELY(method_count < 0)) {
2464 JavaVmExtFromEnv(
env
)->JniAbortF(
"RegisterNatives"
,
"negative method count: %d"
,
2465 method_count);
2466
return
JNI_ERR;
//
Not reached except
in
unit tests.
2467 }
2468 CHECK_NON_NULL_ARGUMENT_FN_NAME(
"RegisterNatives"
, java_class, JNI_ERR);
2469 ScopedObjectAccess soa(
env
);
2470 StackHandleScope<1> hs(soa.Self());
2471 Handle<mirror::Class> c = hs.NewHandle(soa.Decode<mirror::Class>(java_class));
2472
if
(UNLIKELY(method_count == 0)) {
2473 LOG(WARNING) <<
"JNI RegisterNativeMethods: attempt to register 0 native methods for "
2474 << c->PrettyDescriptor();
2475
return
JNI_OK;
2476 }
2477 CHECK_NON_NULL_ARGUMENT_FN_NAME(
"RegisterNatives"
, methods, JNI_ERR);
2478
for
(jint i = 0; i < method_count; ++i) {
2479 const char* name = methods[i].name;
2480 const char* sig = methods[i].signature;
2481 const void* fnPtr = methods[i].fnPtr;
2482
if
(UNLIKELY(name == nullptr)) {
2483 ReportInvalidJNINativeMethod(soa, c.Get(),
"method name"
, i);
2484
return
JNI_ERR;
2485 }
else
if
(UNLIKELY(sig == nullptr)) {
2486 ReportInvalidJNINativeMethod(soa, c.Get(),
"method signature"
, i);
2487
return
JNI_ERR;
2488 }
else
if
(UNLIKELY(fnPtr == nullptr)) {
2489 ReportInvalidJNINativeMethod(soa, c.Get(),
"native function"
, i);
2490
return
JNI_ERR;
2491 }
2492 bool is_fast =
false
;
2493
//
Notes about fast JNI calls:
2494
//
2495
//
On a normal JNI call, the calling thread usually transitions
2496
//
from the kRunnable state to the kNative state. But
if
the
2497
//
called native
function
needs to access any Java object, it
2498
//
will have to transition back to the kRunnable state.
2499
//
2500
//
There is a cost to this double transition. For a JNI call
2501
//
that should be quick, this cost may dominate the call cost.
2502
//
2503
//
On a fast JNI call, the calling thread avoids this double
2504
//
transition by not transitioning from kRunnable to kNative and
2505
//
stays
in
the kRunnable state.
2506
//
2507
//
There are risks to using a fast JNI call because it can delay
2508
//
a response to a thread suspension request
which
is typically
2509
//
used
for
a GC root scanning, etc. If a fast JNI call takes a
2510
//
long
time
, it could cause longer thread suspension latency
2511
//
and GC pauses.
2512
//
2513
//
Thus, fast JNI should be used with care. It should be used
2514
//
for
a JNI call that takes a short amount of
time
(eg. no
2515
//
long-running loop) and does not block (eg. no locks, I
/O
,
2516
//
etc.)
2517
//
2518
//
A
'!'
prefix
in
the signature
in
the JNINativeMethod
2519
//
indicates that it's a fast JNI call and the runtime omits the
2520
//
thread state transition from kRunnable to kNative at the
2521
//
entry.
2522
if
(*sig ==
'!'
) {
2523 is_fast =
true
;
2524 ++sig;
2525 }
2526
2527
//
Note: the right order is to try to
find
the method locally
2528
//
first, either as a direct or a virtual method. Then move to
2529
//
the parent.
2530 ArtMethod* m = nullptr;
2531 bool warn_on_going_to_parent = down_cast<JNIEnvExt*>(
env
)->GetVm()->IsCheckJniEnabled();
2532
for
(ObjPtr<mirror::Class> current_class = c.Get();
2533 current_class != nullptr;
2534 current_class = current_class->GetSuperClass()) {
2535
//
Search first only comparing methods
which
are native.
2536 m = FindMethod<
true
>(current_class, name, sig);
2537
if
(m != nullptr) {
2538
break
;
2539 }
2540
2541
//
Search again comparing to all methods, to
find
non-native methods that match.
2542 m = FindMethod<
false
>(current_class, name, sig);
2543
if
(m != nullptr) {
2544
break
;
2545 }
2546
2547
if
(warn_on_going_to_parent) {
2548 LOG(WARNING) <<
"CheckJNI: method to register \""
<< name <<
"\" not in the given class. "
2549 <<
"This is slow, consider changing your RegisterNatives calls."
;
2550 warn_on_going_to_parent =
false
;
2551 }
2552 }
2553
2554
if
(m == nullptr) {
2555 c->DumpClass(LOG_STREAM(ERROR), mirror::Class::kDumpClassFullDetail);
2556 LOG(ERROR)
2557 <<
"Failed to register native method "
2558 << c->PrettyDescriptor() <<
"."
<< name << sig <<
" in "
2559 << c->GetDexCache()->GetLocation()->ToModifiedUtf8();
2560 ThrowNoSuchMethodError(soa, c.Get(), name, sig,
"static or non-static"
);
2561
return
JNI_ERR;
2562 }
else
if
(!m->IsNative()) {
2563 LOG(ERROR)
2564 <<
"Failed to register non-native method "
2565 << c->PrettyDescriptor() <<
"."
<< name << sig
2566 <<
" as native"
;
2567 ThrowNoSuchMethodError(soa, c.Get(), name, sig,
"native"
);
2568
return
JNI_ERR;
2569 }
2570
2571 VLOG(jni) <<
"[Registering JNI native method "
<< m->PrettyMethod() <<
"]"
;
2572
2573
if
(UNLIKELY(is_fast)) {
2574
//
There are a few reasons to switch:
2575
//
1) We don't support !bang JNI anymore, it will turn to a hard error later.
2576
//
2) @FastNative is actually faster. At least 1.5x faster than !bang JNI.
2577
//
and switching is super easy, remove !
in
C code, add annotation
in
.java code.
2578
//
3) Good chance of hitting DCHECK failures
in
ScopedFastNativeObjectAccess
2579
//
since that checks
for
presence of @FastNative and not
for
!
in
the descriptor.
2580 LOG(WARNING) <<
"!bang JNI is deprecated. Switch to @FastNative for "
<< m->PrettyMethod();
2581 is_fast =
false
;
2582
//
TODO:
make
this a hard register error
in
the future.
2583 }
2584
2585 const void* final_function_ptr = m->RegisterNative(fnPtr);
2586 UNUSED(final_function_ptr);
2587 }
2588
return
JNI_OK;
2589 }