def testFields(self, mapper, CallLater): basePtr, deallocBase = MakeTypePtr(mapper, {}) typePtr, deallocType = MakeTypePtr(mapper, {}) CallLater(deallocBase) CallLater(deallocType) # The purpose of this rigmarole is to enable me to use SOME_VALUE # for every field, rather than creating 'proper' values for every # field -- once I've Retrieved the types, I won't actualise them # again, so I can put any old non-zero nonsense in any field to # check that it gets inherited (or not) mapper.Retrieve(basePtr) mapper.Retrieve(typePtr) CPyMarshal.WriteIntField(typePtr, PyTypeObject, "tp_flags", int(Py_TPFLAGS.HAVE_CLASS)) # end rigmarole CPyMarshal.WritePtrField(typePtr, PyTypeObject, "tp_base", basePtr) for field in INHERIT_FIELDS + DONT_INHERIT_FIELDS: CPyMarshal.WritePtrField(typePtr, PyTypeObject, field, NO_VALUE) CPyMarshal.WritePtrField(basePtr, PyTypeObject, field, SOME_VALUE) mapper.PyType_Ready(typePtr) for field in INHERIT_FIELDS: self.assertEquals(CPyMarshal.ReadPtrField(typePtr, PyTypeObject, field), SOME_VALUE) for field in DONT_INHERIT_FIELDS: self.assertEquals(CPyMarshal.ReadPtrField(typePtr, PyTypeObject, field), NO_VALUE)
def testExtensionTypesAutoActualisable(self): discoveryModes = { "IncRef": lambda f, o: self.assertMaps(mapper, f, o, 5), "Retrieve": lambda f, o: self.assertMaps(mapper, f, o, 4), "DecRef": lambda f, o: self.assertMaps(mapper, f, o, 3), "RefCount": lambda f, o: self.assertMaps(mapper, f, o, 4), } allocator = HGlobalAllocator() mapper = PythonMapper(allocator) deallocTypes = CreateTypes(mapper) # delay deallocs to avoid types with the same addresses causing confusion userTypeDeallocs = [] try: for (mode, TestFunc) in discoveryModes.items(): typePtr, deallocType = MakeTypePtr(mapper, {"tp_name": mode + "Class"}) userTypeDeallocs.append(deallocType) objPtr = allocator.Alloc(Marshal.SizeOf(PyObject)) CPyMarshal.WriteIntField(objPtr, PyObject, "ob_refcnt", 2) CPyMarshal.WritePtrField(objPtr, PyObject, "ob_type", typePtr) discoveryFunc = getattr(mapper, mode) TestFunc(discoveryFunc, objPtr) finally: mapper.Dispose() for deallocFunc in userTypeDeallocs: deallocFunc() deallocTypes()
def create(name, size): block = Marshal.AllocHGlobal(size) if name == 'PyFile_Type': CPyMarshal.Zero(block, size); CPyMarshal.WritePtrField(block, PyTypeObject, 'tp_dealloc', mapper.GetFuncPtr('IC_file_dealloc')) mapper.RegisterData(name, block) blocks.append(block)
def testPySequence_GetItem_DoesNotActualiseTuples(self, mapper, _): obj1 = object() obj1Ptr = mapper.Store(obj1) obj2 = object() obj2Ptr = mapper.Store(obj2) tuplePtr = mapper.PyTuple_New(1) CPyMarshal.WritePtrField(tuplePtr, PyTupleObject, "ob_item", obj1Ptr) # check that GetItem returns correct object, increffed self.assertEquals(mapper.PySequence_GetItem(tuplePtr, 0), obj1Ptr) self.assertEquals(CPyMarshal.ReadIntField(obj1Ptr, PyObject, "ob_refcnt"), 2) # replace item, check that correct object is retrieved CPyMarshal.WritePtrField(tuplePtr, PyTupleObject, "ob_item", obj2Ptr) self.assertEquals(mapper.Retrieve(tuplePtr), (obj2,))
def testPyType_Ready(self, mapper, addToCleanUp): typePtr = Marshal.AllocHGlobal(Marshal.SizeOf(PyTypeObject())) CPyMarshal.Zero(typePtr, Marshal.SizeOf(PyTypeObject())) addToCleanUp(lambda: Marshal.FreeHGlobal(typePtr)) self.assertEquals(mapper.PyType_Ready(typePtr), 0, "wrong") self.assertEquals( CPyMarshal.ReadPtrField(typePtr, PyTypeObject, "ob_type"), mapper.PyType_Type, "failed to fill in missing ob_type") self.assertEquals( CPyMarshal.ReadPtrField(typePtr, PyTypeObject, "tp_base"), mapper.PyBaseObject_Type, "failed to fill in missing tp_base") tp_dict = mapper.Retrieve( CPyMarshal.ReadPtrField(typePtr, PyTypeObject, "tp_dict")) self.assertEquals(mapper.Retrieve(typePtr).__dict__, tp_dict) typeFlags = CPyMarshal.ReadIntField(typePtr, PyTypeObject, "tp_flags") self.assertEquals(typeFlags & UInt32(Py_TPFLAGS.READY), UInt32(Py_TPFLAGS.READY), "did not ready type") self.assertEquals( typeFlags & UInt32(Py_TPFLAGS.HAVE_CLASS), UInt32(Py_TPFLAGS.HAVE_CLASS), "we always set this flag, for no better reason than 'it makes ctypes kinda work'" ) CPyMarshal.WritePtrField(typePtr, PyTypeObject, "ob_type", IntPtr.Zero) self.assertEquals(mapper.PyType_Ready(typePtr), 0, "wrong") self.assertEquals( CPyMarshal.ReadPtrField(typePtr, PyTypeObject, "ob_type"), IntPtr.Zero, "unexpectedly and unnecessarily rereadied type")
def testDestroysObjectsOfUnmanagedTypesFirst(self): frees = [] mapper = PythonMapper(GetAllocatingTestAllocator([], frees)) deallocTypes = CreateTypes(mapper) modulePtr = MakeAndAddEmptyModule(mapper) module = mapper.Retrieve(modulePtr) calls = [] def Del(instancePtr): calls.append(("del", instancePtr)) mapper.PyObject_Free(instancePtr) typeSpec = {'tp_name': 'klass', 'tp_dealloc': Del} typePtr, deallocType = MakeTypePtr(mapper, typeSpec) mapper.PyModule_AddObject(modulePtr, 'klass', typePtr) easyptr = mapper.Store(123) instance1 = module.klass() hardptr = mapper.Store(instance1) instance2 = module.klass() brokenptr = mapper.Store(instance2) CPyMarshal.WritePtrField(brokenptr, PyObject, 'ob_type', IntPtr.Zero) mapper.Dispose() self.assertEquals( frees.index(hardptr) < frees.index(easyptr), True, "failed to dealloc in correct order") self.assertEquals(calls, [('del', hardptr)], "failed to clean up klass instance") deallocType() deallocTypes()
def testActualiseFloat(self, mapper, call_later): fptr = Marshal.AllocHGlobal(Marshal.SizeOf(PyFloatObject)) call_later(lambda: Marshal.FreeHGlobal(fptr)) CPyMarshal.WritePtrField(fptr, PyFloatObject, "ob_type", mapper.PyFloat_Type) CPyMarshal.WriteIntField(fptr, PyFloatObject, "ob_refcnt", 1) CPyMarshal.WriteDoubleField(fptr, PyFloatObject, "ob_fval", 1.234) self.assertEquals(mapper.Retrieve(fptr), 1.234)
def do1(): # see NOTE in interestingptrmaptest obj = object() ref = WeakReference(obj) CPyMarshal.WriteIntField(ptr, PyObject, "ob_refcnt", 1) CPyMarshal.WritePtrField(ptr, PyObject, "ob_type", mapper.PyBaseObject_Type) mapper.StoreBridge(ptr, obj) del obj return ref
def testBaseTypeMissing(self, mapper, CallLater): mapper.PyType_Ready(mapper.PyBaseObject_Type) self.assertEquals(CPyMarshal.ReadPtrField(mapper.PyBaseObject_Type, PyTypeObject, "tp_base"), IntPtr.Zero) typePtr, deallocType = MakeTypePtr(mapper, {}) CallLater(deallocType) CPyMarshal.WritePtrField(typePtr, PyTypeObject, "tp_base", NO_VALUE) mapper.PyType_Ready(typePtr) self.assertEquals(CPyMarshal.ReadPtrField(typePtr, PyTypeObject, "tp_base"), mapper.PyBaseObject_Type)
def testWritePtrField(self): data = Marshal.AllocHGlobal(Marshal.SizeOf(PyObject())) CPyMarshal.Zero(data, Marshal.SizeOf(PyObject())) CPyMarshal.WritePtrField(data, PyObject, "ob_type", IntPtr(12345)) dataStruct = PtrToStructure(data, PyObject) self.assertEquals(dataStruct.ob_type, IntPtr(12345), "failed to write") Marshal.FreeHGlobal(data)
def testReadCStringFieldEmpty(self): data = Marshal.AllocHGlobal(Marshal.SizeOf(PyTypeObject())) CPyMarshal.Zero(data, Marshal.SizeOf(PyTypeObject())) CPyMarshal.WritePtrField(data, PyTypeObject, "tp_doc", IntPtr.Zero) self.assertEquals( CPyMarshal.ReadCStringField(data, PyTypeObject, "tp_doc"), "", "failed to read correctly") Marshal.FreeHGlobal(data)
def testNotAutoActualisableTypes(self, mapper, _): safeTypes = "PyString_Type PyList_Type PyTuple_Type PyType_Type PyFile_Type PyFloat_Type".split() discoveryModes = ("IncRef", "Retrieve", "DecRef", "RefCount") for _type in filter(lambda s: s not in safeTypes, BUILTIN_TYPES): for mode in discoveryModes: objPtr = Marshal.AllocHGlobal(Marshal.SizeOf(PyObject)) CPyMarshal.WriteIntField(objPtr, PyObject, "ob_refcnt", 2) CPyMarshal.WritePtrField(objPtr, PyObject, "ob_type", getattr(mapper, _type)) self.assertRaises(CannotInterpretException, getattr(mapper, mode), objPtr) Marshal.FreeHGlobal(objPtr)
def testReadPtrField(self): data = Marshal.AllocHGlobal(Marshal.SizeOf(PyTypeObject())) CPyMarshal.Zero(data, Marshal.SizeOf(PyTypeObject())) CPyMarshal.WritePtrField(data, PyTypeObject, "tp_doc", IntPtr(12345)) self.assertEquals( CPyMarshal.ReadPtrField(data, PyTypeObject, "tp_doc"), IntPtr(12345), "failed to read") Marshal.FreeHGlobal(data)
def testFinalDecRefComplainsAboutMissing_tp_dealloc(self): frees = [] mapper = PythonMapper(GetAllocatingTestAllocator([], frees)) deallocTypes = CreateTypes(mapper) typePtr = Marshal.AllocHGlobal(Marshal.SizeOf(PyTypeObject())) CPyMarshal.WritePtrField(typePtr, PyTypeObject, "tp_dealloc", IntPtr.Zero) obj = object() objPtr = mapper.Store(obj) CPyMarshal.WritePtrField(objPtr, PyObject, "ob_type", typePtr) mapper.IncRef(objPtr) del frees [:] mapper.DecRef(objPtr) self.assertEquals(frees, [], "freed prematurely") self.assertRaisesClr(CannotInterpretException, mapper.DecRef, objPtr) mapper.Dispose() deallocTypes()
def do1(): obj = object() ref = WeakReference(obj) # need to use same allocator as mapper, otherwise it gets upset on shutdown ptr = allocator.Alloc(Marshal.SizeOf(PyObject())) CPyMarshal.WriteIntField(ptr, PyObject, "ob_refcnt", 2) CPyMarshal.WritePtrField(ptr, PyObject, "ob_type", mapper.PyBaseObject_Type) mapper.StoreBridge(ptr, obj) # refcount > 1 means ref should have been strengthened del obj return ref, ptr
def WriteTypeField(typePtr, name, value): if name in PTR_ARGS: CPyMarshal.WritePtrField(typePtr, PyTypeObject, name, value) return lambda: None if name in INT_ARGS: CPyMarshal.WriteIntField(typePtr, PyTypeObject, name, int(value)) return lambda: None if name in STRING_ARGS: ptr = Marshal.StringToHGlobalAnsi(value) CPyMarshal.WritePtrField(typePtr, PyTypeObject, name, ptr) return lambda: Marshal.FreeHGlobal(ptr) if name in TABLE_ARGS: ptr, dealloc = MakeItemsTablePtr(value) CPyMarshal.WritePtrField(typePtr, PyTypeObject, name, ptr) return dealloc if name in FUNC_ARGS: if value is not None: dgt = FUNC_ARGS[name](value) CPyMarshal.WriteFunctionPtrField(typePtr, PyTypeObject, name, dgt) return GC_NotYet(dgt) return lambda: None raise KeyError("WriteTypeField can't handle %s, %s" % (name, value))
def testReadCStringField(self): data = Marshal.AllocHGlobal(Marshal.SizeOf(PyTypeObject())) CPyMarshal.Zero(data, Marshal.SizeOf(PyTypeObject())) string = "Hey, I am a string. I have tricksy \\escapes\\." strPtr = Marshal.StringToHGlobalAnsi(string) CPyMarshal.WritePtrField(data, PyTypeObject, "tp_doc", strPtr) self.assertEquals( CPyMarshal.ReadCStringField(data, PyTypeObject, "tp_doc"), string, "failed to read correctly") Marshal.FreeHGlobal(data) Marshal.FreeHGlobal(strPtr)
def testDecRefObjectWithZeroRefCountFails(self): allocator = HGlobalAllocator() mapper = PythonMapper(allocator) deallocTypes = CreateTypes(mapper) # need to use same allocator as mapper, otherwise it gets upset on shutdown objPtr = allocator.Alloc(Marshal.SizeOf(PyObject())) CPyMarshal.WriteIntField(objPtr, PyObject, "ob_refcnt", 0) CPyMarshal.WritePtrField(objPtr, PyObject, "ob_type", mapper.PyBaseObject_Type) mapper.StoreBridge(objPtr, object()) self.assertRaisesClr(BadRefCountException, lambda: mapper.DecRef(objPtr)) mapper.Dispose() deallocTypes()
def do(): # see NOTE in interestingptrmaptest obj = object() ref = WeakReference(obj) CPyMarshal.WriteIntField(ptr, PyObject, "ob_refcnt", 1) CPyMarshal.WritePtrField(ptr, PyObject, "ob_type", mapper.PyBaseObject_Type) mapper.StoreBridge(ptr, obj) self.assertEquals(mapper.Retrieve(ptr), obj, "object not stored") self.assertEquals(mapper.Store(obj), ptr, "object not reverse-mapped") mapper.Weaken(obj) CPyMarshal.WriteIntField(ptr, PyObject, "ob_refcnt", 1) mapper.IncRef(ptr) del obj return ref
def testPyBaseObject_TypeDeallocCallsObjTypesFreeFunction(self, mapper, addToCleanUp): calls = [] def Some_FreeFunc(objPtr): calls.append(objPtr) self.freeDgt = dgt_void_ptr(Some_FreeFunc) baseObjTypeBlock = mapper.PyBaseObject_Type objTypeBlock = mapper.PyDict_Type # type not actually important objPtr = Marshal.AllocHGlobal(Marshal.SizeOf(PyObject())) addToCleanUp(lambda: Marshal.FreeHGlobal(objPtr)) CPyMarshal.WriteFunctionPtrField(objTypeBlock, PyTypeObject, "tp_free", self.freeDgt) CPyMarshal.WritePtrField(objPtr, PyObject, "ob_type", objTypeBlock) gcwait() # this should make the function pointers invalid if we forgot to store references to the delegates mapper.IC_PyBaseObject_Dealloc(objPtr) self.assertEquals(calls, [objPtr], "wrong calls")
def testFinalDecRefOfObjectWithTypeCalls_tp_dealloc(self, mapper, _): calls = [] def TypeDealloc(ptr): calls.append(ptr) deallocDgt = dgt_void_ptr(TypeDealloc) deallocFP = Marshal.GetFunctionPointerForDelegate(deallocDgt) typePtr = Marshal.AllocHGlobal(Marshal.SizeOf(PyTypeObject())) deallocPtr = CPyMarshal.Offset(typePtr, Marshal.OffsetOf(PyTypeObject, "tp_dealloc")) CPyMarshal.WritePtr(deallocPtr, deallocFP) obj = object() objPtr = mapper.Store(obj) CPyMarshal.WritePtrField(objPtr, PyObject, "ob_type", typePtr) mapper.IncRef(objPtr) mapper.DecRef(objPtr) self.assertEquals(calls, [], "called prematurely") mapper.DecRef(objPtr) self.assertEquals(calls, [objPtr], "not called when refcount hit 0")
def testStoreUnknownType(self, mapper, _): class C(object): __name__ = "cantankerous.cochineal" cPtr = mapper.Store(C) self.assertEquals(CPyMarshal.ReadIntField(cPtr, PyTypeObject, "ob_refcnt"), 2, "seems easiest to 'leak' types, and ensure they live forever") self.assertEquals(CPyMarshal.ReadPtrField(cPtr, PyTypeObject, "ob_type"), mapper.PyType_Type) self.assertEquals(CPyMarshal.ReadPtrField(cPtr, PyTypeObject, "tp_base"), mapper.PyBaseObject_Type) self.assertEquals(CPyMarshal.ReadPtrField(cPtr, PyTypeObject, "tp_bases"), IntPtr.Zero) self.assertEquals(CPyMarshal.ReadPtrField(cPtr, PyTypeObject, "tp_as_number"), IntPtr.Zero) namePtr = CPyMarshal.ReadPtrField(cPtr, PyTypeObject, "tp_name") self.assertEquals(mapper.Retrieve(namePtr), "cantankerous.cochineal") baseFlags = CPyMarshal.ReadIntField(cPtr, PyTypeObject, "tp_flags") self.assertEquals(baseFlags & UInt32(Py_TPFLAGS.READY), UInt32(Py_TPFLAGS.READY), "did not ready newly-stored type") instancePtr = Marshal.AllocHGlobal(Marshal.SizeOf(PyObject)) CPyMarshal.WritePtrField(instancePtr, PyObject, "ob_type", cPtr) CPyMarshal.WriteIntField(instancePtr, PyObject, "ob_refcnt", 2) instance = mapper.Retrieve(instancePtr) self.assertEquals(isinstance(instance, C), True) self.assertEquals(mapper.Store(instance), instancePtr)