/* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is the Python XPCOM language bindings. * * The Initial Developer of the Original Code is * ActiveState Tool Corp. * Portions created by the Initial Developer are Copyright (C) 2000 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Mark Hammond (original author) * * Unicode corrections by Shane Hathaway (http://hathawaymix.org), * inspired by Mikhail Sobolev * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ // // This code is part of the XPCOM extensions for Python. // // Written May 2000 by Mark Hammond. // // Based heavily on the Python COM support, which is // (c) Mark Hammond and Greg Stein. // // (c) 2000, ActiveState corp. #include "PyXPCOM_std.h" #include #include #include #include // ------------------------------------------------------------------------ // nsString utilities // ------------------------------------------------------------------------ // one day we may know what they look like. static inline PRBool IsNullDOMString( const nsAString& aString ) { return PR_FALSE; } static inline PRBool IsNullDOMString( const nsACString& aString ) { return PR_FALSE; } #define PyUnicode_FromPRUnichar(src, size) \ PyUnicode_DecodeUTF16((char*)(src),sizeof(PRUnichar)*(size),NULL,NULL) // Create a zero-terminated PRUnichar buffer from a Python unicode. // On success, returns 0. On failure, returns -1 and sets an exception. // dest_out must not be null. size_out may be null. static int PyUnicode_AsPRUnichar(PyObject *obj, PRUnichar **dest_out, PRUint32 *size_out) { PRUint32 size; PyObject *s; const void *src; PRUnichar *dest; s = PyUnicode_AsUTF16String(obj); if (!s) return -1; // Drop the UTF-16 byte order mark at the beginning of // the string. (See the docs on PyUnicode_AsUTF16String.) // Some Mozilla libraries don't like the mark. #if PY_MAJOR_VERSION <= 2 size = (PyString_GET_SIZE(s) - 2) / sizeof(PRUnichar); src = PyString_AS_STRING(s) + 2; #else if (!PyBytes_Check(s)) { PyErr_SetString(PyExc_TypeError, "internal error in PyXPCOM, parameter must be a bytes object"); return -1; } size = (PyBytes_GET_SIZE(s) - 2) / sizeof(PRUnichar); src = PyBytes_AS_STRING(s) + 2; #endif dest = (PRUnichar *)nsMemory::Alloc(sizeof(PRUnichar) * (size + 1)); if (!dest) { PyErr_NoMemory(); Py_DECREF(s); return -1; } memcpy(dest, src, sizeof(PRUnichar) * size); Py_DECREF(s); dest[size] = 0; *dest_out = dest; if (size_out) *size_out = size; return 0; } PyObject *PyObject_FromNSString( const nsACString &s, PRBool bAssumeUTF8 /*= PR_FALSE */) { PyObject *ret; if (IsNullDOMString(s)) { ret = Py_None; Py_INCREF(Py_None); } else { if (bAssumeUTF8) { const nsPromiseFlatCString& temp = PromiseFlatCString(s); ret = PyUnicode_DecodeUTF8(temp.get(), temp.Length(), NULL); } else { #if PY_MAJOR_VERSION <= 2 ret = PyString_FromStringAndSize(NULL, s.Length()); #else ret = PyUnicode_FromStringAndSize(NULL, s.Length()); #endif if (!ret) return NULL; // Need "CopyAsciiTo"!? nsACString::const_iterator fromBegin, fromEnd; #if PY_MAJOR_VERSION <= 2 char* dest = (char *)PyString_AS_STRING(ret); #else char* dest = (char *)PyUnicode_AsUTF8(ret); #endif copy_string(s.BeginReading(fromBegin), s.EndReading(fromEnd), dest); } } return ret; } PyObject *PyObject_FromNSString( const nsAString &s ) { PyObject *ret; if (IsNullDOMString(s)) { ret = Py_None; Py_INCREF(Py_None); } else { const nsPromiseFlatString& temp = PromiseFlatString(s); ret = PyUnicode_FromPRUnichar(temp.get(), temp.Length()); } return ret; } PyObject *PyObject_FromNSString( const PRUnichar *s, PRUint32 len /* = (PRUint32)-1*/) { return PyUnicode_FromPRUnichar(s, len==((PRUint32)-1)? nsCRT::strlen(s) : len); } PRBool PyObject_AsNSString( PyObject *val, nsAString &aStr) { if (val == Py_None) { aStr.Truncate(); return NS_OK; } PyObject *val_use = NULL; PRBool ok = PR_TRUE; #if PY_MAJOR_VERSION <= 2 if (!PyString_Check(val) && !PyUnicode_Check(val)) { PyErr_SetString(PyExc_TypeError, "This parameter must be a string or Unicode object"); ok = PR_FALSE; } if (ok && (val_use = PyUnicode_FromObject(val))==NULL) ok = PR_FALSE; #else if (!PyUnicode_Check(val)) { PyErr_SetString(PyExc_TypeError, "This parameter must be a unicode object"); ok = PR_FALSE; } val_use = val; Py_INCREF(val_use); #endif if (ok) { if (PyUnicode_GET_SIZE(val_use) == 0) { aStr.Truncate(); } else { PRUint32 nch; PRUnichar *tempo; // can we do this without the copy? if (PyUnicode_AsPRUnichar(val_use, &tempo, &nch) < 0) return PR_FALSE; aStr.Assign(tempo, nch); nsMemory::Free(tempo); } } Py_XDECREF(val_use); return ok; } // Array utilities static PRUint32 GetArrayElementSize( PRUint8 t) { PRUint32 ret; switch (t & XPT_TDP_TAGMASK) { case nsXPTType::T_U8: case nsXPTType::T_I8: ret = sizeof(PRInt8); break; case nsXPTType::T_I16: case nsXPTType::T_U16: ret = sizeof(PRInt16); break; case nsXPTType::T_I32: case nsXPTType::T_U32: ret = sizeof(PRInt32); break; case nsXPTType::T_I64: case nsXPTType::T_U64: ret = sizeof(PRInt64); break; case nsXPTType::T_FLOAT: ret = sizeof(float); break; case nsXPTType::T_DOUBLE: ret = sizeof(double); break; case nsXPTType::T_BOOL: ret = sizeof(PRBool); break; case nsXPTType::T_CHAR: ret = sizeof(char); break; case nsXPTType::T_WCHAR: ret = sizeof(PRUnichar); break; case nsXPTType::T_IID: case nsXPTType::T_CHAR_STR: case nsXPTType::T_WCHAR_STR: case nsXPTType::T_INTERFACE: case nsXPTType::T_DOMSTRING: case nsXPTType::T_INTERFACE_IS: case nsXPTType::T_PSTRING_SIZE_IS: case nsXPTType::T_CSTRING: case nsXPTType::T_ASTRING: case nsXPTType::T_UTF8STRING: ret = sizeof( void * ); break; default: NS_ABORT_IF_FALSE(0, "Unknown array type code!"); ret = 0; break; } return ret; } static nsresult GetArrayElementIID(Py_nsISupports* self, nsXPTCVariant* dispatchParams, PRUint16 methodIndex, PRUint8 paramIndex, nsIID *result) { nsCOMPtr iim = XPTI_GetInterfaceInfoManager(); nsCOMPtr ii; nsresult rc; rc = iim->GetInfoForIID(&self->m_iid, getter_AddRefs(ii)); if (NS_FAILED(rc)) return rc; const nsXPTMethodInfo *mi; rc = ii->GetMethodInfo(methodIndex, &mi); if (NS_FAILED(rc)) return rc; const nsXPTParamInfo& paramInfo = mi->GetParam(paramIndex); if (!paramInfo.GetType().IsArray()) { PyXPCOM_LogWarning("Passing non-array to GetArrayElementIID\n"); return NS_ERROR_INVALID_ARG; } nsXPTType elemType; rc = ii->GetTypeForParam(methodIndex, ¶mInfo, 1, &elemType); if (NS_FAILED(rc)) return rc; PRUint8 tag = elemType.TagPart(); if (tag == nsXPTType::T_INTERFACE) { rc = ii->GetIIDForParamNoAlloc(methodIndex, ¶mInfo, result); } else if (tag == nsXPTType::T_INTERFACE_IS) { PyXPCOM_LogWarning("Unable to handle T_INTERFACE_IS yet\n"); return NS_ERROR_NOT_IMPLEMENTED; } else { // this may be valid case, for arrays of other types // we don't need IID for rc = NS_ERROR_INVALID_ARG; } return rc; } static void FreeSingleArray(void *array_ptr, PRUint32 sequence_size, PRUint8 array_type) { // Free each array element - NOT the array itself // Thus, we only need to free arrays or pointers. void **p = (void **)array_ptr; PRUint32 i; switch(array_type & XPT_TDP_TAGMASK) { case nsXPTType::T_IID: case nsXPTType::T_CHAR_STR: case nsXPTType::T_WCHAR_STR: for (i=0; iRelease(); Py_END_ALLOW_THREADS; } break; // Ones we know need no deallocation case nsXPTType::T_U8: case nsXPTType::T_I8: case nsXPTType::T_I16: case nsXPTType::T_U16: case nsXPTType::T_I32: case nsXPTType::T_U32: case nsXPTType::T_I64: case nsXPTType::T_U64: case nsXPTType::T_FLOAT: case nsXPTType::T_DOUBLE: case nsXPTType::T_BOOL: case nsXPTType::T_CHAR: case nsXPTType::T_WCHAR: break; // And a warning should new type codes appear, as they may need deallocation. default: PyXPCOM_LogWarning("Deallocating unknown type %d (0x%x) - possible memory leak\n"); break; } } #define FILL_SIMPLE_POINTER( type, val ) *((type *)pthis) = (type)(val) #define BREAK_FALSE {rc=PR_FALSE;break;} static PRBool FillSingleArray(void *array_ptr, PyObject *sequence_ob, PRUint32 sequence_size, PRUint32 array_element_size, PRUint8 array_type, nsIID *pIID) { PRUint8 *pthis = (PRUint8 *)array_ptr; NS_ABORT_IF_FALSE(pthis, "Don't have a valid array to fill!"); PRBool rc = PR_TRUE; // We handle T_U8 specially as a string/Unicode. // If it is NOT a string, we just fall through and allow the standard // sequence unpack code process it (just slower!) #if PY_MAJOR_VERSION <= 2 if ( array_type == nsXPTType::T_U8 && (PyString_Check(sequence_ob) || PyUnicode_Check(sequence_ob))) { #else if ( array_type == nsXPTType::T_U8 && PyUnicode_Check(sequence_ob)) { #endif PRBool release_seq; if (PyUnicode_Check(sequence_ob)) { release_seq = PR_TRUE; #if PY_MAJOR_VERSION <= 2 sequence_ob = PyObject_Str(sequence_ob); #else sequence_ob = PyUnicode_AsUTF8String(sequence_ob); #endif } else release_seq = PR_FALSE; if (!sequence_ob) // presumably a memory error, or Unicode encoding error. return PR_FALSE; #if PY_MAJOR_VERSION <= 2 memcpy(pthis, PyString_AS_STRING(sequence_ob), sequence_size); #else memcpy(pthis, PyUnicode_AsUTF8(sequence_ob), sequence_size); #endif if (release_seq) { Py_DECREF(sequence_ob); } return PR_TRUE; } for (PRUint32 i=0; rc && iRelease(); Py_END_ALLOW_THREADS; } *pp = pnew; // ref-count added by InterfaceFromPyObject break; } default: // try and limp along in this case. // leave rc TRUE PyXPCOM_LogWarning("Converting Python object for an array element - The object type (0x%x) is unknown - leaving param alone!\n", array_type); break; } Py_XDECREF(val_use); Py_DECREF(val); } return rc; } static PyObject *UnpackSingleArray(Py_nsISupports *parent, void *array_ptr, PRUint32 sequence_size, PRUint8 array_type, nsIID *iid) { if (array_ptr==NULL) { Py_INCREF(Py_None); return Py_None; } if (array_type == nsXPTType::T_U8) #if PY_MAJOR_VERSION <= 2 return PyString_FromStringAndSize( (char *)array_ptr, sequence_size ); #else return PyBytes_FromStringAndSize( (char *)array_ptr, sequence_size ); #endif PRUint32 array_element_size = GetArrayElementSize(array_type); PyObject *list_ret = PyList_New(sequence_size); PRUint8 *pthis = (PRUint8 *)array_ptr; for (PRUint32 i=0; iEquals(NS_GET_IID(nsIVariant))) val = PyObject_FromVariant(parent, (nsIVariant *)*pp); else if (parent) val = parent->MakeInterfaceResult(*pp, iid ? *iid : NS_GET_IID(nsISupports)); else val = Py_nsISupports::PyObjectFromInterface( *pp, iid ? *iid : NS_GET_IID(nsISupports), PR_TRUE); break; } default: { char buf[128]; sprintf(buf, "Unknown XPCOM array type flags (0x%x)", array_type); PyXPCOM_LogWarning("%s - returning a string object with this message!\n", buf); #if PY_MAJOR_VERSION <= 2 val = PyString_FromString(buf); #else val = PyUnicode_FromString(buf); #endif break; } } if (val==NULL) { NS_ABORT_IF_FALSE(PyErr_Occurred(), "NULL result in array conversion, but no error set!"); return NULL; } PyList_SET_ITEM(list_ret, i, val); // ref-count consumed. } return list_ret; } // ------------------------------------------------------------------------ // nsIVariant utilities // ------------------------------------------------------------------------ struct BVFTResult { BVFTResult() {pis = NULL;iid=Py_nsIID_NULL;} nsISupports *pis; nsIID iid; }; static PRUint16 BestVariantTypeForPyObject( PyObject *ob, BVFTResult *pdata = NULL) { nsISupports *ps = NULL; nsIID iid; // start with some fast concrete checks. if (ob==Py_None) return nsIDataType::VTYPE_EMPTY; if (ob==Py_True || ob == Py_False) return nsIDataType::VTYPE_BOOL; if (PyInt_Check(ob)) return nsIDataType::VTYPE_INT32; if (PyLong_Check(ob)) return nsIDataType::VTYPE_INT64; if (PyFloat_Check(ob)) return nsIDataType::VTYPE_DOUBLE; #if PY_MAJOR_VERSION <= 2 if (PyString_Check(ob)) return nsIDataType::VTYPE_STRING_SIZE_IS; #endif if (PyUnicode_Check(ob)) return nsIDataType::VTYPE_WSTRING_SIZE_IS; if (PyTuple_Check(ob) || PyList_Check(ob)) { if (PySequence_Length(ob)) return nsIDataType::VTYPE_ARRAY; return nsIDataType::VTYPE_EMPTY_ARRAY; } // Now do expensive or abstract checks. if (Py_nsISupports::InterfaceFromPyObject(ob, NS_GET_IID(nsISupports), &ps, PR_TRUE)) { if (pdata) { pdata->pis = ps; pdata->iid = NS_GET_IID(nsISupports); } else ps->Release(); return nsIDataType::VTYPE_INTERFACE_IS; } else PyErr_Clear(); if (Py_nsIID::IIDFromPyObject(ob, &iid)) { if (pdata) pdata->iid = iid; return nsIDataType::VTYPE_ID; } else PyErr_Clear(); if (PySequence_Check(ob)) { if (PySequence_Length(ob)) return nsIDataType::VTYPE_ARRAY; return nsIDataType::VTYPE_EMPTY_ARRAY; } return (PRUint16)-1; } nsresult PyObject_AsVariant( PyObject *ob, nsIVariant **aRet) { nsresult nr = NS_OK; nsCOMPtr v = do_CreateInstance("@mozilla.org/variant;1", &nr); NS_ENSURE_SUCCESS(nr, nr); // *sigh* - I tried the abstract API (PyNumber_Check, etc) // but our COM instances too often qualify. BVFTResult cvt_result; PRUint16 dt = BestVariantTypeForPyObject(ob, &cvt_result); switch (dt) { case nsIDataType::VTYPE_BOOL: nr = v->SetAsBool(ob==Py_True); break; case nsIDataType::VTYPE_INT32: nr = v->SetAsInt32(PyInt_AsLong(ob)); break; case nsIDataType::VTYPE_INT64: nr = v->SetAsInt64(PyLong_AsLongLong(ob)); break; case nsIDataType::VTYPE_DOUBLE: nr = v->SetAsDouble(PyFloat_AsDouble(ob)); break; case nsIDataType::VTYPE_STRING_SIZE_IS: { #if PY_MAJOR_VERSION <= 2 nr = v->SetAsStringWithSize(PyString_Size(ob), PyString_AsString(ob)); #else Py_ssize_t cb = 0; const char *psz = PyUnicode_AsUTF8AndSize(ob, &cb); nr = v->SetAsStringWithSize(cb, psz); #endif break; } case nsIDataType::VTYPE_WSTRING_SIZE_IS: #if PY_VERSION_HEX >= 0x03030000 if (PyUnicode_GetLength(ob) == 0) { #else if (PyUnicode_GetSize(ob) == 0) { #endif nr = v->SetAsWStringWithSize(0, (PRUnichar*)NULL); } else { PRUint32 nch; PRUnichar *p; if (PyUnicode_AsPRUnichar(ob, &p, &nch) < 0) { PyXPCOM_LogWarning("Failed to convert object to unicode", PyXPCOM_ObTypeName(ob)); nr = NS_ERROR_UNEXPECTED; break; } nr = v->SetAsWStringWithSize(nch, p); nsMemory::Free(p); } break; case nsIDataType::VTYPE_INTERFACE_IS: { nsISupports *ps = cvt_result.pis; nr = v->SetAsInterface(cvt_result.iid, ps); if (ps) { Py_BEGIN_ALLOW_THREADS; // MUST release thread-lock, incase a Python COM object that re-acquires. ps->Release(); Py_END_ALLOW_THREADS; } break; } case nsIDataType::VTYPE_ID: nr = v->SetAsID(cvt_result.iid); break; case nsIDataType::VTYPE_ARRAY: { int seq_length = PySequence_Length(ob); NS_ABORT_IF_FALSE(seq_length!=0, "VTYPE_ARRAY assumes at least one element!"); PyObject *first = PySequence_GetItem(ob, 0); if (!first) break; int array_type = BestVariantTypeForPyObject(first); Py_DECREF(first); // Arrays can't handle all types. This means we lost embedded NULLs. // This should really be fixed in XPCOM. if (array_type == nsIDataType::VTYPE_STRING_SIZE_IS) array_type = nsIDataType::VTYPE_CHAR_STR; if (array_type == nsIDataType::VTYPE_WSTRING_SIZE_IS) array_type = nsIDataType::VTYPE_WCHAR_STR; PRUint32 element_size = GetArrayElementSize(array_type); int cb_buffer_pointer = seq_length * element_size; void *buffer_pointer; if ((buffer_pointer = (void *)nsMemory::Alloc(cb_buffer_pointer)) == nsnull) { nr = NS_ERROR_OUT_OF_MEMORY; break; } memset(buffer_pointer, 0, cb_buffer_pointer); if (FillSingleArray(buffer_pointer, ob, seq_length, element_size, array_type, nsnull)) { nr = v->SetAsArray(array_type, &NS_GET_IID(nsISupports), seq_length, buffer_pointer); FreeSingleArray(buffer_pointer, seq_length, array_type); } else nr = NS_ERROR_UNEXPECTED; nsMemory::Free(buffer_pointer); break; } case nsIDataType::VTYPE_EMPTY: nr = v->SetAsEmpty(); break; case nsIDataType::VTYPE_EMPTY_ARRAY: nr = v->SetAsEmptyArray(); break; case (PRUint16)-1: PyXPCOM_LogWarning("Objects of type '%s' can not be converted to an nsIVariant", PyXPCOM_ObTypeName(ob)); nr = NS_ERROR_UNEXPECTED; default: NS_ABORT_IF_FALSE(0, "BestVariantTypeForPyObject() returned a variant type not handled here!"); PyXPCOM_LogWarning("Objects of type '%s' can not be converted to an nsIVariant", PyXPCOM_ObTypeName(ob)); nr = NS_ERROR_UNEXPECTED; } if (NS_FAILED(nr)) return nr; return v->QueryInterface(NS_GET_IID(nsIVariant), (void **)aRet); } static PyObject *MyBool_FromBool(PRBool v) { PyObject *ret = v ? Py_True : Py_False; Py_INCREF(ret); return ret; } #define GET_FROM_V(Type, FuncGet, FuncConvert) { \ Type t; \ if (NS_FAILED(nr = FuncGet( &t ))) goto done;\ ret = FuncConvert(t);\ break; \ } DECLHIDDEN(PyObject *) PyObject_FromVariantArray( Py_nsISupports *parent, nsIVariant *v) { nsresult nr; NS_PRECONDITION(v, "NULL variant!"); if (!v) return PyXPCOM_BuildPyException(NS_ERROR_INVALID_POINTER); #ifdef NS_DEBUG PRUint16 dt; nr = v->GetDataType(&dt); NS_ABORT_IF_FALSE(dt == nsIDataType::VTYPE_ARRAY, "expected an array variant"); #endif nsIID iid; void *p; PRUint16 type; PRUint32 count; nr = v->GetAsArray(&type, &iid, &count, &p); if (NS_FAILED(nr)) return PyXPCOM_BuildPyException(nr); PyObject *ret = UnpackSingleArray(parent, p, count, (PRUint8)type, &iid); FreeSingleArray(p, count, (PRUint8)type); nsMemory::Free(p); return ret; } PyObject *PyObject_FromVariant( Py_nsISupports *parent, nsIVariant *v) { if (!v) { Py_INCREF(Py_None); return Py_None; } PRUint16 dt; nsresult nr; PyObject *ret = NULL; nr = v->GetDataType(&dt); if (NS_FAILED(nr)) goto done; switch (dt) { case nsIDataType::VTYPE_VOID: case nsIDataType::VTYPE_EMPTY: case nsIDataType::VTYPE_EMPTY_ARRAY: ret = Py_None; Py_INCREF(Py_None); break; case nsIDataType::VTYPE_ARRAY: ret = PyObject_FromVariantArray(parent, v); break; case nsIDataType::VTYPE_INT8: case nsIDataType::VTYPE_INT16: case nsIDataType::VTYPE_INT32: GET_FROM_V(PRInt32, v->GetAsInt32, PyInt_FromLong); case nsIDataType::VTYPE_UINT8: case nsIDataType::VTYPE_UINT16: case nsIDataType::VTYPE_UINT32: GET_FROM_V(PRUint32, v->GetAsUint32, PyLong_FromUnsignedLong); case nsIDataType::VTYPE_INT64: GET_FROM_V(PRInt64, v->GetAsInt64, PyLong_FromLongLong); case nsIDataType::VTYPE_UINT64: GET_FROM_V(PRUint64, v->GetAsUint64, PyLong_FromUnsignedLongLong); case nsIDataType::VTYPE_FLOAT: case nsIDataType::VTYPE_DOUBLE: GET_FROM_V(double, v->GetAsDouble, PyFloat_FromDouble); case nsIDataType::VTYPE_BOOL: GET_FROM_V(PRBool, v->GetAsBool, MyBool_FromBool); default: PyXPCOM_LogWarning("Converting variant to Python object - variant type '%d' unknown - using string.\n", dt); // Fall through to the string case case nsIDataType::VTYPE_CHAR: case nsIDataType::VTYPE_CHAR_STR: case nsIDataType::VTYPE_STRING_SIZE_IS: case nsIDataType::VTYPE_CSTRING: { nsCAutoString s; if (NS_FAILED(nr=v->GetAsACString(s))) goto done; ret = PyObject_FromNSString(s); break; } case nsIDataType::VTYPE_WCHAR: case nsIDataType::VTYPE_DOMSTRING: case nsIDataType::VTYPE_WSTRING_SIZE_IS: case nsIDataType::VTYPE_ASTRING: { nsAutoString s; if (NS_FAILED(nr=v->GetAsAString(s))) goto done; ret = PyObject_FromNSString(s); break; } case nsIDataType::VTYPE_ID: GET_FROM_V(nsIID, v->GetAsID, Py_nsIID::PyObjectFromIID); case nsIDataType::VTYPE_INTERFACE: { nsCOMPtr p; if (NS_FAILED(nr=v->GetAsISupports(getter_AddRefs(p)))) goto done; if (parent) ret = parent->MakeInterfaceResult(p, NS_GET_IID(nsISupports)); else ret = Py_nsISupports::PyObjectFromInterface( p, NS_GET_IID(nsISupports), PR_TRUE); break; } case nsIDataType::VTYPE_INTERFACE_IS: { nsCOMPtr p; nsIID *iid; if (NS_FAILED(nr=v->GetAsInterface(&iid, getter_AddRefs(p)))) goto done; // If the variant itself holds a variant, we should // probably unpack that too? ret = parent->MakeInterfaceResult(p, *iid); break; // case nsIDataType::VTYPE_WCHAR_STR // case nsIDataType::VTYPE_UTF8STRING } } done: if (NS_FAILED(nr)) { NS_ABORT_IF_FALSE(ret==NULL, "Have an error, but also a return val!"); PyXPCOM_BuildPyException(nr); } return ret; } // ------------------------------------------------------------------------ // TypeDescriptor helper class // ------------------------------------------------------------------------ class PythonTypeDescriptor { public: PythonTypeDescriptor() { param_flags = type_flags = argnum = argnum2 = 0; extra = NULL; is_auto_out = PR_FALSE; is_auto_in = PR_FALSE; have_set_auto = PR_FALSE; } ~PythonTypeDescriptor() { Py_XDECREF(extra); } PRUint8 param_flags; PRUint8 type_flags; PRUint8 argnum; /* used for iid_is and size_is */ PRUint8 argnum2; /* used for length_is */ PyObject *extra; // The IID object, or the type of the array. // Extra items to help our processing. // Is this auto-filled by some other "in" param? PRBool is_auto_in; // Is this auto-filled by some other "out" param? PRBool is_auto_out; // If is_auto_out, have I already filled it? Used when multiple // params share a size_is fields - first time sets it, subsequent // time check it. PRBool have_set_auto; }; static int ProcessPythonTypeDescriptors(PythonTypeDescriptor *pdescs, int num) { // Loop over the array, checking all the params marked as having an arg. // If these args nominate another arg as the size_is param, then // we reset the size_is param to _not_ requiring an arg. int i; for (i=0;iRelease(); Py_END_ALLOW_THREADS; } } if (ns_v.IsValDOMString() && ns_v.val.p) { delete (const nsAString *)ns_v.val.p; } if (ns_v.IsValCString() && ns_v.val.p) { delete (const nsACString *)ns_v.val.p; } if (ns_v.IsValUTF8String() && ns_v.val.p) { delete (const nsACString *)ns_v.val.p; } if (ns_v.IsValArray()) { nsXPTCVariant &ns_v = m_var_array[i]; if (ns_v.val.p) { PRUint8 array_type = (PRUint8)PyInt_AsLong(m_python_type_desc_array[i].extra); PRUint32 seq_size = GetSizeIs(i, PR_FALSE); FreeSingleArray(ns_v.val.p, seq_size, array_type); } } // IsOwned must be the last check of the loop, as // this frees the underlying data used above (eg, by the array free process) if (ns_v.IsValAllocated() && !ns_v.IsValInterface() && !ns_v.IsValDOMString()) { NS_ABORT_IF_FALSE(ns_v.IsPtrData(), "expecting a pointer to free"); nsMemory::Free(ns_v.val.p); } } if (m_buffer_array && m_buffer_array[i]) nsMemory::Free(m_buffer_array[i]); } delete [] m_python_type_desc_array; delete [] m_buffer_array; delete [] m_var_array; } PRBool PyXPCOM_InterfaceVariantHelper::Init(PyObject *obParams) { PRBool ok = PR_FALSE; int i; int total_params_needed = 0; if (!PySequence_Check(obParams) || PySequence_Length(obParams)!=2) { PyErr_Format(PyExc_TypeError, "Param descriptors must be a sequence of exactly length 2"); return PR_FALSE; } PyObject *typedescs = PySequence_GetItem(obParams, 0); if (typedescs==NULL) return PR_FALSE; // NOTE: The length of the typedescs may be different than the // args actually passed. The typedescs always include all // hidden params (such as "size_is"), while the actual // args never include this. m_num_array = PySequence_Length(typedescs); if (PyErr_Occurred()) goto done; m_pyparams = PySequence_GetItem(obParams, 1); if (m_pyparams==NULL) goto done; m_python_type_desc_array = new PythonTypeDescriptor[m_num_array]; if (!m_python_type_desc_array) goto done; // Pull apart the type descs and stash them. for (i=0;iMakeInterfaceResult(iret, iid); break; } case nsXPTType::T_INTERFACE_IS: { nsIID iid; nsXPTCVariant &ns_viid = m_var_array[td.argnum]; NS_WARN_IF_FALSE(XPT_TDP_TAG(ns_viid.type)==nsXPTType::T_IID, "The INTERFACE_IS iid describer isnt an IID!"); if (XPT_TDP_TAG(ns_viid.type)==nsXPTType::T_IID) { nsIID *piid = (nsIID *)ns_viid.val.p; if (piid==NULL) // Also serious, but like below, not our fault! iid = NS_GET_IID(nsISupports); else iid = *piid; } else { // This is a pretty serious problem, but not Python's fault! // Just return an nsISupports and hope the caller does whatever // QI they need before using it. NS_ERROR("Failed to get the IID for T_INTERFACE_IS!"); iid = NS_GET_IID(nsISupports); } nsISupports *iret = *((nsISupports **)ns_v.ptr); if (iid.Equals(NS_GET_IID(nsIVariant))) ret = PyObject_FromVariant(m_parent, (nsIVariant *)iret); else ret = m_parent->MakeInterfaceResult(iret, iid); break; } case nsXPTType::T_ARRAY: { if ( (* ((void **)ns_v.ptr)) == NULL) { ret = Py_None; Py_INCREF(Py_None); } if (!PyInt_Check(td.extra)) { PyErr_SetString(PyExc_TypeError, "The array info is not valid"); break; } PRUint8 array_type = (PRUint8)PyInt_AsLong(td.extra); PRUint32 seq_size = GetSizeIs(index, PR_FALSE); nsIID iid; nsresult res = GetArrayElementIID(m_parent, m_var_array, m_methodindex, index, &iid); ret = UnpackSingleArray(m_parent, * ((void **)ns_v.ptr), seq_size, array_type&XPT_TDP_TAGMASK, NS_SUCCEEDED(res) ? &iid : NULL); break; } case nsXPTType::T_PSTRING_SIZE_IS: if (*((char **)ns_v.ptr) == NULL) { ret = Py_None; Py_INCREF(Py_None); } else { PRUint32 string_size = GetSizeIs(index, PR_TRUE); #if PY_MAJOR_VERSION <= 2 ret = PyString_FromStringAndSize( *((char **)ns_v.ptr), string_size ); #else ret = PyUnicode_FromStringAndSize( *((char **)ns_v.ptr), string_size ); #endif } break; case nsXPTType::T_PWSTRING_SIZE_IS: if (*((PRUnichar **)ns_v.ptr) == NULL) { ret = Py_None; Py_INCREF(Py_None); } else { PRUint32 string_size = GetSizeIs(index, PR_TRUE); ret = PyUnicode_FromPRUnichar( *((PRUnichar **)ns_v.ptr), string_size ); } break; default: PyErr_Format(PyExc_ValueError, "Unknown XPCOM type code (0x%x)", XPT_TDP_TAG(ns_v.type)); /* ret remains nsnull */ break; } return ret; } PyObject *PyXPCOM_InterfaceVariantHelper::MakePythonResult() { // First we count the results. int i = 0; int n_results = 0; PyObject *ret = NULL; PRBool have_retval = PR_FALSE; for (i=0;i 1) { ret = PyTuple_New(n_results); if (ret==NULL) return NULL; } int ret_index = 0; int max_index = m_num_array; // Stick the retval at the front if we have have if (have_retval && n_results > 1) { PyObject *val = MakeSinglePythonResult(m_num_array-1); if (val==NULL) { Py_DECREF(ret); return NULL; } PyTuple_SET_ITEM(ret, 0, val); max_index--; ret_index++; } for (i=0;ret_index < n_results && i < max_index;i++) { if (!m_python_type_desc_array[i].is_auto_out) { if (XPT_PD_IS_OUT(m_python_type_desc_array[i].param_flags) || XPT_PD_IS_DIPPER(m_python_type_desc_array[i].param_flags)) { PyObject *val = MakeSinglePythonResult(i); if (val==NULL) { Py_XDECREF(ret); return NULL; } if (n_results > 1) { PyTuple_SET_ITEM(ret, ret_index, val); ret_index++; } else { NS_ABORT_IF_FALSE(ret==NULL, "shouldnt already have a ret!"); ret = val; } } } } } return ret; } /************************************************************************* ************************************************************************** Helpers when IMPLEMENTING interfaces. ************************************************************************** *************************************************************************/ PyXPCOM_GatewayVariantHelper::PyXPCOM_GatewayVariantHelper( PyG_Base *gw, int method_index, const nsXPTMethodInfo *info, nsXPTCMiniVariant* params ) { m_params = params; m_info = info; // no references added - this class is only alive for // a single gateway invocation m_gateway = gw; m_method_index = method_index; m_python_type_desc_array = NULL; m_num_type_descs = 0; } PyXPCOM_GatewayVariantHelper::~PyXPCOM_GatewayVariantHelper() { delete [] m_python_type_desc_array; } PyObject *PyXPCOM_GatewayVariantHelper::MakePyArgs() { NS_PRECONDITION(sizeof(XPTParamDescriptor) == sizeof(nsXPTParamInfo), "We depend on nsXPTParamInfo being a wrapper over the XPTParamDescriptor struct"); // Setup our array of Python typedescs, and determine the number of objects we // pass to Python. m_num_type_descs = m_info->num_args; m_python_type_desc_array = new PythonTypeDescriptor[m_num_type_descs]; if (m_python_type_desc_array==nsnull) return PyErr_NoMemory(); // First loop to count the number of objects // we pass to Python int i; for (i=0;inum_args;i++) { nsXPTParamInfo *pi = (nsXPTParamInfo *)m_info->params+i; PythonTypeDescriptor &td = m_python_type_desc_array[i]; td.param_flags = pi->flags; td.type_flags = pi->type.prefix.flags; td.argnum = pi->type.argnum; td.argnum2 = pi->type.argnum2; } int num_args = ProcessPythonTypeDescriptors(m_python_type_desc_array, m_num_type_descs); PyObject *ret = PyTuple_New(num_args); if (ret==NULL) return NULL; int this_arg = 0; for (i=0;i=0 && this_arg= m_num_type_descs) { PyErr_SetString(PyExc_ValueError, "dont have a valid size_is indicator for this param"); return PR_FALSE; } PRBool is_out = XPT_PD_IS_OUT(m_python_type_desc_array[argnum].param_flags); nsXPTCMiniVariant &ns_v = m_params[argnum]; NS_ABORT_IF_FALSE( (m_python_type_desc_array[argnum].type_flags & XPT_TDP_TAGMASK) == nsXPTType::T_U32, "size param must be Uint32"); return is_out ? *((PRUint32 *)ns_v.val.p) : ns_v.val.u32; } #undef DEREF_IN_OR_OUT #define DEREF_IN_OR_OUT( element, ret_type ) (ret_type)(is_out ? *((ret_type *)ns_v.val.p) : (ret_type)(element)) PyObject *PyXPCOM_GatewayVariantHelper::MakeSingleParam(int index, PythonTypeDescriptor &td) { NS_PRECONDITION(XPT_PD_IS_IN(td.param_flags), "Must be an [in] param!"); nsXPTCMiniVariant &ns_v = m_params[index]; PyObject *ret = NULL; PRBool is_out = XPT_PD_IS_OUT(td.param_flags); switch (td.type_flags & XPT_TDP_TAGMASK) { case nsXPTType::T_I8: ret = PyInt_FromLong( DEREF_IN_OR_OUT(ns_v.val.i8, PRInt8 ) ); break; case nsXPTType::T_I16: ret = PyInt_FromLong( DEREF_IN_OR_OUT(ns_v.val.i16, PRInt16) ); break; case nsXPTType::T_I32: ret = PyInt_FromLong( DEREF_IN_OR_OUT(ns_v.val.i32, PRInt32) ); break; case nsXPTType::T_I64: ret = PyLong_FromLongLong( DEREF_IN_OR_OUT(ns_v.val.i64, PRInt64) ); break; case nsXPTType::T_U8: ret = PyInt_FromLong( DEREF_IN_OR_OUT(ns_v.val.u8, PRUint8) ); break; case nsXPTType::T_U16: ret = PyInt_FromLong( DEREF_IN_OR_OUT(ns_v.val.u16, PRUint16) ); break; case nsXPTType::T_U32: ret = PyInt_FromLong( DEREF_IN_OR_OUT(ns_v.val.u32, PRUint32) ); break; case nsXPTType::T_U64: ret = PyLong_FromUnsignedLongLong( DEREF_IN_OR_OUT(ns_v.val.u64, PRUint64) ); break; case nsXPTType::T_FLOAT: ret = PyFloat_FromDouble( DEREF_IN_OR_OUT(ns_v.val.f, float) ); break; case nsXPTType::T_DOUBLE: ret = PyFloat_FromDouble( DEREF_IN_OR_OUT(ns_v.val.d, double) ); break; case nsXPTType::T_BOOL: { PRBool temp = DEREF_IN_OR_OUT(ns_v.val.b, PRBool); ret = temp ? Py_True : Py_False; Py_INCREF(ret); break; } case nsXPTType::T_CHAR: { char temp = DEREF_IN_OR_OUT(ns_v.val.c, char); #if PY_MAJOR_VERSION <= 2 ret = PyString_FromStringAndSize(&temp, 1); #else ret = PyUnicode_FromStringAndSize(&temp, 1); #endif break; } case nsXPTType::T_WCHAR: { PRUnichar temp = (PRUnichar)DEREF_IN_OR_OUT(ns_v.val.wc, PRUnichar); ret = PyUnicode_FromPRUnichar(&temp, 1); break; } // case nsXPTType::T_VOID: case nsXPTType::T_IID: { ret = Py_nsIID::PyObjectFromIID( * DEREF_IN_OR_OUT(ns_v.val.p, const nsIID *) ); break; } case nsXPTType::T_ASTRING: case nsXPTType::T_DOMSTRING: { NS_ABORT_IF_FALSE(is_out || !XPT_PD_IS_DIPPER(td.param_flags), "DOMStrings can't be inout"); const nsAString *rs = (const nsAString *)ns_v.val.p; ret = PyObject_FromNSString(*rs); break; } case nsXPTType::T_CSTRING: case nsXPTType::T_UTF8STRING: { NS_ABORT_IF_FALSE(is_out || !XPT_PD_IS_DIPPER(td.param_flags), "DOMStrings can't be inout"); const nsCString *rs = (const nsCString *)ns_v.val.p; ret = PyObject_FromNSString(*rs, (td.type_flags & XPT_TDP_TAGMASK)==nsXPTType::T_UTF8STRING); break; } case nsXPTType::T_CHAR_STR: { char *t = DEREF_IN_OR_OUT(ns_v.val.p, char *); if (t==NULL) { ret = Py_None; Py_INCREF(Py_None); } else #if PY_MAJOR_VERSION <= 2 ret = PyString_FromString(t); #else ret = PyUnicode_FromString(t); #endif break; } case nsXPTType::T_WCHAR_STR: { PRUnichar *us = DEREF_IN_OR_OUT(ns_v.val.p, PRUnichar *); if (us==NULL) { ret = Py_None; Py_INCREF(Py_None); } else { ret = PyUnicode_FromPRUnichar( us, nsCRT::strlen(us)); } break; } case nsXPTType::T_INTERFACE_IS: // our Python code does it :-) case nsXPTType::T_INTERFACE: { nsISupports *iret = DEREF_IN_OR_OUT(ns_v.val.p, nsISupports *); nsXPTParamInfo *pi = (nsXPTParamInfo *)m_info->params+index; ret = m_gateway->MakeInterfaceParam(iret, NULL, m_method_index, pi, index); break; } /*** nsISupports *iret = DEREF_IN_OR_OUT(ns_v.val.p, nsISupports *); nsXPTParamInfo *pi = (nsXPTParamInfo *)m_info->params+index; nsXPTCMiniVariant &ns_viid = m_params[td.argnum]; NS_ABORT_IF_FALSE((m_python_type_desc_array[td.argnum].type_flags & XPT_TDP_TAGMASK) == nsXPTType::T_IID, "The INTERFACE_IS iid describer isnt an IID!"); const nsIID * iid = NULL; if (XPT_PD_IS_IN(m_python_type_desc_array[td.argnum].param_flags)) // may still be inout! iid = DEREF_IN_OR_OUT(ns_v.val.p, const nsIID *); ret = m_gateway->MakeInterfaceParam(iret, iid, m_method_index, pi, index); break; } ****/ case nsXPTType::T_ARRAY: { void *t = DEREF_IN_OR_OUT(ns_v.val.p, void *); if (t==NULL) { // JS may send us a NULL here occasionally - as the // type is array, we silently convert this to a zero // length list, a-la JS. ret = PyList_New(0); } else { PRUint8 array_type; nsIID *piid; nsresult ns = GetArrayType(index, &array_type, &piid); if (NS_FAILED(ns)) { PyXPCOM_BuildPyException(ns); break; } PRUint32 seq_size = GetSizeIs(index, PR_FALSE); ret = UnpackSingleArray(NULL, t, seq_size, array_type&XPT_TDP_TAGMASK, piid); } break; } case nsXPTType::T_PSTRING_SIZE_IS: { char *t = DEREF_IN_OR_OUT(ns_v.val.p, char *); PRUint32 string_size = GetSizeIs(index, PR_TRUE); if (t==NULL) { ret = Py_None; Py_INCREF(Py_None); } else #if PY_MAJOR_VERSION <= 2 ret = PyString_FromStringAndSize(t, string_size); #else ret = PyUnicode_FromStringAndSize(t, string_size); #endif break; } case nsXPTType::T_PWSTRING_SIZE_IS: { PRUnichar *t = DEREF_IN_OR_OUT(ns_v.val.p, PRUnichar *); PRUint32 string_size = GetSizeIs(index, PR_TRUE); if (t==NULL) { ret = Py_None; Py_INCREF(Py_None); } else { ret = PyUnicode_FromPRUnichar(t, string_size); } break; } default: // As this is called by external components, // we return _something_ rather than failing before any user code has run! { char buf[128]; sprintf(buf, "Unknown XPCOM type flags (0x%x)", td.type_flags); PyXPCOM_LogWarning("%s - returning a string object with this message!\n", buf); #if PY_MAJOR_VERSION <= 2 ret = PyString_FromString(buf); #else ret = PyUnicode_FromString(buf); #endif break; } } return ret; } nsresult PyXPCOM_GatewayVariantHelper::GetArrayType(PRUint8 index, PRUint8 *ret, nsIID **iid) { nsCOMPtr iim(do_GetService( NS_INTERFACEINFOMANAGER_SERVICE_CONTRACTID)); NS_ABORT_IF_FALSE(iim != nsnull, "Cant get interface from IIM!"); if (iim==nsnull) return NS_ERROR_FAILURE; nsCOMPtr ii; nsresult rc = iim->GetInfoForIID( &m_gateway->m_iid, getter_AddRefs(ii)); if (NS_FAILED(rc)) return rc; nsXPTType datumType; const nsXPTParamInfo& param_info = m_info->GetParam((PRUint8)index); rc = ii->GetTypeForParam(m_method_index, ¶m_info, 1, &datumType); if (NS_FAILED(rc)) return rc; if (iid) { *iid = (nsIID *)&NS_GET_IID(nsISupports); if (XPT_TDP_TAG(datumType)==nsXPTType::T_INTERFACE || XPT_TDP_TAG(datumType)==nsXPTType::T_INTERFACE_IS || XPT_TDP_TAG(datumType)==nsXPTType::T_ARRAY) ii->GetIIDForParam(m_method_index, ¶m_info, iid); } *ret = datumType.flags; return NS_OK; } PRBool PyXPCOM_GatewayVariantHelper::GetIIDForINTERFACE_ID(int index, const nsIID **ppret) { // Not sure if the IID pointed at by by this is allows to be // in or out, so we will allow it. nsXPTParamInfo *pi = (nsXPTParamInfo *)m_info->params+index; nsXPTType typ = pi->GetType(); NS_WARN_IF_FALSE(XPT_TDP_TAG(typ) == nsXPTType::T_IID, "INTERFACE_IS IID param isnt an IID!"); NS_ABORT_IF_FALSE(typ.IsPointer(), "Expecting to re-fill a pointer value."); if (XPT_TDP_TAG(typ) != nsXPTType::T_IID) *ppret = &NS_GET_IID(nsISupports); else { nsXPTCMiniVariant &ns_v = m_params[index]; if (pi->IsOut()) { nsIID **pp = (nsIID **)ns_v.val.p; if (pp && *pp) *ppret = *pp; else *ppret = &NS_GET_IID(nsISupports); } else if (pi->IsIn()) { nsIID *p = (nsIID *)ns_v.val.p; if (p) *ppret = p; else *ppret = &NS_GET_IID(nsISupports); } else { NS_ERROR("Param is not in or out!"); *ppret = &NS_GET_IID(nsISupports); } } return PR_TRUE; } nsIInterfaceInfo *PyXPCOM_GatewayVariantHelper::GetInterfaceInfo() { if (!m_interface_info) { nsCOMPtr iim(do_GetService( NS_INTERFACEINFOMANAGER_SERVICE_CONTRACTID)); if (iim) iim->GetInfoForIID(&m_gateway->m_iid, getter_AddRefs(m_interface_info)); } return m_interface_info; } #undef FILL_SIMPLE_POINTER #define FILL_SIMPLE_POINTER( type, ob ) *((type *)ns_v.val.p) = (type)(ob) nsresult PyXPCOM_GatewayVariantHelper::BackFillVariant( PyObject *val, int index) { nsXPTParamInfo *pi = (nsXPTParamInfo *)m_info->params+index; NS_ABORT_IF_FALSE(pi->IsOut() || pi->IsDipper(), "The value must be marked as [out] (or a dipper) to be back-filled!"); NS_ABORT_IF_FALSE(!pi->IsShared(), "Dont know how to back-fill a shared out param"); nsXPTCMiniVariant &ns_v = m_params[index]; nsXPTType typ = pi->GetType(); PyObject* val_use = NULL; NS_ABORT_IF_FALSE(pi->IsDipper() || ns_v.val.p, "No space for result!"); if (!pi->IsDipper() && !ns_v.val.p) return NS_ERROR_INVALID_POINTER; PRBool rc = PR_TRUE; switch (XPT_TDP_TAG(typ)) { case nsXPTType::T_I8: if ((val_use=PyNumber_Int(val))==NULL) BREAK_FALSE; FILL_SIMPLE_POINTER( PRInt8, PyInt_AsLong(val_use) ); break; case nsXPTType::T_I16: if ((val_use=PyNumber_Int(val))==NULL) BREAK_FALSE; FILL_SIMPLE_POINTER( PRInt16, PyInt_AsLong(val_use) ); break; case nsXPTType::T_I32: if ((val_use=PyNumber_Int(val))==NULL) BREAK_FALSE; FILL_SIMPLE_POINTER( PRInt32, PyInt_AsLong(val_use) ); break; case nsXPTType::T_I64: if ((val_use=PyNumber_Long(val))==NULL) BREAK_FALSE; FILL_SIMPLE_POINTER( PRInt64, PyLong_AsLongLong(val_use) ); break; case nsXPTType::T_U8: if ((val_use=PyNumber_Int(val))==NULL) BREAK_FALSE; FILL_SIMPLE_POINTER( PRUint8, PyInt_AsLong(val_use) ); break; case nsXPTType::T_U16: if ((val_use=PyNumber_Int(val))==NULL) BREAK_FALSE; FILL_SIMPLE_POINTER( PRUint16, PyInt_AsLong(val_use) ); break; case nsXPTType::T_U32: if ((val_use=PyNumber_Int(val))==NULL) BREAK_FALSE; FILL_SIMPLE_POINTER( PRUint32, PyInt_AsLong(val_use) ); break; case nsXPTType::T_U64: if ((val_use=PyNumber_Long(val))==NULL) BREAK_FALSE; FILL_SIMPLE_POINTER( PRUint64, PyLong_AsUnsignedLongLong(val_use) ); break; case nsXPTType::T_FLOAT: if ((val_use=PyNumber_Float(val))==NULL) BREAK_FALSE FILL_SIMPLE_POINTER( float, PyFloat_AsDouble(val_use) ); break; case nsXPTType::T_DOUBLE: if ((val_use=PyNumber_Float(val))==NULL) BREAK_FALSE FILL_SIMPLE_POINTER( double, PyFloat_AsDouble(val_use) ); break; case nsXPTType::T_BOOL: if ((val_use=PyNumber_Int(val))==NULL) BREAK_FALSE FILL_SIMPLE_POINTER( PRBool, PyInt_AsLong(val_use) ); break; case nsXPTType::T_CHAR: #if PY_MAJOR_VERSION <= 2 if (!PyString_Check(val) && !PyUnicode_Check(val)) { PyErr_SetString(PyExc_TypeError, "This parameter must be a string or Unicode object"); BREAK_FALSE; } if ((val_use = PyObject_Str(val))==NULL) BREAK_FALSE; // Sanity check should PyObject_Str() ever loosen its semantics wrt Unicode! NS_ABORT_IF_FALSE(PyString_Check(val_use), "PyObject_Str didnt return a string object!"); FILL_SIMPLE_POINTER( char, *PyString_AS_STRING(val_use) ); #else if (!PyUnicode_Check(val)) { PyErr_SetString(PyExc_TypeError, "This parameter must be a unicode object"); BREAK_FALSE; } # ifndef Py_LIMITED_API FILL_SIMPLE_POINTER( char, *PyUnicode_AS_UNICODE(val) ); # else FILL_SIMPLE_POINTER( char, PyUnicode_ReadChar(val, 0) ); # endif #endif break; case nsXPTType::T_WCHAR: #if PY_MAJOR_VERSION <= 2 if (!PyString_Check(val) && !PyUnicode_Check(val)) { PyErr_SetString(PyExc_TypeError, "This parameter must be a string or Unicode object"); BREAK_FALSE; } #else if (!PyUnicode_Check(val)) { PyErr_SetString(PyExc_TypeError, "This parameter must be a Unicode object"); BREAK_FALSE; } #endif if ((val_use = PyUnicode_FromObject(val))==NULL) BREAK_FALSE; NS_ABORT_IF_FALSE(PyUnicode_Check(val_use), "PyUnicode_FromObject didnt return a Unicode object!"); // Lossy! #ifndef Py_LIMITED_API FILL_SIMPLE_POINTER( PRUnichar, *PyUnicode_AS_UNICODE(val_use) ); #else FILL_SIMPLE_POINTER( PRUnichar, PyUnicode_ReadChar(val_use, 0) ); #endif break; // case nsXPTType::T_VOID: case nsXPTType::T_IID: { nsIID iid; if (!Py_nsIID::IIDFromPyObject(val, &iid)) BREAK_FALSE; nsIID **pp = (nsIID **)ns_v.val.p; // If there is an existing [in] IID, free it. if (*pp && pi->IsIn()) nsMemory::Free(*pp); *pp = (nsIID *)nsMemory::Alloc(sizeof(nsIID)); if (*pp==NULL) { PyErr_NoMemory(); BREAK_FALSE; } memcpy(*pp, &iid, sizeof(iid)); break; } case nsXPTType::T_ASTRING: case nsXPTType::T_DOMSTRING: { nsAString *ws = (nsAString *)ns_v.val.p; NS_ABORT_IF_FALSE(ws->Length() == 0, "Why does this writable string already have chars??"); if (!PyObject_AsNSString(val, *ws)) BREAK_FALSE; break; } case nsXPTType::T_CSTRING: { nsCString *ws = (nsCString *)ns_v.val.p; NS_ABORT_IF_FALSE(ws->Length() == 0, "Why does this writable string already have chars??"); if (val == Py_None) { NS_ABORT_IF_FALSE(0, "dont handle None here yet"); } else { #if PY_MAJOR_VERSION <= 2 if (!PyString_Check(val) && !PyUnicode_Check(val)) { PyErr_SetString(PyExc_TypeError, "This parameter must be a string or Unicode object"); BREAK_FALSE; } val_use = PyObject_Str(val); NS_ABORT_IF_FALSE(PyString_Check(val_use), "PyObject_Str didnt return a string object!"); const char *sz = PyString_AS_STRING(val_use); ws->Assign(sz, PyString_GET_SIZE(val_use)); #else if (!PyUnicode_Check(val)) { PyErr_SetString(PyExc_TypeError, "This parameter must be a unicode object"); BREAK_FALSE; } val_use = PyUnicode_AsUTF8String(val); const char *sz = PyBytes_AS_STRING(val_use); ws->Assign(sz, PyBytes_GET_SIZE(val_use)); #endif } break; } case nsXPTType::T_UTF8STRING: { nsCString *ws = (nsCString *)ns_v.val.p; NS_ABORT_IF_FALSE(ws->Length() == 0, "Why does this writable string already have chars??"); if (val == Py_None) { NS_ABORT_IF_FALSE(0, "dont handle None here yet"); } else { #if PY_MAJOR_VERSION <= 2 if (PyString_Check(val)) { val_use = val; Py_INCREF(val); } else #endif if (PyUnicode_Check(val)) { val_use = PyUnicode_AsUTF8String(val); } else { #if PY_MAJOR_VERSION <= 2 PyErr_SetString(PyExc_TypeError, "UTF8 parameters must be string or Unicode objects"); #else PyErr_SetString(PyExc_TypeError, "UTF8 parameters must be unicode objects"); #endif BREAK_FALSE; } #if PY_MAJOR_VERSION <= 2 NS_ABORT_IF_FALSE(PyString_Check(val_use), "must have a string object!"); const char *sz = PyString_AS_STRING(val_use); ws->Assign(sz, PyString_GET_SIZE(val_use)); #else NS_ABORT_IF_FALSE(PyBytes_Check(val_use), "must have a bytes object!"); const char *sz = PyBytes_AS_STRING(val_use); ws->Assign(sz, PyBytes_GET_SIZE(val_use)); #endif } break; } case nsXPTType::T_CHAR_STR: { // If it is an existing string, free it. char **pp = (char **)ns_v.val.p; if (*pp && pi->IsIn()) nsMemory::Free(*pp); *pp = nsnull; if (val == Py_None) break; // Remains NULL. #if PY_MAJOR_VERSION <= 2 if (!PyString_Check(val) && !PyUnicode_Check(val)) { PyErr_SetString(PyExc_TypeError, "This parameter must be a string or Unicode object"); BREAK_FALSE; } if ((val_use = PyObject_Str(val))==NULL) BREAK_FALSE; // Sanity check should PyObject_Str() ever loosen its semantics wrt Unicode! NS_ABORT_IF_FALSE(PyString_Check(val_use), "PyObject_Str didnt return a string object!"); const char *sz = PyString_AS_STRING(val_use); int nch = PyString_GET_SIZE(val_use); #else if (!PyUnicode_Check(val)) { PyErr_SetString(PyExc_TypeError, "This parameter must be a unicode object"); BREAK_FALSE; } if ((val_use = PyUnicode_AsUTF8String(val))==NULL) BREAK_FALSE; const char *sz = PyBytes_AS_STRING(val_use); int nch = PyBytes_GET_SIZE(val_use); #endif *pp = (char *)nsMemory::Alloc(nch+1); if (*pp==NULL) { PyErr_NoMemory(); BREAK_FALSE; } strncpy(*pp, sz, nch+1); break; } case nsXPTType::T_WCHAR_STR: { // If it is an existing string, free it. PRUnichar **pp = (PRUnichar **)ns_v.val.p; if (*pp && pi->IsIn()) nsMemory::Free(*pp); *pp = nsnull; if (val == Py_None) break; // Remains NULL. #if PY_MAJOR_VERSION <= 2 if (!PyString_Check(val) && !PyUnicode_Check(val)) { PyErr_SetString(PyExc_TypeError, "This parameter must be a string or Unicode object"); BREAK_FALSE; } val_use = PyUnicode_FromObject(val); NS_ABORT_IF_FALSE(PyUnicode_Check(val_use), "PyUnicode_FromObject didnt return a Unicode object!"); #else if (!PyUnicode_Check(val)) { PyErr_SetString(PyExc_TypeError, "This parameter must be a unicode object"); BREAK_FALSE; } val_use = val; Py_INCREF(val_use); #endif if (PyUnicode_AsPRUnichar(val_use, pp, NULL) < 0) BREAK_FALSE; break; } case nsXPTType::T_INTERFACE: { nsISupports *pnew = nsnull; // Find out what IID we are declared to use. nsIID *iid = NULL; nsIInterfaceInfo *ii = GetInterfaceInfo(); if (ii) ii->GetIIDForParam(m_method_index, pi, &iid); // Get it the "standard" way. // We do allow NULL here, even tho doing so will no-doubt crash some objects. // (but there will certainly be objects out there that will allow NULL :-( nsIID iid_use = iid ? *iid : NS_GET_IID(nsISupports); if (!Py_nsISupports::InterfaceFromPyObject(val, iid_use, &pnew, PR_TRUE)) BREAK_FALSE; nsISupports **pp = (nsISupports **)ns_v.val.p; if (*pp && pi->IsIn()) { Py_BEGIN_ALLOW_THREADS; // MUST release thread-lock, incase a Python COM object that re-acquires. (*pp)->Release(); Py_END_ALLOW_THREADS; } *pp = pnew; // ref-count added by InterfaceFromPyObject break; } case nsXPTType::T_INTERFACE_IS: { const nsIID *piid; if (!GetIIDForINTERFACE_ID(pi->type.argnum, &piid)) BREAK_FALSE; nsISupports *pnew = nsnull; // Get it the "standard" way. // We do allow NULL here, even tho doing so will no-doubt crash some objects. // (but there will certainly be objects out there that will allow NULL :-( if (!Py_nsISupports::InterfaceFromPyObject(val, *piid, &pnew, PR_TRUE)) BREAK_FALSE; nsISupports **pp = (nsISupports **)ns_v.val.p; if (*pp && pi->IsIn()) { Py_BEGIN_ALLOW_THREADS; // MUST release thread-lock, incase a Python COM object that re-acquires. (*pp)->Release(); Py_END_ALLOW_THREADS; } *pp = pnew; // ref-count added by InterfaceFromPyObject break; } case nsXPTType::T_PSTRING_SIZE_IS: { const char *sz = nsnull; PRUint32 nch = 0; if (val != Py_None) { #if PY_MAJOR_VERSION <= 2 if (!PyString_Check(val) && !PyUnicode_Check(val)) { PyErr_SetString(PyExc_TypeError, "This parameter must be a string or Unicode object"); BREAK_FALSE; } if ((val_use = PyObject_Str(val))==NULL) BREAK_FALSE; // Sanity check should PyObject_Str() ever loosen its semantics wrt Unicode! NS_ABORT_IF_FALSE(PyString_Check(val_use), "PyObject_Str didnt return a string object!"); sz = PyString_AS_STRING(val_use); nch = PyString_GET_SIZE(val_use); #else if (!PyUnicode_Check(val)) { PyErr_SetString(PyExc_TypeError, "This parameter must be a unicode object"); BREAK_FALSE; } if ((val_use = PyUnicode_AsUTF8String(val))==NULL) BREAK_FALSE; sz = PyBytes_AS_STRING(val_use); nch = PyBytes_GET_SIZE(val_use); #endif } PRBool bBackFill = PR_FALSE; PRBool bCanSetSizeIs = CanSetSizeIs(index, PR_TRUE); // If we can not change the size, check our sequence is correct. if (!bCanSetSizeIs) { PRUint32 existing_size = GetSizeIs(index, PR_TRUE); if (nch != existing_size) { PyErr_Format(PyExc_ValueError, "This function is expecting a string of exactly length %d - %d characters were passed", existing_size, nch); BREAK_FALSE; } // It we have an "inout" param, but an "in" count, then // it is probably a buffer the caller expects us to // fill in-place! bBackFill = pi->IsIn(); } if (bBackFill) { memcpy(*(char **)ns_v.val.p, sz, nch); } else { // If we have an existing string, free it! char **pp = (char **)ns_v.val.p; if (*pp && pi->IsIn()) nsMemory::Free(*pp); *pp = nsnull; if (sz==nsnull) // None specified. break; // Remains NULL. *pp = (char *)nsMemory::Alloc(nch); if (*pp==NULL) { PyErr_NoMemory(); BREAK_FALSE; } memcpy(*pp, sz, nch); if (bCanSetSizeIs) rc = SetSizeIs(index, PR_TRUE, nch); else { NS_ABORT_IF_FALSE(GetSizeIs(index, PR_TRUE) == nch, "Can't set sizeis, but string isnt correct size"); } } break; } case nsXPTType::T_PWSTRING_SIZE_IS: { PRUnichar *sz = nsnull; PRUint32 nch = 0; PRUint32 nbytes = 0; if (val != Py_None) { #if PY_MAJOR_VERSION <= 2 if (!PyString_Check(val) && !PyUnicode_Check(val)) { PyErr_SetString(PyExc_TypeError, "This parameter must be a string or Unicode object"); BREAK_FALSE; } val_use = PyUnicode_FromObject(val); NS_ABORT_IF_FALSE(PyUnicode_Check(val_use), "PyUnicode_FromObject didnt return a Unicode object!"); #else if (!PyUnicode_Check(val)) { PyErr_SetString(PyExc_TypeError, "This parameter must be a unicode object"); BREAK_FALSE; } val_use = val; Py_INCREF(val_use); #endif if (PyUnicode_AsPRUnichar(val_use, &sz, &nch) < 0) BREAK_FALSE; nbytes = sizeof(PRUnichar) * nch; } PRBool bBackFill = PR_FALSE; PRBool bCanSetSizeIs = CanSetSizeIs(index, PR_TRUE); // If we can not change the size, check our sequence is correct. if (!bCanSetSizeIs) { // It is a buffer the caller prolly wants us to fill in-place! PRUint32 existing_size = GetSizeIs(index, PR_TRUE); if (nch != existing_size) { PyErr_Format(PyExc_ValueError, "This function is expecting a string of exactly length %d - %d characters were passed", existing_size, nch); BREAK_FALSE; } // It we have an "inout" param, but an "in" count, then // it is probably a buffer the caller expects us to // fill in-place! bBackFill = pi->IsIn(); } if (bBackFill) { memcpy(*(PRUnichar **)ns_v.val.p, sz, nbytes); } else { // If it is an existing string, free it. PRUnichar **pp = (PRUnichar **)ns_v.val.p; if (*pp && pi->IsIn()) nsMemory::Free(*pp); *pp = sz; sz = nsnull; if (bCanSetSizeIs) rc = SetSizeIs(index, PR_TRUE, nch); else { NS_ABORT_IF_FALSE(GetSizeIs(index, PR_TRUE) == nch, "Can't set sizeis, but string isnt correct size"); } } if (sz) nsMemory::Free(sz); break; } case nsXPTType::T_ARRAY: { // If it is an existing array of the correct size, keep it. PRUint32 sequence_size = 0; PRUint8 array_type; nsIID *piid; nsresult ns = GetArrayType(index, &array_type, &piid); if (NS_FAILED(ns)) return ns; PRUint32 element_size = GetArrayElementSize(array_type); if (val != Py_None) { if (!PySequence_Check(val)) { PyErr_Format(PyExc_TypeError, "Object for xpcom array must be a sequence, not type '%s'", PyXPCOM_ObTypeName(val)); BREAK_FALSE; } sequence_size = PySequence_Length(val); } PRUint32 existing_size = GetSizeIs(index, PR_FALSE); PRBool bBackFill = PR_FALSE; PRBool bCanSetSizeIs = CanSetSizeIs(index, PR_FALSE); // If we can not change the size, check our sequence is correct. if (!bCanSetSizeIs) { // It is a buffer the caller prolly wants us to fill in-place! if (sequence_size != existing_size) { PyErr_Format(PyExc_ValueError, "This function is expecting a sequence of exactly length %d - %d items were passed", existing_size, sequence_size); BREAK_FALSE; } // It we have an "inout" param, but an "in" count, then // it is probably a buffer the caller expects us to // fill in-place! bBackFill = pi->IsIn(); } if (bBackFill) rc = FillSingleArray(*(void **)ns_v.val.p, val, sequence_size, element_size, array_type&XPT_TDP_TAGMASK, piid); else { // If it is an existing array, free it. void **pp = (void **)ns_v.val.p; if (*pp && pi->IsIn()) { FreeSingleArray(*pp, existing_size, array_type); nsMemory::Free(*pp); } *pp = nsnull; if (val == Py_None) break; // Remains NULL. size_t nbytes = sequence_size * element_size; if (nbytes==0) nbytes = 1; // avoid assertion about 0 bytes *pp = (void *)nsMemory::Alloc(nbytes); memset(*pp, 0, nbytes); rc = FillSingleArray(*pp, val, sequence_size, element_size, array_type&XPT_TDP_TAGMASK, piid); if (!rc) break; if (bCanSetSizeIs) rc = SetSizeIs(index, PR_FALSE, sequence_size); else { NS_ABORT_IF_FALSE(GetSizeIs(index, PR_FALSE) == sequence_size, "Can't set sizeis, but string isnt correct size"); } } break; } default: // try and limp along in this case. // leave rc TRUE PyXPCOM_LogWarning("Converting Python object for an [out] param - The object type (0x%x) is unknown - leaving param alone!\n", XPT_TDP_TAG(typ)); break; } Py_XDECREF(val_use); if (!rc) return NS_ERROR_FAILURE; return NS_OK; } nsresult PyXPCOM_GatewayVariantHelper::ProcessPythonResult(PyObject *ret_ob) { // NOTE - although we return an nresult, if we leave a Python // exception set, then our caller may take additional action // (ie, translating our nsresult to a more appropriate nsresult // for the Python exception.) NS_PRECONDITION(!PyErr_Occurred(), "Expecting no Python exception to be pending when processing the return result"); nsresult rc = NS_OK; // If we dont get a tuple back, then the result is only // an int nresult for the underlying function. // (ie, the policy is expected to return (NS_OK, user_retval), // but can also return (say), NS_ERROR_FAILURE if (PyInt_Check(ret_ob)) return PyInt_AsLong(ret_ob); // Now it must be the tuple. if (!PyTuple_Check(ret_ob) || PyTuple_Size(ret_ob)!=2 || !PyInt_Check(PyTuple_GET_ITEM(ret_ob, 0))) { PyErr_SetString(PyExc_TypeError, "The Python result must be a single integer or a tuple of length==2 and first item an int."); return NS_ERROR_FAILURE; } PyObject *user_result = PyTuple_GET_ITEM(ret_ob, 1); // Count up how many results our function needs. int i; int num_results = 0; int last_result = -1; // optimization if we only have one - this is it! int index_retval = -1; for (i=0;iparams+i; if (!m_python_type_desc_array[i].is_auto_out) { if (pi->IsOut() || pi->IsDipper()) { num_results++; last_result = i; } if (pi->IsRetval()) index_retval = i; } } if (num_results==0) { ; // do nothing } else if (num_results==1) { // May or may not be the nominated retval - who cares! NS_ABORT_IF_FALSE(last_result >=0 && last_result < m_num_type_descs, "Have one result, but dont know its index!"); rc = BackFillVariant( user_result, last_result ); } else { // Loop over each one, filling as we go. // We allow arbitary sequences here, but _not_ strings // or Unicode! // NOTE - We ALWAYS do the nominated retval first. // The Python pattern is always: // return retval [, byref1 [, byref2 ...] ] // But the retval is often the last param described in the info. if (!PySequence_Check(user_result) || #if PY_MAJOR_VERSION <= 2 PyString_Check(user_result) || #else PyBytes_Check(user_result) || #endif PyUnicode_Check(user_result)) { PyErr_SetString(PyExc_TypeError, "This function has multiple results, but a sequence was not given to fill them"); return NS_ERROR_FAILURE; } int num_user_results = PySequence_Length(user_result); // If they havent given enough, we dont really care. // although a warning is probably appropriate. if (num_user_results != num_results) { const char *method_name = m_info->GetName(); PyXPCOM_LogWarning("The method '%s' has %d out params, but %d were supplied by the Python code\n", method_name, num_results, num_user_results); } int this_py_index = 0; if (index_retval != -1) { // We always return the nominated result first! PyObject *sub = PySequence_GetItem(user_result, 0); if (sub==NULL) return NS_ERROR_FAILURE; rc = BackFillVariant(sub, index_retval); Py_DECREF(sub); this_py_index = 1; } for (i=0;NS_SUCCEEDED(rc) && iGetParamCount();i++) { // If weve already done it, or dont need to do it! if (i==index_retval || m_python_type_desc_array[i].is_auto_out) continue; nsXPTParamInfo *pi = (nsXPTParamInfo *)m_info->params+i; if (pi->IsOut()) { PyObject *sub = PySequence_GetItem(user_result, this_py_index); if (sub==NULL) return NS_ERROR_FAILURE; rc = BackFillVariant(sub, i); Py_DECREF(sub); this_py_index++; } } } return rc; }