Paul Sokolovsky | e79c6b6 | 2015-09-11 17:57:47 +0300 | [diff] [blame] | 1 | /* |
| 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> |
Paul Sokolovsky | 0eba162 | 2015-09-30 00:54:20 -0700 | [diff] [blame] | 31 | #include <ctype.h> |
Paul Sokolovsky | e79c6b6 | 2015-09-11 17:57:47 +0300 | [diff] [blame] | 32 | |
| 33 | #include "py/nlr.h" |
Paul Sokolovsky | f3ca862 | 2015-09-29 10:05:30 -0700 | [diff] [blame] | 34 | #include "py/runtime0.h" |
Paul Sokolovsky | e79c6b6 | 2015-09-11 17:57:47 +0300 | [diff] [blame] | 35 | #include "py/runtime.h" |
| 36 | #include "py/binary.h" |
| 37 | |
| 38 | #include <jni.h> |
| 39 | |
| 40 | #define JJ(call, ...) (*env)->call(env, __VA_ARGS__) |
Paul Sokolovsky | 7a4b10c | 2015-09-14 00:12:27 +0300 | [diff] [blame] | 41 | #define JJ1(call) (*env)->call(env) |
Paul Sokolovsky | 02041bf | 2015-10-09 00:25:26 +0300 | [diff] [blame] | 42 | #define MATCH(s, static) (!strncmp(s, static, sizeof(static) - 1)) |
Paul Sokolovsky | e79c6b6 | 2015-09-11 17:57:47 +0300 | [diff] [blame] | 43 | |
| 44 | static JavaVM *jvm; |
| 45 | static JNIEnv *env; |
Paul Sokolovsky | 1cb5de2 | 2015-09-17 13:31:16 +0300 | [diff] [blame] | 46 | static jclass Class_class; |
Paul Sokolovsky | e79c6b6 | 2015-09-11 17:57:47 +0300 | [diff] [blame] | 47 | static jclass String_class; |
| 48 | static jmethodID Class_getField_mid; |
| 49 | static jmethodID Class_getMethods_mid; |
| 50 | static jmethodID Class_getConstructors_mid; |
| 51 | static jmethodID Method_getName_mid; |
Paul Sokolovsky | f22be4e | 2015-10-03 08:43:41 -0700 | [diff] [blame] | 52 | static jmethodID Object_toString_mid; |
Paul Sokolovsky | e79c6b6 | 2015-09-11 17:57:47 +0300 | [diff] [blame] | 53 | |
Paul Sokolovsky | b2d880d | 2015-09-21 12:02:22 -0700 | [diff] [blame] | 54 | static jclass List_class; |
| 55 | static jmethodID List_get_mid; |
| 56 | static jmethodID List_set_mid; |
Paul Sokolovsky | f3ca862 | 2015-09-29 10:05:30 -0700 | [diff] [blame] | 57 | static jmethodID List_size_mid; |
Paul Sokolovsky | b2d880d | 2015-09-21 12:02:22 -0700 | [diff] [blame] | 58 | |
Paul Sokolovsky | 9e0a3d4 | 2015-10-02 00:21:07 -0700 | [diff] [blame] | 59 | static jclass IndexException_class; |
| 60 | |
Paul Sokolovsky | e79c6b6 | 2015-09-11 17:57:47 +0300 | [diff] [blame] | 61 | STATIC const mp_obj_type_t jobject_type; |
| 62 | STATIC const mp_obj_type_t jmethod_type; |
| 63 | |
Paul Sokolovsky | 7a4b10c | 2015-09-14 00:12:27 +0300 | [diff] [blame] | 64 | STATIC mp_obj_t new_jobject(jobject jo); |
Paul Sokolovsky | 1cb5de2 | 2015-09-17 13:31:16 +0300 | [diff] [blame] | 65 | STATIC mp_obj_t new_jclass(jclass jc); |
Paul Sokolovsky | e79c6b6 | 2015-09-11 17:57:47 +0300 | [diff] [blame] | 66 | STATIC 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); |
| 67 | |
| 68 | typedef struct _mp_obj_jclass_t { |
| 69 | mp_obj_base_t base; |
| 70 | jclass cls; |
| 71 | } mp_obj_jclass_t; |
| 72 | |
| 73 | typedef struct _mp_obj_jobject_t { |
| 74 | mp_obj_base_t base; |
| 75 | jobject obj; |
| 76 | } mp_obj_jobject_t; |
| 77 | |
| 78 | typedef struct _mp_obj_jmethod_t { |
| 79 | mp_obj_base_t base; |
| 80 | jobject obj; |
| 81 | jmethodID meth; |
| 82 | qstr name; |
Paul Sokolovsky | 7a4b10c | 2015-09-14 00:12:27 +0300 | [diff] [blame] | 83 | bool is_static; |
Paul Sokolovsky | e79c6b6 | 2015-09-11 17:57:47 +0300 | [diff] [blame] | 84 | } mp_obj_jmethod_t; |
| 85 | |
Paul Sokolovsky | e632b1f | 2015-09-23 07:10:55 -0700 | [diff] [blame] | 86 | // Utility functions |
| 87 | |
| 88 | STATIC bool is_object_type(const char *jtypesig) { |
| 89 | while (*jtypesig != ' ' && *jtypesig) { |
| 90 | if (*jtypesig == '.') { |
| 91 | return true; |
| 92 | } |
| 93 | jtypesig++; |
| 94 | } |
| 95 | return false; |
| 96 | } |
| 97 | |
Paul Sokolovsky | c4489a0 | 2015-10-01 01:19:33 -0700 | [diff] [blame] | 98 | STATIC void check_exception(void) { |
| 99 | jobject exc = JJ1(ExceptionOccurred); |
| 100 | if (exc) { |
| 101 | //JJ1(ExceptionDescribe); |
| 102 | mp_obj_t py_e = new_jobject(exc); |
| 103 | JJ1(ExceptionClear); |
Paul Sokolovsky | 9e0a3d4 | 2015-10-02 00:21:07 -0700 | [diff] [blame] | 104 | if (JJ(IsInstanceOf, exc, IndexException_class)) { |
| 105 | nlr_raise(mp_obj_new_exception_arg1(&mp_type_IndexError, py_e)); |
| 106 | } |
Paul Sokolovsky | c4489a0 | 2015-10-01 01:19:33 -0700 | [diff] [blame] | 107 | nlr_raise(mp_obj_new_exception_arg1(&mp_type_Exception, py_e)); |
| 108 | } |
| 109 | } |
| 110 | |
Paul Sokolovsky | 1ea4b77 | 2015-10-04 01:56:40 +0300 | [diff] [blame] | 111 | STATIC void print_jobject(const mp_print_t *print, jobject obj) { |
| 112 | jobject str_o = JJ(CallObjectMethod, obj, Object_toString_mid); |
| 113 | const char *str = JJ(GetStringUTFChars, str_o, NULL); |
| 114 | mp_printf(print, str); |
| 115 | JJ(ReleaseStringUTFChars, str_o, str); |
| 116 | } |
| 117 | |
Paul Sokolovsky | e79c6b6 | 2015-09-11 17:57:47 +0300 | [diff] [blame] | 118 | // jclass |
| 119 | |
| 120 | STATIC void jclass_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { |
Paul Sokolovsky | e79c6b6 | 2015-09-11 17:57:47 +0300 | [diff] [blame] | 121 | mp_obj_jclass_t *self = self_in; |
Paul Sokolovsky | 1ea4b77 | 2015-10-04 01:56:40 +0300 | [diff] [blame] | 122 | if (kind == PRINT_REPR) { |
| 123 | mp_printf(print, "<jclass @%p \"", self->cls); |
| 124 | } |
| 125 | print_jobject(print, self->cls); |
| 126 | if (kind == PRINT_REPR) { |
| 127 | mp_printf(print, "\">"); |
| 128 | } |
Paul Sokolovsky | e79c6b6 | 2015-09-11 17:57:47 +0300 | [diff] [blame] | 129 | } |
| 130 | |
| 131 | STATIC void jclass_attr(mp_obj_t self_in, qstr attr_in, mp_obj_t *dest) { |
| 132 | if (dest[0] == MP_OBJ_NULL) { |
| 133 | // load attribute |
| 134 | mp_obj_jclass_t *self = self_in; |
| 135 | const char *attr = qstr_str(attr_in); |
| 136 | |
| 137 | jstring field_name = JJ(NewStringUTF, attr); |
| 138 | jobject field = JJ(CallObjectMethod, self->cls, Class_getField_mid, field_name); |
Paul Sokolovsky | 7a4b10c | 2015-09-14 00:12:27 +0300 | [diff] [blame] | 139 | if (!JJ1(ExceptionCheck)) { |
| 140 | jfieldID field_id = JJ(FromReflectedField, field); |
| 141 | jobject obj = JJ(GetStaticObjectField, self->cls, field_id); |
| 142 | dest[0] = new_jobject(obj); |
| 143 | return; |
| 144 | } |
| 145 | //JJ1(ExceptionDescribe); |
| 146 | JJ1(ExceptionClear); |
Paul Sokolovsky | e79c6b6 | 2015-09-11 17:57:47 +0300 | [diff] [blame] | 147 | |
Paul Sokolovsky | 7a4b10c | 2015-09-14 00:12:27 +0300 | [diff] [blame] | 148 | mp_obj_jmethod_t *o = m_new_obj(mp_obj_jmethod_t); |
| 149 | o->base.type = &jmethod_type; |
| 150 | o->name = attr_in; |
| 151 | o->meth = NULL; |
| 152 | o->obj = self->cls; |
| 153 | o->is_static = true; |
Paul Sokolovsky | e79c6b6 | 2015-09-11 17:57:47 +0300 | [diff] [blame] | 154 | dest[0] = o; |
| 155 | } |
| 156 | } |
| 157 | |
| 158 | STATIC 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) { |
| 159 | if (n_kw != 0) { |
| 160 | nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "kwargs not supported")); |
| 161 | } |
| 162 | mp_obj_jclass_t *self = self_in; |
| 163 | |
| 164 | jarray methods = JJ(CallObjectMethod, self->cls, Class_getConstructors_mid); |
| 165 | |
| 166 | return call_method(self->cls, NULL, methods, true, n_args, args); |
| 167 | } |
| 168 | |
| 169 | STATIC const mp_map_elem_t jclass_locals_dict_table[] = { |
| 170 | // { MP_OBJ_NEW_QSTR(MP_QSTR_get), (mp_obj_t)&ffivar_get_obj }, |
| 171 | // { MP_OBJ_NEW_QSTR(MP_QSTR_set), (mp_obj_t)&ffivar_set_obj }, |
| 172 | }; |
| 173 | |
| 174 | STATIC MP_DEFINE_CONST_DICT(jclass_locals_dict, jclass_locals_dict_table); |
| 175 | |
| 176 | STATIC const mp_obj_type_t jclass_type = { |
| 177 | { &mp_type_type }, |
| 178 | .name = MP_QSTR_jclass, |
| 179 | .print = jclass_print, |
| 180 | .attr = jclass_attr, |
| 181 | .call = jclass_call, |
| 182 | .locals_dict = (mp_obj_t)&jclass_locals_dict, |
| 183 | }; |
| 184 | |
Paul Sokolovsky | 1cb5de2 | 2015-09-17 13:31:16 +0300 | [diff] [blame] | 185 | STATIC mp_obj_t new_jclass(jclass jc) { |
| 186 | mp_obj_jclass_t *o = m_new_obj(mp_obj_jclass_t); |
| 187 | o->base.type = &jclass_type; |
| 188 | o->cls = jc; |
| 189 | return o; |
| 190 | } |
Paul Sokolovsky | e79c6b6 | 2015-09-11 17:57:47 +0300 | [diff] [blame] | 191 | |
| 192 | // jobject |
| 193 | |
| 194 | STATIC void jobject_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { |
Paul Sokolovsky | e79c6b6 | 2015-09-11 17:57:47 +0300 | [diff] [blame] | 195 | mp_obj_jobject_t *self = self_in; |
Paul Sokolovsky | f22be4e | 2015-10-03 08:43:41 -0700 | [diff] [blame] | 196 | if (kind == PRINT_REPR) { |
| 197 | mp_printf(print, "<jobject @%p \"", self->obj); |
| 198 | } |
Paul Sokolovsky | 1ea4b77 | 2015-10-04 01:56:40 +0300 | [diff] [blame] | 199 | print_jobject(print, self->obj); |
Paul Sokolovsky | f22be4e | 2015-10-03 08:43:41 -0700 | [diff] [blame] | 200 | if (kind == PRINT_REPR) { |
| 201 | mp_printf(print, "\">"); |
| 202 | } |
Paul Sokolovsky | e79c6b6 | 2015-09-11 17:57:47 +0300 | [diff] [blame] | 203 | } |
| 204 | |
| 205 | STATIC void jobject_attr(mp_obj_t self_in, qstr attr_in, mp_obj_t *dest) { |
| 206 | if (dest[0] == MP_OBJ_NULL) { |
| 207 | // load attribute |
| 208 | mp_obj_jobject_t *self = self_in; |
| 209 | |
Paul Sokolovsky | 216b6a4 | 2015-10-08 16:55:59 +0300 | [diff] [blame] | 210 | const char *attr = qstr_str(attr_in); |
| 211 | jclass obj_class = JJ(GetObjectClass, self->obj); |
| 212 | jstring field_name = JJ(NewStringUTF, attr); |
| 213 | jobject field = JJ(CallObjectMethod, obj_class, Class_getField_mid, field_name); |
| 214 | JJ(DeleteLocalRef, field_name); |
| 215 | JJ(DeleteLocalRef, obj_class); |
| 216 | if (!JJ1(ExceptionCheck)) { |
| 217 | jfieldID field_id = JJ(FromReflectedField, field); |
| 218 | JJ(DeleteLocalRef, field); |
| 219 | jobject obj = JJ(GetObjectField, self->obj, field_id); |
| 220 | dest[0] = new_jobject(obj); |
| 221 | return; |
| 222 | } |
| 223 | //JJ1(ExceptionDescribe); |
| 224 | JJ1(ExceptionClear); |
| 225 | |
Paul Sokolovsky | e79c6b6 | 2015-09-11 17:57:47 +0300 | [diff] [blame] | 226 | mp_obj_jmethod_t *o = m_new_obj(mp_obj_jmethod_t); |
| 227 | o->base.type = &jmethod_type; |
| 228 | o->name = attr_in; |
| 229 | o->meth = NULL; |
| 230 | o->obj = self->obj; |
Paul Sokolovsky | 7a4b10c | 2015-09-14 00:12:27 +0300 | [diff] [blame] | 231 | o->is_static = false; |
Paul Sokolovsky | e79c6b6 | 2015-09-11 17:57:47 +0300 | [diff] [blame] | 232 | dest[0] = o; |
| 233 | } |
| 234 | } |
| 235 | |
Paul Sokolovsky | b2d880d | 2015-09-21 12:02:22 -0700 | [diff] [blame] | 236 | STATIC mp_obj_t jobject_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { |
| 237 | mp_obj_jobject_t *self = self_in; |
| 238 | if (!JJ(IsInstanceOf, self->obj, List_class)) { |
| 239 | return MP_OBJ_NULL; |
| 240 | } |
| 241 | |
| 242 | mp_uint_t idx = mp_obj_get_int(index); |
| 243 | |
| 244 | if (value == MP_OBJ_NULL) { |
| 245 | // delete |
| 246 | assert(0); |
| 247 | } else if (value == MP_OBJ_SENTINEL) { |
| 248 | // load |
| 249 | jobject el = JJ(CallObjectMethod, self->obj, List_get_mid, idx); |
Paul Sokolovsky | fd38799 | 2015-10-07 07:39:41 +0300 | [diff] [blame] | 250 | check_exception(); |
Paul Sokolovsky | b2d880d | 2015-09-21 12:02:22 -0700 | [diff] [blame] | 251 | return new_jobject(el); |
| 252 | } else { |
| 253 | // store |
| 254 | assert(0); |
| 255 | } |
| 256 | |
| 257 | |
| 258 | return MP_OBJ_NULL; |
| 259 | } |
| 260 | |
Paul Sokolovsky | f3ca862 | 2015-09-29 10:05:30 -0700 | [diff] [blame] | 261 | STATIC mp_obj_t jobject_unary_op(mp_uint_t op, mp_obj_t self_in) { |
| 262 | mp_obj_jobject_t *self = self_in; |
| 263 | switch (op) { |
| 264 | case MP_UNARY_OP_BOOL: |
| 265 | case MP_UNARY_OP_LEN: { |
| 266 | jint len = JJ(CallIntMethod, self->obj, List_size_mid); |
| 267 | if (op == MP_UNARY_OP_BOOL) { |
Paul Sokolovsky | 1b586f3 | 2015-10-11 12:09:43 +0300 | [diff] [blame] | 268 | return mp_obj_new_bool(len != 0); |
Paul Sokolovsky | f3ca862 | 2015-09-29 10:05:30 -0700 | [diff] [blame] | 269 | } |
| 270 | return MP_OBJ_NEW_SMALL_INT(len); |
| 271 | } |
| 272 | default: |
| 273 | return MP_OBJ_NULL; // op not supported |
| 274 | } |
| 275 | } |
| 276 | |
Paul Sokolovsky | e79c6b6 | 2015-09-11 17:57:47 +0300 | [diff] [blame] | 277 | STATIC const mp_obj_type_t jobject_type = { |
| 278 | { &mp_type_type }, |
| 279 | .name = MP_QSTR_jobject, |
| 280 | .print = jobject_print, |
Paul Sokolovsky | f3ca862 | 2015-09-29 10:05:30 -0700 | [diff] [blame] | 281 | .unary_op = jobject_unary_op, |
Paul Sokolovsky | e79c6b6 | 2015-09-11 17:57:47 +0300 | [diff] [blame] | 282 | .attr = jobject_attr, |
Paul Sokolovsky | b2d880d | 2015-09-21 12:02:22 -0700 | [diff] [blame] | 283 | .subscr = jobject_subscr, |
Paul Sokolovsky | e79c6b6 | 2015-09-11 17:57:47 +0300 | [diff] [blame] | 284 | // .locals_dict = (mp_obj_t)&jobject_locals_dict, |
| 285 | }; |
| 286 | |
Paul Sokolovsky | 26a9b4d | 2015-09-13 01:25:04 +0300 | [diff] [blame] | 287 | STATIC mp_obj_t new_jobject(jobject jo) { |
Paul Sokolovsky | 7e18d3b | 2015-09-24 15:29:22 -0700 | [diff] [blame] | 288 | if (jo == NULL) { |
| 289 | return mp_const_none; |
| 290 | } else if (JJ(IsInstanceOf, jo, String_class)) { |
Paul Sokolovsky | ed22e9b | 2015-09-22 17:01:01 -0700 | [diff] [blame] | 291 | const char *s = JJ(GetStringUTFChars, jo, NULL); |
| 292 | mp_obj_t ret = mp_obj_new_str(s, strlen(s), false); |
| 293 | JJ(ReleaseStringUTFChars, jo, s); |
| 294 | return ret; |
| 295 | } else if (JJ(IsInstanceOf, jo, Class_class)) { |
| 296 | return new_jclass(jo); |
| 297 | } else { |
| 298 | mp_obj_jobject_t *o = m_new_obj(mp_obj_jobject_t); |
| 299 | o->base.type = &jobject_type; |
| 300 | o->obj = jo; |
| 301 | return o; |
| 302 | } |
| 303 | |
Paul Sokolovsky | 26a9b4d | 2015-09-13 01:25:04 +0300 | [diff] [blame] | 304 | } |
| 305 | |
| 306 | |
Paul Sokolovsky | e79c6b6 | 2015-09-11 17:57:47 +0300 | [diff] [blame] | 307 | // jmethod |
| 308 | |
| 309 | STATIC void jmethod_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { |
| 310 | (void)kind; |
| 311 | mp_obj_jmethod_t *self = self_in; |
| 312 | // Variable value printed as cast to int |
| 313 | mp_printf(print, "<jmethod '%s'>", qstr_str(self->name)); |
| 314 | } |
| 315 | |
Paul Sokolovsky | 4e7bde8 | 2015-09-12 00:19:18 +0300 | [diff] [blame] | 316 | #define IMATCH(s, static) ((!strncmp(s, static, sizeof(static) - 1)) && (s += sizeof(static) - 1)) |
Paul Sokolovsky | e79c6b6 | 2015-09-11 17:57:47 +0300 | [diff] [blame] | 317 | |
| 318 | #define CHECK_TYPE(java_type_name) \ |
Paul Sokolovsky | 4e7bde8 | 2015-09-12 00:19:18 +0300 | [diff] [blame] | 319 | if (strncmp(arg_type, java_type_name, sizeof(java_type_name) - 1) != 0) { \ |
| 320 | return false; \ |
Paul Sokolovsky | e79c6b6 | 2015-09-11 17:57:47 +0300 | [diff] [blame] | 321 | } \ |
Paul Sokolovsky | 4e7bde8 | 2015-09-12 00:19:18 +0300 | [diff] [blame] | 322 | arg_type += sizeof(java_type_name) - 1; |
Paul Sokolovsky | e79c6b6 | 2015-09-11 17:57:47 +0300 | [diff] [blame] | 323 | |
| 324 | STATIC const char *strprev(const char *s, char c) { |
| 325 | while (*s != c) { |
| 326 | s--; |
| 327 | } |
| 328 | return s; |
| 329 | } |
| 330 | |
Paul Sokolovsky | 4e7bde8 | 2015-09-12 00:19:18 +0300 | [diff] [blame] | 331 | STATIC bool py2jvalue(const char **jtypesig, mp_obj_t arg, jvalue *out) { |
| 332 | const char *arg_type = *jtypesig; |
| 333 | mp_obj_type_t *type = mp_obj_get_type(arg); |
| 334 | |
| 335 | if (type == &mp_type_str) { |
| 336 | if (IMATCH(arg_type, "java.lang.String") || IMATCH(arg_type, "java.lang.Object")) { |
| 337 | out->l = JJ(NewStringUTF, mp_obj_str_get_str(arg)); |
| 338 | } else { |
| 339 | return false; |
| 340 | } |
| 341 | } else if (type == &mp_type_int) { |
Paul Sokolovsky | 011c7f5 | 2015-09-18 13:19:50 +0300 | [diff] [blame] | 342 | if (IMATCH(arg_type, "int") || IMATCH(arg_type, "long")) { |
| 343 | // TODO: Java long is 64-bit actually |
| 344 | out->j = mp_obj_get_int(arg); |
| 345 | } else { |
| 346 | return false; |
| 347 | } |
Paul Sokolovsky | 1e9d8e1 | 2015-09-19 01:04:41 +0300 | [diff] [blame] | 348 | } else if (type == &jobject_type) { |
| 349 | printf("TODO: Check java arg type!!\n"); |
Paul Sokolovsky | 0eba162 | 2015-09-30 00:54:20 -0700 | [diff] [blame] | 350 | while (isalpha(*arg_type) || *arg_type == '.') { |
| 351 | arg_type++; |
| 352 | } |
Paul Sokolovsky | 1e9d8e1 | 2015-09-19 01:04:41 +0300 | [diff] [blame] | 353 | mp_obj_jobject_t *jo = arg; |
| 354 | out->l = jo->obj; |
Paul Sokolovsky | 7381b7a | 2015-10-10 01:19:28 +0300 | [diff] [blame] | 355 | } else if (type == &mp_type_bool) { |
| 356 | if (IMATCH(arg_type, "boolean")) { |
| 357 | out->z = arg == mp_const_true; |
| 358 | } else { |
| 359 | return false; |
| 360 | } |
| 361 | } else if (arg == mp_const_none) { |
| 362 | //printf("TODO: Check java arg type!!\n"); |
| 363 | while (isalpha(*arg_type) || *arg_type == '.') { |
| 364 | arg_type++; |
| 365 | } |
| 366 | out->l = NULL; |
Paul Sokolovsky | 4e7bde8 | 2015-09-12 00:19:18 +0300 | [diff] [blame] | 367 | } else { |
| 368 | nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "arg type not supported")); |
| 369 | } |
| 370 | |
| 371 | *jtypesig = arg_type; |
| 372 | return true; |
| 373 | } |
| 374 | |
Paul Sokolovsky | 02041bf | 2015-10-09 00:25:26 +0300 | [diff] [blame] | 375 | #if 0 |
Paul Sokolovsky | 26a9b4d | 2015-09-13 01:25:04 +0300 | [diff] [blame] | 376 | // jvalue is known to be union of jobject and friends. And yet from C's |
| 377 | // perspective, it's aggregate object which may require passing via stack |
| 378 | // instead of registers. Work that around by passing jobject and typecasting |
| 379 | // it. |
Paul Sokolovsky | 26a9b4d | 2015-09-13 01:25:04 +0300 | [diff] [blame] | 380 | STATIC mp_obj_t jvalue2py(const char *jtypesig, jobject arg) { |
Paul Sokolovsky | 5167332 | 2015-09-14 00:15:08 +0300 | [diff] [blame] | 381 | if (arg == NULL || MATCH(jtypesig, "void")) { |
Paul Sokolovsky | 26a9b4d | 2015-09-13 01:25:04 +0300 | [diff] [blame] | 382 | return mp_const_none; |
Paul Sokolovsky | 6196aa4 | 2015-09-20 00:36:26 +0300 | [diff] [blame] | 383 | } else if (MATCH(jtypesig, "boolean")) { |
| 384 | return mp_obj_new_bool((bool)arg); |
Paul Sokolovsky | 26a9b4d | 2015-09-13 01:25:04 +0300 | [diff] [blame] | 385 | } else if (MATCH(jtypesig, "int")) { |
| 386 | return mp_obj_new_int((mp_int_t)arg); |
Paul Sokolovsky | e632b1f | 2015-09-23 07:10:55 -0700 | [diff] [blame] | 387 | } else if (is_object_type(jtypesig)) { |
| 388 | // Non-primitive, object type |
| 389 | return new_jobject(arg); |
Paul Sokolovsky | 26a9b4d | 2015-09-13 01:25:04 +0300 | [diff] [blame] | 390 | } |
| 391 | |
Paul Sokolovsky | e632b1f | 2015-09-23 07:10:55 -0700 | [diff] [blame] | 392 | printf("Unknown return type: %s\n", jtypesig); |
Paul Sokolovsky | b230a86 | 2015-09-15 14:07:12 +0300 | [diff] [blame] | 393 | |
Paul Sokolovsky | 26a9b4d | 2015-09-13 01:25:04 +0300 | [diff] [blame] | 394 | return MP_OBJ_NULL; |
| 395 | } |
Paul Sokolovsky | 02041bf | 2015-10-09 00:25:26 +0300 | [diff] [blame] | 396 | #endif |
Paul Sokolovsky | 26a9b4d | 2015-09-13 01:25:04 +0300 | [diff] [blame] | 397 | |
Paul Sokolovsky | e79c6b6 | 2015-09-11 17:57:47 +0300 | [diff] [blame] | 398 | STATIC 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) { |
| 399 | jvalue jargs[n_args]; |
| 400 | // printf("methods=%p\n", methods); |
| 401 | jsize num_methods = JJ(GetArrayLength, methods); |
| 402 | for (int i = 0; i < num_methods; i++) { |
| 403 | jobject meth = JJ(GetObjectArrayElement, methods, i); |
Paul Sokolovsky | f22be4e | 2015-10-03 08:43:41 -0700 | [diff] [blame] | 404 | jobject name_o = JJ(CallObjectMethod, meth, Object_toString_mid); |
Paul Sokolovsky | e79c6b6 | 2015-09-11 17:57:47 +0300 | [diff] [blame] | 405 | const char *decl = JJ(GetStringUTFChars, name_o, NULL); |
| 406 | const char *arg_types = strchr(decl, '(') + 1; |
| 407 | //const char *arg_types_end = strchr(arg_types, ')'); |
| 408 | // printf("method[%d]=%p %s\n", i, meth, decl); |
| 409 | |
| 410 | const char *meth_name = NULL; |
| 411 | const char *ret_type = NULL; |
| 412 | if (!is_constr) { |
| 413 | meth_name = strprev(arg_types, '.') + 1; |
| 414 | ret_type = strprev(meth_name, ' ') - 1; |
| 415 | ret_type = strprev(ret_type, ' ') + 1; |
| 416 | |
| 417 | int name_len = strlen(name); |
| 418 | if (strncmp(name, meth_name, name_len/*arg_types - meth_name - 1*/) || meth_name[name_len] != '('/*(*/) { |
Paul Sokolovsky | 81d64ab | 2015-09-26 08:50:37 -0700 | [diff] [blame] | 419 | goto next_method; |
Paul Sokolovsky | e79c6b6 | 2015-09-11 17:57:47 +0300 | [diff] [blame] | 420 | } |
| 421 | } |
| 422 | // printf("method[%d]=%p %s\n", i, meth, decl); |
| 423 | // printf("!!!%s\n", arg_types); |
| 424 | // printf("name=%p meth_name=%s\n", name, meth_name); |
| 425 | |
| 426 | bool found = true; |
Paul Sokolovsky | 0eba162 | 2015-09-30 00:54:20 -0700 | [diff] [blame] | 427 | for (int i = 0; i < n_args && *arg_types != ')'; i++) { |
Paul Sokolovsky | 4e7bde8 | 2015-09-12 00:19:18 +0300 | [diff] [blame] | 428 | if (!py2jvalue(&arg_types, args[i], &jargs[i])) { |
| 429 | goto next_method; |
Paul Sokolovsky | e79c6b6 | 2015-09-11 17:57:47 +0300 | [diff] [blame] | 430 | } |
| 431 | |
| 432 | if (*arg_types == ',') { |
| 433 | arg_types++; |
| 434 | } |
| 435 | } |
| 436 | |
Paul Sokolovsky | 0eba162 | 2015-09-30 00:54:20 -0700 | [diff] [blame] | 437 | if (*arg_types != ')') { |
| 438 | goto next_method; |
| 439 | } |
| 440 | |
Paul Sokolovsky | e79c6b6 | 2015-09-11 17:57:47 +0300 | [diff] [blame] | 441 | if (found) { |
| 442 | // printf("found!\n"); |
| 443 | jmethodID method_id = JJ(FromReflectedMethod, meth); |
| 444 | jobject res; |
Paul Sokolovsky | 0d28a3e | 2015-09-27 02:30:28 -0700 | [diff] [blame] | 445 | mp_obj_t ret; |
Paul Sokolovsky | e79c6b6 | 2015-09-11 17:57:47 +0300 | [diff] [blame] | 446 | if (is_constr) { |
Paul Sokolovsky | 26a9b4d | 2015-09-13 01:25:04 +0300 | [diff] [blame] | 447 | JJ(ReleaseStringUTFChars, name_o, decl); |
Paul Sokolovsky | 0d28a3e | 2015-09-27 02:30:28 -0700 | [diff] [blame] | 448 | res = JJ(NewObjectA, obj, method_id, jargs); |
Paul Sokolovsky | 26a9b4d | 2015-09-13 01:25:04 +0300 | [diff] [blame] | 449 | return new_jobject(res); |
Paul Sokolovsky | e79c6b6 | 2015-09-11 17:57:47 +0300 | [diff] [blame] | 450 | } else { |
Paul Sokolovsky | c0a79cc | 2015-09-25 17:11:24 -0700 | [diff] [blame] | 451 | if (MATCH(ret_type, "void")) { |
| 452 | JJ(CallVoidMethodA, obj, method_id, jargs); |
Paul Sokolovsky | 41eb705 | 2015-10-14 00:24:36 +0300 | [diff] [blame^] | 453 | check_exception(); |
Paul Sokolovsky | 0d28a3e | 2015-09-27 02:30:28 -0700 | [diff] [blame] | 454 | ret = mp_const_none; |
Paul Sokolovsky | c0a79cc | 2015-09-25 17:11:24 -0700 | [diff] [blame] | 455 | } else if (MATCH(ret_type, "int")) { |
| 456 | jint res = JJ(CallIntMethodA, obj, method_id, jargs); |
Paul Sokolovsky | 41eb705 | 2015-10-14 00:24:36 +0300 | [diff] [blame^] | 457 | check_exception(); |
Paul Sokolovsky | 0d28a3e | 2015-09-27 02:30:28 -0700 | [diff] [blame] | 458 | ret = mp_obj_new_int(res); |
Paul Sokolovsky | c0a79cc | 2015-09-25 17:11:24 -0700 | [diff] [blame] | 459 | } else if (MATCH(ret_type, "boolean")) { |
| 460 | jboolean res = JJ(CallBooleanMethodA, obj, method_id, jargs); |
Paul Sokolovsky | 41eb705 | 2015-10-14 00:24:36 +0300 | [diff] [blame^] | 461 | check_exception(); |
Paul Sokolovsky | 0d28a3e | 2015-09-27 02:30:28 -0700 | [diff] [blame] | 462 | ret = mp_obj_new_bool(res); |
Paul Sokolovsky | c0a79cc | 2015-09-25 17:11:24 -0700 | [diff] [blame] | 463 | } else if (is_object_type(ret_type)) { |
| 464 | res = JJ(CallObjectMethodA, obj, method_id, jargs); |
Paul Sokolovsky | 41eb705 | 2015-10-14 00:24:36 +0300 | [diff] [blame^] | 465 | check_exception(); |
Paul Sokolovsky | 0d28a3e | 2015-09-27 02:30:28 -0700 | [diff] [blame] | 466 | ret = new_jobject(res); |
| 467 | } else { |
Paul Sokolovsky | c0a79cc | 2015-09-25 17:11:24 -0700 | [diff] [blame] | 468 | JJ(ReleaseStringUTFChars, name_o, decl); |
Paul Sokolovsky | 0d28a3e | 2015-09-27 02:30:28 -0700 | [diff] [blame] | 469 | nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "cannot handle return type")); |
Paul Sokolovsky | e79c6b6 | 2015-09-11 17:57:47 +0300 | [diff] [blame] | 470 | } |
Paul Sokolovsky | 0d28a3e | 2015-09-27 02:30:28 -0700 | [diff] [blame] | 471 | |
| 472 | JJ(ReleaseStringUTFChars, name_o, decl); |
Paul Sokolovsky | 7702028 | 2015-09-28 08:36:43 -0700 | [diff] [blame] | 473 | JJ(DeleteLocalRef, name_o); |
| 474 | JJ(DeleteLocalRef, meth); |
Paul Sokolovsky | 0d28a3e | 2015-09-27 02:30:28 -0700 | [diff] [blame] | 475 | return ret; |
Paul Sokolovsky | e79c6b6 | 2015-09-11 17:57:47 +0300 | [diff] [blame] | 476 | } |
| 477 | } |
| 478 | |
Paul Sokolovsky | 4e7bde8 | 2015-09-12 00:19:18 +0300 | [diff] [blame] | 479 | next_method: |
Paul Sokolovsky | e79c6b6 | 2015-09-11 17:57:47 +0300 | [diff] [blame] | 480 | JJ(ReleaseStringUTFChars, name_o, decl); |
Paul Sokolovsky | 7702028 | 2015-09-28 08:36:43 -0700 | [diff] [blame] | 481 | JJ(DeleteLocalRef, name_o); |
| 482 | JJ(DeleteLocalRef, meth); |
Paul Sokolovsky | e79c6b6 | 2015-09-11 17:57:47 +0300 | [diff] [blame] | 483 | } |
| 484 | |
| 485 | nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "method not found")); |
| 486 | } |
| 487 | |
| 488 | |
| 489 | STATIC 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) { |
| 490 | if (n_kw != 0) { |
| 491 | nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "kwargs not supported")); |
| 492 | } |
| 493 | mp_obj_jmethod_t *self = self_in; |
| 494 | |
| 495 | const char *name = qstr_str(self->name); |
| 496 | // jstring meth_name = JJ(NewStringUTF, name); |
| 497 | |
Paul Sokolovsky | 7a4b10c | 2015-09-14 00:12:27 +0300 | [diff] [blame] | 498 | jclass obj_class = self->obj; |
| 499 | if (!self->is_static) { |
| 500 | obj_class = JJ(GetObjectClass, self->obj); |
| 501 | } |
Paul Sokolovsky | e79c6b6 | 2015-09-11 17:57:47 +0300 | [diff] [blame] | 502 | jarray methods = JJ(CallObjectMethod, obj_class, Class_getMethods_mid); |
| 503 | |
| 504 | return call_method(self->obj, name, methods, false, n_args, args); |
| 505 | } |
| 506 | |
| 507 | STATIC const mp_obj_type_t jmethod_type = { |
| 508 | { &mp_type_type }, |
| 509 | .name = MP_QSTR_jmethod, |
| 510 | .print = jmethod_print, |
| 511 | .call = jmethod_call, |
| 512 | // .attr = jobject_attr, |
| 513 | // .locals_dict = (mp_obj_t)&jobject_locals_dict, |
| 514 | }; |
| 515 | |
| 516 | #ifdef __ANDROID__ |
| 517 | #define LIBJVM_SO "libdvm.so" |
| 518 | #else |
| 519 | #define LIBJVM_SO "libjvm.so" |
| 520 | #endif |
| 521 | |
| 522 | STATIC void create_jvm() { |
| 523 | JavaVMInitArgs args; |
| 524 | JavaVMOption options; |
| 525 | options.optionString = "-Djava.class.path=."; |
| 526 | args.version = JNI_VERSION_1_6; |
| 527 | args.nOptions = 1; |
| 528 | args.options = &options; |
| 529 | args.ignoreUnrecognized = 0; |
| 530 | |
| 531 | if (env) { |
| 532 | return; |
| 533 | } |
| 534 | |
| 535 | void *libjvm = dlopen(LIBJVM_SO, RTLD_NOW | RTLD_GLOBAL); |
| 536 | if (!libjvm) { |
| 537 | nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, "unable to load libjvm.so, use LD_LIBRARY_PATH")); |
| 538 | } |
| 539 | int (*_JNI_CreateJavaVM)(void*, void**, void*) = dlsym(libjvm, "JNI_CreateJavaVM"); |
| 540 | |
| 541 | int st = _JNI_CreateJavaVM(&jvm, (void**)&env, &args); |
| 542 | if (st < 0 || !env) { |
| 543 | nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, "unable to create JVM")); |
| 544 | } |
| 545 | |
Paul Sokolovsky | 1cb5de2 | 2015-09-17 13:31:16 +0300 | [diff] [blame] | 546 | Class_class = JJ(FindClass, "java/lang/Class"); |
Paul Sokolovsky | e79c6b6 | 2015-09-11 17:57:47 +0300 | [diff] [blame] | 547 | jclass method_class = JJ(FindClass, "java/lang/reflect/Method"); |
| 548 | String_class = JJ(FindClass, "java/lang/String"); |
| 549 | |
Paul Sokolovsky | f22be4e | 2015-10-03 08:43:41 -0700 | [diff] [blame] | 550 | jclass Object_class = JJ(FindClass, "java/lang/Object"); |
| 551 | Object_toString_mid = JJ(GetMethodID, Object_class, "toString", |
| 552 | "()Ljava/lang/String;"); |
| 553 | |
Paul Sokolovsky | 1cb5de2 | 2015-09-17 13:31:16 +0300 | [diff] [blame] | 554 | Class_getField_mid = (*env)->GetMethodID(env, Class_class, "getField", |
Paul Sokolovsky | e79c6b6 | 2015-09-11 17:57:47 +0300 | [diff] [blame] | 555 | "(Ljava/lang/String;)Ljava/lang/reflect/Field;"); |
Paul Sokolovsky | 1cb5de2 | 2015-09-17 13:31:16 +0300 | [diff] [blame] | 556 | Class_getMethods_mid = (*env)->GetMethodID(env, Class_class, "getMethods", |
Paul Sokolovsky | e79c6b6 | 2015-09-11 17:57:47 +0300 | [diff] [blame] | 557 | "()[Ljava/lang/reflect/Method;"); |
Paul Sokolovsky | 1cb5de2 | 2015-09-17 13:31:16 +0300 | [diff] [blame] | 558 | Class_getConstructors_mid = (*env)->GetMethodID(env, Class_class, "getConstructors", |
Paul Sokolovsky | e79c6b6 | 2015-09-11 17:57:47 +0300 | [diff] [blame] | 559 | "()[Ljava/lang/reflect/Constructor;"); |
| 560 | Method_getName_mid = (*env)->GetMethodID(env, method_class, "getName", |
| 561 | "()Ljava/lang/String;"); |
Paul Sokolovsky | b2d880d | 2015-09-21 12:02:22 -0700 | [diff] [blame] | 562 | |
| 563 | List_class = JJ(FindClass, "java/util/List"); |
| 564 | List_get_mid = JJ(GetMethodID, List_class, "get", |
| 565 | "(I)Ljava/lang/Object;"); |
| 566 | List_set_mid = JJ(GetMethodID, List_class, "set", |
| 567 | "(ILjava/lang/Object;)Ljava/lang/Object;"); |
Paul Sokolovsky | f3ca862 | 2015-09-29 10:05:30 -0700 | [diff] [blame] | 568 | List_size_mid = JJ(GetMethodID, List_class, "size", |
| 569 | "()I"); |
Paul Sokolovsky | 9e0a3d4 | 2015-10-02 00:21:07 -0700 | [diff] [blame] | 570 | IndexException_class = JJ(FindClass, "java/lang/IndexOutOfBoundsException"); |
Paul Sokolovsky | e79c6b6 | 2015-09-11 17:57:47 +0300 | [diff] [blame] | 571 | } |
| 572 | |
| 573 | STATIC mp_obj_t mod_jni_cls(mp_obj_t cls_name_in) { |
| 574 | const char *cls_name = mp_obj_str_get_str(cls_name_in); |
| 575 | if (!env) { |
| 576 | create_jvm(); |
| 577 | } |
| 578 | jclass cls = JJ(FindClass, cls_name); |
| 579 | |
| 580 | mp_obj_jclass_t *o = m_new_obj(mp_obj_jclass_t); |
| 581 | o->base.type = &jclass_type; |
| 582 | o->cls = cls; |
| 583 | return o; |
| 584 | } |
| 585 | MP_DEFINE_CONST_FUN_OBJ_1(mod_jni_cls_obj, mod_jni_cls); |
| 586 | |
Paul Sokolovsky | cb6cf5e | 2015-09-16 01:09:39 +0300 | [diff] [blame] | 587 | STATIC mp_obj_t mod_jni_env() { |
| 588 | return mp_obj_new_int((mp_int_t)env); |
| 589 | } |
| 590 | MP_DEFINE_CONST_FUN_OBJ_0(mod_jni_env_obj, mod_jni_env); |
| 591 | |
Paul Sokolovsky | e79c6b6 | 2015-09-11 17:57:47 +0300 | [diff] [blame] | 592 | STATIC const mp_map_elem_t mp_module_jni_globals_table[] = { |
| 593 | { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_jni) }, |
| 594 | { MP_OBJ_NEW_QSTR(MP_QSTR_cls), (mp_obj_t)&mod_jni_cls_obj }, |
Paul Sokolovsky | cb6cf5e | 2015-09-16 01:09:39 +0300 | [diff] [blame] | 595 | { MP_OBJ_NEW_QSTR(MP_QSTR_env), (mp_obj_t)&mod_jni_env_obj }, |
Paul Sokolovsky | e79c6b6 | 2015-09-11 17:57:47 +0300 | [diff] [blame] | 596 | }; |
| 597 | |
| 598 | STATIC MP_DEFINE_CONST_DICT(mp_module_jni_globals, mp_module_jni_globals_table); |
| 599 | |
| 600 | const mp_obj_module_t mp_module_jni = { |
| 601 | .base = { &mp_type_module }, |
| 602 | .name = MP_QSTR_jni, |
| 603 | .globals = (mp_obj_dict_t*)&mp_module_jni_globals, |
| 604 | }; |