Back to index

lightning-sunbird  0.9+nobinonly
nsJavaXPCOMBindingUtils.cpp
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is Java XPCOM Bindings.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * IBM Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 2005
00019  * IBM Corporation. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *   Javier Pedemonte (jhpedemonte@gmail.com)
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 
00039 #include "nsJavaXPCOMBindingUtils.h"
00040 #include "nsJavaXPTCStub.h"
00041 #include "nsJavaWrapper.h"
00042 #include "jni.h"
00043 #include "nsIInterfaceInfoManager.h"
00044 #include "nsILocalFile.h"
00045 #include "nsEventQueueUtils.h"
00046 #include "nsProxyRelease.h"
00047 
00048 
00049 /* Java JNI globals */
00050 
00051 JavaVM* gCachedJVM = nsnull;
00052 
00053 jclass systemClass = nsnull;
00054 jclass booleanClass = nsnull;
00055 jclass charClass = nsnull;
00056 jclass byteClass = nsnull;
00057 jclass shortClass = nsnull;
00058 jclass intClass = nsnull;
00059 jclass longClass = nsnull;
00060 jclass floatClass = nsnull;
00061 jclass doubleClass = nsnull;
00062 jclass stringClass = nsnull;
00063 jclass nsISupportsClass = nsnull;
00064 jclass xpcomExceptionClass = nsnull;
00065 jclass xpcomJavaProxyClass = nsnull;
00066 jclass weakReferenceClass = nsnull;
00067 jclass javaXPCOMUtilsClass = nsnull;
00068 
00069 jmethodID hashCodeMID = nsnull;
00070 jmethodID booleanValueMID = nsnull;
00071 jmethodID booleanInitMID = nsnull;
00072 jmethodID charValueMID = nsnull;
00073 jmethodID charInitMID = nsnull;
00074 jmethodID byteValueMID = nsnull;
00075 jmethodID byteInitMID = nsnull;
00076 jmethodID shortValueMID = nsnull;
00077 jmethodID shortInitMID = nsnull;
00078 jmethodID intValueMID = nsnull;
00079 jmethodID intInitMID = nsnull;
00080 jmethodID longValueMID = nsnull;
00081 jmethodID longInitMID = nsnull;
00082 jmethodID floatValueMID = nsnull;
00083 jmethodID floatInitMID = nsnull;
00084 jmethodID doubleValueMID = nsnull;
00085 jmethodID doubleInitMID = nsnull;
00086 jmethodID createProxyMID = nsnull;
00087 jmethodID isXPCOMJavaProxyMID = nsnull;
00088 jmethodID getNativeXPCOMInstMID = nsnull;
00089 jmethodID weakReferenceConstructorMID = nsnull;
00090 jmethodID getReferentMID = nsnull;
00091 jmethodID clearReferentMID = nsnull;
00092 jmethodID findClassInLoaderMID = nsnull;
00093 
00094 #ifdef DEBUG_JAVAXPCOM
00095 jmethodID getNameMID = nsnull;
00096 jmethodID proxyToStringMID = nsnull;
00097 #endif
00098 
00099 NativeToJavaProxyMap* gNativeToJavaProxyMap = nsnull;
00100 JavaToXPTCStubMap* gJavaToXPTCStubMap = nsnull;
00101 
00102 PRBool gJavaXPCOMInitialized = PR_FALSE;
00103 PRLock* gJavaXPCOMLock = nsnull;
00104 
00105 static const char* kJavaKeywords[] = {
00106   "abstract", "default"  , "if"        , "private"     , "throw"       ,
00107   "boolean" , "do"       , "implements", "protected"   , "throws"      ,
00108   "break"   , "double"   , "import",     "public"      , "transient"   ,
00109   "byte"    , "else"     , "instanceof", "return"      , "try"         ,
00110   "case"    , "extends"  , "int"       , "short"       , "void"        ,
00111   "catch"   , "final"    , "interface" , "static"      , "volatile"    ,
00112   "char"    , "finally"  , "long"      , "super"       , "while"       ,
00113   "class"   , "float"    , "native"    , "switch"      ,
00114   "const"   , "for"      , "new"       , "synchronized",
00115   "continue", "goto"     , "package"   , "this"        ,
00116     /* added in Java 1.2 */
00117   "strictfp",
00118     /* added in Java 1.4 */
00119   "assert"  ,
00120     /* added in Java 5.0 */
00121   "enum"    ,
00122     /* Java constants */
00123   "true"    , "false"    , "null"      ,
00124     /* java.lang.Object methods                                           *
00125      *    - don't worry about "toString", since it does the same thing    *
00126      *      as Object's "toString"                                        */
00127   "clone"   , "equals"   , "finalize"  , "getClass"    , "hashCode"    ,
00128   "notify"  , "notifyAll", /*"toString"  ,*/ "wait"
00129 };
00130 
00131 nsTHashtable<nsDepCharHashKey>* gJavaKeywords = nsnull;
00132 
00133 
00134 /******************************
00135  *  InitializeJavaGlobals
00136  ******************************/
00137 PRBool
00138 InitializeJavaGlobals(JNIEnv *env)
00139 {
00140   if (gJavaXPCOMInitialized)
00141     return PR_TRUE;
00142 
00143   // Save pointer to JavaVM, which is valid across threads.
00144   jint rc = env->GetJavaVM(&gCachedJVM);
00145   if (rc != 0) {
00146     NS_WARNING("Failed to get JavaVM");
00147     goto init_error;
00148   }
00149 
00150   jclass clazz;
00151   if (!(clazz = env->FindClass("java/lang/System")) ||
00152       !(systemClass = (jclass) env->NewGlobalRef(clazz)) ||
00153       !(hashCodeMID = env->GetStaticMethodID(clazz, "identityHashCode",
00154                                              "(Ljava/lang/Object;)I")))
00155   {
00156     NS_WARNING("Problem creating java.lang.System globals");
00157     goto init_error;
00158   }
00159 
00160   if (!(clazz = env->FindClass("java/lang/Boolean")) ||
00161       !(booleanClass = (jclass) env->NewGlobalRef(clazz)) ||
00162       !(booleanValueMID = env->GetMethodID(clazz, "booleanValue", "()Z")) ||
00163       !(booleanInitMID = env->GetMethodID(clazz, "<init>", "(Z)V")))
00164   {
00165     NS_WARNING("Problem creating java.lang.Boolean globals");
00166     goto init_error;
00167   }
00168 
00169   if (!(clazz = env->FindClass("java/lang/Character")) ||
00170       !(charClass = (jclass) env->NewGlobalRef(clazz)) ||
00171       !(charValueMID = env->GetMethodID(clazz, "charValue", "()C")) ||
00172       !(charInitMID = env->GetMethodID(clazz, "<init>", "(C)V")))
00173   {
00174     NS_WARNING("Problem creating java.lang.Character globals");
00175     goto init_error;
00176   }
00177 
00178   if (!(clazz = env->FindClass("java/lang/Byte")) ||
00179       !(byteClass = (jclass) env->NewGlobalRef(clazz)) ||
00180       !(byteValueMID = env->GetMethodID(clazz, "byteValue", "()B")) ||
00181       !(byteInitMID = env->GetMethodID(clazz, "<init>", "(B)V")))
00182   {
00183     NS_WARNING("Problem creating java.lang.Byte globals");
00184     goto init_error;
00185   }
00186 
00187   if (!(clazz = env->FindClass("java/lang/Short")) ||
00188       !(shortClass = (jclass) env->NewGlobalRef(clazz)) ||
00189       !(shortValueMID = env->GetMethodID(clazz, "shortValue", "()S")) ||
00190       !(shortInitMID = env->GetMethodID(clazz, "<init>", "(S)V")))
00191   {
00192     NS_WARNING("Problem creating java.lang.Short globals");
00193     goto init_error;
00194   }
00195 
00196   if (!(clazz = env->FindClass("java/lang/Integer")) ||
00197       !(intClass = (jclass) env->NewGlobalRef(clazz)) ||
00198       !(intValueMID = env->GetMethodID(clazz, "intValue", "()I")) ||
00199       !(intInitMID = env->GetMethodID(clazz, "<init>", "(I)V")))
00200   {
00201     NS_WARNING("Problem creating java.lang.Integer globals");
00202     goto init_error;
00203   }
00204 
00205   if (!(clazz = env->FindClass("java/lang/Long")) ||
00206       !(longClass = (jclass) env->NewGlobalRef(clazz)) ||
00207       !(longValueMID = env->GetMethodID(clazz, "longValue", "()J")) ||
00208       !(longInitMID = env->GetMethodID(clazz, "<init>", "(J)V")))
00209   {
00210     NS_WARNING("Problem creating java.lang.Long globals");
00211     goto init_error;
00212   }
00213 
00214   if (!(clazz = env->FindClass("java/lang/Float")) ||
00215       !(floatClass = (jclass) env->NewGlobalRef(clazz)) ||
00216       !(floatValueMID = env->GetMethodID(clazz, "floatValue", "()F")) ||
00217       !(floatInitMID = env->GetMethodID(clazz, "<init>", "(F)V")))
00218   {
00219     NS_WARNING("Problem creating java.lang.Float globals");
00220     goto init_error;
00221   }
00222 
00223   if (!(clazz = env->FindClass("java/lang/Double")) ||
00224       !(doubleClass = (jclass) env->NewGlobalRef(clazz)) ||
00225       !(doubleValueMID = env->GetMethodID(clazz, "doubleValue", "()D")) ||
00226       !(doubleInitMID = env->GetMethodID(clazz, "<init>", "(D)V")))
00227   {
00228     NS_WARNING("Problem creating java.lang.Double globals");
00229     goto init_error;
00230   }
00231 
00232   if (!(clazz = env->FindClass("java/lang/String")) ||
00233       !(stringClass = (jclass) env->NewGlobalRef(clazz)))
00234   {
00235     NS_WARNING("Problem creating java.lang.String globals");
00236     goto init_error;
00237   }
00238 
00239   if (!(clazz = env->FindClass("org/mozilla/interfaces/nsISupports")) ||
00240       !(nsISupportsClass = (jclass) env->NewGlobalRef(clazz)))
00241   {
00242     NS_WARNING("Problem creating org.mozilla.interfaces.nsISupports globals");
00243     goto init_error;
00244   }
00245 
00246   if (!(clazz = env->FindClass("org/mozilla/xpcom/XPCOMException")) ||
00247       !(xpcomExceptionClass = (jclass) env->NewGlobalRef(clazz)))
00248   {
00249     NS_WARNING("Problem creating org.mozilla.xpcom.XPCOMException globals");
00250     goto init_error;
00251   }
00252 
00253   if (!(clazz = env->FindClass("org/mozilla/xpcom/internal/XPCOMJavaProxy")) ||
00254       !(xpcomJavaProxyClass = (jclass) env->NewGlobalRef(clazz)) ||
00255       !(createProxyMID = env->GetStaticMethodID(clazz, "createProxy",
00256                                    "(Ljava/lang/Class;J)Ljava/lang/Object;")) ||
00257       !(isXPCOMJavaProxyMID = env->GetStaticMethodID(clazz, "isXPCOMJavaProxy",
00258                                                     "(Ljava/lang/Object;)Z")) ||
00259       !(getNativeXPCOMInstMID = env->GetStaticMethodID(xpcomJavaProxyClass,
00260                                                        "getNativeXPCOMInstance",
00261                                                        "(Ljava/lang/Object;)J")))
00262   {
00263     NS_WARNING("Problem creating org.mozilla.xpcom.internal.XPCOMJavaProxy globals");
00264     goto init_error;
00265   }
00266 
00267   if (!(clazz = env->FindClass("java/lang/ref/WeakReference")) ||
00268       !(weakReferenceClass = (jclass) env->NewGlobalRef(clazz)) ||
00269       !(weakReferenceConstructorMID = env->GetMethodID(weakReferenceClass, 
00270                                            "<init>","(Ljava/lang/Object;)V")) ||
00271       !(getReferentMID = env->GetMethodID(weakReferenceClass,
00272                                           "get", "()Ljava/lang/Object;")) ||
00273       !(clearReferentMID = env->GetMethodID(weakReferenceClass, 
00274                                             "clear", "()V")))
00275   {
00276     NS_WARNING("Problem creating java.lang.ref.WeakReference globals");
00277     goto init_error;
00278   }
00279 
00280   if (!(clazz = env->FindClass("org/mozilla/xpcom/internal/JavaXPCOMMethods")) ||
00281       !(javaXPCOMUtilsClass = (jclass) env->NewGlobalRef(clazz)) ||
00282       !(findClassInLoaderMID = env->GetStaticMethodID(clazz,
00283                     "findClassInLoader",
00284                     "(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Class;")))
00285   {
00286     NS_WARNING("Problem creating org.mozilla.xpcom.internal.JavaXPCOMMethods globals");
00287     goto init_error;
00288   }
00289 
00290 #ifdef DEBUG_JAVAXPCOM
00291   if (!(clazz = env->FindClass("java/lang/Class")) ||
00292       !(getNameMID = env->GetMethodID(clazz, "getName","()Ljava/lang/String;")))
00293   {
00294     NS_WARNING("Problem creating java.lang.Class globals");
00295     goto init_error;
00296   }
00297 
00298   if (!(proxyToStringMID = env->GetStaticMethodID(xpcomJavaProxyClass,
00299                                                   "proxyToString",
00300                                      "(Ljava/lang/Object;)Ljava/lang/String;")))
00301   {
00302     NS_WARNING("Problem creating proxyToString global");
00303     goto init_error;
00304   }
00305 #endif
00306 
00307   gNativeToJavaProxyMap = new NativeToJavaProxyMap();
00308   if (!gNativeToJavaProxyMap || NS_FAILED(gNativeToJavaProxyMap->Init())) {
00309     NS_WARNING("Problem creating NativeToJavaProxyMap");
00310     goto init_error;
00311   }
00312   gJavaToXPTCStubMap = new JavaToXPTCStubMap();
00313   if (!gJavaToXPTCStubMap || NS_FAILED(gJavaToXPTCStubMap->Init())) {
00314     NS_WARNING("Problem creating JavaToXPTCStubMap");
00315     goto init_error;
00316   }
00317 
00318   {
00319     nsresult rv = NS_OK;
00320     PRUint32 size = NS_ARRAY_LENGTH(kJavaKeywords);
00321     gJavaKeywords = new nsTHashtable<nsDepCharHashKey>();
00322     if (!gJavaKeywords || NS_FAILED(gJavaKeywords->Init(size))) {
00323       NS_WARNING("Failed to init JavaKeywords HashSet");
00324       goto init_error;
00325     }
00326     for (PRUint32 i = 0; i < size && NS_SUCCEEDED(rv); i++) {
00327       if (!gJavaKeywords->PutEntry(kJavaKeywords[i])) {
00328         rv = NS_ERROR_OUT_OF_MEMORY;
00329       }
00330     }
00331     if (NS_FAILED(rv)) {
00332       NS_WARNING("Failed to populate JavaKeywords hash");
00333       goto init_error;
00334     }
00335   }
00336 
00337   gJavaXPCOMLock = PR_NewLock();
00338   gJavaXPCOMInitialized = PR_TRUE;
00339   return PR_TRUE;
00340 
00341 init_error:
00342   // If we encounter an error during initialization, then free any globals that
00343   // were allocated, and return false.
00344   FreeJavaGlobals(env);
00345   return PR_FALSE;
00346 }
00347 
00348 /*************************
00349  *    FreeJavaGlobals
00350  *************************/
00351 void
00352 FreeJavaGlobals(JNIEnv* env)
00353 {
00354   PRLock* tempLock = nsnull;
00355   if (gJavaXPCOMLock) {
00356     PR_Lock(gJavaXPCOMLock);
00357 
00358     // null out global lock so no one else can use it
00359     tempLock = gJavaXPCOMLock;
00360     gJavaXPCOMLock = nsnull;
00361   }
00362 
00363   gJavaXPCOMInitialized = PR_FALSE;
00364 
00365   // Free the mappings first, since that process depends on some of the Java
00366   // globals that are freed later.
00367   if (gNativeToJavaProxyMap) {
00368     gNativeToJavaProxyMap->Destroy(env);
00369     delete gNativeToJavaProxyMap;
00370     gNativeToJavaProxyMap = nsnull;
00371   }
00372   if (gJavaToXPTCStubMap) {
00373     gJavaToXPTCStubMap->Destroy();
00374     delete gJavaToXPTCStubMap;
00375     gJavaToXPTCStubMap = nsnull;
00376   }
00377 
00378   // Free remaining Java globals
00379   if (systemClass) {
00380     env->DeleteGlobalRef(systemClass);
00381     systemClass = nsnull;
00382   }
00383   if (booleanClass) {
00384     env->DeleteGlobalRef(booleanClass);
00385     booleanClass = nsnull;
00386   }
00387   if (charClass) {
00388     env->DeleteGlobalRef(charClass);
00389     charClass = nsnull;
00390   }
00391   if (byteClass) {
00392     env->DeleteGlobalRef(byteClass);
00393     byteClass = nsnull;
00394   }
00395   if (shortClass) {
00396     env->DeleteGlobalRef(shortClass);
00397     shortClass = nsnull;
00398   }
00399   if (intClass) {
00400     env->DeleteGlobalRef(intClass);
00401     intClass = nsnull;
00402   }
00403   if (longClass) {
00404     env->DeleteGlobalRef(longClass);
00405     longClass = nsnull;
00406   }
00407   if (floatClass) {
00408     env->DeleteGlobalRef(floatClass);
00409     floatClass = nsnull;
00410   }
00411   if (doubleClass) {
00412     env->DeleteGlobalRef(doubleClass);
00413     doubleClass = nsnull;
00414   }
00415   if (stringClass) {
00416     env->DeleteGlobalRef(stringClass);
00417     stringClass = nsnull;
00418   }
00419   if (nsISupportsClass) {
00420     env->DeleteGlobalRef(nsISupportsClass);
00421     nsISupportsClass = nsnull;
00422   }
00423   if (xpcomExceptionClass) {
00424     env->DeleteGlobalRef(xpcomExceptionClass);
00425     xpcomExceptionClass = nsnull;
00426   }
00427   if (xpcomJavaProxyClass) {
00428     env->DeleteGlobalRef(xpcomJavaProxyClass);
00429     xpcomJavaProxyClass = nsnull;
00430   }
00431   if (weakReferenceClass) {
00432     env->DeleteGlobalRef(weakReferenceClass);
00433     weakReferenceClass = nsnull;
00434   }
00435 
00436   if (gJavaKeywords) {
00437     delete gJavaKeywords;
00438     gJavaKeywords = nsnull;
00439   }
00440 
00441   if (tempLock) {
00442     PR_Unlock(tempLock);
00443     PR_DestroyLock(tempLock);
00444   }
00445 }
00446 
00447 
00448 /**************************************
00449  *  Java<->XPCOM object mappings
00450  **************************************/
00451 
00452 static PLDHashTableOps hash_ops =
00453 {
00454   PL_DHashAllocTable,
00455   PL_DHashFreeTable,
00456   PL_DHashGetKeyStub,
00457   PL_DHashVoidPtrKeyStub,
00458   PL_DHashMatchEntryStub,
00459   PL_DHashMoveEntryStub,
00460   PL_DHashClearEntryStub,
00461   PL_DHashFinalizeStub
00462 };
00463 
00464 // NativeToJavaProxyMap: The common case is that each XPCOM object will have
00465 // one Java proxy.  But there are instances where there will be multiple Java
00466 // proxies for a given XPCOM object, each representing a different interface.
00467 // So we optimize the common case by using a hash table.  Then, if there are
00468 // multiple Java proxies, we cycle through the linked list, comparing IIDs.
00469 
00470 nsresult
00471 NativeToJavaProxyMap::Init()
00472 {
00473   mHashTable = PL_NewDHashTable(&hash_ops, nsnull, sizeof(Entry), 16);
00474   if (!mHashTable)
00475     return NS_ERROR_OUT_OF_MEMORY;
00476   return NS_OK;
00477 }
00478 
00479 PLDHashOperator
00480 DestroyJavaProxyMappingEnum(PLDHashTable* aTable, PLDHashEntryHdr* aHeader,
00481                             PRUint32 aNumber, void* aData)
00482 {
00483   JNIEnv* env = NS_STATIC_CAST(JNIEnv*, aData);
00484   NativeToJavaProxyMap::Entry* entry =
00485                           NS_STATIC_CAST(NativeToJavaProxyMap::Entry*, aHeader);
00486 
00487   // first, delete XPCOM instances from the Java proxies
00488   nsresult rv;
00489   NativeToJavaProxyMap::ProxyList* item = entry->list;
00490   while(item != nsnull) {
00491     void* xpcom_obj;
00492     jobject javaObject = env->CallObjectMethod(item->javaObject, getReferentMID);
00493     rv = GetXPCOMInstFromProxy(env, javaObject, &xpcom_obj);
00494     NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to get XPCOM instance from Java proxy");
00495 
00496     if (NS_SUCCEEDED(rv)) {
00497       JavaXPCOMInstance* inst = NS_STATIC_CAST(JavaXPCOMInstance*, xpcom_obj);
00498 #ifdef DEBUG_JAVAXPCOM
00499       char* iid_str = item->iid.ToString();
00500       LOG(("- NativeToJavaProxyMap (Java=%08x | XPCOM=%08x | IID=%s)\n",
00501            (PRUint32) env->CallStaticIntMethod(systemClass, hashCodeMID,
00502                                                javaObject),
00503            (PRUint32) entry, iid_str));
00504       PR_Free(iid_str);
00505 #endif
00506       delete inst;  // releases native XPCOM object
00507     }
00508 
00509     NativeToJavaProxyMap::ProxyList* next = item->next;
00510     env->CallVoidMethod(item->javaObject, clearReferentMID);
00511     env->DeleteGlobalRef(item->javaObject);
00512     delete item;
00513     item = next;
00514   }
00515 
00516   return PL_DHASH_REMOVE;
00517 }
00518 
00519 nsresult
00520 NativeToJavaProxyMap::Destroy(JNIEnv* env)
00521 {
00522   // This is only called from FreeGlobals(), which already holds the lock.
00523   //  nsAutoLock lock(gJavaXPCOMLock);
00524 
00525   PL_DHashTableEnumerate(mHashTable, DestroyJavaProxyMappingEnum, env);
00526   PL_DHashTableDestroy(mHashTable);
00527   mHashTable = nsnull;
00528 
00529   return NS_OK;
00530 }
00531 
00532 nsresult
00533 NativeToJavaProxyMap::Add(JNIEnv* env, nsISupports* aXPCOMObject,
00534                           const nsIID& aIID, jobject aProxy)
00535 {
00536   nsAutoLock lock(gJavaXPCOMLock);
00537 
00538   Entry* e = NS_STATIC_CAST(Entry*, PL_DHashTableOperate(mHashTable,
00539                                                          aXPCOMObject,
00540                                                          PL_DHASH_ADD));
00541   if (!e)
00542     return NS_ERROR_FAILURE;
00543 
00544   jobject ref = nsnull;
00545   jobject weakRefObj = env->NewObject(weakReferenceClass,
00546                                       weakReferenceConstructorMID, aProxy);
00547   if (weakRefObj)
00548     ref = env->NewGlobalRef(weakRefObj);
00549   if (!ref)
00550     return NS_ERROR_OUT_OF_MEMORY;
00551 
00552   // Add Java proxy weak reference ref to start of list
00553   ProxyList* item = new ProxyList(ref, aIID, e->list);
00554   e->key = aXPCOMObject;
00555   e->list = item;
00556 
00557 #ifdef DEBUG_JAVAXPCOM
00558   char* iid_str = aIID.ToString();
00559   LOG(("+ NativeToJavaProxyMap (Java=%08x | XPCOM=%08x | IID=%s)\n",
00560        (PRUint32) env->CallStaticIntMethod(systemClass, hashCodeMID, aProxy),
00561        (PRUint32) aXPCOMObject, iid_str));
00562   PR_Free(iid_str);
00563 #endif
00564   return NS_OK;
00565 }
00566 
00567 nsresult
00568 NativeToJavaProxyMap::Find(JNIEnv* env, nsISupports* aNativeObject,
00569                            const nsIID& aIID, jobject* aResult)
00570 {
00571   NS_PRECONDITION(aResult != nsnull, "null ptr");
00572   if (!aResult)
00573     return NS_ERROR_FAILURE;
00574 
00575   nsAutoLock lock(gJavaXPCOMLock);
00576 
00577   *aResult = nsnull;
00578   Entry* e = NS_STATIC_CAST(Entry*, PL_DHashTableOperate(mHashTable,
00579                                                          aNativeObject,
00580                                                          PL_DHASH_LOOKUP));
00581 
00582   if (PL_DHASH_ENTRY_IS_FREE(e))
00583     return NS_OK;
00584 
00585   ProxyList* item = e->list;
00586   while (item != nsnull && *aResult == nsnull) {
00587     if (item->iid.Equals(aIID)) {
00588       jobject referentObj = env->CallObjectMethod(item->javaObject,
00589                                                   getReferentMID);
00590       if (!env->IsSameObject(referentObj, NULL)) {
00591         *aResult = referentObj;
00592 #ifdef DEBUG_JAVAXPCOM
00593         char* iid_str = aIID.ToString();
00594         LOG(("< NativeToJavaProxyMap (Java=%08x | XPCOM=%08x | IID=%s)\n",
00595              (PRUint32) env->CallStaticIntMethod(systemClass, hashCodeMID,
00596                                                  *aResult),
00597              (PRUint32) aNativeObject, iid_str));
00598         PR_Free(iid_str);
00599 #endif
00600       }
00601     }
00602     item = item->next;
00603   }
00604 
00605   return NS_OK;
00606 }
00607 
00608 nsresult
00609 NativeToJavaProxyMap::Remove(JNIEnv* env, nsISupports* aNativeObject,
00610                              const nsIID& aIID)
00611 {
00612   // This is only called from finalizeProxy(), which already holds the lock.
00613   //  nsAutoLock lock(gJavaXPCOMLock);
00614 
00615   Entry* e = NS_STATIC_CAST(Entry*, PL_DHashTableOperate(mHashTable,
00616                                                          aNativeObject,
00617                                                          PL_DHASH_LOOKUP));
00618 
00619   if (PL_DHASH_ENTRY_IS_FREE(e)) {
00620     NS_WARNING("XPCOM object not found in hash table");
00621     return NS_ERROR_FAILURE;
00622   }
00623 
00624   ProxyList* item = e->list;
00625   ProxyList* last = e->list;
00626   while (item != nsnull) {
00627     if (item->iid.Equals(aIID)) {
00628 #ifdef DEBUG_JAVAXPCOM
00629       char* iid_str = aIID.ToString();
00630       LOG(("- NativeToJavaProxyMap (Java=%08x | XPCOM=%08x | IID=%s)\n",
00631            (PRUint32) env->CallStaticIntMethod(systemClass, hashCodeMID,
00632                                                item->javaObject),
00633            (PRUint32) aNativeObject, iid_str));
00634       PR_Free(iid_str);
00635 #endif
00636 
00637       env->CallVoidMethod(item->javaObject, clearReferentMID);
00638       env->DeleteGlobalRef(item->javaObject);
00639       if (item == e->list) {
00640         e->list = item->next;
00641         if (e->list == nsnull)
00642           PL_DHashTableOperate(mHashTable, aNativeObject, PL_DHASH_REMOVE);
00643       } else {
00644         last->next = item->next;
00645       }
00646 
00647       delete item;
00648       return NS_OK;
00649     }
00650 
00651     last = item;
00652     item = item->next;
00653   }
00654 
00655   NS_WARNING("Java proxy matching given IID not found");
00656   return NS_ERROR_FAILURE;
00657 }
00658 
00659 nsresult
00660 JavaToXPTCStubMap::Init()
00661 {
00662   mHashTable = PL_NewDHashTable(&hash_ops, nsnull, sizeof(Entry), 16);
00663   if (!mHashTable)
00664     return NS_ERROR_OUT_OF_MEMORY;
00665   return NS_OK;
00666 }
00667 
00668 
00669 PLDHashOperator
00670 DestroyXPTCMappingEnum(PLDHashTable* aTable, PLDHashEntryHdr* aHeader,
00671                        PRUint32 aNumber, void* aData)
00672 {
00673   JavaToXPTCStubMap::Entry* entry =
00674                              NS_STATIC_CAST(JavaToXPTCStubMap::Entry*, aHeader);
00675 
00676   // The XPTC stub will be released by the XPCOM side, if it hasn't been
00677   // already.  We just need to delete the Java global ref held by the XPTC stub,
00678   // so the Java garbage collector can handle the Java object when necessary.
00679   entry->xptcstub->DeleteStrongRef();
00680 
00681   return PL_DHASH_REMOVE;
00682 }
00683 
00684 nsresult
00685 JavaToXPTCStubMap::Destroy()
00686 {
00687   // This is only called from FreeGlobals(), which already holds the lock.
00688   //  nsAutoLock lock(gJavaXPCOMLock);
00689 
00690   PL_DHashTableEnumerate(mHashTable, DestroyXPTCMappingEnum, nsnull);
00691   PL_DHashTableDestroy(mHashTable);
00692   mHashTable = nsnull;
00693 
00694   return NS_OK;
00695 }
00696 
00697 nsresult
00698 JavaToXPTCStubMap::Add(jint aJavaObjectHashCode, nsJavaXPTCStub* aProxy)
00699 {
00700   nsAutoLock lock(gJavaXPCOMLock);
00701 
00702   Entry* e = NS_STATIC_CAST(Entry*,
00703                             PL_DHashTableOperate(mHashTable,
00704                                            NS_INT32_TO_PTR(aJavaObjectHashCode),
00705                                            PL_DHASH_ADD));
00706   if (!e)
00707     return NS_ERROR_FAILURE;
00708 
00709   NS_ASSERTION(e->key == nsnull,
00710                "XPTCStub for given Java object already exists in hash table");
00711   e->key = aJavaObjectHashCode;
00712   e->xptcstub = aProxy;
00713 
00714 #ifdef DEBUG_JAVAXPCOM
00715   nsIInterfaceInfo* iface_info;
00716   aProxy->GetInterfaceInfo(&iface_info);
00717   nsIID* iid;
00718   iface_info->GetInterfaceIID(&iid);
00719   char* iid_str = iid->ToString();
00720   LOG(("+ JavaToXPTCStubMap (Java=%08x | XPCOM=%08x | IID=%s)\n",
00721        (PRUint32) aJavaObjectHashCode, (PRUint32) aProxy, iid_str));
00722   PR_Free(iid_str);
00723   nsMemory::Free(iid);
00724   NS_RELEASE(iface_info);
00725 #endif
00726   return NS_OK;
00727 }
00728 
00729 nsresult
00730 JavaToXPTCStubMap::Find(jint aJavaObjectHashCode, const nsIID& aIID,
00731                         nsJavaXPTCStub** aResult)
00732 {
00733   NS_PRECONDITION(aResult != nsnull, "null ptr");
00734   if (!aResult)
00735     return NS_ERROR_FAILURE;
00736 
00737   nsAutoLock lock(gJavaXPCOMLock);
00738 
00739   *aResult = nsnull;
00740   Entry* e = NS_STATIC_CAST(Entry*,
00741                             PL_DHashTableOperate(mHashTable,
00742                                            NS_INT32_TO_PTR(aJavaObjectHashCode),
00743                                            PL_DHASH_LOOKUP));
00744 
00745   if (PL_DHASH_ENTRY_IS_FREE(e))
00746     return NS_OK;
00747 
00748   nsresult rv = e->xptcstub->QueryInterface(aIID, (void**) aResult);
00749 
00750 #ifdef DEBUG_JAVAXPCOM
00751   if (NS_SUCCEEDED(rv)) {
00752     char* iid_str = aIID.ToString();
00753     LOG(("< JavaToXPTCStubMap (Java=%08x | XPCOM=%08x | IID=%s)\n",
00754          (PRUint32) aJavaObjectHashCode, (PRUint32) *aResult, iid_str));
00755     PR_Free(iid_str);
00756   }
00757 #endif
00758 
00759   // NS_NOINTERFACE is not an error condition
00760   if (rv == NS_NOINTERFACE)
00761     rv = NS_OK;
00762   return rv;
00763 }
00764 
00765 nsresult
00766 JavaToXPTCStubMap::Remove(jint aJavaObjectHashCode)
00767 {
00768   PL_DHashTableOperate(mHashTable, NS_INT32_TO_PTR(aJavaObjectHashCode),
00769                        PL_DHASH_REMOVE);
00770 
00771 #ifdef DEBUG_JAVAXPCOM
00772   LOG(("- JavaToXPTCStubMap (Java=%08x)\n", (PRUint32) aJavaObjectHashCode));
00773 #endif
00774 
00775   return NS_OK;
00776 }
00777 
00778 
00779 /**********************************************************
00780  *    JavaXPCOMInstance
00781  *********************************************************/
00782 JavaXPCOMInstance::JavaXPCOMInstance(nsISupports* aInstance,
00783                                      nsIInterfaceInfo* aIInfo)
00784     : mInstance(aInstance)
00785     , mIInfo(aIInfo)
00786 {
00787   NS_ADDREF(mInstance);
00788   NS_ADDREF(mIInfo);
00789 }
00790 
00791 JavaXPCOMInstance::~JavaXPCOMInstance()
00792 {
00793   // Need to release these objects on the main thread.
00794   nsCOMPtr<nsIEventQueue> eventQ;
00795   nsresult rv = NS_GetMainEventQ(getter_AddRefs(eventQ));
00796   if (NS_SUCCEEDED(rv)) {
00797     rv = NS_ProxyRelease(eventQ, mInstance);
00798     rv += NS_ProxyRelease(eventQ, mIInfo);
00799   }
00800   NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to release using NS_ProxyRelease");
00801 }
00802 
00803 
00804 /*******************************
00805  *  Helper functions
00806  *******************************/
00807 
00808 nsresult
00809 GetNewOrUsedJavaObject(JNIEnv* env, nsISupports* aXPCOMObject,
00810                        const nsIID& aIID, jobject aObjectLoader,
00811                        jobject* aResult)
00812 {
00813   NS_PRECONDITION(aResult != nsnull, "null ptr");
00814   if (!aResult)
00815     return NS_ERROR_NULL_POINTER;
00816 
00817   nsresult rv;
00818   nsJavaXPTCStub* stub = nsnull;
00819   aXPCOMObject->QueryInterface(NS_GET_IID(nsJavaXPTCStub), (void**) &stub);
00820   if (stub) {
00821     // Get Java object directly from nsJavaXPTCStub
00822     *aResult = stub->GetJavaObject();
00823     NS_ASSERTION(*aResult != nsnull, "nsJavaXPTCStub w/o matching Java object");
00824     NS_RELEASE(stub);
00825     return NS_OK;
00826   }
00827 
00828   // Get the root nsISupports of the xpcom object
00829   nsCOMPtr<nsISupports> rootObject = do_QueryInterface(aXPCOMObject, &rv);
00830   NS_ENSURE_SUCCESS(rv, rv);
00831 
00832   // Get associated Java object from hash table
00833   rv = gNativeToJavaProxyMap->Find(env, rootObject, aIID, aResult);
00834   NS_ENSURE_SUCCESS(rv, rv);
00835   if (*aResult)
00836     return NS_OK;
00837 
00838   // No Java object is associated with the given XPCOM object, so we
00839   // create a Java proxy.
00840   return CreateJavaProxy(env, rootObject, aIID, aObjectLoader, aResult);
00841 }
00842 
00843 nsresult
00844 GetNewOrUsedXPCOMObject(JNIEnv* env, jobject aJavaObject, const nsIID& aIID,
00845                         nsISupports** aResult)
00846 {
00847   NS_PRECONDITION(aResult != nsnull, "null ptr");
00848   if (!aResult)
00849     return NS_ERROR_NULL_POINTER;
00850 
00851   nsresult rv;
00852   *aResult = nsnull;
00853 
00854   // Check if the given Java object is actually one of our Java proxies.  If so,
00855   // then we query the associated XPCOM object directly from the proxy.
00856   // If Java object is not a proxy, then we try to find associated XPCOM object
00857   // in the mapping table.
00858   jboolean isProxy = env->CallStaticBooleanMethod(xpcomJavaProxyClass,
00859                                                   isXPCOMJavaProxyMID,
00860                                                   aJavaObject);
00861   if (env->ExceptionCheck())
00862     return NS_ERROR_FAILURE;
00863 
00864   if (isProxy) {
00865     void* inst;
00866     rv = GetXPCOMInstFromProxy(env, aJavaObject, &inst);
00867     NS_ENSURE_SUCCESS(rv, rv);
00868 
00869     nsISupports* rootObject =
00870               NS_STATIC_CAST(JavaXPCOMInstance*, inst)->GetInstance();
00871     rv = rootObject->QueryInterface(aIID, (void**) aResult);
00872     NS_ENSURE_SUCCESS(rv, rv);
00873 
00874     return NS_OK;
00875   }
00876 
00877   nsJavaXPTCStub* stub;
00878   jint hash = env->CallStaticIntMethod(systemClass, hashCodeMID, aJavaObject);
00879   rv = gJavaToXPTCStubMap->Find(hash, aIID, &stub);
00880   NS_ENSURE_SUCCESS(rv, rv);
00881   if (stub) {
00882     // stub is already AddRef'd and QI'd
00883     *aResult = NS_STATIC_CAST(nsISupports*,
00884                               NS_STATIC_CAST(nsXPTCStubBase*, stub));
00885     return NS_OK;
00886   }
00887 
00888   // If there is no corresponding XPCOM object, then that means that the
00889   // parameter is a non-generated class (that is, it is not one of our
00890   // Java stubs that represent an exising XPCOM object).  So we need to
00891   // create an XPCOM stub, that can route any method calls to the class.
00892 
00893   // Get interface info for class
00894   nsCOMPtr<nsIInterfaceInfoManager> iim = XPTI_GetInterfaceInfoManager();
00895 
00896   nsCOMPtr<nsIInterfaceInfo> iinfo;
00897   rv = iim->GetInfoForIID(&aIID, getter_AddRefs(iinfo));
00898   NS_ENSURE_SUCCESS(rv, rv);
00899 
00900   // Create XPCOM stub
00901   stub = new nsJavaXPTCStub(aJavaObject, iinfo);
00902   if (!stub) {
00903     return NS_ERROR_OUT_OF_MEMORY;
00904   }
00905   rv = gJavaToXPTCStubMap->Add(hash, stub);
00906   if (NS_FAILED(rv)) {
00907     delete stub;
00908     return rv;
00909   }
00910 
00911   NS_ADDREF(stub);
00912   *aResult = NS_STATIC_CAST(nsISupports*,
00913                             NS_STATIC_CAST(nsXPTCStubBase*, stub));
00914 
00915   return NS_OK;
00916 }
00917 
00918 nsresult
00919 GetIIDForMethodParam(nsIInterfaceInfo *iinfo, const nsXPTMethodInfo *methodInfo,
00920                      const nsXPTParamInfo &paramInfo, PRUint8 paramType,
00921                      PRUint16 methodIndex, nsXPTCMiniVariant *dispatchParams,
00922                      PRBool isFullVariantArray, nsID &result)
00923 {
00924   nsresult rv;
00925 
00926   switch (paramType)
00927   {
00928     case nsXPTType::T_INTERFACE:
00929       rv = iinfo->GetIIDForParamNoAlloc(methodIndex, &paramInfo, &result);
00930       break;
00931 
00932     case nsXPTType::T_INTERFACE_IS:
00933     {
00934       PRUint8 argnum;
00935       rv = iinfo->GetInterfaceIsArgNumberForParam(methodIndex, &paramInfo,
00936                                                   &argnum);
00937       if (NS_FAILED(rv))
00938         break;
00939 
00940       const nsXPTParamInfo& arg_param = methodInfo->GetParam(argnum);
00941       const nsXPTType& arg_type = arg_param.GetType();
00942 
00943       // The xpidl compiler ensures this. We reaffirm it for safety.
00944       if (!arg_type.IsPointer() || arg_type.TagPart() != nsXPTType::T_IID) {
00945         rv = NS_ERROR_UNEXPECTED;
00946         break;
00947       }
00948 
00949       nsID *p = nsnull;
00950       if (isFullVariantArray) {
00951         p = (nsID *) ((nsXPTCVariant*) dispatchParams)[argnum].val.p;
00952       } else {
00953         p = (nsID *) dispatchParams[argnum].val.p;
00954       }
00955       if (!p)
00956         return NS_ERROR_UNEXPECTED;
00957 
00958       result = *p;
00959       break;
00960     }
00961 
00962     default:
00963       rv = NS_ERROR_UNEXPECTED;
00964   }
00965   return rv;
00966 }
00967 
00968 
00969 /*******************************
00970  *  JNI helper functions
00971  *******************************/
00972 
00973 JNIEnv*
00974 GetJNIEnv()
00975 {
00976   JNIEnv* env;
00977   jint rc = gCachedJVM->GetEnv((void**) &env, JNI_VERSION_1_2);
00978   NS_ASSERTION(rc == JNI_OK && env != nsnull,
00979                "Current thread not attached to given JVM instance");
00980   return env;
00981 }
00982 
00983 void
00984 ThrowException(JNIEnv* env, const nsresult aErrorCode, const char* aMessage)
00985 {
00986   // Only throw this exception if one hasn't already been thrown, so we don't
00987   // mask a previous exception/error.
00988   if (env->ExceptionCheck())
00989     return;
00990 
00991   // If the error code we get is for an Out Of Memory error, try to throw an
00992   // OutOfMemoryError.  The JVM may have enough memory to create this error.
00993   if (aErrorCode == NS_ERROR_OUT_OF_MEMORY) {
00994     jclass clazz = env->FindClass("java/lang/OutOfMemoryError");
00995     if (clazz) {
00996       env->ThrowNew(clazz, aMessage);
00997     }
00998     env->DeleteLocalRef(clazz);
00999     return;
01000   }
01001 
01002   // If the error was not handled above, then create an XPCOMException with the
01003   // given error code and message.
01004 
01005   // Create parameters and method signature. Max of 2 params.  The error code
01006   // comes before the message string.
01007   PRInt64 errorCode = aErrorCode ? aErrorCode : NS_ERROR_FAILURE;
01008   nsCAutoString methodSig("(J");
01009   jstring message = nsnull;
01010   if (aMessage) {
01011     message = env->NewStringUTF(aMessage);
01012     if (!message) {
01013       return;
01014     }
01015     methodSig.AppendLiteral("Ljava/lang/String;");
01016   }
01017   methodSig.AppendLiteral(")V");
01018 
01019   // In some instances (such as in shutdownXPCOM() and termEmbedding()), we
01020   // will need to throw an exception when JavaXPCOM has already been
01021   // terminated.  In such a case, 'xpcomExceptionClass' will be null.  So we
01022   // reset it temporarily in order to throw the appropriate exception.
01023   if (xpcomExceptionClass == nsnull) {
01024     xpcomExceptionClass = env->FindClass("org/mozilla/xpcom/XPCOMException");
01025     if (!xpcomExceptionClass) {
01026       return;
01027     }
01028   }
01029 
01030   // create exception object
01031   jthrowable throwObj = nsnull;
01032   jmethodID mid = env->GetMethodID(xpcomExceptionClass, "<init>",
01033                                    methodSig.get());
01034   if (mid) {
01035     throwObj = (jthrowable) env->NewObject(xpcomExceptionClass, mid, errorCode,
01036                                            message);
01037   }
01038   NS_ASSERTION(throwObj, "Failed to create XPCOMException object");
01039 
01040   // throw exception
01041   if (throwObj) {
01042     env->Throw(throwObj);
01043   }
01044 }
01045 
01046 nsAString*
01047 jstring_to_nsAString(JNIEnv* env, jstring aString)
01048 {
01049   const PRUnichar* buf = nsnull;
01050   if (aString) {
01051     buf = env->GetStringChars(aString, nsnull);
01052     if (!buf)
01053       return nsnull;  // exception already thrown
01054   }
01055 
01056   nsString* str = new nsString(buf);
01057 
01058   if (aString) {
01059     env->ReleaseStringChars(aString, buf);
01060   } else {
01061     str->SetIsVoid(PR_TRUE);
01062   }
01063 
01064   // returns string, or nsnull if 'new' failed
01065   return str;
01066 }
01067 
01068 nsACString*
01069 jstring_to_nsACString(JNIEnv* env, jstring aString)
01070 {
01071   const char* buf = nsnull;
01072   if (aString) {
01073     buf = env->GetStringUTFChars(aString, nsnull);
01074     if (!buf)
01075       return nsnull;  // exception already thrown
01076   }
01077 
01078   nsCString* str = new nsCString(buf);
01079 
01080   if (aString) {
01081     env->ReleaseStringUTFChars(aString, buf);
01082   } else {
01083     str->SetIsVoid(PR_TRUE);
01084   }
01085 
01086   // returns string, or nsnull if 'new' failed
01087   return str;
01088 }
01089 
01090 nsresult
01091 File_to_nsILocalFile(JNIEnv* env, jobject aFile, nsILocalFile** aLocalFile)
01092 {
01093   nsresult rv = NS_ERROR_FAILURE;
01094   jstring pathName = nsnull;
01095   jclass clazz = env->FindClass("java/io/File");
01096   if (clazz) {
01097     jmethodID pathMID = env->GetMethodID(clazz, "getCanonicalPath",
01098                                          "()Ljava/lang/String;");
01099     if (pathMID) {
01100       pathName = (jstring) env->CallObjectMethod(aFile, pathMID);
01101       if (pathName != nsnull && !env->ExceptionCheck())
01102         rv = NS_OK;
01103     }
01104   }
01105 
01106   if (NS_SUCCEEDED(rv)) {
01107     nsAString* path = jstring_to_nsAString(env, pathName);
01108     if (!path)
01109       rv = NS_ERROR_OUT_OF_MEMORY;
01110 
01111     if (NS_SUCCEEDED(rv)) {
01112       rv = NS_NewLocalFile(*path, false, aLocalFile);
01113       delete path;
01114     }
01115   }
01116 
01117   return rv;
01118 }
01119