In computer science, a type signature or type annotation defines the inputs and outputs for a function, subroutine or method. A type signature includes the number of arguments, the types of arguments and the order of the arguments contained by a function. A type signature is typically used during overload resolution for choosing the correct definition of a function to be called among many overloaded forms.
In the Java virtual machine, internal type signatures are used to identify methods and classes at the level of the virtual machine code.
Example: The method String String.substring(int, int) is represented in bytecode as Ljava/lang/String.substring(II)Ljava/lang/String;.
The signature of main() method looks like this: public static void main(String[] args)
And in the disassembled bytecode, it takes the form ofLsome/package/Main/main:([Ljava/lang/String;)V.
The method signature for the main() method contains three modifiers: public indicates that the main() method can be called by any object. static indicates that the main() method is a class method. void indicates that the main() method has no return value.
Compiled from "Toast.java"
public class android.widget.Toast {
public static final int LENGTH_LONG;
descriptor: I
public static final int LENGTH_SHORT;
descriptor: I
public android.widget.Toast(android.content.Context);
descriptor: (Landroid/content/Context;)V
public void show();
descriptor: ()V
public void cancel();
descriptor: ()V
public void setView(android.view.View);
descriptor: (Landroid/view/View;)V
public android.view.View getView();
descriptor: ()Landroid/view/View;
public void setDuration(int);
descriptor: (I)V
public int getDuration();
descriptor: ()I
public void setMargin(float, float);
descriptor: (FF)V
public float getHorizontalMargin();
descriptor: ()F
public float getVerticalMargin();
descriptor: ()F
public void setGravity(int, int, int);
descriptor: (III)V
public int getGravity();
descriptor: ()I
public int getXOffset();
descriptor: ()I
public int getYOffset();
descriptor: ()I
public static android.widget.Toast makeText(android.content.Context, java.lang.CharSequence, int);
descriptor: (Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;
public static android.widget.Toast makeText(android.content.Context, int, int) throws android.content.res.Resources$NotFoundException;
descriptor: (Landroid/content/Context;II)Landroid/widget/Toast;
public void setText(int);
descriptor: (I)V
public void setText(java.lang.CharSequence);
descriptor: (Ljava/lang/CharSequence;)V
}
if (UNLIKELY(is_fast)) {
// There are a few reasons to switch:// 1) We don't support !bang JNI anymore, it will turn to a hard error later.// 2) @FastNative is actually faster. At least 1.5x faster than !bang JNI.// and switching is super easy, remove ! in C code, add annotation in .java code.// 3) Good chance of hitting DCHECK failures in ScopedFastNativeObjectAccess// since that checks for presence of @FastNative and not for ! in the descriptor.
LOG(WARNING) << "!bang JNI is deprecated. Switch to @FastNative for " << m->PrettyMethod();
is_fast = false;
// TODO: make this a hard register error in the future.
}
static jint RegisterNatives(JNIEnv* env,
jclass java_class,
const JNINativeMethod* methods,
jint method_count){
if (UNLIKELY(method_count < 0)) {
JavaVmExtFromEnv(env)->JniAbortF("RegisterNatives", "negative method count: %d",
method_count);
return JNI_ERR; // Not reached except in unit tests.
}
CHECK_NON_NULL_ARGUMENT_FN_NAME("RegisterNatives", java_class, JNI_ERR);
ScopedObjectAccess soa(env);
StackHandleScope<1> hs(soa.Self());
Handle<mirror::Class> c = hs.NewHandle(soa.Decode<mirror::Class>(java_class));
if (UNLIKELY(method_count == 0)) {
LOG(WARNING) << "JNI RegisterNativeMethods: attempt to register 0 native methods for "
<< c->PrettyDescriptor();
return JNI_OK;
}
CHECK_NON_NULL_ARGUMENT_FN_NAME("RegisterNatives", methods, JNI_ERR);
for (jint i = 0; i < method_count; ++i) {
constchar* name = methods[i].name;
constchar* sig = methods[i].signature;
constvoid* fnPtr = methods[i].fnPtr;
if (UNLIKELY(name == nullptr)) {
ReportInvalidJNINativeMethod(soa, c.Get(), "method name", i);
return JNI_ERR;
} elseif (UNLIKELY(sig == nullptr)) {
ReportInvalidJNINativeMethod(soa, c.Get(), "method signature", i);
return JNI_ERR;
} elseif (UNLIKELY(fnPtr == nullptr)) {
ReportInvalidJNINativeMethod(soa, c.Get(), "native function", i);
return JNI_ERR;
}
bool is_fast = false;
// Notes about fast JNI calls://// On a normal JNI call, the calling thread usually transitions// from the kRunnable state to the kNative state. But if the// called native function needs to access any Java object, it// will have to transition back to the kRunnable state.//// There is a cost to this double transition. For a JNI call// that should be quick, this cost may dominate the call cost.//// On a fast JNI call, the calling thread avoids this double// transition by not transitioning from kRunnable to kNative and// stays in the kRunnable state.//// There are risks to using a fast JNI call because it can delay// a response to a thread suspension request which is typically// used for a GC root scanning, etc. If a fast JNI call takes a// long time, it could cause longer thread suspension latency// and GC pauses.//// Thus, fast JNI should be used with care. It should be used// for a JNI call that takes a short amount of time (eg. no// long-running loop) and does not block (eg. no locks, I/O,// etc.)//// A '!' prefix in the signature in the JNINativeMethod// indicates that it's a fast JNI call and the runtime omits the// thread state transition from kRunnable to kNative at the// entry.if (*sig == '!') {
is_fast = true;
++sig;
}
// Note: the right order is to try to find the method locally// first, either as a direct or a virtual method. Then move to// the parent.
ArtMethod* m = nullptr;
bool warn_on_going_to_parent = down_cast<JNIEnvExt*>(env)->GetVm()->IsCheckJniEnabled();
for (ObjPtr<mirror::Class> current_class = c.Get();
current_class != nullptr;
current_class = current_class->GetSuperClass()) {
// Search first only comparing methods which are native.
m = FindMethod<true>(current_class, name, sig);
if (m != nullptr) {
break;
}
// Search again comparing to all methods, to find non-native methods that match.
m = FindMethod<false>(current_class, name, sig);
if (m != nullptr) {
break;
}
if (warn_on_going_to_parent) {
LOG(WARNING) << "CheckJNI: method to register \"" << name << "\" not in the given class. "
<< "This is slow, consider changing your RegisterNatives calls.";
warn_on_going_to_parent = false;
}
}
if (m == nullptr) {
c->DumpClass(LOG_STREAM(ERROR), mirror::Class::kDumpClassFullDetail);
LOG(ERROR)
<< "Failed to register native method "
<< c->PrettyDescriptor() << "." << name << sig << " in "
<< c->GetDexCache()->GetLocation()->ToModifiedUtf8();
ThrowNoSuchMethodError(soa, c.Get(), name, sig, "static or non-static");
return JNI_ERR;
} elseif (!m->IsNative()) {
LOG(ERROR)
<< "Failed to register non-native method "
<< c->PrettyDescriptor() << "." << name << sig
<< " as native";
ThrowNoSuchMethodError(soa, c.Get(), name, sig, "native");
return JNI_ERR;
}
VLOG(jni) << "[Registering JNI native method " << m->PrettyMethod() << "]";
if (UNLIKELY(is_fast)) {
// There are a few reasons to switch:// 1) We don't support !bang JNI anymore, it will turn to a hard error later.// 2) @FastNative is actually faster. At least 1.5x faster than !bang JNI.// and switching is super easy, remove ! in C code, add annotation in .java code.// 3) Good chance of hitting DCHECK failures in ScopedFastNativeObjectAccess// since that checks for presence of @FastNative and not for ! in the descriptor.
LOG(WARNING) << "!bang JNI is deprecated. Switch to @FastNative for " << m->PrettyMethod();
is_fast = false;
// TODO: make this a hard register error in the future.
}
constvoid* final_function_ptr = m->RegisterNative(fnPtr);
UNUSED(final_function_ptr);
}
return JNI_OK;
}