Skip to content

Commit

Permalink
Use method_getNumberOfArguments, method_copyArgumentType and method_c…
Browse files Browse the repository at this point in the history
…opyReturnType to define argument and return types
  • Loading branch information
misl6 committed Dec 4, 2022
1 parent 4635143 commit a8a3fe2
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 53 deletions.
3 changes: 2 additions & 1 deletion pyobjus/common.pxi
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,9 @@ cdef extern from "objc/runtime.h":
const_char_ptr sel_getName(SEL)

SEL method_getName(Method)
const_char_ptr method_getTypeEncoding(Method)
unsigned int method_getNumberOfArguments(Method)
const_char_ptr method_copyArgumentType(Method method, int)
const_char_ptr method_copyReturnType(Method)
objc_method_description* method_getDescription(Method m)

Class object_getClass(id obj)
Expand Down
49 changes: 28 additions & 21 deletions pyobjus/pyobjus.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,6 @@ def convert_return_value(retval, clsname, methodname):

cdef class ObjcMethod(object):
cdef bytes name
cdef bytes signature
cdef int is_static
cdef object signature_return
cdef object signature_current_args
Expand All @@ -206,7 +205,7 @@ cdef class ObjcMethod(object):
cdef ffi_type **f_arg_types
cdef object objc_name

def __cinit__(self, signature, objc_name, **kwargs):
def __cinit__(self, objc_name, signature_args, signature_return, **kwargs):
self.is_ready = 0
self.f_result_type = NULL
self.f_arg_types = NULL
Expand All @@ -233,10 +232,12 @@ cdef class ObjcMethod(object):
#self.f_result_type = NULL
# TODO: Memory management

def __init__(self, bytes signature, bytes objc_name, **kwargs):
def __init__(self, bytes objc_name, signature_args, signature_return, **kwargs):
super(ObjcMethod, self).__init__()
self.signature = <bytes>signature
self.signature_return, self.signature_args = parse_signature(signature)

self.signature_args = [clean_type_specifier(sign_arg) for sign_arg in signature_args]
self.signature_return = clean_type_specifier(signature_return)

self.is_static = kwargs.get('static', False)
self.name = kwargs.get('name')
self.objc_name = objc_name
Expand Down Expand Up @@ -267,8 +268,8 @@ cdef class ObjcMethod(object):
else:
self.name = self.objc_name

if self.signature_return[0].startswith((b'(', b'{')):
sig = self.signature_return[0]
if self.signature_return.startswith((b'(', b'{')):
sig = self.signature_return
self.return_type = sig[1:-1].split(b'=', 1)

self.selector = sel_registerName(<bytes>self.name)
Expand All @@ -285,12 +286,12 @@ cdef class ObjcMethod(object):
dprint('signature: {}'.format(signature_args))

# resolve f_result_type
if self.signature_return[0].startswith(b'('):
if self.signature_return.startswith(b'('):
self.f_result_type = type_encoding_to_ffitype(
self.signature_return[0], str_in_union=True)
self.signature_return, str_in_union=True)
else:
self.f_result_type = type_encoding_to_ffitype(
self.signature_return[0])
self.signature_return)

# casting is needed here because otherwise we will get warning at compile
cdef unsigned int num_args = <unsigned int>len(signature_args)
Expand All @@ -308,12 +309,12 @@ cdef class ObjcMethod(object):
# populate f_args_type array for FFI prep
cdef int index = 0
for arg in signature_args:
if arg[0].startswith(b'('):
if arg.startswith(b'('):
raise ObjcException(
'Currently passing unions as arguments by '
'value is not supported in pyobjus!')
dprint('argument ==>', arg, len(signature_args))
self.f_arg_types[index] = type_encoding_to_ffitype(arg[0])
self.f_arg_types[index] = type_encoding_to_ffitype(arg)
index = index + 1

# FFI PREP
Expand Down Expand Up @@ -355,7 +356,6 @@ cdef class ObjcMethod(object):
cdef int index
cdef size_t size
cdef ObjcClassInstance arg_objcclass
cdef size_t result_size = <size_t>int(self.signature_return[1])

# check that we have at least the same number of arguments as the
# signature want (having more than expected could signify that the called
Expand Down Expand Up @@ -400,7 +400,7 @@ cdef class ObjcMethod(object):
sig_index = -1
signature_args.append(signature_args[-1])

sig, offset, attr = sig_full = signature_args[sig_index]
sig = signature_args[sig_index]
arg_size = type_encoding_to_ffitype(sig).size

