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 <objc/objc-class.h>
11#import <Foundation/Foundation.h>
12#import "ocdata_conv.h"
13#import "RBObject.h"
14#import "mdl_osxobjc.h"
15#import <CoreFoundation/CFString.h> // CFStringEncoding
16#import "st.h"
17#import "BridgeSupport.h"
18#import "internal_macros.h"
19
20#define CACHE_LOCKING 0
21
22#define DATACONV_LOG(fmt, args...) DLOG("DATACNV", fmt, ##args)
23
24static struct st_table *rb2ocCache;
25static struct st_table *oc2rbCache;
26
27static VALUE _ocid_to_rbobj (VALUE context_obj, id ocid, BOOL is_class);
28
29#if CACHE_LOCKING
30static pthread_mutex_t rb2ocCacheLock;
31static pthread_mutex_t oc2rbCacheLock;
32# define CACHE_LOCK(x)      (pthread_mutex_lock(x))
33# define CACHE_UNLOCK(x)    (pthread_mutex_unlock(x))
34#else
35# define CACHE_LOCK(x)
36# define CACHE_UNLOCK(x)
37#endif
38
39#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
40// On MacOS X 10.4 or earlier, +signatureWithObjCTypes: is a SPI
41@interface NSMethodSignature (WarningKiller)
42+ (id) signatureWithObjCTypes:(const char*)types;
43@end
44#endif
45
46@interface RBObject (Private)
47- (id)_initWithRubyObject: (VALUE)rbobj retains: (BOOL) flag;
48@end
49
50void init_rb2oc_cache(void)
51{
52  rb2ocCache = st_init_numtable();
53#if CACHE_LOCKING
54  pthread_mutex_init(&rb2ocCacheLock, NULL);
55#endif
56}
57
58void init_oc2rb_cache(void)
59{
60  oc2rbCache = st_init_numtable();
61#if CACHE_LOCKING
62  pthread_mutex_init(&oc2rbCacheLock, NULL);
63#endif
64}
65
66void remove_from_oc2rb_cache(id ocid)
67{
68  CACHE_LOCK(&oc2rbCacheLock);
69  st_delete(oc2rbCache, (st_data_t *)&ocid, NULL);
70  CACHE_UNLOCK(&oc2rbCacheLock);
71}
72
73void remove_from_rb2oc_cache(VALUE rbobj)
74{
75  CACHE_LOCK(&rb2ocCacheLock);
76  st_delete(rb2ocCache, (st_data_t *)&rbobj, NULL);
77  CACHE_UNLOCK(&rb2ocCacheLock);
78}
79
80static BOOL
81convert_cary(VALUE *result, void *ocdata, char *octype_str, BOOL to_ruby)
82{
83  long i, count, size, pos;
84  VALUE ary;
85  BOOL ok;
86
87  octype_str++;
88
89  // first, get the number of entries
90  count = 0;
91  while (isdigit(*octype_str)) {
92    count *= 10;
93    count += (long)(*octype_str - '0');
94    octype_str++;
95  }
96
97  // second, remove the trailing ']'
98  pos = strlen(octype_str) - 1;
99  octype_str[pos] = '\0';      /*  ((char*)octype_str)[pos] = '\0'; */
100  size = ocdata_size(octype_str);
101
102  // third, do the conversion
103  if (to_ruby) {
104    ary = rb_ary_new();
105    for (i = 0; i < count; i++) {
106      VALUE entry;
107      void *p;
108
109      p = *(void **)ocdata + (i * size);
110      if (!ocdata_to_rbobj(Qnil, octype_str, p, &entry, NO)) {
111        *result = Qnil;
112        ok = NO;
113        goto bail;
114      }
115      rb_ary_push(ary, entry);
116    }
117
118    *result = ary;
119    ok = YES;
120  }
121  else {
122    volatile VALUE ary;
123
124    ary = *result;
125
126    Check_Type(ary, T_ARRAY);
127    if (RARRAY(ary)->len > count)
128      rb_raise(rb_eArgError,
129        "Given Array expected with maximum %d elements, but got %d",
130        count, RARRAY(ary)->len);
131
132    for (i = 0; i < RARRAY(ary)->len; i++) {
133      VALUE val;
134      void *p;
135
136      val = RARRAY(ary)->ptr[i];
137      p = ocdata + (i * size);
138      if (!rbobj_to_ocdata(val, octype_str, p, NO)) {
139        ok = NO;
140        goto bail;
141      }
142    }
143    ok = YES;
144  }
145
146bail:
147  // put back the trailing ']'
148  octype_str[pos] = ']'; /* ((char*)octype_str)[pos] = ']'; */
149  return ok;
150}
151
152static BOOL
153rbobj_to_cary (VALUE obj, void *data, const char *octype_str)
154{
155  return convert_cary(&obj, data, (char *)octype_str, NO);
156}
157
158static BOOL
159cary_to_rbary (void *data, const char *octype_str, VALUE *result)
160{
161  return convert_cary(result, data, (char *)octype_str, YES);
162}
163
164size_t ocdata_alloc_size(const char* octype_str)
165{
166#if BYTE_ORDER == BIG_ENDIAN
167  size_t size = ocdata_size(octype_str);
168  if (size == 0) return 0;
169  return size < sizeof(void*) ? sizeof(void*) : size;
170#else
171  return ocdata_size(octype_str);
172#endif
173}
174
175size_t
176ocdata_size(const char* octype_str)
177{
178  size_t result;
179  struct bsBoxed *bs_boxed;
180
181  if (*octype_str == _C_CONST)
182    octype_str++;
183
184  bs_boxed = find_bs_boxed_by_encoding(octype_str);
185  if (bs_boxed != NULL)
186    return bs_boxed_size(bs_boxed);
187
188  if (find_bs_cf_type_by_encoding(octype_str) != NULL)
189    octype_str = "@";
190
191  result = 0;
192
193  switch (*octype_str) {
194    case _C_ID:
195    case _C_CLASS:
196      result = sizeof(id);
197      break;
198
199    case _C_SEL:
200      result = sizeof(SEL);
201      break;
202
203    case _C_CHR:
204    case _C_UCHR:
205      result = sizeof(char);
206      break;
207
208    case _C_SHT:
209    case _C_USHT:
210      result = sizeof(short);
211      break;
212
213    case _C_INT:
214    case _C_UINT:
215      result = sizeof(int);
216      break;
217
218    case _C_LNG:
219    case _C_ULNG:
220      result = sizeof(long);
221      break;
222
223#if HAVE_LONG_LONG
224    case _C_LNG_LNG:
225      result = sizeof(long long);
226      break;
227
228    case _C_ULNG_LNG:
229      result = sizeof(unsigned long long);
230      break;
231#endif
232
233    case _C_FLT:
234      result = sizeof(float);
235      break;
236
237    case _C_DBL:
238      result = sizeof(double);
239      break;
240
241    case _C_CHARPTR:
242      result = sizeof(char*);
243      break;
244
245    case _C_VOID:
246      result = 0;
247      break;
248
249    case _C_BOOL:
250      result = sizeof(BOOL);
251      break;
252
253    case _C_PTR:
254      result = sizeof(void*);
255      break;
256
257    case _C_BFLD:
258      if (octype_str != NULL) {
259        char *type;
260        long lng;
261
262        type = (char *)octype_str;
263        lng  = strtol(type, &type, 10);
264
265        // while next type is a bit field
266        while (*type == _C_BFLD) {
267          long next_lng;
268
269          // skip over _C_BFLD
270          type++;
271
272          // get next bit field length
273          next_lng = strtol(type, &type, 10);
274
275          // if spans next word then align to next word
276          if ((lng & ~31) != ((lng + next_lng) & ~31))
277            lng = (lng + 31) & ~31;
278
279          // increment running length
280          lng += next_lng;
281        }
282        result = (lng + 7) / 8;
283      }
284      break;
285
286    default:
287      @try {
288        NSGetSizeAndAlignment(octype_str,
289#if __LP64__
290          (unsigned long *)&result,
291#else
292          (unsigned int *)&result,
293#endif
294          NULL);
295        }
296        @catch (id exception) {
297          rb_raise(rb_eRuntimeError, "Cannot compute size of type `%s' : %s",
298            octype_str, [[exception description] UTF8String]);
299        }
300      break;
301  }
302
303  return result;
304}
305
306void *
307ocdata_malloc(const char* octype_str)
308{
309  size_t s = ocdata_alloc_size(octype_str);
310  if (s == 0) return NULL;
311  return malloc(s);
312}
313
314BOOL
315ocdata_to_rbobj (VALUE context_obj, const char *octype_str, const void *ocdata, VALUE *result, BOOL from_libffi)
316{
317  BOOL f_success = YES;
318  volatile VALUE rbval = Qnil;
319  struct bsBoxed *bs_boxed;
320
321#if BYTE_ORDER == BIG_ENDIAN
322  // libffi casts all types as a void pointer, which is problematic on PPC for types sized less than a void pointer (char, uchar, short, ushort, ...), as we have to shift the bytes to get the real value.
323  if (from_libffi) {
324    int delta = sizeof(void *) - ocdata_size(octype_str);
325    if (delta > 0)
326      ocdata += delta;
327  }
328#endif
329
330  octype_str = encoding_skip_qualifiers(octype_str);
331
332  bs_boxed = find_bs_boxed_by_encoding(octype_str);
333  if (bs_boxed != NULL) {
334    *result = rb_bs_boxed_new_from_ocdata(bs_boxed, (void *)ocdata);
335    return YES;
336  }
337
338  if (find_bs_cf_type_by_encoding(octype_str) != NULL)
339    octype_str = "@";
340
341  switch (*octype_str) {
342    case _C_ID:
343    case _C_CLASS:
344      rbval = _ocid_to_rbobj(context_obj, *(id*)ocdata, *octype_str == _C_CLASS);
345      break;
346
347    case _C_PTR:
348      if (is_boxed_ptr(octype_str, &bs_boxed)) {
349        rbval = rb_bs_boxed_ptr_new_from_ocdata(bs_boxed, *(void **)ocdata);
350      }
351      else {
352        void *cptr = *(void**)ocdata;
353        rbval = cptr == NULL ? Qnil : objcptr_s_new_with_cptr (cptr, octype_str);
354      }
355      break;
356
357    case _C_ARY_B:
358      f_success = cary_to_rbary(*(void **)ocdata, octype_str, (VALUE*)&rbval);
359      break;
360
361    case _C_BOOL:
362      rbval = bool_to_rbobj(*(BOOL*)ocdata);
363      break;
364
365    case _C_SEL:
366      rbval = rb_str_new2(sel_getName(*(SEL*)ocdata));
367      break;
368
369    case _C_CHR:
370      rbval = INT2NUM(*(char*)ocdata);
371      break;
372
373    case _C_UCHR:
374      rbval = UINT2NUM(*(unsigned char*)ocdata);
375      break;
376
377    case _C_SHT:
378      rbval = INT2NUM(*(short*)ocdata);
379      break;
380
381    case _C_USHT:
382      rbval = UINT2NUM(*(unsigned short*)ocdata);
383      break;
384
385    case _C_INT:
386      rbval = INT2NUM(*(int*)ocdata);
387      break;
388
389    case _C_UINT:
390      rbval = UINT2NUM(*(unsigned int*)ocdata);
391      break;
392
393    case _C_LNG:
394      rbval = INT2NUM(*(long*)ocdata);
395      break;
396
397    case _C_ULNG:
398      rbval = UINT2NUM(*(unsigned long*)ocdata);
399      break;
400
401#if HAVE_LONG_LONG
402    case _C_LNG_LNG:
403      rbval = LL2NUM(*(long long*)ocdata);
404      break;
405
406    case _C_ULNG_LNG:
407      rbval = ULL2NUM(*(unsigned long long*)ocdata);
408      break;
409#endif
410
411    case _C_FLT:
412      rbval = rb_float_new((double)(*(float*)ocdata));
413      break;
414
415    case _C_DBL:
416      rbval = rb_float_new(*(double*)ocdata);
417      break;
418
419    case _C_CHARPTR:
420      if (*(void **)ocdata == NULL)
421        rbval = Qnil;
422      else
423        rbval = rb_str_new2(*(char **)ocdata);
424      break;
425
426    default:
427      f_success = NO;
428      rbval = Qnil;
429      break;
430	}
431
432  if (f_success)
433    *result = rbval;
434
435  return f_success;
436}
437
438static BOOL
439rbary_to_nsary (VALUE rbary, id* nsary)
440{
441  long i, len;
442  id *objects;
443
444  len = RARRAY(rbary)->len;
445  objects = (id *)alloca(sizeof(id) * len);
446  ASSERT_ALLOC(objects);
447
448  for (i = 0; i < len; i++)
449    if (!rbobj_to_nsobj(RARRAY(rbary)->ptr[i], &objects[i]))
450      return NO;
451
452  *nsary = [[[NSMutableArray alloc] initWithObjects:objects count:len] autorelease];
453  return YES;
454}
455
456// FIXME: we should use the CoreFoundation API for x_to_y functions
457// (should be faster than Foundation)
458
459static BOOL
460rbhash_to_nsdic (VALUE rbhash, id* nsdic)
461{
462  volatile VALUE ary_keys;
463  VALUE* keys;
464  VALUE val;
465  long i, len;
466  id *nskeys, *nsvals;
467
468  ary_keys = rb_funcall(rbhash, rb_intern("keys"), 0);
469  len = RARRAY(ary_keys)->len;
470  keys = RARRAY(ary_keys)->ptr;
471
472  nskeys = (id *)alloca(sizeof(id) * len);
473  ASSERT_ALLOC(nskeys);
474  nsvals = (id *)alloca(sizeof(id) * len);
475  ASSERT_ALLOC(nsvals);
476
477  for (i = 0; i < len; i++) {
478    if (!rbobj_to_nsobj(keys[i], &nskeys[i]))
479      return NO;
480    val = rb_hash_aref(rbhash, keys[i]);
481    if (!rbobj_to_nsobj(val, &nsvals[i]))
482      return NO;
483  }
484
485  *nsdic = [[[NSMutableDictionary alloc] initWithObjects:nsvals forKeys:nskeys count:len] autorelease];
486  return YES;
487}
488
489static BOOL
490rbbool_to_nsnum (VALUE rbval, id* nsval)
491{
492  *nsval = [NSNumber numberWithBool:RTEST(rbval)];
493  return YES;
494}
495
496static BOOL
497rbint_to_nsnum (VALUE rbval, id* nsval)
498{
499#if HAVE_LONG_LONG
500  long long val;
501  val = NUM2LL(rbval);
502  *nsval = [NSNumber numberWithLongLong:val];
503#else
504  long val;
505  val = NUM2LONG(rbval);
506  *nsval = [NSNumber numberWithLong:val];
507#endif
508  return YES;
509}
510
511static BOOL
512rbfloat_to_nsnum (VALUE rbval, id* nsval)
513{
514  double val;
515  val = NUM2DBL(rbval);
516  *nsval = [NSNumber numberWithDouble:val];
517  return YES;
518}
519
520static BOOL
521rbtime_to_nsdate (VALUE rbval, id* nsval)
522{
523  NSTimeInterval seconds;
524  seconds = NUM2LONG(rb_funcall(rbval, rb_intern("to_i"), 0));
525  *nsval = [NSDate dateWithTimeIntervalSince1970:seconds];
526  return [(*nsval) isKindOfClass: [NSDate class]];
527}
528
529static BOOL
530rbobj_convert_to_nsobj (VALUE obj, id* nsobj)
531{
532  switch (TYPE(obj)) {
533    case T_NIL:
534      *nsobj = nil;
535      return YES;
536
537    case T_STRING:
538      obj = rb_obj_as_string(obj);
539      *nsobj = rbstr_to_ocstr(obj);
540      return YES;
541
542    case T_SYMBOL:
543      obj = rb_obj_as_string(obj);
544      *nsobj = [NSString stringWithUTF8String: RSTRING(obj)->ptr];
545      return YES;
546
547    case T_ARRAY:
548      return rbary_to_nsary(obj, nsobj);
549
550    case T_HASH:
551      return rbhash_to_nsdic(obj, nsobj);
552
553    case T_TRUE:
554    case T_FALSE:
555      return rbbool_to_nsnum(obj, nsobj);
556
557    case T_FIXNUM:
558    case T_BIGNUM:
559      return rbint_to_nsnum(obj, nsobj);
560
561    case T_FLOAT:
562      return rbfloat_to_nsnum(obj, nsobj);
563
564    default:
565      if (rb_obj_is_kind_of(obj, rb_cTime))
566        return rbtime_to_nsdate(obj, nsobj);
567
568      *nsobj = [[[RBObject alloc] initWithRubyObject:obj] autorelease];
569      return YES;
570  }
571  return YES;
572}
573
574BOOL
575rbobj_to_nsobj (VALUE obj, id* nsobj)
576{
577  BOOL  ok;
578
579  if (obj == Qnil) {
580    *nsobj = nil;
581    return YES;
582  }
583
584  // Cache new Objective-C object addresses in an internal table to
585  // avoid duplication.
586  //
587  // We are locking the access to the cache twice (lookup + insert) as
588  // rbobj_convert_to_nsobj is succeptible to call us again, to avoid
589  // a deadlock.
590
591  CACHE_LOCK(&rb2ocCacheLock);
592  ok = st_lookup(rb2ocCache, (st_data_t)obj, (st_data_t *)nsobj);
593  CACHE_UNLOCK(&rb2ocCacheLock);
594
595  if (!ok) {
596    *nsobj = rbobj_get_ocid(obj);
597    if (*nsobj != nil || rbobj_convert_to_nsobj(obj, nsobj)) {
598      BOOL  magic_cookie;
599      if (*nsobj == nil) return YES;
600
601      magic_cookie = find_magic_cookie_const_by_value(*nsobj) != NULL;
602      if (magic_cookie || ([*nsobj isProxy] && [*nsobj isRBObject])) {
603        CACHE_LOCK(&rb2ocCacheLock);
604        // Check out that the hash is still empty for us, to avoid a race condition.
605        if (!st_lookup(rb2ocCache, (st_data_t)obj, (st_data_t *)nsobj))
606          st_insert(rb2ocCache, (st_data_t)obj, (st_data_t)*nsobj);
607        CACHE_UNLOCK(&rb2ocCacheLock);
608      }
609      ok = YES;
610    }
611  }
612
613  return ok;
614}
615
616BOOL
617rbobj_to_bool (VALUE obj)
618{
619  return RTEST(obj) ? YES : NO;
620}
621
622VALUE
623bool_to_rbobj (BOOL val)
624{
625  return (val ? Qtrue : Qfalse);
626}
627
628VALUE
629sel_to_rbobj (SEL val)
630{
631  VALUE rbobj;
632
633  // FIXME: this should be optimized
634
635  if (ocdata_to_rbobj(Qnil, ":", &val, &rbobj, NO)) {
636    rbobj = rb_obj_as_string(rbobj);
637    // str.tr!(':','_')
638    rb_funcall(rbobj, rb_intern("tr!"), 2, rb_str_new2(":"), rb_str_new2("_"));
639    // str.sub!(/_+$/,'')
640    rb_funcall(rbobj, rb_intern("sub!"), 2, rb_str_new2("_+$"), rb_str_new2(""));
641  }
642  else {
643    rbobj = Qnil;
644  }
645  return rbobj;
646}
647
648VALUE
649int_to_rbobj (int val)
650{
651  return INT2NUM(val);
652}
653
654VALUE
655uint_to_rbobj (unsigned int val)
656{
657  return UINT2NUM(val);
658}
659
660VALUE
661double_to_rbobj (double val)
662{
663  return rb_float_new(val);
664}
665
666VALUE
667ocid_to_rbobj_cache_only (id ocid)
668{
669  VALUE result;
670  BOOL  ok;
671
672  CACHE_LOCK(&oc2rbCacheLock);
673  ok = st_lookup(oc2rbCache, (st_data_t)ocid, (st_data_t *)&result);
674  CACHE_UNLOCK(&oc2rbCacheLock);
675
676  return ok ? result : Qnil;
677}
678
679static VALUE
680_ocid_to_rbobj (VALUE context_obj, id ocid, BOOL is_class)
681{
682  VALUE result;
683  BOOL  ok, shouldCache;
684  struct bsConst *  bs_const;
685
686  if (ocid == nil)
687    return Qnil;
688
689  // Cache new Ruby object addresses in an internal table to
690  // avoid duplication.
691  //
692  // We are locking the access to the cache twice (lookup + insert) as
693  // ocobj_s_new is succeptible to call us again, to avoid a deadlock.
694
695  bs_const = find_magic_cookie_const_by_value(ocid);
696
697  if (bs_const == NULL
698      && (is_class
699          || [ocid isProxy]
700          || find_bs_cf_type_by_type_id(CFGetTypeID(ocid)) != NULL)) {
701    // We don't cache CF-based objects because we don't have yet a reliable
702    // way to remove them from the cache.
703    ok = shouldCache = NO;
704  }
705  else {
706    CACHE_LOCK(&oc2rbCacheLock);
707    ok = st_lookup(oc2rbCache, (st_data_t)ocid, (st_data_t *)&result);
708    CACHE_UNLOCK(&oc2rbCacheLock);
709    shouldCache = context_obj != Qfalse;
710  }
711
712  if (!ok) {
713    if (bs_const != NULL) {
714      result = ocobj_s_new_with_class_name(ocid, bs_const->class_name);
715    }
716    else {
717      result = ocid_get_rbobj(ocid);
718      if (result == Qnil)
719        result = rbobj_get_ocid(context_obj) == ocid
720          ? context_obj : ocobj_s_new(ocid);
721    }
722
723    if (shouldCache) {
724      CACHE_LOCK(&oc2rbCacheLock);
725      // Check out that the hash is still empty for us, to avoid a race
726      // condition.
727      if (!st_lookup(oc2rbCache, (st_data_t)ocid, (st_data_t *)&result))
728        st_insert(oc2rbCache, (st_data_t)ocid, (st_data_t)result);
729      CACHE_UNLOCK(&oc2rbCacheLock);
730    }
731  }
732
733  return result;
734}
735
736VALUE
737ocid_to_rbobj (VALUE context_obj, id ocid)
738{
739  return _ocid_to_rbobj(context_obj, ocid, NO);
740}
741
742static SEL
743rbobj_to_cselstr (VALUE obj)
744{
745  int i;
746  volatile VALUE str;
747  char *sel;
748
749  str = rb_obj_is_kind_of(obj, rb_cString)
750    ? obj : rb_obj_as_string(obj);
751
752  if (rb_ivar_defined(str, rb_intern("@__is_sel__")) == Qtrue)
753    return sel_registerName(RSTRING(str)->ptr);
754
755  sel = (char *)alloca(RSTRING(str)->len + 1);
756  sel[0] = RSTRING(str)->ptr[0];
757  for (i = 1; i < RSTRING(str)->len; i++) {
758    char c = RSTRING(str)->ptr[i];
759    if (c == '_')
760      c = ':';
761    sel[i] = c;
762  }
763  sel[RSTRING(str)->len] = '\0';
764
765  return sel_registerName(sel);
766}
767
768SEL
769rbobj_to_nssel (VALUE obj)
770{
771  return NIL_P(obj) ? NULL : rbobj_to_cselstr(obj);
772}
773
774struct funcptr_closure_context {
775  char *    rettype;
776  char **   argtypes;
777  unsigned  argc;
778  VALUE     block;
779};
780
781static void
782funcptr_closure_handler (ffi_cif *cif, void *resp, void **args, void *userdata)
783{
784  struct funcptr_closure_context *context;
785  volatile VALUE rb_args;
786  unsigned i;
787  VALUE retval;
788
789  context = (struct funcptr_closure_context *)userdata;
790  rb_args = rb_ary_new2(context->argc);
791
792  for (i = 0; i < context->argc; i++) {
793    VALUE arg;
794
795    if (!ocdata_to_rbobj(Qnil, context->argtypes[i], args[i], &arg, NO))
796      rb_raise(rb_eRuntimeError, "Can't convert Objective-C argument #%d of octype '%s' to Ruby value", i, context->argtypes[i]);
797
798    DATACONV_LOG("converted arg #%d of type %s to Ruby value %p", i, context->argtypes[i], arg);
799
800    rb_ary_store(rb_args, i, arg);
801  }
802
803  DATACONV_LOG("calling Ruby block with %d args...", RARRAY(rb_args)->len);
804  retval = rb_funcall2(context->block, rb_intern("call"), RARRAY(rb_args)->len, RARRAY(rb_args)->ptr);
805  DATACONV_LOG("called Ruby block");
806
807  if (*encoding_skip_to_first_type(context->rettype) != _C_VOID) {
808    if (!rbobj_to_ocdata(retval, context->rettype, resp, YES))
809      rb_raise(rb_eRuntimeError, "Can't convert return Ruby value to Objective-C value of octype '%s'", context->rettype);
810  }
811}
812
813static BOOL
814rbobj_to_funcptr (VALUE obj, void **cptr, const char *octype_str)
815{
816  unsigned  argc;
817  unsigned  i;
818  char *    rettype;
819  char **   argtypes;
820  int       block_arity;
821  struct funcptr_closure_context *  context;
822
823  if (TYPE(obj) == T_NIL) {
824    *cptr = NULL;
825    return YES;
826  }
827
828  if (rb_obj_is_kind_of(obj, rb_cProc) == Qfalse)
829    return NO;
830
831  if (*octype_str != '?')
832    return NO;
833  octype_str++;
834  if (octype_str == NULL)
835    return NO;
836
837  decode_method_encoding(octype_str, nil, &argc, &rettype, &argtypes, NO);
838
839  block_arity = FIX2INT(rb_funcall(obj, rb_intern("arity"), 0));
840  if (block_arity != argc) {
841    free(rettype);
842    if (argtypes != NULL) {
843      for (i = 0; i < argc; i++)
844        free(argtypes[i]);
845      free(argtypes);
846    }
847    // Should we return NO there? Probably better to raise an exception directly.
848    rb_raise(rb_eArgError, "Given Proc object has an invalid number of arguments (expected %d, got %d)",
849             argc, block_arity);
850    return NO;  // to be sure...
851  }
852
853  context = (struct funcptr_closure_context *)malloc(sizeof(struct funcptr_closure_context));
854  ASSERT_ALLOC(context);
855  context->rettype = rettype;
856  context->argtypes = argtypes;
857  context->argc = argc;
858  context->block = obj;
859
860  *cptr = ffi_make_closure(rettype, (const char **)argtypes, argc, funcptr_closure_handler, context);
861
862  return YES;
863}
864
865static BOOL
866rbobj_to_objcptr (VALUE obj, void** cptr, const char *octype_str)
867{
868  if (TYPE(obj) == T_NIL) {
869    *cptr = NULL;
870  }
871  else if (TYPE(obj) == T_STRING) {
872    *cptr = RSTRING(obj)->ptr;
873  }
874  else if (TYPE(obj) == T_ARRAY) {
875    if (RARRAY(obj)->len > 0) {
876      size_t len;
877      void *ary;
878      unsigned i;
879
880      len = ocdata_size(octype_str);
881      ary = *cptr;
882
883      for (i = 0; i < RARRAY(obj)->len; i++) {
884        if (!rbobj_to_ocdata(RARRAY(obj)->ptr[i], octype_str, ary + (i * len), NO))
885          return NO;
886      }
887    }
888    else {
889      *cptr = NULL;
890    }
891  }
892  else if (rb_obj_is_kind_of(obj, objid_s_class()) == Qtrue) {
893    *cptr = OBJCID_ID(obj);
894  }
895  else if (rb_obj_is_kind_of(obj, objcptr_s_class()) == Qtrue) {
896    *cptr = objcptr_cptr(obj);
897  }
898  else if (rb_obj_is_kind_of(obj, objboxed_s_class()) == Qtrue) {
899    struct bsBoxed *bs_boxed;
900    void *data;
901    BOOL ok;
902
903    bs_boxed = find_bs_boxed_for_klass(CLASS_OF(obj));
904    if (bs_boxed == NULL)
905      return NO;
906
907    data = rb_bs_boxed_get_data(obj, bs_boxed->encoding, NULL, &ok, YES);
908    if (!ok)
909      return NO;
910    *cptr = data;
911  }
912  else {
913    return NO;
914  }
915  return YES;
916}
917
918static BOOL
919rbobj_to_idptr (VALUE obj, id** idptr)
920{
921  if (TYPE(obj) == T_NIL) {
922    *idptr = nil;
923  }
924  else if (TYPE(obj) == T_ARRAY) {
925    if (RARRAY(obj)->len > 0) {
926      id *ary;
927      unsigned i;
928
929      ary = *idptr;
930      for (i = 0; i < RARRAY(obj)->len; i++) {
931        if (!rbobj_to_nsobj(RARRAY(obj)->ptr[i], &ary[i])) {
932          *idptr = nil;
933          return NO;
934        }
935      }
936    }
937    else {
938      *idptr = nil;
939    }
940  }
941  else if (rb_obj_is_kind_of(obj, objid_s_class()) == Qtrue) {
942    id old_id = OBJCID_ID(obj);
943    if (old_id) [old_id release];
944    OBJCID_ID(obj) = nil;
945    *idptr = OBJCID_IDPTR(obj);
946  }
947  else {
948    return NO;
949  }
950  return YES;
951}
952
953BOOL
954rbobj_to_ocdata (VALUE obj, const char *octype_str, void* ocdata, BOOL to_libffi)
955{
956  BOOL f_success = YES;
957
958#if BYTE_ORDER == BIG_ENDIAN
959  // libffi casts all types as a void pointer, which is problematic on PPC for types sized less than a void pointer (char, uchar, short, ushort, ...), as we have to shift the bytes to get the real value.
960  if (to_libffi) {
961    int delta = sizeof(void *) - ocdata_size(octype_str);
962    if (delta > 0) {
963      memset(ocdata, 0, delta);
964      ocdata += delta;
965    }
966  }
967#endif
968
969  octype_str = encoding_skip_qualifiers(octype_str);
970
971  // Make sure we convert booleans to NSNumber booleans.
972  if (*octype_str != _C_ID && *octype_str != _C_BOOL) {
973    if (TYPE(obj) == T_TRUE) {
974      obj = INT2NUM(1);
975    }
976    else if (TYPE(obj) == T_FALSE) {
977      obj = INT2NUM(0);
978    }
979  }
980
981  if (find_bs_boxed_by_encoding(octype_str) != NULL) {
982    void *data;
983    size_t size;
984
985    data = rb_bs_boxed_get_data(obj, octype_str, &size, &f_success, YES);
986    if (f_success) {
987      if (data == NULL)
988        *(void **)ocdata = NULL;
989      else
990        memcpy(ocdata, data, size);
991      return YES;
992    }
993  }
994
995  if (find_bs_cf_type_by_encoding(octype_str) != NULL)
996    octype_str = "@";
997
998  switch (*octype_str) {
999    case _C_ID:
1000    case _C_CLASS:
1001    {
1002      id nsobj;
1003      f_success = rbobj_to_nsobj(obj, &nsobj);
1004      if (f_success) *(id*)ocdata = nsobj;
1005      break;
1006    }
1007
1008    case _C_SEL:
1009      *(SEL*)ocdata = rbobj_to_nssel(obj);
1010      break;
1011
1012    case _C_UCHR:
1013      *(unsigned char*)ocdata = (unsigned char) NUM2UINT(rb_Integer(obj));
1014      break;
1015
1016    case _C_BOOL:
1017      {
1018        unsigned char v;
1019
1020        switch (TYPE(obj)) {
1021          case T_FALSE:
1022          case T_NIL:
1023            v = 0;
1024            break;
1025          case T_TRUE:
1026          // All other types should be converted as true, to follow the
1027          // Ruby semantics (where for example any integer is always true,
1028          // even 0).
1029          default:
1030            v = 1;
1031            break;
1032        }
1033        *(unsigned char*)ocdata = v;
1034      }
1035      break;
1036
1037    case _C_CHR:
1038      *(char*)ocdata = (char) NUM2INT(rb_Integer(obj));
1039      break;
1040
1041    case _C_SHT:
1042      *(short*)ocdata = (short) NUM2INT(rb_Integer(obj));
1043      break;
1044
1045    case _C_USHT:
1046      *(unsigned short*)ocdata = (unsigned short) NUM2UINT(rb_Integer(obj));
1047      break;
1048
1049    case _C_INT:
1050      *(int*)ocdata = (int) NUM2INT(rb_Integer(obj));
1051      break;
1052
1053    case _C_UINT:
1054      *(unsigned int*)ocdata = (unsigned int) NUM2UINT(rb_Integer(obj));
1055      break;
1056
1057    case _C_LNG:
1058      *(long*)ocdata = (long) NUM2LONG(rb_Integer(obj));
1059      break;
1060
1061    case _C_ULNG:
1062      *(unsigned long*)ocdata = (unsigned long) NUM2ULONG(rb_Integer(obj));
1063      break;
1064
1065#if HAVE_LONG_LONG
1066    case _C_LNG_LNG:
1067      *(long long*)ocdata = (long long) NUM2LL(rb_Integer(obj));
1068      break;
1069
1070    case _C_ULNG_LNG:
1071      *(unsigned long long*)ocdata = (unsigned long long) NUM2ULL(rb_Integer(obj));
1072      break;
1073#endif
1074
1075    case _C_FLT:
1076      *(float*)ocdata = (float) RFLOAT(rb_Float(obj))->value;
1077      break;
1078
1079    case _C_DBL:
1080      *(double*)ocdata = RFLOAT(rb_Float(obj))->value;
1081      break;
1082
1083    case _C_CHARPTR:
1084      {
1085        VALUE str = rb_obj_as_string(obj);
1086        *(char**)ocdata = StringValuePtr(str);
1087      }
1088      break;
1089
1090    case _C_PTR:
1091      if (is_id_ptr(octype_str)) {
1092        f_success = rbobj_to_idptr(obj, ocdata);
1093      }
1094      else {
1095        f_success = rbobj_to_objcptr(obj, ocdata, octype_str + 1);
1096        if (!f_success)
1097          f_success = rbobj_to_funcptr(obj, ocdata, octype_str + 1);
1098      }
1099      break;
1100
1101    case _C_ARY_B:
1102      f_success = rbobj_to_cary(obj, ocdata, octype_str);
1103      break;
1104
1105    default:
1106      f_success = NO;
1107      break;
1108  }
1109
1110  return f_success;
1111}
1112
1113static
1114NSStringEncoding kcode_to_nsencoding (const char* kcode)
1115{
1116  if (strcmp(kcode, "UTF8") == 0)
1117    return NSUTF8StringEncoding;
1118  else if (strcmp(kcode, "SJIS") == 0)
1119    return CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingMacJapanese);
1120  else if (strcmp(kcode, "EUC") == 0)
1121    return NSJapaneseEUCStringEncoding;
1122  else // "NONE"
1123    return NSUTF8StringEncoding;
1124}
1125#define KCODE_NSSTRENCODING kcode_to_nsencoding(rb_get_kcode())
1126
1127id
1128rbstr_to_ocstr(VALUE obj)
1129{
1130  return [[[NSMutableString alloc] initWithData:[NSData dataWithBytes:RSTRING(obj)->ptr
1131			    			 length: RSTRING(obj)->len]
1132			    encoding:KCODE_NSSTRENCODING] autorelease];
1133}
1134
1135VALUE
1136ocstr_to_rbstr(id ocstr)
1137{
1138  NSData * data = [(NSString *)ocstr dataUsingEncoding:KCODE_NSSTRENCODING
1139				     allowLossyConversion:YES];
1140  return rb_str_new ([data bytes], [data length]);
1141}
1142
1143static void
1144__decode_method_encoding_with_method_signature(NSMethodSignature *methodSignature, unsigned *argc, char **retval_type, char ***arg_types, BOOL strip_first_two_args)
1145{
1146  *argc = [methodSignature numberOfArguments];
1147  if (strip_first_two_args)
1148    *argc -= 2;
1149  *retval_type = strdup([methodSignature methodReturnType]);
1150  if (*argc > 0) {
1151    unsigned i;
1152    char **l_arg_types;
1153    l_arg_types = (char **)malloc(sizeof(char *) * *argc);
1154    for (i = 0; i < *argc; i++)
1155      l_arg_types[i] = strdup([methodSignature getArgumentTypeAtIndex:i + (strip_first_two_args ? 2 : 0)]);
1156    *arg_types = l_arg_types;
1157  }
1158  else {
1159    *arg_types = NULL;
1160  }
1161}
1162
1163static inline const char *
1164__iterate_until(const char *type, char end)
1165{
1166  char begin;
1167  unsigned nested;
1168
1169  begin = *type;
1170  nested = 0;
1171
1172  do {
1173    type++;
1174    if (*type == begin) {
1175      nested++;
1176    }
1177    else if (*type == end) {
1178      if (nested == 0)
1179        return type;
1180      nested--;
1181    }
1182  }
1183  while (YES);
1184
1185  return NULL;
1186}
1187
1188BOOL
1189is_id_ptr (const char *type)
1190{
1191  if (*type != _C_PTR)
1192    return NO;
1193
1194  type++;
1195  type = encoding_skip_to_first_type(type);
1196
1197  return *type == _C_ID;
1198}
1199
1200BOOL
1201is_boxed_ptr (const char *type, struct bsBoxed **boxed)
1202{
1203  struct bsBoxed *b;
1204
1205  if (*type != _C_PTR)
1206    return NO;
1207
1208  type++;
1209
1210  b = find_bs_boxed_by_encoding(type);
1211  if (b != NULL) {
1212    if (boxed != NULL)
1213      *boxed = b;
1214    return YES;
1215  }
1216
1217  return NO;
1218}
1219
1220const char *
1221encoding_skip_to_first_type(const char *type)
1222{
1223  while (YES) {
1224    switch (*type) {
1225      case _C_CONST:
1226      case _C_PTR:
1227      case 'O': // bycopy
1228      case 'n': // in
1229      case 'o': // out
1230      case 'N': // inout
1231      case 'V': // oneway
1232        type++;
1233        break;
1234
1235      default:
1236        return type;
1237    }
1238  }
1239  return NULL;
1240}
1241
1242const char *
1243encoding_skip_qualifiers(const char *type)
1244{
1245  while (YES) {
1246    switch (*type) {
1247      case _C_CONST:
1248      case 'O': // bycopy
1249      case 'n': // in
1250      case 'o': // out
1251      case 'N': // inout
1252      case 'V': // oneway
1253        type++;
1254        break;
1255
1256      default:
1257        return type;
1258    }
1259  }
1260  return NULL;
1261}
1262
1263static const char *
1264__get_first_encoding(const char *type, char *buf, size_t buf_len)
1265{
1266  const char *orig_type;
1267  const char *p;
1268
1269  orig_type = type;
1270
1271  type = encoding_skip_to_first_type(type);
1272
1273  switch (*type) {
1274    case '\0':
1275      return NULL;
1276    case _C_ARY_B:
1277      type = __iterate_until(type, _C_ARY_E);
1278      break;
1279    case _C_STRUCT_B:
1280      type = __iterate_until(type, _C_STRUCT_E);
1281      break;
1282    case _C_UNION_B:
1283      type = __iterate_until(type, _C_UNION_E);
1284      break;
1285  }
1286
1287  type++;
1288  p = type;
1289  while (*p >= '0' && *p <= '9') { p++; }
1290
1291  if (buf != NULL) {
1292    size_t len = (long)(type - orig_type);
1293    assert(len < buf_len);
1294    strncpy(buf, orig_type, len);
1295    buf[len] = '\0';
1296  }
1297
1298  return p;
1299}
1300
1301// 10.4 or lower, use NSMethodSignature.
1302// Otherwise, use the Objective-C runtime API, which is faster and more reliable with structures encoding.
1303void
1304decode_method_encoding(const char *encoding, NSMethodSignature *methodSignature, unsigned *argc, char **retval_type, char ***arg_types, BOOL strip_first_two_args)
1305{
1306  assert(encoding != NULL || methodSignature != nil);
1307
1308  if (encoding == NULL) {
1309    DATACONV_LOG("decoding method encoding using method signature %p", methodSignature);
1310    __decode_method_encoding_with_method_signature(methodSignature, argc, retval_type, arg_types, strip_first_two_args);
1311  }
1312  else {
1313    char buf[1024];
1314
1315    DATACONV_LOG("decoding method encoding '%s' manually", encoding);
1316    encoding = __get_first_encoding(encoding, buf, sizeof buf);
1317    DATACONV_LOG("retval -> %s", buf);
1318    *retval_type = strdup(buf);
1319    if (strip_first_two_args) {
1320      DATACONV_LOG("skipping first two args");
1321      encoding = __get_first_encoding(encoding, NULL, 0);
1322      encoding = __get_first_encoding(encoding, NULL, 0);
1323    }
1324    *argc = 0;
1325    // Do a first pass to know the argc
1326    if (encoding != NULL) {
1327      const char *p = encoding;
1328      while ((p = __get_first_encoding(p, NULL, 0)) != NULL) { (*argc)++; }
1329    }
1330    DATACONV_LOG("argc -> %d", *argc);
1331    if (*argc > 0) {
1332      unsigned i;
1333      char **p;
1334      i = 0;
1335      p = (char **)malloc(sizeof(char *) * (*argc));
1336      while ((encoding = __get_first_encoding(encoding, buf, sizeof buf)) != NULL) {
1337        DATACONV_LOG("arg[%d] -> %s", i, buf);
1338        p[i++] = strdup(buf);
1339      }
1340      *arg_types = p;
1341    }
1342    else {
1343      *arg_types = NULL;
1344    }
1345  }
1346}
1347
1348void
1349set_octypes_for_format_str (char **octypes, unsigned len, char *format_str)
1350{
1351  unsigned i, j, format_str_len;
1352
1353  // We cannot display this log because the given `format_str' format string will be evaluated within
1354  // the output message. Fortunately, Ruby format string APIs do not do that.
1355  //DATACONV_LOG("decoding format string `%s' types for %d argument(s)", format_str, len);
1356
1357  format_str_len = strlen(format_str);
1358  i = j = 0;
1359
1360  while (i < format_str_len) {
1361    if (format_str[i++] != '%')
1362      continue;
1363    if (i < format_str_len && format_str[i] == '%') {
1364      i++;
1365      continue;
1366    }
1367    while (i < format_str_len) {
1368      char *type = NULL;
1369      switch (format_str[i++]) {
1370        case 'd':
1371        case 'i':
1372        case 'o':
1373        case 'u':
1374        case 'x':
1375        case 'X':
1376        case 'c':
1377        case 'C':
1378          type = "i"; // _C_INT;
1379          break;
1380
1381        case 'D':
1382        case 'O':
1383        case 'U':
1384          type = "l"; // _C_LNG;
1385          break;
1386
1387        case 'f':
1388        case 'F':
1389        case 'e':
1390        case 'E':
1391        case 'g':
1392        case 'G':
1393        case 'a':
1394        case 'A':
1395          type = "d"; // _C_DBL;
1396          break;
1397
1398        case 's':
1399        case 'S':
1400          type = "*"; // _C_CHARPTR;
1401          break;
1402
1403        case 'p':
1404          type = "^"; // _C_PTR;
1405          break;
1406
1407        case '@':
1408          type = "@"; // _C_ID;
1409          break;
1410      }
1411
1412      if (type != NULL) {
1413        DATACONV_LOG("found format string token #%d of type '%s'", j, type);
1414
1415        if (len == 0 || j >= len)
1416          rb_raise(rb_eArgError, "Too much tokens in the format string `%s' for the given %d argument(s)", format_str, len);
1417
1418        octypes[j++] = type;
1419
1420        break;
1421      }
1422    }
1423  }
1424  for (; j < len; j++)
1425    octypes[j] = "@"; // _C_ID;
1426}
1427