d3c75939bd635d37a8b5873a75078f56cd8c9a1c
[nativez] / src / notzed.nativez / jni / nativez-jni.c
1 /*
2  * Copyright (C) 2017,2019 Michael Zucchi
3  *
4  * This file is part of nativez <https://www.zedzone.space/software/nativez.html>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are
8  * met:
9  *
10  *     (1) Redistributions of source code must retain the above copyright
11  *     notice, this list of conditions and the following disclaimer. 
12  *
13  *     (2) Redistributions in binary form must reproduce the above copyright
14  *     notice, this list of conditions and the following disclaimer in
15  *     the documentation and/or other materials provided with the
16  *     distribution.  
17  *     
18  *     (3)The name of the author may not be used to
19  *     endorse or promote products derived from this software without
20  *     specific prior written permission.
21  * 
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25  * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
26  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
27  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
28  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
30  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
31  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE. 
33  * 
34  */
35
36 #include <stdlib.h>
37 #include <stdint.h>
38
39 #include "nativez.h"
40
41 #define NZOBJECT "au/notzed/nativez/NativeZ"
42
43 static JavaVM *vm;
44
45 static jclass Buffer_classid;
46 static jmethodID Buffer_hasArray_;
47 static jmethodID Buffer_isDirect_;
48 static jmethodID Buffer_array_;
49 static jmethodID Buffer_arrayOffset_;
50 static jmethodID Buffer_position_;
51 static jmethodID Buffer_position_i;
52 static jmethodID Buffer_limit_;
53 static jmethodID Buffer_limit_i;
54
55 static jclass ByteBuffer_classid;
56 static jmethodID ByteBuffer_order_l;
57
58 static jobject ByteOrder_nativeOrder; // ByteOrder.nativeOrder()
59
60 static jclass NativeZ_classid;
61 static jmethodID NativeZ_create_lj;
62 static jmethodID NativeZ_register_l;
63 static jmethodID NativeZ_resolve_lj;
64 static jmethodID NativeZ_refer_lj;
65 static jfieldID NativeZ_p;
66
67 static const NZRefTable reftable[] = {
68         JCLASS(Buffer_classid, "java/nio/Buffer"),
69         JMETHOD(Buffer_hasArray_, "hasArray", "()Z"),
70         JMETHOD(Buffer_isDirect_, "isDirect", "()Z"),
71         JMETHOD(Buffer_array_, "array", "()Ljava/lang/Object;"),
72         JMETHOD(Buffer_arrayOffset_, "arrayOffset", "()I"),
73         JMETHOD(Buffer_position_, "position", "()I"),
74         JMETHOD(Buffer_position_i, "position", "(I)Ljava/nio/Buffer;"),
75         JMETHOD(Buffer_limit_, "limit", "()I"),
76         JMETHOD(Buffer_limit_i, "limit", "(I)Ljava/nio/Buffer;"),
77         
78         JCLASS(ByteBuffer_classid, "java/nio/ByteBuffer"),
79         JMETHOD(ByteBuffer_order_l, "order", "(Ljava/nio/ByteOrder;)Ljava/nio/ByteBuffer;"),
80
81         JCLASS(NativeZ_classid, NZOBJECT),
82         JSMETHOD(NativeZ_create_lj, "create", "(Ljava/lang/Class;J)L" NZOBJECT ";"),
83         JSMETHOD(NativeZ_register_l, "register", "(L" NZOBJECT ";)L" NZOBJECT ";"),
84         JSMETHOD(NativeZ_resolve_lj, "resolve", "(Ljava/lang/Class;J)L" NZOBJECT ";"),
85         JSMETHOD(NativeZ_refer_lj, "refer", "(Ljava/lang/Class;J)L" NZOBJECT ";"),
86         JFIELD(NativeZ_p, "p", "J"),
87
88         JEND
89 };
90
91 jint nativez_OnLoad(JavaVM *vmi, JNIEnv *env) {
92         /* Save VM - required for callbacks from threads */
93         vm = vmi;
94
95         if (nativez_ResolveReferences(env, reftable) != 0)
96                 return -1;
97
98         jclass jc = (*env)->FindClass(env, "java/nio/ByteOrder");
99         if (jc == NULL)
100                 return -1;
101         jmethodID ByteOrder_nativeOrder_ = (*env)->GetStaticMethodID(env, jc, "nativeOrder", "()Ljava/nio/ByteOrder;");
102         ByteOrder_nativeOrder = (*env)->NewGlobalRef(env, (*env)->CallStaticObjectMethodA(env, jc, ByteOrder_nativeOrder_, NULL));
103
104         return 0;
105 }
106
107 void *nativez_AllocAligned(size_t align, size_t size) {
108         void *mem;
109 #ifdef HAVE_ALIGNED_MALLOC
110         mem = _aligned_malloc(size, align);
111         return mem;
112 #else
113         if (posix_memalign(&mem, align, size) == 0)
114                 return mem;
115         return NULL;
116 #endif
117 }
118
119 void nativez_FreeAligned(void *mem) {
120 #ifdef HAVE_ALIGNED_MALLOC
121         _aligned_free(mem);
122 #else
123         free(mem);
124 #endif
125 }
126
127 JNIEnv *nativez_AttachCurrentThread(void) {
128         JNIEnv *env = NULL;
129         
130         (*vm)->AttachCurrentThread(vm, (void **)&env, NULL);
131
132         // one assumes this is null if error return?
133         return env;
134 }
135
136 JNIEnv *nativez_AttachCurrentThreadAsDaemon(void) {
137         JNIEnv *env = NULL;
138         
139         (*vm)->AttachCurrentThreadAsDaemon(vm, (void **)&env, NULL);
140
141         // one assumes this is null if error return?
142         return env;
143 }
144
145 /* ********************************************************************** */
146
147 void nativez_ThrowException(JNIEnv *env, const char *type, const char *msg) {
148         jclass jc = (*env)->FindClass(env, type);
149
150         if (jc)
151                 (*env)->ThrowNew(env, jc, msg);
152         else {
153                 fprintf(stderr, "Unable to throw exception `%s': `%s'\n", type, msg);
154                 fflush(stderr);
155         }
156 }
157
158 void nativez_ThrowOutOfMemoryError(JNIEnv *env, const char *msg) {
159         nativez_ThrowException(env, "java/lang/OutOfMemoryError", msg);
160 }
161
162 /* ********************************************************************** */
163
164 jobject nativez_NewDirectBuffer(JNIEnv *env, void *data, jlong size) {
165         jobject jo;
166
167         jo = (*env)->NewDirectByteBuffer(env, data, size);
168         if (jo) {
169                 jvalue arg = { .l = ByteOrder_nativeOrder };
170
171                 (*env)->CallObjectMethodA(env, jo, ByteBuffer_order_l, &arg);
172         }
173
174         return jo;
175 }
176
177 jboolean nativez_BufferHasArray(JNIEnv *env, jobject jbuffer) {
178         return (*env)->CallBooleanMethodA(env, jbuffer, Buffer_hasArray_, NULL);
179 }
180
181 jboolean nativez_BufferIsDirect(JNIEnv *env, jobject jbuffer) {
182         return (*env)->CallBooleanMethodA(env, jbuffer, Buffer_isDirect_, NULL);
183 }
184
185 jarray nativez_BufferArray(JNIEnv *env, jobject jbuffer) {
186         return (*env)->CallObjectMethodA(env, jbuffer, Buffer_array_, NULL);
187 }
188
189 jint nativez_BufferArrayOffset(JNIEnv *env, jobject jbuffer) {
190         return (*env)->CallIntMethodA(env, jbuffer, Buffer_arrayOffset_, NULL);
191 }
192
193 jint nativez_BufferPosition(JNIEnv *env, jobject jbuffer) {
194         return (*env)->CallIntMethodA(env, jbuffer, Buffer_position_, NULL);
195 }
196
197 jint nativez_BufferLimit(JNIEnv *env, jobject jbuffer) {
198         return (*env)->CallIntMethodA(env, jbuffer, Buffer_limit_, NULL);
199 }
200
201 void nativez_BufferSetPosition(JNIEnv *env, jobject jbuffer, jint jposition) {
202         jvalue arg = { .i = jposition };
203         (void)(*env)->CallObjectMethodA(env, jbuffer, Buffer_position_i, &arg);
204 }
205                                                                        
206 void nativez_BufferSetLimit(JNIEnv *env, jobject jbuffer, jint jlimit) {
207         jvalue arg = { .i = jlimit };
208         (void)(*env)->CallObjectMethodA(env, jbuffer, Buffer_limit_i, &arg);
209 }
210
211 const char *nativez_GetString(JNIEnv *env, jstring js) {
212         return js ? (*env)->GetStringUTFChars(env, js, NULL) : NULL;
213 }
214
215 void nativez_ReleaseString(JNIEnv *env, jstring js, const char *s) {
216         if (js)
217                 (*env)->ReleaseStringUTFChars(env, js, s);
218 }
219
220 jstring nativez_NewString(JNIEnv *env, const char *s) {
221         return s ? (*env)->NewStringUTF(env, s) : NULL;
222 }
223
224 /* ********************************************************************** */
225
226 int nativez_ResolveReferences(JNIEnv *env, const NZRefTable *table) {
227         jclass jc = NULL;
228         const char *cname = NULL;
229         int failed = 0;
230         
231         for (int i=0;table[i].type;i++) {
232                 switch (table[i].type) {
233                 case 1:
234                         jc = (*env)->FindClass(env, table[i].name);
235                         if (!jc) {
236                                 fprintf(stderr, "Unablet to resolve class `%s'\n", table[i].name);
237                                 fflush(stderr);
238                                 return -1;
239                         }
240                         if (table[i].ptr) {
241                                 jc = (*env)->NewGlobalRef(env, jc);
242                                 *(table[i].ptr) = jc;
243                         }
244                         cname = table[i].name;
245                         break;
246                 case 2: // method
247                         *(table[i].ptr) = (*env)->GetMethodID(env, jc, table[i].name, table[i].signature);
248                         break;
249                 case 3: // static method
250                         *(table[i].ptr) = (*env)->GetStaticMethodID(env, jc, table[i].name, table[i].signature);
251                         break;
252                 case 4: // field
253                         *(table[i].ptr) = (*env)->GetFieldID(env, jc, table[i].name, table[i].signature);
254                         break;
255                 case 5: // static field
256                         *(table[i].ptr) = (*env)->GetStaticFieldID(env, jc, table[i].name, table[i].signature);
257                         break;
258                 }
259
260                 if ((table[i].ptr) && !*(table[i].ptr)) {
261                         failed--;
262                         fprintf(stderr, "Unable to resolve `%s.%s' sig `%s'\n", cname, table[i].name, table[i].signature);
263                         fflush(stderr);
264                 }
265         }
266
267         return failed;
268 }
269
270 /* ********************************************************************** */
271
272 int nativez_NonNull(JNIEnv *env, const char *what, void *ptr) {
273         if (ptr)
274                 return 1;
275
276         nativez_ThrowException(env, "java/lang/NullPointerException", what);
277         return 0;
278 }
279
280 /* ********************************************************************** */
281
282 /**
283  * Retrieve pointer arguments.
284  *
285  * MUST always call ReleasePointers() with the same arguments regardles of return value.
286  */
287 jboolean nativez_GetPointers(JNIEnv *env, const char *desc, nzpointer * __restrict info) {
288         char c;
289         while ((c = *desc++)) {
290                 switch (c) {
291                 case 'P':
292                         if (!info->object)
293                                 goto nullPointer;
294                 case 'p':
295                         if (info->object && !(info->p.v = (*env)->GetPrimitiveArrayCritical(env, info->object, NULL)))
296                                 return 0;
297                         break;
298                 case 'U':
299                         if (!info->object)
300                                 goto nullPointer;
301                 case 'u':
302                         if (info->object && !(info->p.U = (*env)->GetStringUTFChars(env, info->object, NULL)))
303                                 return 0;
304                         break;
305                 case 'N':
306                         if (!info->object)
307                                 goto nullPointer;
308                 case 'n':
309                         if (info->object)
310                                 info->p.N = (void *)(uintptr_t)(*env)->GetLongField(env, info->object, NativeZ_p);
311                         break;
312                 default:
313                         return 0;
314                 }
315                 info++;
316         }
317         return 1;
318
319  nullPointer:
320         nativez_ThrowException(env, "java/lang/NullPointerException", "Argument missing");
321         return 0;
322 }
323
324 /**
325  * Release pointer arguments.
326  */
327 void nativez_ReleasePointers(JNIEnv *env, const char *desc, nzpointer * __restrict info) {
328         char c;
329         while ((c = *desc++)) {
330                 if (info->p.v) {
331                         switch (c) {
332                         case 'P':
333                         case 'p':
334                                 (*env)->ReleasePrimitiveArrayCritical(env, info->object, info->p.v, 0);
335                                 break;
336                         case 'U':
337                         case 'u':
338                                 (*env)->ReleaseStringUTFChars(env, info->object, info->p.U);
339                                 break;
340                         case 'N':
341                         case 'n':
342                                 break;
343                         }
344                 }
345                 info++;
346         }
347 }
348
349 /* ********************************************************************** */
350
351 jobject NativeZ_create(JNIEnv *env, jclass jc, void *p) {
352         if (p) {
353                 jvalue jargs[] = {
354                         { .l = jc },
355                         { .j = (uintptr_t)p }
356                 };
357
358                 return (*env)->CallStaticObjectMethodA(env, NativeZ_classid, NativeZ_create_lj, jargs);
359         } else
360                 return NULL;
361 }
362
363 jobject NativeZ_register(JNIEnv *env, jobject jo) {
364         if (jo) {
365                 jvalue jargs[] = {
366                         { .l = jo }
367                 };
368
369                 return (*env)->CallStaticObjectMethodA(env, NativeZ_classid, NativeZ_register_l, jargs);
370         } else
371                 return NULL;
372 }
373
374 jobject NativeZ_resolve(JNIEnv *env, jclass jc, void *p) {
375         if (p) {
376                 jvalue jargs[] = {
377                         { .l = jc },
378                         { .j = (uintptr_t)p }
379                 };
380
381                 return (*env)->CallStaticObjectMethodA(env, NativeZ_classid, NativeZ_resolve_lj, jargs);
382         } else
383                 return NULL;
384 }
385
386 jobject NativeZ_refer(JNIEnv *env, jclass jc, void *p) {
387         if (p) {
388                 jvalue jargs[] = {
389                         { .l = jc },
390                         { .j = (uintptr_t)p }
391                 };
392
393                 return (*env)->CallStaticObjectMethodA(env, NativeZ_classid, NativeZ_refer_lj, jargs);
394         } else
395                 return NULL;
396 }
397
398 void *NativeZ_getP(JNIEnv *env, jobject jo) {
399         if (jo)
400                 return (void *)(uintptr_t)(*env)->GetLongField(env, jo, NativeZ_p);
401         else
402                 return NULL;
403 }