284 lines
13 KiB
C
284 lines
13 KiB
C
/*
|
|
* 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.
|
|
*/
|
|
|
|
/**
|
|
* Compile-time, zero-cost checking of JNI signatures against their C++ function type.
|
|
* This can trigger compile-time assertions if any of the input is invalid:
|
|
* (a) The signature specified does not conform to the JNI function descriptor syntax.
|
|
* (b) The C++ function is itself an invalid JNI function (e.g. missing JNIEnv*, etc).
|
|
* (c) The descriptor does not match the C++ function (e.g. "()V" will not match jint(jint)).
|
|
*
|
|
* The fundamental macros are as following:
|
|
* MAKE_JNI_[FAST_|CRITICAL_]NATIVE_METHOD - Create a checked JNINativeMethod{name, sig, func}.
|
|
* MAKE_JNI_[FAST_|CRITICAL_]NATIVE_METHOD_AUTOSIG - Same as above, but infer the JNI signature.
|
|
*
|
|
* Usage examples:
|
|
* // path/to/package/KlassName.java
|
|
* class KlassName {
|
|
* native jobject normal(int x);
|
|
* @FastNative native jobject fast(int x);
|
|
* @CriticalNative native int critical(long ptr);
|
|
* }
|
|
* // path_to_package_KlassName.cpp
|
|
* jobject KlassName_normal(JNIEnv*,jobject,jint) {...}
|
|
* jobject KlassName_fast(JNIEnv*,jobject,jint) {...}
|
|
* jint KlassName_critical(jlong) {...}
|
|
*
|
|
* // Manually specify each signature:
|
|
* JNINativeMethod[] gMethods = {
|
|
* MAKE_JNI_NATIVE_METHOD("normal", "(I)Ljava/lang/Object;", KlassName_normal),
|
|
* MAKE_JNI_FAST_NATIVE_METHOD("fast", "(I)Ljava/lang/Object;", KlassName_fast),
|
|
* MAKE_JNI_CRITICAL_NATIVE_METHOD("critical", "(Z)I", KlassName_critical),
|
|
* };
|
|
*
|
|
* // Automatically infer the signature:
|
|
* JNINativeMethod[] gMethodsAutomaticSignature = {
|
|
* MAKE_JNI_NATIVE_METHOD_AUTOSIG("normal", KlassName_normal),
|
|
* MAKE_JNI_FAST_NATIVE_METHOD_AUTOSIG("fast", KlassName_fast),
|
|
* MAKE_JNI_CRITICAL_NATIVE_METHOD_AUTOSIG("critical", KlassName_critical),
|
|
* };
|
|
*
|
|
* // and then call JNIEnv::RegisterNatives with gMethods as usual.
|
|
*
|
|
* For convenience the following macros are defined:
|
|
* [FAST_|CRITICAL_]NATIVE_METHOD - Return JNINativeMethod for class, func name, and signature.
|
|
* OVERLOADED_[FAST_|CRITICAL_]NATIVE_METHOD - Same as above but allows a separate func identifier.
|
|
* [FAST_|CRITICAL_]NATIVE_METHOD_AUTOSIG - Return JNINativeMethod, sig inferred from function.
|
|
*
|
|
* The FAST_ prefix corresponds to functions annotated with @FastNative,
|
|
* and the CRITICAL_ prefix corresponds to functions annotated with @CriticalNative.
|
|
* See dalvik.annotation.optimization.CriticalNative for more details.
|
|
*
|
|
* =======================================
|
|
* Checking rules
|
|
* =======================================
|
|
*
|
|
* ---------------------------------------
|
|
* JNI descriptor syntax for functions
|
|
*
|
|
* Refer to "Chapter 3: JNI Types and Data Structures" of the JNI specification
|
|
* under the subsection "Type Signatures" table entry "method type".
|
|
*
|
|
* JNI signatures not conforming to the above syntax are rejected.
|
|
* ---------------------------------------
|
|
* C++ function types
|
|
*
|
|
* A normal or @FastNative JNI function type must be of the form
|
|
*
|
|
* ReturnType (JNIEnv*, jclass|jobject, [ArgTypes...]) {}
|
|
*
|
|
* A @CriticalNative JNI function type:
|
|
*
|
|
* must be of the form... ReturnType ([ArgTypes...]){}
|
|
* and must not contain any Reference Types.
|
|
*
|
|
* Refer to "Chapter 3: JNI Types and Data Structures" of the JNI specification
|
|
* under the subsection "Primitive Types" and "Reference Types" for the list
|
|
* of valid argument/return types.
|
|
*
|
|
* C++ function types not conforming to the above requirements are rejected.
|
|
* ---------------------------------------
|
|
* Matching of C++ function type against JNI function descriptor.
|
|
*
|
|
* Assuming all of the above conditions are met for signature and C++ type validity,
|
|
* then matching between the signature and the type validity can occur:
|
|
*
|
|
* Given a signature (Args...)Ret and the
|
|
* C++ function type of the form "CRet fn(JNIEnv*, jclass|jobject, CArgs...)",
|
|
* or for @CriticalNative of the form "CRet fn(CArgs...)"
|
|
*
|
|
* The number of Args... and the number of CArgs... must be equal.
|
|
*
|
|
* If so, attempt to match every component from the signature and function type
|
|
* against each other:
|
|
*
|
|
* ReturnType:
|
|
* V <-> void
|
|
* ArgumentType
|
|
*
|
|
* ArgumentType:
|
|
* PrimitiveType
|
|
* ReferenceType [except for @CriticalNative]
|
|
*
|
|
* PrimitiveType:
|
|
* Z <-> jboolean
|
|
* B <-> jbyte
|
|
* C <-> jchar
|
|
* S <-> jshort
|
|
* I <-> jint
|
|
* J <-> jlong
|
|
* F <-> jfloat
|
|
* D <-> jdouble
|
|
*
|
|
* ReferenceType:
|
|
* Ljava/lang/String; <-> jstring
|
|
* Ljava/lang/Class; <-> jclass
|
|
* L*; <- jobject
|
|
* Ljava/lang/Throwable; -> jthrowable
|
|
* L*; <- jthrowable
|
|
* [ PrimitiveType <-> ${CPrimitiveType}Array
|
|
* [ ReferenceType <-> jobjectArray
|
|
* [* <- jarray
|
|
*
|
|
* Wherein <-> represents a strong match (if the left or right pattern occurs,
|
|
* then left must match right, otherwise matching fails). <- and -> represent
|
|
* weak matches (that is, other match rules can be still attempted).
|
|
*
|
|
* Sidenote: Whilst a jobject could also represent a jclass, jstring, etc,
|
|
* the stricter approach is taken: the most exact C++ type must be used.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
// The below basic macros do not perform automatic stringification,
|
|
// invoked e.g. as MAKE_JNI_NATIVE_METHOD("some_name", "()V", void_fn)
|
|
|
|
// An expression that evaluates to JNINativeMethod { name, signature, function },
|
|
// and applies the above compile-time checking for signature+function.
|
|
// The equivalent Java Language code must not be annotated with @FastNative/@CriticalNative.
|
|
#define MAKE_JNI_NATIVE_METHOD(name, signature, function) \
|
|
_NATIVEHELPER_JNI_MAKE_METHOD(kNormalNative, name, signature, function)
|
|
|
|
// An expression that evaluates to JNINativeMethod { name, signature, function },
|
|
// and applies the above compile-time checking for signature+function.
|
|
// The equivalent Java Language code must be annotated with @FastNative.
|
|
#define MAKE_JNI_FAST_NATIVE_METHOD(name, signature, function) \
|
|
_NATIVEHELPER_JNI_MAKE_METHOD(kFastNative, name, signature, function)
|
|
|
|
// An expression that evaluates to JNINativeMethod { name, signature, function },
|
|
// and applies the above compile-time checking for signature+function.
|
|
// The equivalent Java Language code must be annotated with @CriticalNative.
|
|
#define MAKE_JNI_CRITICAL_NATIVE_METHOD(name, signature, function) \
|
|
_NATIVEHELPER_JNI_MAKE_METHOD(kCriticalNative, name, signature, function)
|
|
|
|
// Automatically signature-inferencing macros are also available,
|
|
// which also checks the C++ function types for validity:
|
|
|
|
// An expression that evalutes to JNINativeMethod { name, infersig(function), function) }
|
|
// by inferring the signature at compile-time. Only works when the C++ function type
|
|
// corresponds to one unambigous JNI parameter (e.g. 'jintArray' -> '[I' but 'jobject' -> ???).
|
|
//
|
|
// The equivalent Java Language code must not be annotated with @FastNative/@CriticalNative.
|
|
#define MAKE_JNI_NATIVE_METHOD_AUTOSIG(name, function) \
|
|
_NATIVEHELPER_JNI_MAKE_METHOD_AUTOSIG(kNormalNative, name, function)
|
|
|
|
// An expression that evalutes to JNINativeMethod { name, infersig(function), function) }
|
|
// by inferring the signature at compile-time. Only works when the C++ function type
|
|
// corresponds to one unambigous JNI parameter (e.g. 'jintArray' -> '[I' but 'jobject' -> ???).
|
|
//
|
|
// The equivalent Java Language code must be annotated with @FastNative.
|
|
#define MAKE_JNI_FAST_NATIVE_METHOD_AUTOSIG(name, function) \
|
|
_NATIVEHELPER_JNI_MAKE_METHOD_AUTOSIG(kFastNative, name, function)
|
|
|
|
// An expression that evalutes to JNINativeMethod { name, infersig(function), function) }
|
|
// by inferring the signature at compile-time.
|
|
//
|
|
// The equivalent Java Language code must be annotated with @CriticalNative.
|
|
#define MAKE_JNI_CRITICAL_NATIVE_METHOD_AUTOSIG(name, function) \
|
|
_NATIVEHELPER_JNI_MAKE_METHOD_AUTOSIG(kCriticalNative, name, function)
|
|
|
|
// Convenience macros when the functions follow the naming convention:
|
|
// .java file .cpp file
|
|
// JavaLanguageName <-> ${ClassName}_${JavaLanguageName}
|
|
//
|
|
// Stringification is done automatically, invoked as:
|
|
// NATIVE_[FAST_|CRITICAL]_METHOD(ClassName, JavaLanguageName, Signature)
|
|
//
|
|
// Intended to construct a JNINativeMethod.
|
|
// (Assumes the C name is the ClassName_JavaMethodName).
|
|
//
|
|
// The Java Language code must be annotated with one of (none,@FastNative,@CriticalNative)
|
|
// for the (none,FAST_,CRITICAL_) variants of these macros.
|
|
|
|
#define NATIVE_METHOD(className, functionName, signature) \
|
|
MAKE_JNI_NATIVE_METHOD(#functionName, signature, className ## _ ## functionName)
|
|
|
|
#define OVERLOADED_NATIVE_METHOD(className, functionName, signature, identifier) \
|
|
MAKE_JNI_NATIVE_METHOD(#functionName, signature, className ## _ ## identifier)
|
|
|
|
#define NATIVE_METHOD_AUTOSIG(className, functionName) \
|
|
MAKE_JNI_NATIVE_METHOD_AUTOSIG(#functionName, className ## _ ## functionName)
|
|
|
|
#define FAST_NATIVE_METHOD(className, functionName, signature) \
|
|
MAKE_JNI_FAST_NATIVE_METHOD(#functionName, signature, className ## _ ## functionName)
|
|
|
|
#define OVERLOADED_FAST_NATIVE_METHOD(className, functionName, signature, identifier) \
|
|
MAKE_JNI_FAST_NATIVE_METHOD(#functionName, signature, className ## _ ## identifier)
|
|
|
|
#define FAST_NATIVE_METHOD_AUTOSIG(className, functionName) \
|
|
MAKE_JNI_FAST_NATIVE_METHOD_AUTOSIG(#functionName, className ## _ ## functionName)
|
|
|
|
#define CRITICAL_NATIVE_METHOD(className, functionName, signature) \
|
|
MAKE_JNI_CRITICAL_NATIVE_METHOD(#functionName, signature, className ## _ ## functionName)
|
|
|
|
#define OVERLOADED_CRITICAL_NATIVE_METHOD(className, functionName, signature, identifier) \
|
|
MAKE_JNI_CRITICAL_NATIVE_METHOD(#functionName, signature, className ## _ ## identifier)
|
|
|
|
#define CRITICAL_NATIVE_METHOD_AUTOSIG(className, functionName) \
|
|
MAKE_JNI_CRITICAL_NATIVE_METHOD_AUTOSIG(#functionName, className ## _ ## functionName)
|
|
|
|
////////////////////////////////////////////////////////
|
|
// IMPLEMENTATION ONLY.
|
|
// DO NOT USE DIRECTLY.
|
|
////////////////////////////////////////////////////////
|
|
|
|
#if defined(__cplusplus) && __cplusplus >= 201402L
|
|
#include "nativehelper/detail/signature_checker.h" // for MAKE_CHECKED_JNI_NATIVE_METHOD
|
|
#endif
|
|
|
|
// Expands to an expression whose type is JNINativeMethod.
|
|
// This is for older versions of C++ or C, so it has no compile-time checking.
|
|
#define _NATIVEHELPER_JNI_MAKE_METHOD_OLD(kind, name, sig, fn) \
|
|
( \
|
|
(JNINativeMethod) { \
|
|
(name), \
|
|
(sig), \
|
|
_NATIVEHELPER_JNI_MACRO_CAST(reinterpret_cast, void *)(fn) \
|
|
} \
|
|
)
|
|
|
|
// C++14 or better, use compile-time checking.
|
|
#if defined(__cplusplus) && __cplusplus >= 201402L
|
|
// Expands to a compound expression whose type is JNINativeMethod.
|
|
#define _NATIVEHELPER_JNI_MAKE_METHOD(kind, name, sig, fn) \
|
|
MAKE_CHECKED_JNI_NATIVE_METHOD(kind, name, sig, fn)
|
|
|
|
// Expands to a compound expression whose type is JNINativeMethod.
|
|
#define _NATIVEHELPER_JNI_MAKE_METHOD_AUTOSIG(kind, name, function) \
|
|
MAKE_INFERRED_JNI_NATIVE_METHOD(kind, name, function)
|
|
|
|
#else
|
|
// Older versions of C++ or C code get the regular macro that's unchecked.
|
|
// Expands to a compound expression whose type is JNINativeMethod.
|
|
#define _NATIVEHELPER_JNI_MAKE_METHOD(kind, name, sig, fn) \
|
|
_NATIVEHELPER_JNI_MAKE_METHOD_OLD(kind, name, sig, fn)
|
|
|
|
// Need C++14 or newer to use the AUTOSIG macros.
|
|
#define _NATIVEHELPER_JNI_MAKE_METHOD_AUTOSIG(kind, name, function) \
|
|
static_assert(false, "Cannot infer JNI signatures prior to C++14 for function " #function);
|
|
|
|
#endif // C++14 check
|
|
|
|
// C-style cast for C, C++-style cast for C++ to avoid warnings/errors.
|
|
#if defined(__cplusplus)
|
|
#define _NATIVEHELPER_JNI_MACRO_CAST(which_cast, to) \
|
|
which_cast<to>
|
|
#else
|
|
#define _NATIVEHELPER_JNI_MACRO_CAST(which_cast, to) \
|
|
(to)
|
|
#endif
|
|
|