1 /*
2 Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
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_array.hpp
26 */
27
28 #ifndef jtie_tconv_array_impl_hpp
29 #define jtie_tconv_array_impl_hpp
30
31 #include <assert.h> // not using namespaces yet
32 #include <jni.h>
33
34 #include "jtie_tconv_value.hpp"
35 #include "jtie_tconv_value_impl.hpp"
36 #include "jtie_tconv_object_impl.hpp"
37
38 // ---------------------------------------------------------------------------
39 // Utilities for Java array <-> C array type conversions
40 // ---------------------------------------------------------------------------
41
42 /**
43 * A class with helper methods to convert native arrays into Java arrays
44 * and vice versa.
45 * ArrayConv's function signatures support both, const and non-const base type
46 * specializations.
47 *
48 * This class only documents prototypes, no functions are declared or
49 * defined here, in order to have undefined type mappings result in a
50 * compilation error.
51 */
52 template< typename JA,
53 typename CA >
54 struct ArrayConv {
55 /**
56 * Returns the body of the primitive array, or NULL if the operation
57 * fails.
58 * The result is valid until releaseArrayElements() is called.
59 * Returns a non-const C array to allow for use in non-const context.
60 *
61 * Pre condition:
62 * - no JNI exception is pending: assert(!env->ExceptionCheck())
63 * - argument: assert(j != NULL)
64 * - argument: assert(env != NULL)
65 *
66 * Post condition:
67 * - return value:
68 * non-NULL:
69 * - this thread has no pending JNI exception (!env->ExceptionCheck())
70 * - the return value is valid and initialized array
71 * - corresponding releaseArrayElements() must be called
72 * NULL:
73 * - this thread has a pending JNI exception (env->ExceptionCheck())
74 * - corresponding releaseArrayElements() must not be called
75 */
76 #if 0 // disabled on purpose, only document function
77 static CA
78 getArrayElements(JNIEnv * env, JA j, jboolean * isCopy) {
79 TRACE("CA ArrayConv.getArrayElements(JNIEnv *, JA, jboolean *)");
80 (void)env; (void)j; (void)isCopy;
81 static_assert(false, "missing specialization of array conversion");
82 return 0;
83 }
84 #endif // disabled on purpose, only document function
85
86 /**
87 * Informs the VM that the native code no longer needs access to elems.
88 * Accepts a const C array to allow for use in const context.
89 *
90 * Pre condition:
91 * - getArrayElements() has been called with 'j' and returned 'c'
92 * - argument: assert(j != NULL)
93 * - argument: assert(c != NULL)
94 * - argument: assert(env != NULL)
95 *
96 * Post condition:
97 * - In case of a pending exception (env->ExceptionCheck()), only these
98 * safe JNI functions have been called:
99 * - ExceptionOccurred
100 * - ExceptionDescribe
101 * - ExceptionClear
102 * - ExceptionCheck
103 * - ReleaseStringChars
104 * - ReleaseStringUTFchars
105 * - ReleaseStringCritical
106 * - Release<Type>ArrayElements
107 * - ReleasePrimitiveArrayCritical
108 * - DeleteLocalRef
109 * - DeleteGlobalRef
110 * - DeleteWeakGlobalRef
111 * - MonitorExit
112 */
113 #if 0 // disabled on purpose, only document function
114 static void
115 releaseArrayElements(JNIEnv * env, JA j, const CA c, jint mode) {
116 TRACE("void ArrayConv.releaseArrayElements(JNIEnv *, JA, const CA, jint)");
117 (void)env; (void)j; (void)c; (void)mode;
118 static_assert(false, "missing specialization of array conversion");
119 return 0;
120 }
121 #endif // disabled on purpose, only document function
122
123 /**
124 * Constructs a new primitive array object with elements from a buffer.
125 * Accepts a const C array to allow for use in const context.
126 *
127 * Pre condition:
128 * - no JNI exception is pending: assert(!env->ExceptionCheck())
129 * - argument: assert(c != NULL)
130 * - argument: assert(env != NULL)
131 *
132 * Post condition:
133 * - return value:
134 * non-NULL:
135 * - this thread has no pending JNI exception (!env->ExceptionCheck())
136 * - the return value is a valid and initialized Java array
137 * NULL:
138 * - a JNI exception is pending (env->ExceptionCheck())
139 *
140 * In other words, any errors during the result conversion must be
141 * signaled by registering a Java exception with the VM.
142 */
143 #if 0 // disabled on purpose, only document function
144 static JA
145 newArray(JNIEnv * env, jsize len, const CA c) {
146 TRACE("JA ArrayConv.newArray(JNIEnv *, jsize, const CA)");
147 (void)env; (void)len; (void)c;
148 static_assert(false, "missing specialization of array conversion");
149 return 0;
150 }
151 #endif // disabled on purpose, only document function
152 };
153
154 /**
155 * Implements ArrayConv on primitive array types.
156 *
157 * The JNI headers, but not the spec, of JDKs >= 1.4 declare the last
158 * parameter of Set<Jtype>ArrayRegion(..., const J *) correctly as const
159 */
160 template< typename JA,
161 typename J,
162 typename C,
163 J * (JNIEnv::*GET)(JA, jboolean *),
164 void (JNIEnv::*REL)(JA, J *, jint),
165 JA (JNIEnv::*NEW)(jsize),
166 void (JNIEnv::*SET)(JA, jsize, jsize, const J *) >
167 struct PrimArrayConvImpl {
168 static C *
getArrayElementsPrimArrayConvImpl169 getArrayElements(JNIEnv * env, JA j, jboolean * isCopy) {
170 TRACE("C * PrimArrayConvImpl.getArrayElements(JNIEnv *, JA, jboolean *)");
171 // XXX currently, only exact-width base type conversions supported
172 assert(sizeof(J) == sizeof(C));
173 assert(j != NULL);
174
175 // init return value to error
176 C * c = NULL;
177
178 J * ja = (env->*GET)(j, isCopy);
179 if (env->ExceptionCheck() != JNI_OK) {
180 // exception pending
181 } else {
182 // the JNI Spec (1.4..6) on Get<PrimitiveType>ArrayElements is
183 // not explicit on whether an exception has been registered
184 // when the operation returns NULL; so, better code defensively:
185 if (ja == NULL) {
186 const char * cl = "java/lang/AssertionError";
187 const char * m = ("JTie: a JNI Get<PrimitiveType>ArrayElements"
188 " function call returned NULL but has not"
189 " registered an exception with the VM"
190 " (file: " __FILE__ ")");
191 registerException(env, cl, m);
192 } else {
193 // ok, convert pointer types
194 c = reinterpret_cast< C * >(ja);
195 }
196 }
197 return c;
198 }
199
200 static void
releaseArrayElementsPrimArrayConvImpl201 releaseArrayElements(JNIEnv * env, JA j, const C * c, jint mode) {
202 TRACE("void PrimArrayConvImpl.releaseArrayElements(JNIEnv *, JA, const C *, jint)");
203 // XXX currently, only exact-width base type conversions supported
204 assert(sizeof(J) == sizeof(C));
205 assert(j != NULL);
206 assert(c != NULL);
207
208 if (c != NULL) {
209 // ok to strip const, pinned arrays are not changed by release
210 // and copies cannot be used after release
211 C * ca = const_cast< C * >(c);
212 // convert pointer types
213 (env->*REL)(j, reinterpret_cast< J * >(ca), mode);
214 }
215 }
216
217 static JA
newArrayPrimArrayConvImpl218 newArray(JNIEnv * env, jsize len, const C * c) {
219 TRACE("JA PrimArrayConvImpl.newArray(JNIEnv *, jsize, const C *)");
220 // XXX currently, only exact-width base type conversions supported
221 assert(sizeof(J) == sizeof(C));
222 assert(c != NULL);
223
224 // init return value to error
225 JA j = NULL;
226
227 JA ja = (env->*NEW)(len);
228 if (env->ExceptionCheck() != JNI_OK) {
229 // exception pending
230 } else {
231 // the JNI Spec (1.4..6) on New<PrimitiveType>Array is not
232 // explicit on whether an exception has been registered when the
233 // operation returns NULL; so, better code defensively:
234 if (ja == NULL) {
235 const char * cl = "java/lang/AssertionError";
236 const char * m = ("JTie: a JNI New<PrimitiveType>Array"
237 " function call returned NULL but has not"
238 " registered an exception with the VM"
239 " (file: " __FILE__ ")");
240 registerException(env, cl, m);
241 } else {
242 // convert pointer types
243 const J * cjc = reinterpret_cast< const J * >(c);
244
245 // copy values to Java array
246 (env->*SET)(ja, 0, len, cjc);
247 if (env->ExceptionCheck() != JNI_OK) {
248 // exception pending
249 assert(false); // coding error: invalid index
250 } else {
251 // ok
252 j = ja;
253 }
254 }
255 }
256 return j;
257 }
258
259 private:
PrimArrayConvImplPrimArrayConvImpl260 PrimArrayConvImpl() {
261 // prohibit unsupported array type casts
262 is_valid_primitive_type_mapping< J, C >();
263 }
264 };
265
266 /**
267 * Implements ArrayConv for Java Object array types.
268 *
269 * Please, note that on balance this type of object array mapping, while
270 * having a few merits, has turned out inferior to other array mappings: It
271 * - comes with an unavoidable performance overhead (value-copy semantics)
272 * - displays different argument conversion semantics (pass-by-value for
273 * Java-to-C while pass-by-reference for C-to-Java) and
274 * - hence, complicates the usage and caller's object management (making it
275 * more error-prone to memory leaks or integrity errors), as well as
276 * - hampers the maintenance of this class due to fine points in the code.
277 *
278 * The reason for the difficulties of this mapping stems from an asymmetry:
279 * The natural 1-1 mapping of Java 'MyClass[]' is C 'MyClass**' while C
280 * 'MyClass*', strictly speaking, has no Java Object array pendant, for
281 * lack of notion of (contigous arrays of) embedded Java Objects.
282 *
283 * While assumed to work, this mapping has not been used and tested yet;
284 * we're keeping it, for it still meets best Java programmers' expectations
285 * for C object arrays to be mapped to Java Object[].
286 */
287 template< typename J, typename C >
288 struct ObjectArrayConvImpl {
289 static C *
getArrayElementsObjectArrayConvImpl290 getArrayElements(JNIEnv * env, jobjectArray j, jboolean * isCopy) {
291 TRACE("C * ObjectArrayConvImpl.getArrayElements(JNIEnv *, jobjectArray, jboolean *)");
292 assert(j != NULL);
293
294 // init return value to error
295 C * c = NULL;
296
297 const jsize n = env->GetArrayLength(j);
298 if (env->ExceptionCheck() != JNI_OK) {
299 // exception pending
300 assert(false); // coding error: invalid argument
301 } else {
302 // ISO C++: 'new' throws std::bad_alloc if unsuccessful
303 C * ca = new C[n];
304
305 cstatus s = copyToCObjectArray(ca, j, n, env);
306 if (s != 0) {
307 // exception pending
308 assert(env->ExceptionCheck() != JNI_OK);
309 delete[] ca;
310 } else {
311 // assign isCopy out parameter
312 if (isCopy != NULL) {
313 *isCopy
314 = ResultBasicT< jboolean, bool >::convert(true, env);
315 }
316
317 // ok
318 c = ca;
319 }
320 }
321 return c;
322 }
323
324 static void
releaseArrayElementsObjectArrayConvImpl325 releaseArrayElements(JNIEnv * env, jobjectArray j, const C * c, jint mode) {
326 TRACE("void ObjectArrayConvImpl.releaseArrayElements(JNIEnv *, jobjectArray, const C *, jint)");
327 assert(j != NULL);
328 assert(c != NULL);
329 delete[] c;
330 }
331
332 static jobjectArray
newArrayObjectArrayConvImpl333 newArray(JNIEnv * env, jsize len, const C * c) {
334 TRACE("jobjectArray ObjectArrayConvImpl.newArray(JNIEnv *, jsize, const C *)");
335 assert(c != NULL);
336
337 // init return value to error
338 jobjectArray j = NULL;
339 jobjectArray ja = NULL;
340
341 // get a (local or global) class object reference
342 jclass cls = ObjectResult< J *, C * >::J_ctor::getClass(env);
343 if (cls == NULL) {
344 // exception pending
345 } else {
346 J * ja = newJavaObjectArray(cls, len, env);
347 if (ja == NULL) {
348 // exception pending
349 } else {
350 cstatus s = copyToJavaObjectArray(ja, c, len, env);
351 if (s != 0) {
352 // exception pending
353 assert(env->ExceptionCheck() != JNI_OK);
354 } else {
355 // ok
356 j = ja;
357 }
358 }
359
360 // release reference (if needed)
361 ObjectResult< J *, C * >::J_ctor::releaseRef(env, cls);
362 }
363 return j;
364 }
365
366 private:
367 // Returns a new Java Object array with all elements initialized to null;
368 // in case of a NULL return, an exception is pending.
369 J *
370 newJavaObjectArray(jclass cls, jsize n, JNIEnv * env);
371
372 // Copies objects referred by a Java Object array over a C object array;
373 // a non-zero return value indicates failure with an exception pending.
374 cstatus
375 copyToCObjectArray(C * c, jobjectArray j, jsize n, JNIEnv * env);
376
377 // Initializes a Java Object array with references from a C object array;
378 // a non-zero return value indicates failure with an exception pending.
379 cstatus
380 copyToJavaObjectArray(jobjectArray j, C * c, jsize n, JNIEnv * env);
381 };
382
383 template< typename J, typename C >
384 inline J *
385 ObjectArrayConvImpl< J, C >::
newJavaObjectArray(jclass cls,jsize n,JNIEnv * env)386 newJavaObjectArray(jclass cls, jsize n, JNIEnv * env) {
387 assert(cls);
388
389 // init return value to error
390 J * j = NULL;
391
392 jobjectArray ja = env->NewObjectArray(n, cls, NULL);
393 if (env->ExceptionCheck() != JNI_OK) {
394 // exception pending
395 } else {
396 // the JNI Spec (1.4..6) on NewObjectArray is not explicit on
397 // whether an exception has been registered when the operation
398 // returns NULL; so, better code defensively:
399 if (ja == NULL) {
400 const char * cl = "java/lang/AssertionError";
401 const char * m = ("JTie: a JNI NewObjectArray function call"
402 " returned NULL but has not registered an"
403 " exception with the VM"
404 " (file: " __FILE__ ")");
405 registerException(env, cl, m);
406 } else {
407 // ok
408 j = ja;
409 }
410 }
411 return j;
412 }
413
414 template< typename J, typename C >
415 inline cstatus
416 ObjectArrayConvImpl< J, C >::
copyToCObjectArray(C * c,jobjectArray j,jsize n,JNIEnv * env)417 copyToCObjectArray(C * c, jobjectArray j, jsize n, JNIEnv * env) {
418 assert(j != NULL);
419 assert(c != NULL);
420
421 // init return value to error
422 cstatus s = -1;
423
424 // copy objects referenced from Java array to new array
425 jsize i;
426 for (i = 0; i < n; i++) {
427 // get the Java array element
428 _jobject * jfo = env->GetObjectArrayElement(j, i);
429 if (env->ExceptionCheck() != JNI_OK) {
430 // exception pending
431 assert(false); // coding error: invalid index
432 break;
433 }
434 assert(env->ExceptionCheck() == JNI_OK);
435
436 // get the instance referenced by Java array element
437 _jtie_Object * jao = cast< _jtie_Object *, _jobject * >(jfo);
438 if (jao == NULL) {
439 const char * cl = "java/lang/IllegalArgumentException";
440 const char * m = ("JTie: the Java Object array must not have"
441 " null as elements when mapped to a"
442 " C object array (file: " __FILE__ ")");
443 registerException(env, cl, m);
444 break;
445 }
446 assert(jao != NULL);
447
448 // get the C/C++ object referenced by Java array element
449 C * co = ObjectParam< _jtie_Object *, C * >::convert(s, jao, env);
450 assert(s != 0 || co != NULL);
451 if (s != 0) {
452 // exception pending
453 break;
454 }
455 assert(co != NULL);
456
457 // copy referenced object to array element
458 // - copy-by-value semantics for Java only knows object references;
459 // - therefore, this mapping requires an accessible copy c'tor;
460 // - asymmetry to copyToCObjectArray()'s reference semantics
461 c[i] = *co;
462 }
463 if (i < n) {
464 // exception pending
465 } else {
466 // ok
467 s = 0;
468 }
469
470 return c;
471 }
472
473 template< typename J, typename C >
474 inline cstatus
475 ObjectArrayConvImpl< J, C >::
copyToJavaObjectArray(jobjectArray j,C * c,jsize n,JNIEnv * env)476 copyToJavaObjectArray(jobjectArray j, C * c, jsize n, JNIEnv * env) {
477 assert(c != NULL);
478 assert(j != NULL);
479
480 // init return value to error
481 cstatus s = -1;
482
483 // copy C object references to Java array
484 jsize i;
485 for (i = 0; i < n; i++) {
486 // obtain reference to array element
487 // - no need for value-copy, would burden app's object management
488 // - asymmetry to copyToJavaObjectArray()'s value-copy semantics
489 C * co = c + i;
490
491 // get a Java object reference
492 J * jao = ObjectResult< J *, C * >::convert(co, env);
493 if (jao == NULL) {
494 // exception pending
495 assert(env->ExceptionCheck() != JNI_OK);
496 break;
497 }
498 assert(jao != NULL);
499
500 // set the Java array element
501 _jobject * jfo = cast< _jobject *, J * >(jao);
502 env->SetObjectArrayElement(j, i, jfo);
503 if (env->ExceptionCheck() != JNI_OK) {
504 // exception pending
505 assert(false); // coding error: invalid index or jao not subclass
506 break;
507 }
508 assert(env->ExceptionCheck() == JNI_OK);
509 }
510 if (i < n) {
511 // exception pending
512 } else {
513 // ok
514 s = 0;
515 }
516
517 return c;
518 }
519
520 // ---------------------------------------------------------------------------
521 // Specializations for array conversions
522 // ---------------------------------------------------------------------------
523
524 // Avoid mapping types by broad, generic rules, which easily results in
525 // template instantiation ambiguities for non-primitive types. Therefore,
526 // we enumerate all specicializations for primitive types.
527
528 // extend specializations to array of const
529 // Not sure why this generalized const specialization
530 // template< typename JA,
531 // typename CA >
532 // struct ArrayConv< JA, const CA > : ArrayConv< JA, CA > {};
533 // doesn't resolve (no effect); specializing individually for each type then.
534
535 // specialize arrays conversion helper (for non-const and const)
536 #define JTIE_SPECIALIZE_ARRAY_TYPE_HELPER( JA, J, JN, C ) \
537 template<> \
538 struct ArrayConv< JA, C * > \
539 : PrimArrayConvImpl< JA, J, C, \
540 &JNIEnv::Get##JN##ArrayElements, \
541 &JNIEnv::Release##JN##ArrayElements, \
542 &JNIEnv::New##JN##Array, \
543 &JNIEnv::Set##JN##ArrayRegion > {}; \
544 template<> \
545 struct ArrayConv< JA, const C * > \
546 : ArrayConv< JA, C * > {};
547
548 // ---------------------------------------------------------------------------
549 // Specializations for exact-width type array conversions
550 // ---------------------------------------------------------------------------
551
552 JTIE_SPECIALIZE_ARRAY_TYPE_HELPER(jbooleanArray, jboolean, Boolean, bool)
553
554 JTIE_SPECIALIZE_ARRAY_TYPE_HELPER(jbyteArray, jbyte, Byte, char)
555 JTIE_SPECIALIZE_ARRAY_TYPE_HELPER(jbyteArray, jbyte, Byte, signed char)
556 JTIE_SPECIALIZE_ARRAY_TYPE_HELPER(jbyteArray, jbyte, Byte, unsigned char)
557
558 JTIE_SPECIALIZE_ARRAY_TYPE_HELPER(jfloatArray, jfloat, Float, float)
559 JTIE_SPECIALIZE_ARRAY_TYPE_HELPER(jdoubleArray, jdouble, Double, double)
560
561
562 // ---------------------------------------------------------------------------
563 // Specializations for variable-width type array conversions
564 // ---------------------------------------------------------------------------
565
566 // jshort in LP32, ILP32, LP64, ILP64, LLP64
567 JTIE_SPECIALIZE_ARRAY_TYPE_HELPER(jshortArray, jshort, Short, signed short)
568 JTIE_SPECIALIZE_ARRAY_TYPE_HELPER(jshortArray, jshort, Short, unsigned short)
569
570 // jshort in LP32
571 JTIE_SPECIALIZE_ARRAY_TYPE_HELPER(jshortArray, jshort, Short, signed int)
572 JTIE_SPECIALIZE_ARRAY_TYPE_HELPER(jshortArray, jshort, Short, unsigned int)
573
574 // jint in ILP32, LP64, LLP64
575 JTIE_SPECIALIZE_ARRAY_TYPE_HELPER(jintArray, jint, Int, signed int)
576 JTIE_SPECIALIZE_ARRAY_TYPE_HELPER(jintArray, jint, Int, unsigned int)
577
578 // jint in LP32, ILP32, LLP64
579 JTIE_SPECIALIZE_ARRAY_TYPE_HELPER(jintArray, jint, Int, signed long)
580 JTIE_SPECIALIZE_ARRAY_TYPE_HELPER(jintArray, jint, Int, unsigned long)
581
582 // jlong in ILP64
583 JTIE_SPECIALIZE_ARRAY_TYPE_HELPER(jlongArray, jlong, Long, signed int)
584 JTIE_SPECIALIZE_ARRAY_TYPE_HELPER(jlongArray, jlong, Long, unsigned int)
585
586 // jlong in LP64, ILP64
587 JTIE_SPECIALIZE_ARRAY_TYPE_HELPER(jlongArray, jlong, Long, signed long)
588 JTIE_SPECIALIZE_ARRAY_TYPE_HELPER(jlongArray, jlong, Long, unsigned long)
589
590 // jlong in LLP64
591 JTIE_SPECIALIZE_ARRAY_TYPE_HELPER(jlongArray, jlong, Long, signed long long)
592 JTIE_SPECIALIZE_ARRAY_TYPE_HELPER(jlongArray, jlong, Long, unsigned long long)
593
594 // jdouble
595 JTIE_SPECIALIZE_ARRAY_TYPE_HELPER(jdoubleArray, jdouble, Double, long double)
596
597 // ---------------------------------------------------------------------------
598
599 #endif // jtie_tconv_array_impl_hpp
600