1/* 2 * Custom subclass of PyUnicode_Type, to allow for transparent bridging of 3 * strings 4 */ 5 6#include "pyobjc.h" 7 8#include <stddef.h> 9#include <Foundation/NSString.h> 10 11typedef struct { 12 PyUnicodeObject base; 13 PyObject* weakrefs; 14 id nsstr; 15 PyObject* py_nsstr; 16} PyObjCUnicodeObject; 17 18PyDoc_STRVAR(class_doc, 19 "objc.pyobjc_unicode\n" 20 "\n" 21 "Subclass of unicode for representing NSString values. Use \n" 22 "the method nsstring to access the NSString. \n" 23 "Note that instances are immutable and won't be updated when\n" 24 "the value of the NSString changes." 25); 26 27static void 28class_dealloc(PyObject* obj) 29{ 30 PyObjCUnicodeObject* uobj = (PyObjCUnicodeObject*)obj; 31 id nsstr = uobj->nsstr; 32 PyObject* weakrefs = uobj->weakrefs; 33 PyObject* py_nsstr = uobj->py_nsstr; 34 35 PyObjC_UnregisterPythonProxy(nsstr, obj); 36 37 Py_XDECREF(py_nsstr); 38 if (nsstr) { 39 CFRelease(nsstr); 40 } 41 42 if (weakrefs) { 43 PyObject_ClearWeakRefs(obj); 44 } 45 46 PyUnicode_Type.tp_dealloc(obj); 47} 48 49static PyObject* 50meth_nsstring(PyObject* self) 51{ 52 PyObjCUnicodeObject* uobj = (PyObjCUnicodeObject*)self; 53 if (uobj->py_nsstr == NULL) { 54 uobj->py_nsstr = PyObjCObject_New(uobj->nsstr, 55 PyObjCObject_kDEFAULT, YES); 56 } 57 Py_INCREF(uobj->py_nsstr); 58 return uobj->py_nsstr; 59} 60 61 62static PyObject* 63meth_getattro(PyObject *o, PyObject *attr_name) 64{ 65 PyObject *res; 66 res = PyObject_GenericGetAttr(o, attr_name); 67 if (res == NULL) { 68 PyErr_Clear(); 69 PyObject *py_nsstr = meth_nsstring(o); 70 res = PyObject_GenericGetAttr(py_nsstr, attr_name); 71 Py_XDECREF(py_nsstr); 72 } 73 return res; 74} 75 76static PyObject* 77meth_reduce(PyObject* self) 78{ 79 PyObject* retVal = NULL; 80 PyObject *v = NULL; 81 PyObject *v2 = NULL; 82 83 retVal = PyTuple_New(2); 84 if (retVal == NULL) goto error; 85 86 v = (PyObject*)&PyUnicode_Type; 87 Py_INCREF(v); 88 PyTuple_SET_ITEM(retVal, 0, v); 89 90 v = PyUnicode_FromObject(self); 91 if (v == NULL ) goto error; 92 93 v2 = PyTuple_New(1); 94 if (v2 == NULL) goto error; 95 PyTuple_SET_ITEM(v2, 0, v); 96 PyTuple_SET_ITEM(retVal, 1, v2); 97 98 return retVal; 99 100error: 101 Py_XDECREF(retVal); 102 Py_XDECREF(v); 103 return NULL; 104} 105 106static PyMethodDef class_methods[] = { 107 { 108 "nsstring", 109 (PyCFunction)meth_nsstring, 110 METH_NOARGS, 111 "directly access NSString instance" 112 }, 113 { 114 "__reduce__", 115 (PyCFunction)meth_reduce, 116 METH_NOARGS, 117 "Used for pickling" 118 }, 119 { 0, 0, 0, 0 } /* sentinel */ 120}; 121 122static PyObject* 123nsstring_get__pyobjc_object__(PyObject *self, void *closure __attribute__((__unused__))) { 124 return meth_nsstring(self); 125} 126 127static PyGetSetDef nsstring_getsetters[] = { 128 { 129 "__pyobjc_object__", 130 (getter)nsstring_get__pyobjc_object__, NULL, 131 "raw NSString instance", 132 NULL 133 }, 134 { 135 NULL, 136 NULL, NULL, 137 NULL, 138 NULL 139 } 140}; 141 142static PyObject* 143class_new( 144 PyTypeObject* type __attribute__((__unused__)), 145 PyObject* args __attribute__((__unused__)), 146 PyObject* kwds __attribute__((__unused__))) 147{ 148 PyErr_SetString(PyExc_TypeError, 149 "Cannot create instances of 'objc.unicode' in Python"); 150 return NULL; 151} 152 153PyTypeObject PyObjCUnicode_Type = { 154 PyObject_HEAD_INIT(&PyType_Type) 155 0, /* ob_size */ 156 "objc.pyobjc_unicode", /* tp_name */ 157 sizeof(PyObjCUnicodeObject), /* tp_basicsize */ 158 0, /* tp_itemsize */ 159 /* methods */ 160 class_dealloc, /* tp_dealloc */ 161 0, /* tp_print */ 162 0, /* tp_getattr */ 163 0, /* tp_setattr */ 164 0, /* tp_compare */ 165 0, /* tp_repr */ 166 0, /* tp_as_number */ 167 0, /* tp_as_sequence */ 168 0, /* tp_as_mapping */ 169 0, /* tp_hash */ 170 0, /* tp_call */ 171 0, /* tp_str */ 172 meth_getattro, /* tp_getattro */ 173 0, /* tp_setattro */ 174 0, /* tp_as_buffer */ 175 Py_TPFLAGS_DEFAULT, /* tp_flags */ 176 class_doc, /* tp_doc */ 177 0, /* tp_traverse */ 178 0, /* tp_clear */ 179 0, /* tp_richcompare */ 180 offsetof(PyObjCUnicodeObject, weakrefs), /* tp_weaklistoffset */ 181 0, /* tp_iter */ 182 0, /* tp_iternext */ 183 class_methods, /* tp_methods */ 184 0, /* tp_members */ 185 nsstring_getsetters, /* tp_getset */ 186 &PyUnicode_Type, /* tp_base */ 187 0, /* tp_dict */ 188 0, /* tp_descr_get */ 189 0, /* tp_descr_set */ 190 0, /* tp_dictoffset */ 191 0, /* tp_init */ 192 0, /* tp_alloc */ 193 class_new, /* tp_new */ 194 0, /* tp_free */ 195 0, /* tp_is_gc */ 196 0, /* tp_bases */ 197 0, /* tp_mro */ 198 0, /* tp_cache */ 199 0, /* tp_subclasses */ 200 0, /* tp_weaklist */ 201 0 /* tp_del */ 202#if PY_VERSION_HEX >= 0x02060000 203 , 0 /* tp_version_tag */ 204#endif 205 206}; 207 208PyObject* 209PyObjCUnicode_New(NSString* value) 210{ 211 /* Conversion to PyUnicode without creating an autoreleased object. 212 * 213 * NOTE: A final optimization is removing the copy of 'characters', but 214 * that can only be done when sizeof(unichar) == Py_UNICODE_SIZE. 215 * 216 * The reason for doing this: NSThread 217 * +detachNewThreadSelector:toTarget:withObject:, with a string 218 * as one of the arguments. 219 * 220 * Another reason is that the following loop 'leaks' memory when using 221 * -UTF8String: 222 * while True: 223 * NSString.alloc().init() 224 * 225 * while the following doesn't: 226 * 227 * while True: 228 * NSArray.alloc().init() 229 */ 230 PyObjCUnicodeObject* result; 231// XXX - I don't know how to get gcc to let me use sizeof(unichar) 232#ifdef PyObjC_UNICODE_FAST_PATH 233 Py_ssize_t length = [value length]; 234 235 if (length < 0) { 236 PyErr_SetString(PyExc_SystemError, "string with negative length"); 237 return NULL; 238 } 239 result = PyObject_New(PyObjCUnicodeObject, &PyObjCUnicode_Type); 240 Py_UNICODE* tptr = PyMem_NEW(Py_UNICODE, length); 241 PyUnicode_AS_UNICODE(result) = tptr; 242 tptr = NULL; 243 244 if (PyUnicode_AS_UNICODE(result) == NULL) { 245 Py_DECREF((PyObject*)result); 246 PyErr_NoMemory(); 247 return NULL; 248 } 249 [value getCharacters:(unichar *)PyUnicode_AS_UNICODE(result)]; 250 PyUnicode_GET_SIZE(result) = length; 251#else 252 int i, length; 253 unichar* volatile characters = NULL; 254 NSRange range; 255 256 PyObjC_DURING 257 length = [value length]; 258 characters = PyMem_Malloc(sizeof(unichar) * length); 259 if (characters == NULL) { 260 PyErr_NoMemory(); 261 NS_VALUERETURN(NULL, PyObject*); 262 } 263 264 range = NSMakeRange(0, length); 265 266 [value getCharacters: characters range: range]; 267 268 PyObjC_HANDLER 269 if (characters) { 270 PyMem_Free(characters); 271 characters = NULL; 272 } 273 PyObjCErr_FromObjC(localException); 274 NS_VALUERETURN(NULL, PyObject*); 275 PyObjC_ENDHANDLER 276 277 result = PyObject_New(PyObjCUnicodeObject, &PyObjCUnicode_Type); 278 PyUnicode_AS_UNICODE(result) = PyMem_NEW(Py_UNICODE, length); 279 if (PyUnicode_AS_UNICODE(result) == NULL) { 280 Py_DECREF((PyObject*)result); 281 PyMem_Free(characters); characters = NULL; 282 PyErr_NoMemory(); 283 return NULL; 284 } 285 PyUnicode_GET_SIZE(result) = length; 286 for (i = 0; i < length; i++) { 287 PyUnicode_AS_UNICODE(result)[i] = (Py_UNICODE)(characters[i]); 288 } 289 PyMem_Free(characters); characters = NULL; 290#endif 291 292 result->base.defenc = NULL; 293 294 result->base.hash = -1; 295 296 if (PyUnicode_GET_SIZE(result) == 0) { 297 result->base.hash = 0; 298 } 299 300 result->weakrefs = NULL; 301 result->py_nsstr = NULL; 302 result->nsstr = value; 303 CFRetain(value); 304 305 return (PyObject*)result; 306} 307 308NSString* 309PyObjCUnicode_Extract(PyObject* value) 310{ 311 if (!PyObjCUnicode_Check(value)) { 312 PyErr_BadInternalCall(); 313 return NULL; 314 } 315 316 return ((PyObjCUnicodeObject*)value)->nsstr; 317} 318