Back to index

lightning-sunbird  0.9+nobinonly
XPCDispConvert.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  * ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is the IDispatch implementation for XPConnect.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * David Bradley.
00019  * Portions created by the Initial Developer are Copyright (C) 2002
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
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 
00043 #include "xpcprivate.h"
00044 
00045 VARTYPE XPCDispConvert::JSTypeToCOMType(XPCCallContext& ccx, jsval val)
00046 {
00047     if(JSVAL_IS_PRIMITIVE(val))
00048     {
00049         if(JSVAL_IS_STRING(val))
00050         {
00051             return VT_BSTR;
00052         }
00053         if(JSVAL_IS_INT(val))
00054         {
00055             return VT_I4;
00056         }
00057         if(JSVAL_IS_DOUBLE(val))
00058         {
00059             return VT_R8;
00060         }
00061         if(JSVAL_IS_BOOLEAN(val))
00062         {
00063             return VT_BOOL;
00064         }
00065         if(JSVAL_IS_VOID(val))
00066         {
00067             return VT_EMPTY;
00068         }
00069         if(JSVAL_IS_NULL(val))
00070         {
00071             return VT_NULL;
00072         }
00073     }
00074     else
00075     {
00076         if(JS_IsArrayObject(ccx, JSVAL_TO_OBJECT(val)))
00077             return VT_ARRAY | VT_VARIANT;
00078         return VT_DISPATCH;
00079     }
00080     NS_ERROR("XPCDispConvert::JSTypeToCOMType was unable to identify the type of the jsval");
00081     return VT_EMPTY;
00082 }
00083 
00084 JSBool XPCDispConvert::JSArrayToCOMArray(XPCCallContext& ccx, JSObject *obj, 
00085                                          VARIANT & var, nsresult& err)
00086 {
00087     err = NS_OK;
00088     jsuint len;
00089     if(!JS_GetArrayLength(ccx, obj, &len))
00090     {
00091         // TODO: I think we should create a specific error for this
00092         err = NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY;
00093         return PR_FALSE;
00094     }
00095     // Create the safe array of variants and populate it
00096     SAFEARRAY * array = nsnull;
00097     VARIANT* varArray = 0;
00098     for(jsuint index = 0; index < len; ++index)
00099     {
00100         jsval val;
00101         if(JS_GetElement(ccx, obj, index, &val))
00102         {
00103             if(!JSVAL_IS_VOID(val))
00104             {
00105                 if(!array)
00106                 {
00107                     // Create an array that starts at index, and has len 
00108                     // elements
00109                     array = SafeArrayCreateVector(VT_VARIANT, index, len - index);
00110                     if(!array)
00111                     {
00112                         err = NS_ERROR_OUT_OF_MEMORY;
00113                         return JS_FALSE;
00114                     }
00115                     if(FAILED(SafeArrayAccessData(array, NS_REINTERPRET_CAST(void**,&varArray))))
00116                     {
00117                         err = NS_ERROR_FAILURE;
00118                         return JS_FALSE;
00119                     }
00120                 }
00121                 if(!JSToCOM(ccx, val, *varArray, err))
00122                 {
00123                     SafeArrayUnaccessData(array);
00124                     err = NS_ERROR_FAILURE;
00125                     // This cleans up the elements as well
00126                     SafeArrayDestroyData(array);
00127                     return JS_FALSE;
00128                 }
00129             }
00130             if(varArray)
00131                 ++varArray;
00132         }
00133     }
00134     SafeArrayUnaccessData(array);
00135     var.vt = VT_ARRAY | VT_VARIANT;
00136     var.parray = array;
00137     return JS_TRUE;
00138 }
00139 
00140 #define XPC_ASSIGN(src, dest, data) *dest.p##data = src.data
00141 
00150 inline
00151 JSBool xpc_CopyVariantByRef(VARIANT & src, VARIANT & dest)
00152 {
00153     VARIANT temp;
00154     VARTYPE vt = dest.vt & ~(VT_BYREF);
00155     if(vt != src.vt)
00156     {
00157         // TODO: This fails more than I had hoped, we may want to
00158         // add some logic to handle more conversions
00159         if(FAILED(VariantChangeType(&temp, &src, VARIANT_ALPHABOOL, vt)))
00160         {
00161             return JS_FALSE;
00162         }
00163     }
00164     else
00165         temp = src;
00166     switch (vt)
00167     {
00168         case VT_I2:
00169         {
00170             XPC_ASSIGN(temp, dest, iVal);
00171         }
00172         break;
00173            case VT_I4:
00174         {
00175             XPC_ASSIGN(temp, dest, lVal);
00176         }
00177         break;
00178            case VT_R4:
00179         {
00180             XPC_ASSIGN(temp, dest, fltVal);
00181         }
00182         break;
00183            case VT_R8:
00184         {
00185             XPC_ASSIGN(temp, dest, dblVal);
00186         }
00187         break;
00188            case VT_CY:
00189         {
00190             XPC_ASSIGN(temp, dest, cyVal);
00191         }
00192         break;
00193            case VT_DATE:
00194         {
00195             XPC_ASSIGN(temp, dest, date);
00196         }
00197         break;
00198            case VT_BSTR:
00199         {
00200             XPC_ASSIGN(temp, dest, bstrVal);
00201         }
00202         break;
00203            case VT_DISPATCH:
00204         {
00205             XPC_ASSIGN(temp, dest, pdispVal);
00206         }
00207         break;
00208            case VT_ERROR:
00209         {
00210             XPC_ASSIGN(temp, dest, scode);
00211         }
00212         break;
00213            case VT_BOOL:
00214         {
00215             XPC_ASSIGN(temp, dest, boolVal);
00216         }
00217         break;
00218            case VT_VARIANT:
00219         {
00220             // Not Supported right now
00221             return JS_FALSE;
00222         }
00223         break;
00224            case VT_I1:
00225         {
00226             XPC_ASSIGN(temp, dest, cVal);
00227         }
00228         break;
00229            case VT_UI1:
00230         {
00231             XPC_ASSIGN(temp, dest, bVal);
00232         }
00233         break;
00234            case VT_UI2:
00235         {
00236             XPC_ASSIGN(temp, dest, iVal);
00237         }
00238         break;
00239            case VT_UI4:
00240         {
00241             XPC_ASSIGN(temp, dest, uiVal);
00242         }
00243         break;
00244            case VT_INT:
00245         {
00246             XPC_ASSIGN(temp, dest, intVal);
00247         }
00248         break;
00249            case VT_UINT:
00250         {
00251             XPC_ASSIGN(temp, dest, uintVal);
00252         }
00253         break;
00254         default:
00255         {
00256             return JS_FALSE;
00257         }
00258     }
00259     return JS_TRUE;
00260 }
00261 
00262 JSBool XPCDispConvert::JSToCOM(XPCCallContext& ccx,
00263                                jsval src,
00264                                VARIANT & dest,
00265                                nsresult& err,
00266                                JSBool isByRef)
00267 {
00268     err = NS_OK;
00269     VARIANT byRefVariant;
00270     VARIANT * varDest = isByRef ? &byRefVariant : &dest;
00271     varDest->vt = JSTypeToCOMType(ccx, src);
00272     switch (varDest->vt)
00273     {
00274         case VT_BSTR:
00275         {
00276             JSString* str = JSVAL_TO_STRING(src);
00277             jschar * chars = JS_GetStringChars(str);
00278             if(!chars)
00279             {
00280                 err = NS_ERROR_XPC_BAD_CONVERT_NATIVE;
00281                 // Avoid cleaning up garbage
00282                 varDest->vt = VT_EMPTY;
00283                 return JS_FALSE;
00284             }
00285 
00286             CComBSTR val(JS_GetStringLength(str),
00287                          NS_REINTERPRET_CAST(const WCHAR *, chars));
00288             varDest->bstrVal = val.Detach();
00289         }
00290         break;
00291         case VT_I4:
00292         {
00293             varDest->vt = VT_I4;
00294             varDest->lVal = JSVAL_TO_INT(src);
00295         }
00296         break;
00297         case VT_R8:
00298         {
00299             varDest->vt = VT_R8;
00300             varDest->dblVal = *JSVAL_TO_DOUBLE(src);
00301         }
00302         break;
00303         case VT_EMPTY:
00304         case VT_NULL:
00305         break;
00306         case VT_ARRAY | VT_VARIANT:
00307         {
00308             JSObject * obj = JSVAL_TO_OBJECT(src);
00309             return JSArrayToCOMArray(ccx, obj, *varDest, err);
00310         }
00311         break;
00312         case VT_DISPATCH:
00313         {
00314             JSObject * obj = JSVAL_TO_OBJECT(src);
00315             IUnknown * pUnknown = nsnull;
00316             if(!XPCConvert::JSObject2NativeInterface(
00317                 ccx, 
00318                 (void**)&pUnknown, 
00319                 obj, 
00320                 &NSID_IDISPATCH,
00321                 nsnull, 
00322                 &err))
00323             {
00324                 // Avoid cleaning up garbage
00325                 varDest->vt = VT_EMPTY;
00326                 return JS_FALSE;
00327             }
00328             varDest->vt = VT_DISPATCH;
00329             pUnknown->QueryInterface(IID_IDispatch, 
00330                                      NS_REINTERPRET_CAST(void**,
00331                                                          &varDest->pdispVal));
00332             NS_IF_RELEASE(pUnknown);
00333         }
00334         break;
00335         case VT_BOOL:
00336         {
00337             varDest->boolVal = JSVAL_TO_BOOLEAN(src) ? VARIANT_TRUE : VARIANT_FALSE;
00338         }
00339         break;
00340         default:
00341         {
00342             NS_ERROR("This is out of synce with XPCDispConvert::JSTypeToCOMType");
00343             err = NS_ERROR_XPC_BAD_CONVERT_NATIVE;
00344             // Avoid cleaning up garbage
00345             varDest->vt = VT_EMPTY;
00346             return JS_FALSE;
00347         }
00348         break;
00349     }
00350     if(isByRef)
00351     {
00352         if(!xpc_CopyVariantByRef(byRefVariant, dest))
00353         {
00354             // Avoid cleaning up garbage
00355             dest.vt = VT_EMPTY;
00356         }
00357     }
00358     return JS_TRUE;
00359 }
00360 
00361 JSBool XPCDispConvert::COMArrayToJSArray(XPCCallContext& ccx,
00362                                          const VARIANT & src,
00363                                          jsval & dest, nsresult& err)
00364 {
00365     err = NS_OK;
00366     // We only support one dimensional arrays for now
00367     if(SafeArrayGetDim(src.parray) != 1)
00368     {
00369         err = NS_ERROR_FAILURE;
00370         return JS_FALSE;
00371     }
00372     // Get the upper bound;
00373     long ubound;
00374     if(FAILED(SafeArrayGetUBound(src.parray, 1, &ubound)))
00375     {
00376         err = NS_ERROR_FAILURE;
00377         return JS_FALSE;
00378     }
00379     // Get the lower bound
00380     long lbound;
00381     if(FAILED(SafeArrayGetLBound(src.parray, 1, &lbound)))
00382     {
00383         err = NS_ERROR_FAILURE;
00384         return JS_FALSE;
00385     }
00386     // Create the JS Array
00387     JSObject * array = JS_NewArrayObject(ccx, ubound - lbound + 1, nsnull);
00388     if(!array)
00389     {
00390         err = NS_ERROR_OUT_OF_MEMORY;
00391         return JS_FALSE;
00392     }
00393     AUTO_MARK_JSVAL(ccx, OBJECT_TO_JSVAL(array));
00394     // Divine the type of our array
00395     VARTYPE vartype;
00396     if((src.vt & VT_ARRAY) != 0)
00397     {
00398         vartype = src.vt & ~VT_ARRAY;
00399     }
00400     else // This was maybe a VT_SAFEARRAY
00401     {
00402         if(FAILED(SafeArrayGetVartype(src.parray, &vartype)))
00403             return JS_FALSE;
00404     }
00405     jsval val = JSVAL_NULL;
00406     AUTO_MARK_JSVAL(ccx, &val);
00407     for(long index = lbound; index <= ubound; ++index)
00408     {
00409         // Divine the type of our array
00410         _variant_t var;
00411         var.vt = vartype;
00412         if(FAILED(SafeArrayGetElement(src.parray, &index, &var.byref)))
00413         {
00414             err = NS_ERROR_FAILURE;
00415             return JS_FALSE;
00416         }
00417         if(!COMToJS(ccx, var, val, err))
00418             return JS_FALSE;
00419         JS_SetElement(ccx, array, index, &val);
00420     }
00421     dest = OBJECT_TO_JSVAL(array);
00422     return JS_TRUE;
00423 }
00424 
00432 inline
00433 jsval StringToJSVal(JSContext* cx, const PRUnichar * str, PRUint32 len)
00434 {
00435     JSString * s = JS_NewUCStringCopyN(cx,
00436                                        NS_REINTERPRET_CAST(const jschar *, str),
00437                                        len);
00438     if(s)
00439         return STRING_TO_JSVAL(s);
00440     else
00441         return JSVAL_NULL;
00442 }
00443 
00444 
00445 #define VALUE(val) (isPtr ? *src.p##val : src.val)
00446 JSBool XPCDispConvert::COMToJS(XPCCallContext& ccx, const VARIANT& src,
00447                                jsval& dest, nsresult& err)
00448 {
00449     err = NS_OK;
00450     if(src.vt & VT_ARRAY || src.vt == VT_SAFEARRAY)
00451     {
00452         return COMArrayToJSArray(ccx, src, dest, err);
00453     }
00454     PRBool isPtr = src.vt & VT_BYREF;
00455     switch (src.vt & ~(VT_BYREF))
00456     {
00457         case VT_UINT:
00458         {
00459             return JS_NewNumberValue(ccx, VALUE(uintVal), &dest);
00460         }
00461         break;
00462         case VT_UI4:
00463         {
00464             return JS_NewNumberValue(ccx, VALUE(ulVal), &dest);
00465         }
00466         break;
00467         case VT_INT:
00468         {
00469             return JS_NewNumberValue(ccx, VALUE(intVal), &dest);
00470         }
00471         break;
00472         case VT_I4:
00473         {
00474             return JS_NewNumberValue(ccx, VALUE(lVal), &dest);
00475         }
00476         break;
00477         case VT_UI1:
00478         {
00479             dest = INT_TO_JSVAL(VALUE(bVal));
00480         }
00481         break;
00482         case VT_I1:
00483         {
00484             dest = INT_TO_JSVAL(VALUE(cVal));
00485         }
00486         break;
00487         case VT_UI2:
00488         {
00489             dest = INT_TO_JSVAL(VALUE(uiVal));
00490         }
00491         break;
00492         case VT_I2:
00493         {
00494             dest = INT_TO_JSVAL(VALUE(iVal));
00495         }
00496         break;
00497         case VT_R4:
00498         {
00499             return JS_NewNumberValue(ccx, VALUE(fltVal), &dest);
00500         }
00501         break;
00502         case VT_R8:
00503         {
00504             return JS_NewNumberValue(ccx, VALUE(dblVal), &dest);
00505         }
00506         break;
00507         case VT_BOOL:
00508         {
00509             dest = BOOLEAN_TO_JSVAL(VALUE(boolVal) != VARIANT_FALSE ? JS_TRUE : JS_FALSE);
00510         }
00511         break;
00512         case VT_DISPATCH:
00513         {
00514             XPCDispObject::WrapIDispatch(VALUE(pdispVal), ccx,
00515                                          JS_GetGlobalObject(ccx), &dest);
00516         }
00517         break;
00518         case VT_DATE:
00519         {
00520             // Convert date to string and frees it when we're done
00521             _bstr_t str(src);
00522             dest = StringToJSVal(ccx, str, str.length());
00523         }
00524         break;
00525         case VT_EMPTY:
00526         {
00527             dest = JSVAL_VOID;
00528         }
00529         break;
00530         case VT_NULL:
00531         {
00532             dest = JSVAL_NULL;
00533         }
00534         break;
00535         case VT_ERROR:
00536         {
00537             return JS_NewNumberValue(ccx, VALUE(scode), &dest);
00538         }
00539         break;
00540         case VT_CY:
00541         {
00542             return JS_NewNumberValue(
00543                 ccx, 
00544                 NS_STATIC_CAST(double,
00545                                isPtr ? src.pcyVal->int64 : 
00546                                        src.cyVal.int64) / 100.0,
00547                 &dest);
00548         }
00549         break;
00553         case VT_UNKNOWN:
00554         default:
00555         {
00556             // Last ditch effort to convert to string
00557             if(FAILED(VariantChangeType(NS_CONST_CAST(VARIANT*,&src), 
00558                                          NS_CONST_CAST(VARIANT*,&src), 
00559                                          VARIANT_ALPHABOOL, VT_BSTR)))
00560             {
00561                 err = NS_ERROR_XPC_BAD_CONVERT_JS;
00562                 return JS_FALSE;
00563             }
00564             isPtr = FALSE;
00565         } // Fall through on success
00566         case VT_BSTR:
00567         {
00568             dest = StringToJSVal(ccx, VALUE(bstrVal), SysStringLen(VALUE(bstrVal)));
00569         }
00570         break;
00571     }
00572     return JS_TRUE;
00573 }