Commits

Ronald Oussoren committed 53a80d0

Add tests for blocks that return a large struct (NSRect)

The tests that actually call these blocks are disabled for
now because they crash the interpreter (because PyObjC doesn't
have the code needed to support these)

Comments (0)

Files changed (4)

pyobjc-core/Modules/objc/block_support.m

     PyObject* invoke_cleanup;
 };
 
-extern const char* PyObjCBlock_GetSignature(void* _block)
+const char*
+PyObjCBlock_GetSignature(void* _block)
 {
     struct block_literal* block = (struct block_literal*)_block;
     struct block_descriptor_basic* descriptor = (struct block_descriptor_basic*)block->descriptor;
-    int offset = 0;
+    size_t offset = 0;
 
-    offset = 0;
     if (block->flags & BLOCK_HAS_COPY_DISPOSE) {
         offset += 2;
     }
+
     if (block->flags & BLOCK_HAS_SIGNATURE) {
         return descriptor->rest[offset];
     }
     return 0;
 }
 
+
 _block_func_ptr
 PyObjCBlock_GetFunction(void* block)
 {

pyobjc-core/Modules/objc/module.m

     return Py_None;
 }
 
+static PyObject*
+block_signature(PyObject* mod __attribute__((__unused__)), PyObject* block)
+{
+    if (!PyObjCObject_Check(block) || !PyObjCObject_IsBlock(block)) {
+        PyErr_SetString(PyExc_ValueError, "Not a block");
+        return NULL;
+    }
+
+    const char* sig = PyObjCBlock_GetSignature(PyObjCObject_GetObject(block));
+    if (sig == NULL) {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+
+    return PyBytes_FromString(sig);
+}
+
+
+
 static PyMethodDef mod_methods[] = {
     {
         "propertiesForClass",
         METH_VARARGS,
         "_block_call(block, signature, args, kwds) -> retval" },
 
+    { "_block_signature", (PyCFunction)block_signature,
+        METH_O, "return signature string for a block, or None" },
     { "_typestr2typestr", (PyCFunction)typestr2typestr,
         METH_O, "private function" },
 

pyobjc-core/Modules/objc/test/block.m

 
 -(int(^)(void))getIntBlock;
 -(double(^)(double,double))getFloatBlock;
+-(NSRect(^)(double, double, double, double))getStructBlock;
 -(void)callIntBlock:(void(^)(int))block withValue:(int)value;
 -(double)callDoubleBlock:(double(^)(double, double))block withValue:(double)v1 andValue:(double)v2;
 #endif
 
 #if PyObjC_BUILD_RELEASE >= 1006 && (__GNUC__ >= 4 && __GNUC_MINOR__ >= 2)
 
+-(NSRect(^)(double, double, double, double))getStructBlock
+{
+    return [[^(double a, double b, double c, double d) {
+        return NSMakeRect(a, b, c, d);
+    } copy] autorelease];
+}
+
 -(int(^)(void))getIntBlock
 {
     return [[^{ return 42; } copy] autorelease];
 {
     return block(v1, v2);
 }
+
+-(NSRect)callStructBlock: (NSRect(^)(double, double, double, double))block
+                   withA:(double)a b:(double)b c:(double)c d:(double)d
+{
+    return block(a, b, c, d);
+}
+
 #endif
 
 @end

pyobjc-core/PyObjCTest/test_blocks.py

 from PyObjCTools.TestSupport import *
 from PyObjCTest.block import OCTestBlock
 import objc
+import sys
+
+if sys.maxsize > 2 ** 32:
+    NSRect_tp = b'{CGRect={CGPoint=dd}{CGSize=dd}}'
+else:
+    NSRect_tp = b'{_NSRect={_NSPoint=ff}{_NSSize=ff}}'
 
 objc.parseBridgeSupport('''\
     <?xml version='1.0'?>
         self.assertEqual(obj.callDoubleBlock_withValue_andValue_(callback, 2.0, 3.5), 7.0)
         self.assertEqual(obj.callDoubleBlock_withValue_andValue_(callback, 2.5, 10), 25.0)
 
+    @min_os_level('10.6')
+    @onlyIf(blocksEnabled, "no blocks")
+    def testBlockToObjC3(self):
+        return
+
+        obj = OCTestBlock.alloc().init()
+
+        lst = []
+        def callback(a, b, c, d):
+            return ((a, b), (c, d))
+
+        v = obj.callStructBlock_withA_b_c_d_(callback, 1.5, 2.5, 3.5, 4.5)
+        print(v)
+
 
     @min_os_level('10.6')
     @onlyIf(blocksEnabled, "no blocks")
         value = block(2.5, 7.0)
         self.assertEqual(value, 9.5)
 
+    @min_os_level('10.6')
+    @onlyIf(blocksEnabled, "no blocks")
+    def testBlockFromObjC3(self):
+        obj = OCTestBlock.alloc().init()
+
+        return
+
+        block = obj.getStructBlock()
+        value = block(1, 2, 3, 4)
+        self.assertEqual(len(value), 4)
+        self.assertEqual(list(value), ((1.0, 2.0), (3.0, 4.0)))
+
+
+    @min_os_level('10.6')
+    @onlyIf(blocksEnabled, "no blocks")
+    def testBlockSignatures(self):
+        obj = OCTestBlock.alloc().init()
+
+        block = obj.getFloatBlock()
+        sig = objc.splitSignature(objc._block_signature(block))
+        self.assertEqual(sig,  (objc._C_DBL, objc._C_ID + b'?', objc._C_DBL, objc._C_DBL))
+
+        block = obj.getStructBlock()
+        sig = objc.splitSignature(objc._block_signature(block))
+        self.assertEqual(sig,  (NSRect_tp, objc._C_ID + b'?', objc._C_DBL, objc._C_DBL, objc._C_DBL, objc._C_DBL))
+
+
+
 if __name__ == "__main__":
     main()
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.