1 /*
2 Copyright (c) 2010, 2021, Oracle and/or its affiliates.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License, version 2.0,
6 as published by the Free Software Foundation.
7
8 This program is also distributed with certain software (including
9 but not limited to OpenSSL) that is licensed under separate terms,
10 as designated in a particular file or component or in included license
11 documentation. The authors of MySQL hereby grant you an additional
12 permission to link the program and your derivative works with the
13 separately licensed software that they have included with MySQL.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License, version 2.0, for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 */
24 /*
25 * jtie_tconv_idcache_impl.hpp
26 */
27
28 #ifndef jtie_tconv_idcache_impl_hpp
29 #define jtie_tconv_idcache_impl_hpp
30
31 #include <assert.h> // not using namespaces yet
32 #include <jni.h>
33
34 //#include "helpers.hpp"
35
36 // ---------------------------------------------------------------------------
37 // JNI field/method IDs access & caching
38 // ---------------------------------------------------------------------------
39
40 #if 0 // XXX ddefines not used at this time
41
42 // compilation flags
43 #define JTIE_NO_JNI_ID_CACHING 0
44 #define JTIE_WEAK_JNI_ID_CACHING 1
45 #define JTIE_STRONG_JNI_ID_CACHING 2
46
47 #if !defined(JTIE_JNI_ID_CACHING)
48 # define JNI_ID_CACHING WEAK_CACHING
49 #elif (JTIE_JNI_ID_CACHING == JTIE_NO_JNI_ID_CACHING)
50 # define JNI_ID_CACHING NO_CACHING
51 #elif (JTIE_JNI_ID_CACHING == JTIE_WEAK_JNI_ID_CACHING)
52 # define JNI_ID_CACHING WEAK_CACHING
53 #elif (JTIE_JNI_ID_CACHING == JTIE_STRONG_JNI_ID_CACHING)
54 # define JNI_ID_CACHING STRONG_CACHING
55 #else
56 # error "Illegal value of JTIE_JNI_ID_CACHING"
57 #endif
58
59 #endif // XXX ddefines not used at this time
60
61 // ---------------------------------------------------------------------------
62
63 // Local JNI helper functions
64
65 template< typename T >
66 inline T
67 jniGetMemberID(JNIEnv * env,
68 jclass cls,
69 const char * name, const char * descriptor);
70
71 template<>
72 inline jmethodID
jniGetMemberID(JNIEnv * env,jclass cls,const char * name,const char * descriptor)73 jniGetMemberID< jmethodID >(JNIEnv * env,
74 jclass cls,
75 const char * name, const char * descriptor) {
76 return env->GetMethodID(cls, name, descriptor);
77 }
78
79 template<>
80 inline jfieldID
jniGetMemberID(JNIEnv * env,jclass cls,const char * name,const char * descriptor)81 jniGetMemberID< jfieldID >(JNIEnv * env,
82 jclass cls,
83 const char * name, const char * descriptor) {
84 return env->GetFieldID(cls, name, descriptor);
85 }
86
87 // ---------------------------------------------------------------------------
88
89 /**
90 * Defines an info type describing a field member of a Java class.
91 */
92 #define JTIE_DEFINE_FIELD_MEMBER_INFO( T ) \
93 JTIE_DEFINE_CLASS_MEMBER_INFO( T, _jfieldID )
94
95 /**
96 * Defines an info type describing a method member of a Java class.
97 */
98 #define JTIE_DEFINE_METHOD_MEMBER_INFO( T ) \
99 JTIE_DEFINE_CLASS_MEMBER_INFO( T, _jmethodID )
100
101 /**
102 * Defines an info type describing a member of a Java class.
103 */
104 #define JTIE_DEFINE_CLASS_MEMBER_INFO( T, IDT ) \
105 struct T { \
106 static const char * const jclass_name; \
107 static const char * const member_name; \
108 static const char * const member_descriptor; \
109 typedef IDT * memberID_t; \
110 };
111
112 /**
113 * Instantiates an info type describing a member of a Java class.
114 */
115 // XXX: unify JTIE_INSTANTIATE_CLASS_MEMBER_INFO_0 and _1
116 // Windows CL requires this version for T = non-template type
117 #define JTIE_INSTANTIATE_CLASS_MEMBER_INFO_0( T, JCN, JMN, JMD ) \
118 const char * const T::jclass_name = JCN; \
119 const char * const T::member_name = JMN; \
120 const char * const T::member_descriptor = JMD; \
121 template<> unsigned long MemberId< T >::nIdLookUps = 0; \
122 template<> jclass MemberIdCache< T >::gClassRef = NULL; \
123 template<> T::memberID_t MemberIdCache< T >::mid = NULL; \
124 template struct MemberId< T >; \
125 template struct MemberIdCache< T >;
126
127 /**
128 * Instantiates an info type describing a member of a Java class.
129 */
130 // XXX: unify JTIE_INSTANTIATE_CLASS_MEMBER_INFO_0 and _1
131 // Windows CL requires this version for T = template type
132 #define JTIE_INSTANTIATE_CLASS_MEMBER_INFO_1( T, JCN, JMN, JMD ) \
133 template<> const char * const T::jclass_name = JCN; \
134 template<> const char * const T::member_name = JMN; \
135 template<> const char * const T::member_descriptor = JMD; \
136 template<> unsigned long MemberId< T >::nIdLookUps = 0; \
137 template<> jclass MemberIdCache< T >::gClassRef = NULL; \
138 template<> T::memberID_t MemberIdCache< T >::mid = NULL; \
139 template struct MemberId< T >; \
140 template struct MemberIdCache< T >;
141
142 /**
143 * Provides uniform access to the JNI Field/Method ID of a Java class member
144 * specified by a info type M.
145 *
146 * This base class does not cache the member ID and the class object, but
147 * it retrieves the member ID from JNI upon each access; different caching
148 * strategies are provided by derived classes.
149 *
150 * This class (and its derived classes) impose a strict usage pattern.
151 * For example, given definitions...
152 *
153 * // Defines the field info type for MyClass.myField.
154 * JTIE_DEFINE_FIELD_MEMBER_INFO(_MyClass_myField)
155 *
156 * // Provides a (cached) access to field Id of MyClass.myField.
157 * typedef JniMemberId< _MyClass_myField > MyClass_myField;
158 *
159 * any use of a member ID must be bracketed by getClass() and releaseRef():
160 *
161 * // obtain a class reference
162 * jclass cls = MyClass_myField::getClass(env);
163 * if (cls == NULL) {
164 * // exception pending
165 * } else {
166 * // get the field ID valid along with the class reference
167 * jfieldID fid = MyClass_myField::getId(env, cls);
168 * if (fid == NULL) {
169 * // exception pending
170 * } else {
171 * // OK to access field using 'fid'
172 * }
173 * // allow for releasing the class reference
174 * MyClass_myField::releaseRef(env, cls);
175 * }
176 *
177 * Derived classes implement any caching underneath this usage pattern.
178 */
179 template< typename M >
180 struct MemberId {
181 typedef typename M::memberID_t ID_t;
182
183 // number of JNI Get<Field|Method>ID() invocations for statistics
184 static unsigned long nIdLookUps;
185
186 /**
187 * Allows for storing a (global) class reference.
188 *
189 * Usually only called from getClass(), but enables "cache preloading"
190 * from a (native, static) function called at class initialization.
191 *
192 * Pre condition:
193 * - this thread has no pending JNI exception (!env->ExceptionCheck())
194 * - a valid local or global class reference: assert(cls != NULL)
195 */
setClassMemberId196 static void setClass(JNIEnv * env, jclass cls) {
197 assert(cls != NULL);
198 (void)env; (void)cls;
199 }
200
201 /**
202 * Returns a JNI Reference to the class declaring the member specified
203 * by info type M.
204 *
205 * Depending upon the underlying caching strategy, a returned reference
206 * may be local or global, weak or strong; the scope of its use must be
207 * demarcated by releaseRef().
208 *
209 * Pre condition:
210 * - this thread has no pending JNI exception (!env->ExceptionCheck())
211 *
212 * Post condition:
213 * - return value is
214 * NULL:
215 * - this thread has a pending JNI exception (env->ExceptionCheck())
216 * otherwise:
217 * - this thread has no pending JNI exception (!env->ExceptionCheck())
218 * - the returned reference is valid (at least) until releaseRef()
219 */
getClassMemberId220 static jclass getClass(JNIEnv * env) {
221 assert(env->ExceptionCheck() == JNI_OK);
222 jclass cls = env->FindClass(M::jclass_name);
223 if (cls == NULL) { // break out for better diagnostics
224 assert(env->ExceptionCheck() != JNI_OK); // exception pending
225 env->ExceptionDescribe(); // print error diagnostics to stderr
226
227 #if 0 // for debugging: raise a fatal error
228 #ifdef _WIN32
229 #define SNPRINTF _snprintf
230 #else
231 #define SNPRINTF snprintf
232 #endif
233 char m[1024];
234 SNPRINTF(m, sizeof(m), "JTie: failed to find Java class '%s'",
235 (M::jclass_name == NULL ? "NULL" : M::jclass_name));
236 fprintf(stderr, "%s\n", m);
237 env->FatalError(m);
238 #endif
239 } else {
240 assert(env->ExceptionCheck() == JNI_OK); // ok
241 }
242 return cls;
243 }
244
245 /**
246 * Returns the JNI Field/Method ID of a Java class member.
247 *
248 * The member ID is only valid along with a class object obtained by
249 * getClass() and before releaseRef().
250 *
251 * Pre condition:
252 * - this thread has no pending JNI exception (!env->ExceptionCheck())
253 * - a valid class reference obtained by getClass(): assert(cls != NULL)
254 *
255 * Post condition:
256 * - return value is
257 * NULL:
258 * - this thread has a pending JNI exception (env->ExceptionCheck())
259 * otherwise:
260 * - this thread has no pending JNI exception (!env->ExceptionCheck())
261 * - the returned member ID is valid (at least) until releaseRef()
262 */
getIdMemberId263 static ID_t getId(JNIEnv * env, jclass cls) {
264 assert(cls != NULL);
265 // multithreaded access ok, inaccurate if non-atomic increment
266 nIdLookUps++;
267 return jniGetMemberID< ID_t >(env, cls,
268 M::member_name, M::member_descriptor);
269 }
270
271 /**
272 * Allows for a class reference to be released along with any member IDs.
273 *
274 * Pre condition:
275 * - a valid class reference obtained by getClass(): assert(cls != NULL)
276 * - this thread may have a pending JNI exception (env->ExceptionCheck())
277 */
releaseRefMemberId278 static void releaseRef(JNIEnv * env, jclass cls) {
279 assert(cls != NULL);
280 env->DeleteLocalRef(cls);
281 }
282 };
283
284 /**
285 * Base class for caching of JNI Field/Method IDs.
286 */
287 template< typename M >
288 struct MemberIdCache : MemberId< M > {
289 typedef typename M::memberID_t ID_t;
290
getIdMemberIdCache291 static ID_t getId(JNIEnv * env, jclass cls) {
292 assert(cls != NULL);
293 // the cached member id is only valid along with global class ref
294 assert(env->IsSameObject(gClassRef, NULL) == JNI_FALSE);
295 (void)env; (void)cls;
296 return mid;
297 }
298
299 protected:
300 // the cached global (weak or strong) class ref
301 static jclass gClassRef;
302
303 // the cached member id (only valid along with global class ref)
304 static ID_t mid;
305 };
306
307 /**
308 * Provides caching of JNI Field/Method IDs using weak class references,
309 * allowing classes to be unloaded when no longer used by Java code.
310 */
311 template< typename M >
312 struct MemberIdWeakCache : MemberIdCache< M > {
313 typedef MemberId< M > A;
314 typedef MemberIdCache< M > Base;
315
setClassMemberIdWeakCache316 static void setClass(JNIEnv * env, jclass cls) {
317 assert(cls != NULL);
318
319 // multithreaded access ok, sets same class/member object
320 Base::gClassRef = static_cast< jclass >(env->NewWeakGlobalRef(cls));
321 Base::mid = A::getId(env, cls);
322 }
323
324 using Base::getId; // use as inherited (some compiler wanted this)
325
getClassMemberIdWeakCache326 static jclass getClass(JNIEnv * env) {
327 // a weak global class ref may refer to a freed object at any time
328 // (i.e.: env->IsSameObject(Base::gClassRef, NULL))
329 // unless we've obtained a strong (local or global) non-NULL class ref
330 jclass cls = static_cast< jclass >(env->NewLocalRef(Base::gClassRef));
331 if (cls == NULL) {
332 // global class ref was NULL or referencing a freed object
333 cls = A::getClass(env);
334 if (cls == NULL) {
335 // exception pending
336 } else {
337 setClass(env, cls);
338 }
339 }
340 return cls;
341 }
342
releaseRefMemberIdWeakCache343 static void releaseRef(JNIEnv * env, jclass cls) {
344 assert(cls != NULL);
345 env->DeleteLocalRef(cls);
346 }
347 };
348
349 /**
350 * Provides caching of JNI Field/Method IDs using strong class references,
351 * preventing classes from being unloaded even if no longer used by Java code.
352 */
353 template< typename M >
354 struct MemberIdStrongCache : MemberIdCache< M > {
355 typedef MemberId< M > A;
356 typedef MemberIdCache< M > Base;
357
setClassMemberIdStrongCache358 static void setClass(JNIEnv * env, jclass cls) {
359 assert(cls != NULL);
360
361 // multithreaded access ok, sets same class/member object
362 Base::gClassRef = static_cast< jclass >(env->NewGlobalRef(cls));
363 Base::mid = A::getId(env, cls);
364 }
365
366 using Base::getId; // use as inherited (some compiler wanted this)
367
getClassMemberIdStrongCache368 static jclass getClass(JNIEnv * env) {
369 jclass cls = Base::gClassRef;
370 if (cls == NULL) {
371 cls = A::getClass(env);
372 if (cls == NULL) {
373 // exception pending
374 } else {
375 setClass(env, cls);
376 }
377 }
378 return cls;
379 }
380
releaseRefMemberIdStrongCache381 static void releaseRef(JNIEnv * env, jclass cls) {
382 assert(cls != NULL);
383 (void)env; (void)cls;
384 }
385 };
386
387 /**
388 * Provides caching of JNI Field/Method IDs using weak class references
389 * with preloading (at class initialization) -- VERY TRICKY, NOT SUPPORTED.
390 */
391 template< typename M >
392 struct MemberIdPreloadedWeakCache : MemberIdWeakCache< M > {
393 typedef MemberIdWeakCache< M > Base;
394
395 using Base::setClass; // use as inherited (some compiler wanted this)
396
397 using Base::getId; // use as inherited (some compiler wanted this)
398
getClassMemberIdPreloadedWeakCache399 static jclass getClass(JNIEnv * env) {
400 // weak global class ref is assumed to be preloaded and valid
401 jclass cls = Base::gClassRef;
402 assert(env->IsSameObject(cls, NULL) == JNI_FALSE);
403 return cls;
404 }
405
releaseRefMemberIdPreloadedWeakCache406 static void releaseRef(JNIEnv * env, jclass cls) {
407 assert(cls != NULL);
408 (void)env; (void)cls;
409 }
410 };
411
412 /**
413 * Provides caching of JNI Field/Method IDs using strong class references
414 * with preloading (at class initialization) -- VERY TRICKY, NOT SUPPORTED.
415 */
416 template< typename M >
417 struct MemberIdPreloadedStrongCache : MemberIdStrongCache< M > {
418 typedef MemberIdStrongCache< M > Base;
419
420 using Base::setClass; // use as inherited (some compiler wanted this)
421
422 using Base::getId; // use as inherited (some compiler wanted this)
423
getClassMemberIdPreloadedStrongCache424 static jclass getClass(JNIEnv * env) {
425 // strong global class ref is assumed to be preloaded and valid
426 jclass cls = Base::gClassRef;
427 assert(env->IsSameObject(cls, NULL) == JNI_FALSE);
428 return cls;
429 }
430
431 using Base::releaseRef; // use as inherited (some compiler wanted this)
432 };
433
434 // XXX document
435
436 /**
437 * The supported caching strategies for member IDs and class references.
438 */
439 enum JniMemberIdCaching {
440 NO_CACHING
441 ,WEAK_CACHING
442 ,STRONG_CACHING
443 #if 0 // preloaded caching very tricky, not supported at this time
444 ,WEAK_CACHING_PRELOAD
445 ,STRONG_CACHING_PRELOAD
446 #endif // preloaded caching very tricky, not supported at this time
447 };
448
449 /**
450 * Generic class for member ID access with selection of caching strategy.
451 */
452 template< JniMemberIdCaching S, typename M >
453 struct JniMemberId;
454
455 template< typename M >
456 struct JniMemberId< NO_CACHING, M >
457 : MemberId< M > {};
458
459 template< typename M >
460 struct JniMemberId< WEAK_CACHING, M >
461 : MemberIdWeakCache< M > {};
462
463 template< typename M >
464 struct JniMemberId< STRONG_CACHING, M >
465 : MemberIdStrongCache< M > {};
466
467 #if 0 // preloaded caching very tricky, not supported at this time
468 template< typename M >
469 struct JniMemberId< WEAK_CACHING_PRELOAD, M >
470 : MemberIdPreloadedWeakCache< M > {};
471
472 template< typename M >
473 struct JniMemberId< STRONG_CACHING_PRELOAD, M >
474 : MemberIdPreloadedStrongCache< M > {};
475 #endif // preloaded caching very tricky, not supported at this time
476
477 // ---------------------------------------------------------------------------
478
479 #endif // jtie_tconv_idcache_impl_hpp
480