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