/** cifframe.m - Wrapper/Objective-C interface for ffi function interface Copyright (C) 1999, Free Software Foundation, Inc. Written by: Adam Fedor Date: Dec 1999, rewritten Apr 2002 This file is part of the GNUstep Base Library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111 USA. */ #import "common.h" #ifdef HAVE_MALLOC_H #include #endif #ifdef HAVE_ALLOCA_H #include #endif #include "cifframe.h" #import "Foundation/NSException.h" #import "Foundation/NSData.h" #import "GSInvocation.h" #if defined(ALPHA) || (defined(MIPS) && (_MIPS_SIM == _ABIN32)) typedef long long smallret_t; #else typedef int smallret_t; #endif /* ffi defines types in a very odd way that doesn't map to the normal objective-c type (see ffi.h). Here we make up for that */ #if GS_SIZEOF_SHORT == 2 #define gsffi_type_ushort ffi_type_uint16 #define gsffi_type_sshort ffi_type_sint16 #elif GS_SIZEOF_SHORT == 4 #define gsffi_type_ushort ffi_type_uint32 #define gsffi_type_sshort ffi_type_sint32 #else #error FFI Sizeof SHORT case not handled #endif #if GS_SIZEOF_INT == 2 #define gsffi_type_uint ffi_type_uint16 #define gsffi_type_sint ffi_type_sint16 #elif GS_SIZEOF_INT == 4 #define gsffi_type_uint ffi_type_uint32 #define gsffi_type_sint ffi_type_sint32 #elif GS_SIZEOF_INT == 8 #define gsffi_type_uint ffi_type_uint64 #define gsffi_type_sint ffi_type_sint64 #else #error FFI Sizeof INT case not handled #endif #if GS_SIZEOF_LONG == 2 #define gsffi_type_ulong ffi_type_uint16 #define gsffi_type_slong ffi_type_sint16 #elif GS_SIZEOF_LONG == 4 #define gsffi_type_ulong ffi_type_uint32 #define gsffi_type_slong ffi_type_sint32 #elif GS_SIZEOF_LONG == 8 #define gsffi_type_ulong ffi_type_uint64 #define gsffi_type_slong ffi_type_sint64 #else #error FFI Sizeof LONG case not handled #endif #ifdef _C_LNG_LNG #if GS_SIZEOF_LONG_LONG == 8 #define gsffi_type_ulong_long ffi_type_uint64 #define gsffi_type_slong_long ffi_type_sint64 #else #error FFI Sizeof LONG LONG case not handled #endif #endif ffi_type *cifframe_type(const char *typePtr, const char **advance); /* Best guess at the space needed for a structure, since we don't know for sure until it's calculated in ffi_prep_cif, which is too late */ int cifframe_guess_struct_size(ffi_type *stype) { int i, size; unsigned align = __alignof(double); if (stype->elements == NULL) return stype->size; size = 0; i = 0; while (stype->elements[i]) { if (stype->elements[i]->elements) size += cifframe_guess_struct_size(stype->elements[i]); else size += stype->elements[i]->size; if (size % align != 0) { size += (align - size % align); } i++; } return size; } NSMutableData * cifframe_from_signature (NSMethodSignature *info) { unsigned size = sizeof(cifframe_t); unsigned align = __alignof(double); unsigned type_offset = 0; unsigned offset = 0; NSMutableData *result; void *buf; int i; int numargs = [info numberOfArguments]; ffi_type *rtype; ffi_type *arg_types[numargs]; cifframe_t *cframe; /* FIXME: in cifframe_type, return values/arguments that are structures have custom ffi_types with are allocated separately. We should allocate them in our cifframe so we don't leak memory. Or maybe we could cache structure types? */ rtype = cifframe_type([info methodReturnType], NULL); for (i = 0; i < numargs; i++) { arg_types[i] = cifframe_type([info getArgumentTypeAtIndex: i], NULL); } if (numargs > 0) { if (size % align != 0) { size += align - (size % align); } type_offset = size; /* Make room to copy the arg_types */ size += sizeof(ffi_type *) * numargs; if (size % align != 0) { size += align - (size % align); } offset = size; size += numargs * sizeof(void*); if (size % align != 0) { size += (align - (size % align)); } for (i = 0; i < numargs; i++) { if (arg_types[i]->elements) size += cifframe_guess_struct_size(arg_types[i]); else size += arg_types[i]->size; if (size % align != 0) { size += (align - size % align); } } } result = [NSMutableData dataWithCapacity: size]; [result setLength: size]; cframe = buf = [result mutableBytes]; if (cframe) { cframe->nargs = numargs; cframe->arg_types = buf + type_offset; memcpy(cframe->arg_types, arg_types, sizeof(ffi_type *) * numargs); cframe->values = buf + offset; if (ffi_prep_cif (&cframe->cif, FFI_DEFAULT_ABI, cframe->nargs, rtype, cframe->arg_types) != FFI_OK) { cframe = NULL; result = NULL; } else { /* Set values locations. This must be done after ffi_prep_cif so that any structure sizes get calculated first. */ offset += numargs * sizeof(void*); if (offset % align != 0) { offset += align - (offset % align); } for (i = 0; i < cframe->nargs; i++) { cframe->values[i] = buf + offset; offset += arg_types[i]->size; if (offset % align != 0) { offset += (align - offset % align); } } } } return result; } void cifframe_set_arg(cifframe_t *cframe, int index, void *buffer, int size) { if (index < 0 || index >= cframe->nargs) return; memcpy(cframe->values[index], buffer, size); } void cifframe_get_arg(cifframe_t *cframe, int index, void *buffer, int size) { if (index < 0 || index >= cframe->nargs) return; memcpy(buffer, cframe->values[index], size); } void * cifframe_arg_addr(cifframe_t *cframe, int index) { if (index < 0 || index >= cframe->nargs) return NULL; return cframe->values[index]; } /* * Get the ffi_type for this type */ ffi_type * cifframe_type(const char *typePtr, const char **advance) { const char *type; ffi_type *ftype; typePtr = objc_skip_type_qualifiers (typePtr); type = typePtr; /* * Scan for size and alignment information. */ switch (*typePtr++) { case _C_ID: ftype = &ffi_type_pointer; break; case _C_CLASS: ftype = &ffi_type_pointer; break; case _C_SEL: ftype = &ffi_type_pointer; break; case _C_CHR: ftype = &ffi_type_schar; break; case _C_UCHR: ftype = &ffi_type_uchar; break; case _C_SHT: ftype = &gsffi_type_sshort; break; case _C_USHT: ftype = &gsffi_type_ushort; break; case _C_INT: ftype = &gsffi_type_sint; break; case _C_UINT: ftype = &gsffi_type_uint; break; case _C_LNG: ftype = &gsffi_type_slong; break; case _C_ULNG: ftype = &gsffi_type_ulong; break; #ifdef _C_LNG_LNG case _C_LNG_LNG: ftype = &gsffi_type_slong_long; break; case _C_ULNG_LNG: ftype = &gsffi_type_ulong_long; break; #endif case _C_FLT: ftype = &ffi_type_float; break; case _C_DBL: ftype = &ffi_type_double; break; case _C_PTR: ftype = &ffi_type_pointer; if (*typePtr == '?') { typePtr++; } else { const char *adv; cifframe_type(typePtr, &adv); typePtr = adv; } break; case _C_ATOM: case _C_CHARPTR: ftype = &ffi_type_pointer; break; case _C_ARY_B: { const char *adv; ftype = &ffi_type_pointer; while (isdigit(*typePtr)) { typePtr++; } cifframe_type(typePtr, &adv); typePtr = adv; typePtr++; /* Skip end-of-array */ } break; case _C_STRUCT_B: { int types, maxtypes, size; ffi_type *local; const char *adv; unsigned align = __alignof(double); /* Standard structures can be handled using cached type information. Since the switch statement has already skipped the _C_STRUCT_B character, we must use typePtr-1 below to successfully match the type encoding with one of the standard type encodings. The same holds for skipping past the whole structure type's encoding with objc_skip_typespec. */ if (GSSelectorTypesMatch(typePtr - 1, @encode(NSRange))) { static ffi_type *elems[3]; static ffi_type stype = { 0 }; if (stype.type == 0) { const char *t = @encode(NSUInteger); if (*t == _C_ULNG) { elems[0] = &gsffi_type_ulong; } #ifdef _C_LNG_LNG else if (*t == _C_ULNG_LNG) { elems[0] = &gsffi_type_ulong_long; } #endif else { elems[0] = &gsffi_type_uint; } elems[1] = elems[0]; elems[2] = 0; stype.elements = elems; stype.type = FFI_TYPE_STRUCT; } ftype = &stype; typePtr = objc_skip_typespec (typePtr - 1); break; } else if (GSSelectorTypesMatch(typePtr - 1, @encode(NSSize))) { static ffi_type *elems[3]; static ffi_type stype = { 0 }; if (stype.type == 0) { if (*@encode(CGFloat) == _C_DBL) { elems[0] = &ffi_type_double; } else { elems[0] = &ffi_type_float; } elems[1] = elems[0]; elems[2] = 0; stype.elements = elems; stype.type = FFI_TYPE_STRUCT; } ftype = &stype; typePtr = objc_skip_typespec (typePtr - 1); break; } else if (GSSelectorTypesMatch(typePtr - 1, @encode(NSRect))) { static ffi_type *elems[3]; static ffi_type stype = { 0 }; if (stype.type == 0) { /* An NSRect is an NSPoint and an NSSize, but those * two structures are actually identical. */ elems[0] = cifframe_type(@encode(NSSize), NULL); elems[1] = elems[0]; elems[2] = 0; stype.elements = elems; stype.type = FFI_TYPE_STRUCT; } ftype = &stype; typePtr = objc_skip_typespec (typePtr - 1); break; } /* * Skip "=" stuff. */ while (*typePtr != _C_STRUCT_E) { if (*typePtr++ == '=') { break; } } types = 0; maxtypes = 4; size = sizeof(ffi_type); if (size % align != 0) { size += (align - (size % align)); } ftype = malloc(size + (maxtypes+1)*sizeof(ffi_type)); ftype->size = 0; ftype->alignment = 0; ftype->type = FFI_TYPE_STRUCT; ftype->elements = (void*)ftype + size; /* * Continue accumulating structure size. */ while (*typePtr != _C_STRUCT_E) { local = cifframe_type(typePtr, &adv); typePtr = adv; NSCAssert(typePtr, @"End of signature while parsing"); ftype->elements[types++] = local; if (types >= maxtypes) { maxtypes *=2; ftype = realloc(ftype, size + (maxtypes+1)*sizeof(ffi_type)); ftype->elements = (void*)ftype + size; } } ftype->elements[types] = NULL; typePtr++; /* Skip end-of-struct */ } break; case _C_UNION_B: { const char *adv; int max_align = 0; /* * Skip "=" stuff. */ while (*typePtr != _C_UNION_E) { if (*typePtr++ == '=') { break; } } ftype = NULL; while (*typePtr != _C_UNION_E) { ffi_type *local; int align = objc_alignof_type(typePtr); local = cifframe_type(typePtr, &adv); typePtr = adv; NSCAssert(typePtr, @"End of signature while parsing"); if (align > max_align) { if (ftype && ftype->type == FFI_TYPE_STRUCT) free(ftype); ftype = local; max_align = align; } } typePtr++; /* Skip end-of-union */ } break; case _C_VOID: ftype = &ffi_type_void; break; default: ftype = &ffi_type_void; NSCAssert(0, @"Unknown type in sig"); } /* Skip past any offset information, if there is any */ if (*type != _C_PTR || *type == '?') { if (*typePtr == '+') typePtr++; if (*typePtr == '-') typePtr++; while (isdigit(*typePtr)) typePtr++; } if (advance) *advance = typePtr; return ftype; } /*-------------------------------------------------------------------------*/ /* Functions for handling sending and receiving messages accross a connection */ /* Some return types actually get coded differently. We need to convert back to the expected return type */ BOOL cifframe_decode_arg (const char *type, void* buffer) { type = objc_skip_type_qualifiers (type); switch (*type) { case _C_CHR: case _C_UCHR: { *(unsigned char*)buffer = (unsigned char)(*((smallret_t *)buffer)); break; } case _C_SHT: case _C_USHT: { *(unsigned short*)buffer = (unsigned short)(*((smallret_t *)buffer)); break; } case _C_INT: case _C_UINT: { *(unsigned int*)buffer = (unsigned int)(*((smallret_t *)buffer)); break; } default: return NO; } return YES; } BOOL cifframe_encode_arg (const char *type, void* buffer) { type = objc_skip_type_qualifiers (type); switch (*type) { case _C_CHR: case _C_UCHR: { *(smallret_t *)buffer = (smallret_t)(*((unsigned char *)buffer)); break; } case _C_SHT: case _C_USHT: { *(smallret_t *)buffer = (smallret_t)(*((unsigned short *)buffer)); break; } case _C_INT: case _C_UINT: { *(smallret_t *)buffer = (smallret_t)(*((unsigned int *)buffer)); break; } default: return NO; } return YES; }