Main Page   Compound List   File List   Compound Members   File Members  

TclXPCOMStubs.cpp

Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  *
00003  * The contents of this file are subject to the Mozilla Public License Version
00004  * 1.1 (the "License"); you may not use this file except in compliance with
00005  * the License. You may obtain a copy of the License at
00006  * http://www.mozilla.org/MPL/
00007  *
00008  * Software distributed under the License is distributed on an "AS IS" basis,
00009  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00010  * for the specific language governing rights and limitations under the
00011  * License.
00012  *
00013  * The Original Code is TclXPCOM.
00014  * 
00015  * The Initial Developer of the Original Code is Mark Follett.
00016  * Portions created by Mark Follett are Copyright (C) 2001-2002
00017  * Mark Follett.  All Rights Reserved.
00018  * 
00019  * Contributor(s):
00020  *     Mark Follett <mef123@myrealbox.com> (Original Author)
00021  *
00022  * Alternatively, the contents of this file may be used under the terms of
00023  * either the GNU General Public License Version 2 or later (the "GPL"), or
00024  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00025  * in which case the provisions of the GPL or the LGPL are applicable instead
00026  * of those above. If you wish to allow use of your version of this file only
00027  * under the terms of either the GPL or the LGPL, and not to allow others to
00028  * use your version of this file under the terms of the MPL, indicate your
00029  * decision by deleting the provisions above and replace them with the notice
00030  * and other provisions required by the GPL or the LGPL. If you do not delete
00031  * the provisions above, a recipient may use your version of this file under
00032  * the terms of any one of the MPL, the GPL or the LGPL.
00033  */
00034 
00040 #include "TclXPCOMPrivate.h"
00041 
00042 
00043 #define STUBS_BUFFERSIZE        16
00044 
00045 
00051 typedef struct CurrentStubInfo {
00052     nsISupports       *currentstub;  
00053     nsIInterfaceInfo  *interfaceinfo;
00054     int                nestlevel;    
00055     nsresult           returnCode;   
00056 } CurrentStubInfo;
00057 
00061 static Tcl_ThreadDataKey currentStubKey;
00062 
00063 
00069 void
00070 TclXPCOM_GetCurrentComponentContext(
00071     nsIInterfaceInfo **interfaceinfo,
00072     nsISupports      **isupports     
00073 )
00074 {
00075     NS_PRECONDITION(interfaceinfo, "null pointer");
00076     NS_PRECONDITION(isupports, "null pointer");
00077 
00078     CurrentStubInfo *stubinfo;
00079     stubinfo = (CurrentStubInfo *)Tcl_GetThreadData(&currentStubKey, sizeof(CurrentStubInfo));
00080     *isupports     = stubinfo->currentstub;
00081     *interfaceinfo = stubinfo->interfaceinfo;
00082 }
00083 
00084 
00091 int
00092 TclXPCOM_SetReturnCode(
00093     nsresult res 
00094 )
00095 {
00096     CurrentStubInfo *stubinfo;
00097 
00098     stubinfo = (CurrentStubInfo *)Tcl_GetThreadData(&currentStubKey, sizeof(CurrentStubInfo));
00099 
00100     if (stubinfo->currentstub)
00101     {
00102         stubinfo->returnCode = res;
00103         return 1;
00104     } else
00105     {
00106         return 0;
00107     }
00108 }
00109 
00110 
00115 void
00116 CleanupOutParameters(
00117     void             **params,      
00118     nsXPTParamInfo   *paraminfos,   
00119     PRUint8           paramcount,   
00120     nsIInterfaceInfo *interfaceinfo,
00121     PRUint16          methodindex   
00122 )
00123 {
00124     PRUint8 i;
00125     nsXPTType type;
00126 
00127     NS_PRECONDITION(params, "null pointer");
00128     NS_PRECONDITION(paraminfos, "null pointer");
00129     NS_PRECONDITION(interfaceinfo, "null pointer");
00130 
00131     for (i = 0; i < paramcount; i++)
00132     {
00133         if (paraminfos[i].IsOut())
00134         {
00135             type = paraminfos[i].GetType();
00136             CleanupParam(params, params[i], &type, interfaceinfo, methodindex, &paraminfos[i]);
00137             if (type.IsPointer())
00138             {
00139                 *(void **)params[i] = NULL;
00140             }
00141         }
00142     }
00143 }
00144 
00145 
00152 Tcl_Obj *
00153 NewOutParam(
00154     int stubid,    
00155     int paramindex 
00156 )
00157 {
00158     char str[128];
00159     sprintf(str, "::xpcom::outparams(%u-%u)", stubid, paramindex);
00160     return Tcl_NewStringObj(str, -1);
00161 }
00162 
00163 
00168 void
00169 CleanupTclOutVariables(
00170     int             stubid,    
00171     Tcl_Interp     *interp,    
00172     nsXPTParamInfo *paraminfos,
00173     PRUint8         paramcount,
00174     PRUint16       *tclparams  
00175 )
00176 {
00177     PRUint8 i;
00178     Tcl_Obj *paramname;
00179 
00180     NS_PRECONDITION(interp, "null pointer");
00181     NS_PRECONDITION(paraminfos, "null pointer");
00182     NS_PRECONDITION(tclparams, "null pointer");
00183 
00184     for (i = 0; i < paramcount; i++)
00185     {
00186         if (!(tclparams[i] & PARAM_IS_SKIPPED)
00187             && (paraminfos[i].IsOut() || paraminfos[i].IsDipper())
00188             && !(paraminfos[i].IsRetval()))
00189         {
00190             paramname = NewOutParam(stubid, i);
00191             Tcl_UnsetVar2(interp, Tcl_GetString(paramname), NULL, TCL_GLOBAL_ONLY);
00192             Tcl_DecrRefCount(paramname);
00193         }
00194     }
00195 }
00196 
00197 
00202 void
00203 AddObjErrorInfoForBinding(
00204     Tcl_Interp       *interp,       
00205     nsISupports      *isupports,    
00206     nsIInterfaceInfo *interfaceinfo,
00207     PRUint16          methodindex,  
00208     Tcl_Obj          *script        
00209 )
00210 {
00211     const char *name;
00212     const nsXPTMethodInfo *methodinfo;
00213     Tcl_Obj *isupportsobj;
00214 
00215     NS_PRECONDITION(interp, "null pointer");
00216     NS_PRECONDITION(isupports, "null pointer");
00217     NS_PRECONDITION(interfaceinfo, "null pointer");
00218     NS_PRECONDITION(script, "null pointer");
00219 
00220     Tcl_AddObjErrorInfo(interp, "\nwhile executing binding:\n\"", -1);
00221     Tcl_AddObjErrorInfo(interp, Tcl_GetString(script), -1);
00222     Tcl_AddObjErrorInfo(interp, "\"\nfor interface ", -1);
00223     
00224     interfaceinfo->GetMethodInfo(methodindex, &methodinfo);
00225     
00226     if (methodinfo->IsGetter())
00227     {
00228         Tcl_AddObjErrorInfo(interp, "attribute getter \"", -1);
00229     } else if (methodinfo->IsSetter())
00230     {
00231         Tcl_AddObjErrorInfo(interp, "attribute setter \"", -1);
00232     } else
00233     {
00234         Tcl_AddObjErrorInfo(interp, "method \"", -1);
00235     }
00236     
00237     interfaceinfo->GetNameShared(&name);
00238     Tcl_AddObjErrorInfo(interp, name, -1);
00239     Tcl_AddObjErrorInfo(interp, "::", -1);
00240     name = methodinfo->GetName();
00241     Tcl_AddObjErrorInfo(interp, name, -1);
00242     Tcl_AddObjErrorInfo(interp, "\"", -1);
00243     
00244     Tcl_AddObjErrorInfo(interp, "\nin component context \"", -1);
00245     
00246     isupportsobj = TclXPCOM_NewISupportsObj(isupports, interfaceinfo);
00247     Tcl_AddObjErrorInfo(interp, Tcl_GetString(isupportsobj), -1);
00248     Tcl_DecrRefCount(isupportsobj);
00249     
00250     Tcl_AddObjErrorInfo(interp, "\"", -1);
00251 }
00252 
00253 
00259 nsresult
00260 TclXPCOM_CallMethod(
00261     Tcl_Interp        *interp,       
00262     nsXPTCMiniVariant *variants,     
00263     nsISupports       *isupports,    
00264     nsIInterfaceInfo  *interfaceinfo,
00265     PRUint16           methodindex,  
00266     Tcl_Obj           *script        
00267 )
00268 {
00269     int rv;
00270     nsresult res;
00271     nsXPTType type;
00272     char str[128];
00273     
00274     Tcl_SavedResult savedresult;
00275     Tcl_Obj  *errorcode, *errorinfo;
00276     Tcl_Obj  *param, *paramname;
00277     void    **params;
00278     void     *stackedparams[STUBS_BUFFERSIZE];
00279 
00280     PRUint16 j, *tclparams;
00281     PRUint8  i, paramcount;
00282     nsXPTParamInfo *paraminfos;
00283     MethodInfo *minfo;
00284 
00285     int bgerror;
00286     int stubid;
00287     CurrentStubInfo *stubinfo, savedstubinfo;
00288 
00289     NS_PRECONDITION(interp, "null pointer");
00290     NS_PRECONDITION(variants, "null pointer");
00291     NS_PRECONDITION(isupports, "null pointer");
00292     NS_PRECONDITION(interfaceinfo, "null pointer");
00293     NS_PRECONDITION(script, "null pointer");
00294 
00295     minfo = GetMethodInfo(interfaceinfo, methodindex);
00296     tclparams  = minfo->tclparams;
00297     paramcount = minfo->paramcount;
00298     paraminfos = minfo->paraminfos;
00299 
00300     if (paramcount > STUBS_BUFFERSIZE)
00301     {
00302         params = (void **)memmgr->Alloc(sizeof(void *) * paramcount);
00303     } else
00304     {
00305         params = stackedparams;
00306     }
00307 
00308 
00309     Tcl_Preserve(interp);
00310     Tcl_SaveResult(interp, &savedresult);
00311     errorcode = Tcl_GetVar2Ex(interp, "errorCode", NULL, TCL_GLOBAL_ONLY);
00312     errorinfo = Tcl_GetVar2Ex(interp, "errorInfo", NULL, TCL_GLOBAL_ONLY);
00313 
00314     if (errorcode)
00315     {
00316         Tcl_IncrRefCount(errorcode);
00317     }
00318 
00319     if (errorinfo)
00320     {
00321         Tcl_IncrRefCount(errorinfo);
00322     }
00323 
00324     script = Tcl_DuplicateObj(script);
00325     Tcl_IncrRefCount(script);
00326 
00327     stubinfo = (CurrentStubInfo *)Tcl_GetThreadData(&currentStubKey, sizeof(CurrentStubInfo));
00328     savedstubinfo = *stubinfo;
00329 
00330     stubinfo->currentstub   = isupports;
00331     stubinfo->interfaceinfo = interfaceinfo;
00332     stubinfo->returnCode    = NS_OK;
00333     stubinfo->nestlevel++;
00334 
00335     stubid = stubinfo->nestlevel;
00336 
00337     /*  
00338     C++ Calling TCL
00339     0) make sure no out or in/out parameter pointers are NULL (calling function is crappy)
00340     1) read in and in/out parameters
00341     2) free any in/out parameters
00342     3) write NULL to in/out and out parameters
00343     --- thread check,  if on different thread,  post an event & wait for return
00344     4) eval tcl
00345     5) if ok, write in/out and out parameters (shared is weird)
00346     6) if writing fails (bad tcl script returns), cleanup in/out and out parameters and write NULL to them
00347     7) return code
00348     */
00349 
00350     bgerror = 0;
00351 
00352     for (i = 0; i < paramcount; i++)
00353     {
00354         if (paraminfos[i].IsOut())
00355         {
00356             params[i] = variants[i].val.p;
00357             //0) make sure no out or in/out parameter pointers are NULL (calling function is crappy)
00358             if (!params[i])
00359             {
00360                 // return error,  calling function is crap
00361                 res = NS_ERROR_NULL_POINTER;
00362                 goto done;
00363             }
00364 
00365             if (!paraminfos[i].IsIn() && paraminfos[i].GetType().IsPointer())
00366             {
00367                 *(void **)params[i] = NULL;
00368             }
00369         } else
00370         {
00371             params[i] = &variants[i];
00372         }
00373     }
00374 
00375     //1) read in and in/out parameters
00376     for (i = 0; i < paramcount; i++)
00377     {
00378         if ((tclparams[i] & PARAM_IS_SKIPPED) || (tclparams[i] & PARAM_IS_RETVAL))
00379         {
00380             continue;
00381         }
00382 
00383         j = (tclparams[i] & PARAM_INDEX_MASK);
00384         
00385         if (paraminfos[i].IsIn() && !paraminfos[i].IsDipper())
00386         {
00387             // read parameter
00388             type = paraminfos[i].GetType();
00389 
00390             res = Native2Tcl(params, params[i], (nsXPTType *)&paraminfos[i].type, interfaceinfo, methodindex, &paraminfos[i], &param);
00391             if (NS_FAILED(res))
00392             {
00393                 goto done;
00394             }
00395             
00396             // append parameter to arg list or append name of set variable
00397             if (paraminfos[i].IsOut())
00398             {
00399                 // create a variable
00400                 paramname = NewOutParam(stubid, i);
00401                 Tcl_ObjSetVar2(interp, paramname, NULL, param, TCL_GLOBAL_ONLY);
00402                 param = paramname;
00403             }
00404         } else
00405         {
00406             param = NewOutParam(stubid, i);
00407             Tcl_UnsetVar2(interp, Tcl_GetString(param), NULL, TCL_GLOBAL_ONLY);
00408         }
00409 
00410         rv = Tcl_ListObjAppendElement(NULL, script, param);
00411         NS_ASSERTION((rv == TCL_OK), "script is not a valid list");
00412     }
00413 
00414     //2) free any in/out parameters
00415     //3) write NULL to in/out and out parameters
00416     CleanupOutParameters(params, paraminfos, paramcount, interfaceinfo, methodindex);
00417 
00418     //4) eval tcl
00419     rv = Tcl_EvalObjEx(interp, script, TCL_EVAL_DIRECT | TCL_EVAL_GLOBAL);
00420 
00421     if (rv == TCL_OK)
00422     {
00423         res = stubinfo->returnCode;
00424     } else
00425     {
00426         if (!TclXPCOM_GetErrorCode(interp, &res))
00427         {
00428             res = NS_ERROR_FAILURE;
00429             bgerror = 1;
00430         }
00431     }
00432 
00433     if (NS_FAILED(res))
00434     {
00435         goto done;
00436     }
00437     
00438     //5) if ok, write in/out and out parameters (shared is weird)
00439     for (i = 0; i < paramcount; i++)
00440     {
00441         if (tclparams[i] & PARAM_IS_SKIPPED)
00442         {
00443             continue;
00444         }
00445 
00446         j = (tclparams[i] & PARAM_INDEX_MASK);
00447         
00448         if (paraminfos[i].IsOut() || paraminfos[i].IsDipper())
00449         {
00450             if (paraminfos[i].IsRetval())
00451             {
00452                 param = Tcl_GetObjResult(interp);
00453             } else
00454             {
00455                 paramname = NewOutParam(stubid, i);
00456                 param = Tcl_ObjGetVar2(interp, paramname, NULL, TCL_GLOBAL_ONLY);
00457                 Tcl_DecrRefCount(paramname);
00458                 if (!param)
00459                 {
00460                     // error if param is null
00461                     Tcl_ResetResult(interp);
00462                     Tcl_AppendResult(interp, "out parameter was not set", NULL);
00463                     rv = TCL_ERROR;
00464                 }
00465             }
00466             
00467             if (rv == TCL_OK)
00468             {
00469                 type = paraminfos[i].GetType();
00470                 rv = Tcl2Native(interp, param, params, params[i], &type, tclparams[i] & PARAM_IS_SHARED_OUT, interfaceinfo, methodindex, &paraminfos[i]);
00471             }
00472 
00473             // if error,  while 'writing parameter #'
00474             if (rv != TCL_OK)
00475             {
00476                 //6) if writing fails (bad tcl script returns), cleanup in/out and out parameters and write NULL to them
00477                 if (paraminfos[i].IsRetval())
00478                 {
00479                     sprintf(str, "\nwhile writing return parameter");
00480                 } else
00481                 {
00482                     sprintf(str, "\nwhile writing out parameter #%d", j);
00483                 }
00484                 Tcl_AddObjErrorInfo(interp, str, -1);
00485 
00486                 CleanupOutParameters(params, paraminfos, paramcount, interfaceinfo, methodindex);
00487                 res = NS_ERROR_FAILURE;
00488                 bgerror = 1;
00489                 goto done;
00490             }
00491         }
00492     }
00493 
00494 done:
00495     
00496     CleanupTclOutVariables(stubid, interp, paraminfos, paramcount, tclparams);
00497 
00498     if (bgerror)
00499     {
00500         AddObjErrorInfoForBinding(interp, isupports, interfaceinfo, methodindex, script);
00501         TclXPCOM_BackgroundError(interp);
00502     }
00503 
00504     if (paramcount > STUBS_BUFFERSIZE)
00505     {
00506         memmgr->Free(params);
00507     }
00508 
00509     *stubinfo = savedstubinfo;
00510     Tcl_DecrRefCount(script);
00511 
00512     if (errorcode)
00513     {
00514         Tcl_SetVar2Ex(interp, "errorCode", NULL, errorcode, TCL_GLOBAL_ONLY);
00515         Tcl_DecrRefCount(errorcode);
00516     }
00517 
00518     if (errorinfo)
00519     {
00520         Tcl_SetVar2Ex(interp, "errorInfo", NULL, errorinfo, TCL_GLOBAL_ONLY);
00521         Tcl_DecrRefCount(errorinfo);
00522     }
00523 
00524     Tcl_RestoreResult(interp, &savedresult);
00525     Tcl_Release(interp);
00526     return res;
00527 }
00528 

Generated on Fri Jun 14 23:25:51 2002 for TclXPCOM by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002