Added some useful variables to java.make
[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 #include <string.h>
39
40 #include "nativez.h"
41 #include "nativez-jni.h"
42
43 static JavaVM *vm;
44
45 static jobject ByteOrder_nativeOrder; // ByteOrder.nativeOrder()
46
47 static int fail(const char *ctx, const char *what) __attribute__ ((noinline));
48 static int fail(const char *ctx, const char *what) {
49         fprintf(stderr, "%s: %s\n", ctx, what);
50         perror(ctx);
51         fflush(stderr);
52         return -1;
53 }
54
55 jint nativez_OnLoad(JavaVM *vmi, JNIEnv *env) {
56         /* Save VM - required for callbacks from threads */
57         vm = vmi;
58
59         if (nativez_ResolveReferences(env, java_names, &java) != 0)
60                 return -1;
61
62         jclass jc = (*env)->FindClass(env, "java/nio/ByteOrder");
63         if (jc == NULL)
64                 return -1;
65         jmethodID ByteOrder_nativeOrder_ = (*env)->GetStaticMethodID(env, jc, "nativeOrder", "()Ljava/nio/ByteOrder;");
66         ByteOrder_nativeOrder = (*env)->NewGlobalRef(env, (*env)->CallStaticObjectMethodA(env, jc, ByteOrder_nativeOrder_, NULL));
67
68         return 0;
69 }
70
71 void *nativez_AllocAligned(size_t align, size_t size) {
72         void *mem;
73 #ifdef HAVE_ALIGNED_MALLOC
74         mem = _aligned_malloc(size, align);
75         return mem;
76 #else
77         if (posix_memalign(&mem, align, size) == 0)
78                 return mem;
79         return NULL;
80 #endif
81 }
82
83 void nativez_FreeAligned(void *mem) {
84 #ifdef HAVE_ALIGNED_MALLOC
85         _aligned_free(mem);
86 #else
87         free(mem);
88 #endif
89 }
90
91 JNIEnv *nativez_AttachCurrentThread(void) {
92         JNIEnv *env = NULL;
93         
94         (*vm)->AttachCurrentThread(vm, (void **)&env, NULL);
95
96         // one assumes this is null if error return?
97         return env;
98 }
99
100 JNIEnv *nativez_AttachCurrentThreadAsDaemon(void) {
101         JNIEnv *env = NULL;
102         
103         (*vm)->AttachCurrentThreadAsDaemon(vm, (void **)&env, NULL);
104
105         // one assumes this is null if error return?
106         return env;
107 }
108
109 /* ********************************************************************** */
110
111 void nativez_ThrowException(JNIEnv *env, const char *type, const char *msg) {
112         jclass jc = (*env)->FindClass(env, type);
113
114         if (jc)
115                 (*env)->ThrowNew(env, jc, msg);
116         else {
117                 fprintf(stderr, "Unable to throw exception `%s': `%s'\n", type, msg);
118                 fflush(stderr);
119         }
120 }
121
122 void nativez_ThrowOutOfMemoryError(JNIEnv *env, const char *msg) {
123         nativez_ThrowException(env, "java/lang/OutOfMemoryError", msg);
124 }
125
126 /* ********************************************************************** */
127
128 jobject nativez_NewDirectBuffer(JNIEnv *env, void *data, jlong size) {
129         jobject jo;
130
131         jo = (*env)->NewDirectByteBuffer(env, data, size);
132         if (jo) {
133                 jvalue arg = { .l = ByteOrder_nativeOrder };
134
135                 (*env)->CallObjectMethodA(env, jo, ByteBuffer_order_l, &arg);
136         }
137
138         return jo;
139 }
140
141 jboolean nativez_BufferHasArray(JNIEnv *env, jobject jbuffer) {
142         return (*env)->CallBooleanMethodA(env, jbuffer, Buffer_hasArray_, NULL);
143 }
144
145 jboolean nativez_BufferIsDirect(JNIEnv *env, jobject jbuffer) {
146         return (*env)->CallBooleanMethodA(env, jbuffer, Buffer_isDirect_, NULL);
147 }
148
149 jarray nativez_BufferArray(JNIEnv *env, jobject jbuffer) {
150         return (*env)->CallObjectMethodA(env, jbuffer, Buffer_array_, NULL);
151 }
152
153 jint nativez_BufferArrayOffset(JNIEnv *env, jobject jbuffer) {
154         return (*env)->CallIntMethodA(env, jbuffer, Buffer_arrayOffset_, NULL);
155 }
156
157 jint nativez_BufferPosition(JNIEnv *env, jobject jbuffer) {
158         return (*env)->CallIntMethodA(env, jbuffer, Buffer_position_, NULL);
159 }
160
161 jint nativez_BufferLimit(JNIEnv *env, jobject jbuffer) {
162         return (*env)->CallIntMethodA(env, jbuffer, Buffer_limit_, NULL);
163 }
164
165 void nativez_BufferSetPosition(JNIEnv *env, jobject jbuffer, jint jposition) {
166         jvalue arg = { .i = jposition };
167         (void)(*env)->CallObjectMethodA(env, jbuffer, Buffer_position_i, &arg);
168 }
169                                                                        
170 void nativez_BufferSetLimit(JNIEnv *env, jobject jbuffer, jint jlimit) {
171         jvalue arg = { .i = jlimit };
172         (void)(*env)->CallObjectMethodA(env, jbuffer, Buffer_limit_i, &arg);
173 }
174
175 const char *nativez_GetString(JNIEnv *env, jstring js) {
176         return js ? (*env)->GetStringUTFChars(env, js, NULL) : NULL;
177 }
178
179 void nativez_ReleaseString(JNIEnv *env, jstring js, const char *s) {
180         if (js)
181                 (*env)->ReleaseStringUTFChars(env, js, s);
182 }
183
184 jstring nativez_NewString(JNIEnv *env, const char *s) {
185         return s ? (*env)->NewStringUTF(env, s) : NULL;
186 }
187
188 /* ********************************************************************** */
189
190 int nativez_ResolveReferences(JNIEnv *env, const char *jn_names, void *jnp) {
191         jclass jc = NULL;
192
193         const char *name = jn_names;
194         int index = 0;
195         void **jn = jnp;
196         const char *cname = "?";
197
198         while (*name) {
199                 const char *next = name + strlen(name) + 1;
200
201                 switch (*name) {
202                 case '#': // class
203                         jc = (*env)->FindClass(env, name + 1);
204                         jn[index] = jc = (*env)->NewGlobalRef(env, jc);
205                         cname = name + 1;
206                         break;
207                 case ',': // field
208                         jn[index] = (*env)->GetFieldID(env, jc, name+1, next);
209                         break;
210                 case ';': // static field
211                         jn[index] = (*env)->GetStaticFieldID(env, jc, name+1, next);
212                         break;
213                 case '.': // method
214                         jn[index] = (*env)->GetMethodID(env, jc, name+1, next);
215                         break;
216                 case ':': // static method
217                         jn[index] = (*env)->GetStaticMethodID(env, jc, name+1, next);
218                         break;
219                 default:
220                         return fail("Invalid table", name);
221                 }
222
223                 if (!jn[index])
224                         return fail(cname, name+1);
225
226                 if (*name != '#')
227                         next = next + strlen(next) + 1;
228                 name = next;
229                 index += 1;
230         }
231
232         return 0;
233 }
234
235 /* ********************************************************************** */
236
237 int nativez_NonNull(JNIEnv *env, const char *what, void *ptr) {
238         if (ptr)
239                 return 1;
240
241         nativez_ThrowException(env, "java/lang/NullPointerException", what);
242         return 0;
243 }
244
245 /* ********************************************************************** */
246
247 /**
248  * Retrieve pointer arguments.
249  *
250  * MUST always call ReleasePointers() with the same arguments regardles of return value.
251  */
252 jboolean nativez_GetPointers(JNIEnv *env, const char *desc, nzpointer * __restrict info) {
253         char c;
254         while ((c = *desc++)) {
255                 switch (c) {
256                 case 'P':
257                         if (!info->object)
258                                 goto nullPointer;
259                 case 'p':
260                         if (info->object && !(info->p.v = (*env)->GetPrimitiveArrayCritical(env, info->object, NULL)))
261                                 return 0;
262                         break;
263                 case 'U':
264                         if (!info->object)
265                                 goto nullPointer;
266                 case 'u':
267                         if (info->object && !(info->p.U = (*env)->GetStringUTFChars(env, info->object, NULL)))
268                                 return 0;
269                         break;
270                 case 'N':
271                         if (!info->object)
272                                 goto nullPointer;
273                 case 'n':
274                         if (info->object)
275                                 info->p.N = (void *)(uintptr_t)(*env)->GetLongField(env, info->object, NativeZ_p);
276                         break;
277                 default:
278                         return 0;
279                 }
280                 info++;
281         }
282         return 1;
283
284  nullPointer:
285         nativez_ThrowException(env, "java/lang/NullPointerException", "Argument missing");
286         return 0;
287 }
288
289 /**
290  * Release pointer arguments.
291  */
292 void nativez_ReleasePointers(JNIEnv *env, const char *desc, nzpointer * __restrict info) {
293         char c;
294         while ((c = *desc++)) {
295                 if (info->p.v) {
296                         switch (c) {
297                         case 'P':
298                         case 'p':
299                                 (*env)->ReleasePrimitiveArrayCritical(env, info->object, info->p.v, 0);
300                                 break;
301                         case 'U':
302                         case 'u':
303                                 (*env)->ReleaseStringUTFChars(env, info->object, info->p.U);
304                                 break;
305                         case 'N':
306                         case 'n':
307                                 break;
308                         }
309                 }
310                 info++;
311         }
312 }
313
314 /* ********************************************************************** */
315
316 jobject NativeZ_create(JNIEnv *env, jclass jc, void *p) {
317         if (p) {
318                 jvalue jargs[] = {
319                         { .l = jc },
320                         { .j = (uintptr_t)p }
321                 };
322
323                 return (*env)->CallStaticObjectMethodA(env, NativeZ_classid, NativeZ_create_lj, jargs);
324         } else
325                 return NULL;
326 }
327
328 jobject NativeZ_register(JNIEnv *env, jobject jo) {
329         if (jo) {
330                 jvalue jargs[] = {
331                         { .l = jo }
332                 };
333
334                 return (*env)->CallStaticObjectMethodA(env, NativeZ_classid, NativeZ_register_l, jargs);
335         } else
336                 return NULL;
337 }
338
339 jobject NativeZ_resolve(JNIEnv *env, jclass jc, void *p) {
340         if (p) {
341                 jvalue jargs[] = {
342                         { .l = jc },
343                         { .j = (uintptr_t)p }
344                 };
345
346                 return (*env)->CallStaticObjectMethodA(env, NativeZ_classid, NativeZ_resolve_lj, jargs);
347         } else
348                 return NULL;
349 }
350
351 jobject NativeZ_refer(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_refer_lj, jargs);
359         } else
360                 return NULL;
361 }
362
363 void *NativeZ_getP(JNIEnv *env, jobject jo) {
364         if (jo)
365                 return (void *)(uintptr_t)(*env)->GetLongField(env, jo, NativeZ_p);
366         else
367                 return NULL;
368 }