Igor Murashkin | 31927e4 | 2017-02-17 15:50:15 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2017 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
Igor Murashkin | a1969c4 | 2018-02-16 13:30:57 -0800 | [diff] [blame] | 17 | /** |
| 18 | * Compile-time, zero-cost checking of JNI signatures against their C++ function type. |
| 19 | * This can trigger compile-time assertions if any of the input is invalid: |
| 20 | * (a) The signature specified does not conform to the JNI function descriptor syntax. |
| 21 | * (b) The C++ function is itself an invalid JNI function (e.g. missing JNIEnv*, etc). |
| 22 | * (c) The descriptor does not match the C++ function (e.g. "()V" will not match jint(jint)). |
Igor Murashkin | 31927e4 | 2017-02-17 15:50:15 -0800 | [diff] [blame] | 23 | * |
Igor Murashkin | a1969c4 | 2018-02-16 13:30:57 -0800 | [diff] [blame] | 24 | * The fundamental macros are as following: |
| 25 | * MAKE_JNI_[FAST_|CRITICAL_]NATIVE_METHOD - Create a checked JNINativeMethod{name, sig, func}. |
| 26 | * MAKE_JNI_[FAST_|CRITICAL_]NATIVE_METHOD_AUTOSIG - Same as above, but infer the JNI signature. |
| 27 | * |
| 28 | * Usage examples: |
| 29 | * // path/to/package/KlassName.java |
| 30 | * class KlassName { |
| 31 | * native jobject normal(int x); |
| 32 | * @FastNative native jobject fast(int x); |
| 33 | * @CriticalNative native int critical(long ptr); |
| 34 | * } |
| 35 | * // path_to_package_KlassName.cpp |
| 36 | * jobject KlassName_normal(JNIEnv*,jobject,jint) {...} |
| 37 | * jobject KlassName_fast(JNIEnv*,jobject,jint) {...} |
| 38 | * jint KlassName_critical(jlong) {...} |
| 39 | * |
| 40 | * // Manually specify each signature: |
| 41 | * JNINativeMethod[] gMethods = { |
| 42 | * MAKE_JNI_NATIVE_METHOD("normal", "(I)Ljava/lang/Object;", KlassName_normal), |
| 43 | * MAKE_JNI_FAST_NATIVE_METHOD("fast", "(I)Ljava/lang/Object;", KlassName_fast), |
| 44 | * MAKE_JNI_CRITICAL_NATIVE_METHOD("critical", "(Z)I", KlassName_critical), |
| 45 | * }; |
| 46 | * |
| 47 | * // Automatically infer the signature: |
| 48 | * JNINativeMethod[] gMethodsAutomaticSignature = { |
| 49 | * MAKE_JNI_NATIVE_METHOD_AUTOSIG("normal", KlassName_normal), |
| 50 | * MAKE_JNI_FAST_NATIVE_METHOD_AUTOSIG("fast", KlassName_fast), |
| 51 | * MAKE_JNI_CRITICAL_NATIVE_METHOD_AUTOSIG("critical", KlassName_critical), |
| 52 | * }; |
| 53 | * |
| 54 | * // and then call JNIEnv::RegisterNatives with gMethods as usual. |
| 55 | * |
| 56 | * For convenience the following macros are defined: |
| 57 | * [FAST_|CRITICAL_]NATIVE_METHOD - Return JNINativeMethod for class, func name, and signature. |
| 58 | * OVERLOADED_[FAST_|CRITICAL_]NATIVE_METHOD - Same as above but allows a separate func identifier. |
| 59 | * [FAST_|CRITICAL_]NATIVE_METHOD_AUTOSIG - Return JNINativeMethod, sig inferred from function. |
| 60 | * |
| 61 | * The FAST_ prefix corresponds to functions annotated with @FastNative, |
| 62 | * and the CRITICAL_ prefix corresponds to functions annotated with @CriticalNative. |
| 63 | * See dalvik.annotation.optimization.CriticalNative for more details. |
| 64 | * |
| 65 | * ======================================= |
| 66 | * Checking rules |
| 67 | * ======================================= |
| 68 | * |
| 69 | * --------------------------------------- |
| 70 | * JNI descriptor syntax for functions |
| 71 | * |
| 72 | * Refer to "Chapter 3: JNI Types and Data Structures" of the JNI specification |
| 73 | * under the subsection "Type Signatures" table entry "method type". |
| 74 | * |
| 75 | * JNI signatures not conforming to the above syntax are rejected. |
| 76 | * --------------------------------------- |
| 77 | * C++ function types |
| 78 | * |
| 79 | * A normal or @FastNative JNI function type must be of the form |
| 80 | * |
| 81 | * ReturnType (JNIEnv*, jclass|jobject, [ArgTypes...]) {} |
| 82 | * |
| 83 | * A @CriticalNative JNI function type: |
| 84 | * |
| 85 | * must be of the form... ReturnType ([ArgTypes...]){} |
| 86 | * and must not contain any Reference Types. |
| 87 | * |
| 88 | * Refer to "Chapter 3: JNI Types and Data Structures" of the JNI specification |
| 89 | * under the subsection "Primitive Types" and "Reference Types" for the list |
| 90 | * of valid argument/return types. |
| 91 | * |
| 92 | * C++ function types not conforming to the above requirements are rejected. |
| 93 | * --------------------------------------- |
| 94 | * Matching of C++ function type against JNI function descriptor. |
| 95 | * |
| 96 | * Assuming all of the above conditions are met for signature and C++ type validity, |
| 97 | * then matching between the signature and the type validity can occur: |
| 98 | * |
| 99 | * Given a signature (Args...)Ret and the |
| 100 | * C++ function type of the form "CRet fn(JNIEnv*, jclass|jobject, CArgs...)", |
| 101 | * or for @CriticalNative of the form "CRet fn(CArgs...)" |
| 102 | * |
| 103 | * The number of Args... and the number of CArgs... must be equal. |
| 104 | * |
| 105 | * If so, attempt to match every component from the signature and function type |
| 106 | * against each other: |
| 107 | * |
| 108 | * ReturnType: |
| 109 | * V <-> void |
| 110 | * ArgumentType |
| 111 | * |
| 112 | * ArgumentType: |
| 113 | * PrimitiveType |
| 114 | * ReferenceType [except for @CriticalNative] |
| 115 | * |
| 116 | * PrimitiveType: |
| 117 | * Z <-> jboolean |
| 118 | * B <-> jbyte |
| 119 | * C <-> jchar |
| 120 | * S <-> jshort |
| 121 | * I <-> jint |
| 122 | * J <-> jlong |
| 123 | * F <-> jfloat |
| 124 | * D <-> jdouble |
| 125 | * |
| 126 | * ReferenceType: |
| 127 | * Ljava/lang/String; <-> jstring |
| 128 | * Ljava/lang/Class; <-> jclass |
| 129 | * L*; <- jobject |
| 130 | * Ljava/lang/Throwable; -> jthrowable |
| 131 | * L*; <- jthrowable |
| 132 | * [ PrimitiveType <-> ${CPrimitiveType}Array |
| 133 | * [ ReferenceType <-> jobjectArray |
| 134 | * [* <- jarray |
| 135 | * |
| 136 | * Wherein <-> represents a strong match (if the left or right pattern occurs, |
| 137 | * then left must match right, otherwise matching fails). <- and -> represent |
| 138 | * weak matches (that is, other match rules can be still attempted). |
| 139 | * |
| 140 | * Sidenote: Whilst a jobject could also represent a jclass, jstring, etc, |
| 141 | * the stricter approach is taken: the most exact C++ type must be used. |
Igor Murashkin | 31927e4 | 2017-02-17 15:50:15 -0800 | [diff] [blame] | 142 | */ |
| 143 | |
| 144 | #ifndef NATIVEHELPER_JNI_MACROS_H |
| 145 | #define NATIVEHELPER_JNI_MACROS_H |
| 146 | |
Igor Murashkin | a1969c4 | 2018-02-16 13:30:57 -0800 | [diff] [blame] | 147 | // The below basic macros do not perform automatic stringification, |
| 148 | // invoked e.g. as MAKE_JNI_NATIVE_METHOD("some_name", "()V", void_fn) |
Igor Murashkin | 31927e4 | 2017-02-17 15:50:15 -0800 | [diff] [blame] | 149 | |
Igor Murashkin | a1969c4 | 2018-02-16 13:30:57 -0800 | [diff] [blame] | 150 | // An expression that evaluates to JNINativeMethod { name, signature, function }, |
| 151 | // and applies the above compile-time checking for signature+function. |
| 152 | // The equivalent Java Language code must not be annotated with @FastNative/@CriticalNative. |
| 153 | #define MAKE_JNI_NATIVE_METHOD(name, signature, function) \ |
| 154 | _NATIVEHELPER_JNI_MAKE_METHOD(kNormalNative, name, signature, function) |
| 155 | |
| 156 | // An expression that evaluates to JNINativeMethod { name, signature, function }, |
| 157 | // and applies the above compile-time checking for signature+function. |
| 158 | // The equivalent Java Language code must be annotated with @FastNative. |
| 159 | #define MAKE_JNI_FAST_NATIVE_METHOD(name, signature, function) \ |
| 160 | _NATIVEHELPER_JNI_MAKE_METHOD(kFastNative, name, signature, function) |
| 161 | |
| 162 | // An expression that evaluates to JNINativeMethod { name, signature, function }, |
| 163 | // and applies the above compile-time checking for signature+function. |
| 164 | // The equivalent Java Language code must be annotated with @CriticalNative. |
| 165 | #define MAKE_JNI_CRITICAL_NATIVE_METHOD(name, signature, function) \ |
| 166 | _NATIVEHELPER_JNI_MAKE_METHOD(kCriticalNative, name, signature, function) |
| 167 | |
| 168 | // Automatically signature-inferencing macros are also available, |
| 169 | // which also checks the C++ function types for validity: |
| 170 | |
| 171 | // An expression that evalutes to JNINativeMethod { name, infersig(function), function) } |
| 172 | // by inferring the signature at compile-time. Only works when the C++ function type |
| 173 | // corresponds to one unambigous JNI parameter (e.g. 'jintArray' -> '[I' but 'jobject' -> ???). |
| 174 | // |
| 175 | // The equivalent Java Language code must not be annotated with @FastNative/@CriticalNative. |
| 176 | #define MAKE_JNI_NATIVE_METHOD_AUTOSIG(name, function) \ |
| 177 | _NATIVEHELPER_JNI_MAKE_METHOD_AUTOSIG(kNormalNative, name, function) |
| 178 | |
| 179 | // An expression that evalutes to JNINativeMethod { name, infersig(function), function) } |
| 180 | // by inferring the signature at compile-time. Only works when the C++ function type |
| 181 | // corresponds to one unambigous JNI parameter (e.g. 'jintArray' -> '[I' but 'jobject' -> ???). |
| 182 | // |
| 183 | // The equivalent Java Language code must be annotated with @FastNative. |
| 184 | #define MAKE_JNI_FAST_NATIVE_METHOD_AUTOSIG(name, function) \ |
| 185 | _NATIVEHELPER_JNI_MAKE_METHOD_AUTOSIG(kFastNative, name, function) |
| 186 | |
| 187 | // An expression that evalutes to JNINativeMethod { name, infersig(function), function) } |
| 188 | // by inferring the signature at compile-time. |
| 189 | // |
| 190 | // The equivalent Java Language code must be annotated with @CriticalNative. |
| 191 | #define MAKE_JNI_CRITICAL_NATIVE_METHOD_AUTOSIG(name, function) \ |
| 192 | _NATIVEHELPER_JNI_MAKE_METHOD_AUTOSIG(kCriticalNative, name, function) |
| 193 | |
| 194 | // Convenience macros when the functions follow the naming convention: |
| 195 | // .java file .cpp file |
| 196 | // JavaLanguageName <-> ${ClassName}_${JavaLanguageName} |
| 197 | // |
| 198 | // Stringification is done automatically, invoked as: |
| 199 | // NATIVE_[FAST_|CRITICAL]_METHOD(ClassName, JavaLanguageName, Signature) |
| 200 | // |
Igor Murashkin | 31927e4 | 2017-02-17 15:50:15 -0800 | [diff] [blame] | 201 | // Intended to construct a JNINativeMethod. |
| 202 | // (Assumes the C name is the ClassName_JavaMethodName). |
Igor Murashkin | 31927e4 | 2017-02-17 15:50:15 -0800 | [diff] [blame] | 203 | // |
Igor Murashkin | a1969c4 | 2018-02-16 13:30:57 -0800 | [diff] [blame] | 204 | // The Java Language code must be annotated with one of (none,@FastNative,@CriticalNative) |
| 205 | // for the (none,FAST_,CRITICAL_) variants of these macros. |
| 206 | |
| 207 | #ifdef NATIVE_METHOD // Remove definition from JniConstants.h |
| 208 | #undef NATIVE_METHOD |
Igor Murashkin | 31927e4 | 2017-02-17 15:50:15 -0800 | [diff] [blame] | 209 | #endif |
| 210 | |
Igor Murashkin | a1969c4 | 2018-02-16 13:30:57 -0800 | [diff] [blame] | 211 | #define NATIVE_METHOD(className, functionName, signature) \ |
| 212 | MAKE_JNI_NATIVE_METHOD(#functionName, signature, className ## _ ## functionName) |
| 213 | |
| 214 | #define OVERLOADED_NATIVE_METHOD(className, functionName, signature, identifier) \ |
| 215 | MAKE_JNI_NATIVE_METHOD(#functionName, signature, className ## _ ## identifier) |
| 216 | |
| 217 | #define NATIVE_METHOD_AUTOSIG(className, functionName) \ |
| 218 | MAKE_JNI_NATIVE_METHOD_AUTOSIG(#functionName, className ## _ ## functionName) |
| 219 | |
| 220 | #define FAST_NATIVE_METHOD(className, functionName, signature) \ |
| 221 | MAKE_JNI_FAST_NATIVE_METHOD(#functionName, signature, className ## _ ## functionName) |
| 222 | |
| 223 | #define OVERLOADED_FAST_NATIVE_METHOD(className, functionName, signature, identifier) \ |
| 224 | MAKE_JNI_FAST_NATIVE_METHOD(#functionName, signature, className ## _ ## identifier) |
| 225 | |
| 226 | #define FAST_NATIVE_METHOD_AUTOSIG(className, functionName) \ |
| 227 | MAKE_JNI_FAST_NATIVE_METHOD_AUTOSIG(#functionName, className ## _ ## functionName) |
| 228 | |
| 229 | #define CRITICAL_NATIVE_METHOD(className, functionName, signature) \ |
| 230 | MAKE_JNI_CRITICAL_NATIVE_METHOD(#functionName, signature, className ## _ ## functionName) |
| 231 | |
| 232 | #define OVERLOADED_CRITICAL_NATIVE_METHOD(className, functionName, signature, identifier) \ |
| 233 | MAKE_JNI_CRITICAL_NATIVE_METHOD(#functionName, signature, className ## _ ## identifier) |
| 234 | |
| 235 | #define CRITICAL_NATIVE_METHOD_AUTOSIG(className, functionName) \ |
| 236 | MAKE_JNI_CRITICAL_NATIVE_METHOD_AUTOSIG(#functionName, className ## _ ## functionName) |
| 237 | |
Igor Murashkin | 31927e4 | 2017-02-17 15:50:15 -0800 | [diff] [blame] | 238 | //////////////////////////////////////////////////////// |
| 239 | // IMPLEMENTATION ONLY. |
| 240 | // DO NOT USE DIRECTLY. |
| 241 | //////////////////////////////////////////////////////// |
| 242 | |
Igor Murashkin | a1969c4 | 2018-02-16 13:30:57 -0800 | [diff] [blame] | 243 | #if defined(__cplusplus) && __cplusplus >= 201402L |
| 244 | #include "nativehelper/detail/signature_checker.h" // for MAKE_CHECKED_JNI_NATIVE_METHOD |
| 245 | #endif |
| 246 | |
| 247 | // Expands to an expression whose type is JNINativeMethod. |
| 248 | // This is for older versions of C++ or C, so it has no compile-time checking. |
| 249 | #define _NATIVEHELPER_JNI_MAKE_METHOD_OLD(kind, name, sig, fn) \ |
| 250 | ( \ |
| 251 | (JNINativeMethod) { \ |
| 252 | (name), \ |
| 253 | (sig), \ |
| 254 | _NATIVEHELPER_JNI_MACRO_CAST(reinterpret_cast, void *)(fn) \ |
| 255 | } \ |
| 256 | ) |
| 257 | |
| 258 | // C++14 or better, use compile-time checking. |
| 259 | #if defined(__cplusplus) && __cplusplus >= 201402L |
| 260 | // Expands to a compound expression whose type is JNINativeMethod. |
| 261 | #define _NATIVEHELPER_JNI_MAKE_METHOD(kind, name, sig, fn) \ |
| 262 | MAKE_CHECKED_JNI_NATIVE_METHOD(kind, name, sig, fn) |
| 263 | |
| 264 | // Expands to a compound expression whose type is JNINativeMethod. |
| 265 | #define _NATIVEHELPER_JNI_MAKE_METHOD_AUTOSIG(kind, name, function) \ |
| 266 | MAKE_INFERRED_JNI_NATIVE_METHOD(kind, name, function) |
| 267 | |
| 268 | #else |
| 269 | // Older versions of C++ or C code get the regular macro that's unchecked. |
| 270 | // Expands to a compound expression whose type is JNINativeMethod. |
| 271 | #define _NATIVEHELPER_JNI_MAKE_METHOD(kind, name, sig, fn) \ |
| 272 | _NATIVEHELPER_JNI_MAKE_METHOD_OLD(kind, name, sig, fn) |
| 273 | |
| 274 | // Need C++14 or newer to use the AUTOSIG macros. |
| 275 | #define _NATIVEHELPER_JNI_MAKE_METHOD_AUTOSIG(kind, name, function) \ |
| 276 | static_assert(false, "Cannot infer JNI signatures prior to C++14 for function " #function); |
| 277 | |
| 278 | #endif // C++14 check |
Igor Murashkin | 31927e4 | 2017-02-17 15:50:15 -0800 | [diff] [blame] | 279 | |
| 280 | // C-style cast for C, C++-style cast for C++ to avoid warnings/errors. |
| 281 | #if defined(__cplusplus) |
Igor Murashkin | a1969c4 | 2018-02-16 13:30:57 -0800 | [diff] [blame] | 282 | #define _NATIVEHELPER_JNI_MACRO_CAST(which_cast, to) \ |
| 283 | which_cast<to> |
Igor Murashkin | 31927e4 | 2017-02-17 15:50:15 -0800 | [diff] [blame] | 284 | #else |
Igor Murashkin | a1969c4 | 2018-02-16 13:30:57 -0800 | [diff] [blame] | 285 | #define _NATIVEHELPER_JNI_MACRO_CAST(which_cast, to) \ |
Igor Murashkin | 31927e4 | 2017-02-17 15:50:15 -0800 | [diff] [blame] | 286 | (to) |
| 287 | #endif |
| 288 | |
Igor Murashkin | a1969c4 | 2018-02-16 13:30:57 -0800 | [diff] [blame] | 289 | #endif // NATIVEHELPER_JNI_MACROS_H |