Back to index

lightning-sunbird  0.9+nobinonly
jsj_field.c
Go to the documentation of this file.
00001 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is Mozilla Communicator client code, released
00017  * March 31, 1998.
00018  *
00019  * The Initial Developer of the Original Code is
00020  * Netscape Communications Corporation.
00021  * Portions created by the Initial Developer are Copyright (C) 1998
00022  * the Initial Developer. All Rights Reserved.
00023  *
00024  * Contributor(s):
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either of the GNU General Public License Version 2 or later (the "GPL"),
00028  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039  
00040 /*
00041  * This file is part of the Java-vendor-neutral implementation of LiveConnect
00042  *
00043  * It contains the code used to reflect Java fields as properties of
00044  * JavaObject objects and the code to access those fields.
00045  *
00046  */
00047 
00048 #include <stdlib.h>
00049 
00050 #include "jsj_private.h"      /* LiveConnect internals */
00051 
00052 /*
00053  * Add a single field, described by java_field, to the JavaMemberDescriptor
00054  * named by field_name within the given JavaClassDescriptor.
00055  *
00056  * Returns JS_TRUE on success. Otherwise, returns JS_FALSE and reports an error.
00057  */
00058 static JSBool
00059 add_java_field_to_class_descriptor(JSContext *cx,
00060                                    JNIEnv *jEnv,
00061                                    JavaClassDescriptor *class_descriptor, 
00062                                    jstring field_name_jstr,
00063                                    jobject java_field,  /* a java.lang.reflect.Field */
00064                                    jint modifiers)
00065 {
00066     jclass fieldType;
00067     jfieldID fieldID;
00068     jclass java_class;
00069 
00070     JSBool is_static_field;
00071     JavaMemberDescriptor *member_descriptor = NULL;
00072     const char *sig_cstr = NULL;
00073     const char *field_name = NULL;
00074     JavaSignature *signature = NULL;
00075     JavaFieldSpec *field_spec = NULL;
00076         
00077     is_static_field = modifiers & ACC_STATIC;
00078     if (is_static_field) {
00079         member_descriptor = jsj_GetJavaStaticMemberDescriptor(cx, jEnv, class_descriptor, field_name_jstr);
00080     } else {
00081         member_descriptor = jsj_GetJavaMemberDescriptor(cx, jEnv, class_descriptor, field_name_jstr);
00082     }
00083     if (!member_descriptor)
00084         goto error;
00085     
00086     field_spec = (JavaFieldSpec*)JS_malloc(cx, sizeof(JavaFieldSpec));
00087     if (!field_spec)
00088         goto error;
00089 
00090     field_spec->modifiers = modifiers;
00091 
00092     /* Get the Java class corresponding to the type of the field */
00093     fieldType = (*jEnv)->CallObjectMethod(jEnv, java_field, jlrField_getType);
00094     if (!fieldType) {
00095         jsj_UnexpectedJavaError(cx, jEnv,
00096                                 "Unable to determine type of field using"
00097                                 " java.lang.reflect.Field.getType()");
00098         goto error;
00099     }
00100     
00101     signature = jsj_GetJavaClassDescriptor(cx, jEnv, fieldType);
00102     (*jEnv)->DeleteLocalRef(jEnv, fieldType);
00103     if (!signature)
00104         goto error;
00105     field_spec->signature = signature;
00106 
00107     field_name = jsj_DupJavaStringUTF(cx, jEnv, field_name_jstr);
00108     if (!field_name)
00109         goto error;
00110     field_spec->name = field_name;
00111 
00112     /* Compute the JNI-style (string-based) signature of the field type */
00113     sig_cstr = jsj_ConvertJavaSignatureToString(cx, signature);
00114     if (!sig_cstr)
00115         goto error;
00116 
00117     /* Compute the JNI fieldID and cache it for quick field access */
00118     java_class = class_descriptor->java_class;
00119     if (is_static_field)
00120         fieldID = (*jEnv)->GetStaticFieldID(jEnv, java_class, field_name, sig_cstr);
00121     else
00122         fieldID = (*jEnv)->GetFieldID(jEnv, java_class, field_name, sig_cstr);
00123     if (!fieldID) {
00124        jsj_UnexpectedJavaError(cx, jEnv,
00125                            "Can't get Java field ID for class %s, field %s (sig=%s)",
00126                            class_descriptor->name, field_name, sig_cstr);
00127        goto error;
00128     }
00129     field_spec->fieldID = fieldID;
00130     
00131     JS_free(cx, (char*)sig_cstr);
00132     
00133     member_descriptor->field = field_spec;
00134 
00135     /* Success */
00136     return JS_TRUE;
00137 
00138 error:
00139     if (field_spec) {
00140         JS_FREE_IF(cx, (char*)field_spec->name);
00141         JS_free(cx, field_spec);
00142     }
00143     JS_FREE_IF(cx, (char*)sig_cstr);
00144     if (signature)
00145         jsj_ReleaseJavaClassDescriptor(cx, jEnv, signature);
00146     return JS_FALSE;
00147 }
00148 
00149 /*
00150  * Free up a JavaFieldSpec and all its resources.
00151  */
00152 void
00153 jsj_DestroyFieldSpec(JSContext *cx, JNIEnv *jEnv, JavaFieldSpec *field)
00154 {
00155     JS_FREE_IF(cx, (char*)field->name);
00156     jsj_ReleaseJavaClassDescriptor(cx, jEnv, field->signature);
00157     JS_free(cx, field);
00158 }
00159 
00160 /*
00161  * Add a JavaMemberDescriptor to the collection of members in class_descriptor
00162  * for every public field of the identified Java class.  (A separate collection
00163  * is kept in class_descriptor for static and instance members.)
00164  * If reflect_only_static_fields is set, instance fields are not reflected.  If
00165  * it isn't set, only instance fields are reflected and static fields are not
00166  * reflected.
00167  *
00168  * Returns JS_TRUE on success. Otherwise, returns JS_FALSE and reports an error.
00169  */
00170 JSBool 
00171 jsj_ReflectJavaFields(JSContext *cx, JNIEnv *jEnv, JavaClassDescriptor *class_descriptor,
00172                       JSBool reflect_only_static_fields)
00173 {
00174     int i;
00175     JSBool ok;
00176     jint modifiers;
00177     jobject java_field;
00178     jstring field_name_jstr;
00179     jarray joFieldArray;
00180     jsize num_fields;
00181     jclass java_class;
00182 
00183     /* Get a java array of java.lang.reflect.Field objects, by calling
00184        java.lang.Class.getFields(). */
00185     java_class = class_descriptor->java_class;
00186     joFieldArray = (*jEnv)->CallObjectMethod(jEnv, java_class, jlClass_getFields);
00187     if (!joFieldArray) {
00188         jsj_UnexpectedJavaError(cx, jEnv,
00189                                 "Can't determine Java object's fields "
00190                                 "using java.lang.Class.getFields()");
00191         return JS_FALSE;
00192     }
00193 
00194     /* Iterate over the class fields */
00195     num_fields = (*jEnv)->GetArrayLength(jEnv, joFieldArray);
00196     for (i = 0; i < num_fields; i++) {
00197        
00198         /* Get the i'th reflected field */
00199         java_field = (*jEnv)->GetObjectArrayElement(jEnv, joFieldArray, i);
00200         if (!java_field) {
00201             jsj_UnexpectedJavaError(cx, jEnv, "Can't access a Field[] array");
00202             return JS_FALSE;
00203         }
00204 
00205         /* Get the field modifiers, e.g. static, public, private, etc. */
00206         modifiers = (*jEnv)->CallIntMethod(jEnv, java_field, jlrField_getModifiers);
00207         if ((*jEnv)->ExceptionOccurred(jEnv)) {
00208             jsj_UnexpectedJavaError(cx, jEnv,
00209                                     "Can't access a Field's modifiers using"
00210                                     "java.lang.reflect.Field.getModifiers()");
00211             return JS_FALSE;
00212         }
00213 
00214         /* Don't allow access to private or protected Java fields. */
00215         if (!(modifiers & ACC_PUBLIC))
00216             goto no_reflect;
00217 
00218         /* Reflect all instance fields or all static fields, but not both */
00219         if (reflect_only_static_fields != ((modifiers & ACC_STATIC) != 0))
00220             goto no_reflect;
00221 
00222         /* Determine the unqualified name of the field */
00223         field_name_jstr = (*jEnv)->CallObjectMethod(jEnv, java_field, jlrField_getName);
00224         if (!field_name_jstr) {
00225             jsj_UnexpectedJavaError(cx, jEnv,
00226                                     "Can't obtain a Field's name"
00227                                     "java.lang.reflect.Field.getName()");
00228             return JS_FALSE;
00229         }
00230         
00231         /* Add a JavaFieldSpec object to the JavaClassDescriptor */
00232         ok = add_java_field_to_class_descriptor(cx, jEnv, class_descriptor, field_name_jstr,
00233                                                 java_field, modifiers);
00234         if (!ok)
00235             return JS_FALSE;
00236 
00237         (*jEnv)->DeleteLocalRef(jEnv, field_name_jstr);
00238         field_name_jstr = NULL;
00239 
00240 no_reflect:
00241         (*jEnv)->DeleteLocalRef(jEnv, java_field);
00242         java_field = NULL;
00243     }
00244 
00245     (*jEnv)->DeleteLocalRef(jEnv, joFieldArray);
00246 
00247     /* Success */
00248     return JS_TRUE;
00249 }
00250 
00251 /*
00252  * Read the value of a Java field and return it as a JavaScript value.
00253  * If the field is static, then java_obj is a Java class, otherwise
00254  * it's a Java instance object.
00255  *
00256  * Returns JS_TRUE on success. Otherwise, returns JS_FALSE and reports an error.
00257  */
00258 JSBool
00259 jsj_GetJavaFieldValue(JSContext *cx, JNIEnv *jEnv, JavaFieldSpec *field_spec,
00260                       jobject java_obj, jsval *vp)
00261 {
00262     JSBool is_static_field, success;
00263     jvalue java_value;
00264     JavaSignature *signature;
00265     JavaSignatureChar field_type;
00266     jfieldID fieldID = field_spec->fieldID;
00267 
00268     is_static_field = field_spec->modifiers & ACC_STATIC;
00269 
00270 #define GET_JAVA_FIELD(Type,member)                                          \
00271     JS_BEGIN_MACRO                                                           \
00272     if (is_static_field)                                                     \
00273         java_value.member =                                                  \
00274             (*jEnv)->GetStatic##Type##Field(jEnv, (*jEnv)->GetObjectClass(jEnv, java_obj), fieldID);        \
00275     else                                                                     \
00276         java_value.member =                                                  \
00277             (*jEnv)->Get##Type##Field(jEnv, java_obj, fieldID);              \
00278     if ((*jEnv)->ExceptionOccurred(jEnv)) {                                  \
00279         jsj_UnexpectedJavaError(cx, jEnv, "Error reading Java field");           \
00280         return JS_FALSE;                                                     \
00281     }                                                                        \
00282     JS_END_MACRO
00283 
00284     signature = field_spec->signature;
00285     field_type = signature->type;
00286     switch(field_type) {
00287     case JAVA_SIGNATURE_BYTE:
00288         GET_JAVA_FIELD(Byte,b);
00289         break;
00290 
00291     case JAVA_SIGNATURE_CHAR:
00292         GET_JAVA_FIELD(Char,c);
00293         break;
00294 
00295     case JAVA_SIGNATURE_SHORT:
00296         GET_JAVA_FIELD(Short,s);
00297         break;
00298 
00299     case JAVA_SIGNATURE_INT:
00300         GET_JAVA_FIELD(Int,i);
00301         break;
00302 
00303     case JAVA_SIGNATURE_BOOLEAN:
00304         GET_JAVA_FIELD(Boolean,z);
00305         break;
00306 
00307     case JAVA_SIGNATURE_LONG:
00308         GET_JAVA_FIELD(Long,j);
00309         break;
00310   
00311     case JAVA_SIGNATURE_FLOAT:
00312         GET_JAVA_FIELD(Float,f);
00313         break;
00314 
00315     case JAVA_SIGNATURE_DOUBLE:
00316         GET_JAVA_FIELD(Double,d);
00317         break;
00318      
00319     case JAVA_SIGNATURE_UNKNOWN:
00320     case JAVA_SIGNATURE_VOID:
00321         JS_ASSERT(0);        /* Unknown java type signature */
00322         return JS_FALSE;
00323         
00324     /* Non-primitive (reference) type */
00325     default:
00326         JS_ASSERT(IS_REFERENCE_TYPE(field_type));
00327         GET_JAVA_FIELD(Object,l);
00328         success = jsj_ConvertJavaObjectToJSValue(cx, jEnv, java_value.l, vp);
00329         (*jEnv)->DeleteLocalRef(jEnv, java_value.l);
00330         return success;
00331     }
00332     
00333 #undef GET_JAVA_FIELD
00334 
00335     return jsj_ConvertJavaValueToJSValue(cx, jEnv, signature, &java_value, vp);
00336 }
00337 
00338 JSBool
00339 jsj_SetJavaFieldValue(JSContext *cx, JNIEnv *jEnv, JavaFieldSpec *field_spec,
00340                       jclass java_obj, jsval js_val)
00341 {
00342     JSBool is_static_field, is_local_ref;
00343     int dummy_cost;
00344     jvalue java_value;
00345     JavaSignature *signature;
00346     JavaSignatureChar field_type;
00347     jfieldID fieldID = field_spec->fieldID;
00348 
00349     is_static_field = field_spec->modifiers & ACC_STATIC;
00350 
00351 #define SET_JAVA_FIELD(Type,member)                                          \
00352     JS_BEGIN_MACRO                                                           \
00353     if (is_static_field) {                                                   \
00354         (*jEnv)->SetStatic##Type##Field(jEnv, java_obj, fieldID,             \
00355                                         java_value.member);                  \
00356     } else {                                                                 \
00357         (*jEnv)->Set##Type##Field(jEnv, java_obj, fieldID,java_value.member);\
00358     }                                                                        \
00359     if ((*jEnv)->ExceptionOccurred(jEnv)) {                                  \
00360         jsj_UnexpectedJavaError(cx, jEnv, "Error assigning to Java field");      \
00361         return JS_FALSE;                                                     \
00362     }                                                                        \
00363     JS_END_MACRO
00364 
00365     signature = field_spec->signature;
00366     if (!jsj_ConvertJSValueToJavaValue(cx, jEnv, js_val, signature, &dummy_cost,
00367                                        &java_value, &is_local_ref))
00368         return JS_FALSE;
00369 
00370     field_type = signature->type;
00371     switch(field_type) {
00372     case JAVA_SIGNATURE_BYTE:
00373         SET_JAVA_FIELD(Byte,b);
00374         break;
00375 
00376     case JAVA_SIGNATURE_CHAR:
00377         SET_JAVA_FIELD(Char,c);
00378         break;
00379 
00380     case JAVA_SIGNATURE_SHORT:
00381         SET_JAVA_FIELD(Short,s);
00382         break;
00383 
00384     case JAVA_SIGNATURE_INT:
00385         SET_JAVA_FIELD(Int,i);
00386         break;
00387 
00388     case JAVA_SIGNATURE_BOOLEAN:
00389         SET_JAVA_FIELD(Boolean,z);
00390         break;
00391 
00392     case JAVA_SIGNATURE_LONG:
00393         SET_JAVA_FIELD(Long,j);
00394         break;
00395   
00396     case JAVA_SIGNATURE_FLOAT:
00397         SET_JAVA_FIELD(Float,f);
00398         break;
00399 
00400     case JAVA_SIGNATURE_DOUBLE:
00401         SET_JAVA_FIELD(Double,d);
00402         break;
00403         
00404     /* Non-primitive (reference) type */
00405     default:
00406         JS_ASSERT(IS_REFERENCE_TYPE(field_type));
00407         SET_JAVA_FIELD(Object,l);
00408         if (is_local_ref)
00409             (*jEnv)->DeleteLocalRef(jEnv, java_value.l);
00410         break;
00411 
00412     case JAVA_SIGNATURE_UNKNOWN:
00413     case JAVA_SIGNATURE_VOID:
00414         JS_ASSERT(0);        /* Unknown java type signature */
00415         return JS_FALSE;
00416     }
00417 
00418 #undef SET_JAVA_FIELD
00419     
00420     return JS_TRUE;
00421 }