9c7aeb2be0fa6e57ea04d3c092c774b938324fee
[scilab.git] / scilab / modules / jvm / src / c / JniUtils.c
1 /*------------------------------------------------------------------------*/
2 /* file: JniUtils.cpp                                                     */
3 /* Copyright INRIA 2007                                                   */
4 /* Authors : Jean-Baptiste Silvy                                          */
5 /* desc : Set of functions to simplify the use of JNI. These              */
6 /*        functions are used to call Java from C code                     */
7 /*------------------------------------------------------------------------*/
8
9 #include <string.h>
10
11 #include "JniUtils.h"
12 #include "MALLOC.h"
13 #include "Scierror.h"
14
15 /*------------------------------------------------------------------------------------------*/
16 /** Static variable containing the jvm. */
17 static JavaVM * sciJVM  = NULL ;
18 /** Static variable containing the current java environment. */
19 static JNIEnv * sciJEnv = NULL ;
20
21 /*------------------------------------------------------------------------------------------*/
22 jniCallMethodCache * jniCreateCallMethodCache( void )
23 {
24   jniCallMethodCache * newCache = MALLOC(sizeof(jniCallMethodCache)) ;
25   if ( newCache == NULL ) { return NULL ; }
26
27   newCache->methodId      = NULL ;
28   newCache->instanceClass = NULL ;
29
30   return newCache;
31 }
32 /*------------------------------------------------------------------------------------------*/
33 void jniDestroyCallMethodCache( jniCallMethodCache * cache )
34 {
35   if ( cache != NULL )
36   {
37      cache->methodId = NULL ;
38      if ( cache->instanceClass != NULL )
39      {
40        (*sciJEnv)->DeleteGlobalRef(sciJEnv, cache->instanceClass) ;
41      }
42      FREE(cache) ;
43   }
44 }
45 /*------------------------------------------------------------------------------------------*/
46 void jniIntializeCallMethodCache( jniCallMethodCache * cache, jclass instanceClass, jmethodID methodId )
47 {
48   cache->methodId = methodId ;
49
50   cache->instanceClass = (*sciJEnv)->NewGlobalRef(sciJEnv, instanceClass) ;
51 }
52 /*------------------------------------------------------------------------------------------*/
53 BOOL jniIsCallMethodCacheInitialized( jniCallMethodCache * cache )
54 {
55   return ( cache != NULL && cache->instanceClass != NULL ) ;
56 }
57 /*------------------------------------------------------------------------------------------*/
58 void jniInitUtils( JavaVM * jvm )
59 {
60   sciJVM = jvm ;
61   jniUpdateCurrentEnv() ;
62 }
63 /*------------------------------------------------------------------------------------------*/
64 void jniCloseUtils( void )
65 {
66   sciJEnv = NULL ;
67   sciJVM  = NULL ;
68 }
69 /*------------------------------------------------------------------------------------------*/
70 void jniSetCurrentEnv( JNIEnv * env )
71 {
72   sciJEnv = env ;
73 }
74 /*------------------------------------------------------------------------------------------*/
75 void jniUpdateCurrentEnv( void )
76 {
77   /* tips from sun, use AttachCurrentThread to always get the right environment */ 
78   (*sciJVM)->AttachCurrentThread( sciJVM, (void **) &sciJEnv, NULL ) ;
79   //(*sciJVM)->GetEnv( sciJVM, (void **) &sciJEnv, JNI_VERSION_1_6 ) ;
80   /* clear all previous exceptions pending on the thread */
81   (*sciJEnv)->ExceptionClear( sciJEnv ) ;
82 }
83 /*------------------------------------------------------------------------------------------*/
84 JNIEnv * jniGetCurrentJavaEnv( void )
85 {
86   return sciJEnv ;
87 }
88 /*------------------------------------------------------------------------------------------*/
89 jdoubleArray jniCreateDoubleArrayCopy( const jdouble * cArray, int nbElements )
90 {
91   jdoubleArray res = (*sciJEnv)->NewDoubleArray( sciJEnv, nbElements ) ;
92   (*sciJEnv)->SetDoubleArrayRegion( sciJEnv, res, 0, nbElements, cArray ) ;
93   if ( !jniCheckLastCall(FALSE) )
94   {
95     return NULL ;
96   }
97   return res ;
98 }
99 /*------------------------------------------------------------------------------------------*/
100 jintArray jniCreateIntArrayCopy( const jint * cArray, int nbElements )
101 {
102   jintArray res = (*sciJEnv)->NewIntArray( sciJEnv, nbElements ) ;
103   (*sciJEnv)->SetIntArrayRegion( sciJEnv, res, 0, nbElements, cArray ) ;
104   if ( !jniCheckLastCall(FALSE) )
105   {
106     return NULL ;
107   }
108   return res ;
109 }
110 /*------------------------------------------------------------------------------------------*/
111 void jniCopyJavaDoubleArray( const jdoubleArray javaArray, jdouble * cArray )
112 {
113   int arrayLength = (*sciJEnv)->GetArrayLength( sciJEnv, javaArray ) ;
114   (*sciJEnv)->GetDoubleArrayRegion( sciJEnv, javaArray, 0, arrayLength, cArray ) ;
115 }
116 /*------------------------------------------------------------------------------------------*/
117 void jniCopyJavaIntArray( const jintArray javaArray, jint * cArray )
118 {
119   int arrayLength = (*sciJEnv)->GetArrayLength( sciJEnv, javaArray ) ;
120   (*sciJEnv)->GetIntArrayRegion( sciJEnv, javaArray, 0, arrayLength, cArray ) ;
121 }
122 /*------------------------------------------------------------------------------------------*/
123 jstring jniCreateStringCopy( const char * cString )
124 {
125   jstring res = (*sciJEnv)->NewStringUTF( sciJEnv, cString ) ;
126   if ( !jniCheckLastCall(FALSE) )
127   {
128     return NULL ;
129   }
130   return res ;
131 }
132 /*------------------------------------------------------------------------------------------*/
133 void jniDeleteLocalEntity( jobject entity )
134 {
135   if ( entity != NULL )
136   {
137     (*sciJEnv)->DeleteLocalRef( sciJEnv, entity ) ;
138   }
139 }
140 /*------------------------------------------------------------------------------------------*/
141 void jniDeleteGlobalEntity( jobject entity )
142 {
143   if ( entity != NULL )
144   {
145     (*sciJEnv)->DeleteGlobalRef( sciJEnv, entity ) ;
146   }
147 }
148 /*------------------------------------------------------------------------------------------*/
149 BOOL jniCreateDefaultInstance( const char * className, jclass * instanceClass, jobject * instance )
150 {
151   jmethodID constructObject = NULL ;
152   jobject localInstance ;
153   jclass  localClass ;
154   
155   localClass = (*sciJEnv)->FindClass( sciJEnv, className ) ;
156   if ( !jniCheckLastCall(TRUE) )
157   {
158     Scierror( 999, "Unable to find class %s.\r\n", className ) ;
159     *instanceClass = NULL ;
160     *instance      = NULL ;
161     return FALSE ;
162   }
163
164   *instanceClass = (*sciJEnv)->NewGlobalRef(sciJEnv, localClass) ;
165
166   (*sciJEnv)->DeleteLocalRef(sciJEnv, localClass) ;
167   localClass = NULL ;
168
169   /* "()V" for no parameters and return void */
170   /* "<init>" for constructor */
171   constructObject = (*sciJEnv)->GetMethodID( sciJEnv, *instanceClass, "<init>", "()V" ) ;
172
173   localInstance = (*sciJEnv)->NewObject( sciJEnv, *instanceClass, constructObject ) ;
174   if ( !jniCheckLastCall(TRUE) )
175   {
176     Scierror( 999, "Unable to create an instance of class %s.\r\n", className ) ;
177     (*sciJEnv)->DeleteGlobalRef(sciJEnv, *instanceClass) ;
178     *instanceClass = NULL ;
179     *instance      = NULL ;
180     return FALSE ;
181   }
182   
183   *instance = (*sciJEnv)->NewGlobalRef(sciJEnv, localInstance) ;
184   (*sciJEnv)->DeleteLocalRef(sciJEnv, localInstance) ;
185   localInstance = NULL;
186
187   return TRUE ;
188 }
189 /*------------------------------------------------------------------------------------------*/
190 BOOL jniCreateDefaultInstanceSafe( const char * className, jclass * instanceClass, jobject * instance )
191 {
192   if ( instanceClass == NULL || instance == NULL ) { return FALSE ; }
193   jniUpdateCurrentEnv() ;
194   return jniCreateDefaultInstance( className, instanceClass, instance ) ;
195 }
196 /*------------------------------------------------------------------------------------------*/
197 BOOL jniCallVoidFunctionV( jobject instance, jclass instanceClass, const char * functionName, const char * paramTypes, va_list args )
198 {
199   jmethodID   voidMethod = NULL ;
200   jclass      instanceClasse = (*sciJEnv)->GetObjectClass( sciJEnv, instance ) ; /* retrieve the class of the object */
201   char      * callingSequence = NULL ;
202
203   /* Add (...)V around the paramList */
204   callingSequence = MALLOC( ( strlen(paramTypes) + 4 ) * sizeof(char) ) ; /* 3 for ()V and 1 for 0 terminating character */
205   if ( callingSequence == NULL ) { return FALSE ; }
206
207   sprintf( callingSequence, "(%s)V", paramTypes ) ;
208
209   /* Find the method in the class */
210   voidMethod = (*sciJEnv)->GetMethodID( sciJEnv, instanceClass, functionName, callingSequence ) ;
211   if ( !jniCheckLastCall(TRUE) || voidMethod == NULL )
212   {
213     Scierror( 999, "Unable to find method %s.\r\n", functionName ) ;
214     FREE( callingSequence ) ;
215     return FALSE ;
216   }
217
218   /* Call the function with the optionals parameters */
219   (*sciJEnv)->CallVoidMethodV( sciJEnv, instance, voidMethod, args ) ;
220   if ( !jniCheckLastCall(TRUE) )
221   {
222     Scierror( 999, "Error when calling method %s.\r\n", functionName ) ;
223     FREE( callingSequence ) ;
224     return FALSE ;
225   }
226
227   //(*sciJEnv)->DeleteLocalRef(sciJEnv, instanceClass) ;
228   FREE( callingSequence ) ;
229   return TRUE ;
230 }
231 /*------------------------------------------------------------------------------------------*/
232 BOOL jniCallVoidFunction( jobject instance, jclass instanceClass, const char * functionName, const char * paramTypes, ... )
233 {
234   BOOL status = FALSE ;
235   va_list args ;
236   va_start( args, paramTypes ) ;
237   status = jniCallVoidFunctionV( instance, instanceClass, functionName, paramTypes, args ) ;
238   va_end(args);
239   
240   return status ;
241 }
242 /*------------------------------------------------------------------------------------------*/
243 BOOL jniCallVoidFunctionSafe( jobject instance, jclass instanceClass, const char * functionName, const char * paramTypes, ... )
244 {
245   va_list args ;
246   BOOL status = FALSE ;
247
248   if ( instance == NULL ) { return FALSE ; }
249
250   jniUpdateCurrentEnv() ;
251
252   /* Call the function with the optionals parameters */
253   va_start( args, paramTypes ) ;
254   status = jniCallVoidFunctionV( instance, instanceClass, functionName, paramTypes, args ) ;
255   va_end(args);
256   return status ;
257
258 }
259 /*------------------------------------------------------------------------------------------*/
260 jvalue jniCallMemberFunction( jobject instance, jniCallMethodCache * cache, const char * functionName, const char * descriptor, ... )
261 {
262   va_list args ;
263   jvalue res;
264
265   /* Call the function with the optionals parameters */
266   va_start( args, descriptor ) ;
267   res = jniCallMemberFunctionV( instance, cache, functionName, descriptor, args ) ;
268   va_end(args);
269   return res ;
270 }
271 /*------------------------------------------------------------------------------------------*/
272 jvalue jniCallMemberFunctionSafe( jobject instance, jniCallMethodCache * cache, const char * functionName, const char * descriptor, ... )
273 {
274   va_list args ;
275   jvalue res;
276
277   jniInitJValue(&res) ;
278
279   if ( instance == NULL ) { return res ; }
280
281   jniUpdateCurrentEnv() ;
282
283   /* Call the function with the optionals parameters */
284   va_start( args, descriptor ) ;
285   res = jniCallMemberFunctionV( instance, cache, functionName, descriptor, args ) ;
286   va_end(args);
287   return res ;
288 }
289 /*------------------------------------------------------------------------------------------*/
290 jvalue jniCallMemberFunctionV( jobject instance, jniCallMethodCache * cache, const char * functionName, const char * descriptor, va_list args )
291 {
292   jclass instanceClass = NULL;
293   jmethodID methodId = NULL;
294   jvalue res;
295   int returnTypeIndex = 0 ;
296   
297   jniInitJValue(&res) ;
298   
299   if ((*sciJEnv)->EnsureLocalCapacity(sciJEnv, 2) == JNI_OK)
300   {
301     if ( !jniIsCallMethodCacheInitialized(cache) )
302     {
303       // Need to intialize cache
304       instanceClass = (*sciJEnv)->GetObjectClass(sciJEnv, instance) ;
305       methodId = (*sciJEnv)->GetMethodID(sciJEnv, instanceClass, functionName, descriptor ) ;
306       if ( cache != NULL )
307       {
308         jniIntializeCallMethodCache(cache, instanceClass, methodId) ;
309       }
310     }
311     else
312     {
313       // cache already initialized
314       instanceClass = cache->instanceClass ;
315       methodId = cache->methodId ;
316     }
317
318     if ( methodId == NULL )
319     {
320       Scierror( 999, "Error when calling function %s.\r\n", functionName ) ;
321       return res;
322     }
323
324     /* parse the descriptor to find return type */
325     while( descriptor[returnTypeIndex] != ')' )
326     {
327       returnTypeIndex++ ;
328     }
329     returnTypeIndex++;
330
331     switch( descriptor[returnTypeIndex] )
332     {
333     case 'V':
334       (*sciJEnv)->CallVoidMethodV(sciJEnv, instance, methodId, args) ;
335       break;
336     case 'Z':
337       res.z = (*sciJEnv)->CallBooleanMethodV(sciJEnv, instance, methodId, args) ;
338       break;
339     case 'B':
340       res.b = (*sciJEnv)->CallByteMethodV(sciJEnv, instance, methodId, args) ;
341       break;
342     case 'C':
343       res.c = (*sciJEnv)->CallCharMethodV(sciJEnv, instance, methodId, args) ;
344       break;
345     case 'S':
346       res.s = (*sciJEnv)->CallShortMethodV(sciJEnv, instance, methodId, args) ;
347       break;
348     case 'I':
349       res.i = (*sciJEnv)->CallIntMethodV(sciJEnv, instance, methodId, args) ;
350       break;
351     case 'J':
352       res.j = (*sciJEnv)->CallLongMethodV(sciJEnv, instance, methodId, args) ;
353       break;
354     case 'F':
355       res.f = (*sciJEnv)->CallFloatMethodV(sciJEnv, instance, methodId, args) ;
356       break;
357     case 'D':
358       res.d = (*sciJEnv)->CallDoubleMethodV(sciJEnv, instance, methodId, args) ;
359       break;
360     case '[':
361     case'L':
362       res.l = (*sciJEnv)->CallObjectMethodV(sciJEnv, instance, methodId, args) ;
363       break;
364     default:
365       Scierror( 999, "Error when calling function %s.\r\n", functionName ) ;
366       break;
367     }
368
369     if ( !jniCheckLastCall(TRUE) )
370     {
371       Scierror( 999, "Error when calling function %s.\r\n", functionName ) ;
372       return res ;
373     }
374
375   }
376   return res;
377
378 }
379 /*------------------------------------------------------------------------------------------*/
380 void jniInitJValue( jvalue * value )
381 {
382   value->b = 0 ;
383   value->c = 0 ;
384   value->d = 0.0 ;
385   value->f = 0.0 ;
386   value->i = 0 ;
387   value->j = 0 ;
388   value->l = NULL ;
389   value->s = 0 ;
390   value->z = 0 ;
391 }
392 /*------------------------------------------------------------------------------------------*/
393 double jniGetDoubleValue( jvalue value )
394 {
395   return value.d ;
396 }
397 /*------------------------------------------------------------------------------------------*/
398 int jniGetIntValue( jvalue value )
399 {
400   return value.i ;
401 }
402 /*------------------------------------------------------------------------------------------*/
403 BOOL jniCheckLastCall( BOOL dumpStack )
404 {
405   jniUpdateCurrentEnv() ;
406   if ( !sciJEnv ) { return FALSE ; }
407
408   if ( (*sciJEnv)->ExceptionOccurred(sciJEnv) )
409   {
410     if ( dumpStack )
411     {
412       (*sciJEnv)->ExceptionDescribe(sciJEnv) ;
413     }
414     return FALSE ;
415   }
416   return TRUE ;
417 }
418 /*------------------------------------------------------------------------------------------*/