/* * Rjb - Ruby <-> Java Bridge * Copyright(c) 2004,2005,2006,2007,2008,2009,2010,2011,2012 arton * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * $Id: rjb.c 199 2012-12-17 13:31:18Z arton $ */ #define RJB_VERSION "1.5.1" #include "ruby.h" #include "extconf.h" #if RJB_RUBY_VERSION_CODE < 190 #include "st.h" #else #include "ruby/st.h" #endif #include "jniwrap.h" #include "jp_co_infoseek_hp_arton_rjb_RBridge.h" #include "riconv.h" #include "rjb.h" #include "ctype.h" /* * Method Modifier Flag defined in * http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#88358 */ #define ACC_PUBLIC 0x0001 #define ACC_PRIVATE 0x0002 #define ACC_PROTECTED 0x0004 #define ACC_STATIC 0x0008 #define ACC_FINAL 0x0010 #define ACC_VOLATILE 0x0040 #define ACC_TRANSIENT 0x0080 #define RJB_FIND_CLASS(var, name) \ var = rjb_find_class_by_name(jenv, name); \ rjb_check_exception(jenv, 1) #define RJB_HOLD_CLASS(var, name) \ var = rjb_find_class_by_name(jenv, name); \ rjb_check_exception(jenv, 1); \ var = (*jenv)->NewGlobalRef(jenv, var) #define RJB_LOAD_METHOD(var, obj, name, sig) \ var = (*jenv)->GetMethodID(jenv, obj, name, sig); \ rjb_check_exception(jenv, 1) #define RJB_LOAD_STATIC_METHOD(var, obj, name, sig) \ var = (*jenv)->GetStaticMethodID(jenv, obj, name, sig); \ rjb_check_exception(jenv, 1) #if defined(RUBINIUS) #define CLASS_NEW(obj, name) rb_define_class_under(rjb, name, obj) #define CLASS_INHERITED(spr, kls) RTEST(rb_funcall(spr, rb_intern(">="), 1, kls)) #else #define CLASS_NEW(obj, name) rb_define_class_under(rjb, name, obj) #define CLASS_INHERITED(spr, kls) RTEST(rb_funcall(spr, rb_intern(">="), 1, kls)) #endif #define IS_RJB_OBJECT(v) (CLASS_INHERITED(rjbi, rb_obj_class(v)) || rb_obj_class(v) == rjb || CLASS_INHERITED(rjbb, rb_obj_class(v))) #define USER_INITIALIZE "@user_initialize" static void register_class(VALUE, VALUE); static VALUE import_class(JNIEnv* jenv, jclass, VALUE); static VALUE register_instance(JNIEnv* jenv, VALUE klass, struct jv_data*, jobject); static VALUE rjb_s_free(struct jv_data*); static VALUE rjb_class_forname(int argc, VALUE* argv, VALUE self); static void setup_metadata(JNIEnv* jenv, VALUE self, struct jv_data*, VALUE classname); static VALUE jarray2rv(JNIEnv* jenv, jvalue val); static jarray r2objarray(JNIEnv* jenv, VALUE v, const char* cls); static VALUE jv2rv_withprim(JNIEnv* jenv, jobject o); static J2R get_arrayconv(const char* cname, char* depth); static jarray r2barray(JNIEnv* jenv, VALUE v, const char* cls); static VALUE rjb_s_bind(VALUE self, VALUE rbobj, VALUE itfname); static VALUE rjb; static VALUE jklass; static VALUE rjbc; static VALUE rjbi; static VALUE rjbb; static VALUE rjba; static ID user_initialize; static ID initialize_proxy; static ID cvar_classpath; static ID anonymousblock; static ID id_call; VALUE rjb_loaded_classes; static VALUE proxies; JavaVM* rjb_jvm; jclass rjb_rbridge; jmethodID rjb_register_bridge; jmethodID rjb_load_class; static JNIEnv* main_jenv; static VALUE primitive_conversion = Qfalse; /* * Object cache, never destroyed */ /* method */ static jmethodID method_getModifiers; static jmethodID method_getName; static jmethodID getParameterTypes; static jmethodID getReturnType; /* field */ static jmethodID field_getModifiers; static jmethodID field_getName; static jmethodID field_getType; /* constructor */ static jmethodID ctrGetParameterTypes; /* class */ static jclass j_class; jmethodID rjb_class_getName; /* throwable */ jclass rjb_j_throwable; jmethodID rjb_throwable_getMessage; /* String global reference */ static jclass j_string; static jmethodID str_tostring; /* Object global reference */ static jclass j_object; /* ClassLoader */ static jclass j_classloader; static jmethodID get_system_classloader; /* URLClassLoader */ static jclass j_url_loader; static jobject url_loader; static jmethodID url_loader_new; static jmethodID url_geturls; static jmethodID url_add_url; /* URL global reference */ static jclass j_url; static jmethodID url_new; enum PrimitiveType { PRM_INT = 0, PRM_LONG, PRM_DOUBLE, PRM_BOOLEAN, PRM_CHARACTER, PRM_SHORT, PRM_BYTE, PRM_FLOAT, /* */ PRM_LAST }; /* * Native type conversion table */ typedef struct jobject_ruby_table { const char* classname; const char* to_prim_method; const char* prmsig; const char* ctrsig; jclass klass; /* primitive class */ jmethodID to_prim_id; jmethodID ctr_id; J2R func; } jprimitive_table; JNIEnv* rjb_attach_current_thread(void) { JNIEnv* env; if (!rjb_jvm) return NULL; (*rjb_jvm)->AttachCurrentThread(rjb_jvm, (void**)&env, '\0'); return env; } void rjb_release_string(JNIEnv *jenv, jstring str, const char* chrs) { (*jenv)->ReleaseStringUTFChars(jenv, str, chrs); (*jenv)->DeleteLocalRef(jenv, str); } static char* java2jniname(char* jnicls) { char* p; for (p = jnicls; *p; p++) { if (*p == '.') { *p = '/'; } } return jnicls; } static char* jniname2java(char* jniname) { char* p; for (p = jniname; *p; p++) { if (*p == '/') { *p = '.'; } } return jniname; } static char* next_sig(char* p) { if (!*p) { return p; } if (*p == '[') { p++; } if (*p == 'L') { while (*p && *p != ';') { p++; } } return (*p) ? ++p : p; } static VALUE jstring2val(JNIEnv* jenv, jstring s) { const char* p; VALUE v; if (s == NULL) { return Qnil; } p = (*jenv)->GetStringUTFChars(jenv, s, NULL); v = rb_str_new2(p); v = exticonv_utf8_to_local(v); rjb_release_string(jenv, s, p); return v; } /* * Type conversion tables */ typedef struct type_conversion_table { const char* jtype; const char* jntype; R2J r2j; J2R j2r; J2R ja2r; R2JARRAY r2ja; off_t jcall; /* for instance method */ off_t jscall; /* for static method */ } jconv_table; /* * conversion methods * val will be released in this function. */ static VALUE jv2rclass(JNIEnv* jenv, jclass jc) { const char* cname; VALUE clsname; VALUE v; jstring nm = (*jenv)->CallObjectMethod(jenv, jc, rjb_class_getName); rjb_check_exception(jenv, 0); cname = (*jenv)->GetStringUTFChars(jenv, nm, NULL); clsname = rb_str_new2(cname); rjb_release_string(jenv, nm, cname); v = rb_hash_aref(rjb_loaded_classes, clsname); if (v == Qnil) { v = import_class(jenv, jc, clsname); } (*jenv)->DeleteLocalRef(jenv, jc); return v; } static VALUE jv2rv_r(JNIEnv* jenv, jvalue val) { const char* cname; jstring nm; jclass klass; VALUE clsname; VALUE v; struct jv_data* ptr; /* object to ruby */ if (!val.l) return Qnil; klass = (*jenv)->GetObjectClass(jenv, val.l); if ((*jenv)->IsSameObject(jenv, klass, j_class)) { (*jenv)->DeleteLocalRef(jenv, klass); return jv2rclass(jenv, val.l); } nm = (*jenv)->CallObjectMethod(jenv, klass, rjb_class_getName); rjb_check_exception(jenv, 0); cname = (*jenv)->GetStringUTFChars(jenv, nm, NULL); if (*cname == '[') { char depth = 0; J2R j2r = get_arrayconv(cname, &depth); rjb_release_string(jenv, nm, cname); v = j2r(jenv, val); (*jenv)->DeleteLocalRef(jenv, klass); (*jenv)->DeleteLocalRef(jenv, val.l); return v; } clsname = rb_str_new2(cname); rjb_release_string(jenv, nm, cname); v = rb_hash_aref(rjb_loaded_classes, clsname); if (v == Qnil) { v = import_class(jenv, klass, clsname); } Data_Get_Struct(v, struct jv_data, ptr); v = register_instance(jenv, v, (struct jv_data*)ptr, val.l); (*jenv)->DeleteLocalRef(jenv, klass); (*jenv)->DeleteLocalRef(jenv, val.l); return v; } VALUE jv2rv(JNIEnv* jenv, jvalue val) { if (RTEST(primitive_conversion)) { return jv2rv_withprim(jenv, val.l); } return jv2rv_r(jenv, val); } static VALUE jvoid2rv(JNIEnv* jenv, jvalue val) { return Qnil; } static VALUE jbyte2rv(JNIEnv* jenv, jvalue val) { return INT2NUM(val.b); } static VALUE jchar2rv(JNIEnv* jenv, jvalue val) { return INT2NUM(val.c); } static VALUE jdouble2rv(JNIEnv* jenv, jvalue val) { return rb_float_new(val.d); } static VALUE jfloat2rv(JNIEnv* jenv, jvalue val) { return rb_float_new((double)val.f); } static VALUE jint2rv(JNIEnv* jenv, jvalue val) { return INT2NUM(val.i); } static VALUE jlong2rv(JNIEnv* jenv, jvalue val) { #if HAVE_LONG_LONG return LL2NUM(val.j); #else char bignum[64]; sprintf(bignum, "%ld * 0x100000000 + 0x%lx", (long)(val.j >> 32), (unsigned long)val.j); return rb_eval_string(bignum); #endif } static VALUE jshort2rv(JNIEnv* jenv, jvalue val) { return INT2NUM(val.s); } static VALUE jboolean2rv(JNIEnv* jenv, jvalue val) { return (val.z) ? Qtrue : Qfalse; } static VALUE jstring2rv(JNIEnv* jenv, jvalue val) { return jstring2val(jenv, (jstring)val.l); } static VALUE ja2r(J2R conv, JNIEnv* jenv, jvalue val, int depth) { jsize len; VALUE v; int i; if (!val.l) return Qnil; if (depth == 1) { return conv(jenv, val); } len = (*jenv)->GetArrayLength(jenv, val.l); v = rb_ary_new2(len); for (i = 0; i < len; i++) { jvalue wrap; wrap.l = (*jenv)->GetObjectArrayElement(jenv, val.l, i); rb_ary_push(v, ja2r(conv, jenv, wrap, depth - 1)); } (*jenv)->DeleteLocalRef(jenv, val.l); return v; } static VALUE jarray2rv(JNIEnv* jenv, jvalue val) { jsize len; VALUE v; int i; if (!val.l) return Qnil; len = (*jenv)->GetArrayLength(jenv, val.l); v = rb_ary_new2(len); for (i = 0; i < len; i++) { jvalue wrap; wrap.l = (*jenv)->GetObjectArrayElement(jenv, val.l, i); /* wrap.l will be release in jv2rv */ rb_ary_push(v, jv2rv(jenv, wrap)); } (*jenv)->DeleteLocalRef(jenv, val.l); return v; } static VALUE ca2rv(JNIEnv* jenv, void* p) { return INT2FIX(*(jchar*)p); } static VALUE da2rv(JNIEnv* jenv, void* p) { return rb_float_new(*(jdouble*)p); } static VALUE fa2rv(JNIEnv* jenv, void* p) { return rb_float_new(*(jfloat*)p); } static VALUE ia2rv(JNIEnv* jenv, void* p) { return INT2NUM(*(jint*)p); } static VALUE la2rv(JNIEnv* jenv, void* p) { #if HAVE_LONG_LONG return LL2NUM(*(jlong*)p); #else return LONG2NUM(*(jlong*)p); #endif } static VALUE sa2rv(JNIEnv* jenv, void* p) { return INT2FIX(*(jshort*)p); } static VALUE ba2rv(JNIEnv* jenv, void* p) { return (*(jboolean*)p) ? Qtrue : Qfalse; } /* * val : released in this function. */ static VALUE call_conv(JNIEnv* jenv, jvalue val, size_t sz, void* p, CONV conv, size_t fnc) { int i; char* cp = (char*)p; jsize len = (*jenv)->GetArrayLength(jenv, val.l); VALUE v = rb_ary_new2(len); for (i = 0; i < len; i++) { rb_ary_push(v, conv(jenv, cp)); cp += sz; } (*(RELEASEARRAY*)(((char*)*jenv) + fnc))(jenv, val.l, p, JNI_ABORT); (*jenv)->DeleteLocalRef(jenv, val.l); return v; } static VALUE jbytearray2rv(JNIEnv* jenv, jvalue val) { jsize len = (*jenv)->GetArrayLength(jenv, val.l); jbyte* p = (*jenv)->GetByteArrayElements(jenv, val.l, NULL); VALUE v = rb_str_new((char*)p, len); (*jenv)->ReleaseByteArrayElements(jenv, val.l, p, JNI_ABORT); (*jenv)->DeleteLocalRef(jenv, val.l); return v; } static VALUE jchararray2rv(JNIEnv* jenv, jvalue val) { jchar* p = (*jenv)->GetCharArrayElements(jenv, val.l, NULL); return call_conv(jenv, val, sizeof(jchar), p, ca2rv, offsetof(struct JNINativeInterface_, ReleaseCharArrayElements)); } static VALUE jdoublearray2rv(JNIEnv* jenv, jvalue val) { jdouble* p = (*jenv)->GetDoubleArrayElements(jenv, val.l, NULL); return call_conv(jenv, val, sizeof(jdouble), p, da2rv, offsetof(struct JNINativeInterface_, ReleaseDoubleArrayElements)); } static VALUE jfloatarray2rv(JNIEnv* jenv, jvalue val) { jfloat* p = (*jenv)->GetFloatArrayElements(jenv, val.l, NULL); return call_conv(jenv, val, sizeof(jfloat), p, fa2rv, offsetof(struct JNINativeInterface_, ReleaseFloatArrayElements)); } static VALUE jintarray2rv(JNIEnv* jenv, jvalue val) { jint* p = (*jenv)->GetIntArrayElements(jenv, val.l, NULL); return call_conv(jenv, val, sizeof(jint), p, ia2rv, offsetof(struct JNINativeInterface_, ReleaseIntArrayElements)); } static VALUE jlongarray2rv(JNIEnv* jenv, jvalue val) { jlong* p = (*jenv)->GetLongArrayElements(jenv, val.l, NULL); return call_conv(jenv, val, sizeof(jlong), p, la2rv, offsetof(struct JNINativeInterface_, ReleaseLongArrayElements)); } static VALUE jshortarray2rv(JNIEnv* jenv, jvalue val) { jshort* p = (*jenv)->GetShortArrayElements(jenv, val.l, NULL); return call_conv(jenv, val, sizeof(jshort), p, sa2rv, offsetof(struct JNINativeInterface_, ReleaseShortArrayElements)); } static VALUE jstringarray2rv(JNIEnv* jenv, jvalue val) { int i; jsize len = (*jenv)->GetArrayLength(jenv, val.l); VALUE v = rb_ary_new2(len); for (i = 0; i < len; i++) { jobject p = (*jenv)->GetObjectArrayElement(jenv, val.l, i); rb_ary_push(v, jstring2val(jenv, (jstring)p)); } (*jenv)->DeleteLocalRef(jenv, val.l); return v; } static VALUE jbooleanarray2rv(JNIEnv* jenv, jvalue val) { jboolean* p = (*jenv)->GetBooleanArrayElements(jenv, val.l, NULL); return call_conv(jenv, val, sizeof(jboolean), p, ba2rv, offsetof(struct JNINativeInterface_, ReleaseBooleanArrayElements)); } /* * table that handles java primitive type. * index: according to enum PrimitiveType. */ static jprimitive_table jpcvt[] = { { "java/lang/Integer", "intValue", "()I", "(I)V", NULL, 0, 0, jint2rv, }, { "java/lang/Long", "longValue", "()J", "(J)V", NULL, 0, 0, jlong2rv, }, { "java/lang/Double", "doubleValue", "()D", "(D)V", NULL, 0, 0, jdouble2rv, }, { "java/lang/Boolean", "booleanValue", "()Z", "(Z)Ljava/lang/Boolean;", NULL, 0, 0, jboolean2rv, }, { "java/lang/Character", "charValue", "()C", NULL, NULL, 0, 0, jchar2rv, }, { "java/lang/Short", "intValue", "()I", NULL, NULL, 0, 0, jint2rv, }, { "java/lang/Byte", "intValue", "()I", NULL, NULL, 0, 0, jint2rv, }, { "java/lang/Float", "doubleValue", "()D", NULL, NULL, 0, 0, jdouble2rv, }, }; /* * o will be released in this function. */ static VALUE jv2rv_withprim(JNIEnv* jenv, jobject o) { jvalue jv; int i; jclass klass; jv.j = 0; if (!o) rb_raise(rb_eRuntimeError, "Object is NULL"); klass = (*jenv)->GetObjectClass(jenv, o); for (i = PRM_INT; i < PRM_LAST; i++) { if ((*jenv)->IsSameObject(jenv, jpcvt[i].klass, klass)) { switch (*(jpcvt[i].to_prim_method)) { case 'i': jv.i = (*jenv)->CallIntMethod(jenv, o, jpcvt[i].to_prim_id); break; case 'b': jv.z = (*jenv)->CallBooleanMethod(jenv, o, jpcvt[i].to_prim_id); break; case 'd': jv.d = (*jenv)->CallDoubleMethod(jenv, o, jpcvt[i].to_prim_id); break; case 'c': jv.c = (*jenv)->CallCharMethod(jenv, o, jpcvt[i].to_prim_id); break; case 'l': jv.j = (*jenv)->CallLongMethod(jenv, o, jpcvt[i].to_prim_id); break; default: rb_raise(rb_eRuntimeError, "no convertor defined(%d)", i); break; } (*jenv)->DeleteLocalRef(jenv, o); return jpcvt[i].func(jenv, jv); } } if ((*jenv)->IsSameObject(jenv, j_string, klass)) { return jstring2val(jenv, o); } jv.l = o; return jv2rv_r(jenv, jv); } /* * functions convert VALUE to jvalue */ static void rv2jv(JNIEnv* jenv, VALUE val, jvalue* jv, const char* psig, int release) { jv->l = NULL; } static void rv2jbyte(JNIEnv* jenv, VALUE val, jvalue* jv, const char* psig, int release) { if (!release) jv->b = (jbyte)NUM2INT(val); } static void rv2jchar(JNIEnv* jenv, VALUE val, jvalue* jv, const char* psig, int release) { if (!release) jv->c = (jchar)NUM2INT(val); } static void rv2jdouble(JNIEnv* jenv, VALUE val, jvalue* jv, const char* psig, int release) { if (release) return; switch (TYPE(val)) { case T_FIXNUM: jv->d = NUM2INT(val); break; case T_FLOAT: jv->d = NUM2DBL(val); break; default: rb_raise(rb_eRuntimeError, "can't change to double"); break; } } static void rv2jfloat(JNIEnv* jenv, VALUE val, jvalue* jv, const char* psig, int release) { if (release) return; switch (TYPE(val)) { case T_FIXNUM: jv->f = (float)NUM2INT(val); break; case T_FLOAT: jv->f = (float)NUM2DBL(val); break; default: rb_raise(rb_eRuntimeError, "can't change to float"); break; } } static void rv2jint(JNIEnv* jenv, VALUE val, jvalue* jv, const char* psig, int release) { if (!release) jv->i = NUM2INT(val); } static void rv2jlong(JNIEnv* jenv, VALUE val, jvalue* jv, const char* psig, int release) { if (release) return; switch (TYPE(val)) { case T_FIXNUM: jv->j = FIX2LONG(val); break; default: #if HAVE_LONG_LONG jv->j = NUM2LL(val); #else rb_raise(rb_eRuntimeError, "can't change to long"); #endif break; } } static void rv2jshort(JNIEnv* jenv, VALUE val, jvalue* jv, const char* psig, int release) { if (release) return; if (TYPE(val) == T_FIXNUM) { int n = FIX2INT(val); if (abs(n) < 0x7fff) { jv->s = (short)n; return; } } rb_raise(rb_eRuntimeError, "can't change to short"); } static void rv2jboolean(JNIEnv* jenv, VALUE val, jvalue* jv, const char* psig, int release) { if (!release) jv->z = (RTEST(val)) ? JNI_TRUE : JNI_FALSE; } static void rv2jstring(JNIEnv* jenv, VALUE val, jvalue* jv, const char* psig, int release) { if (!release) { if (TYPE(val) == T_DATA && IS_RJB_OBJECT(val)) { struct jvi_data* ptr; Data_Get_Struct(val, struct jvi_data, ptr); if ((*jenv)->IsInstanceOf(jenv, ptr->obj, j_string)) { jv->l = ptr->obj; } else { jmethodID tostr; jstring js; tostr = (*jenv)->GetMethodID(jenv, ptr->klass, "toString", "()Ljava/lang/String;"); rjb_check_exception(jenv, 0); js = (*jenv)->CallObjectMethod(jenv, ptr->obj, tostr); rjb_check_exception(jenv, 0); jv->l = js; } } else { if (NIL_P(val)) { jv->l = NULL; } else { val = exticonv_local_to_utf8(val); jv->l = (*jenv)->NewStringUTF(jenv, StringValuePtr(val)); } } } else { if (TYPE(val) == T_DATA) { if (IS_RJB_OBJECT(val)) { struct jvi_data* ptr; Data_Get_Struct(val, struct jvi_data, ptr); if ((*jenv)->IsInstanceOf(jenv, ptr->obj, j_string)) { return; /* never delete at this time */ } } } (*jenv)->DeleteLocalRef(jenv, jv->l); } } /* * psig may be NULL (from proxy/array call) */ static void rv2jobject(JNIEnv* jenv, VALUE val, jvalue* jv, const char* psig, int release) { if (!release) { jv->l = NULL; if (val == Qtrue || val == Qfalse) { jv->l = (*jenv)->CallStaticObjectMethod(jenv, jpcvt[PRM_BOOLEAN].klass, jpcvt[PRM_BOOLEAN].ctr_id, (val == Qtrue) ? JNI_TRUE : JNI_FALSE); } else if (NIL_P(val)) { /* no-op */ } else if (FIXNUM_P(val)) { jvalue arg; int idx = PRM_INT; #if HAVE_LONG_LONG arg.j = FIX2LONG(val); if (arg.j < INT_MIN || arg.j > INT_MAX) { idx = PRM_LONG; } #else arg.i = FIX2LONG(val); #endif jv->l = (*jenv)->NewObject(jenv, jpcvt[idx].klass, jpcvt[idx].ctr_id, arg); } else { jvalue arg; switch (TYPE(val)) { case T_DATA: if (IS_RJB_OBJECT(val)) { /* TODO: check instanceof (class (in psig) ) */ struct jvi_data* ptr; Data_Get_Struct(val, struct jvi_data, ptr); jv->l = ptr->obj; } else if (rb_obj_class(val) == rjbb) { struct rj_bridge* ptr; Data_Get_Struct(val, struct rj_bridge, ptr); jv->l = ptr->proxy; } else if (CLASS_INHERITED(rjbc, rb_obj_class(val))) { struct jv_data* ptr; Data_Get_Struct(val, struct jv_data, ptr); jv->l = ptr->idata.obj; } break; case T_STRING: if (psig && *psig == '[' && *(psig + 1) == 'B') { jv->l = r2barray(jenv, val, NULL); } else { rv2jstring(jenv, val, jv, NULL, 0); } break; case T_FLOAT: arg.d = NUM2DBL(val); jv->l = (*jenv)->NewObject(jenv, jpcvt[PRM_DOUBLE].klass, jpcvt[PRM_DOUBLE].ctr_id, arg.d); break; case T_ARRAY: jv->l = r2objarray(jenv, val, "Ljava/lang/Object;"); break; #if HAVE_LONG_LONG case T_BIGNUM: arg.j = rb_big2ll(val); jv->l = (*jenv)->NewObject(jenv, jpcvt[PRM_LONG].klass, jpcvt[PRM_LONG].ctr_id, arg); break; #endif case T_OBJECT: default: #if defined(DEBUG) fprintf(stderr, "rtype:%d, sig=%s\n", TYPE(val), psig); fflush(stderr); #endif rb_raise(rb_eRuntimeError, "can't convert to java type"); break; } } } else { switch (TYPE(val)) { case T_STRING: case T_FLOAT: case T_ARRAY: case T_BIGNUM: if (jv->l) (*jenv)->DeleteLocalRef(jenv, jv->l); break; } } } static void check_fixnumarray(VALUE v) { size_t i; size_t len = RARRAY_LEN(v); VALUE* p = RARRAY_PTR(v); /* check all fixnum (overflow is permit) */ for (i = 0; i < len; i++) { if (!FIXNUM_P(*p++)) { rb_raise(rb_eRuntimeError, "array element must be a fixnum"); } } } static jarray r2barray(JNIEnv* jenv, VALUE v, const char* cls) { jarray ary = NULL; if (TYPE(v) == T_STRING) { ary = (*jenv)->NewByteArray(jenv, (jint)RSTRING_LEN(v)); (*jenv)->SetByteArrayRegion(jenv, ary, 0, (jint)RSTRING_LEN(v), (const jbyte*)RSTRING_PTR(v)); } else if (TYPE(v) == T_ARRAY) { int i; jbyte* pb; check_fixnumarray(v); ary = (*jenv)->NewByteArray(jenv, (jint)RARRAY_LEN(v)); pb = (*jenv)->GetByteArrayElements(jenv, ary, NULL); for (i = 0; i < RARRAY_LEN(v); i++) { *(pb + i) = (jbyte)FIX2ULONG(RARRAY_PTR(v)[i]); } (*jenv)->ReleaseByteArrayElements(jenv, ary, pb, 0); } if (!ary) { rb_raise(rb_eRuntimeError, "can't coerce to byte array"); } return ary; } static jarray r2carray(JNIEnv* jenv, VALUE v, const char* cls) { jarray ary = NULL; if (TYPE(v) == T_ARRAY) { int i; jchar* pb; check_fixnumarray(v); ary = (*jenv)->NewCharArray(jenv, (jint)RARRAY_LEN(v)); pb = (*jenv)->GetCharArrayElements(jenv, ary, NULL); for (i = 0; i < RARRAY_LEN(v); i++) { *(pb + i) = (jchar)FIX2ULONG(RARRAY_PTR(v)[i]); } (*jenv)->ReleaseCharArrayElements(jenv, ary, pb, 0); return ary; } rb_raise(rb_eRuntimeError, "can't coerce to char array"); } static jarray r2darray(JNIEnv* jenv, VALUE v, const char* cls) { jarray ary = NULL; if (TYPE(v) == T_ARRAY) { int i; jdouble* pb; ary = (*jenv)->NewDoubleArray(jenv, (jint)RARRAY_LEN(v)); pb = (*jenv)->GetDoubleArrayElements(jenv, ary, NULL); for (i = 0; i < RARRAY_LEN(v); i++) { *(pb + i) = (jdouble)rb_num2dbl(RARRAY_PTR(v)[i]); } (*jenv)->ReleaseDoubleArrayElements(jenv, ary, pb, 0); return ary; } rb_raise(rb_eRuntimeError, "can't coerce to double array"); } static jarray r2farray(JNIEnv* jenv, VALUE v, const char* cls) { jarray ary = NULL; if (TYPE(v) == T_ARRAY) { int i; jfloat* pb; ary = (*jenv)->NewFloatArray(jenv, (jint)RARRAY_LEN(v)); pb = (*jenv)->GetFloatArrayElements(jenv, ary, NULL); for (i = 0; i < RARRAY_LEN(v); i++) { *(pb + i) = (jfloat)rb_num2dbl(RARRAY_PTR(v)[i]); } (*jenv)->ReleaseFloatArrayElements(jenv, ary, pb, 0); return ary; } rb_raise(rb_eRuntimeError, "can't coerce to float array"); } static jarray r2iarray(JNIEnv* jenv, VALUE v, const char* cls) { jarray ary = NULL; if (TYPE(v) == T_ARRAY) { int i; jint* pb; check_fixnumarray(v); ary = (*jenv)->NewIntArray(jenv, (jint)RARRAY_LEN(v)); pb = (*jenv)->GetIntArrayElements(jenv, ary, NULL); for (i = 0; i < RARRAY_LEN(v); i++) { *(pb + i) = (jint)FIX2LONG(RARRAY_PTR(v)[i]); } (*jenv)->ReleaseIntArrayElements(jenv, ary, pb, 0); return ary; } rb_raise(rb_eRuntimeError, "can't coerce to int array"); } static jarray r2larray(JNIEnv* jenv, VALUE v, const char* cls) { jarray ary = NULL; if (TYPE(v) == T_ARRAY) { int i; jlong* pb; ary = (*jenv)->NewLongArray(jenv, (jint)RARRAY_LEN(v)); pb = (*jenv)->GetLongArrayElements(jenv, ary, NULL); for (i = 0; i < RARRAY_LEN(v); i++) { #if HAVE_LONG_LONG *(pb + i) = (jlong)rb_num2ll(RARRAY_PTR(v)[i]); #else *(pb + i) = (jlong)FIX2LONG(RARRAY_PTR(v)[i]); #endif } (*jenv)->ReleaseLongArrayElements(jenv, ary, pb, 0); return ary; } rb_raise(rb_eRuntimeError, "can't coerce to long array"); } static jarray r2sarray(JNIEnv* jenv, VALUE v, const char* cls) { jarray ary = NULL; if (TYPE(v) == T_ARRAY) { int i; jshort* pb; check_fixnumarray(v); ary = (*jenv)->NewShortArray(jenv, (jint)RARRAY_LEN(v)); pb = (*jenv)->GetShortArrayElements(jenv, ary, NULL); for (i = 0; i < RARRAY_LEN(v); i++) { *(pb + i) = (jshort)FIX2LONG(RARRAY_PTR(v)[i]); } (*jenv)->ReleaseShortArrayElements(jenv, ary, pb, 0); return ary; } rb_raise(rb_eRuntimeError, "can't coerce to short array"); } static jarray r2boolarray(JNIEnv* jenv, VALUE v, const char* cls) { jarray ary = NULL; if (TYPE(v) == T_ARRAY) { int i; jboolean* pb; ary = (*jenv)->NewBooleanArray(jenv, (jint)RARRAY_LEN(v)); pb = (*jenv)->GetBooleanArrayElements(jenv, ary, NULL); for (i = 0; i < RARRAY_LEN(v); i++) { *(pb + i) = (!RTEST(RARRAY_PTR(v)[i])) ? JNI_FALSE : JNI_TRUE; } (*jenv)->ReleaseBooleanArrayElements(jenv, ary, pb, 0); return ary; } rb_raise(rb_eRuntimeError, "can't coerce to boolean array"); } static jarray r2voidarray(JNIEnv* jenv, VALUE v, const char* cls) { rb_raise(rb_eRuntimeError, "void never arrayed"); } static jarray r2objarray(JNIEnv* jenv, VALUE v, const char* cls) { jarray ary = NULL; if (TYPE(v) == T_ARRAY) { int i; jclass jcls = NULL; char* p = strchr(cls, ';'); if (p) { volatile VALUE clsname = rb_str_new(cls + 1, p - cls - 1); // skip first 'L' jcls = rjb_find_class(jenv, clsname); } ary = (*jenv)->NewObjectArray(jenv, (jint)RARRAY_LEN(v), (jcls) ? jcls : j_object, NULL); rjb_check_exception(jenv, 0); for (i = 0; i < RARRAY_LEN(v); i++) { jvalue jv; rv2jobject(jenv, RARRAY_PTR(v)[i], &jv, NULL, 0); (*jenv)->SetObjectArrayElement(jenv, ary, i, jv.l); } return ary; } rb_raise(rb_eRuntimeError, "can't coerce to object array"); } /* * Type convertion tables */ static const jconv_table jcvt[] = { { "byte", "B", rv2jbyte, jbyte2rv, jbytearray2rv, r2barray, offsetof(struct JNINativeInterface_, CallByteMethodA), offsetof(struct JNINativeInterface_, CallStaticByteMethodA), }, { "char", "C", rv2jchar, jchar2rv, jchararray2rv, r2carray, offsetof(struct JNINativeInterface_, CallCharMethodA), offsetof(struct JNINativeInterface_, CallStaticCharMethodA), }, { "double", "D", rv2jdouble, jdouble2rv, jdoublearray2rv, r2darray, offsetof(struct JNINativeInterface_, CallDoubleMethodA), offsetof(struct JNINativeInterface_, CallStaticDoubleMethodA), }, { "float", "F", rv2jfloat, jfloat2rv, jfloatarray2rv, r2farray, offsetof(struct JNINativeInterface_, CallFloatMethodA), offsetof(struct JNINativeInterface_, CallStaticFloatMethodA), }, { "int", "I", rv2jint, jint2rv, jintarray2rv, r2iarray, offsetof(struct JNINativeInterface_, CallIntMethodA), offsetof(struct JNINativeInterface_, CallStaticIntMethodA), }, { "long", "J", rv2jlong, jlong2rv, jlongarray2rv, r2larray, offsetof(struct JNINativeInterface_, CallLongMethodA), offsetof(struct JNINativeInterface_, CallStaticLongMethodA), }, { "short", "S", rv2jshort, jshort2rv, jshortarray2rv, r2sarray, offsetof(struct JNINativeInterface_, CallShortMethodA), offsetof(struct JNINativeInterface_, CallStaticShortMethodA), }, { "boolean", "Z", rv2jboolean, jboolean2rv, jbooleanarray2rv, r2boolarray, offsetof(struct JNINativeInterface_, CallBooleanMethodA), offsetof(struct JNINativeInterface_, CallStaticBooleanMethodA), }, { "void", "V", rv2jv, jvoid2rv, NULL, r2voidarray, offsetof(struct JNINativeInterface_, CallVoidMethodA), offsetof(struct JNINativeInterface_, CallStaticVoidMethodA), }, { "java.lang.String", "Ljava.lang.String;", rv2jstring, jstring2rv, jstringarray2rv, r2objarray, offsetof(struct JNINativeInterface_, CallObjectMethodA), offsetof(struct JNINativeInterface_, CallStaticObjectMethodA), }, }; static void rv2jarray(JNIEnv* jenv, VALUE val, jvalue* jv, const char* psig, int release) { if (*psig != '[') { rb_raise(rb_eRuntimeError, "argument signature not array"); } if (release) { if (TYPE(val) == T_STRING && *(psig + 1) == 'B') { // copy array's contents into arg string jsize len = (*jenv)->GetArrayLength(jenv, jv->l); jbyte* p = (*jenv)->GetByteArrayElements(jenv, jv->l, NULL); if (len <= RSTRING_LEN(val)) { memcpy(StringValuePtr(val), p, len); } else { VALUE src = rb_str_new((char*)p, len); rb_str_set_len(val, 0); rb_str_append(val, src); } } (*jenv)->DeleteLocalRef(jenv, jv->l); } else { jint i; jarray ja = NULL; if (NIL_P(val)) { /* no-op, null for an array */ } else if (*(psig + 1) == '[') { if (TYPE(val) != T_ARRAY) { rb_raise(rb_eRuntimeError, "array's rank unmatch"); } ja = (*jenv)->NewObjectArray(jenv, (jint)RARRAY_LEN(val), j_object, NULL); rjb_check_exception(jenv, 0); for (i = 0; i < (jint)RARRAY_LEN(val); i++) { jvalue jv; rv2jarray(jenv, RARRAY_PTR(val)[i], &jv, psig + 1, 0); (*jenv)->SetObjectArrayElement(jenv, ja, (jint)i, jv.l); } } else { R2JARRAY r2a = r2objarray; for (i = 0; i < (jint)COUNTOF(jcvt); i++) { if (*(psig + 1) == jcvt[i].jntype[0]) { r2a = jcvt[i].r2ja; break; } } ja = r2a(jenv, val, psig + 1); } jv->l = ja; } } /* */ static R2J get_r2j(JNIEnv* jenv, jobject o, int* siglen, char* sigp) { size_t len, i; const char* cname; R2J result = NULL; jstring nm = (*jenv)->CallObjectMethod(jenv, o, rjb_class_getName); rjb_check_exception(jenv, 0); cname = (*jenv)->GetStringUTFChars(jenv, nm, NULL); if (*cname == '[') { if (siglen) { len = strlen(cname); *siglen += (int)len; strcpy(sigp, cname); } result = rv2jarray; } else { for (i = 0; i < COUNTOF(jcvt); i++) { if (!strcmp(cname, jcvt[i].jtype)) { if (siglen) { *siglen += (int)strlen(jcvt[i].jntype); strcpy(sigp, jcvt[i].jntype); } result = jcvt[i].r2j; break; } } if (!result) { if (siglen) { *siglen += sprintf(sigp, "L%s;", cname); } result = rv2jobject; } } rjb_release_string(jenv, nm, cname); return result; } static J2R get_arrayconv(const char* cname, char* pdepth) { size_t i; size_t start; for (start = 1; *(cname + start) == '['; start++); *pdepth = (char)start; for (i = 0; i < COUNTOF(jcvt); i++) { if (*(cname + start) == jcvt[i].jntype[0]) { if (jcvt[i].jntype[0] == 'L' && strncmp(cname + start, jcvt[i].jntype, strlen(jcvt[i].jntype))) { break; } return jcvt[i].ja2r; } } return &jarray2rv; } static J2R get_j2r(JNIEnv* jenv, jobject cls, char* psig, char* pdepth, char* ppsig, off_t* piv, int static_method) { size_t i; J2R result = NULL; const char* cname; const char* jname = NULL; jstring nm = (*jenv)->CallObjectMethod(jenv, cls, rjb_class_getName); rjb_check_exception(jenv, 0); cname = (*jenv)->GetStringUTFChars(jenv, nm, NULL); if (*cname == '[') { result = get_arrayconv(cname, pdepth); jname = cname; } else { for (i = 0; i < COUNTOF(jcvt); i++) { if (!strcmp(cname, jcvt[i].jtype)) { result = jcvt[i].j2r; *piv = (static_method) ? jcvt[i].jscall : jcvt[i].jcall; if (jcvt[i].jntype[0] != 'L') { *psig = jcvt[i].jntype[0]; } jname = jcvt[i].jntype; break; } } } if (ppsig) { if (!jname) { sprintf(ppsig, "L%s;", cname); } else { strcpy(ppsig, jname); } java2jniname(ppsig); } rjb_release_string(jenv, nm, cname); return result; } static void setup_j2r(JNIEnv* jenv, jobject cls, struct cls_method* pm, int static_method) { off_t iv = 0; J2R result = get_j2r(jenv, cls, &pm->basic.result_signature, &pm->basic.result_arraydepth, NULL, &iv, static_method); pm->result_convert = (result) ? result : jv2rv; if (iv) { pm->method = iv; } else { pm->method = (static_method) ? offsetof(struct JNINativeInterface_, CallStaticObjectMethodA) : offsetof(struct JNINativeInterface_, CallObjectMethodA); } } static void fill_convert(JNIEnv* jenv, struct cls_constructor* cls, jobjectArray tp, int count) { int i, siglen; R2J* tbl = ALLOC_N(R2J, count); char** sig = (char**)ALLOCA_N(char*, count); char siga[256]; cls->arg_convert = tbl; memset(tbl, 0, sizeof(R2J) * count); siglen = 0; for (i = 0; i < count; i++) { jobject o = (*jenv)->GetObjectArrayElement(jenv, tp, i); *(tbl + i) = get_r2j(jenv, o, &siglen, siga); *(sig + i) = ALLOCA_N(char, strlen(siga) + 1); strcpy(*(sig + i), siga); } cls->method_signature = ALLOC_N(char, siglen + 1); *(cls->method_signature) = 0; for (i = 0; i < count; i++) { strcat(cls->method_signature, *(sig + i)); } } /* * create method info structure * m = instance of Method class * c = instance of the class */ static void setup_methodbase(JNIEnv* jenv, struct cls_constructor* pm, jobjectArray parama, jsize pcount) { pm->arg_count = pcount; pm->method_signature = NULL; pm->result_signature = 'O'; pm->result_arraydepth = 0; pm->arg_convert = NULL; if (pcount) { fill_convert(jenv, pm, parama, pcount); } } static void register_methodinfo(struct cls_method* newpm, st_table* tbl) { struct cls_method* pm; if (st_lookup(tbl, newpm->name, (st_data_t*)&pm)) { newpm->next = pm->next; pm->next = newpm; } else { newpm->next = NULL; st_insert(tbl, newpm->name, (VALUE)newpm); } } static struct cls_method* clone_methodinfo(struct cls_method* pm) { struct cls_method* result = ALLOC(struct cls_method); memcpy(result, pm, sizeof(struct cls_method)); return result; } static int make_alias(const char* jname, char* rname) { int ret = 0; while (*jname) { if (isupper(*jname)) { *rname++ = '_'; *rname++ = tolower(*jname++); ret = 1; } else { *rname++ = *jname++; } } *rname = '\0'; return ret; } static void create_methodinfo(JNIEnv* jenv, st_table* tbl, jobject m, int static_method) { struct cls_method* result; struct cls_method* pm; const char* jname; int alias; jstring nm; jobjectArray parama; jobject cls; jsize param_count; char* rname; result = ALLOC(struct cls_method); parama = (*jenv)->CallObjectMethod(jenv, m, getParameterTypes); rjb_check_exception(jenv, 0); param_count = (*jenv)->GetArrayLength(jenv, parama); rjb_check_exception(jenv, 0); setup_methodbase(jenv, &result->basic, parama, param_count); nm = (*jenv)->CallObjectMethod(jenv, m, method_getName); rjb_check_exception(jenv, 0); jname = (*jenv)->GetStringUTFChars(jenv, nm, NULL); rname = ALLOCA_N(char, strlen(jname) * 2 + 8); alias = make_alias(jname, rname); result->name = rb_intern(jname); rjb_release_string(jenv, nm, jname); result->basic.id = (*jenv)->FromReflectedMethod(jenv, m); rjb_check_exception(jenv, 0); cls = (*jenv)->CallObjectMethod(jenv, m, getReturnType); rjb_check_exception(jenv, 0); setup_j2r(jenv, cls, result, static_method); (*jenv)->DeleteLocalRef(jenv, cls); result->static_method = static_method; register_methodinfo(result, tbl); /* create method alias */ pm = NULL; if (strlen(rname) > 3 && (*rname == 'g' || *rname == 's') && *(rname + 1) == 'e' && *(rname + 2) == 't') { pm = clone_methodinfo(result); if (*rname == 's') { if (result->basic.arg_count == 1) { rname += 3; strcat(rname, "="); } } else { rname += 3; } if (*rname == '_') rname++; } else if (strlen(rname) > 2 && result->basic.result_signature == 'Z' && *rname == 'i' && *(rname + 1) == 's') { pm = clone_methodinfo(result); rname += 2; if (*rname == '_') rname++; strcat(rname, "?"); } else if (alias) { pm = clone_methodinfo(result); } if (pm) { pm->name = rb_intern(rname); register_methodinfo(pm, tbl); } } static void create_fieldinfo(JNIEnv* jenv, st_table* tbl, jobject f, int readonly, int static_field) { struct cls_field* result; const char* jname; jstring nm; jobject cls; char sigs[256]; off_t iv = 0; result = ALLOC(struct cls_field); memset(result, 0, sizeof(struct cls_field)); nm = (*jenv)->CallObjectMethod(jenv, f, field_getName); rjb_check_exception(jenv, 0); jname = (*jenv)->GetStringUTFChars(jenv, nm, NULL); result->name = rb_intern(jname); rjb_release_string(jenv, nm, jname); result->id = (*jenv)->FromReflectedField(jenv, f); rjb_check_exception(jenv, 0); cls = (*jenv)->CallObjectMethod(jenv, f, field_getType); rjb_check_exception(jenv, 0); result->value_convert = get_j2r(jenv, cls, &result->result_signature, &result->result_arraydepth, sigs, &iv, 0); result->arg_convert = get_r2j(jenv, cls, NULL, NULL); (*jenv)->DeleteLocalRef(jenv, cls); result->field_signature = ALLOC_N(char, strlen(sigs) + 1); strcpy(result->field_signature, sigs); if (!result->value_convert) result->value_convert = jv2rv; result->readonly = readonly; result->static_field = static_field; st_insert(tbl, result->name, (VALUE)result); } static void setup_constructors(JNIEnv* jenv, struct cls_constructor*** pptr, jobjectArray methods) { int i; struct cls_constructor* pc; jsize mcount = (*jenv)->GetArrayLength(jenv, methods); struct cls_constructor** tbl = ALLOC_N(struct cls_constructor*, mcount + 1); *pptr = tbl; for (i = 0; i < mcount; i++) { jobjectArray parama; jsize pcount; jobject c = (*jenv)->GetObjectArrayElement(jenv, methods, i); rjb_check_exception(jenv, 0); pc = ALLOC(struct cls_constructor); tbl[i] = pc; parama = (*jenv)->CallObjectMethod(jenv, c, ctrGetParameterTypes); rjb_check_exception(jenv, 0); pcount = (*jenv)->GetArrayLength(jenv, parama); rjb_check_exception(jenv, 0); setup_methodbase(jenv, pc, parama, pcount); pc->id = (*jenv)->FromReflectedMethod(jenv, c); (*jenv)->DeleteLocalRef(jenv, c); } tbl[mcount] = NULL; } static void setup_methods(JNIEnv* jenv, st_table** tbl, st_table** static_tbl, jobjectArray methods) { int i; jint modifier; jsize mcount = (*jenv)->GetArrayLength(jenv, methods); *tbl = st_init_numtable_with_size(mcount); *static_tbl = st_init_numtable(); for (i = 0; i < mcount; i++) { jobject m = (*jenv)->GetObjectArrayElement(jenv, methods, i); rjb_check_exception(jenv, 0); modifier = (*jenv)->CallIntMethod(jenv, m, method_getModifiers); if (!(modifier & ACC_STATIC)) { create_methodinfo(jenv, *tbl, m, 0); } else { create_methodinfo(jenv, *static_tbl, m, 1); } (*jenv)->DeleteLocalRef(jenv, m); } } static void setup_fields(JNIEnv* jenv, st_table** tbl, jobjectArray flds) { int i; jint modifier; jsize fcount = (*jenv)->GetArrayLength(jenv, flds); *tbl = st_init_numtable_with_size(fcount); for (i = 0; i < fcount; i++) { jobject f = (*jenv)->GetObjectArrayElement(jenv, flds, i); rjb_check_exception(jenv, 0); modifier = (*jenv)->CallIntMethod(jenv, f, field_getModifiers); create_fieldinfo(jenv, *tbl, f, modifier & ACC_FINAL, modifier & ACC_STATIC); (*jenv)->DeleteLocalRef(jenv, f); } } static void load_constants(JNIEnv* jenv, jclass klass, VALUE self, jobjectArray flds) { int i; jint modifier; jsize fcount = (*jenv)->GetArrayLength(jenv, flds); for (i = 0; i < fcount; i++) { jobject f = (*jenv)->GetObjectArrayElement(jenv, flds, i); rjb_check_exception(jenv, 0); modifier = (*jenv)->CallIntMethod(jenv, f, field_getModifiers); rjb_check_exception(jenv, 0); if ((modifier & (ACC_PUBLIC | ACC_STATIC | ACC_FINAL)) == (ACC_PUBLIC | ACC_STATIC | ACC_FINAL)) { jstring nm; const char* cname; jobject cls; char sig; char depth; off_t iv; J2R j2r; jvalue jv; jfieldID jfid; char sigs[256]; char* pname; /* constants make define directly in the ruby object */ cls = (*jenv)->CallObjectMethod(jenv, f, field_getType); rjb_check_exception(jenv, 0); iv = 0; sig = depth = 0; j2r = get_j2r(jenv, cls, &sig, &depth, sigs, &iv, 1); if (!j2r) j2r = jv2rv; (*jenv)->DeleteLocalRef(jenv, cls); nm = (*jenv)->CallObjectMethod(jenv, f, field_getName); rjb_check_exception(jenv, 0); cname = (*jenv)->GetStringUTFChars(jenv, nm, NULL); rjb_check_exception(jenv, 0); jfid = (*jenv)->GetStaticFieldID(jenv, klass, cname, sigs); rjb_check_exception(jenv, 0); switch (sig) { case 'D': jv.d = (*jenv)->GetStaticDoubleField(jenv, klass, jfid); break; case 'Z': jv.z = (*jenv)->GetStaticBooleanField(jenv, klass, jfid); break; case 'B': jv.b = (*jenv)->GetStaticByteField(jenv, klass, jfid); break; case 'F': jv.f = (*jenv)->GetStaticFloatField(jenv, klass, jfid); break; case 'C': jv.c = (*jenv)->GetStaticCharField(jenv, klass, jfid); break; case 'S': jv.s = (*jenv)->GetStaticShortField(jenv, klass, jfid); break; case 'J': jv.j = (*jenv)->GetStaticLongField(jenv, klass, jfid); break; case 'I': jv.i = (*jenv)->GetStaticIntField(jenv, klass, jfid); break; default: jv.l = (*jenv)->GetStaticObjectField(jenv, klass, jfid); break; } pname = (char*)cname; if (!isupper(*cname)) { pname = ALLOCA_N(char, strlen(cname) + 1); strcpy(pname, cname); *pname = toupper(*pname); if (!isupper(*pname) || rb_const_defined(rb_obj_class(self), rb_intern(pname))) { pname = NULL; } } if (pname) { rb_define_const(rb_obj_class(self), pname, j2r(jenv, jv)); } rjb_release_string(jenv, nm, cname); } (*jenv)->DeleteLocalRef(jenv, f); } } static void setup_metadata(JNIEnv* jenv, VALUE self, struct jv_data* ptr, VALUE classname) { jmethodID mid; jobjectArray methods; jobjectArray flds; jclass klass = (*jenv)->GetObjectClass(jenv, ptr->idata.obj); ptr->idata.klass = (*jenv)->NewGlobalRef(jenv, klass); rjb_check_exception(jenv, 0); mid = (*jenv)->GetMethodID(jenv, klass, "getMethods", "()[Ljava/lang/reflect/Method;"); rjb_check_exception(jenv, 0); methods = (*jenv)->CallNonvirtualObjectMethod(jenv, ptr->idata.obj, klass, mid); rjb_check_exception(jenv, 0); setup_methods(jenv, &ptr->idata.methods, &ptr->static_methods, methods); mid = (*jenv)->GetMethodID(jenv, klass, "getConstructors", "()[Ljava/lang/reflect/Constructor;"); rjb_check_exception(jenv, 0); methods = (*jenv)->CallNonvirtualObjectMethod(jenv, ptr->idata.obj, klass, mid); rjb_check_exception(jenv, 0); setup_constructors(jenv, &ptr->constructors, methods); mid = (*jenv)->GetMethodID(jenv, klass, "getFields", "()[Ljava/lang/reflect/Field;"); rjb_check_exception(jenv, 0); flds = (*jenv)->CallNonvirtualObjectMethod(jenv, ptr->idata.obj, klass, mid); rjb_check_exception(jenv, 0); setup_fields(jenv, &ptr->idata.fields, flds); register_class(self, classname); load_constants(jenv, ptr->idata.obj, self, flds); } /* * load Java Virtual Machine * def load(class_path = '', vmargs = []) * class_path: passes for the class dir and jar name * vmargs: strng array of vmarg (such as -Xrs) * * change in rjb 0.1.7, omit first argument for JNI version. * because I misunderstood the number means (JVM but JNI). */ static VALUE rjb_s_load(int argc, VALUE* argv, VALUE self) { JNIEnv* jenv; JavaVMInitArgs vm_args; jint res; VALUE classpath; VALUE user_path; VALUE vm_argv; char* userpath; ID stradd = rb_intern("<<"); ID pathsep = rb_intern("PATH_SEPARATOR"); int i; jclass jmethod; jclass jfield; jclass jconstructor; if (rjb_jvm) { return Qnil; } memset(&vm_args, 0, sizeof(vm_args)); vm_args.version = JNI_VERSION_1_4; rb_scan_args(argc, argv, "02", &user_path, &vm_argv); if (!NIL_P(user_path)) { Check_Type(user_path, T_STRING); } else { user_path = rb_str_new2("."); } classpath = rb_cvar_get(rjb, cvar_classpath); for (i = 0; i < RARRAY_LEN(classpath); i++) { rb_funcall(user_path, stradd, 1, rb_const_get(rb_cFile, pathsep)); rb_funcall(user_path, stradd, 1, rb_ary_entry(classpath, 0)); } userpath = StringValueCStr(user_path); if (!NIL_P(vm_argv)) { Check_Type(vm_argv, T_ARRAY); } jenv = NULL; res = rjb_create_jvm(&jenv, &vm_args, userpath, vm_argv); if (res < 0) { rjb_jvm = NULL; rb_raise(rb_eRuntimeError, "can't create Java VM"); } else { main_jenv = jenv; } RJB_FIND_CLASS(jconstructor, "java/lang/reflect/Constructor"); RJB_LOAD_METHOD(ctrGetParameterTypes, jconstructor, "getParameterTypes", "()[Ljava/lang/Class;"); RJB_FIND_CLASS(jmethod, "java/lang/reflect/Method"); RJB_LOAD_METHOD(method_getModifiers, jmethod, "getModifiers", "()I"); RJB_LOAD_METHOD(method_getName, jmethod, "getName", "()Ljava/lang/String;"); RJB_LOAD_METHOD(getParameterTypes, jmethod, "getParameterTypes", "()[Ljava/lang/Class;"); RJB_LOAD_METHOD(getReturnType, jmethod, "getReturnType", "()Ljava/lang/Class;"); rjb_check_exception(jenv, 1); RJB_FIND_CLASS(jfield, "java/lang/reflect/Field"); RJB_LOAD_METHOD(field_getModifiers, jfield, "getModifiers", "()I"); RJB_LOAD_METHOD(field_getName, jfield, "getName", "()Ljava/lang/String;"); RJB_LOAD_METHOD(field_getType, jfield, "getType", "()Ljava/lang/Class;"); rjb_check_exception(jenv, 1); RJB_HOLD_CLASS(j_class, "java/lang/Class"); RJB_LOAD_METHOD(rjb_class_getName, j_class, "getName", "()Ljava/lang/String;"); rjb_check_exception(jenv, 1); RJB_HOLD_CLASS(rjb_j_throwable, "java/lang/Throwable"); RJB_LOAD_METHOD(rjb_throwable_getMessage, rjb_j_throwable, "getMessage", "()Ljava/lang/String;"); rjb_check_exception(jenv, 1); RJB_HOLD_CLASS(j_string, "java/lang/String"); RJB_LOAD_METHOD(str_tostring, j_string, "toString", "()Ljava/lang/String;"); rjb_check_exception(jenv, 1); RJB_HOLD_CLASS(j_object, "java/lang/Object"); rjb_check_exception(jenv, 1); RJB_HOLD_CLASS(j_url, "java/net/URL"); RJB_LOAD_METHOD(url_new, j_url, "", "(Ljava/lang/String;)V"); rjb_check_exception(jenv, 1); for (i = PRM_INT; i < PRM_LAST; i++) { jclass klass; RJB_FIND_CLASS(klass, jpcvt[i].classname); if (i == PRM_BOOLEAN) { RJB_LOAD_STATIC_METHOD(jpcvt[i].ctr_id, klass, "valueOf", jpcvt[i].ctrsig); } else if (jpcvt[i].ctrsig) { RJB_LOAD_METHOD(jpcvt[i].ctr_id, klass, "", jpcvt[i].ctrsig); } RJB_LOAD_METHOD(jpcvt[i].to_prim_id, klass, jpcvt[i].to_prim_method, jpcvt[i].prmsig); jpcvt[i].klass = (*jenv)->NewGlobalRef(jenv, klass); } jklass = import_class(jenv, j_class, rb_str_new2("java.lang.Class")); rb_define_method(rb_singleton_class(jklass), "forName", rjb_class_forname, -1); rb_define_alias(rb_singleton_class(jklass), "for_name", "forName"); rb_gc_register_address(&jklass); return Qnil; } /* * load Java Virtual Machine with default arguments. */ VALUE rjb_load_vm_default() { if (rjb_jvm) return Qfalse; rb_warning("Rjb::implicit jvm loading"); return rjb_s_load(0, NULL, 0); } /* * common prelude */ JNIEnv* rjb_prelude() { JNIEnv* jenv = NULL; rjb_load_vm_default(); jenv = rjb_attach_current_thread(); (*jenv)->ExceptionClear(jenv); return jenv; } jobject get_systemloader(JNIEnv* jenv) { if (!j_classloader) { RJB_HOLD_CLASS(j_classloader, "java/lang/ClassLoader"); RJB_LOAD_STATIC_METHOD(get_system_classloader, j_classloader, "getSystemClassLoader", "()Ljava/lang/ClassLoader;"); rjb_check_exception(jenv, 1); } return (*jenv)->CallStaticObjectMethod(jenv, j_classloader, get_system_classloader); } static jobject get_class_loader(JNIEnv* jenv) { return (url_loader) ? url_loader : get_systemloader(jenv); } /* * unload Java Virtual Machine * * def unload() * classes.clear * unload(jvm) * end */ static int clear_classes(VALUE key, VALUE val, VALUE dummy) { return ST_DELETE; } static VALUE rjb_s_unload(int argc, VALUE* argv, VALUE self) { int result = 0; #if defined(HAVE_RB_HASH_FOREACH) || defined(RUBINIUS) rb_hash_foreach(rjb_loaded_classes, clear_classes, 0); #else #if defined(RHASH_TBL) st_foreach(RHASH_TBL(rjb_loaded_classes), clear_classes, 0); #else st_foreach(RHASH(rjb_loaded_classes)->tbl, clear_classes, 0); #endif #endif if (rjb_jvm) { JNIEnv* jenv = rjb_attach_current_thread(); (*jenv)->ExceptionClear(jenv); result = (*rjb_jvm)->DestroyJavaVM(rjb_jvm); rjb_jvm = NULL; rjb_unload_vm(); } return INT2NUM(result); } static VALUE rjb_s_loaded(VALUE self) { return (rjb_jvm) ? Qtrue : Qfalse; } /* * return all classes that were already loaded. * this method simply returns the global hash, * but it's safe because the hash was frozen. */ static VALUE rjb_s_classes(VALUE self) { return rjb_loaded_classes; } /** * For JRuby conpatible option */ static VALUE rjb_s_set_pconversion(VALUE self, VALUE val) { primitive_conversion = (RTEST(val)) ? Qtrue : Qfalse; return val; } /** * For JRuby conpatible option */ static VALUE rjb_s_get_pconversion(VALUE self) { return primitive_conversion; } /* * free java class */ #if 0 static void free_constructor(struct cls_constructor* p) { free(p->arg_convert); free(p->method_signature); } static int free_method_item(ID key, struct cls_method* pm, int dummy) { for (; pm; pm = pm->next) { free_constructor(&pm->basic); } return ST_CONTINUE; } #endif /* * finalize Object instance */ static VALUE rjb_delete_ref(struct jvi_data* ptr) { JNIEnv* jenv = rjb_attach_current_thread(); if (jenv) { (*jenv)->DeleteGlobalRef(jenv, ptr->obj); } return Qnil; } /* * finalize Bridge instance */ static VALUE rj_bridge_free(struct rj_bridge* ptr) { JNIEnv* jenv = rjb_attach_current_thread(); if (jenv) { (*jenv)->DeleteLocalRef(jenv, ptr->proxy); (*jenv)->DeleteLocalRef(jenv, ptr->bridge); } return Qnil; } /* * mark wrapped object in the Bridge */ static void rj_bridge_mark(struct rj_bridge* ptr) { rb_gc_mark(ptr->wrapped); } /* * finalize Class instance */ static VALUE rjb_s_free(struct jv_data* ptr) { /* class never delete JNIEnv* jenv = rjb_attach_current_thread(); struct cls_constructor** c; rjb_delete_ref(&ptr->idata); if (ptr->constructors) { for (c = ptr->constructors; *c; c++) { free_constructor(*c); } } free(ptr->constructors); if (ptr->idata.methods) { st_foreach(ptr->idata.methods, (int(*)())free_method_item, 0); st_free_table(ptr->idata.methods); } (*jenv)->DeleteGlobalRef(jenv, ptr->idata.klass); st_delete(RHASH(rjb_loaded_classes)->tbl, clsname, NULL); */ return Qnil; } /* * create new instance of this class */ static VALUE createinstance(JNIEnv* jenv, int argc, VALUE* argv, VALUE self, struct cls_constructor* pc) { int i; char* psig = pc->method_signature; jobject obj = NULL; VALUE result; struct jv_data* jklass; struct jvi_data* org; jvalue* args = (argc) ? ALLOCA_N(jvalue, argc) : NULL; Data_Get_Struct(self, struct jv_data, jklass); org = &jklass->idata; for (i = 0; i < argc; i++) { R2J pr2j = *(pc->arg_convert + i); pr2j(jenv, argv[i], args + i, psig, 0); psig = next_sig(psig); rjb_check_exception(jenv, 1); } obj = (*jenv)->NewObjectA(jenv, org->obj, pc->id, args); if (!obj) { rjb_check_exception(jenv, 1); } psig = pc->method_signature; for (i = 0; i < argc; i++) { R2J pr2j = *(pc->arg_convert + i); pr2j(jenv, argv[i], args + i, psig, 1); psig = next_sig(psig); } result = register_instance(jenv, self, jklass, obj); (*jenv)->DeleteLocalRef(jenv, obj); return result; } static VALUE import_class(JNIEnv* jenv, jclass jcls, VALUE clsname) { VALUE v; VALUE rexp; struct jv_data* ptr; char* pclsname = StringValueCStr(clsname); char* nm = ALLOCA_N(char, strlen(pclsname) + 1); strcpy(nm, pclsname); *nm = toupper(*nm); for (pclsname = nm; *pclsname; pclsname++) { if (*pclsname == '.') { *pclsname = '_'; } } rexp = rb_define_class_under(rjb, nm, rjbc); ptr = ALLOC(struct jv_data); memset(ptr, 0, sizeof(struct jv_data)); v = Data_Wrap_Struct(rexp, NULL, rjb_s_free, ptr); ptr->idata.obj = (*jenv)->NewGlobalRef(jenv, jcls); setup_metadata(jenv, v, ptr, clsname); return v; } static VALUE rjb_a_initialize(VALUE self, VALUE proc) { return rb_ivar_set(self, anonymousblock, proc); } static VALUE rjb_a_missing(int argc, VALUE* argv, VALUE self) { VALUE proc = rb_ivar_get(self, anonymousblock); return rb_funcall2(proc, id_call, argc, argv); } static VALUE rjb_i_prepare_proxy(VALUE self) { return rb_funcall(self, rb_intern("instance_eval"), 1, rb_str_new2("instance_eval(&" USER_INITIALIZE ")")); } static VALUE register_instance(JNIEnv* jenv, VALUE klass, struct jv_data* org, jobject obj) { volatile VALUE v; VALUE iproc; struct jvi_data* ptr = ALLOC(struct jvi_data); memset(ptr, 0, sizeof(struct jvi_data)); v = Data_Wrap_Struct(rjbi, NULL, rjb_delete_ref, ptr); ptr->klass = org->idata.obj; ptr->obj = (*jenv)->NewGlobalRef(jenv, obj); ptr->methods = org->idata.methods; ptr->fields = org->idata.fields; iproc = rb_ivar_get(klass, user_initialize); if (iproc != Qnil) { rb_ivar_set(v, user_initialize, iproc); rb_funcall(v, rb_intern("_prepare_proxy"), 0, 0); } rb_funcall(v, initialize_proxy, 0, 0); return v; } /* * temporary signature check * return !0 if found */ #define UNMATCHED 0 #define SATISFIED 1 #define SOSO 2 #define PREFERABLE 3 static int check_rtype(JNIEnv* jenv, VALUE* pv, char* p) { char* pcls = NULL; if (*p == 'L') { char* pt = strchr(p, ';'); if (pt) { size_t len = pt - p - 1; pcls = ALLOCA_N(char, len + 1); strncpy(pcls, p + 1, len); *(pcls + len) = '\0'; } } if (pcls && !strcmp("java.lang.Object", pcls)) { return SATISFIED; } switch (TYPE(*pv)) { case T_FIXNUM: if (strchr("IJ", *p)) return SOSO; return strchr("BCDFS", *p) != NULL; case T_BIGNUM: return strchr("BCDFIJS", *p) != NULL; case T_FLOAT: if (*p == 'D') return SOSO; if (*p == 'F') return SATISFIED; return UNMATCHED; case T_STRING: if (pcls && (!strcmp("java.lang.String", pcls) || !strcmp("java.lang.CharSequence", pcls))) { return PREFERABLE; } else if (*p == '[' && *(p + 1) == 'B') { return SATISFIED; } return UNMATCHED; case T_TRUE: case T_FALSE: return (*p == 'Z') ? SOSO : UNMATCHED; case T_ARRAY: return (*p == '[') ? SOSO : UNMATCHED; case T_DATA: if (IS_RJB_OBJECT(*pv) && pcls) { /* imported object */ jclass cls; struct jvi_data* ptr; int result = 0; if (!strcmp("java.lang.String", pcls)) return SATISFIED; Data_Get_Struct(*pv, struct jvi_data, ptr); RJB_FIND_CLASS(cls, java2jniname(pcls)); if (cls) { result = (cls && (*jenv)->IsInstanceOf(jenv, ptr->obj, cls)); (*jenv)->DeleteLocalRef(jenv, cls); } return (result) ? PREFERABLE : UNMATCHED; } else if (pcls) { VALUE blockobj = rb_class_new_instance(1, pv, rjba); *pv = rjb_s_bind(rjbb, blockobj, rb_str_new2(pcls)); } /* fall down to the next case */ case T_OBJECT: /* fall down to the next case */ default: if (pcls || *p == '[') { return SATISFIED; } return UNMATCHED; } } /* * new instance with signature */ static VALUE rjb_newinstance_s(int argc, VALUE* argv, VALUE self) { VALUE vsig, rest; char* sig; VALUE ret = Qnil; struct jv_data* ptr; int found = 0; JNIEnv* jenv = rjb_prelude(); rb_scan_args(argc, argv, "1*", &vsig, &rest); sig = StringValueCStr(vsig); Data_Get_Struct(self, struct jv_data, ptr); if (ptr->constructors) { struct cls_constructor** pc = ptr->constructors; for (pc = ptr->constructors; *pc; pc++) { if ((*pc)->arg_count == argc - 1 && !strcmp(sig, (*pc)->method_signature)) { found = 1; ret = createinstance(jenv, argc - 1, argv + 1, self, *pc); break; } } } if (!found) { rb_raise(rb_eRuntimeError, "Constructor not found"); } return ret; } static VALUE rjb_newinstance(int argc, VALUE* argv, VALUE self) { VALUE ret = Qnil; struct jv_data* ptr; struct cls_constructor** pc; struct cls_constructor** found_pc = NULL; int found = 0; int weight = 0; int cweight; JNIEnv* jenv = rjb_prelude(); Data_Get_Struct(self, struct jv_data, ptr); if (ptr->constructors) { int i; char* psig; for (pc = ptr->constructors; *pc; pc++) { found = 0; if ((*pc)->arg_count == argc) { found = 1; cweight = 0; psig = (*pc)->method_signature; for (i = 0; i < argc; i++) { int w = check_rtype(jenv, argv + i, psig); if (!w) { found = 0; break; } cweight += w; psig = next_sig(psig); } } if (found) { if (cweight > weight || weight == 0) { found_pc = pc; weight = cweight; } } } if (found_pc) { ret = createinstance(jenv, argc, argv, self, *found_pc); } } if (!found_pc) { rb_raise(rb_eRuntimeError, "Constructor not found"); } return ret; } /* * find java class using added classloader */ jclass rjb_find_class_by_name(JNIEnv* jenv, const char* name) { jclass cls; if (url_loader) { jvalue v; char* binname = ALLOCA_N(char, strlen(name) + 32); strcpy(binname, name); v.l = (*jenv)->NewStringUTF(jenv, jniname2java(binname)); cls = (*jenv)->CallObjectMethod(jenv, url_loader, rjb_load_class, v); (*jenv)->DeleteLocalRef(jenv, v.l); } else { cls = (*jenv)->FindClass(jenv, name); } return cls; } /* * find java class from classname */ jclass rjb_find_class(JNIEnv* jenv, VALUE name) { char* cname; char* jnicls; Check_Type(name, T_STRING); cname = StringValueCStr(name); jnicls = ALLOCA_N(char, strlen(cname) + 1); strcpy(jnicls, cname); return rjb_find_class_by_name(jenv, java2jniname(jnicls)); } /* * get specified method signature */ static VALUE get_signatures(VALUE mname, st_table* st) { VALUE ret; struct cls_method* pm; ID rmid = rb_to_id(mname); if (!st_lookup(st, rmid, (st_data_t*)&pm)) { const char* tname = rb_id2name(rmid); rb_raise(rb_eRuntimeError, "Fail: unknown method name `%s'", tname); } ret = rb_ary_new(); for (; pm; pm = pm->next) { if (pm->basic.method_signature) { rb_ary_push(ret, rb_str_new2(pm->basic.method_signature)); } else { rb_ary_push(ret, Qnil); } } return ret; } static VALUE rjb_get_signatures(VALUE self, VALUE mname) { struct jv_data* ptr; Data_Get_Struct(self, struct jv_data, ptr); return get_signatures(mname, ptr->idata.methods); } static VALUE rjb_get_static_signatures(VALUE self, VALUE mname) { struct jv_data* ptr; Data_Get_Struct(self, struct jv_data, ptr); return get_signatures(mname, ptr->static_methods); } static VALUE rjb_get_ctor_signatures(VALUE self) { VALUE ret; struct jv_data* ptr; struct cls_constructor** pc; Data_Get_Struct(self, struct jv_data, ptr); ret = rb_ary_new(); if (ptr->constructors) { for (pc = ptr->constructors; *pc; pc++) { const char* sig = (*pc)->method_signature; rb_ary_push(ret, rb_str_new2(sig ? sig : "")); } } return ret; } /* * jclass Rjb::bind(rbobj, interface_name) */ static VALUE rjb_s_bind(VALUE self, VALUE rbobj, VALUE itfname) { VALUE result = Qnil; jclass itf; JNIEnv* jenv = rjb_prelude(); itf = rjb_find_class(jenv, itfname); rjb_check_exception(jenv, 1); if (itf) { struct rj_bridge* ptr = ALLOC(struct rj_bridge); memset(ptr, 0, sizeof(struct rj_bridge)); ptr->bridge = (*jenv)->NewGlobalRef(jenv, (*jenv)->AllocObject(jenv, rjb_rbridge)); if (!ptr->bridge) { free(ptr); rjb_check_exception(jenv, 1); return Qnil; } ptr->proxy = (*jenv)->CallObjectMethod(jenv, ptr->bridge, rjb_register_bridge, itf); ptr->proxy = (*jenv)->NewGlobalRef(jenv, ptr->proxy); ptr->wrapped = rbobj; result = Data_Wrap_Struct(rjbb, rj_bridge_mark, rj_bridge_free, ptr); rb_ary_push(proxies, result); rb_ivar_set(result, rb_intern("@wrapped"), rbobj); } return result; } /* * Rjb's class is not Class but Object, so add class_eval for the Java class. */ static VALUE rjb_class_eval(int argc, VALUE* argv, VALUE self) { if (rb_block_given_p()) { rb_ivar_set(self, user_initialize, rb_block_proc()); } return self; } static VALUE rjb_s_impl(VALUE self) { VALUE obj; VALUE proc; rb_need_block(); proc = rb_block_proc(); obj = rb_class_new_instance(1, &proc, rjba); return rjb_s_bind(rjbb, obj, rb_funcall(self, rb_intern("name"), 0)); } /* * jclass Rjb::bind(rbobj, interface_name) */ static VALUE rjb_s_unbind(VALUE self, VALUE rbobj) { #if defined(RUBINIUS) return rb_funcall(proxies, rb_intern("delete"), 1, rbobj); #else return rb_ary_delete(proxies, rbobj); #endif } /* * Jclass Rjb::import(classname) */ static VALUE rjb_s_import(VALUE self, VALUE clsname) { JNIEnv* jenv; jclass jcls; VALUE v = rb_hash_aref(rjb_loaded_classes, clsname); if (v != Qnil) { return v; } jenv = rjb_prelude(); jcls = rjb_find_class(jenv, clsname); if (!jcls) { rjb_check_exception(jenv, 0); rb_raise(rb_eRuntimeError, "`%s' not found", StringValueCStr(clsname)); } v = import_class(jenv, jcls, clsname); return v; } static void register_class(VALUE self, VALUE clsname) { rb_define_singleton_method(self, "new", rjb_newinstance, -1); rb_define_singleton_method(self, "new_with_sig", rjb_newinstance_s, -1); rb_define_singleton_method(self, "class_eval", rjb_class_eval, -1); rb_define_singleton_method(self, "sigs", rjb_get_signatures, 1); rb_define_singleton_method(self, "static_sigs", rjb_get_static_signatures, 1); rb_define_singleton_method(self, "ctor_sigs", rjb_get_ctor_signatures, 0); rb_ivar_set(self, user_initialize, Qnil); /* * the hash was frozen, so it need to call st_ func directly. */ #if defined(HAVE_RB_HASH_ASET) || defined(RUBINIUS) rb_hash_aset(rjb_loaded_classes, clsname, self); #else #ifdef RHASH_TBL st_insert(RHASH_TBL(rjb_loaded_classes), clsname, self); #else st_insert(RHASH(rjb_loaded_classes)->tbl, clsname, self); #endif #endif } static jobject conv_jarname_to_url(JNIEnv* jenv, VALUE jarname) { jvalue arg; jobject url; #if defined(DOSISH) size_t len; #endif char* jarp; char* urlp; SafeStringValue(jarname); jarp = StringValueCStr(jarname); urlp = ALLOCA_N(char, strlen(jarp) + 32); if (strncmp(jarp, "http:", 5) && strncmp(jarp, "https:", 6)) { #if defined(DOSISH) if (strlen(jarp) > 1 && jarp[1] == ':') { sprintf(urlp, "file:///%s", jarp); } else #endif { sprintf(urlp, "file://%s", jarp); } } else { strcpy(urlp, jarp); } #if defined(DOSISH) for (len = 0; len < strlen(urlp); len++) { if (urlp[len] == '\\') { urlp[len] = '/'; } } #endif arg.l = (*jenv)->NewStringUTF(jenv, urlp); rjb_check_exception(jenv, 0); url = (*jenv)->NewObject(jenv, j_url, url_new, arg); rjb_check_exception(jenv, 0); return url; } /* * Rjb::add_classpath(jarname) */ static VALUE rjb_s_add_classpath(VALUE self, VALUE jarname) { VALUE cpath = rb_cvar_get(self, cvar_classpath); SafeStringValue(jarname); rb_ary_push(cpath, jarname); return cpath; } /* * Rjb::add_jar(jarname) */ static VALUE rjb_s_add_jar(VALUE self, VALUE jarname) { size_t i; JNIEnv* jenv; size_t count; jvalue args[2]; if (rb_type(jarname) != T_ARRAY) { SafeStringValue(jarname); count = 0; } else { count = RARRAY_LEN(jarname); } jenv = rjb_prelude(); if (!j_url_loader) { j_url_loader = (*jenv)->NewGlobalRef(jenv, (*jenv)->FindClass(jenv, "java/net/URLClassLoader")); RJB_LOAD_METHOD(rjb_load_class, j_url_loader, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); RJB_LOAD_METHOD(url_loader_new, j_url_loader, "", "([Ljava/net/URL;Ljava/lang/ClassLoader;)V"); RJB_LOAD_METHOD(url_geturls, j_url_loader, "getURLs", "()[Ljava/net/URL;"); RJB_LOAD_METHOD(url_add_url, j_url_loader, "addURL", "(Ljava/net/URL;)V"); } if (!url_loader) { args[0].l = (*jenv)->NewObjectArray(jenv, (jsize)((count == 0) ? 1 : count), j_url, NULL); rjb_check_exception(jenv, 0); if (!count) { (*jenv)->SetObjectArrayElement(jenv, args[0].l, 0, conv_jarname_to_url(jenv, jarname)); } else { for (i = 0; i < count; i++) { (*jenv)->SetObjectArrayElement(jenv, args[0].l, (jint)i, conv_jarname_to_url(jenv, rb_ary_entry(jarname, i))); } } rjb_check_exception(jenv, 0); args[1].l = get_class_loader(jenv); url_loader = (*jenv)->NewObjectA(jenv, j_url_loader, url_loader_new, args); rjb_check_exception(jenv, 0); (*jenv)->NewGlobalRef(jenv, url_loader); (*jenv)->DeleteLocalRef(jenv, args[0].l); } else { jvalue v; if (count) { for (i = 0; i < count; i++) { v.l = conv_jarname_to_url(jenv, rb_ary_entry(jarname, i)); (*jenv)->CallObjectMethod(jenv, url_loader, url_add_url, v); rjb_check_exception(jenv, 0); (*jenv)->DeleteLocalRef(jenv, v.l); } } else { v.l = conv_jarname_to_url(jenv, jarname); (*jenv)->CallObjectMethod(jenv, url_loader, url_add_url, v); rjb_check_exception(jenv, 0); (*jenv)->DeleteLocalRef(jenv, v.l); } } return Qtrue; } static VALUE rjb_s_urls(VALUE self) { JNIEnv* jenv; jvalue ret; if (!url_loader) return Qnil; jenv = rjb_prelude(); ret.l = (*jenv)->CallObjectMethod(jenv, url_loader, url_geturls); return jarray2rv(jenv, ret); } /* * return class name */ static VALUE rjb_i_class(VALUE self) { JNIEnv* jenv = rjb_attach_current_thread(); struct jvi_data* ptr; jstring nm; Data_Get_Struct(self, struct jvi_data, ptr); nm = (*jenv)->CallObjectMethod(jenv, ptr->klass, rjb_class_getName); rjb_check_exception(jenv, 0); return jstring2val(jenv, nm); } /* * invoker */ static VALUE getter(JNIEnv* jenv, struct cls_field* pf, struct jvi_data* ptr) { jvalue jv; switch (pf->result_signature) { case 'D': if (pf->static_field) { jv.d = (*jenv)->GetStaticDoubleField(jenv, ptr->klass, pf->id); } else { jv.d = (*jenv)->GetDoubleField(jenv, ptr->obj, pf->id); } break; case 'Z': if (pf->static_field) { jv.z = (*jenv)->GetStaticBooleanField(jenv, ptr->klass, pf->id); } else { jv.z = (*jenv)->GetBooleanField(jenv, ptr->obj, pf->id); } break; case 'B': if (pf->static_field) { jv.b = (*jenv)->GetStaticByteField(jenv, ptr->klass, pf->id); } else { jv.b = (*jenv)->GetByteField(jenv, ptr->obj, pf->id); } break; case 'F': if (pf->static_field) { jv.f = (*jenv)->GetStaticFloatField(jenv, ptr->klass, pf->id); } else { jv.f = (*jenv)->GetFloatField(jenv, ptr->obj, pf->id); } break; case 'C': if (pf->static_field) { jv.c = (*jenv)->GetStaticCharField(jenv, ptr->klass, pf->id); } else { jv.c = (*jenv)->GetCharField(jenv, ptr->obj, pf->id); } break; case 'S': if (pf->static_field) { jv.s = (*jenv)->GetStaticShortField(jenv, ptr->klass, pf->id); } else { jv.s = (*jenv)->GetShortField(jenv, ptr->obj, pf->id); } break; case 'J': if (pf->static_field) { jv.j = (*jenv)->GetStaticLongField(jenv, ptr->klass, pf->id); } else { jv.j = (*jenv)->GetLongField(jenv, ptr->obj, pf->id); } break; case 'I': if (pf->static_field) { jv.i = (*jenv)->GetStaticIntField(jenv, ptr->klass, pf->id); } else { jv.i = (*jenv)->GetIntField(jenv, ptr->obj, pf->id); } break; default: if (pf->static_field) { jv.l = (*jenv)->GetStaticObjectField(jenv, ptr->klass, pf->id); } else { jv.l = (*jenv)->GetObjectField(jenv, ptr->obj, pf->id); } break; } if (pf->result_arraydepth) { return ja2r(pf->value_convert, jenv, jv, pf->result_arraydepth); } else { return pf->value_convert(jenv, jv); } } static void setter(JNIEnv* jenv, struct cls_field* pf, struct jvi_data* ptr, VALUE val) { jvalue jv; pf->arg_convert(jenv, val, &jv, pf->field_signature, 0); switch (*pf->field_signature) { case 'D': if (pf->static_field) { (*jenv)->SetStaticDoubleField(jenv, ptr->klass, pf->id, jv.d); } else { (*jenv)->SetDoubleField(jenv, ptr->obj, pf->id, jv.d); } break; case 'Z': if (pf->static_field) { (*jenv)->SetStaticBooleanField(jenv, ptr->klass, pf->id, jv.z); } else { (*jenv)->SetBooleanField(jenv, ptr->obj, pf->id, jv.z); } break; case 'B': if (pf->static_field) { (*jenv)->SetStaticByteField(jenv, ptr->klass, pf->id, jv.b); } else { (*jenv)->SetByteField(jenv, ptr->obj, pf->id, jv.b); } break; case 'F': if (pf->static_field) { (*jenv)->SetStaticFloatField(jenv, ptr->klass, pf->id, jv.f); } else { (*jenv)->SetFloatField(jenv, ptr->obj, pf->id, jv.f); } break; case 'C': if (pf->static_field) { (*jenv)->SetStaticCharField(jenv, ptr->klass, pf->id, jv.c); } else { (*jenv)->SetCharField(jenv, ptr->obj, pf->id, jv.c); } break; case 'S': if (pf->static_field) { (*jenv)->SetStaticShortField(jenv, ptr->klass, pf->id, jv.s); } else { (*jenv)->SetShortField(jenv, ptr->obj, pf->id, jv.s); } break; case 'J': if (pf->static_field) { (*jenv)->SetStaticLongField(jenv, ptr->klass, pf->id, jv.j); } else { (*jenv)->SetLongField(jenv, ptr->obj, pf->id, jv.j); } break; case 'I': if (pf->static_field) { (*jenv)->SetStaticIntField(jenv, ptr->klass, pf->id, jv.i); } else { (*jenv)->SetIntField(jenv, ptr->obj, pf->id, jv.i); } break; default: if (pf->static_field) { (*jenv)->SetStaticObjectField(jenv, ptr->klass, pf->id, jv.l); } else { (*jenv)->SetObjectField(jenv, ptr->obj, pf->id, jv.l); } break; } pf->arg_convert(jenv, val, &jv, pf->field_signature, 1); } static VALUE invoke(JNIEnv* jenv, struct cls_method* pm, struct jvi_data* ptr, int argc, VALUE* argv, const char* sig) { int i, found, cweight; jvalue jv; jvalue* args; char* psig; struct cls_method* orgpm = pm; struct cls_method* prefer_pm = NULL; int weight = 0; if (rb_block_given_p()) { VALUE* pargs = ALLOCA_N(VALUE, argc + 1); memcpy(pargs, argv, argc * sizeof(VALUE)); *(pargs + argc) = rb_block_proc(); ++argc; argv = pargs; } for (; pm; pm = pm->next) { found = 0; if (argc == pm->basic.arg_count) { if (sig && pm->basic.method_signature) { if (!strcmp(sig, pm->basic.method_signature)) { found = 1; prefer_pm = pm; break; } } else { found = 1; cweight = 0; psig = pm->basic.method_signature; for (i = 0; i < argc; i++) { int w = check_rtype(jenv, argv + i, psig); if (!w) { found = 0; break; } cweight += w; psig = next_sig(psig); } } } if (found) { if (cweight > weight || weight == 0) { prefer_pm = pm; weight = cweight; } } } if (!prefer_pm) { const char* tname = rb_id2name(orgpm->name); if (sig) { rb_raise(rb_eRuntimeError, "Fail: unknown method name `%s(\'%s\')'", tname, sig); } else { rb_raise(rb_eRuntimeError, "Fail: unknown method name `%s'", tname); } } args = (argc) ? ALLOCA_N(jvalue, argc) : NULL; psig = prefer_pm->basic.method_signature; for (i = 0; i < argc; i++) { R2J pr2j = *(prefer_pm->basic.arg_convert + i); pr2j(jenv, argv[i], args + i, psig, 0); psig = next_sig(psig); } switch (prefer_pm->basic.result_signature) { case 'D': { INVOKEAD voked = *(INVOKEAD*)(((char*)*jenv) + prefer_pm->method); jv.d = voked(jenv, ptr->obj, prefer_pm->basic.id, args); } break; case 'Z': case 'B': { INVOKEAZ vokez = *(INVOKEAZ*)(((char*)*jenv) + prefer_pm->method); jv.z = vokez(jenv, ptr->obj, prefer_pm->basic.id, args); } break; case 'F': { INVOKEAF vokef = *(INVOKEAF*)(((char*)*jenv) + prefer_pm->method); jv.f = vokef(jenv, ptr->obj, prefer_pm->basic.id, args); } break; case 'C': case 'S': { INVOKEAS vokes = *(INVOKEAS*)(((char*)*jenv) + prefer_pm->method); jv.s = vokes(jenv, ptr->obj, prefer_pm->basic.id, args); } break; #if HAVE_LONG_LONG case 'J': { INVOKEAL vokel = *(INVOKEAL*)(((char*)*jenv) + prefer_pm->method); jv.j = vokel(jenv, ptr->obj, prefer_pm->basic.id, args); } break; #endif default: { INVOKEA voke = *(INVOKEA*)(((char*)*jenv) + prefer_pm->method); jv.l = voke(jenv, ptr->obj, prefer_pm->basic.id, args); } break; } rjb_check_exception(jenv, 1); psig = prefer_pm->basic.method_signature; for (i = 0; i < argc; i++) { R2J pr2j = *(prefer_pm->basic.arg_convert + i); pr2j(jenv, argv[i], args + i, psig, 1); psig = next_sig(psig); } if (prefer_pm->basic.result_arraydepth) { return ja2r(prefer_pm->result_convert, jenv, jv, prefer_pm->basic.result_arraydepth); } else { return prefer_pm->result_convert(jenv, jv); } } /* * Object invocation */ static VALUE invoke_by_instance(ID rmid, int argc, VALUE* argv, struct jvi_data* ptr, char* sig) { VALUE ret = Qnil; JNIEnv* jenv = rjb_attach_current_thread(); struct cls_field* pf; struct cls_method* pm; const char* tname = rb_id2name(rmid); if (argc == 0 && st_lookup(ptr->fields, rmid, (st_data_t*)&pf)) { ret = getter(jenv, pf, ptr); } else { if (argc == 1 && *(tname + strlen(tname) - 1) == '=') { char* fname = ALLOCA_N(char, strlen(tname) + 1); strcpy(fname, tname); fname[strlen(tname) - 1] = '\0'; if (st_lookup(ptr->fields, rb_intern(fname), (st_data_t*)&pf)) { setter(jenv, pf, ptr, *argv); return ret; } /* fall through for the setter alias name */ } if (st_lookup(ptr->methods, rmid, (st_data_t*)&pm)) { ret = invoke(jenv, pm, ptr, argc, argv, sig); } else { rb_raise(rb_eRuntimeError, "Fail: unknown method name `%s'", tname); } } return ret; } static VALUE get_signature(int* argc, VALUE* argv, VALUE* rmid) { VALUE vsig; rb_scan_args(*argc, argv, "1*", rmid, &vsig); if (*argc == 1) { ++*argc; vsig = Qnil; } else { vsig = *(argv + 1); } return vsig; } static VALUE rjb_i_invoke(int argc, VALUE* argv, VALUE self) { VALUE vsig, rmid; char* sig; struct jvi_data* ptr; vsig = get_signature(&argc, argv, &rmid); rmid = rb_to_id(rmid); sig = NIL_P(vsig) ? NULL : StringValueCStr(vsig); Data_Get_Struct(self, struct jvi_data, ptr); return invoke_by_instance(rmid, argc - 2, argv + 2, ptr, sig); } static VALUE rjb_i_missing(int argc, VALUE* argv, VALUE self) { struct jvi_data* ptr; ID rmid = rb_to_id(argv[0]); Data_Get_Struct(self, struct jvi_data, ptr); return invoke_by_instance(rmid, argc -1, argv + 1, ptr, NULL); } /* * Class invocation (first static method, then instance method) */ static VALUE invoke_by_class(ID rmid, int argc, VALUE* argv, struct jv_data* ptr, char* sig) { VALUE ret = Qnil; struct jv_data* clsptr; struct cls_field* pf; struct cls_method* pm; const char* tname = rb_id2name(rmid); JNIEnv* jenv = rjb_attach_current_thread(); Data_Get_Struct(jklass, struct jv_data, clsptr); if (argc == 0 && st_lookup(ptr->idata.fields, rmid, (st_data_t*)&pf)) { if (!pf->static_field) { rb_raise(rb_eRuntimeError, "instance field `%s' for class", tname); } ret = getter(jenv, pf, &ptr->idata); } else if (argc == 1 && *(tname + strlen(tname) - 1) == '=') { char* fname = ALLOCA_N(char, strlen(tname) + 1); strcpy(fname, tname); fname[strlen(tname) - 1] = '\0'; if (st_lookup(ptr->idata.fields, rb_intern(fname), (st_data_t*)&pf)) { if (!pf->static_field) { rb_raise(rb_eRuntimeError, "instance field `%s' for class", fname); } setter(jenv, pf, &ptr->idata, *argv); } else { rb_raise(rb_eRuntimeError, "Fail: unknown field name `%s'", fname); } } else if (st_lookup(ptr->static_methods, rmid, (st_data_t*)&pm)) { ret = invoke(jenv, pm, &ptr->idata, argc, argv, sig); } else if (st_lookup(clsptr->idata.methods, rmid, (st_data_t*)&pm)) { ret = invoke(jenv, pm, &ptr->idata, argc, argv, sig); } else { if (st_lookup(ptr->idata.methods, rmid, (st_data_t*)&pm)) { rb_raise(rb_eRuntimeError, "instance method `%s' for class", tname); } else { rb_raise(rb_eRuntimeError, "Fail: unknown method name `%s'", tname); } } return ret; } static VALUE rjb_invoke(int argc, VALUE* argv, VALUE self) { VALUE vsig, rmid; char* sig; struct jv_data* ptr; vsig = get_signature(&argc, argv, &rmid); rmid = rb_to_id(rmid); sig = NIL_P(vsig) ? NULL : StringValueCStr(vsig); Data_Get_Struct(self, struct jv_data, ptr); return invoke_by_class(rmid, argc - 2, argv + 2, ptr, sig); } static VALUE find_const(VALUE pv) { VALUE* p = (VALUE*)pv; return rb_const_get(*p, (ID)*(p + 1)); } static VALUE rjb_missing(int argc, VALUE* argv, VALUE self) { struct jv_data* ptr; ID rmid = rb_to_id(argv[0]); const char* rmname = rb_id2name(rmid); if (isupper(*rmname)) { VALUE r, args[2]; int state = 0; args[0] = rb_obj_class(self); args[1] = rmid; r = rb_protect(find_const, (VALUE)args, &state); if (!state) { return r; } } Data_Get_Struct(self, struct jv_data, ptr); return invoke_by_class(rmid, argc - 1, argv + 1, ptr, NULL); } /* * Class#forName entry. */ static VALUE rjb_class_forname(int argc, VALUE* argv, VALUE self) { if (argc == 1) { return rjb_s_import(self, *argv); } else { struct jv_data* ptr; ID rmid = rb_intern("forName"); Data_Get_Struct(self, struct jv_data, ptr); return invoke_by_class(rmid, argc, argv, ptr, NULL); } } /* * Class initializer called by Ruby while requiring this library */ void Init_rjbcore() { #if RJB_RUBY_VERSION_CODE < 190 #if defined(RUBINIUS) rb_require("iconv"); #else rb_protect((VALUE(*)(VALUE))rb_require, (VALUE)"iconv", NULL); #endif #endif rjb_loaded_classes = rb_hash_new(); #ifndef RUBINIUS OBJ_FREEZE(rjb_loaded_classes); #endif rb_global_variable(&rjb_loaded_classes); proxies = rb_ary_new(); rb_global_variable(&proxies); user_initialize = rb_intern(USER_INITIALIZE); initialize_proxy = rb_intern("initialize_proxy"); rjb = rb_define_module("Rjb"); rb_define_module_function(rjb, "load", rjb_s_load, -1); rb_define_module_function(rjb, "unload", rjb_s_unload, -1); rb_define_module_function(rjb, "loaded?", rjb_s_loaded, 0); rb_define_module_function(rjb, "import", rjb_s_import, 1); rb_define_module_function(rjb, "bind", rjb_s_bind, 2); rb_define_module_function(rjb, "unbind", rjb_s_unbind, 1); rb_define_module_function(rjb, "classes", rjb_s_classes, 0); rb_define_module_function(rjb, "throw", rjb_s_throw, -1); rb_define_module_function(rjb, "primitive_conversion=", rjb_s_set_pconversion, 1); rb_define_module_function(rjb, "primitive_conversion", rjb_s_get_pconversion, 0); rb_define_module_function(rjb, "add_classpath", rjb_s_add_classpath, 1); rb_define_module_function(rjb, "add_jar", rjb_s_add_jar, 1); rb_define_alias(rjb, "add_jars", "add_jar"); rb_define_module_function(rjb, "urls", rjb_s_urls, 0); rb_define_const(rjb, "VERSION", rb_str_new2(RJB_VERSION)); rb_define_class_variable(rjb, "@@classpath", rb_ary_new()); cvar_classpath = rb_intern("@@classpath"); /* Java class object */ rjbc = CLASS_NEW(rb_cObject, "Rjb_JavaClass"); rb_gc_register_address(&rjbc); rb_define_method(rjbc, "method_missing", rjb_missing, -1); rb_define_method(rjbc, "impl", rjb_s_impl, 0); rb_define_method(rjbc, "_invoke", rjb_invoke, -1); rb_define_method(rjbc, "_classname", rjb_i_class, 0); /* Java instance object */ rjbi = CLASS_NEW(rb_cObject, "Rjb_JavaProxy"); rb_gc_register_address(&rjbi); rb_define_method(rjbi, "method_missing", rjb_i_missing, -1); rb_define_method(rjbi, "_invoke", rjb_i_invoke, -1); rb_define_method(rjbi, "_classname", rjb_i_class, 0); rb_define_method(rjbi, "_prepare_proxy", rjb_i_prepare_proxy, 0); rb_define_alias(rjbi, "include", "extend"); /* Ruby-Java Bridge object */ rjbb = CLASS_NEW(rb_cObject, "Rjb_JavaBridge"); rb_gc_register_address(&rjbb); /* anonymous interface object */ rjba = CLASS_NEW(rb_cObject, "Rjb_AnonymousClass"); rb_gc_register_address(&rjba); rb_define_method(rjba, "initialize", rjb_a_initialize, 1); rb_define_method(rjba, "method_missing", rjb_a_missing, -1); anonymousblock = rb_intern("@anon_block"); id_call = rb_intern("call"); } VALUE rjb_safe_funcall(VALUE args) { VALUE* argp = (VALUE*)args; return rb_funcall2(*argp, *(argp + 1), (int)*(argp + 2), argp + 3); } /** Entry point from JavaVM through java.reflect.Proxy */ JNIEXPORT jobject JNICALL Java_jp_co_infoseek_hp_arton_rjb_RBridge_call (JNIEnv * jenv, jobject bridge, jstring name, jobject proxy, jobjectArray args) { int i; jvalue j; memset(&j, 0, sizeof(j)); for (i = 0; i < RARRAY_LEN(proxies); i++) { struct rj_bridge* ptr; VALUE val = RARRAY_PTR(proxies)[i]; Data_Get_Struct(val, struct rj_bridge, ptr); if ((*jenv)->IsSameObject(jenv, proxy, ptr->proxy)) { int sstat; VALUE result; VALUE* argv = NULL; int argc = 3; ID id = rb_to_id(jstring2val(jenv, name)); if (args) { int i; jsize js = (*jenv)->GetArrayLength(jenv, args); argc += (int)js; argv = ALLOCA_N(VALUE, argc); memset(argv, 0, sizeof(VALUE*) * argc); for (i = 3; i < argc; i++) { jobject f = (*jenv)->GetObjectArrayElement(jenv, args, i - 3); /* f will be release in jv2rv_withprim */ *(argv + i) = jv2rv_withprim(jenv, f); } } else { argv = ALLOCA_N(VALUE, argc + 1); memset(argv, 0, sizeof(VALUE*) * (argc + 1)); } *argv = ptr->wrapped; *(argv + 1) = id; *(argv + 2) = argc - 3; result = rb_protect(rjb_safe_funcall, (VALUE)argv, &sstat); rv2jobject(jenv, result, &j, NULL, 0); /* I can't delete this object... */ break; } } return j.l; }