def testTypeMappings(self, mapper, _): for (k, v) in BUILTIN_TYPES.items(): typePtr = getattr(mapper, k) self.assertEquals(CPyMarshal.ReadCStringField(typePtr, PyTypeObject, 'tp_name'), v.__name__) if typePtr == mapper.PyFile_Type: self.assertNotEquals(mapper.Retrieve(typePtr), v, "failed to map PyFile_Type to something-that-isn't file") else: self.assertEquals(mapper.Retrieve(typePtr), v, "failed to map " + k) if typePtr in (mapper.PyType_Type, mapper.PyBaseObject_Type): # surprising refcount because of the unmanaged PyFile malarkey self.assertEquals(mapper.RefCount(typePtr), 2, "failed to add reference to " + k) else: self.assertEquals(mapper.RefCount(typePtr), 1, "failed to add reference to " + k) mapper.PyType_Ready(typePtr) self.assertNotEquals(CPyMarshal.ReadIntField(typePtr, PyTypeObject, "tp_basicsize"), 0) basePtr = CPyMarshal.ReadPtrField(typePtr, PyTypeObject, "tp_base") if k == "PyBaseObject_Type": self.assertEquals(basePtr, IntPtr.Zero) elif k == "PyBool_Type": self.assertEquals(basePtr, mapper.PyInt_Type) else: self.assertEquals(basePtr, mapper.PyBaseObject_Type)
def testReadyBuiltinTypes(self, mapper, _): mapper.ReadyBuiltinTypes() for _type in BUILTIN_TYPES: typePtr = getattr(mapper, _type) basePtr = CPyMarshal.ReadPtrField(typePtr, PyTypeObject, "tp_base") if typePtr != mapper.PySeqIter_Type: # PySeqIter_Type is suprrisingly tedious to turn into a proper PythonType in C# tp_dict = mapper.Retrieve( CPyMarshal.ReadPtrField(typePtr, PyTypeObject, "tp_dict")) self.assertEquals(mapper.Retrieve(typePtr).__dict__, tp_dict) if typePtr not in (mapper.PyBaseObject_Type, mapper.PyBool_Type): self.assertEquals( CPyMarshal.ReadPtrField(typePtr, PyTypeObject, "tp_base"), mapper.PyBaseObject_Type) if typePtr == mapper.PyBool_Type: self.assertEquals( CPyMarshal.ReadPtrField(typePtr, PyTypeObject, "tp_base"), mapper.PyInt_Type) typeTypePtr = CPyMarshal.ReadPtrField(typePtr, PyTypeObject, "ob_type") if typePtr != mapper.PyType_Type: self.assertEquals(typeTypePtr, mapper.PyType_Type)
def testUnmanagedThreadState(self, mapper, _): mapper.ReleaseGIL() # current thread state should be null if nobody has the GIL self.assertEquals(CPyMarshal.ReadPtr(mapper._PyThreadState_Current), IntPtr.Zero) mapper.EnsureGIL() mapper.LastException = NameError("Harold") ts = CPyMarshal.ReadPtr(mapper._PyThreadState_Current) curexc_type = CPyMarshal.ReadPtrField(ts, PyThreadState, "curexc_type") curexc_value = CPyMarshal.ReadPtrField(ts, PyThreadState, "curexc_value") self.assertEquals(mapper.Retrieve(curexc_type), NameError) self.assertEquals(mapper.Retrieve(curexc_value), "Harold") mapper.ReleaseGIL() def CheckOtherThread(): mapper.EnsureGIL() ts2 = CPyMarshal.ReadPtr(mapper._PyThreadState_Current) self.assertNotEquals(ts2, ts) curexc_type = CPyMarshal.ReadPtrField(ts2, PyThreadState, "curexc_type") curexc_value = CPyMarshal.ReadPtrField(ts2, PyThreadState, "curexc_value") self.assertEquals(curexc_type, IntPtr.Zero) self.assertEquals(curexc_value, IntPtr.Zero) mapper.ReleaseGIL() thread = Thread(ThreadStart(CheckOtherThread)) thread.Start() thread.Join() mapper.EnsureGIL()
def testPyList_DeallocDecRefsItemsAndCallsCorrectFreeFunction(self): frees = [] mapper = PythonMapper(GetAllocatingTestAllocator([], frees)) deallocTypes = CreateTypes(mapper) calls = [] def CustomFree(ptr): calls.append(ptr) mapper.PyObject_Free(listPtr) self.freeDgt = dgt_void_ptr(CustomFree) CPyMarshal.WriteFunctionPtrField(mapper.PyList_Type, PyTypeObject, "tp_free", self.freeDgt) listPtr = mapper.Store([1, 2, 3]) itemPtrs = [] dataStore = CPyMarshal.ReadPtrField(listPtr, PyListObject, "ob_item") for _ in range(3): itemPtrs.append(CPyMarshal.ReadPtr(dataStore)) dataStore = OffsetPtr(dataStore, CPyMarshal.PtrSize) mapper.IC_PyList_Dealloc(listPtr) for itemPtr in itemPtrs: self.assertEquals(itemPtr in frees, True, "did not decref item") self.assertEquals(calls, [listPtr], "did not call type's free function") mapper.Dispose() deallocTypes()
def CreateInstance(mapper, calls): listPtr = mapper.Store([1, 2, 3]) dataStore = CPyMarshal.ReadPtrField(listPtr, PyListObject, "ob_item") for _ in range(3): itemPtrs.append(CPyMarshal.ReadPtr(dataStore)) dataStore = OffsetPtr(dataStore, CPyMarshal.PtrSize) return listPtr
def log_info(obj, size=None): """ Print useful debugging information about the first argument. Optional second argument allows you to specify how many bytes of the object's unmanaged representation to print; it is not generally useful or wise to make use of it. """ print print 'before storing:' _mapper.LogMappingInfo(id(obj)) print print 'after storing:' objPtr = _mapper.Store(obj) if size is None: typePtr = CPyMarshal.ReadPtrField(objPtr, PyObject, "ob_type") size = CPyMarshal.ReadIntField(typePtr, PyTypeObject, "tp_basicsize") itemsize = CPyMarshal.ReadIntField(typePtr, PyTypeObject, "tp_itemsize") if itemsize > 0: itemcount = CPyMarshal.ReadIntField(objPtr, PyVarObject, "ob_size") size += itemcount * itemsize print 'printing %d bytes of object at %x' % (size, objPtr) CPyMarshal.Log(objPtr, size) print _mapper.DecRef(objPtr)
def assertTypeDeallocWorks(self, typename, CreateMapper, CreateInstance, TestConsequences): mapper = CreateMapper() deallocTypes = CreateTypes(mapper) calls = [] def tp_free(ptr): calls.append(("tp_free", ptr)) self.tp_freeDgt = dgt_void_ptr(tp_free) CPyMarshal.WriteFunctionPtrField(getattr(mapper, typename), PyTypeObject, "tp_free", self.tp_freeDgt) objPtr = CreateInstance(mapper, calls) deallocDgt = CPyMarshal.ReadFunctionPtrField(getattr(mapper, typename), PyTypeObject, "tp_dealloc", dgt_void_ptr) deallocDgt(objPtr) TestConsequences(mapper, objPtr, calls) mapper.Dispose() deallocTypes()
def testStoreSlice(self, mapper, _): obj = slice(1, 2, 3) objPtr = mapper.Store(obj) self.assertEquals(mapper.RefCount(objPtr), 1) self.assertEquals(mapper.Retrieve(objPtr), obj) self.assertEquals( CPyMarshal.ReadPtrField(objPtr, PySliceObject, "ob_type"), mapper.PySlice_Type) self.assertEquals( CPyMarshal.ReadIntField(objPtr, PySliceObject, "ob_refcnt"), 1) startPtr = CPyMarshal.ReadPtrField(objPtr, PySliceObject, "start") self.assertEquals(mapper.Retrieve(startPtr), 1) mapper.IncRef(startPtr) stopPtr = CPyMarshal.ReadPtrField(objPtr, PySliceObject, "stop") self.assertEquals(mapper.Retrieve(stopPtr), 2) mapper.IncRef(stopPtr) stepPtr = CPyMarshal.ReadPtrField(objPtr, PySliceObject, "step") self.assertEquals(mapper.Retrieve(stepPtr), 3) mapper.IncRef(stepPtr) mapper.DecRef(objPtr) self.assertEquals(mapper.RefCount(startPtr), 1) self.assertEquals(mapper.RefCount(stopPtr), 1) self.assertEquals(mapper.RefCount(stepPtr), 1)
def assertPyTuple_New_Works(self, length): allocs = [] mapper = PythonMapper(GetAllocatingTestAllocator(allocs, [])) typeBlock = Marshal.AllocHGlobal(Marshal.SizeOf(PyTypeObject)) mapper.RegisterData("PyTuple_Type", typeBlock) tuplePtr = mapper.PyTuple_New(length) expectedSize = Marshal.SizeOf(PyTupleObject) + (CPyMarshal.PtrSize * (length - 1)) self.assertEquals(allocs, [(tuplePtr, expectedSize)], "bad alloc") tupleStruct = Marshal.PtrToStructure(tuplePtr, PyTupleObject) self.assertEquals(tupleStruct.ob_refcnt, 1, "bad refcount") self.assertEquals(tupleStruct.ob_type, mapper.PyTuple_Type, "bad type") self.assertEquals(tupleStruct.ob_size, length, "bad size") self.assertEquals(mapper.PyTuple_Size(tuplePtr), length, "should still work with uninitialised tuple imo") dataPtr = OffsetPtr(tuplePtr, Marshal.OffsetOf(PyTupleObject, "ob_item")) itemPtrs = [] for i in range(length): self.assertEquals(CPyMarshal.ReadPtr(dataPtr), IntPtr.Zero, "item memory not zeroed") itemPtr = mapper.Store(i + 100) CPyMarshal.WritePtr(dataPtr, itemPtr) itemPtrs.append(itemPtr) dataPtr = OffsetPtr(dataPtr, CPyMarshal.PtrSize) immutableTuple = mapper.Retrieve(tuplePtr) self.assertEquals(immutableTuple, tuple(i + 100 for i in range(length)), "broken") tuplePtr2 = mapper.Store(immutableTuple) self.assertEquals(tuplePtr2, tuplePtr, "didn't realise already had this object stored") self.assertEquals(mapper.RefCount(tuplePtr), 2, "didn't incref") mapper.Dispose() Marshal.FreeHGlobal(typeBlock)
def testInternExisting(self, mapper, addToCleanUp): testString = "mars needs women" + self.getStringWithValues(1, 256) bytes = self.byteArrayFromString(testString) testData = self.ptrFromByteArray(bytes) sp1 = mapper.PyString_FromString(testData) addToCleanUp(lambda: Marshal.FreeHGlobal(sp1p)) sp2 = mapper.PyString_InternFromString(testData) addToCleanUp(lambda: Marshal.FreeHGlobal(testData)) self.assertNotEquals(sp1, sp2) self.assertFalse(mapper.Retrieve(sp1) is mapper.Retrieve(sp2)) self.assertEquals(mapper.RefCount(sp1), 1) self.assertEquals( mapper.RefCount(sp2), 2, 'failed to grab extra reference to induce immortality') mapper.IncRef(sp1) sp1p = Marshal.AllocHGlobal(Marshal.SizeOf(IntPtr)) CPyMarshal.WritePtr(sp1p, sp1) mapper.PyString_InternInPlace(sp1p) sp1i = CPyMarshal.ReadPtr(sp1p) self.assertEquals(sp1i, sp2, 'failed to intern') self.assertTrue(mapper.Retrieve(sp1i) is mapper.Retrieve(sp2)) self.assertEquals(mapper.RefCount(sp1), 1, 'failed to decref old string') self.assertEquals(mapper.RefCount(sp2), 3, 'failed to incref interned string')
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 testStoreTupleCreatesTupleType(self): allocs = [] mapper = PythonMapper(GetAllocatingTestAllocator(allocs, [])) typeBlock = Marshal.AllocHGlobal(Marshal.SizeOf(PyTypeObject())) mapper.RegisterData("PyTuple_Type", typeBlock) theTuple = (0, 1, 2) tuplePtr = mapper.Store(theTuple) self.assertEquals( CPyMarshal.ReadPtrField(tuplePtr, PyTupleObject, "ob_type"), typeBlock, "wrong type") dataPtr = OffsetPtr(tuplePtr, Marshal.OffsetOf(PyTupleObject, "ob_item")) for i in range(3): item = mapper.Retrieve(CPyMarshal.ReadPtr(dataPtr)) self.assertEquals(item, i, "did not store data") dataPtr = OffsetPtr(dataPtr, CPyMarshal.PtrSize) tuplePtr2 = mapper.Store(theTuple) self.assertEquals(tuplePtr2, tuplePtr, "didn't realise already had this tuple") self.assertEquals(mapper.RefCount(tuplePtr), 2, "didn't incref") mapper.Dispose() Marshal.FreeHGlobal(typeBlock)
def testSizes(self, mapper, _): tp_basicsize = CPyMarshal.ReadIntField(mapper.PyString_Type, PyTypeObject, 'tp_basicsize') self.assertNotEquals(tp_basicsize, 0) tp_itemsize = CPyMarshal.ReadIntField(mapper.PyString_Type, PyTypeObject, 'tp_itemsize') self.assertNotEquals(tp_itemsize, 0)
def testNumberMethods(self, mapper, _): numberTypes = ("PyInt_Type", "PyLong_Type", "PyFloat_Type", "PyComplex_Type") implementedFields = { "nb_int": mapper.GetFuncPtr("PyNumber_Int"), "nb_long": mapper.GetFuncPtr("PyNumber_Long"), "nb_float": mapper.GetFuncPtr("PyNumber_Float"), "nb_multiply": mapper.GetFuncPtr("PyNumber_Multiply") } for _type in numberTypes: typePtr = getattr(mapper, _type) nmPtr = CPyMarshal.ReadPtrField(typePtr, PyTypeObject, "tp_as_number") self.assertNotEquals(nmPtr, IntPtr.Zero) for field in implementedFields: fieldPtr = CPyMarshal.ReadPtrField(nmPtr, PyNumberMethods, field) self.assertNotEquals(fieldPtr, IntPtr.Zero) self.assertEquals(fieldPtr, implementedFields[field]) flags = CPyMarshal.ReadIntField(typePtr, PyTypeObject, "tp_flags") hasIndex = bool(flags & int(Py_TPFLAGS.HAVE_INDEX)) if (not _type in ("PyFloat_Type", "PyComplex_Type")): self.assertEquals(hasIndex, True, _type) fieldPtr = CPyMarshal.ReadPtrField(nmPtr, PyNumberMethods, "nb_index") self.assertNotEquals(fieldPtr, IntPtr.Zero) self.assertEquals(fieldPtr, mapper.GetFuncPtr("PyNumber_Index")) else: self.assertEquals(hasIndex, False)
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 testSomeItems(self): allocs = [] mapper = PythonMapper(GetAllocatingTestAllocator(allocs, [])) deallocTypes = CreateTypes(mapper) typeSpec = { "tp_basicsize": 32, "tp_itemsize": 64, } typePtr, deallocType = MakeTypePtr(mapper, typeSpec) del allocs[:] result = mapper.PyType_GenericAlloc(typePtr, 3) self.assertEquals(allocs, [(result, 224)], "allocated wrong") refcount = CPyMarshal.ReadIntField(result, PyObject, "ob_refcnt") self.assertEquals(refcount, 1, "bad initialisation") instanceType = CPyMarshal.ReadPtrField(result, PyObject, "ob_type") self.assertEquals(instanceType, typePtr, "bad type ptr") size = CPyMarshal.ReadIntField(result, PyVarObject, "ob_size") self.assertEquals(size, 3, "bad ob_size") headerSize = Marshal.SizeOf(PyVarObject) zerosPtr = OffsetPtr(result, headerSize) for i in range(224 - headerSize): self.assertEquals(CPyMarshal.ReadByte(zerosPtr), 0, "not zeroed") zerosPtr = OffsetPtr(zerosPtr, 1) mapper.Dispose() deallocTypes() deallocType()
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 test_PyTuple_Resize_TooBig(self, mapper, addDealloc): tuplePtrPtr = Marshal.AllocHGlobal(CPyMarshal.PtrSize) addDealloc(lambda: Marshal.FreeHGlobal(tuplePtrPtr)) tuplePtr = mapper.PyTuple_New(1) CPyMarshal.WritePtr(tuplePtrPtr, tuplePtr) self.assertEquals(mapper._PyTuple_Resize(tuplePtrPtr, 2000000000), -1) self.assertEquals(CPyMarshal.ReadPtr(tuplePtrPtr), IntPtr.Zero)
def testStoreIPyFile(self, mapper, _): f = open(*READ_ARGS) fPtr = mapper.Store(f) self.assertEquals(CPyMarshal.ReadPtrField(fPtr, PyObject, 'ob_type'), mapper.PyFile_Type) self.assertEquals(CPyMarshal.ReadIntField(fPtr, PyObject, 'ob_refcnt'), 1) self.assertEquals(CPyMarshal.ReadIntField(fPtr, PyFileObject, 'f_fp'), -2) self.assertEquals(mapper.Retrieve(CPyMarshal.ReadPtrField(fPtr, PyFileObject, 'f_name')), READ_ARGS[0]) self.assertEquals(mapper.Retrieve(CPyMarshal.ReadPtrField(fPtr, PyFileObject, 'f_mode')), READ_ARGS[1])
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 testWriteDoubleField(self): data = Marshal.AllocHGlobal(Marshal.SizeOf(PyFloatObject())) CPyMarshal.Zero(data, Marshal.SizeOf(PyFloatObject())) CPyMarshal.WriteDoubleField(data, PyFloatObject, "ob_fval", 7.6e-5) dataStruct = PtrToStructure(data, PyFloatObject) self.assertEquals(dataStruct.ob_fval, 7.6e-5) Marshal.FreeHGlobal(data)
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 testWriteIntField(self): data = Marshal.AllocHGlobal(Marshal.SizeOf(PyIntObject())) CPyMarshal.Zero(data, Marshal.SizeOf(PyIntObject())) for value in (Int32.MaxValue, Int32.MinValue): CPyMarshal.WriteIntField(data, PyIntObject, "ob_ival", value) dataStruct = PtrToStructure(data, PyIntObject) self.assertEquals(dataStruct.ob_ival, value, "failed to write") Marshal.FreeHGlobal(data)
def testReadUInt(self): data = Marshal.AllocHGlobal(CPyMarshal.IntSize) Marshal.WriteInt32(data, 0) self.assertEquals(CPyMarshal.ReadUInt(data), 0, "wrong") Marshal.WriteInt32(data, -1) self.assertEquals(CPyMarshal.ReadUInt(data), 0xFFFFFFFF, "wrong") Marshal.FreeHGlobal(data)
def testReadByte(self): data = Marshal.AllocHGlobal(1) Marshal.WriteByte(data, 0) self.assertEquals(CPyMarshal.ReadByte(data), 0, "wrong") Marshal.WriteByte(data, 255) self.assertEquals(CPyMarshal.ReadByte(data), 255, "wrong") 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 testStringifiers(self, mapper, _): IC_PyString_Str = mapper.GetFuncPtr("IC_PyString_Str") tp_str = CPyMarshal.ReadPtrField(mapper.PyString_Type, PyTypeObject, "tp_str") self.assertEquals(tp_str, IC_PyString_Str) PyObject_Repr = mapper.GetFuncPtr("PyObject_Repr") tp_repr = CPyMarshal.ReadPtrField(mapper.PyString_Type, PyTypeObject, "tp_repr") self.assertEquals(tp_repr, PyObject_Repr)
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 testPyInt_FromSsize_t(self, mapper, _): for value in (0, Int32.MaxValue, Int32.MinValue): ptr = mapper.PyInt_FromSsize_t(value) self.assertEquals(mapper.Retrieve(ptr), value) self.assertEquals( CPyMarshal.ReadPtrField(ptr, PyIntObject, "ob_type"), mapper.PyInt_Type) self.assertEquals( CPyMarshal.ReadIntField(ptr, PyIntObject, "ob_ival"), value) mapper.DecRef(ptr)