VirtualBox

source: vbox/trunk/src/libs/xpcom18a4/python/src/TypeObject.cpp

Last change on this file was 90631, checked in by vboxsync, 3 years ago

libs/XPCOM: Just use PyObject_Init() everywhere (also for Python < 3.9) instead of _Py_NewReference(). bugref:10079

  • Property svn:eol-style set to native
File size: 14.8 KB
Line 
1/* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3 *
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
8 *
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
12 * License.
13 *
14 * The Original Code is the Python XPCOM language bindings.
15 *
16 * The Initial Developer of the Original Code is
17 * ActiveState Tool Corp.
18 * Portions created by the Initial Developer are Copyright (C) 2000
19 * the Initial Developer. All Rights Reserved.
20 *
21 * Contributor(s):
22 * Mark Hammond <mhammond@skippinet.com.au> (original author)
23 *
24 * Alternatively, the contents of this file may be used under the terms of
25 * either the GNU General Public License Version 2 or later (the "GPL"), or
26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
35 *
36 * ***** END LICENSE BLOCK ***** */
37
38//
39// This code is part of the XPCOM extensions for Python.
40//
41// Written May 2000 by Mark Hammond.
42//
43// Based heavily on the Python COM support, which is
44// (c) Mark Hammond and Greg Stein.
45//
46// (c) 2000, ActiveState corp.
47
48#include "PyXPCOM_std.h"
49#include <nsIInterfaceInfoManager.h>
50#include <nsXPCOM.h>
51#include <nsISupportsPrimitives.h>
52
53#if defined(Py_LIMITED_API) && defined(RT_OS_LINUX)
54# include <features.h>
55# ifdef __GLIBC_PREREQ
56# if __GLIBC_PREREQ(2,9)
57# define PYXPCOM_HAVE_PIPE2
58# include <fcntl.h>
59# endif
60# endif
61#endif
62
63
64#ifndef Py_LIMITED_API
65static PyTypeObject PyInterfaceType_Type = {
66 PyVarObject_HEAD_INIT(&PyType_Type, 0)
67 "interface-type", /* Name of this type */
68 sizeof(PyTypeObject), /* Basic object size */
69 0, /* Item size for varobject */
70 0, /*tp_dealloc*/
71 0, /*tp_print*/
72 PyType_Type.tp_getattr, /*tp_getattr*/
73 0, /*tp_setattr*/
74 0, /*tp_compare*/
75 PyType_Type.tp_repr, /*tp_repr*/
76 0, /*tp_as_number*/
77 0, /*tp_as_sequence*/
78 0, /*tp_as_mapping*/
79 0, /*tp_hash*/
80 0, /*tp_call*/
81 0, /*tp_str*/
82 0, /* tp_getattro */
83 0, /*tp_setattro */
84 0, /* tp_as_buffer */
85 0, /* tp_flags */
86 "Define the behavior of a PythonCOM Interface type.",
87};
88
89#else /* Py_LIMITED_API */
90
91/** The offset of PyTypeObject::ob_name. */
92static size_t g_offObTypeNameMember = sizeof(PyVarObject);
93
94/**
95 * Base type object for XPCOM interfaces. Created dynamicially.
96 */
97static PyTypeObject *g_pPyInterfaceTypeObj = NULL;
98
99/**
100 * Gets the base XPCOM interface type object, creating it if needed.
101 */
102static PyTypeObject *PyXPCOM_CreateInterfaceType(void)
103{
104 static char g_szTypeDoc[] = "Define the behavior of a PythonCOM Interface type."; /* need non-const */
105 PyType_Slot aTypeSlots[] = {
106 { Py_tp_doc, g_szTypeDoc },
107 { 0, NULL } /* terminator */
108 };
109 static const char g_szClassNm[] = "interface-type";
110 PyType_Spec TypeSpec = {
111 /* .name: */ g_szClassNm,
112 /* .basicsize: */ 0,
113 /* .itemsize: */ 0,
114 /* .flags: */ Py_TPFLAGS_BASETYPE,
115 /* .slots: */ aTypeSlots,
116 };
117
118 PyObject *exc_typ = NULL, *exc_val = NULL, *exc_tb = NULL;
119 PyErr_Fetch(&exc_typ, &exc_val, &exc_tb); /* goes south in PyType_Ready if we don't clear exceptions first. */
120
121 PyTypeObject *pTypeObj = (PyTypeObject *)PyType_FromSpec(&TypeSpec);
122 assert(pTypeObj);
123
124 PyErr_Restore(exc_typ, exc_val, exc_tb);
125 g_pPyInterfaceTypeObj = pTypeObj;
126
127 /*
128 * Verify/correct g_offObTypeNameMember.
129 *
130 * Using pipe+write to probe the memory content, banking on the kernel
131 * to return EFAULT when we pass it an invalid address.
132 */
133 for (size_t off = sizeof(PyVarObject); off < sizeof(PyVarObject) + 64; off += sizeof(char *)) {
134 const char * const pszProbe = *(const char **)((uintptr_t)(pTypeObj) + off);
135 if (RT_VALID_PTR(pszProbe)) {
136 int fds[2] = { -1, -1 };
137# ifdef PYXPCOM_HAVE_PIPE2
138 int rc = pipe2(fds, O_CLOEXEC);
139# else
140 int rc = pipe(fds);
141# endif
142 if (rc)
143 break;
144
145 ssize_t cbWritten = write(fds[1], pszProbe, sizeof(g_szClassNm));
146 if (cbWritten == (ssize_t)sizeof(g_szClassNm)) {
147 char szReadBack[sizeof(g_szClassNm)];
148 ssize_t offRead = 0;
149 while (offRead < cbWritten) {
150 ssize_t cbRead = read(fds[0], &szReadBack[offRead], cbWritten - offRead);
151 if (cbRead >= 0) {
152 offRead += cbRead;
153 } else if (errno != EINTR)
154 break;
155 }
156 if ( cbWritten == offRead
157 && memcmp(szReadBack, g_szClassNm, sizeof(szReadBack)) == 0) {
158 g_offObTypeNameMember = off;
159 close(fds[0]);
160 close(fds[1]);
161 return pTypeObj;
162 }
163 }
164 close(fds[0]);
165 close(fds[1]);
166 }
167 }
168 assert(0);
169
170 return pTypeObj;
171}
172
173/**
174 * Gets the base XPCOM interface type object, creating it if needed.
175 */
176static PyTypeObject *PyXPCOM_GetInterfaceType(void)
177{
178 PyTypeObject *pTypeObj = g_pPyInterfaceTypeObj;
179 if (pTypeObj)
180 return pTypeObj;
181 return PyXPCOM_CreateInterfaceType();
182}
183
184/**
185 * Get the PyTypeObject::ob_name value.
186 *
187 * @todo This is _horrible_, but there appears to be no simple tp_name getter
188 * till https://bugs.python.org/issue31497 (2017 / 3.7.0). But even then
189 * it is not part of the limited API.
190 */
191const char *PyXPCOMGetObTypeName(PyTypeObject *pTypeObj)
192{
193 return *(const char **)((uintptr_t)(pTypeObj) + g_offObTypeNameMember);
194}
195
196#endif /* Py_LIMITED_API */
197
198/*static*/ PRBool
199PyXPCOM_TypeObject::IsType(PyTypeObject *t)
200{
201#if PY_MAJOR_VERSION <= 2
202 return t->ob_type == &PyInterfaceType_Type;
203#elif !defined(Py_LIMITED_API)
204 return Py_TYPE(t) == &PyInterfaceType_Type;
205#else
206 return Py_TYPE(t) == g_pPyInterfaceTypeObj /* Typically not the case as t->ob_type is &PyType_Type */
207 || PyType_IsSubtype(t, g_pPyInterfaceTypeObj); /* rather than g_pPyInterfaceTypeObj because of PyType_FromSpec(). */
208#endif
209}
210
211////////////////////////////////////////////////////////////////////
212//
213// The type methods
214//
215/*static*/PyObject *
216PyXPCOM_TypeObject::Py_getattr(PyObject *self, char *name)
217{
218 return ((Py_nsISupports *)self)->getattr(name);
219}
220
221/*static*/int
222PyXPCOM_TypeObject::Py_setattr(PyObject *op, char *name, PyObject *v)
223{
224 return ((Py_nsISupports *)op)->setattr(name, v);
225}
226
227// @pymethod int|Py_nsISupports|__cmp__|Implements XPCOM rules for object identity.
228/*static*/int
229PyXPCOM_TypeObject::Py_cmp(PyObject *self, PyObject *other)
230{
231 // @comm NOTE: Copied from COM - have not confirmed these rules are true for XPCOM
232 // @comm As per the XPCOM rules for object identity, both objects are queried for nsISupports, and these values compared.
233 // The only meaningful test is for equality - the result of other comparisons is undefined
234 // (ie, determined by the object's relative addresses in memory.
235 nsISupports *pUnkOther;
236 nsISupports *pUnkThis;
237 if (!Py_nsISupports::InterfaceFromPyObject(self, NS_GET_IID(nsISupports), &pUnkThis, PR_FALSE))
238 return -1;
239 if (!Py_nsISupports::InterfaceFromPyObject(other, NS_GET_IID(nsISupports), &pUnkOther, PR_FALSE)) {
240 pUnkThis->Release();
241 return -1;
242 }
243 int rc = pUnkThis==pUnkOther ? 0 :
244 (pUnkThis < pUnkOther ? -1 : 1);
245 pUnkThis->Release();
246 pUnkOther->Release();
247 return rc;
248}
249
250/*static*/PyObject *
251PyXPCOM_TypeObject::Py_richcmp(PyObject *self, PyObject *other, int op)
252{
253 PyObject *result = NULL;
254 int rc = Py_cmp(self, other);
255 switch (op)
256 {
257 case Py_LT:
258 result = rc < 0 ? Py_True : Py_False;
259 break;
260 case Py_LE:
261 result = rc <= 0 ? Py_True : Py_False;
262 break;
263 case Py_EQ:
264 result = rc == 0 ? Py_True : Py_False;
265 break;
266 case Py_NE:
267 result = rc != 0 ? Py_True : Py_False;
268 break;
269 case Py_GT:
270 result = rc > 0 ? Py_True : Py_False;
271 break;
272 case Py_GE:
273 result = rc >= 0 ? Py_True : Py_False;
274 break;
275 }
276 Py_XINCREF(result);
277 return result;
278}
279
280// @pymethod int|Py_nsISupports|__hash__|Implement a hash-code for the XPCOM object using XPCOM identity rules.
281#if PY_VERSION_HEX >= 0x03020000
282/*static*/Py_hash_t PyXPCOM_TypeObject::Py_hash(PyObject *self)
283#else
284/*static*/long PyXPCOM_TypeObject::Py_hash(PyObject *self)
285#endif
286{
287 // We always return the value of the nsISupports *.
288 nsISupports *pUnkThis;
289 if (!Py_nsISupports::InterfaceFromPyObject(self, NS_GET_IID(nsISupports), &pUnkThis, PR_FALSE))
290 return -1;
291#if PY_VERSION_HEX >= 0x03020000
292 Py_hash_t ret = _Py_HashPointer(pUnkThis);
293#else
294 long ret = _Py_HashPointer(pUnkThis);
295#endif
296 pUnkThis->Release();
297 return ret;
298}
299
300// @method string|Py_nsISupports|__repr__|Called to create a representation of a Py_nsISupports object
301/*static */PyObject *
302PyXPCOM_TypeObject::Py_repr(PyObject *self)
303{
304 // @comm The repr of this object displays both the object's address, and its attached nsISupports's address
305 Py_nsISupports *pis = (Py_nsISupports *)self;
306 // Try and get the IID name.
307 char *iid_repr = nsnull;
308 nsCOMPtr<nsIInterfaceInfoManager> iim(do_GetService(
309 NS_INTERFACEINFOMANAGER_SERVICE_CONTRACTID));
310 if (iim!=nsnull)
311 iim->GetNameForIID(&pis->m_iid, &iid_repr);
312 if (iid_repr==nsnull)
313 // no IIM available, or it doesnt know the name.
314 iid_repr = pis->m_iid.ToString();
315 // XXX - need some sort of buffer overflow.
316 char buf[512];
317#ifdef VBOX
318 snprintf(buf, sizeof(buf), "<XPCOM object (%s) at %p/%p>",
319 iid_repr, (void *)self, (void *)pis->m_obj.get());
320#else
321 sprintf(buf, "<XPCOM object (%s) at 0x%p/0x%p>",
322 iid_repr, (void *)self, (void *)pis->m_obj.get());
323#endif
324 nsMemory::Free(iid_repr);
325#if PY_MAJOR_VERSION <= 2
326 return PyString_FromString(buf);
327#else
328 return PyUnicode_FromString(buf);
329#endif
330}
331
332/*static */PyObject *
333PyXPCOM_TypeObject::Py_str(PyObject *self)
334{
335 Py_nsISupports *pis = (Py_nsISupports *)self;
336 nsresult rv;
337 char *val = NULL;
338 Py_BEGIN_ALLOW_THREADS;
339 { // scope to kill pointer while thread-lock released.
340 nsCOMPtr<nsISupportsCString> ss( do_QueryInterface(pis->m_obj, &rv ));
341 if (NS_SUCCEEDED(rv))
342 rv = ss->ToString(&val);
343 } // end-scope
344 Py_END_ALLOW_THREADS;
345 PyObject *ret;
346 if (NS_FAILED(rv))
347 ret = Py_repr(self);
348 else
349#if PY_MAJOR_VERSION <= 2
350 ret = PyString_FromString(val);
351#else
352 ret = PyUnicode_FromString(val);
353#endif
354 if (val) nsMemory::Free(val);
355 return ret;
356}
357
358/* static */void
359PyXPCOM_TypeObject::Py_dealloc(PyObject *self)
360{
361 delete (Py_nsISupports *)self;
362}
363
364PyXPCOM_TypeObject::PyXPCOM_TypeObject( const char *name, PyXPCOM_TypeObject *pBase, int typeSize, struct PyMethodDef* methodList, PyXPCOM_I_CTOR thector)
365{
366#ifndef Py_LIMITED_API
367 static const PyTypeObject type_template = {
368 PyVarObject_HEAD_INIT(&PyInterfaceType_Type, 0)
369 "XPCOMTypeTemplate", /*tp_name*/
370 sizeof(Py_nsISupports), /*tp_basicsize*/
371 0, /*tp_itemsize*/
372 Py_dealloc, /* tp_dealloc */
373 0, /* tp_print */
374 Py_getattr, /* tp_getattr */
375 Py_setattr, /* tp_setattr */
376#if PY_MAJOR_VERSION <= 2
377 Py_cmp, /* tp_compare */
378#else
379 0, /* reserved */
380#endif
381 Py_repr, /* tp_repr */
382 0, /* tp_as_number*/
383 0, /* tp_as_sequence */
384 0, /* tp_as_mapping */
385 Py_hash, /* tp_hash */
386 0, /* tp_call */
387 Py_str, /* tp_str */
388 0, /* tp_getattro */
389 0, /* tp_setattro */
390 0, /* tp_as_buffer */
391 0, /* tp_flags */
392 0, /* tp_doc */
393 0, /* tp_traverse */
394 0, /* tp_clear */
395 Py_richcmp, /* tp_richcompare */
396 0, /* tp_weaklistoffset */
397 0, /* tp_iter */
398 0, /* tp_iternext */
399 0, /* tp_methods */
400 0, /* tp_members */
401 0, /* tp_getset */
402 0, /* tp_base */
403 };
404
405 *((PyTypeObject *)this) = type_template;
406#else /* Py_LIMITED_API */
407 /* Create the type specs: */
408 PyType_Slot aTypeSlots[] = {
409 { Py_tp_base, PyXPCOM_GetInterfaceType() },
410 { Py_tp_dealloc, (void *)(uintptr_t)&PyXPCOM_TypeObject::Py_dealloc },
411 { Py_tp_getattr, (void *)(uintptr_t)&PyXPCOM_TypeObject::Py_getattr },
412 { Py_tp_setattr, (void *)(uintptr_t)&PyXPCOM_TypeObject::Py_setattr },
413 { Py_tp_repr, (void *)(uintptr_t)&PyXPCOM_TypeObject::Py_repr },
414 { Py_tp_hash, (void *)(uintptr_t)&PyXPCOM_TypeObject::Py_hash },
415 { Py_tp_str, (void *)(uintptr_t)&PyXPCOM_TypeObject::Py_str },
416 { Py_tp_richcompare, (void *)(uintptr_t)&PyXPCOM_TypeObject::Py_richcmp },
417 { 0, NULL } /* terminator */
418 };
419 PyType_Spec TypeSpec = {
420 /* .name: */ name,
421 /* .basicsize: */ typeSize,
422 /* .itemsize: */ 0,
423 /* .flags: */ Py_TPFLAGS_BASETYPE /*?*/,
424 /* .slots: */ aTypeSlots,
425 };
426
427 PyObject *exc_typ = NULL, *exc_val = NULL, *exc_tb = NULL;
428 PyErr_Fetch(&exc_typ, &exc_val, &exc_tb); /* goes south in PyType_Ready if we don't clear exceptions first. */
429
430 m_pTypeObj = (PyTypeObject *)PyType_FromSpec(&TypeSpec);
431 assert(m_pTypeObj);
432
433 PyErr_Restore(exc_typ, exc_val, exc_tb);
434
435 /* Initialize the PyObject part - needed so we can keep instance in a PyDict: */
436 ob_type = PyXPCOM_GetInterfaceType();
437 PyObject_Init(this, ob_type); /* VBox: Needed for 3.9 and up (also works on Python 2.7), includes _Py_NewReferences. @bugref{10079} */
438
439#endif /* Py_LIMITED_API */
440
441 chain.methods = methodList;
442 chain.link = pBase ? &pBase->chain : NULL;
443
444 baseType = pBase;
445 ctor = thector;
446
447#ifndef Py_LIMITED_API
448 // cast away const, as Python doesnt use it.
449 tp_name = (char *)name;
450 tp_basicsize = typeSize;
451#endif
452}
453
454PyXPCOM_TypeObject::~PyXPCOM_TypeObject()
455{
456}
457
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use