# we already know the ffitype/size being used
Expand Down Expand Up @@ -430,7 +430,7 @@ cdef class ObjcMethod(object):
if res_ptr == NULL:
raise MemoryError('Unable to allocate res_ptr')

if not self.signature_return[0].startswith((b'(', b'{')):
if not self.signature_return.startswith((b'(', b'{')):
ffi_call(&self.f_cif, <void(*)()><id(*)(id, SEL)>objc_msgSend, res_ptr, f_args)

else:
Expand All @@ -456,7 +456,7 @@ cdef class ObjcMethod(object):
size_ret = ctypes.sizeof(obj_ret)

stret = False
if self.signature_return[0].startswith((b'{', b'(')) and size_ret > 16:
if self.signature_return.startswith((b'{', b'(')) and size_ret > 16:
stret = True

if stret and MACOS_HAVE_OBJMSGSEND_STRET:
Expand Down Expand Up @@ -485,8 +485,8 @@ cdef class ObjcMethod(object):
cdef ObjcClassInstance cret
cdef bytes bret

sig = self.signature_return[0]
dprint("return signature", self.signature_return[0], of_type="i")
sig = self.signature_return
dprint("return signature", self.signature_return, of_type="i")

if sig == b'@':
ret_id = (<id>res_ptr[0])
Expand Down Expand Up @@ -524,10 +524,13 @@ cdef objc_method_to_py(Method method, main_cls_name, static=True):
'''

cdef char* method_name = <char*>sel_getName(method_getName(method))
cdef char* method_args = <char*>method_getTypeEncoding(method)
cdef unsigned int method_args_len = method_getNumberOfArguments(method)
cdef char* method_return_type = method_copyReturnType(method)
cdef list method_args_types = [method_copyArgumentType(method, i) for i in range(method_args_len)]

cdef basestring py_name = (<bytes>method_name).replace(b":", b"_").decode("utf-8")

return py_name, ObjcMethod(<bytes>method_args, method_name, static=static, main_cls_name=main_cls_name)
return py_name, ObjcMethod(method_name, method_args_types, method_return_type, static=static, main_cls_name=main_cls_name)

cdef class_get_methods(Class cls, static=False, main_cls_name=None):
cdef unsigned int index, num_methods
Expand Down Expand Up @@ -600,7 +603,11 @@ cdef get_class_method(Class cls, char *name):
ObjcMethod instance
'''
cdef Method m_cls = class_getClassMethod(cls, sel_registerName(name))
return ObjcMethod(<bytes><char*>method_getTypeEncoding(m_cls), name, static=True, \
cdef unsigned int method_args_len = method_getNumberOfArguments(m_cls)
cdef char* method_return_type = method_copyReturnType(m_cls)
cdef list method_args_types = [method_copyArgumentType(m_cls, i) for i in range(method_args_len)]

return ObjcMethod(name, method_args_types, method_return_type, static=True, \
main_cls_name=class_getName(cls))

cdef resolve_super_class_methods(Class cls, instance_methods=True):
Expand Down
38 changes: 7 additions & 31 deletions pyobjus/type_enc.pxi
Original file line number Diff line number Diff line change
@@ -1,36 +1,12 @@
def seperate_encoding(sig):
c = sig[0][:1]

if c in b'rnNoORV':
sig = (sig[0][1:], sig[1], c)
else:
sig = (sig[0], sig[1], None)
def clean_type_specifier(sig):
"""
Clean up the type specifier for a function signature.
See: https://gcc.gnu.org/onlinedocs/gcc-5.3.0/gcc/Type-encoding.html
"""
if sig[:1] in b'rnNoORV':
return sig[1:]
return sig


def parse_signature(bytes signature):
parts = re.split(b'(\d+)', signature)[:-1]
signature_return = seperate_encoding(parts[0:2])
parts = parts[2:]
signature_args = [seperate_encoding(x) for x in zip(parts[0::2], parts[1::2])]

# reassembly for array
if b'[' in signature:
tmp_sig = []
arr_sig = b''
for item in signature_args:
if item[0].startswith(b'['):
arr_sig += item[0] + item[1]
elif item[0].endswith(b']'):
arr_sig += item[0]
tmp_sig.append((arr_sig, item[1], item[2]))
else:
tmp_sig.append(item)
signature_args = tmp_sig

return signature_return, signature_args


def signature_types_to_list(type_encoding):
type_enc_list = []
curvy_brace_count = 0
Expand Down

0 comments on commit a8a3fe2

Please sign in to comment.