blob: 08c6ece4de476f2bbaec6e683f7752821e610f25 [file] [log] [blame]
Paul Sokolovskye79c6b62015-09-11 17:57:47 +03001/*
2 * This file is part of the Micro Python project, http://micropython.org/
3 *
4 * The MIT License (MIT)
5 *
6 * Copyright (c) 2015 Paul Sokolovsky
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to deal
10 * in the Software without restriction, including without limitation the rights
11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 * copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 * THE SOFTWARE.
25 */
26
27#include <assert.h>
28#include <string.h>
29#include <errno.h>
30#include <dlfcn.h>
31
32#include "py/nlr.h"
33#include "py/runtime.h"
34#include "py/binary.h"
35
36#include <jni.h>
37
38#define JJ(call, ...) (*env)->call(env, __VA_ARGS__)
Paul Sokolovsky7a4b10c2015-09-14 00:12:27 +030039#define JJ1(call) (*env)->call(env)
Paul Sokolovskye79c6b62015-09-11 17:57:47 +030040
41static JavaVM *jvm;
42static JNIEnv *env;
Paul Sokolovsky1cb5de22015-09-17 13:31:16 +030043static jclass Class_class;
Paul Sokolovskye79c6b62015-09-11 17:57:47 +030044static jclass String_class;
45static jmethodID Class_getField_mid;
46static jmethodID Class_getMethods_mid;
47static jmethodID Class_getConstructors_mid;
48static jmethodID Method_getName_mid;
49static jmethodID Method_toString_mid;
50
Paul Sokolovskyb2d880d2015-09-21 12:02:22 -070051static jclass List_class;
52static jmethodID List_get_mid;
53static jmethodID List_set_mid;
54
Paul Sokolovskye79c6b62015-09-11 17:57:47 +030055STATIC const mp_obj_type_t jobject_type;
56STATIC const mp_obj_type_t jmethod_type;
57
Paul Sokolovsky7a4b10c2015-09-14 00:12:27 +030058STATIC mp_obj_t new_jobject(jobject jo);
Paul Sokolovsky1cb5de22015-09-17 13:31:16 +030059STATIC mp_obj_t new_jclass(jclass jc);
Paul Sokolovskye79c6b62015-09-11 17:57:47 +030060STATIC mp_obj_t call_method(jobject obj, const char *name, jarray methods, bool is_constr, mp_uint_t n_args, const mp_obj_t *args);
61
62typedef struct _mp_obj_jclass_t {
63 mp_obj_base_t base;
64 jclass cls;
65} mp_obj_jclass_t;
66
67typedef struct _mp_obj_jobject_t {
68 mp_obj_base_t base;
69 jobject obj;
70} mp_obj_jobject_t;
71
72typedef struct _mp_obj_jmethod_t {
73 mp_obj_base_t base;
74 jobject obj;
75 jmethodID meth;
76 qstr name;
Paul Sokolovsky7a4b10c2015-09-14 00:12:27 +030077 bool is_static;
Paul Sokolovskye79c6b62015-09-11 17:57:47 +030078} mp_obj_jmethod_t;
79
80// jclass
81
82STATIC void jclass_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
83 (void)kind;
84 mp_obj_jclass_t *self = self_in;
85 // Variable value printed as cast to int
86 mp_printf(print, "<jclass @%p>", self->cls);
87}
88
89STATIC void jclass_attr(mp_obj_t self_in, qstr attr_in, mp_obj_t *dest) {
90 if (dest[0] == MP_OBJ_NULL) {
91 // load attribute
92 mp_obj_jclass_t *self = self_in;
93 const char *attr = qstr_str(attr_in);
94
95 jstring field_name = JJ(NewStringUTF, attr);
96 jobject field = JJ(CallObjectMethod, self->cls, Class_getField_mid, field_name);
Paul Sokolovsky7a4b10c2015-09-14 00:12:27 +030097 if (!JJ1(ExceptionCheck)) {
98 jfieldID field_id = JJ(FromReflectedField, field);
99 jobject obj = JJ(GetStaticObjectField, self->cls, field_id);
100 dest[0] = new_jobject(obj);
101 return;
102 }
103 //JJ1(ExceptionDescribe);
104 JJ1(ExceptionClear);
Paul Sokolovskye79c6b62015-09-11 17:57:47 +0300105
Paul Sokolovsky7a4b10c2015-09-14 00:12:27 +0300106 mp_obj_jmethod_t *o = m_new_obj(mp_obj_jmethod_t);
107 o->base.type = &jmethod_type;
108 o->name = attr_in;
109 o->meth = NULL;
110 o->obj = self->cls;
111 o->is_static = true;
Paul Sokolovskye79c6b62015-09-11 17:57:47 +0300112 dest[0] = o;
113 }
114}
115
116STATIC mp_obj_t jclass_call(mp_obj_t self_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args) {
117 if (n_kw != 0) {
118 nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "kwargs not supported"));
119 }
120 mp_obj_jclass_t *self = self_in;
121
122 jarray methods = JJ(CallObjectMethod, self->cls, Class_getConstructors_mid);
123
124 return call_method(self->cls, NULL, methods, true, n_args, args);
125}
126
127STATIC const mp_map_elem_t jclass_locals_dict_table[] = {
128// { MP_OBJ_NEW_QSTR(MP_QSTR_get), (mp_obj_t)&ffivar_get_obj },
129// { MP_OBJ_NEW_QSTR(MP_QSTR_set), (mp_obj_t)&ffivar_set_obj },
130};
131
132STATIC MP_DEFINE_CONST_DICT(jclass_locals_dict, jclass_locals_dict_table);
133
134STATIC const mp_obj_type_t jclass_type = {
135 { &mp_type_type },
136 .name = MP_QSTR_jclass,
137 .print = jclass_print,
138 .attr = jclass_attr,
139 .call = jclass_call,
140 .locals_dict = (mp_obj_t)&jclass_locals_dict,
141};
142
Paul Sokolovsky1cb5de22015-09-17 13:31:16 +0300143STATIC mp_obj_t new_jclass(jclass jc) {
144 mp_obj_jclass_t *o = m_new_obj(mp_obj_jclass_t);
145 o->base.type = &jclass_type;
146 o->cls = jc;
147 return o;
148}
Paul Sokolovskye79c6b62015-09-11 17:57:47 +0300149
150// jobject
151
152STATIC void jobject_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
153 (void)kind;
154 mp_obj_jobject_t *self = self_in;
155 // Variable value printed as cast to int
156 mp_printf(print, "<jobject @%p>", self->obj);
157}
158
159STATIC void jobject_attr(mp_obj_t self_in, qstr attr_in, mp_obj_t *dest) {
160 if (dest[0] == MP_OBJ_NULL) {
161 // load attribute
162 mp_obj_jobject_t *self = self_in;
163
164 mp_obj_jmethod_t *o = m_new_obj(mp_obj_jmethod_t);
165 o->base.type = &jmethod_type;
166 o->name = attr_in;
167 o->meth = NULL;
168 o->obj = self->obj;
Paul Sokolovsky7a4b10c2015-09-14 00:12:27 +0300169 o->is_static = false;
Paul Sokolovskye79c6b62015-09-11 17:57:47 +0300170 dest[0] = o;
171 }
172}
173
Paul Sokolovskyb2d880d2015-09-21 12:02:22 -0700174STATIC mp_obj_t jobject_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) {
175 mp_obj_jobject_t *self = self_in;
176 if (!JJ(IsInstanceOf, self->obj, List_class)) {
177 return MP_OBJ_NULL;
178 }
179
180 mp_uint_t idx = mp_obj_get_int(index);
181
182 if (value == MP_OBJ_NULL) {
183 // delete
184 assert(0);
185 } else if (value == MP_OBJ_SENTINEL) {
186 // load
187 jobject el = JJ(CallObjectMethod, self->obj, List_get_mid, idx);
188 return new_jobject(el);
189 } else {
190 // store
191 assert(0);
192 }
193
194
195return MP_OBJ_NULL;
196}
197
Paul Sokolovskye79c6b62015-09-11 17:57:47 +0300198STATIC const mp_obj_type_t jobject_type = {
199 { &mp_type_type },
200 .name = MP_QSTR_jobject,
201 .print = jobject_print,
202 .attr = jobject_attr,
Paul Sokolovskyb2d880d2015-09-21 12:02:22 -0700203 .subscr = jobject_subscr,
Paul Sokolovskye79c6b62015-09-11 17:57:47 +0300204// .locals_dict = (mp_obj_t)&jobject_locals_dict,
205};
206
Paul Sokolovsky26a9b4d2015-09-13 01:25:04 +0300207STATIC mp_obj_t new_jobject(jobject jo) {
Paul Sokolovskyed22e9b2015-09-22 17:01:01 -0700208 if (JJ(IsInstanceOf, jo, String_class)) {
209 const char *s = JJ(GetStringUTFChars, jo, NULL);
210 mp_obj_t ret = mp_obj_new_str(s, strlen(s), false);
211 JJ(ReleaseStringUTFChars, jo, s);
212 return ret;
213 } else if (JJ(IsInstanceOf, jo, Class_class)) {
214 return new_jclass(jo);
215 } else {
216 mp_obj_jobject_t *o = m_new_obj(mp_obj_jobject_t);
217 o->base.type = &jobject_type;
218 o->obj = jo;
219 return o;
220 }
221
Paul Sokolovsky26a9b4d2015-09-13 01:25:04 +0300222}
223
224
Paul Sokolovskye79c6b62015-09-11 17:57:47 +0300225// jmethod
226
227STATIC void jmethod_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
228 (void)kind;
229 mp_obj_jmethod_t *self = self_in;
230 // Variable value printed as cast to int
231 mp_printf(print, "<jmethod '%s'>", qstr_str(self->name));
232}
233
Paul Sokolovsky4e7bde82015-09-12 00:19:18 +0300234#define IMATCH(s, static) ((!strncmp(s, static, sizeof(static) - 1)) && (s += sizeof(static) - 1))
Paul Sokolovskye79c6b62015-09-11 17:57:47 +0300235
236#define CHECK_TYPE(java_type_name) \
Paul Sokolovsky4e7bde82015-09-12 00:19:18 +0300237 if (strncmp(arg_type, java_type_name, sizeof(java_type_name) - 1) != 0) { \
238 return false; \
Paul Sokolovskye79c6b62015-09-11 17:57:47 +0300239 } \
Paul Sokolovsky4e7bde82015-09-12 00:19:18 +0300240 arg_type += sizeof(java_type_name) - 1;
Paul Sokolovskye79c6b62015-09-11 17:57:47 +0300241
242STATIC const char *strprev(const char *s, char c) {
243 while (*s != c) {
244 s--;
245 }
246 return s;
247}
248
Paul Sokolovsky4e7bde82015-09-12 00:19:18 +0300249STATIC bool py2jvalue(const char **jtypesig, mp_obj_t arg, jvalue *out) {
250 const char *arg_type = *jtypesig;
251 mp_obj_type_t *type = mp_obj_get_type(arg);
252
253 if (type == &mp_type_str) {
254 if (IMATCH(arg_type, "java.lang.String") || IMATCH(arg_type, "java.lang.Object")) {
255 out->l = JJ(NewStringUTF, mp_obj_str_get_str(arg));
256 } else {
257 return false;
258 }
259 } else if (type == &mp_type_int) {
Paul Sokolovsky011c7f52015-09-18 13:19:50 +0300260 if (IMATCH(arg_type, "int") || IMATCH(arg_type, "long")) {
261 // TODO: Java long is 64-bit actually
262 out->j = mp_obj_get_int(arg);
263 } else {
264 return false;
265 }
Paul Sokolovsky1e9d8e12015-09-19 01:04:41 +0300266 } else if (type == &jobject_type) {
267 printf("TODO: Check java arg type!!\n");
268 mp_obj_jobject_t *jo = arg;
269 out->l = jo->obj;
Paul Sokolovsky4e7bde82015-09-12 00:19:18 +0300270 } else {
271 nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "arg type not supported"));
272 }
273
274 *jtypesig = arg_type;
275 return true;
276}
277
Paul Sokolovsky26a9b4d2015-09-13 01:25:04 +0300278// jvalue is known to be union of jobject and friends. And yet from C's
279// perspective, it's aggregate object which may require passing via stack
280// instead of registers. Work that around by passing jobject and typecasting
281// it.
282#define MATCH(s, static) (!strncmp(s, static, sizeof(static) - 1))
283STATIC mp_obj_t jvalue2py(const char *jtypesig, jobject arg) {
Paul Sokolovsky6196aa42015-09-20 00:36:26 +0300284 const char *org_jtype = jtypesig;
Paul Sokolovsky51673322015-09-14 00:15:08 +0300285 if (arg == NULL || MATCH(jtypesig, "void")) {
Paul Sokolovsky26a9b4d2015-09-13 01:25:04 +0300286 return mp_const_none;
Paul Sokolovsky6196aa42015-09-20 00:36:26 +0300287 } else if (MATCH(jtypesig, "boolean")) {
288 return mp_obj_new_bool((bool)arg);
Paul Sokolovsky26a9b4d2015-09-13 01:25:04 +0300289 } else if (MATCH(jtypesig, "int")) {
290 return mp_obj_new_int((mp_int_t)arg);
Paul Sokolovskyb230a862015-09-15 14:07:12 +0300291 } else {
292 while (*jtypesig != ' ' && *jtypesig) {
293 if (*jtypesig == '.') {
294 // Non-primitive, object type
Paul Sokolovskyed22e9b2015-09-22 17:01:01 -0700295 return new_jobject(arg);
Paul Sokolovskyb230a862015-09-15 14:07:12 +0300296 }
297 jtypesig++;
Paul Sokolovsky26a9b4d2015-09-13 01:25:04 +0300298 }
299 }
300
Paul Sokolovsky6196aa42015-09-20 00:36:26 +0300301 printf("Unknown return type: %s\n", org_jtype);
Paul Sokolovskyb230a862015-09-15 14:07:12 +0300302
Paul Sokolovsky26a9b4d2015-09-13 01:25:04 +0300303 return MP_OBJ_NULL;
304}
305
Paul Sokolovskye79c6b62015-09-11 17:57:47 +0300306STATIC mp_obj_t call_method(jobject obj, const char *name, jarray methods, bool is_constr, mp_uint_t n_args, const mp_obj_t *args) {
307 jvalue jargs[n_args];
308// printf("methods=%p\n", methods);
309 jsize num_methods = JJ(GetArrayLength, methods);
310 for (int i = 0; i < num_methods; i++) {
311 jobject meth = JJ(GetObjectArrayElement, methods, i);
312 jobject name_o = JJ(CallObjectMethod, meth, Method_toString_mid);
313 const char *decl = JJ(GetStringUTFChars, name_o, NULL);
314 const char *arg_types = strchr(decl, '(') + 1;
315 //const char *arg_types_end = strchr(arg_types, ')');
316// printf("method[%d]=%p %s\n", i, meth, decl);
317
318 const char *meth_name = NULL;
319 const char *ret_type = NULL;
320 if (!is_constr) {
321 meth_name = strprev(arg_types, '.') + 1;
322 ret_type = strprev(meth_name, ' ') - 1;
323 ret_type = strprev(ret_type, ' ') + 1;
324
325 int name_len = strlen(name);
326 if (strncmp(name, meth_name, name_len/*arg_types - meth_name - 1*/) || meth_name[name_len] != '('/*(*/) {
327 continue;
328 }
329 }
330// printf("method[%d]=%p %s\n", i, meth, decl);
331// printf("!!!%s\n", arg_types);
332// printf("name=%p meth_name=%s\n", name, meth_name);
333
334 bool found = true;
335 for (int i = 0; i < n_args; i++) {
Paul Sokolovsky4e7bde82015-09-12 00:19:18 +0300336 if (!py2jvalue(&arg_types, args[i], &jargs[i])) {
337 goto next_method;
Paul Sokolovskye79c6b62015-09-11 17:57:47 +0300338 }
339
340 if (*arg_types == ',') {
341 arg_types++;
342 }
343 }
344
345 if (found) {
346// printf("found!\n");
347 jmethodID method_id = JJ(FromReflectedMethod, meth);
348 jobject res;
349 if (is_constr) {
350 res = JJ(NewObjectA, obj, method_id, jargs);
Paul Sokolovsky26a9b4d2015-09-13 01:25:04 +0300351 JJ(ReleaseStringUTFChars, name_o, decl);
352 return new_jobject(res);
Paul Sokolovskye79c6b62015-09-11 17:57:47 +0300353 } else {
354 res = JJ(CallObjectMethodA, obj, method_id, jargs);
Paul Sokolovsky26a9b4d2015-09-13 01:25:04 +0300355 mp_obj_t ret = jvalue2py(ret_type, res);
Paul Sokolovskye79c6b62015-09-11 17:57:47 +0300356 JJ(ReleaseStringUTFChars, name_o, decl);
357 if (ret != MP_OBJ_NULL) {
358 return ret;
359 }
360 nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "cannot handle return type"));
361 }
362 }
363
Paul Sokolovsky4e7bde82015-09-12 00:19:18 +0300364next_method:
Paul Sokolovskye79c6b62015-09-11 17:57:47 +0300365 JJ(ReleaseStringUTFChars, name_o, decl);
366 }
367
368 nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "method not found"));
369}
370
371
372STATIC mp_obj_t jmethod_call(mp_obj_t self_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args) {
373 if (n_kw != 0) {
374 nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "kwargs not supported"));
375 }
376 mp_obj_jmethod_t *self = self_in;
377
378 const char *name = qstr_str(self->name);
379// jstring meth_name = JJ(NewStringUTF, name);
380
Paul Sokolovsky7a4b10c2015-09-14 00:12:27 +0300381 jclass obj_class = self->obj;
382 if (!self->is_static) {
383 obj_class = JJ(GetObjectClass, self->obj);
384 }
Paul Sokolovskye79c6b62015-09-11 17:57:47 +0300385 jarray methods = JJ(CallObjectMethod, obj_class, Class_getMethods_mid);
386
387 return call_method(self->obj, name, methods, false, n_args, args);
388}
389
390STATIC const mp_obj_type_t jmethod_type = {
391 { &mp_type_type },
392 .name = MP_QSTR_jmethod,
393 .print = jmethod_print,
394 .call = jmethod_call,
395// .attr = jobject_attr,
396// .locals_dict = (mp_obj_t)&jobject_locals_dict,
397};
398
399#ifdef __ANDROID__
400#define LIBJVM_SO "libdvm.so"
401#else
402#define LIBJVM_SO "libjvm.so"
403#endif
404
405STATIC void create_jvm() {
406 JavaVMInitArgs args;
407 JavaVMOption options;
408 options.optionString = "-Djava.class.path=.";
409 args.version = JNI_VERSION_1_6;
410 args.nOptions = 1;
411 args.options = &options;
412 args.ignoreUnrecognized = 0;
413
414 if (env) {
415 return;
416 }
417
418 void *libjvm = dlopen(LIBJVM_SO, RTLD_NOW | RTLD_GLOBAL);
419 if (!libjvm) {
420 nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, "unable to load libjvm.so, use LD_LIBRARY_PATH"));
421 }
422 int (*_JNI_CreateJavaVM)(void*, void**, void*) = dlsym(libjvm, "JNI_CreateJavaVM");
423
424 int st = _JNI_CreateJavaVM(&jvm, (void**)&env, &args);
425 if (st < 0 || !env) {
426 nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, "unable to create JVM"));
427 }
428
Paul Sokolovsky1cb5de22015-09-17 13:31:16 +0300429 Class_class = JJ(FindClass, "java/lang/Class");
Paul Sokolovskye79c6b62015-09-11 17:57:47 +0300430 jclass method_class = JJ(FindClass, "java/lang/reflect/Method");
431 String_class = JJ(FindClass, "java/lang/String");
432
Paul Sokolovsky1cb5de22015-09-17 13:31:16 +0300433 Class_getField_mid = (*env)->GetMethodID(env, Class_class, "getField",
Paul Sokolovskye79c6b62015-09-11 17:57:47 +0300434 "(Ljava/lang/String;)Ljava/lang/reflect/Field;");
Paul Sokolovsky1cb5de22015-09-17 13:31:16 +0300435 Class_getMethods_mid = (*env)->GetMethodID(env, Class_class, "getMethods",
Paul Sokolovskye79c6b62015-09-11 17:57:47 +0300436 "()[Ljava/lang/reflect/Method;");
Paul Sokolovsky1cb5de22015-09-17 13:31:16 +0300437 Class_getConstructors_mid = (*env)->GetMethodID(env, Class_class, "getConstructors",
Paul Sokolovskye79c6b62015-09-11 17:57:47 +0300438 "()[Ljava/lang/reflect/Constructor;");
439 Method_getName_mid = (*env)->GetMethodID(env, method_class, "getName",
440 "()Ljava/lang/String;");
441 Method_toString_mid = (*env)->GetMethodID(env, method_class, "toString",
442 "()Ljava/lang/String;");
Paul Sokolovskyb2d880d2015-09-21 12:02:22 -0700443
444 List_class = JJ(FindClass, "java/util/List");
445 List_get_mid = JJ(GetMethodID, List_class, "get",
446 "(I)Ljava/lang/Object;");
447 List_set_mid = JJ(GetMethodID, List_class, "set",
448 "(ILjava/lang/Object;)Ljava/lang/Object;");
Paul Sokolovskye79c6b62015-09-11 17:57:47 +0300449}
450
451STATIC mp_obj_t mod_jni_cls(mp_obj_t cls_name_in) {
452 const char *cls_name = mp_obj_str_get_str(cls_name_in);
453 if (!env) {
454 create_jvm();
455 }
456 jclass cls = JJ(FindClass, cls_name);
457
458 mp_obj_jclass_t *o = m_new_obj(mp_obj_jclass_t);
459 o->base.type = &jclass_type;
460 o->cls = cls;
461 return o;
462}
463MP_DEFINE_CONST_FUN_OBJ_1(mod_jni_cls_obj, mod_jni_cls);
464
Paul Sokolovskycb6cf5e2015-09-16 01:09:39 +0300465STATIC mp_obj_t mod_jni_env() {
466 return mp_obj_new_int((mp_int_t)env);
467}
468MP_DEFINE_CONST_FUN_OBJ_0(mod_jni_env_obj, mod_jni_env);
469
Paul Sokolovskye79c6b62015-09-11 17:57:47 +0300470STATIC const mp_map_elem_t mp_module_jni_globals_table[] = {
471 { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_jni) },
472 { MP_OBJ_NEW_QSTR(MP_QSTR_cls), (mp_obj_t)&mod_jni_cls_obj },
Paul Sokolovskycb6cf5e2015-09-16 01:09:39 +0300473 { MP_OBJ_NEW_QSTR(MP_QSTR_env), (mp_obj_t)&mod_jni_env_obj },
Paul Sokolovskye79c6b62015-09-11 17:57:47 +0300474};
475
476STATIC MP_DEFINE_CONST_DICT(mp_module_jni_globals, mp_module_jni_globals_table);
477
478const mp_obj_module_t mp_module_jni = {
479 .base = { &mp_type_module },
480 .name = MP_QSTR_jni,
481 .globals = (mp_obj_dict_t*)&mp_module_jni_globals,
482};