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