ext/rjb.c in rjb-1.4.3 vs ext/rjb.c in rjb-1.4.5

- old
+ new

@@ -1,3299 +1,3317 @@ -/* - * 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 198 2012-11-13 15:55:45Z arton $ - */ - -#define RJB_VERSION "1.4.3" - -#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 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; - ary = (*jenv)->NewObjectArray(jenv, (jint)RARRAY_LEN(v), 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(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, "<init>", "(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, "<init>", 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) -{ - 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 - */ -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 1; - } - switch (TYPE(*pv)) - { - case T_FIXNUM: - return strchr("BCDFIJS", *p) != NULL; - case T_FLOAT: - return strchr("DF", *p) != NULL; - case T_STRING: - return pcls && !strcmp("java.lang.String", pcls) || *p == '[' && *(p + 1) == 'B'; - case T_TRUE: - case T_FALSE: - return *p == 'Z'; - case T_ARRAY: - return *p == '['; - 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 1; - 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; - } 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 1; - } - return 0; - } -} - -/* - * 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; - int found = 0; - 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++) - { - if ((*pc)->arg_count == argc) - { - found = 1; - psig = (*pc)->method_signature; - for (i = 0; i < argc; i++) - { - if (!check_rtype(jenv, argv + i, psig)) - { - found = 0; - break; - } - psig = next_sig(psig); - } - if (found) - { - ret = createinstance(jenv, argc, argv, self, *pc); - break; - } - } - } - } - if (!found) { - 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++) - { - rb_ary_push(ret, rb_str_new2((*pc)->method_signature)); - } - } - 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; - size_t len; - 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, "<init>", - "([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; - jvalue jv; - jvalue* args; - char* psig; - struct cls_method* orgpm = pm; - - 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 (found = 0; pm; pm = pm->next) - { - if (argc == pm->basic.arg_count) - { - if (sig) - { - if (!strcmp(sig, pm->basic.method_signature)) - { - found = 1; - break; - } - } - else - { - psig = pm->basic.method_signature; - found = 1; - for (i = 0; i < argc; i++) - { - if (!check_rtype(jenv, argv + i, psig)) - { - found = 0; - break; - } - psig = next_sig(psig); - } - if (found) break; - } - } - } - if (!found) - { - 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 = pm->basic.method_signature; - for (i = 0; i < argc; i++) - { - R2J pr2j = *(pm->basic.arg_convert + i); - pr2j(jenv, argv[i], args + i, psig, 0); - psig = next_sig(psig); - } - switch (pm->basic.result_signature) - { - case 'D': - { - INVOKEAD voked = *(INVOKEAD*)(((char*)*jenv) + pm->method); - jv.d = voked(jenv, ptr->obj, pm->basic.id, args); - } - break; - case 'Z': - case 'B': - { - INVOKEAZ vokez = *(INVOKEAZ*)(((char*)*jenv) + pm->method); - jv.z = vokez(jenv, ptr->obj, pm->basic.id, args); - } - break; - case 'F': - { - INVOKEAF vokef = *(INVOKEAF*)(((char*)*jenv) + pm->method); - jv.f = vokef(jenv, ptr->obj, pm->basic.id, args); - } - break; - case 'C': - case 'S': - { - INVOKEAS vokes = *(INVOKEAS*)(((char*)*jenv) + pm->method); - jv.s = vokes(jenv, ptr->obj, pm->basic.id, args); - } - break; -#if HAVE_LONG_LONG - case 'J': - { - INVOKEAL vokel = *(INVOKEAL*)(((char*)*jenv) + pm->method); - jv.j = vokel(jenv, ptr->obj, pm->basic.id, args); - } - break; -#endif - default: - { - INVOKEA voke = *(INVOKEA*)(((char*)*jenv) + pm->method); - jv.l = voke(jenv, ptr->obj, pm->basic.id, args); - } - break; - } - rjb_check_exception(jenv, 1); - psig = pm->basic.method_signature; - for (i = 0; i < argc; i++) - { - R2J pr2j = *(pm->basic.arg_convert + i); - pr2j(jenv, argv[i], args + i, psig, 1); - psig = next_sig(psig); - } - if (pm->basic.result_arraydepth) - { - return ja2r(pm->result_convert, jenv, jv, pm->basic.result_arraydepth); - } - else - { - return 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 rjb_i_invoke(int argc, VALUE* argv, VALUE self) -{ - VALUE vsig, rmid, rest; - char* sig; - struct jvi_data* ptr; - - rb_scan_args(argc, argv, "2*", &rmid, &vsig, &rest); - rmid = rb_to_id(rmid); - sig = 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, rest; - char* sig; - struct jv_data* ptr; - - rb_scan_args(argc, argv, "2*", &rmid, &vsig, &rest); - rmid = rb_to_id(rmid); - sig = 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 call_blcock(int argc, VALUE* argv) -{ - return rb_yield_values2(argc, argv); -} - -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; -} +/* + * 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.4.5" + +#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 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; + ary = (*jenv)->NewObjectArray(jenv, (jint)RARRAY_LEN(v), 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(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, "<init>", "(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, "<init>", 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) +{ + 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 + */ +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 1; + } + switch (TYPE(*pv)) + { + case T_FIXNUM: + return strchr("BCDFIJS", *p) != NULL; + case T_BIGNUM: + return strchr("BCDFIJS", *p) != NULL; + case T_FLOAT: + return strchr("DF", *p) != NULL; + case T_STRING: + return pcls && !strcmp("java.lang.String", pcls) || *p == '[' && *(p + 1) == 'B'; + case T_TRUE: + case T_FALSE: + return *p == 'Z'; + case T_ARRAY: + return *p == '['; + 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 1; + 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; + } 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 1; + } + return 0; + } +} + +/* + * 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; + int found = 0; + 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++) + { + if ((*pc)->arg_count == argc) + { + found = 1; + psig = (*pc)->method_signature; + for (i = 0; i < argc; i++) + { + if (!check_rtype(jenv, argv + i, psig)) + { + found = 0; + break; + } + psig = next_sig(psig); + } + if (found) + { + ret = createinstance(jenv, argc, argv, self, *pc); + break; + } + } + } + } + if (!found) { + 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++) + { + rb_ary_push(ret, rb_str_new2((*pc)->method_signature)); + } + } + 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; + size_t len; + 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, "<init>", + "([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; + jvalue jv; + jvalue* args; + char* psig; + struct cls_method* orgpm = pm; + + 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 (found = 0; pm; pm = pm->next) + { + if (argc == pm->basic.arg_count) + { + if (sig && pm->basic.method_signature) + { + if (!strcmp(sig, pm->basic.method_signature)) + { + found = 1; + break; + } + } + else + { + psig = pm->basic.method_signature; + found = 1; + for (i = 0; i < argc; i++) + { + if (!check_rtype(jenv, argv + i, psig)) + { + found = 0; + break; + } + psig = next_sig(psig); + } + if (found) break; + } + } + } + if (!found) + { + 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 = pm->basic.method_signature; + for (i = 0; i < argc; i++) + { + R2J pr2j = *(pm->basic.arg_convert + i); + pr2j(jenv, argv[i], args + i, psig, 0); + psig = next_sig(psig); + } + switch (pm->basic.result_signature) + { + case 'D': + { + INVOKEAD voked = *(INVOKEAD*)(((char*)*jenv) + pm->method); + jv.d = voked(jenv, ptr->obj, pm->basic.id, args); + } + break; + case 'Z': + case 'B': + { + INVOKEAZ vokez = *(INVOKEAZ*)(((char*)*jenv) + pm->method); + jv.z = vokez(jenv, ptr->obj, pm->basic.id, args); + } + break; + case 'F': + { + INVOKEAF vokef = *(INVOKEAF*)(((char*)*jenv) + pm->method); + jv.f = vokef(jenv, ptr->obj, pm->basic.id, args); + } + break; + case 'C': + case 'S': + { + INVOKEAS vokes = *(INVOKEAS*)(((char*)*jenv) + pm->method); + jv.s = vokes(jenv, ptr->obj, pm->basic.id, args); + } + break; +#if HAVE_LONG_LONG + case 'J': + { + INVOKEAL vokel = *(INVOKEAL*)(((char*)*jenv) + pm->method); + jv.j = vokel(jenv, ptr->obj, pm->basic.id, args); + } + break; +#endif + default: + { + INVOKEA voke = *(INVOKEA*)(((char*)*jenv) + pm->method); + jv.l = voke(jenv, ptr->obj, pm->basic.id, args); + } + break; + } + rjb_check_exception(jenv, 1); + psig = pm->basic.method_signature; + for (i = 0; i < argc; i++) + { + R2J pr2j = *(pm->basic.arg_convert + i); + pr2j(jenv, argv[i], args + i, psig, 1); + psig = next_sig(psig); + } + if (pm->basic.result_arraydepth) + { + return ja2r(pm->result_convert, jenv, jv, pm->basic.result_arraydepth); + } + else + { + return 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 call_blcock(int argc, VALUE* argv) +{ + return rb_yield_values2(argc, argv); +} + +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; +}