|
[Pyobjc-checkins] CVS: pyobjc/Modules/objc libffi_support.m,NONE,1.1 alloc_hack.m,1.2,1.3 objc_support.m,1.9,1.10 pyobjc.h,1.11,1.12 register.m,1.4,1.5 super-call.m,1.4,1.5
From: Ronald Oussoren <ronaldoussoren@us...> - 2002-12-22 17:47
|
Update of /cvsroot/pyobjc/pyobjc/Modules/objc In directory sc8-pr-cvs1:/tmp/cvs-serv25595/Modules/objc Modified Files: alloc_hack.m objc_support.m pyobjc.h register.m super-call.m Added Files: libffi_support.m Log Message: - setup.py: By default strip the .so files, this seriously reduces the size of module objc._objc. - First stab at support for libffi, off by default. * Edit setup.py to enable. * Needs libffi from GCC (NOT the libffi-release on sources.redhat.com) - Minor bugfix for execute_and_pythonify_objc_method (copy-paste error) --- NEW FILE: libffi_support.m --- /* * Support for libffi (http://sources.redhat.com/libffi) * * libffi is a library that makes it possible to dynamicly create calls * to C functions (without knowing the signature at compile-time). It also * provides a way to create closures, that is dynamicly create functions with * a runtime specified interface. * * This file contains functions to dynamicly call objc_msgSendSuper and to * dynamicly create IMPs for use in Objective-C method dispatch tables. The * file 'register.m' contains compile-time generated equivalents of these. * * NOTES: * - libffi support is optionial because it is highly experimental. To avoid * code duplication it is my (Ronald's) intention to move to a situation * where libffi support is required. This should also make it possible to * simplify some code. * - If libffi stays optional, we should refactor ObjC_FFICaller (in this file) * and execute_and_pythonify_objc_method (in objc_support.m): These are * almost identical. */ #ifndef OC_WITH_LIBFFI static int OBJC_FFI_SUPPORT_dummy = 0; #warning "No FFI support" #else /* OC_WITH_LIBFFI */ #include "ffi.h" #include "pyobjc.h" #include "objc_support.h" #ifndef FFI_CLOSURES # error "Need FFI_CLOSURES!" #endif static int count_struct(const char* argtype) { int res = 0; if (*argtype != _C_STRUCT_B) return -1; argtype++; while (*argtype != _C_STRUCT_E) { argtype = objc_skip_typespec(argtype); res ++; } return res; } static void free_type(void *obj) { free(((ffi_type*)obj)->elements); free(obj); } static ffi_type* signature_to_ffi_type(const char* argtype); static ffi_type* struct_to_ffi_type(const char* argtype) { static PyObject* struct_types = NULL; PyObject* v; ffi_type* type; int field_count; const char* curtype; if (struct_types == NULL) { struct_types = PyDict_New(); if (struct_types == NULL) return NULL; } v = PyDict_GetItemString(struct_types, (char*)argtype); if (v != NULL) { return (ffi_type*)PyCObject_AsVoidPtr(v); } /* We don't have a type description yet, dynamicly * create it. */ field_count = count_struct(argtype); if (field_count == -1) abort(); type = malloc(sizeof(*type)); if (type == NULL) { PyErr_NoMemory(); return NULL; } type->size = 0; type->alignment = 0; type->type = FFI_TYPE_STRUCT; type->elements = malloc((1+field_count) * sizeof(*type->elements)); if (type->elements == NULL) { free(type); PyErr_NoMemory(); return NULL; } field_count = 0; curtype = argtype+1; while (*curtype != _C_STRUCT_E) { type->elements[field_count] = signature_to_ffi_type(curtype); if (type->elements[field_count] == NULL) { free(type->elements); return NULL; } field_count++; curtype = objc_skip_typespec(curtype); } type->elements[field_count] = NULL; v = PyCObject_FromVoidPtr(type, free_type); if (v == NULL) { free_type(type); return NULL; } PyDict_SetItemString(struct_types, (char*)argtype, v); if (PyErr_Occurred()) { Py_DECREF(v); return NULL; } return type; } static ffi_type* signature_to_ffi_type(const char* argtype) { switch (*argtype) { case _C_ID: return &ffi_type_pointer; case _C_CLASS: return &ffi_type_pointer; case _C_SEL: return &ffi_type_pointer; case _C_CHR: return &ffi_type_schar; case _C_UCHR: return &ffi_type_uchar; case _C_SHT: return &ffi_type_sshort; case _C_USHT: return &ffi_type_ushort; case _C_INT: return &ffi_type_sint; case _C_UINT: return &ffi_type_uint; case _C_LNG: return &ffi_type_slong; case _C_ULNG: return &ffi_type_ulong; case _C_LNGLNG: return &ffi_type_sint64; case _C_ULNGLNG: return &ffi_type_uint64; case _C_FLT: return &ffi_type_float; case _C_DBL: return &ffi_type_double; case _C_CHARPTR: return &ffi_type_pointer; case _C_PTR: return &ffi_type_pointer; case _C_ARY_B: return &ffi_type_pointer; case _C_IN: case _C_OUT: case _C_INOUT: case _C_CONST: return signature_to_ffi_type(argtype+1); case _C_STRUCT_B: return struct_to_ffi_type(argtype); default: ObjCErr_Set(PyExc_NotImplementedError, "Type '%s' not supported", argtype); return NULL; } } /* This function decodes its arguments into Python values, then * calls the python method and finally encodes the return value */ static void method_stub(ffi_cif* cif, void* resp, void** args, void* userdata) { NSMethodSignature* methinfo = (NSMethodSignature*)userdata; int objc_argcount; int i; PyObject* arglist; PyObject* res; PyObject* v; int have_output = 0; objc_argcount = [methinfo numberOfArguments]; arglist = PyList_New(0); PyList_Append(arglist, pythonify_c_value("@", args[0])); /* First translate from Objective-C to python */ for (i = 2; i < objc_argcount; i++) { const char* argtype = [methinfo getArgumentTypeAtIndex:i]; switch (*argtype) { case _C_PTR: have_output ++; v = pythonify_c_value(argtype+1, *(void**)args[i]); break; case _C_INOUT: if (argtype[1] == _C_PTR) { have_output ++; } /* FALL THROUGH */ case _C_IN: case _C_CONST: if (argtype[1] == _C_PTR) { v = pythonify_c_value(argtype+2, *(void**)args[i]); } else { v = pythonify_c_value(argtype+2, args[i]); } break; case _C_OUT: /* Skip output parameter */ if (argtype[1] == _C_PTR) { have_output ++; } continue; default: v = pythonify_c_value(argtype, args[i]); } if (v == NULL) { Py_DECREF(arglist); ObjCErr_ToObjC(); return; } PyList_Append(arglist, v); } v = PyList_AsTuple(arglist); if (v == NULL) { Py_DECREF(arglist); ObjCErr_ToObjC(); return; } Py_DECREF(arglist); arglist = v; res = ObjC_call_to_python(*(id*)args[0], *(SEL*)args[1], arglist); Py_DECREF(arglist); if (!have_output) { const char* err; err = depythonify_c_value([methinfo methodReturnType], res, resp); Py_DECREF(res); if (err) { ObjCErr_Set(PyExc_TypeError, "%s: Cannot encode return value: %s\n", SELNAME(*(SEL*)args[1]), err); ObjCErr_ToObjC(); } } else { /* We have some output parameter, locate them and encode * their values */ int idx; PyObject* real_res; if (!PyTuple_Check(res) || PyTuple_Size(res) != have_output+1) { ObjCErr_Set(PyExc_TypeError, "%s: Need tuple of %d arguments as result", SELNAME(*(SEL*)args[1]), have_output+1); ObjCErr_ToObjC(); } real_res = PyTuple_GET_ITEM(res, 0); idx = 1; for (i = 2; i < objc_argcount; i++) { const char* argtype = [methinfo getArgumentTypeAtIndex:i]; const char* err; switch (*argtype) { case _C_PTR: argtype ++; break; case _C_INOUT: case _C_OUT: if (argtype[1] != _C_PTR) { continue; } argtype += 2; break; default: continue; } v = PyTuple_GET_ITEM(res, idx++); err = depythonify_c_value(argtype, v, args[i]); Py_DECREF(res); if (err) { ObjCErr_Set(PyExc_TypeError, "%s: Cannot encode output argument " " %d: %s", SELNAME(*(SEL*)args[1]), i, err); ObjCErr_ToObjC(); } } } } /* * Return an IMP that is suitable for forwarding a method with the specified * signature from Objective-C to Python. */ IMP ObjC_MakeIMPForSignature(char* signature) { NSMethodSignature* methinfo; int objc_argcount; ffi_cif *cif; ffi_closure *cl; ffi_type** cl_arg_types; ffi_type* cl_ret_type; ffi_status rv; int i; methinfo = [NSMethodSignature signatureWithObjCTypes:signature]; objc_argcount = [methinfo numberOfArguments]; cl_arg_types = malloc(sizeof(ffi_type*) * objc_argcount); if (cl_arg_types == NULL) { PyErr_NoMemory(); return NULL; } cl_ret_type = signature_to_ffi_type([methinfo methodReturnType]); if (cl_ret_type == NULL) { [methinfo release]; free(cl_arg_types); return NULL; } for (i = 0; i < objc_argcount; i++) { cl_arg_types[i] = signature_to_ffi_type( [methinfo getArgumentTypeAtIndex:i]); if (cl_arg_types[i] == NULL) { [methinfo release]; free(cl_arg_types); return NULL; } } cif = malloc(sizeof(*cif)); if (cif == NULL) { free(cl_arg_types); [methinfo release]; PyErr_NoMemory(); return NULL; } rv = ffi_prep_cif(cif, FFI_DEFAULT_ABI, objc_argcount, cl_ret_type, cl_arg_types); if (rv != FFI_OK) { free(cl_arg_types); [methinfo release]; ObjCErr_Set(PyExc_RuntimeError, "Cannot create FFI CIF: %d", rv); return NULL; } cl = malloc(sizeof(*cl)); if (cl == NULL) { free(cl_arg_types); free(cif); [methinfo release]; PyErr_NoMemory(); return NULL; } rv = ffi_prep_closure(cl, cif, method_stub, methinfo); if (rv != FFI_OK) { [methinfo release]; ObjCErr_Set(PyExc_RuntimeError, "Cannot create FFI closure: %d", rv); return NULL; } return (IMP)cl; } /* FIXME: * This is a clone of execute_and_pythonify_objc_method in objc_support.m * Need to either abstract away differences or remove one of these... * * Changes w.r.t. execute_and_... * - All arguments are stored in 'argbuf', not only the structs * - libffi support */ PyObject * ObjC_FFICaller(PyObject *aMeth, PyObject* self, PyObject *args) { size_t argbuf_len = 0; size_t argbuf_cur = 0; unsigned char* argbuf = NULL; /* by-reference arguments */ size_t byref_in_count = 0; size_t byref_out_count = 0; size_t plain_count = 0; size_t objc_argcount; size_t py_arg; int i; int* byref = NULL; /* offset for arguments in argbuf */ const char* rettype; NSMethodSignature* methinfo; ObjCNativeSelector* meth = (ObjCNativeSelector*)aMeth; PyObject* objc_result = NULL; PyObject* result = NULL; id self_obj = nil; struct objc_super super; ffi_cif cif; ffi_type* arglist[64]; /* XX: Magic constant */ void* values[64]; int r; id msgResult; methinfo = [NSMethodSignature signatureWithObjCTypes:meth->sel_signature]; objc_argcount = [methinfo numberOfArguments]; /* First count the number of by reference parameters, and the number * of bytes of storage needed for them. Note that arguments 0 and 1 * are self and the selector, no need to count counted or checked those. */ for (i = 2; i < objc_argcount; i++) { const char *argtype = [methinfo getArgumentTypeAtIndex:i]; switch (*argtype) { case _C_PTR: byref_in_count ++; byref_out_count ++; argbuf_len += objc_sizeof_type(argtype+1); break; case _C_INOUT: if (argtype[1] == _C_PTR) { byref_out_count ++; byref_in_count ++; argbuf_len += objc_sizeof_type(argtype+2); } else { argbuf_len += objc_sizeof_type(argtype+1); } break; case _C_IN: case _C_CONST: if (argtype[1] == _C_PTR) { byref_in_count ++; argbuf_len += objc_sizeof_type(argtype+2); } else { argbuf_len += objc_sizeof_type(argtype+1); } break; case _C_OUT: if (argtype[1] == _C_PTR) { byref_out_count ++; argbuf_len += objc_sizeof_type(argtype+2); } else { argbuf_len += objc_sizeof_type(argtype+1); } break; case _C_STRUCT_B: case _C_UNION_B: case _C_ARY_B: plain_count++; argbuf_len += objc_sizeof_type(argtype); break; default: argbuf_len += objc_sizeof_type(argtype); plain_count++; break; } } /* * We need input arguments for every normal argument and for every * input argument that is passed by reference. */ if (PyTuple_Size(args) != (plain_count + byref_in_count)) { ObjCErr_Set(PyExc_TypeError, "Need %d arguments, got %d", plain_count + byref_in_count, PyTuple_Size(args)); goto error_cleanup; } if (argbuf_len) { argbuf = PyMem_Malloc(argbuf_len); if (argbuf == 0) { PyErr_NoMemory(); goto error_cleanup; } byref = PyMem_Malloc(sizeof(int) * objc_argcount); if (byref == NULL) { PyErr_NoMemory(); goto error_cleanup; } memset(byref, 0, sizeof(int) * objc_argcount); } else { argbuf = NULL; byref = NULL; } /* Set 'self' argument, for class methods we use the class */ if (meth->sel_flags & ObjCSelector_kCLASS_METHOD) { if (ObjCObject_Check(self)) { self_obj = ObjCObject_GetObject(self)->isa; } else if (ObjCClass_Check(self)) { self_obj = ObjCClass_GetClass(self); } else { PyErr_SetString(PyExc_TypeError, "Need objective-C object or class as self"); goto error_cleanup; } } else { if (ObjCObject_Check(self)) { self_obj = ObjCObject_GetObject(self); } else { PyErr_SetString(PyExc_TypeError, "Need objective-C object as self"); goto error_cleanup; } } super.receiver = self_obj; super.class = meth->sel_class; arglist[0] = &ffi_type_pointer; values[0] = &super; arglist[1] = &ffi_type_pointer; values[1] = meth->sel_selector; py_arg = 0; for (i = 2; i < objc_argcount; i++) { const char* error; PyObject *argument; const char *argtype = [methinfo getArgumentTypeAtIndex:i]; if (argtype[0] == _C_OUT && argtype[1] == _C_PTR) { /* Just allocate room in argbuf and set that*/ void* arg; byref[i] = argbuf_cur; arg = argbuf + argbuf_cur; arglist[i] = &ffi_type_pointer; values[i] = arg; argbuf_cur += objc_sizeof_type(argtype+2); } else { /* Encode argument, maybe after allocating space */ if (argtype[0] == _C_OUT) argtype ++; argument = PyTuple_GET_ITEM (args, py_arg); switch (*argtype) { case _C_STRUCT_B: case _C_ARY_B: case _C_UNION_B: /* Allocate space and encode */ { void* arg = argbuf + argbuf_cur; argbuf_cur += objc_sizeof_type(argtype); byref[i] = argbuf_cur; error = depythonify_c_value ( argtype, argument, arg); arglist[i] = signature_to_ffi_type( argtype); values[i] = arg; } break; case _C_PTR: /* Allocate space and encode */ { void* arg = argbuf + argbuf_cur; argbuf_cur += objc_sizeof_type(argtype+2); byref[i] = argbuf_cur; error = depythonify_c_value ( argtype+2, argument, arg); arglist[i] = &ffi_type_pointer; values[i] = arg; } break; case _C_INOUT: case _C_IN: case _C_CONST: if (argbuf[1] == _C_PTR) { /* Allocate space and encode */ void* arg = argbuf + argbuf_cur; argbuf_cur += objc_sizeof_type(argtype+2); byref[i] = argbuf_cur; error = depythonify_c_value ( argtype+2, argument, arg); arglist[i] = &ffi_type_pointer; values[i] = arg; } else { /* just encode */ void* arg = argbuf + argbuf_cur; argbuf_cur += objc_sizeof_type(argtype+1); error = depythonify_c_value ( argtype+1, argument, arg); arglist[i] = signature_to_ffi_type( argtype+2); values[i] = arg; } break; default: { void* arg = argbuf + argbuf_cur; argbuf_cur += objc_sizeof_type(argtype); error = depythonify_c_value ( argtype, argument, arg); arglist[i] = signature_to_ffi_type(argtype); values[i] = arg; } } if (error) { const char* typeend = objc_skip_typespec(argtype); ObjCErr_Set(PyExc_TypeError, "expected %s for argument %d: its " "typespec is '%.*s'", error, py_arg+1, typeend-argtype, argtype); goto error_cleanup; } py_arg++; } } PyErr_Clear(); r = ffi_prep_cif(&cif, FFI_DEFAULT_ABI, objc_argcount, &ffi_type_pointer, arglist); if (r != FFI_OK) { ObjCErr_Set(PyExc_RuntimeError, "Cannot setup FFI CIF [%d]", r); goto error_cleanup; } /* XXX This only works for short return values! */ NS_DURING ffi_call(&cif, FFI_FN(objc_msgSendSuper), &msgResult, values); NS_HANDLER ObjCErr_FromObjC(localException); NS_ENDHANDLER if (PyErr_Occurred()) goto error_cleanup; rettype = [methinfo methodReturnType]; if ( (*rettype != _C_VOID) && ([methinfo isOneway] == NO) ) { objc_result = pythonify_c_value ( [methinfo methodReturnType], &msgResult); } else { Py_INCREF(Py_None); objc_result = Py_None; } if (byref_out_count == 0) { result = objc_result; } else { result = PyTuple_New(byref_out_count+1); if (result == 0) goto error_cleanup; if (PyTuple_SetItem(result, 0, objc_result) < 0) { goto error_cleanup; } objc_result = NULL; py_arg = 1; for (i = 2; i < objc_argcount; i++) { const char *argtype = [methinfo getArgumentTypeAtIndex:i]; void* arg; PyObject* v; switch (*argtype) { case _C_PTR: arg = argbuf + byref[i]; v = pythonify_c_value(argtype+1, arg); if (!v) goto error_cleanup; if (PyTuple_SetItem(result, py_arg++, v) < 0) { Py_DECREF(v); goto error_cleanup; } break; case _C_INOUT: case _C_OUT: if (argtype[1] == _C_PTR) { arg = argbuf + byref[i]; v = pythonify_c_value(argtype+2, arg); if (!v) goto error_cleanup; if (PyTuple_SetItem(result, py_arg++, v) < 0) { Py_DECREF(v); goto error_cleanup; } } break; } } } PyMem_Free(argbuf); argbuf = NULL; PyMem_Free(byref); byref = NULL; [methinfo release]; methinfo = nil; return result; error_cleanup: if (objc_result) { Py_DECREF(objc_result); objc_result = NULL; } if (result) { Py_DECREF(result); result = NULL; } if (argbuf) { PyMem_Free(argbuf); argbuf = NULL; } if (byref) { PyMem_Free(byref); byref = NULL; } if (methinfo) { [methinfo release]; methinfo = nil; } return NULL; } #endif /* OC_WITH_LIBFFI */ Index: alloc_hack.m =================================================================== RCS file: /cvsroot/pyobjc/pyobjc/Modules/objc/alloc_hack.m,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** alloc_hack.m 20 Dec 2002 20:44:42 -0000 1.2 --- alloc_hack.m 22 Dec 2002 17:47:39 -0000 1.3 *************** *** 49,52 **** --- 49,53 ---- } + /* XXX: Shouldn't we use method->sel_class here? */ super.receiver = (id)ObjCClass_GetClass(self); super.class = (Class)(super.receiver)->isa; Index: objc_support.m =================================================================== RCS file: /cvsroot/pyobjc/pyobjc/Modules/objc/objc_support.m,v retrieving revision 1.9 retrieving revision 1.10 diff -C2 -d -r1.9 -r1.10 *** objc_support.m 20 Dec 2002 20:58:27 -0000 1.9 --- objc_support.m 22 Dec 2002 17:47:39 -0000 1.10 *************** *** 1310,1314 **** /* just encode */ error = depythonify_c_value ( ! argtype+2, argument, argbuffer); --- 1310,1314 ---- /* just encode */ error = depythonify_c_value ( ! argtype+1, argument, argbuffer); Index: pyobjc.h =================================================================== RCS file: /cvsroot/pyobjc/pyobjc/Modules/objc/pyobjc.h,v retrieving revision 1.11 retrieving revision 1.12 diff -C2 -d -r1.11 -r1.12 *** pyobjc.h 20 Dec 2002 21:13:28 -0000 1.11 --- pyobjc.h 22 Dec 2002 17:47:39 -0000 1.12 *************** *** 199,201 **** --- 199,208 ---- int ObjC_InstallAllocHack(void); + #ifdef OC_WITH_LIBFFI + + IMP ObjC_MakeIMPForSignature(char* signature); + PyObject *ObjC_FFICaller(PyObject *aMeth, PyObject* self, PyObject *args); + + #endif /* OC_WITH_LIBFFI */ + #endif /* META_H */ Index: register.m =================================================================== RCS file: /cvsroot/pyobjc/pyobjc/Modules/objc/register.m,v retrieving revision 1.4 retrieving revision 1.5 diff -C2 -d -r1.4 -r1.5 *** register.m 20 Dec 2002 20:56:29 -0000 1.4 --- register.m 22 Dec 2002 17:47:39 -0000 1.5 *************** *** 2,5 **** --- 2,20 ---- * WARNING: This is a generated file, do not change */ + #ifdef OC_WITH_LIBFFI + + #include <Python.h> + #include <objc/objc.h> + #include <objc/objc-runtime.h> + #include <Foundation/NSException.h> + #define PYOBJC_METHOD_STUB_IMPL + #include "pyobjc-api.h" + + int ObjC_RegisterStdStubs(struct pyobjc_api* api) + { + return 0; + } + + #else #include <Python.h> *************** *** 11,14 **** --- 26,31 ---- static struct pyobjc_api* ObjC_API; typedef int (*superfunc)(int); + + /* signature: c@:@@ */ static char *************** *** 56725,56726 **** --- 56742,56745 ---- return 0; } + + #endif Index: super-call.m =================================================================== RCS file: /cvsroot/pyobjc/pyobjc/Modules/objc/super-call.m,v retrieving revision 1.4 retrieving revision 1.5 diff -C2 -d -r1.4 -r1.5 *** super-call.m 30 Oct 2002 12:02:35 -0000 1.4 --- super-call.m 22 Dec 2002 17:47:44 -0000 1.5 *************** *** 131,134 **** --- 131,136 ---- [sig release]; } + + int ObjC_RegisterSignatureMapping( *************** *** 267,270 **** --- 269,299 ---- } + #ifdef OC_WITH_LIBFFI + static struct registry* create_ffi(char* signature) + { + IMP ffiImp = NULL; + int r; + + PyErr_Clear(); + + ffiImp = ObjC_MakeIMPForSignature(signature); + if (ffiImp == NULL) + goto error; + + r = ObjC_RegisterSignatureMapping(signature, ObjC_FFICaller, ffiImp); + if (r == -1) { + PyErr_Print(); + goto error; + } + + return find_signature(signature); + error: + /* TODO: Clean up ffiImp */ + return NULL; + } + + #endif /* OC_WITH_LIBFFI */ + + IMP ObjC_FindIMPForSignature(char* signature) { *************** *** 272,277 **** r = find_signature(signature); ! return r?r->call_to_python:NULL; } --- 301,318 ---- r = find_signature(signature); + if (r) { + return r->call_to_python; + } ! #ifdef OC_WITH_LIBFFI ! ! r = create_ffi(signature); ! if (r) { ! return r->call_to_python; ! } ! ! #endif /* OC_WITH_LIBFFI */ ! ! return NULL; } *************** *** 306,309 **** --- 347,360 ---- return generic->call_to_python; } + + #ifdef OC_WITH_LIBFFI + + generic = create_ffi(ObjCSelector_Signature(objc_sel)); + if (generic) { + return generic->call_to_python; + } + + #endif /* OC_WITH_LIBFFI */ + return NULL; } *************** *** 335,338 **** --- 386,399 ---- return generic->call_to_super; } + + #ifdef OC_WITH_LIBFFI + + generic = create_ffi(m->method_types); + if (generic) { + return generic->call_to_super; + } + + #endif /* OC_WITH_LIBFFI */ + return NULL; } |
| Thread | Author | Date |
|---|---|---|
| [Pyobjc-checkins] CVS: pyobjc/Modules/objc libffi_support.m,NONE,1.1 alloc_hack.m,1.2,1.3 objc_support.m,1.9,1.10 pyobjc.h,1.11,1.12 register.m,1.4,1.5 super-call.m,1.4,1.5 | Ronald Oussoren <ronaldoussoren@us...> |