1/*
2 * Copyright (c) 2006-2008, The RubyCocoa Project.
3 * Copyright (c) 2001-2006, FUJIMOTO Hisakuni.
4 * All Rights Reserved.
5 *
6 * RubyCocoa is free software, covered under either the Ruby's license or the
7 * LGPL. See the COPYRIGHT file for more information.
8 */
9
10#import <Foundation/Foundation.h>
11#import "libffi.h"
12#import "ocdata_conv.h"
13#import "BridgeSupport.h"
14#import "ocexception.h"
15#import "cls_objcid.h"
16#import "cls_objcptr.h"
17#import "internal_macros.h"
18#import <st.h>
19#include <sys/mman.h>   // for mmap()
20
21#define FFI_LOG(fmt, args...) DLOG("LIBFFI", fmt, ##args)
22
23ffi_type *
24bs_boxed_ffi_type(struct bsBoxed *bs_boxed)
25{
26  if (bs_boxed->ffi_type == NULL) {
27    if (bs_boxed->type == bsBoxedStructType) {
28      unsigned i;
29
30      bs_boxed->ffi_type = (ffi_type *)malloc(sizeof(ffi_type));
31      ASSERT_ALLOC(bs_boxed->ffi_type);
32
33      bs_boxed->ffi_type->size = 0; // IMPORTANT: we need to leave this to 0 and not set the real size
34      bs_boxed->ffi_type->alignment = 0;
35      bs_boxed->ffi_type->type = FFI_TYPE_STRUCT;
36      bs_boxed->ffi_type->elements = malloc((bs_boxed->opt.s.field_count + 1) * sizeof(ffi_type *));
37      ASSERT_ALLOC(bs_boxed->ffi_type->elements);
38      for (i = 0; i < bs_boxed->opt.s.field_count; i++) {
39        char *octypestr;
40
41        octypestr = bs_boxed->opt.s.fields[i].encoding;
42        bs_boxed->ffi_type->elements[i] = ffi_type_for_octype(octypestr);
43      }
44      bs_boxed->ffi_type->elements[bs_boxed->opt.s.field_count] = NULL;
45    }
46    else if (bs_boxed->type == bsBoxedOpaqueType) {
47      // FIXME we assume that boxed types are pointers, but maybe we should analyze the encoding.
48      bs_boxed->ffi_type = &ffi_type_pointer;
49    }
50  }
51
52  return bs_boxed->ffi_type;
53}
54
55static struct st_table *ary_ffi_types = NULL;
56
57static ffi_type *
58fake_ary_ffi_type (unsigned bytes, unsigned align)
59{
60  ffi_type *type;
61  unsigned i;
62
63  assert(bytes > 0);
64
65  if (ary_ffi_types == NULL)
66    ary_ffi_types = st_init_numtable();
67
68  if (st_lookup(ary_ffi_types, (st_data_t)bytes, (st_data_t *)&type))
69    return type;
70
71  type = (ffi_type *)malloc(sizeof(ffi_type));
72  ASSERT_ALLOC(type);
73
74  type->size = bytes;
75  type->alignment = align;
76  type->type = FFI_TYPE_STRUCT;
77  type->elements = malloc(bytes * sizeof(ffi_type *));
78  ASSERT_ALLOC(type->elements);
79  for (i = 0; i < bytes; i++)
80    type->elements[i] = &ffi_type_uchar;
81
82  st_insert(ary_ffi_types, (st_data_t)bytes, (st_data_t)type);
83
84  return type;
85}
86
87ffi_type *
88ffi_type_for_octype (const char *octypestr)
89{
90  octypestr = encoding_skip_qualifiers(octypestr);
91
92  switch (*octypestr) {
93    case _C_ID:
94    case _C_CLASS:
95    case _C_SEL:
96    case _C_CHARPTR:
97    case _C_PTR:
98      return &ffi_type_pointer;
99
100    case _C_BOOL:
101    case _C_UCHR:
102      return &ffi_type_uchar;
103
104    case _C_CHR:
105      return &ffi_type_schar;
106
107    case _C_SHT:
108      return &ffi_type_sshort;
109
110    case _C_USHT:
111      return &ffi_type_ushort;
112
113    case _C_INT:
114      return &ffi_type_sint;
115
116    case _C_UINT:
117      return &ffi_type_uint;
118
119    case _C_LNG:
120      return sizeof(int) == sizeof(long) ? &ffi_type_sint : &ffi_type_slong;
121
122#if defined(_C_LNG_LNG)
123    case _C_LNG_LNG:
124      return &ffi_type_sint64;
125#endif
126
127    case _C_ULNG:
128      return sizeof(unsigned int) == sizeof(unsigned long) ? &ffi_type_uint : &ffi_type_ulong;
129
130#if defined(_C_ULNG_LNG)
131    case _C_ULNG_LNG:
132      return &ffi_type_uint64;
133#endif
134
135    case _C_FLT:
136      return &ffi_type_float;
137
138    case _C_DBL:
139      return &ffi_type_double;
140
141    case _C_VOID:
142      return &ffi_type_void;
143
144    case _C_BFLD:
145      {
146        unsigned int size;
147
148        size = ocdata_size(octypestr);
149        if (size > 0) {
150          if (size == 1)
151            return &ffi_type_uchar;
152          else if (size == 2)
153            return &ffi_type_ushort;
154          else if (size <= 4)
155            return &ffi_type_uint;
156          else
157            return fake_ary_ffi_type(size, 0);
158        }
159      }
160      break;
161
162    case _C_ARY_B:
163      {
164#if __LP64__
165        unsigned long size, align;
166#else
167        unsigned int size, align;
168#endif
169
170        @try {
171          NSGetSizeAndAlignment(octypestr, &size, &align);
172        }
173        @catch (id exception) {
174          rb_raise(rb_eRuntimeError, "Cannot compute size of type `%s' : %s",
175            octypestr, [[exception description] UTF8String]);
176        }
177
178        if (size > 0)
179          return fake_ary_ffi_type(size, align);
180      }
181      break;
182
183    default:
184      {
185        struct bsBoxed *bs_boxed;
186
187        bs_boxed = find_bs_boxed_by_encoding(octypestr);
188        if (bs_boxed != NULL)
189          return bs_boxed_ffi_type(bs_boxed);
190      }
191      break;
192  }
193
194  NSLog (@"XXX returning ffi type void for unrecognized encoding '%s'", octypestr);
195
196  return &ffi_type_void;
197}
198
199static inline BOOL
200__is_in(int elem, int *array, unsigned count)
201{
202  unsigned i;
203  for (i = 0; i < count; i++) {
204    if (array[i] == elem)
205      return YES;
206  }
207  return NO;
208}
209
210VALUE
211rb_ffi_dispatch (
212  struct bsCallEntry *call_entry,
213  char **arg_octypes,
214  int expected_argc,
215  int given_argc,
216  int argc_delta,
217  VALUE *argv,
218  ffi_type **arg_types,
219  void **arg_values,
220  char *ret_octype,
221  void *func_sym,
222  void (*retain_if_necessary)(VALUE arg, BOOL retval, void *ctx),
223  void *retain_if_necessary_ctx,
224  VALUE *result)
225{
226  int         length_args[MAX_ARGS];
227  unsigned    length_args_count;
228  int         pointers_args[MAX_ARGS];
229  unsigned    pointers_args_count;
230  unsigned    skipped;
231  int         i;
232  ffi_type *  ret_type;
233  void *      retval = NULL;
234  ffi_cif     cif;
235  VALUE       exception;
236
237#define ARG_OCTYPESTR(i) \
238  (arg_octypes != NULL ? arg_octypes[i] : call_entry->argv[i].octypestr)
239
240#define IS_POINTER_ARG(idx) \
241  (__is_in(idx, pointers_args, pointers_args_count))
242
243#define IS_LENGTH_ARG(idx) \
244  (__is_in(idx, length_args, length_args_count))
245
246  FFI_LOG("argc expected %d given %d delta %d", expected_argc, given_argc,
247    argc_delta);
248
249  // Check arguments count.
250  length_args_count = pointers_args_count = 0;
251  if (call_entry != NULL) {
252    for (i = 0; i < call_entry->argc; i++) {
253      struct bsArg *arg;
254
255      arg = &call_entry->argv[i];
256      // The given argument is a C array with a length determined by the value
257      // of another argument, like:
258      //   [NSArray +arrayWithObjects:length:]
259      // If 'in' or 'inout, the 'length' argument is not necessary (but the
260      // 'array' is). If 'out', the 'array' argument is not necessary (but the
261      // 'length' is).
262      if (arg->c_ary_type == bsCArrayArgDelimitedByArg) {
263        unsigned j;
264        BOOL already;
265
266        // Some methods may accept multiple 'array' 'in' arguments that refer
267        // to the same 'length' argument, like:
268        //   [NSDictionary +dictionaryWithObjects:forKeys:count:]
269        for (j = 0, already = NO; j < length_args_count; j++) {
270          if (length_args[j] == arg->c_ary_type_value) {
271            already = YES;
272            break;
273          }
274        }
275        if (already)
276          continue;
277
278        length_args[length_args_count++] = arg->c_ary_type_value;
279      }
280    }
281    FFI_LOG("detected %d array length argument(s)", length_args_count);
282  }
283
284  if (expected_argc - length_args_count != given_argc) {
285    for (i = 0; i < expected_argc; i++) {
286      char *type = ARG_OCTYPESTR(i);
287      type = (char *)encoding_skip_qualifiers(type);
288      if (given_argc + pointers_args_count < expected_argc
289          && (i >= given_argc || !NIL_P(argv[i]))
290          && ((*type == _C_PTR && find_bs_cf_type_by_encoding(type) == NULL)
291               || *type == _C_ARY_B)) {
292        struct bsArg *bs_arg;
293
294        bs_arg = find_bs_arg_by_index(call_entry, i, expected_argc);
295        if (bs_arg == NULL || bs_arg->type_modifier == bsTypeModifierOut)
296          pointers_args[pointers_args_count++] = i;
297      }
298    }
299    FFI_LOG("detected %d omitted pointer(s)", pointers_args_count);
300    if (pointers_args_count + given_argc != expected_argc)
301      return rb_err_new(rb_eArgError,
302        "wrong number of argument(s) (expected %d, got %d)", expected_argc,
303        given_argc);
304  }
305
306  for (i = skipped = 0; i < expected_argc; i++) {
307    const char *octype_str;
308
309    octype_str = ARG_OCTYPESTR(i);
310    // C-array-length-like argument, which should be already defined
311    // at the same time than the C-array-like argument, unless it's
312    // returned by reference or specifically provided.
313    if (IS_LENGTH_ARG(i) && *octype_str != _C_PTR) {
314      if (given_argc + skipped < expected_argc) {
315        skipped++;
316      }
317      else {
318        VALUE arg;
319        int *value;
320        int *prev_len;
321
322        arg = argv[i - skipped];
323        Check_Type(arg, T_FIXNUM);
324
325        value = OCDATA_ALLOCA(octype_str);
326        if (!rbobj_to_ocdata(arg, octype_str, value, NO))
327          return rb_err_new(ocdataconv_err_class(), "Cannot convert the argument #%d as '%s' to Objective-C", i, octype_str);
328
329        prev_len = arg_values[i + argc_delta];
330        if (prev_len != NULL && (*prev_len < *value || *value < 0))
331          return rb_err_new(rb_eArgError, "Incorrect array length of argument #%d (expected a non negative value greater or equal to %d, got %d)", i, *prev_len, *value);
332
333        arg_types[i + argc_delta] = ffi_type_for_octype(octype_str);
334        arg_values[i + argc_delta] = value;
335      }
336    }
337    // Omitted pointer.
338    else if (IS_POINTER_ARG(i)) {
339      void *value;
340      arg_types[i + argc_delta] = &ffi_type_pointer;
341      if (*octype_str == _C_PTR) {
342        // Regular pointer.
343        value = alloca(sizeof(void *));
344        *(void **)value = OCDATA_ALLOCA(octype_str+1);
345      }
346      else {
347        // C_ARY.
348        value = alloca(sizeof(void *));
349        *(void **)value = OCDATA_ALLOCA(octype_str);
350      }
351      void **p = *(void ***)value;
352      *p = NULL;
353      arg_values[i + argc_delta] = value;
354      FFI_LOG("omitted_pointer[%d] (%p) : %s", i, arg_values[i + argc_delta],
355        octype_str);
356      skipped++;
357    }
358    // Regular argument.
359    else {
360      volatile VALUE arg;
361      void *value;
362      BOOL is_c_array;
363      int len;
364      struct bsArg *bs_arg;
365
366      arg = argv[i - skipped];
367      bs_arg = find_bs_arg_by_index(call_entry, i, expected_argc);
368
369      if (bs_arg != NULL) {
370        if (!bs_arg->null_accepted && NIL_P(arg))
371          return rb_err_new(rb_eArgError, "Argument #%d cannot be nil", i);
372        if (bs_arg->octypestr != NULL)
373          octype_str = bs_arg->octypestr;
374        is_c_array = bs_arg->c_ary_type != bsCArrayArgUndefined;
375      }
376      else {
377        is_c_array = NO;
378      }
379
380      // C-array-like argument.
381      if (is_c_array) {
382        const char * ptype;
383
384        ptype = octype_str;
385        ptype = encoding_skip_qualifiers(ptype);
386        if (*ptype != _C_PTR && *ptype != _C_ARY_B && *ptype != _C_CHARPTR)
387          return rb_err_new(rb_eRuntimeError, "Internal error: argument #%d is not a defined as a pointer in the runtime or it is described as such in the metadata", i);
388        ptype++;
389
390        if (NIL_P(arg))
391          len = 0;
392        else if (TYPE(arg) == T_STRING)
393          len = RSTRING(arg)->len;
394        else if (TYPE(arg) == T_ARRAY)
395          len = RARRAY(arg)->len; // XXX should be RARRAY(arg)->len * ocdata_sizeof(...)
396        else if (rb_obj_is_kind_of(arg, objcptr_s_class()))
397          len = objcptr_allocated_size(arg);
398        else {
399          return rb_err_new(rb_eArgError, "Expected either String/Array/ObjcPtr for argument #%d (but got %s).", i, rb_obj_classname(arg));
400        }
401
402        if (bs_arg->c_ary_type == bsCArrayArgFixedLength) {
403          int expected_len = bs_arg->c_ary_type_value * ocdata_size(ptype);
404          if (expected_len != len)
405            return rb_err_new(rb_eArgError, "Argument #%d has an invalid length (expected %d, got %d)", i, expected_len, len);
406        }
407        else if (bs_arg->c_ary_type == bsCArrayArgDelimitedByArg) {
408          int * prev_len;
409
410          prev_len = arg_values[bs_arg->c_ary_type_value + argc_delta];
411          if (prev_len != NULL && (*prev_len > len || *prev_len < 0))
412            return rb_err_new(rb_eArgError, "Incorrect array length of argument #%d (expected a non negative value greater or equal to %d, got %d)", i, len, *prev_len);
413          FFI_LOG("arg[%d] (%p) : %s (defined as a C array delimited by arg #%d in the metadata)", i, arg, octype_str, bs_arg->c_ary_type_value);
414        }
415        value = OCDATA_ALLOCA(octype_str);
416        if (len > 0)
417          *(void **) value = alloca(ocdata_size(ptype) * len);
418      }
419      // Regular argument.
420      else {
421        FFI_LOG("arg[%d] (%p) : %s", i, arg, octype_str);
422        len = 0;
423        value = OCDATA_ALLOCA(octype_str);
424      }
425
426      if (!rbobj_to_ocdata(arg, octype_str, value, NO))
427        return rb_err_new(ocdataconv_err_class(), "Cannot convert the argument #%d as '%s' to Objective-C", i, octype_str);
428
429      // Register the selector value as an informal protocol, based on the metadata annotation.
430      if (*octype_str == _C_SEL && bs_arg != NULL && bs_arg->sel_of_type != NULL && *(char **)value != NULL) {
431        struct bsInformalProtocolMethod * inf_prot_method;
432
433        inf_prot_method = find_bs_informal_protocol_method(*(char **)value, NO);
434        if (inf_prot_method != NULL) {
435          if (strcmp(inf_prot_method->encoding, bs_arg->sel_of_type) != 0)
436            return rb_err_new(ocdataconv_err_class(), "Cannot register the given selector '%s' as an informal protocol method of type '%s', because another informal protocol method is already registered with the same signature (but with another type, '%s'. Please rename your selector.", *(char **)value, bs_arg->sel_of_type, inf_prot_method->encoding);
437        }
438        else {
439          inf_prot_method = (struct bsInformalProtocolMethod *)malloc(sizeof(struct bsInformalProtocolMethod));
440          ASSERT_ALLOC(inf_prot_method);
441          inf_prot_method->selector = *(char **)value; // no need to dup it, selectors are unique
442          inf_prot_method->is_class_method = NO;
443          inf_prot_method->encoding = bs_arg->sel_of_type;
444          inf_prot_method->protocol_name = NULL; // unnamed
445          register_bs_informal_protocol_method(inf_prot_method);
446          FFI_LOG("registered informal protocol method '%s' of type '%s'", *(char **)value, bs_arg->sel_of_type);
447        }
448      }
449
450      arg_types[i + argc_delta] = ffi_type_for_octype(octype_str);
451      arg_values[i + argc_delta] = value;
452
453      if (is_c_array && bs_arg->c_ary_type == bsCArrayArgDelimitedByArg) {
454        int * plen;
455
456        FFI_LOG("arg[%d] defined as the array length (%d)", bs_arg->c_ary_type_value, len);
457        plen = (int *) alloca(sizeof(int));
458        *plen = len;
459        arg_values[bs_arg->c_ary_type_value + argc_delta] = plen;
460        arg_types[bs_arg->c_ary_type_value + argc_delta] = &ffi_type_uint;
461      }
462    }
463  }
464
465  // Prepare return type/val.
466  if (call_entry != NULL
467      && call_entry->retval != NULL
468      && call_entry->retval->octypestr != NULL
469      && strcmp(call_entry->retval->octypestr, ret_octype) != 0) {
470
471    FFI_LOG("coercing result from octype '%s' to octype '%s'", ret_octype, call_entry->retval->octypestr);
472    ret_octype = call_entry->retval->octypestr;
473  }
474  FFI_LOG("retval : %s", ret_octype);
475  ret_type = ffi_type_for_octype(ret_octype);
476  if (ret_type != &ffi_type_void) {
477    size_t ret_len = MAX(sizeof(long), ocdata_size(ret_octype));
478    FFI_LOG("allocated %ld bytes for the result", ret_len);
479    retval = alloca(ret_len);
480  }
481
482  // Prepare cif.
483  int cif_ret_status = ffi_prep_cif(&cif, FFI_DEFAULT_ABI, expected_argc + argc_delta, ret_type, arg_types);
484  if (cif_ret_status != FFI_OK)
485    rb_fatal("Can't prepare the cif");
486
487  // Call function.
488  exception = Qnil;
489  @try {
490    FFI_LOG("ffi_call %p with %d args", func_sym, expected_argc + argc_delta);
491    ffi_call(&cif, FFI_FN(func_sym), (ffi_arg *)retval, arg_values);
492    FFI_LOG("ffi_call done");
493  }
494  @catch (id oc_exception) {
495    FFI_LOG("got objc exception '%@' -- forwarding...", oc_exception);
496    exception = oc_err_new(oc_exception);
497  }
498
499  // Return exception if catched.
500  if (!NIL_P(exception))
501    return exception;
502
503  // Get result as argument.
504  for (i = 0; i < expected_argc; i++) {
505    VALUE arg;
506
507    arg = (i < given_argc) ? argv[i] : Qnil;
508    if (arg == Qnil)
509      continue;
510    if (!is_id_ptr(ARG_OCTYPESTR(i)))
511      continue;
512    if (rb_obj_is_kind_of(arg, objid_s_class()) != Qtrue)
513      continue;
514    FFI_LOG("got passed-by-reference argument %d", i);
515    (*retain_if_necessary)(arg, NO, retain_if_necessary_ctx);
516  }
517
518  // Get result
519  if (ret_type != &ffi_type_void) {
520    FFI_LOG("getting return value (%p of type '%s')", retval, ret_octype);
521
522    if (!ocdata_to_rbobj(Qnil, ret_octype, retval, result, YES))
523      return rb_err_new(ocdataconv_err_class(), "Cannot convert the result as '%s' to Ruby", ret_octype);
524
525    FFI_LOG("got return value");
526    (*retain_if_necessary)(*result, YES, retain_if_necessary_ctx);
527  }
528  else {
529    *result = Qnil;
530  }
531
532  // Get omitted pointers result, and pack them with the result in an array.
533  if (pointers_args_count > 0) {
534    volatile VALUE retval_ary;
535
536    retval_ary = rb_ary_new();
537    if (*ret_octype != _C_VOID) {
538      // Don't test if *result is nil, as nil may have been returned!
539      rb_ary_push(retval_ary, *result);
540    }
541
542    for (i = 0; i < expected_argc; i++) {
543      void *value;
544
545      if (!IS_POINTER_ARG(i))
546        continue;
547
548      value = arg_values[i + argc_delta];
549      if (value != NULL) {
550        volatile VALUE rbval;
551        const char *octype_str;
552        struct bsArg *bs_arg;
553        char fake_octype_str[512];
554
555        octype_str = ARG_OCTYPESTR(i);
556        octype_str = encoding_skip_qualifiers(octype_str);
557        if (*octype_str == _C_PTR)
558          octype_str++;
559        octype_str = encoding_skip_qualifiers(octype_str);
560        FFI_LOG("got omitted_pointer[%d] : %s (%p)", i, octype_str, value);
561        rbval = Qnil;
562        if ((*octype_str == _C_PTR || *octype_str == _C_ARY_B)
563            && (bs_arg = find_bs_arg_by_index(call_entry, i, expected_argc))
564              != NULL) {
565
566          switch (bs_arg->c_ary_type) {
567            case bsCArrayArgDelimitedByArg:
568              {
569                void *length_data;
570                long length_value;
571
572                length_data =
573                  arg_values[bs_arg->c_ary_type_value + argc_delta];
574                if (length_data == NULL) {
575                  length_value = 0;
576                }
577                else if (IS_POINTER_ARG(bs_arg->c_ary_type_value)) {
578                  long *p = *(long **)length_data;
579                  length_value = *p;
580                }
581                else {
582                  length_value = *(long *)length_data;
583                }
584
585                if (length_value > 0) {
586                  if (*octype_str == _C_ARY_B) {
587                    char *p = (char *)octype_str;
588                    do { p++; } while (isdigit(*p));
589                    snprintf(fake_octype_str, sizeof fake_octype_str,
590                      "[%ld%s", length_value, p);
591                    octype_str = fake_octype_str;
592                  }
593                  else {
594                    rbval = rb_str_new((char *)value,
595                      length_value * ocdata_size(octype_str));
596                  }
597                }
598                else {
599                  FFI_LOG("array length should have been returned by argument #%d, but it's invalid (%ld), defaulting on ObjCPtr", bs_arg->c_ary_type_value, length_value);
600                }
601              }
602              break;
603
604            case bsCArrayArgFixedLength:
605              rbval = rb_str_new((char *)value, bs_arg->c_ary_type_value);
606              break;
607
608            case bsCArrayArgDelimitedByNull:
609              rbval = rb_str_new2((char *)value);
610              break;
611
612            default:
613              // Do nothing.
614              break;
615          }
616        }
617
618        if (NIL_P(rbval)) {
619          void *p;
620          if (*octype_str == _C_ARY_B)
621            p = &value;
622          else
623            p = *(void **)value;
624          if (!ocdata_to_rbobj(Qnil, octype_str, p, (VALUE*)&rbval, YES))
625            return rb_err_new(ocdataconv_err_class(), "Cannot convert the passed-by-reference argument #%d as '%s' to Ruby", i, octype_str);
626        }
627        (*retain_if_necessary)(rbval, NO, retain_if_necessary_ctx);
628        rb_ary_push(retval_ary, rbval);
629      }
630      else {
631        FFI_LOG("omitted pointer[%d] is nil, skipping...", i);
632      }
633    }
634
635    *result = RARRAY(retval_ary)->len == 1 ? RARRAY(retval_ary)->ptr[0] : RARRAY(retval_ary)->len == 0 ? Qnil : retval_ary;
636  }
637
638  FFI_LOG("ffi dispatch done");
639
640  return Qnil;
641}
642
643void *
644ffi_make_closure(const char *rettype, const char **argtypes, unsigned argc, void (*handler)(ffi_cif *,void *,void **,void *), void *context)
645{
646  const char *error;
647  unsigned i;
648  ffi_type *retval_ffi_type;
649  ffi_type **arg_ffi_types;
650  ffi_cif *cif;
651  ffi_closure *closure;
652
653  error = NULL;
654  cif = NULL;
655  closure = NULL;
656
657  FFI_LOG("make closure argc %d", argc);
658
659  arg_ffi_types = (ffi_type **)malloc(sizeof(ffi_type *) * (argc + 1));
660  if (arg_ffi_types == NULL) {
661    error = "Can't allocate memory";
662    goto bails;
663  }
664
665  for (i = 0; i < argc; i++) {
666    arg_ffi_types[i] = ffi_type_for_octype(argtypes[i]);
667    FFI_LOG("arg[%d] -> ffi_type %p", i, arg_ffi_types[i]);
668  }
669  retval_ffi_type = ffi_type_for_octype(rettype);
670  arg_ffi_types[argc] = NULL;
671
672  cif = (ffi_cif *)malloc(sizeof(ffi_cif));
673  if (cif == NULL) {
674    error = "Can't allocate memory";
675    goto bails;
676  }
677
678  if (ffi_prep_cif(cif, FFI_DEFAULT_ABI, argc, retval_ffi_type, arg_ffi_types) != FFI_OK) {
679    error = "Can't prepare cif";
680    goto bails;
681  }
682
683  // Allocate a page to hold the closure with read and write permissions.
684  if ((closure = mmap(NULL, sizeof(ffi_closure), PROT_READ | PROT_WRITE,
685				  MAP_ANON | MAP_PRIVATE, -1, 0)) == (void*)-1)
686  {
687    error = "Can't allocate memory";
688    goto bails;
689  }
690
691  if (ffi_prep_closure(closure, cif, handler, context) != FFI_OK) {
692    error = "Can't prepare closure";
693    goto bails;
694  }
695
696  // Ensure that the closure will execute on all architectures.
697  if (mprotect(closure, sizeof(closure), PROT_READ | PROT_EXEC) == -1)
698  {
699    error = "Can't mark the closure with PROT_EXEC";
700    goto bails;
701  }
702
703  goto done;
704
705bails:
706  if (arg_ffi_types != NULL)
707    free(arg_ffi_types);
708  if (cif != NULL)
709    free(cif);
710  if (closure != NULL)
711    free(closure);
712  if (error != NULL)
713    rb_raise(rb_eRuntimeError, error);
714
715done:
716  return closure;
717}
718
719@interface __OVMIXThreadDispatcher : NSObject
720{
721  void (*_handler)(ffi_cif *,void *,void **,void *);
722  void (*_finished_handler)(ffi_cif *,void *,void **,void *);
723  ffi_cif * _cif;
724  void * _resp;
725  void ** _args;
726  void * _userdata;
727}
728@end
729
730@implementation __OVMIXThreadDispatcher
731
732- (id)initWithClosure:(void (*)(ffi_cif *,void *,void **,void *))handler
733  cif:(ffi_cif *)cif
734  resp:(void *)resp
735  args:(void **)args
736  userdata:(void *)userdata
737  finished_handler:(void (*)(ffi_cif *,void *,void **,void *))finished_handler
738{
739  self = [super init];
740  if (self != NULL) {
741    _handler = handler;
742    _cif = cif;
743    _resp = resp;
744    _args = args;
745    _userdata = userdata;
746    _finished_handler = finished_handler;
747  }
748  return self;
749}
750
751extern NSThread *rubycocoaThread;
752
753- (void)dispatch
754{
755  DISPATCH_ON_RUBYCOCOA_THREAD(self, @selector(syncDispatch));
756}
757
758- (void)syncDispatch
759{
760  (*_handler)(_cif, _resp, _args, _userdata);
761  (*_finished_handler)(_cif, _resp, _args, _userdata);
762}
763
764@end
765
766void ffi_dispatch_closure_in_main_thread(
767  void (*handler)(ffi_cif *,void *,void **,void *),
768  ffi_cif *cif,
769  void *resp,
770  void **args,
771  void *userdata,
772  void (*finished_handler)(ffi_cif *,void *,void **,void *))
773{
774  __OVMIXThreadDispatcher * dispatcher;
775
776  dispatcher = [[__OVMIXThreadDispatcher alloc]
777    initWithClosure:handler
778    cif:cif
779    resp:resp
780    args:args
781    userdata:userdata
782    finished_handler:finished_handler];
783  [dispatcher dispatch];
784  [dispatcher release];
785}
786