1//
2//  gravity_objc.c
3//  GravityObjC
4//
5//  Created by Marco Bambini on 07/02/21.
6//
7
8#import <Foundation/Foundation.h>
9#import <objc/runtime.h>
10
11#import "gravity_macros.h"
12#import "gravity_utils.h"
13#import "gravity_core.h"
14#import "gravity_hash.h"
15#import "gravity_objc.h"
16
17// MARK: - Macros -
18
19#define GRAVITY_BRIDGE_DEGUB            0
20#define GRAVITY_BRIDGE_DEGUB_XDATA      0
21#define GRAVITY_BRIDGE_DEBUG_MEMORY     0
22#define GRAVITY_BRIDGE_RETAIN           1
23
24#if GRAVITY_BRIDGE_RETAIN
25#define RETAIN_OBJC_VALUE(v)            (void *)CFBridgingRetain(v)
26#define FREE_OBJC_VALUE(v)              CFBridgingRelease((CFTypeRef)v)
27#else
28#define RETAIN_OBJC_VALUE(v)            ((__bridge void*)v)
29#define FREE_OBJC_VALUE(v)
30#endif
31
32#if GRAVITY_BRIDGE_DEGUB
33#define DEBUG_BRIDGE(...)               NSLog(__VA_ARGS__)
34#else
35#define DEBUG_BRIDGE(...)
36#endif
37
38#if GRAVITY_BRIDGE_DEGUB_XDATA
39#define DEBUG_XDATA(...)                NSLog(__VA_ARGS__)
40#else
41#define DEBUG_XDATA(...)
42#endif
43
44#define BRIDGE_NAME                     "ObjC"
45#define BRIDGE_LOAD                     "register"
46#define BRIDGE_EXECUTE                  "exec"          // MUST BE equal to GRAVITY_INTERNAL_EXEC_NAME
47
48#define RETURN_VALUE(_v,_i)             do {gravity_vm_setslot(vm, _v, _i); return true;} while(0)
49#define RETURN_NOVALUE()                return true
50#define RETURN_ERROR(...)               do {                                                                        \
51                                            char buffer[4096];                                                        \
52                                            snprintf(buffer, sizeof(buffer), __VA_ARGS__);                            \
53                                            gravity_fiber_seterror(gravity_vm_fiber(vm), (const char *) buffer);    \
54                                            return false;                                                            \
55                                        } while(0)
56
57#define CHECK_INT(_v)                   (VALUE_ISA_INT(_v) || VALUE_ISA_BOOL(_v) || VALUE_ISA_NULL(_v))
58#define CHECK_FLOAT(_v)                 (VALUE_ISA_FLOAT(_v))
59#define CHECK_NUMBER(_v)                (CHECK_INT(_v) || CHECK_FLOAT(_v))
60#define CONVERT_NUMBER(_v)              VALUE_ISA_FLOAT(_v) ? _v.f : _v.n
61#define SANITY_CHECK_VALUE(_v)          if (VALUE_ISA_NULL(_v) || (VALUE_ISA_NOTVALID(_v))) return nil;
62
63#define RETURN_NIL_ON_NULL              1
64
65// MARK: - Common native ObjC type -
66
67// opaque data types
68typedef struct objc_bridge_var_t objc_bridge_var_t;
69typedef struct objc_bridge_func_t objc_bridge_func_t;
70
71typedef NS_ENUM(uint32_t, objc_bridge_type) {
72    OBJC_BRIDGE_TYPE_UNKNOWN        =    0,             // unhandled case
73
74    // basic C types: https://en.wikipedia.org/wiki/C_data_types
75    OBJC_BRIDGE_TYPE_VOID           =    1,             // no return type
76    OBJC_BRIDGE_TYPE_INT8           =    2,             // char, signed char
77    OBJC_BRIDGE_TYPE_INT16          =    3,             // short, short int, signed short, signed short int
78    OBJC_BRIDGE_TYPE_INT32          =    4,             // int, long, long int, signed long, signed long int
79    OBJC_BRIDGE_TYPE_INT64          =    5,             // long long, long long int, signed long long, signed long long int
80    OBJC_BRIDGE_TYPE_UINT8          =    6,             // unsigned char
81    OBJC_BRIDGE_TYPE_UINT16         =    7,             // unsigned short, unsigned short int
82    OBJC_BRIDGE_TYPE_UINT32         =    8,             // unsigned int, unsigned long, unsigned long int
83    OBJC_BRIDGE_TYPE_UINT64         =    9,             // unsigned long long, unsigned long long int
84    OBJC_BRIDGE_TYPE_FLOAT          =    10,            // float
85    OBJC_BRIDGE_TYPE_DOUBLE         =    11,            // double
86    OBJC_BRIDGE_TYPE_LDOUBLE        =    12,            // long double (unused)
87    OBJC_BRIDGE_TYPE_BOOL           =    13,            // bool, boolean, Boolean, BOOL
88    OBJC_BRIDGE_TYPE_VPTR           =    14,            // void*
89    OBJC_BRIDGE_TYPE_CPTR           =    15,            // char*
90
91    // ObjC basic types
92    OBJC_BRIDGE_TYPE_INIT           =    16,            // implicit return value of init
93    OBJC_BRIDGE_TYPE_ID             =    17,            // for OBJC_BRIDGE_TYPE_ID it is object responsibility to sanity check it
94    OBJC_BRIDGE_TYPE_CLASS          =    18,            // UNUSED
95    OBJC_BRIDGE_TYPE_SEL            =    19,            // UNUSED
96    OBJC_BRIDGE_TYPE_NSINTEGER      =    20,            // on 32-bit systems, these are defined to be 32-bit signed/unsigned,
97    OBJC_BRIDGE_TYPE_NSUINTEGER     =    21,            // and on 64-bit systems, they are 64-bit integers.
98    OBJC_BRIDGE_TYPE_NSNUMBER       =    22,
99    OBJC_BRIDGE_TYPE_NSSTRING       =    23,
100    OBJC_BRIDGE_TYPE_NSARRAY        =    24,
101    OBJC_BRIDGE_TYPE_NSDICTIONARY   =    25,
102    OBJC_BRIDGE_TYPE_NSDATE         =    26,
103    OBJC_BRIDGE_TYPE_NSDATA         =    27,
104
105    // Struct based types
106    OBJC_BRIDGE_TYPE_POINT          =    28,
107    OBJC_BRIDGE_TYPE_RECT           =    29,
108    OBJC_BRIDGE_TYPE_SIZE           =    30,
109    OBJC_BRIDGE_TYPE_EDGEINSETS     =    31,
110    OBJC_BRIDGE_TYPE_OFFSET         =    32,
111    OBJC_BRIDGE_TYPE_RANGE          =    33,
112
113    // Custom complex types
114    OBJC_BRIDGE_TYPE_IMAGE          =    34,
115    OBJC_BRIDGE_TYPE_COLOR          =    35,
116    OBJC_BRIDGE_TYPE_GRADIENT       =    36,
117    OBJC_BRIDGE_TYPE_MOVIE          =    37,
118    OBJC_BRIDGE_TYPE_SOUND          =    38,
119    OBJC_BRIDGE_TYPE_FONT           =    39,
120
121    OBJC_BRIDGE_TYPE_GRAVITY        =    40,             // pass through of native gravity objects
122    OBJC_BRIDGE_TYPE_CLOSURE        =    41,             // must be closure function (or a runtime error is generated)
123
124    OBJC_BRIDGE_TYPE_USER           =   100,             // MUST BE LATEST ENTRY means handled by the bridge conversion protocol
125} ;
126
127// MARK: - Internal Prototypes -
128
129static inline gravity_value_t convert_id2gravity (gravity_vm *vm, id value);
130static inline NSValue *convert_gravity2nsrangevalue (gravity_vm *vm, gravity_value_t value);
131static inline NSDictionary *convert_gravity2nsdictionary (gravity_vm *vm, gravity_value_t v);
132static inline NSArray* convert_gravity2nsarray (gravity_vm *vm, gravity_value_t v);
133
134bool bridge_initinstance (gravity_vm *vm, void *xdata, gravity_value_t ctx, gravity_instance_t *instance, gravity_value_t args[], int16_t nargs);
135bool bridge_setvalue (gravity_vm *vm, void *xdata, gravity_value_t target, const char *key, gravity_value_t value);
136bool bridge_getvalue (gravity_vm *vm, void *xdata, gravity_value_t target, const char *key, uint32_t rindex);
137bool bridge_setundef (gravity_vm *vm, void *xdata, gravity_value_t target, const char *key, gravity_value_t value);
138bool bridge_getundef (gravity_vm *vm, void *xdata, gravity_value_t target, const char *key, uint32_t rindex);
139bool bridge_execute  (gravity_vm *vm, void *xdata, gravity_value_t ctx, gravity_value_t args[], int16_t nargs, uint32_t rindex);
140void bridge_blacken  (gravity_vm *vm, void *xdata);
141void *bridge_duplicate (gravity_vm *vm, void *xdata);
142void bridge_free (gravity_vm *vm, gravity_object_t *obj);
143bool bridge_equals (gravity_vm *vm, void *obj_1, void *obj_2);
144void *bridge_clone (gravity_vm *vm, void *xdata);
145const char *bridge_string (gravity_vm *vm, void *xdata, uint32_t *len);
146
147// xdata
148objc_bridge_var_t *objc_bridge_var_new (objc_bridge_type type, const char *key);
149void objc_bridge_var_set_type (objc_bridge_var_t *xdata, objc_bridge_type type);
150void objc_bridge_var_free (objc_bridge_var_t *xdata);
151
152objc_bridge_func_t *objc_bridge_func_new (SEL selector, const char *name, uint16_t nargs, objc_bridge_type rettype);
153void objc_bridge_func_set_name (objc_bridge_func_t *xdata, const char *name);
154void objc_bridge_func_set_rettype (objc_bridge_func_t *xdata, objc_bridge_type rettype);
155void objc_bridge_func_set_argtype (objc_bridge_func_t *xdata, objc_bridge_type type, uint8_t index);
156void objc_bridge_func_set_argvalue (objc_bridge_func_t *xdata, id value, uint8_t index);
157void objc_bridge_func_free (objc_bridge_func_t *xdata);
158
159// conversion
160gravity_instance_t *bridge_instance_byclassname (gravity_vm *vm, id value, const char* name, uint32_t length);
161gravity_value_t bridge_objc2gravity (gravity_vm *vm, id obj, objc_bridge_type type);
162id bridge_gravity2objc (gravity_vm *vm, gravity_value_t value, objc_bridge_type type);
163
164const char *bridge_property_name(gravity_vm *vm, gravity_class_t *c, const char *key);
165objc_bridge_type bridge_property_type(gravity_vm *vm, gravity_class_t *c, const char *key);
166
167gravity_class_t *objc_class_load (gravity_vm *vm, const char *name);
168
169// MARK: - Internal Types -
170
171typedef NS_ENUM(uint8_t, objc_bridge_tag) {
172    OBJC_BRIDGE_TAG_METHOD      = 0,
173    OBJC_BRIDGE_TAG_PROPERTY    = 1
174};
175
176struct objc_bridge_func_t {
177    objc_bridge_tag     tag;
178    SEL                 selector;
179    void                *invocation;
180
181    // exposed name (for better error reporting)
182    const char          *name;
183
184    objc_bridge_type    rettype;
185    NSUInteger          retlength;
186
187    uint16_t            nargs;
188    objc_bridge_type    *argtype;
189    void                **argvalue;
190} objc_bridge_func_s;
191
192struct objc_bridge_var_t {
193    objc_bridge_tag     tag;
194    objc_bridge_type    type;
195    const char          *key;
196
197    // exposed name (for better error reporting)
198    const char          *name;
199} objc_bridge_var_s;
200
201// MARK: - Core functions -
202
203static const char *objc_build_function_name (const char *name, char *buffer, size_t bsize) {
204    size_t len = strlen(name);
205    if (len > bsize) len = bsize - 1;
206
207    bzero(buffer, bsize);
208    for (size_t i=0; i<len; ++i) {
209        if (name[i] == ':') break;
210        buffer[i] = name[i];
211    }
212
213    return buffer;
214}
215
216static objc_bridge_type objc_decode_type (const char *c) {
217    int idx = 0;
218
219    // take in account Objective-C annotations for method parameters and return values
220    // from: https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html
221    // explanation: http://stackoverflow.com/questions/5609564/objective-c-in-out-inout-byref-byval-and-so-on-what-are-they
222    switch (c[0]) {
223        case 'r': // const
224        case 'n': // in
225        case 'N': // inout
226        case 'o': // out
227        case 'O': // bycopy
228        case 'R': // byref
229        case 'V': // oneway
230            ++idx;
231    }
232
233    switch (c[idx]) {
234        case 'c': return OBJC_BRIDGE_TYPE_INT8;
235        case 'i': return OBJC_BRIDGE_TYPE_INT32;
236        case 's': return OBJC_BRIDGE_TYPE_INT16;
237        case 'l': return OBJC_BRIDGE_TYPE_INT32;
238        case 'q': return OBJC_BRIDGE_TYPE_INT64;
239        case 'C': return OBJC_BRIDGE_TYPE_UINT8;
240        case 'I': return OBJC_BRIDGE_TYPE_UINT32;
241        case 'S': return OBJC_BRIDGE_TYPE_UINT16;
242        case 'L': return OBJC_BRIDGE_TYPE_UINT32;
243        case 'Q': return OBJC_BRIDGE_TYPE_UINT64;
244        case 'f': return OBJC_BRIDGE_TYPE_FLOAT;
245        case 'd': return OBJC_BRIDGE_TYPE_DOUBLE;
246        case 'B': return OBJC_BRIDGE_TYPE_BOOL;
247        case 'v': return OBJC_BRIDGE_TYPE_VOID;
248        case '@': {
249            if (c[idx+1] == '"') {
250                // FOUNDATION
251                if (strncmp(&c[idx+2], "NSString", 8) == 0) return OBJC_BRIDGE_TYPE_NSSTRING;
252                if (strncmp(&c[idx+2], "NSMutableString", 15) == 0) return OBJC_BRIDGE_TYPE_NSSTRING;
253                if (strncmp(&c[idx+2], "NSNumber", 8) == 0) return OBJC_BRIDGE_TYPE_NSNUMBER;
254                if (strncmp(&c[idx+2], "NSDecimalNumber", 15) == 0) return OBJC_BRIDGE_TYPE_NSNUMBER;
255                if (strncmp(&c[idx+2], "NSArray", 7) == 0) return OBJC_BRIDGE_TYPE_NSARRAY;
256                if (strncmp(&c[idx+2], "NSMutableArray", 14) == 0) return OBJC_BRIDGE_TYPE_NSARRAY;
257                if (strncmp(&c[idx+2], "NSDictionary", 12) == 0) return OBJC_BRIDGE_TYPE_NSDICTIONARY;
258                if (strncmp(&c[idx+2], "NSMutableDictionary", 19) == 0) return OBJC_BRIDGE_TYPE_NSDICTIONARY;
259                if (strncmp(&c[idx+2], "NSData", 6) == 0) return OBJC_BRIDGE_TYPE_NSDATA;
260                if (strncmp(&c[idx+2], "NSMutableData", 13) == 0) return OBJC_BRIDGE_TYPE_NSDATA;
261                if (strncmp(&c[idx+2], "NSDate", 6) == 0) return OBJC_BRIDGE_TYPE_NSDATE;
262                // IMAGE
263                if (strncmp(&c[idx+2], "NSImage", 7) == 0) return OBJC_BRIDGE_TYPE_IMAGE;
264                if (strncmp(&c[idx+2], "UIImage", 7) == 0) return OBJC_BRIDGE_TYPE_IMAGE;
265                if (strncmp(&c[idx+2], "CREOImage", 9) == 0) return OBJC_BRIDGE_TYPE_IMAGE;
266                // COLOR
267                if (strncmp(&c[idx+2], "NSColor", 7) == 0) return OBJC_BRIDGE_TYPE_COLOR;
268                if (strncmp(&c[idx+2], "UIColor", 7) == 0) return OBJC_BRIDGE_TYPE_COLOR;
269                if (strncmp(&c[idx+2], "CREOColor", 9) == 0) return OBJC_BRIDGE_TYPE_COLOR;
270                // GRADIENT
271                if (strncmp(&c[idx+2], "NSGradient", 10) == 0) return OBJC_BRIDGE_TYPE_GRADIENT;
272                if (strncmp(&c[idx+2], "UIGradient", 10) == 0) return OBJC_BRIDGE_TYPE_GRADIENT;
273                if (strncmp(&c[idx+2], "CREOGradient", 12) == 0) return OBJC_BRIDGE_TYPE_GRADIENT;
274                // FONT
275                if (strncmp(&c[idx+2], "NSFont", 6) == 0) return OBJC_BRIDGE_TYPE_FONT;
276                if (strncmp(&c[idx+2], "UIFont", 6) == 0) return OBJC_BRIDGE_TYPE_FONT;
277                if (strncmp(&c[idx+2], "CREOFont", 8) == 0) return OBJC_BRIDGE_TYPE_FONT;
278                // SOUND
279                if (strncmp(&c[idx+2], "NSSound", 7) == 0) return OBJC_BRIDGE_TYPE_SOUND;
280                if (strncmp(&c[idx+2], "UISound", 7) == 0) return OBJC_BRIDGE_TYPE_SOUND;
281                if (strncmp(&c[idx+2], "CREOSound", 9) == 0) return OBJC_BRIDGE_TYPE_SOUND;
282                // MOVIE
283                if (strncmp(&c[idx+2], "CREOMovie", 9) == 0) return OBJC_BRIDGE_TYPE_MOVIE;
284                // RECT
285                if (strncmp(&c[idx+2], "CREORect", 8) == 0) return OBJC_BRIDGE_TYPE_RECT;
286                // POINT
287                if (strncmp(&c[idx+2], "CREOPoint", 9) == 0) return OBJC_BRIDGE_TYPE_POINT;
288                // SIZE
289                if (strncmp(&c[idx+2], "CREOSize", 8) == 0) return OBJC_BRIDGE_TYPE_SIZE;
290
291            } return OBJC_BRIDGE_TYPE_ID;
292        }
293        case '{':
294            if (c[idx+1] == '"') {
295//                if (strncmp(&c[idx+2], "NSPoint", 7) == 0) return OBJC_BRIDGE_TYPE_POINT;
296//                if (strncmp(&c[idx+2], "CGPoint", 7) == 0) return OBJC_BRIDGE_TYPE_POINT;
297//
298//                if (strncmp(&c[idx+2], "NSRect", 6) == 0) return OBJC_BRIDGE_TYPE_RECT;
299//                if (strncmp(&c[idx+2], "CGRect", 6) == 0) return OBJC_BRIDGE_TYPE_RECT;
300//
301//                if (strncmp(&c[idx+2], "NSSize", 6) == 0) return OBJC_BRIDGE_TYPE_SIZE;
302//                if (strncmp(&c[idx+2], "CGSize", 6) == 0) return OBJC_BRIDGE_TYPE_SIZE;
303//
304//                if (strncmp(&c[idx+2], "NSEdgeInsets", 12) == 0) return OBJC_BRIDGE_TYPE_EDGEINSETS;
305//                if (strncmp(&c[idx+2], "UIEdgeInsets", 12) == 0) return OBJC_BRIDGE_TYPE_EDGEINSETS;
306//
307//                if (strncmp(&c[idx+2], "UIOffset", 8) == 0) return OBJC_BRIDGE_TYPE_OFFSET;
308//
309//                if (strncmp(&c[idx+2], "NSRange", 7) == 0) return OBJC_BRIDGE_TYPE_RANGE;
310            } return OBJC_BRIDGE_TYPE_UNKNOWN;
311
312        case '*': return OBJC_BRIDGE_TYPE_CPTR;
313        case '#': return OBJC_BRIDGE_TYPE_CLASS;
314        case ':': return OBJC_BRIDGE_TYPE_SEL;
315        case '^': return OBJC_BRIDGE_TYPE_VPTR;
316        case '?': return OBJC_BRIDGE_TYPE_UNKNOWN;
317        //case '[': return OBJC_BRIDGE_TYPE_ARRAY;
318        //case '(': return OBJC_BRIDGE_TYPE_UNION;
319        //case 'b': return OBJC_BRIDGE_TYPE_BIT;
320    }
321
322    return OBJC_BRIDGE_TYPE_UNKNOWN;
323}
324
325static objc_bridge_type objc_decode_attributes (const char *attributes, bool *readonly, NSMutableDictionary *toskip) {
326    const char *p = attributes;
327
328    *readonly = false;
329    objc_bridge_type type = OBJC_BRIDGE_TYPE_UNKNOWN;
330
331    while (p[0]) {
332        switch (p[0]) {
333            case 'T': {
334                // property type
335                type = objc_decode_type(&p[1]);
336            } break;
337
338            case 'V': {
339                // property name
340            } break;
341
342            case 'R': {
343                // property readonly flag
344                *readonly = true;
345            } break;
346
347            case 'G':
348            case 'S': {
349                // property custom getter/setter names
350                // must be added to toSkip dictionary
351                NSString *customName = [NSString stringWithUTF8String:&p[1]];
352                customName = [customName substringToIndex:[customName length] - 1];
353                toskip[customName] = [NSNull null];
354            } break;
355        }
356
357        // skip next
358        while (p[0]) {
359            ++p; if (p[0] == ',') {++p; break;}
360        }
361    }
362
363    return type;
364}
365
366// for some strange reasons some properties are reported as methods for example UIView alpha
367static BOOL objc_check_fake_method (Class native_class, gravity_class_t *c, NSString *name, Method m, NSMutableDictionary *toskip) {
368    // check if this method is not really a method but a property
369    // for example UIView.h defines alpha as a CGFloat property
370    // but runtime reports alpha as a pair of methods (a getter and a setter)
371
372    // skip init cases
373    if ([name hasPrefix:@"init"]) return NO;
374
375    BOOL isFake = NO;
376    NSString *getterName = NULL;
377    NSString *setterName = NULL;
378    Method     getter = NULL;
379
380    if ([name hasPrefix:@"set"]) {
381        setterName = name;
382        NSString *temp = [name substringFromIndex:3];
383        NSString *firstChar = [[temp substringToIndex:1] lowercaseString];
384        getterName = [firstChar stringByAppendingString:[temp substringFromIndex:1]];
385        getterName = [getterName substringToIndex:[getterName length] - 1];
386        getter = class_getInstanceMethod(native_class, NSSelectorFromString(setterName));
387        isFake = (getter != nil);
388    } else {
389        getter = m;
390        getterName = name;
391        NSString *firstChar = [[name substringToIndex:1] uppercaseString];
392        setterName = [firstChar stringByAppendingString:[name substringFromIndex:1]];
393        setterName = [NSString stringWithFormat:@"set%@:", setterName];
394        Method setter = class_getInstanceMethod(native_class, NSSelectorFromString(setterName));
395        isFake = (setter != nil);
396    }
397    if (!isFake) return NO;
398
399    // so it seems a fake method
400
401    // check number of arguments (it is a getter so they must be 2, self, _CMD)
402    unsigned int nparams = method_getNumberOfArguments(getter);
403    if (nparams != 2) return NO;
404
405    // check return type
406    char buffer[1024];
407    method_getReturnType(getter, buffer, sizeof(buffer));
408    objc_bridge_type type = objc_decode_type(buffer);
409
410    // a getter that returns a void cannot be a property
411    if (type == OBJC_BRIDGE_TYPE_VOID) return NO;
412
413    // let's convert it to a property using the getter return value
414    // create gravity property and bind it to the class
415    const char *property_name = [getterName UTF8String];
416    objc_bridge_var_t *xdata = objc_bridge_var_new(type, NULL);
417
418    bool readonly = false;
419    gravity_closure_t *fget = gravity_closure_new(NULL, gravity_function_new_bridged(NULL, NULL, (void *)xdata));
420    gravity_closure_t *fset = (readonly) ? NULL : fget;
421    gravity_closure_t *closure = gravity_closure_new(NULL, gravity_function_new_special(NULL, NULL, GRAVITY_BRIDGE_INDEX, fget, fset));
422    gravity_class_bind(c, property_name, VALUE_FROM_OBJECT(closure));
423
424    // set names to skip for next loops
425    toskip[getterName] = [NSNull null];
426    toskip[setterName] = [NSNull null];
427
428    return YES;
429}
430
431static void objc_class_scan (gravity_vm* vm, Class native_class, gravity_class_t *c) {
432    #pragma unused(vm)
433
434    // setup a toSkip dictionary in order to not process custom getter and setter
435    NSMutableDictionary *toskip = [[NSMutableDictionary alloc] init];
436
437    DEBUG_BRIDGE(@"Scanning class: %@", NSStringFromClass(native_class));
438
439    // process properties
440    unsigned int n = 0;
441    objc_property_t *plist = class_copyPropertyList(native_class, &n);
442    for (unsigned int i=0; i<n; ++i) {
443        const char *name = property_getName(plist[i]);
444        const char *attributes = property_getAttributes(plist[i]);
445
446        // reserved internal objc properties to skip
447        if (name[0] == '.') continue;
448        if (name[0] == '_') continue;
449
450        // since it is a property we need to skip getter and setter methods
451        // setup standard getter and setter names
452        NSString *propertyName = [NSString stringWithUTF8String:name];
453
454        // don't know why but UIView reports some properties twice
455        // so I need to check for duplicates here
456        if (toskip[propertyName]) continue;
457
458        // standard getter
459        toskip[propertyName] = [NSNull null];
460
461        // standard setter
462        NSString *firstChar = [propertyName substringToIndex:1];
463        NSString *standardSetter = [[firstChar uppercaseString] stringByAppendingString:[propertyName substringFromIndex:1]];
464        toskip[[NSString stringWithFormat:@"set%@:", standardSetter]] = [NSNull null];
465
466        DEBUG_BRIDGE(@"Property %d/%d: %@", i, n, propertyName);
467
468        bool readonly;
469        objc_bridge_type type = objc_decode_attributes(attributes, &readonly, toskip);
470        objc_bridge_var_t *xdata = objc_bridge_var_new(type, NULL);
471
472        gravity_closure_t *getter = gravity_closure_new(NULL, gravity_function_new_bridged(NULL, NULL, (void *)xdata));
473        gravity_closure_t *setter = (readonly) ? NULL : getter;
474        gravity_closure_t *closure = gravity_closure_new(NULL, gravity_function_new_special(NULL, NULL, GRAVITY_BRIDGE_INDEX, getter, setter));
475        gravity_class_bind(c, name, VALUE_FROM_OBJECT(closure));
476    }
477    if (plist) free(plist);
478
479    // process methods
480    Method *mlist = class_copyMethodList(native_class, &n);
481    for (unsigned int i=0; i<n; ++i) {
482        char buffer[1024];
483        SEL selector = method_getName(mlist[i]);
484        const char *selname = sel_getName(selector);
485        unsigned int nparams = method_getNumberOfArguments(mlist[i]);
486
487        // reserved internal objc methods to skip
488        if (selname[0] == '.') continue;
489        if (selname[0] == '_') continue;
490
491        // check if method is a getter/setter
492        NSString *methodName = [NSString stringWithUTF8String:selname];
493        if (toskip[methodName]) continue;
494
495        // for some strange reasons some properties are reported as methods for example UIView alpha
496        if (objc_check_fake_method(native_class, c, methodName, mlist[i], toskip)) continue;
497
498        // allocate xdata
499        // nparams-2 because there are two implicit parameters (self and _cmd)
500        method_getReturnType(mlist[i], buffer, sizeof(buffer));
501        objc_bridge_func_t *m = objc_bridge_func_new(selector, NULL, nparams-2, objc_decode_type(buffer));
502
503        // get and decode arguments
504        for (unsigned int j=2; j<nparams; ++j) {
505            method_getArgumentType(mlist[i], j, buffer, sizeof(buffer));
506            m->argtype[j-2] = objc_decode_type(buffer);
507        }
508
509        // from exposeName:withName: to exposeName
510        const char *name = objc_build_function_name(selname, buffer, sizeof(buffer));
511
512        // check for special init methods
513        if (string_casencmp(name, "init", 4) == 0) {
514            if (nparams == 2) snprintf(buffer, sizeof(buffer), "%s", CLASS_INTERNAL_INIT_NAME);
515            else snprintf(buffer, sizeof(buffer), "%s%d", CLASS_INTERNAL_INIT_NAME, nparams-2);
516            name = buffer;
517            m->rettype = OBJC_BRIDGE_TYPE_INIT;
518        }
519
520        // bind bridged function to class
521        gravity_closure_t *closure = gravity_closure_new(NULL, gravity_function_new_bridged(NULL, NULL, (void *)m));
522        gravity_class_bind(c, name, VALUE_FROM_OBJECT(closure));
523
524        DEBUG_BRIDGE(@"Method %d/%d: %s", i, n, name);
525    }
526    if (mlist) free(mlist);
527}
528
529// dynamically load an objc class specified by name into vm
530// class is parsed only if not yet loaded into vm
531gravity_class_t *objc_class_load (gravity_vm *vm, const char *name) {
532
533    // check if class is already loaded into VM
534    gravity_value_t v = gravity_vm_getvalue(vm, name, (uint32_t)strlen(name));
535    if (VALUE_ISA_VALID(v)) return VALUE_AS_CLASS(v);
536
537    // lookup class into objc runtime (sanity check)
538    Class native_class = objc_getClass(name);
539    if (native_class == NULL) {
540        gravity_vm_seterror(vm, "Unable to find class name %s in Objective-C runtime", name);
541        return NULL;
542    }
543
544    // recursively scan class hierarchy
545    gravity_class_t *result = NULL;
546    gravity_class_t *base = NULL;
547    while (native_class) {
548        DEBUG_BRIDGE(@"Loading class %s", name);
549
550        // create gravity class
551        gravity_class_t *c = gravity_class_new_pair(vm, name, NULL, 0, 0);
552        gravity_class_setxdata(c, RETAIN_OBJC_VALUE(native_class));
553
554        // instance
555        objc_class_scan(vm, native_class, c);
556
557        // meta
558        objc_class_scan(vm, objc_getMetaClass(name), c->objclass);
559
560        // classes loaded from bridge are globally availables
561        gravity_vm_setvalue(vm, name, VALUE_FROM_OBJECT(c));
562
563        // c is overwritten in the loop, so save the first class and returns it
564        if (!result) result = c;
565
566        // set super class
567        if (base) gravity_class_setsuper(base, c);
568
569        // check for superclass
570        native_class = class_getSuperclass(native_class);
571        if (!native_class) break;
572
573        if (native_class == [NSObject class]) break;
574
575        // check if superclass is already loaded into gravity
576        name = class_getName(native_class);
577        gravity_value_t _v = gravity_vm_getvalue(vm, name, (uint32_t)strlen(name));
578        if (VALUE_ISA_VALID(_v)) {
579            DEBUG_BRIDGE(@"Loading class %s (already found in hierarchy)", name);
580            // super class is already registered in gravity runtime
581            // so set c super and stop loop
582            gravity_class_setsuper(c, (gravity_class_t *)VALUE_AS_OBJECT(_v));
583            break;
584        }
585
586        // save base to set super
587        base = c;
588    }
589
590    return result;
591}
592
593static bool objc_load (gravity_vm* vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
594    #pragma unused(nargs)
595
596    const char *nativeName = VALUE_AS_CSTRING(args[1]);
597    gravity_gc_setenabled(vm, false);
598    gravity_class_t *c = objc_class_load(vm, nativeName);
599    gravity_gc_setenabled(vm, true);
600    if (!c) return false;
601
602    RETURN_VALUE(VALUE_FROM_OBJECT(c), rindex);
603    return true;
604}
605
606static bool objc_exec (gravity_vm* vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
607    // sanity check parameters
608    if (!VALUE_ISA_INSTANCE(args[1])) RETURN_ERROR("objc.exec 1st parameter must be an instance");
609    if (!VALUE_ISA_STRING(args[2])) RETURN_ERROR("objc.exec 2nd parameter must be a string");
610
611    // unbox parameters
612    gravity_instance_t *instance = VALUE_AS_INSTANCE(args[1]);
613    gravity_string_t *message = VALUE_AS_STRING(args[2]);
614
615    // sanity check objc
616    if (!instance->xdata) RETURN_ERROR("objc.exec 1st parameter must be an objc object");
617
618    // sanity check selector
619    SEL selector = NSSelectorFromString(@(message->s));
620    id obj = (__bridge id)instance->xdata;
621    if (![obj respondsToSelector:selector]) {
622        RETURN_ERROR("objc object does not respond to given selector");
623    }
624
625    // retrieve method from objc runtime
626    Method m = class_getInstanceMethod([obj class], selector);
627    if (!m) RETURN_ERROR("objc object does not respond to given selector");
628
629    // decode method
630    char            buffer[1024];
631    unsigned int    nparams = method_getNumberOfArguments(m);
632
633    // allocate xdata
634    // nparams-2 because there are two implicit parameters (self and _cmd)
635    method_getReturnType(m, buffer, sizeof(buffer));
636    objc_bridge_func_t *data = objc_bridge_func_new(selector, NULL, nparams-2, objc_decode_type(buffer));
637
638    // get and decode arguments
639    for (unsigned int j=2; j<nparams; ++j) {
640        method_getArgumentType(m, j, buffer, sizeof(buffer));
641        data->argtype[j-2] = objc_decode_type(buffer);
642    }
643
644    // execute objc selector
645    args[2] = args[1];
646    bool result = bridge_execute(vm, (void *)data, args[1], &args[2], nargs-2, rindex);
647
648    // free temp data
649    objc_bridge_func_free(data);
650
651    return result;
652}
653
654// MARK: - Public functions -
655
656void objc_register (gravity_vm *vm) {
657    // register bridge delegate into VM
658    gravity_delegate_t *delegate = gravity_vm_delegate(vm);
659    delegate->bridge_initinstance = bridge_initinstance;
660    delegate->bridge_getvalue = bridge_getvalue;
661    delegate->bridge_setvalue = bridge_setvalue;
662    delegate->bridge_getundef = bridge_getundef;
663    delegate->bridge_setundef = bridge_setundef;
664    delegate->bridge_execute = bridge_execute;
665    delegate->bridge_blacken = bridge_blacken;
666    delegate->bridge_equals = bridge_equals;
667    delegate->bridge_string = bridge_string;
668    delegate->bridge_free = bridge_free;
669    delegate->bridge_clone = bridge_clone;
670
671    // register objc.loadClass method
672    gravity_gc_setenabled(vm, false);
673
674    // register objc class
675    gravity_class_t *c = gravity_class_new_pair(vm, BRIDGE_NAME, NULL, 0, 0);
676
677    // register class_load
678    gravity_closure_t *closure1 = gravity_closure_new(vm, gravity_function_new_internal(vm, NULL, objc_load, 0));
679    gravity_class_bind(gravity_class_get_meta(c), BRIDGE_LOAD, VALUE_FROM_OBJECT(closure1));
680
681    // register exec
682    gravity_closure_t *closure2 = gravity_closure_new(vm, gravity_function_new_internal(vm, NULL, objc_exec, 0));
683    gravity_class_bind(gravity_class_get_meta(c), BRIDGE_EXECUTE, VALUE_FROM_OBJECT(closure2));
684
685    gravity_vm_setvalue(vm, BRIDGE_NAME, VALUE_FROM_OBJECT(c));
686    gravity_gc_setenabled(vm, true);
687}
688
689// MARK: - xdata Management -
690
691objc_bridge_var_t *objc_bridge_var_new (objc_bridge_type type, const char *key) {
692    objc_bridge_var_t *xdata = (objc_bridge_var_t *) mem_alloc(NULL, sizeof(objc_bridge_var_t));
693
694    xdata->tag = OBJC_BRIDGE_TAG_PROPERTY;
695    xdata->type = type;
696    xdata->key = key; // objc real property name (if different from the exposed one)
697
698    return xdata;
699}
700
701void objc_bridge_var_set_type (objc_bridge_var_t *xdata, objc_bridge_type type) {
702    xdata->type = type;
703}
704
705void objc_bridge_var_free (objc_bridge_var_t *xdata) {
706    if (xdata->key) mem_free(xdata->key);
707    mem_free(xdata);
708}
709
710#pragma mark -
711
712objc_bridge_func_t *objc_bridge_func_new (SEL selector, const char *name, uint16_t nargs, objc_bridge_type rettype) {
713    objc_bridge_func_t *xdata = mem_alloc(NULL, sizeof(objc_bridge_func_t));
714
715    xdata->tag = OBJC_BRIDGE_TAG_METHOD;
716    xdata->selector = selector;
717    xdata->nargs = nargs;
718    xdata->rettype = rettype;
719    xdata->argtype = NULL;
720    xdata->argvalue = NULL;
721    xdata->name = (name) ? string_dup(name) : NULL;
722
723    if (nargs) xdata->argtype = (objc_bridge_type *)calloc(nargs, sizeof(objc_bridge_type));
724    return xdata;
725}
726
727void objc_bridge_func_set_name (objc_bridge_func_t *xdata, const char *name) {
728    xdata->name = (name) ? string_dup(name) : NULL;
729}
730
731const char *objc_bridge_get_exposed_name (objc_bridge_func_t *xdata) {
732    if (xdata->rettype == OBJC_BRIDGE_TYPE_INIT) return "init";
733    if (xdata->name) return xdata->name;
734    if (xdata->selector) return NSStringFromSelector(xdata->selector).UTF8String;
735    return "N/A";
736}
737
738void objc_bridge_func_set_rettype (objc_bridge_func_t *xdata, objc_bridge_type rettype) {
739    xdata->rettype = rettype;
740}
741
742void objc_bridge_func_set_argtype (objc_bridge_func_t *xdata, objc_bridge_type type, uint8_t index) {
743    assert(index < xdata->nargs);
744    xdata->argtype[index] = type;
745}
746
747void objc_bridge_func_set_argvalue (objc_bridge_func_t *xdata, id value, uint8_t index) {
748    assert(index < xdata->nargs);
749    if (!xdata->argvalue) xdata->argvalue = (void **)calloc(xdata->nargs, sizeof(void *));
750    xdata->argvalue[index] = (void *)CFBridgingRetain(value);
751}
752
753void objc_bridge_func_free (objc_bridge_func_t *xdata) {
754    if (xdata->invocation) CFBridgingRelease((CFTypeRef)xdata->invocation);
755    if (xdata->argtype) free(xdata->argtype);
756    if (xdata->name) mem_free(xdata->name);
757    if (xdata->argvalue) {
758        for (uint32_t i=0; i<xdata->nargs; ++i) {
759            if (xdata->argvalue[i]) CFBridgingRelease((CFTypeRef)xdata->argvalue[i]);
760        }
761        free(xdata->argvalue);
762    }
763    mem_free(xdata);
764}
765
766// MARK: - Gravity => ObjC -
767
768static inline id convert_gravity2id (gravity_vm *vm, gravity_value_t value) {
769    if (VALUE_ISA_INT(value)) return @(value.n);
770    if (VALUE_ISA_FLOAT(value)) return @(value.f);
771    if (VALUE_ISA_BOOL(value)) return [NSNumber numberWithBool:(value.n)];
772    if ((VALUE_ISA_NULL(value)) || (VALUE_ISA_UNDEFINED(value))) return nil;
773    if (VALUE_ISA_STRING(value)) return [NSString stringWithUTF8String:VALUE_AS_CSTRING(value)];
774    if (VALUE_ISA_RANGE(value)) return convert_gravity2nsrangevalue(vm, value);
775    if (VALUE_ISA_MAP(value)) return convert_gravity2nsdictionary(vm, value);
776    if (VALUE_ISA_LIST(value)) return convert_gravity2nsarray(vm, value);
777
778    if (!VALUE_ISA_INSTANCE(value)) return nil;
779    return (__bridge id)gravity_value_xdata(value);
780}
781
782static inline id convert_gravity2type (gravity_vm *vm, gravity_value_t value, objc_bridge_type type) {
783    id obj = convert_gravity2id(vm, value);
784    // can be nil
785    return obj;
786}
787
788static inline NSRange convert_gravity2nsrange (gravity_vm *vm, gravity_value_t value) {
789    #pragma unused(vm)
790    if (VALUE_ISA_RANGE(value)) {
791        gravity_range_t *r = VALUE_AS_RANGE(value);
792        return NSMakeRange((NSUInteger)r->from, (NSUInteger)r->to);
793    }
794    return NSMakeRange(0, 0);
795}
796
797static inline NSValue *convert_gravity2nsrangevalue (gravity_vm *vm, gravity_value_t value) {
798    NSRange range = convert_gravity2nsrange(vm, value);
799    return [NSValue valueWithRange:NSMakeRange(range.location, range.length)];
800}
801
802static void convert_nsdictionary_callback (gravity_hash_t *hashtable, gravity_value_t key, gravity_value_t value, void *data1, void *data2) {
803    #pragma unused (hashtable)
804    NSMutableDictionary *d = (__bridge NSMutableDictionary*)data1;
805    gravity_vm *vm = (gravity_vm *)data2;
806    d[@(VALUE_AS_CSTRING(key))] = convert_gravity2id(vm, value);
807}
808
809static inline NSArray* convert_gravity2nsarray (gravity_vm *vm, gravity_value_t v) {
810    #pragma unused(vm)
811
812    #if RETURN_NIL_ON_NULL
813    if (VALUE_ISA_NULL(v) || VALUE_ISA_UNDEFINED(v)) return nil;
814    #endif
815
816    NSMutableArray *r = [NSMutableArray array];
817    if (VALUE_ISA_LIST(v)) {
818        gravity_list_t *list = VALUE_AS_LIST(v);
819        for (uint32_t i=0; i<marray_size(list->array); ++i) {
820            id obj = convert_gravity2id(vm, marray_get(list->array, i));
821            if (obj) [r addObject:obj];
822        }
823    }
824    return r;
825}
826
827static inline NSDictionary *convert_gravity2nsdictionary (gravity_vm *vm, gravity_value_t v) {
828    #pragma unused(vm)
829
830    #if RETURN_NIL_ON_NULL
831    if (VALUE_ISA_NULL(v) || VALUE_ISA_UNDEFINED(v)) return nil;
832    #endif
833
834    NSMutableDictionary *d = [NSMutableDictionary dictionary];
835    if (VALUE_ISA_MAP(v)) {
836        gravity_map_t *map = VALUE_AS_MAP(v);
837        gravity_hash_iterate2(map->hash, convert_nsdictionary_callback, (__bridge void *)d, (void*)vm);
838    }
839    return d;
840}
841
842static inline id convert_gravity2obj (gravity_vm *vm, gravity_value_t value, objc_bridge_type type) {
843    #if RETURN_NIL_ON_NULL
844    if (VALUE_ISA_NULL(value) || VALUE_ISA_UNDEFINED(value)) {
845        // STRUCT BASED VALUE
846        return nil;
847    }
848    #endif
849
850    switch (type) {
851        case OBJC_BRIDGE_TYPE_UNKNOWN:
852        case OBJC_BRIDGE_TYPE_VOID:
853        case OBJC_BRIDGE_TYPE_VPTR:
854        case OBJC_BRIDGE_TYPE_CPTR:
855        case OBJC_BRIDGE_TYPE_CLASS:
856        case OBJC_BRIDGE_TYPE_SEL: {
857            NSLog(@"Unsupported conversion in gravity2obj");
858            return nil;
859        }
860
861        case OBJC_BRIDGE_TYPE_CLOSURE: {
862            return nil;
863        }
864
865        case OBJC_BRIDGE_TYPE_BOOL: {
866            gravity_value_t v = convert_value2bool(vm, value);
867            SANITY_CHECK_VALUE(v);
868            return @((BOOL)v.n);
869        };
870
871        case OBJC_BRIDGE_TYPE_INT8: {
872            gravity_value_t v = convert_value2int(vm, value);
873            SANITY_CHECK_VALUE(v);
874            return @((int8_t)v.n);
875        };
876
877        case OBJC_BRIDGE_TYPE_INT16: {
878            gravity_value_t v = convert_value2int(vm, value);
879            SANITY_CHECK_VALUE(v);
880            return @((int16_t)v.n);
881        };
882
883        case OBJC_BRIDGE_TYPE_INT32: {
884            gravity_value_t v = convert_value2int(vm, value);
885            SANITY_CHECK_VALUE(v);
886            return @((int32_t)v.n);
887        };
888
889        case OBJC_BRIDGE_TYPE_INT64: {
890            gravity_value_t v = convert_value2int(vm, value);
891            SANITY_CHECK_VALUE(v);
892            return @((int64_t)v.n);
893        };
894
895        case OBJC_BRIDGE_TYPE_UINT8: {
896            gravity_value_t v = convert_value2int(vm, value);
897            SANITY_CHECK_VALUE(v);
898            return @((uint8_t)v.n);
899        };
900
901        case OBJC_BRIDGE_TYPE_UINT16: {
902            gravity_value_t v = convert_value2int(vm, value);
903            SANITY_CHECK_VALUE(v);
904            return @((uint16_t)v.n);
905        };
906
907        case OBJC_BRIDGE_TYPE_UINT32: {
908            gravity_value_t v = convert_value2int(vm, value);
909            SANITY_CHECK_VALUE(v);
910            return @((uint32_t)v.n);
911        };
912
913        case OBJC_BRIDGE_TYPE_UINT64: {
914            gravity_value_t v = convert_value2int(vm, value);
915            SANITY_CHECK_VALUE(v);
916            return @((uint64_t)v.n);
917        };
918
919        case OBJC_BRIDGE_TYPE_FLOAT: {
920            gravity_value_t v = convert_value2float(vm, value);
921            SANITY_CHECK_VALUE(v);
922            return @((float)v.f);
923        };
924
925        case OBJC_BRIDGE_TYPE_LDOUBLE:
926        case OBJC_BRIDGE_TYPE_DOUBLE: {
927            gravity_value_t v = convert_value2float(vm, value);
928            SANITY_CHECK_VALUE(v);
929            return @((double)v.f);
930        };
931
932        case OBJC_BRIDGE_TYPE_NSINTEGER: {
933            gravity_value_t v = convert_value2int(vm, value);
934            SANITY_CHECK_VALUE(v);
935            return @((NSInteger)v.n);
936        };
937        case OBJC_BRIDGE_TYPE_NSUINTEGER: {
938            gravity_value_t v = convert_value2int(vm, value);
939            SANITY_CHECK_VALUE(v);
940            return @((NSUInteger)v.n);
941        };
942
943        case OBJC_BRIDGE_TYPE_NSNUMBER: {
944            if (VALUE_ISA_INT(value)) return @(value.n);
945            else if (VALUE_ISA_FLOAT(value)) return @(value.f);
946
947            gravity_value_t v = convert_value2int(vm, value);
948            SANITY_CHECK_VALUE(v);
949            return @(v.n);
950        };
951
952        case OBJC_BRIDGE_TYPE_NSSTRING: {
953            gravity_value_t v = convert_value2string(vm, value);
954            SANITY_CHECK_VALUE(v);
955            return [NSString stringWithUTF8String:VALUE_AS_CSTRING(v)];
956        }
957
958        case OBJC_BRIDGE_TYPE_NSARRAY:
959            return convert_gravity2nsarray(vm, value);
960
961        case OBJC_BRIDGE_TYPE_NSDICTIONARY:
962            return convert_gravity2nsdictionary(vm, value);
963
964        case OBJC_BRIDGE_TYPE_RANGE:
965            return convert_gravity2nsrangevalue(vm, value);
966
967        case OBJC_BRIDGE_TYPE_POINT:
968        case OBJC_BRIDGE_TYPE_RECT:
969        case OBJC_BRIDGE_TYPE_SIZE:
970        case OBJC_BRIDGE_TYPE_OFFSET:
971        case OBJC_BRIDGE_TYPE_EDGEINSETS:
972        case OBJC_BRIDGE_TYPE_FONT:
973        case OBJC_BRIDGE_TYPE_SOUND:
974        case OBJC_BRIDGE_TYPE_MOVIE:
975        case OBJC_BRIDGE_TYPE_GRADIENT:
976        case OBJC_BRIDGE_TYPE_NSDATE:
977        case OBJC_BRIDGE_TYPE_NSDATA:
978        case OBJC_BRIDGE_TYPE_IMAGE:
979        case OBJC_BRIDGE_TYPE_COLOR:
980            return convert_gravity2type(vm, value, type);
981
982        case OBJC_BRIDGE_TYPE_GRAVITY:
983        case OBJC_BRIDGE_TYPE_INIT:
984        case OBJC_BRIDGE_TYPE_ID: return convert_gravity2id(vm, value);
985
986        case OBJC_BRIDGE_TYPE_USER:
987            return convert_gravity2type(vm, value, OBJC_BRIDGE_TYPE_USER);
988    }
989
990    return convert_gravity2type(vm, value, type);
991}
992
993// MARK: - ObjC => Gravity -
994
995static inline gravity_value_t convert_nsnumber2gravity (gravity_vm *vm, NSNumber *value) {
996    const char *internal = [value objCType];
997    switch (internal[0]) {
998        case 'c': return bridge_objc2gravity(vm, value, OBJC_BRIDGE_TYPE_BOOL);
999        case 'i': return bridge_objc2gravity(vm, value, OBJC_BRIDGE_TYPE_INT32);
1000        case 's': return bridge_objc2gravity(vm, value, OBJC_BRIDGE_TYPE_INT16);
1001        case 'l': return bridge_objc2gravity(vm, value, OBJC_BRIDGE_TYPE_INT32);
1002        case 'q': return bridge_objc2gravity(vm, value, OBJC_BRIDGE_TYPE_INT64);
1003        case 'C': return bridge_objc2gravity(vm, value, OBJC_BRIDGE_TYPE_UINT8);
1004        case 'I': return bridge_objc2gravity(vm, value, OBJC_BRIDGE_TYPE_UINT32);
1005        case 'S': return bridge_objc2gravity(vm, value, OBJC_BRIDGE_TYPE_UINT16);
1006        case 'L': return bridge_objc2gravity(vm, value, OBJC_BRIDGE_TYPE_UINT32);
1007        case 'Q': return bridge_objc2gravity(vm, value, OBJC_BRIDGE_TYPE_UINT64);
1008        case 'f': return bridge_objc2gravity(vm, value, OBJC_BRIDGE_TYPE_FLOAT);
1009        case 'd': return bridge_objc2gravity(vm, value, OBJC_BRIDGE_TYPE_DOUBLE);
1010        case 'B': return bridge_objc2gravity(vm, value, OBJC_BRIDGE_TYPE_BOOL);
1011    }
1012    return VALUE_FROM_NULL;
1013}
1014
1015static inline gravity_value_t convert_nsstring2gravity (gravity_vm *vm, NSString *value) {
1016    return VALUE_FROM_STRING(vm, value.UTF8String, (uint32_t)[value lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
1017}
1018
1019static inline gravity_value_t convert_creo2gravity (gravity_vm *vm, id value, objc_bridge_type type, BOOL value_retained) {
1020    #ifdef CREO_PROJECT
1021    if ([value respondsToSelector:@selector(runtimeInstance)]) {
1022        // if the creo objects already has an associated gravity instance, return it
1023        CREORuntimeInstance *runtimeInstance = [(id<CREORuntimeInstanceProtocol>)value runtimeInstance];
1024        gravity_instance_t *instance = (gravity_instance_t *)runtimeInstance.instance;
1025        if (instance) return VALUE_FROM_OBJECT(instance);
1026    }
1027
1028    if ((!value) || ([value isKindOfClass:[NSNull class]])) return VALUE_FROM_NULL;
1029    if ([value isKindOfClass:[NSNumber class]]) return convert_nsnumber2gravity(vm, value);
1030    if ([value isKindOfClass:[NSString class]]) return convert_nsstring2gravity(vm, value);
1031
1032    id<CREORuntimeDelegate> delegate = (__bridge id<CREORuntimeDelegate>)(gravity_vm_getdata(vm));
1033    Class c = (type != OBJC_BRIDGE_TYPE_UNKNOWN) ? [delegate classByTag:type] : nil;
1034    if (!c) c = [value class];
1035
1036    NSString *name = [delegate classExposedNameByRealName:NSStringFromClass(c)];
1037    gravity_value_t v = gravity_vm_getvalue(vm, name.UTF8String, (uint32_t)name.length);
1038    if (!VALUE_ISA_CLASS(v)) return VALUE_FROM_NULL;
1039
1040    gravity_class_t *c2 = VALUE_AS_CLASS(v);
1041    gravity_instance_t *instance = gravity_instance_new(vm, c2);
1042    gravity_instance_setxdata(instance, (value_retained) ? (__bridge void *)value : RETAIN_OBJC_VALUE(value));
1043    set_runtime_instance(vm, value, instance);
1044    return VALUE_FROM_OBJECT(instance);
1045    #else
1046    #pragma unused (vm, value, type)
1047    return VALUE_FROM_NULL;
1048    #endif
1049}
1050
1051static inline gravity_value_t convert_nsarray2gravity (gravity_vm *vm, NSArray *r) {
1052    NSUInteger count = r.count;
1053    gravity_list_t *list = gravity_list_new(vm, (uint32_t)count);
1054    if (!list) return VALUE_FROM_NULL;
1055
1056    for (id obj in r) {
1057        gravity_value_t v = convert_id2gravity(vm, obj);
1058        marray_push(gravity_value_t, list->array, v);
1059    }
1060
1061    return VALUE_FROM_OBJECT(list);
1062}
1063
1064static inline gravity_value_t convert_nsdictionary2gravity (gravity_vm *vm, NSDictionary *d) {
1065    NSUInteger count = d.allKeys.count;
1066    gravity_map_t *map = gravity_map_new(vm, (uint32_t)count);
1067
1068    for (NSString *key in d.allKeys) {
1069        id obj = d[key];
1070        gravity_value_t v = bridge_objc2gravity(vm, obj, OBJC_BRIDGE_TYPE_ID);
1071        gravity_value_t k = VALUE_FROM_STRING(vm, key.UTF8String, (uint32_t)[key lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
1072        gravity_hash_insert(map->hash, k, v);
1073    }
1074
1075    return VALUE_FROM_OBJECT(map);
1076}
1077
1078static inline gravity_value_t convert_nsvalue2gravity (gravity_vm *vm, id obj, objc_bridge_type type) {
1079    // called ONLY when I am sure that obj isKindOfClass NSValue
1080    NSValue *value = (NSValue*)obj;
1081
1082    if (((type == OBJC_BRIDGE_TYPE_UNKNOWN) || (type == OBJC_BRIDGE_TYPE_RANGE)) && (strcmp(value.objCType, @encode(NSRange)) == 0)) {
1083        NSRange v = [value rangeValue];
1084        return VALUE_FROM_OBJECT(gravity_range_new(vm, v.location, v.length, true));
1085    }
1086
1087    #ifdef CREO_PROJECT
1088    Class c = nil;
1089
1090    if (((type == OBJC_BRIDGE_TYPE_UNKNOWN) || (type == OBJC_BRIDGE_TYPE_POINT)) && (strcmp(value.objCType, @encode(CGPoint)) == 0)) c = CREOPoint.class;
1091    else if (((type == OBJC_BRIDGE_TYPE_UNKNOWN) || (type == OBJC_BRIDGE_TYPE_RECT)) && (strcmp(value.objCType, @encode(CGRect)) == 0)) c = CREORect.class;
1092    else if (((type == OBJC_BRIDGE_TYPE_UNKNOWN) || (type == OBJC_BRIDGE_TYPE_SIZE)) && (strcmp(value.objCType, @encode(CGSize)) == 0)) c = CREOSize.class;
1093    else if (((type == OBJC_BRIDGE_TYPE_UNKNOWN) || (type == OBJC_BRIDGE_TYPE_OFFSET)) && (strcmp(value.objCType, @encode(UIOffset)) == 0)) c = CREOOffset.class;
1094    else if (((type == OBJC_BRIDGE_TYPE_UNKNOWN) || (type == OBJC_BRIDGE_TYPE_EDGEINSETS)) && (strcmp(value.objCType, @encode(UIEdgeInsets)) == 0)) c = CREOEdgeInsets.class;
1095
1096    if (c) {
1097        CREOStruct *creoObj = (CREOStruct *)[[c alloc] init];
1098        [creoObj setValue:value];
1099        return convert_creo2gravity(vm, creoObj, type, NO);
1100    }
1101    #endif
1102
1103    return VALUE_FROM_NULL;
1104}
1105
1106static inline gravity_value_t convert_id2gravity (gravity_vm *vm, id value) {
1107    // not sure if its a good idea to not trigger a crash in this case
1108    if (!value) return VALUE_FROM_NULL;
1109
1110    // NSNumber case
1111    if ([value isKindOfClass:[NSNumber class]]) {
1112        return convert_nsnumber2gravity(vm, value);
1113    }
1114
1115    // NSString case
1116    if ([value isKindOfClass:[NSString class]]) {
1117        return convert_nsstring2gravity(vm, value);
1118    }
1119
1120    // NSArray case
1121    if ([value isKindOfClass:[NSArray class]]) {
1122        return convert_nsarray2gravity(vm, value);
1123    }
1124
1125    // NSDictionary case
1126    if ([value isKindOfClass:[NSDictionary class]]) {
1127        return convert_nsdictionary2gravity(vm, value);
1128    }
1129
1130    // NSValue case
1131    if ([value isKindOfClass:[NSValue class]]) {
1132        return convert_nsvalue2gravity(vm, value, OBJC_BRIDGE_TYPE_UNKNOWN);
1133    }
1134
1135    // NSNull case
1136    if ([value isKindOfClass:[NSNull class]]) {
1137        return VALUE_FROM_NULL;
1138    }
1139
1140    return convert_creo2gravity(vm, value, OBJC_BRIDGE_TYPE_UNKNOWN, NO);
1141}
1142
1143// MARK: - Bridge Utils -
1144
1145id bridge_gravity2objc (gravity_vm *vm, gravity_value_t value, objc_bridge_type type) {
1146    // must be protected because it is used by Creo in every event
1147    gravity_gc_setenabled(vm, false);
1148    id v = convert_gravity2obj(vm, value, type);
1149    gravity_gc_setenabled(vm, true);
1150    return v;
1151}
1152
1153gravity_instance_t *bridge_instance_byclassname (gravity_vm *vm, id value, const char* name, uint32_t length) {
1154    gravity_value_t v = gravity_vm_getvalue(vm, name, length);
1155    if (!VALUE_ISA_CLASS(v)) return NULL;
1156
1157    gravity_class_t *c2 = VALUE_AS_CLASS(v);
1158    gravity_instance_t *instance = gravity_instance_new(vm, c2);
1159    gravity_instance_setxdata(instance, RETAIN_OBJC_VALUE(value));
1160    return instance;
1161}
1162
1163static gravity_value_t bridge_objc2gravity_retain_flag (gravity_vm *vm, id obj, objc_bridge_type type, BOOL value_retained) {
1164    // sanity check
1165    if (!obj) return VALUE_FROM_NULL;
1166
1167    @try {
1168        switch (type) {
1169            case OBJC_BRIDGE_TYPE_UNKNOWN: {
1170                return convert_creo2gravity(vm, obj, OBJC_BRIDGE_TYPE_UNKNOWN, value_retained);
1171            }
1172
1173            case OBJC_BRIDGE_TYPE_SEL:
1174            case OBJC_BRIDGE_TYPE_VPTR:
1175            case OBJC_BRIDGE_TYPE_CPTR:
1176            case OBJC_BRIDGE_TYPE_VOID:
1177            case OBJC_BRIDGE_TYPE_CLASS: {
1178                return VALUE_FROM_NULL;
1179            }
1180
1181            case OBJC_BRIDGE_TYPE_INIT:
1182            case OBJC_BRIDGE_TYPE_ID:
1183            case OBJC_BRIDGE_TYPE_USER:
1184            case OBJC_BRIDGE_TYPE_GRAVITY: {
1185                return convert_id2gravity(vm, obj);
1186            }
1187
1188            case OBJC_BRIDGE_TYPE_RANGE: {
1189                return convert_nsvalue2gravity(vm, obj, type);
1190            }
1191
1192            case OBJC_BRIDGE_TYPE_POINT:
1193            case OBJC_BRIDGE_TYPE_RECT:
1194            case OBJC_BRIDGE_TYPE_SIZE:
1195            case OBJC_BRIDGE_TYPE_OFFSET:
1196            case OBJC_BRIDGE_TYPE_EDGEINSETS: {
1197                if ([obj isKindOfClass:[NSValue class]]) return convert_nsvalue2gravity(vm, obj, type);
1198                return convert_creo2gravity(vm, obj, type, value_retained);
1199            }
1200
1201            case OBJC_BRIDGE_TYPE_NSDATE:
1202            case OBJC_BRIDGE_TYPE_NSDATA:
1203            case OBJC_BRIDGE_TYPE_IMAGE:
1204            case OBJC_BRIDGE_TYPE_COLOR:
1205            case OBJC_BRIDGE_TYPE_GRADIENT:
1206            case OBJC_BRIDGE_TYPE_MOVIE:
1207            case OBJC_BRIDGE_TYPE_SOUND:
1208            case OBJC_BRIDGE_TYPE_FONT: {
1209                return convert_creo2gravity(vm, obj, type, value_retained);
1210            }
1211
1212            case OBJC_BRIDGE_TYPE_NSARRAY: {
1213                if ([obj isKindOfClass:[NSArray class]])
1214                    return convert_nsarray2gravity(vm, obj);
1215                else
1216                    return convert_nsarray2gravity(vm, @[obj]);
1217            }
1218
1219            case OBJC_BRIDGE_TYPE_NSDICTIONARY: {
1220                if ([obj isKindOfClass:[NSDictionary class]])
1221                    return convert_nsdictionary2gravity(vm, obj);
1222                else
1223                    return convert_creo2gravity(vm, obj, type, value_retained);
1224            }
1225
1226            case OBJC_BRIDGE_TYPE_NSNUMBER: {
1227                if ([obj isKindOfClass:[NSNumber class]])
1228                    return convert_nsnumber2gravity(vm, obj);
1229                else
1230                    return convert_creo2gravity(vm, obj, type, value_retained);
1231            }
1232
1233            case OBJC_BRIDGE_TYPE_INT8: {
1234                int8_t value = [obj charValue];
1235                return VALUE_FROM_INT((gravity_int_t)value);
1236            }
1237
1238            case OBJC_BRIDGE_TYPE_INT16: {
1239                int16_t value = [obj shortValue];
1240                return VALUE_FROM_INT((gravity_int_t)value);
1241            }
1242
1243            case OBJC_BRIDGE_TYPE_INT32: {
1244                int32_t value = (int32_t)[obj longValue];
1245                return VALUE_FROM_INT((gravity_int_t)value);
1246            }
1247
1248            case OBJC_BRIDGE_TYPE_INT64: {
1249                int64_t value = [obj longLongValue];
1250                return VALUE_FROM_INT((gravity_int_t)value);
1251            }
1252
1253            case OBJC_BRIDGE_TYPE_UINT8: {
1254                uint8_t value = [obj unsignedCharValue];
1255                return VALUE_FROM_INT((gravity_int_t)value);
1256            }
1257
1258            case OBJC_BRIDGE_TYPE_UINT16: {
1259                uint16_t value = [obj unsignedShortValue];
1260                return VALUE_FROM_INT((gravity_int_t)value);
1261            }
1262
1263            case OBJC_BRIDGE_TYPE_UINT32: {
1264                uint32_t value = (uint32_t)[obj unsignedLongValue];
1265                return VALUE_FROM_INT((gravity_int_t)value);
1266            }
1267
1268            case OBJC_BRIDGE_TYPE_UINT64: {
1269                uint64_t value = [obj unsignedLongLongValue];
1270                return VALUE_FROM_INT((gravity_int_t)value);
1271            }
1272
1273            case OBJC_BRIDGE_TYPE_FLOAT: {
1274                float value = [obj floatValue];
1275                return VALUE_FROM_FLOAT((gravity_float_t)value);
1276            }
1277
1278            case OBJC_BRIDGE_TYPE_LDOUBLE:
1279            case OBJC_BRIDGE_TYPE_DOUBLE: {
1280                double value = [obj doubleValue];
1281                return VALUE_FROM_FLOAT((gravity_float_t)value);
1282            }
1283
1284            case OBJC_BRIDGE_TYPE_BOOL: {
1285                BOOL value = [obj boolValue];
1286                return VALUE_FROM_BOOL(value);
1287            }
1288
1289            case OBJC_BRIDGE_TYPE_NSINTEGER: {
1290                NSInteger value = [obj integerValue];
1291                return VALUE_FROM_INT((gravity_int_t)value);
1292            }
1293
1294            case OBJC_BRIDGE_TYPE_NSUINTEGER: {
1295                NSUInteger value = [obj unsignedIntegerValue];
1296                return VALUE_FROM_INT((gravity_int_t)value);
1297            }
1298
1299            case OBJC_BRIDGE_TYPE_NSSTRING: {
1300                return VALUE_FROM_STRING(vm, ((NSString*)obj).UTF8String, (uint32_t)[(NSString*)obj lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
1301            }
1302
1303            default: {
1304                return convert_creo2gravity(vm, obj, type, value_retained);
1305            }
1306        }
1307    } @catch (NSException *exception) {
1308        NSLog(@"bridge_objc2gravity %@ (%@ %d)", exception.reason, obj, type);
1309        return convert_creo2gravity(vm, obj, type, value_retained);
1310    }
1311
1312    return VALUE_FROM_NULL;
1313}
1314
1315gravity_value_t bridge_objc2gravity (gravity_vm *vm, id obj, objc_bridge_type type) {
1316    // must be protected because it is used by Creo in every event
1317    gravity_gc_setenabled(vm, false);
1318    gravity_value_t v = bridge_objc2gravity_retain_flag(vm, obj, type, NO);
1319    gravity_gc_setenabled(vm, true);
1320    return v;
1321}
1322
1323static objc_bridge_var_t *bridge_property (gravity_vm *vm, gravity_class_t *c, const char *key) {
1324    #pragma unused(vm)
1325
1326    STATICVALUE_FROM_STRING(k, key, strlen(key));
1327    gravity_object_t *obj = gravity_class_lookup(c, k);
1328    if (!obj) return NULL;
1329
1330    if (!OBJECT_ISA_CLOSURE(obj)) return NULL;
1331    gravity_closure_t *closure = (gravity_closure_t*)obj;
1332    if (closure->f->tag != EXEC_TYPE_SPECIAL) return NULL;
1333    if (closure->f->index != GRAVITY_BRIDGE_INDEX) return NULL;
1334    closure = (closure->f->special[0]) ? closure->f->special[0] : closure->f->special[1];
1335    if (!closure || (!closure->f)) return NULL;
1336    if (!closure->f->xdata) return NULL;
1337
1338    return (objc_bridge_var_t *)closure->f->xdata;
1339}
1340
1341objc_bridge_type bridge_property_type (gravity_vm *vm, gravity_class_t *c, const char *key) {
1342    objc_bridge_var_t *property = bridge_property(vm, c, key);
1343    if (!property) return OBJC_BRIDGE_TYPE_UNKNOWN;
1344    return property->type;
1345}
1346
1347const char *bridge_property_name (gravity_vm *vm, gravity_class_t *c, const char *key) {
1348    objc_bridge_var_t *property = bridge_property(vm, c, key);
1349    if (!property) return key;
1350    if (!property->key) return key;
1351    return property->key;
1352}
1353
1354// MARK: - Delegate -
1355
1356bool bridge_initinstance (gravity_vm *vm, void *xdata, gravity_value_t ctx, gravity_instance_t *instance, gravity_value_t args[], int16_t nargs) {
1357    gravity_class_t *class = instance->objclass;
1358    Class c = (__bridge Class)(class->xdata);
1359
1360    // special case to force to use xdata directly
1361    if (VALUE_ISA_NULL(ctx) && args == NULL && nargs == 1) c = (__bridge Class)xdata;
1362
1363    id obj = [c alloc];
1364    gravity_instance_setxdata(instance, RETAIN_OBJC_VALUE(obj));
1365
1366    if (nargs == 1) {
1367        // no arguments so just execute the init (obj2 can be different than obj, for example the NSDate init)
1368        id obj2 = [obj init];
1369        if (!obj2) RETURN_ERROR("Unable to initialize object.");
1370
1371        #ifdef CREO_PROJECT
1372        set_runtime_instance(vm, obj2, instance);
1373        #endif
1374
1375        if (obj != obj2) {
1376            // note1:
1377            // when the two objects are different (alloc != init) it means that in init there is a code like
1378            // self = something
1379            // and this line automatically send a release message to the original object so an explicit release
1380            // is not needed
1381            // RELEASE_OBJC_VALUE(obj);
1382            gravity_instance_setxdata(instance, RETAIN_OBJC_VALUE(obj2));
1383        }
1384        RETURN_NOVALUE();
1385    }
1386
1387    // there are more arguments so execute the init function
1388    void *saved = gravity_vm_getdata(vm);
1389    args[0] = VALUE_FROM_OBJECT(instance);
1390    if (!bridge_execute(vm, xdata, ctx, args, nargs, GRAVITY_DATA_REGISTER)) {
1391        gravity_instance_setxdata(instance, NULL);
1392        return false;
1393    }
1394
1395    // obj2 can be different from obj if the init method returns a different object from the previously allocated one
1396    id obj2 = (__bridge id)(gravity_vm_getdata(vm));
1397    gravity_vm_setdata(vm, saved);
1398    if (!obj2) {
1399        gravity_instance_setxdata(instance, NULL);
1400        NSString *name = NULL;
1401        RETURN_ERROR("Unable to initialize object of type %s.", (name) ? name.UTF8String : class->identifier);
1402    }
1403
1404    #if GRAVITY_BRIDGE_DEBUG_MEMORY
1405    NSLog(@"Created instance %p (%@)", obj2, NSStringFromClass([obj2 class]));
1406    #endif
1407
1408    if (obj != obj2) {
1409        // see note1 above
1410        // RELEASE_OBJC_VALUE(obj);
1411        // obj2 has already been retained in the bridge_execute
1412        gravity_instance_setxdata(instance, (__bridge void *)(obj2));
1413    }
1414    RETURN_NOVALUE();
1415}
1416
1417bool bridge_setvalue (gravity_vm *vm, void *xdata, gravity_value_t target, const char *key, gravity_value_t value) {
1418    DEBUG_BRIDGE(@"bridge_setvalue %s", key);
1419
1420    // obtain property type and optional key from class xdata
1421    objc_bridge_var_t *property = (objc_bridge_var_t *)xdata;
1422    if (property->key) key = property->key;
1423
1424    id objcValue = convert_gravity2obj(vm, value, property->type);
1425
1426    // get objc obj from target xdata
1427    id obj = (__bridge id)gravity_value_xdata(target);
1428    if (!obj) return false;
1429
1430    @try {
1431        [obj setValue:objcValue forKey:@(key)];
1432    }
1433    @catch (NSException * e) {
1434        gravity_vm_seterror(vm, "An error occurred while writing key %s (%s).", key, [[e reason] UTF8String]);
1435        return false;
1436    }
1437
1438    RETURN_NOVALUE();
1439}
1440
1441bool bridge_getvalue (gravity_vm *vm, void *xdata, gravity_value_t target, const char *key, uint32_t rindex) {
1442    DEBUG_BRIDGE(@"bridge_getvalue %s", key);
1443
1444    // obtain property type and optional key from class xdata
1445    objc_bridge_var_t *property = (objc_bridge_var_t *)xdata;
1446    if (property->key) key = property->key;
1447
1448    // get objc obj from target xdata
1449    id obj = (__bridge id)gravity_value_xdata(target);
1450    if (!obj) return false;
1451
1452    id result;
1453    @try {
1454        result = [obj valueForKey:@(key)];
1455    }
1456    @catch (NSException * e) {
1457        gravity_vm_seterror(vm, "An error occurred while reading key %s (%s).", key, [[e reason] UTF8String]);
1458        return false;
1459    }
1460
1461    gravity_value_t value = bridge_objc2gravity(vm, result, property->type);
1462    RETURN_VALUE(value, rindex);
1463}
1464
1465bool bridge_setundef (gravity_vm *vm, void *xdata, gravity_value_t target, const char *key, gravity_value_t value) {
1466    #pragma unused(vm, xdata, target, key, value)
1467    RETURN_NOVALUE();
1468}
1469
1470bool bridge_getundef (gravity_vm *vm, void *xdata, gravity_value_t target, const char *key, uint32_t rindex) {
1471    #pragma unused(vm, xdata, target, key, rindex)
1472    RETURN_NOVALUE();
1473}
1474
1475bool bridge_execute (gravity_vm *vm, void *data, gravity_value_t ctx, gravity_value_t args[], int16_t nargs, uint32_t rindex) {
1476    gravity_value_t        target = args[0];
1477    objc_bridge_func_t    *xdata = (objc_bridge_func_t *)data;
1478    id                    callee = (__bridge id)gravity_value_xdata(target);
1479    NSMutableArray        *arguments = [NSMutableArray arrayWithCapacity:nargs];
1480
1481    // internal debug var
1482    // struct objc_bridge_func_s *ddata = (struct objc_bridge_func_s *)data;
1483
1484    if (!callee || !xdata) {
1485        // not an instance nor a class... so a runtime error I guess
1486        RETURN_ERROR("Unable to process bridge request because target is not an instance nor a class.");
1487    }
1488
1489    NSInvocation *invocation = (xdata->invocation) ? (__bridge NSInvocation *)(xdata->invocation) : nil;
1490    if (!invocation) {
1491        NSMethodSignature *signature = [callee methodSignatureForSelector:xdata->selector];
1492        if (!signature) {
1493            const char *name = objc_bridge_get_exposed_name(xdata);
1494            const char *s = NSStringFromSelector(xdata->selector).UTF8String;
1495            RETURN_ERROR("Unable to process bridge request because signature for method %s (selector %s) cannot be build.", name, s);
1496        }
1497
1498        xdata->retlength = [signature methodReturnLength];
1499        invocation = [NSInvocation invocationWithMethodSignature:signature];
1500        [invocation setSelector:xdata->selector];                    // hidden _cmd parameter
1501        xdata->invocation = (void *)CFBridgingRetain(invocation);    // cache invocation
1502        if (xdata->rettype > OBJC_BRIDGE_TYPE_USER) xdata->rettype = OBJC_BRIDGE_TYPE_USER;
1503    }
1504
1505    if (!invocation) {
1506        RETURN_ERROR("Unable to process bridge request because invocation cannot be build.");
1507    }
1508
1509    // nargs is at least ALWAYS 1 because of the implicit target argument
1510    // last check added for default values
1511    if ((nargs>1) && (nargs-1 < xdata->nargs) && (!xdata->argvalue)) {
1512        const char *name = objc_bridge_get_exposed_name(xdata);
1513        RETURN_ERROR("Unable to call %s because of missing parameters (passed %d, required %d)", name, nargs-1, xdata->nargs);
1514    }
1515
1516    #if ENABLE_RUNTIME_CONTEXT
1517    if ([callee respondsToSelector:@selector(runtimeInstance)]) {
1518        CREORuntimeInstance *runtimeInstance = [callee runtimeInstance];
1519        if (VALUE_ISA_INSTANCE(ctx)) runtimeInstance.context = (void *)VALUE_AS_INSTANCE(ctx);
1520        else if (VALUE_ISA_CLASS(ctx)) runtimeInstance.context = (void *)VALUE_AS_CLASS(ctx);
1521        else runtimeInstance.context = NULL;
1522    }
1523    #endif
1524
1525    // setup parameters (i starts from 2 due to implicit arguments)
1526    for (uint16_t i=0, j=1, k=2; i<xdata->nargs; ++i, ++j, ++k) {
1527        gravity_value_t gravity_value = (j<nargs) ? args[j] : VALUE_FROM_NULL;
1528        BOOL is_default_value = NO;
1529
1530        // check for special default value case
1531        if (((j>=nargs) || VALUE_ISA_UNDEFINED(gravity_value)) && xdata->argvalue) {
1532
1533            // sanity check
1534            if (!xdata->argvalue[i]) {
1535                const char *name = objc_bridge_get_exposed_name(xdata);
1536                RETURN_ERROR("Unable to call %s because of missing parameters (passed %d, required %d)", name, nargs-1, xdata->nargs);
1537            }
1538
1539            // unbox default value
1540            if ((__bridge id)xdata->argvalue[i] == (id)[NSNull null]) gravity_value = VALUE_FROM_NULL;
1541            else gravity_value = bridge_objc2gravity_retain_flag(vm, (__bridge id)xdata->argvalue[i], xdata->argtype[i], YES);
1542
1543            is_default_value = YES;
1544        }
1545
1546        // convert argument
1547        switch (xdata->argtype[i]) {
1548
1549            case OBJC_BRIDGE_TYPE_INIT:
1550            case OBJC_BRIDGE_TYPE_UNKNOWN:
1551            case OBJC_BRIDGE_TYPE_CPTR:
1552            case OBJC_BRIDGE_TYPE_CLASS:
1553            case OBJC_BRIDGE_TYPE_SEL:
1554            case OBJC_BRIDGE_TYPE_VOID:
1555            case OBJC_BRIDGE_TYPE_LDOUBLE:
1556                assert(0);
1557
1558            case OBJC_BRIDGE_TYPE_CLOSURE: {
1559                // extra check for argumennt to be a real closure
1560                gravity_closure_t *closure;
1561                if (VALUE_ISA_NULL(gravity_value)) closure = NULL;
1562                else if (VALUE_ISA_CLOSURE(gravity_value)) closure = VALUE_AS_CLOSURE(gravity_value);
1563                else RETURN_ERROR("Unable to convert parameter %d to Closure (in %s).", k-1, objc_bridge_get_exposed_name(xdata));
1564                [invocation setArgument:&closure atIndex:k];
1565            } break;
1566
1567            case OBJC_BRIDGE_TYPE_GRAVITY:
1568            case OBJC_BRIDGE_TYPE_VPTR: {
1569                // this case is used when an unknown number of arguments can be passed to an event
1570                void *ptr;
1571                if (gravity_value_isobject(gravity_value)) ptr = VALUE_AS_OBJECT(gravity_value);
1572                else ptr = NULL;
1573                [invocation setArgument:&ptr atIndex:k];
1574            } break;
1575
1576            case OBJC_BRIDGE_TYPE_INT8: {
1577                gravity_value_t v = convert_value2int(vm, gravity_value);
1578                if (VALUE_ISA_NOTVALID(v)) RETURN_ERROR("Unable to convert parameter %d to Int (in %s).", k-1, objc_bridge_get_exposed_name(xdata));
1579
1580                char value = (char)v.n;
1581                [invocation setArgument:&value atIndex:k];
1582            } break;
1583
1584            case OBJC_BRIDGE_TYPE_INT16: {
1585                gravity_value_t v = convert_value2int(vm, gravity_value);
1586                if (VALUE_ISA_NOTVALID(v)) RETURN_ERROR("Unable to convert parameter %d to Int (in %s).", k-1, objc_bridge_get_exposed_name(xdata));
1587
1588                short value = (short)v.n;
1589                [invocation setArgument:&value atIndex:k];
1590            } break;
1591
1592            case OBJC_BRIDGE_TYPE_INT32: {
1593                gravity_value_t v = convert_value2int(vm, gravity_value);
1594                if (VALUE_ISA_NOTVALID(v)) RETURN_ERROR("Unable to convert parameter %d to Int (in %s).", k-1, objc_bridge_get_exposed_name(xdata));
1595
1596                long value = (long)v.n;
1597                [invocation setArgument:&value atIndex:k];
1598            } break;
1599
1600            case OBJC_BRIDGE_TYPE_INT64: {
1601                gravity_value_t v = convert_value2int(vm, gravity_value);
1602                if (VALUE_ISA_NOTVALID(v)) RETURN_ERROR("Unable to convert parameter %d to Int (in %s).", k-1, objc_bridge_get_exposed_name(xdata));
1603
1604                long long value = (long long)v.n;
1605                [invocation setArgument:&value atIndex:k];
1606            } break;
1607
1608            case OBJC_BRIDGE_TYPE_UINT8: {
1609                gravity_value_t v = convert_value2int(vm, gravity_value);
1610                if (VALUE_ISA_NOTVALID(v)) RETURN_ERROR("Unable to convert parameter %d to Int (in %s).", k-1, objc_bridge_get_exposed_name(xdata));
1611
1612                unsigned char value = (unsigned char)v.n;
1613                [invocation setArgument:&value atIndex:k];
1614            } break;
1615
1616            case OBJC_BRIDGE_TYPE_UINT16: {
1617                gravity_value_t v = convert_value2int(vm, gravity_value);
1618                if (VALUE_ISA_NOTVALID(v)) RETURN_ERROR("Unable to convert parameter %d to Int (in %s).", k-1, objc_bridge_get_exposed_name(xdata));
1619
1620                unsigned short value = (unsigned short)v.n;
1621                [invocation setArgument:&value atIndex:k];
1622            } break;
1623
1624            case OBJC_BRIDGE_TYPE_UINT32: {
1625                gravity_value_t v = convert_value2int(vm, gravity_value);
1626                if (VALUE_ISA_NOTVALID(v)) RETURN_ERROR("Unable to convert parameter %d to Int (in %s).", k-1, objc_bridge_get_exposed_name(xdata));
1627
1628                unsigned long value = (unsigned long)v.n;
1629                [invocation setArgument:&value atIndex:k];
1630            } break;
1631
1632            case OBJC_BRIDGE_TYPE_UINT64: {
1633                gravity_value_t v = convert_value2int(vm, gravity_value);
1634                if (VALUE_ISA_NOTVALID(v)) RETURN_ERROR("Unable to convert parameter %d to Int (in %s).", k-1, objc_bridge_get_exposed_name(xdata));
1635
1636                unsigned long long value = (unsigned long long)v.n;
1637                [invocation setArgument:&value atIndex:k];
1638            } break;
1639
1640            case OBJC_BRIDGE_TYPE_FLOAT: {
1641                gravity_value_t v = convert_value2float(vm, gravity_value);
1642                if (VALUE_ISA_NOTVALID(v)) RETURN_ERROR("Unable to convert parameter %d to Float (in %s).", k-1, objc_bridge_get_exposed_name(xdata));
1643
1644                float value = (float)v.f;
1645                [invocation setArgument:&value atIndex:k];
1646            } break;
1647
1648            case OBJC_BRIDGE_TYPE_DOUBLE: {
1649                gravity_value_t v = convert_value2float(vm, gravity_value);
1650                if (VALUE_ISA_NOTVALID(v)) RETURN_ERROR("Unable to convert parameter %d to Float (in %s).", k-1, objc_bridge_get_exposed_name(xdata));
1651
1652                double value = (double)v.f;
1653                [invocation setArgument:&value atIndex:k];
1654            } break;
1655
1656            case OBJC_BRIDGE_TYPE_BOOL: {
1657                gravity_value_t v = convert_value2bool(vm, gravity_value);
1658                if (VALUE_ISA_NOTVALID(v)) RETURN_ERROR("Unable to convert parameter %d to Bool (in %s).", k-1, objc_bridge_get_exposed_name(xdata));
1659
1660                bool value = (bool)v.n;
1661                [invocation setArgument:&value atIndex:k];
1662            } break;
1663
1664            case OBJC_BRIDGE_TYPE_NSINTEGER: {
1665                gravity_value_t v = convert_value2int(vm, gravity_value);
1666                if (VALUE_ISA_NOTVALID(v)) RETURN_ERROR("Unable to convert parameter %d to Int (in %s).", k-1, objc_bridge_get_exposed_name(xdata));
1667
1668                NSInteger value = (NSInteger)v.n;
1669                [invocation setArgument:&value atIndex:k];
1670            } break;
1671
1672            case OBJC_BRIDGE_TYPE_NSUINTEGER: {
1673                gravity_value_t v = convert_value2int(vm, gravity_value);
1674                if (VALUE_ISA_NOTVALID(v)) RETURN_ERROR("Unable to convert parameter %d to Int (in %s).", k-1, objc_bridge_get_exposed_name(xdata));
1675
1676                NSUInteger value = (NSUInteger)v.n;
1677                [invocation setArgument:&value atIndex:k];
1678            } break;
1679
1680            case OBJC_BRIDGE_TYPE_ID: {
1681                id value = nil;
1682
1683                if (VALUE_ISA_INSTANCE(gravity_value)) value = (__bridge id)(VALUE_AS_INSTANCE(gravity_value)->xdata);
1684                else if (VALUE_ISA_LIST(gravity_value)) value = convert_gravity2nsarray(vm, gravity_value);
1685                else if (VALUE_ISA_MAP(gravity_value)) value = convert_gravity2nsdictionary(vm, gravity_value);
1686                else value = convert_gravity2obj(vm, gravity_value, OBJC_BRIDGE_TYPE_ID);
1687
1688                if (value) [arguments addObject:value];
1689                [invocation setArgument:&value atIndex:k];
1690            } break;
1691
1692            case OBJC_BRIDGE_TYPE_NSNUMBER: {
1693                NSNumber *value = nil;
1694                if (VALUE_ISA_INT(gravity_value)) value = @(gravity_value.n);
1695                else if (VALUE_ISA_FLOAT(gravity_value)) value = @(gravity_value.f);
1696                else {
1697                    gravity_value_t v = convert_value2int(vm, gravity_value);
1698                    if (VALUE_ISA_NOTVALID(v)) RETURN_ERROR("Unable to convert parameter %d to Int (in %s).", k-1, objc_bridge_get_exposed_name(xdata));
1699                    value = @(v.n);
1700                }
1701
1702                [arguments addObject:value];
1703                [invocation setArgument:&value atIndex:k];
1704            } break;
1705
1706            case OBJC_BRIDGE_TYPE_NSSTRING: {
1707                if (VALUE_ISA_NULL(gravity_value) && is_default_value) {
1708                    id value = nil;
1709                    [invocation setArgument:&value atIndex:k];
1710                } else {
1711                    gravity_value_t v = convert_value2string(vm, gravity_value);
1712                    if (VALUE_ISA_NOTVALID(v)) RETURN_ERROR("Unable to convert parameter %d to String (in %s).", k-1, objc_bridge_get_exposed_name(xdata));
1713
1714                    NSString *value = [NSString stringWithUTF8String:VALUE_AS_CSTRING(v)];
1715                    [arguments addObject:value];
1716                    [invocation setArgument:&value atIndex:k];
1717                }
1718            } break;
1719
1720            case OBJC_BRIDGE_TYPE_NSARRAY: {
1721                id value = convert_gravity2nsarray(vm, gravity_value);
1722                if (value) [arguments addObject:value];
1723                [invocation setArgument:&value atIndex:k];
1724            } break;
1725
1726            case OBJC_BRIDGE_TYPE_NSDICTIONARY: {
1727                id value = convert_gravity2nsdictionary(vm, gravity_value);
1728                if (value) [arguments addObject:value];
1729                [invocation setArgument:&value atIndex:k];
1730            } break;
1731
1732            // STRUCT BASED ARGUMENTS
1733            case OBJC_BRIDGE_TYPE_POINT: {
1734                // guarantee to return a non null NSValue
1735                NSValue *value = (NSValue *)convert_gravity2type(vm, gravity_value, xdata->argtype[i]);
1736                CGPoint point = value.pointValue;
1737                [invocation setArgument:&point atIndex:k];
1738            } break;
1739
1740            case OBJC_BRIDGE_TYPE_RECT: {
1741                // guarantee to return a non null NSValue
1742                NSValue *value = (NSValue *)convert_gravity2type(vm, gravity_value, xdata->argtype[i]);
1743                CGRect rect = value.rectValue;
1744                [invocation setArgument:&rect atIndex:k];
1745            } break;
1746
1747            case OBJC_BRIDGE_TYPE_SIZE: {
1748                // guarantee to return a non null NSValue
1749                NSValue *value = (NSValue *)convert_gravity2type(vm, gravity_value, xdata->argtype[i]);
1750                CGSize size = value.sizeValue;
1751                [invocation setArgument:&size atIndex:k];
1752            } break;
1753
1754            case OBJC_BRIDGE_TYPE_EDGEINSETS: {
1755                #if TARGET_OS_IPHONE
1756                // guarantee to return a non null NSValue
1757                NSValue *value = (NSValue *)convert_gravity2type(vm, gravity_value, xdata->argtype[i]);
1758                UIEdgeInsets insets = value.edgeInsetsValue;
1759                [invocation setArgument:&insets atIndex:k];
1760                #endif
1761            } break;
1762
1763            case OBJC_BRIDGE_TYPE_OFFSET: {
1764                #if TARGET_OS_IPHONE
1765                // guarantee to return a non null NSValue
1766                NSValue *value = (NSValue *)convert_gravity2type(vm, gravity_value, xdata->argtype[i]);
1767                UIOffset offset = value.offsetValue;
1768                [invocation setArgument:&offset atIndex:k];
1769                #endif
1770            } break;
1771
1772            case OBJC_BRIDGE_TYPE_RANGE: {
1773                NSRange r = convert_gravity2nsrange (vm, gravity_value);
1774                [invocation setArgument:&r atIndex:k];
1775            } break;
1776
1777            // OBJ BASED ARGUMENTS
1778            case OBJC_BRIDGE_TYPE_NSDATE:
1779            case OBJC_BRIDGE_TYPE_NSDATA:
1780            case OBJC_BRIDGE_TYPE_IMAGE:
1781            case OBJC_BRIDGE_TYPE_COLOR:
1782            case OBJC_BRIDGE_TYPE_GRADIENT:
1783            case OBJC_BRIDGE_TYPE_SOUND:
1784            case OBJC_BRIDGE_TYPE_MOVIE:
1785            case OBJC_BRIDGE_TYPE_FONT: {
1786                id value = convert_gravity2type(vm, gravity_value, xdata->argtype[i]);
1787                if (value) [arguments addObject:value];
1788                [invocation setArgument:&value atIndex:k];
1789            } break;
1790
1791            case OBJC_BRIDGE_TYPE_USER:
1792            default: {
1793                #ifdef CREO_PROJECT
1794                id<CREORuntimeDelegate> delegate = (__bridge id<CREORuntimeDelegate>)(gravity_vm_getdata(vm));
1795                if (!delegate) RETURN_ERROR("Delegate not set.");
1796
1797                Class c = [delegate classByTag:xdata->argtype[i]];
1798                if (!c) RETURN_ERROR("Unable to find class name for class tag %d", xdata->argtype[i]);
1799                if (!VALUE_ISA_INSTANCE(gravity_value)) {
1800                    gravity_value_t v = convert_value2string(vm, gravity_value);
1801                    const char *cname = NSStringFromClass(c).UTF8String;
1802                    const char *vstring = (VALUE_ISA_STRING(v)) ? VALUE_AS_CSTRING(v) : "N/A";
1803                    RETURN_ERROR("Unable to convert parameter %s to %s in %s.", vstring, cname, xdata->name);
1804                }
1805
1806                gravity_instance_t *instance = VALUE_AS_INSTANCE(gravity_value);
1807                id value = (instance) ? (__bridge id)(instance->xdata) : nil;
1808                if (![value isKindOfClass:c]) {
1809                    RETURN_ERROR("Wrong parameter (position %d of %s).", k-1, objc_bridge_get_exposed_name(xdata));
1810                }
1811
1812                [arguments addObject:value];
1813                [invocation setArgument:&value atIndex:k];
1814                #else
1815                assert(0);
1816                #endif
1817            } break;
1818        }
1819    }
1820
1821    // invoke function
1822    @try {
1823        [invocation invokeWithTarget:callee];
1824    }
1825    @catch (NSException * e) {
1826        gravity_vm_seterror(vm, "An error occurred while calling %s (%s).", objc_bridge_get_exposed_name(xdata), [[e reason] UTF8String]);
1827        return false;
1828    }
1829
1830    // process return value
1831    switch (xdata->rettype) {
1832        case OBJC_BRIDGE_TYPE_UNKNOWN:
1833        case OBJC_BRIDGE_TYPE_LDOUBLE:
1834            assert(0);
1835
1836        case OBJC_BRIDGE_TYPE_VOID: {
1837            gravity_vm_setslot(vm, VALUE_FROM_NULL, rindex);
1838            break;
1839        }
1840
1841        case OBJC_BRIDGE_TYPE_BOOL: {
1842            char buffer[2] = {0};
1843            [invocation getReturnValue:&buffer];
1844            gravity_vm_setslot(vm, (buffer[0] == 0) ? VALUE_FROM_FALSE : VALUE_FROM_TRUE, rindex);
1845            break;
1846        }
1847
1848        case OBJC_BRIDGE_TYPE_INT16: {
1849            short buffer = 0;
1850            [invocation getReturnValue:&buffer];
1851            gravity_vm_setslot(vm, VALUE_FROM_INT(buffer), rindex);
1852            break;
1853        }
1854
1855        case OBJC_BRIDGE_TYPE_INT32: {
1856            long buffer = 0;
1857            [invocation getReturnValue:&buffer];
1858            gravity_vm_setslot(vm, VALUE_FROM_INT(buffer), rindex);
1859            break;
1860        }
1861
1862        case OBJC_BRIDGE_TYPE_INT64: {
1863            long long buffer = 0;
1864            [invocation getReturnValue:&buffer];
1865            gravity_vm_setslot(vm, VALUE_FROM_INT(buffer), rindex);
1866            break;
1867        }
1868
1869        case OBJC_BRIDGE_TYPE_UINT8: {
1870            unsigned char buffer = 0;
1871            [invocation getReturnValue:&buffer];
1872            gravity_vm_setslot(vm, VALUE_FROM_INT(buffer), rindex);
1873            break;
1874        }
1875
1876        case OBJC_BRIDGE_TYPE_UINT16: {
1877            unsigned short buffer = 0;
1878            [invocation getReturnValue:&buffer];
1879            gravity_vm_setslot(vm, VALUE_FROM_INT(buffer), rindex);
1880            break;
1881        }
1882
1883        case OBJC_BRIDGE_TYPE_UINT32: {
1884            unsigned long buffer = 0;
1885            [invocation getReturnValue:&buffer];
1886            gravity_vm_setslot(vm, VALUE_FROM_INT(buffer), rindex);
1887            break;
1888        }
1889
1890        case OBJC_BRIDGE_TYPE_UINT64: {
1891            unsigned long long buffer = 0;
1892            [invocation getReturnValue:&buffer];
1893            gravity_vm_setslot(vm, VALUE_FROM_INT(buffer), rindex);
1894            break;
1895        }
1896
1897        case OBJC_BRIDGE_TYPE_NSINTEGER: {
1898            NSInteger buffer = 0;
1899            [invocation getReturnValue:&buffer];
1900            gravity_vm_setslot(vm, VALUE_FROM_INT(buffer), rindex);
1901            break;
1902        }
1903
1904        case OBJC_BRIDGE_TYPE_NSUINTEGER: {
1905            NSUInteger buffer = 0;
1906            [invocation getReturnValue:&buffer];
1907            gravity_vm_setslot(vm, VALUE_FROM_INT(buffer), rindex);
1908            break;
1909        }
1910
1911        case OBJC_BRIDGE_TYPE_FLOAT: {
1912            float buffer = 0.0;
1913            [invocation getReturnValue:&buffer];
1914            gravity_vm_setslot(vm, VALUE_FROM_FLOAT(buffer), rindex);
1915            break;
1916        }
1917
1918        case OBJC_BRIDGE_TYPE_DOUBLE: {
1919            double buffer = 0.0;
1920            [invocation getReturnValue:&buffer];
1921            gravity_vm_setslot(vm, VALUE_FROM_FLOAT(buffer), rindex);
1922            break;
1923        }
1924
1925        case OBJC_BRIDGE_TYPE_VPTR:
1926        case OBJC_BRIDGE_TYPE_CPTR: {
1927            void *buffer = NULL;
1928            [invocation getReturnValue:&buffer];
1929            assert(0);
1930        }
1931
1932        case OBJC_BRIDGE_TYPE_INIT: {
1933            assert(rindex == GRAVITY_DATA_REGISTER);
1934            id obj = nil;
1935            [invocation getReturnValue:&obj];
1936            gravity_vm_setdata(vm, (void *)CFBridgingRetain(obj));
1937            break;
1938        }
1939
1940        case OBJC_BRIDGE_TYPE_NSNUMBER:
1941        case OBJC_BRIDGE_TYPE_NSSTRING:
1942        case OBJC_BRIDGE_TYPE_NSARRAY:
1943        case OBJC_BRIDGE_TYPE_NSDICTIONARY:
1944        case OBJC_BRIDGE_TYPE_NSDATE:
1945        case OBJC_BRIDGE_TYPE_NSDATA:
1946        case OBJC_BRIDGE_TYPE_ID:
1947        case OBJC_BRIDGE_TYPE_USER:
1948        case OBJC_BRIDGE_TYPE_GRAVITY:
1949
1950        case OBJC_BRIDGE_TYPE_COLOR:
1951        case OBJC_BRIDGE_TYPE_SOUND:
1952        case OBJC_BRIDGE_TYPE_IMAGE:
1953        case OBJC_BRIDGE_TYPE_GRADIENT:
1954        case OBJC_BRIDGE_TYPE_FONT: {
1955            // https://stackoverflow.com/questions/11874056/nsinvocation-getreturnvalue-called-inside-forwardinvocation-makes-the-returned
1956            __unsafe_unretained id obj = nil;
1957            [invocation getReturnValue:&obj];
1958            gravity_vm_setslot(vm, bridge_objc2gravity(vm, obj, xdata->rettype), rindex);
1959            break;
1960        }
1961
1962        case OBJC_BRIDGE_TYPE_POINT: {
1963            CGPoint v;
1964            [invocation getReturnValue:&v];
1965            gravity_vm_setslot(vm, bridge_objc2gravity(vm, @(v), xdata->rettype), rindex);
1966            break;
1967        }
1968
1969        case OBJC_BRIDGE_TYPE_SIZE: {
1970            CGSize v;
1971            [invocation getReturnValue:&v];
1972            gravity_vm_setslot(vm, bridge_objc2gravity(vm, @(v), xdata->rettype), rindex);
1973            break;
1974        }
1975
1976        case OBJC_BRIDGE_TYPE_RECT: {
1977            CGRect v;
1978            [invocation getReturnValue:&v];
1979            gravity_vm_setslot(vm, bridge_objc2gravity(vm, @(v), xdata->rettype), rindex);
1980            break;
1981        }
1982
1983        case OBJC_BRIDGE_TYPE_EDGEINSETS: {
1984            #if TARGET_OS_IPHONE
1985            UIEdgeInsets v;
1986            [invocation getReturnValue:&v];
1987            gravity_vm_setslot(vm, bridge_objc2gravity(vm, [NSValue valueWithUIEdgeInsets:v], xdata->rettype), rindex);
1988            #endif
1989            break;
1990        }
1991
1992        case OBJC_BRIDGE_TYPE_OFFSET: {
1993            #if TARGET_OS_IPHONE
1994            UIOffset v;
1995            [invocation getReturnValue:&v];
1996            gravity_vm_setslot(vm, bridge_objc2gravity(vm, [NSValue valueWithUIOffset:v], xdata->rettype), rindex);
1997            #endif
1998            break;
1999        }
2000
2001        case OBJC_BRIDGE_TYPE_RANGE: {
2002            NSRange v;
2003            [invocation getReturnValue:&v];
2004            gravity_vm_setslot(vm, bridge_objc2gravity(vm, [NSValue valueWithRange:v], xdata->rettype), rindex);
2005            break;
2006        }
2007
2008        default:
2009            /*
2010             OBJC_BRIDGE_TYPE_CLASS
2011             OBJC_BRIDGE_TYPE_SEL
2012             OBJC_BRIDGE_TYPE_MOVIE
2013             */
2014            // default is to ignore return values and not to assert
2015            NSLog(@"Unhandled bridge_execute return value case");
2016            gravity_vm_setslot(vm, VALUE_FROM_NULL, rindex);
2017            break;
2018    }
2019
2020    return true;
2021}
2022
2023const char *bridge_string (gravity_vm *vm, void *xdata, uint32_t *len) {
2024    #pragma unused(vm)
2025    // assuming xdata is an objc object
2026    NSObject *obj = (__bridge NSObject *)(xdata);
2027    if ([obj respondsToSelector:@selector(description)]) {
2028        NSString *description = [obj performSelector:@selector(description)];
2029        *len = (uint32_t)[description lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
2030        return description.UTF8String;
2031    }
2032    return NULL;
2033}
2034
2035void bridge_blacken (gravity_vm *vm, void *xdata) {
2036//    NSObject *obj = (__bridge NSObject *)(xdata);
2037//    if ([obj respondsToSelector:@selector(runtimeInstance)]) {
2038//        id r = [obj valueForKey:@"runtimeInstance"];
2039//        if (!r) return;
2040//    } else return;
2041//
2042///*
2043//    if ([obj respondsToSelector:@selector(gravityInstance)]) {
2044//        gravity_instance_t *instance = (__bridge gravity_instance_t *)[obj valueForKey:@"gravityInstance"];
2045//        if (instance) gravity_instance_blacken(vm, instance);
2046//    }
2047//  */
2048//
2049//    #pragma clang diagnostic push
2050//    #pragma clang diagnostic ignored "-Wundeclared-selector"
2051//    if ([obj respondsToSelector:@selector(gravityBlacken)]) {
2052//        [obj performSelector:@selector(gravityBlacken)];
2053//    }
2054//    #pragma clang diagnostic pop
2055}
2056
2057bool bridge_equals (gravity_vm *vm, void *obj_1, void *obj_2) {
2058    #pragma unused(vm)
2059    // assuming both obj1 and obj2 are objc objects
2060    NSObject *obj1 = (__bridge NSObject *)(obj_1);
2061    NSObject *obj2 = (__bridge NSObject *)(obj_2);
2062    if ([obj1 respondsToSelector:@selector(isEqual:)]) {
2063        return [obj1 isEqual:obj2];
2064    }
2065    return false;
2066}
2067
2068void *bridge_clone (gravity_vm *vm, void *xdata) {
2069    if (!xdata) return NULL;
2070    NSObject *clone = nil;
2071
2072    #ifdef CREO_PROJECT
2073    NSObject *obj = (__bridge NSObject *)(xdata);
2074    MKObjectID objectID = [obj objectID];
2075    gravity_delegate_t  *delegate = gravity_vm_delegate(vm);
2076    CREOApplication *app = (__bridge CREOApplication *)delegate->xdata;
2077    if (objectID != MKNotFound) {
2078        clone = [app createObjectWithID:objectID container:nil error:nil useCache:NO];
2079    } else {
2080        clone = [app createObjectWithClass:obj.class];
2081        if ([obj respondsToSelector:@selector(value)]) {
2082            [clone setValue:[obj valueForKey:@"value"] forKey:@"value"];
2083        }
2084    }
2085// WE CURRENTLY DO NOT SUPPORT PROPERTY SET VIA CODE (ONLY INSPECTOR PROPERTIES ARE SUPPORTED)
2086//    if (clone) {
2087//        unsigned int outCount, i;
2088//        objc_property_t *properties = class_copyPropertyList([obj class], &outCount);
2089//        for (i = 0; i < outCount; i++) {
2090//            objc_property_t property = properties[i];
2091//            const char *propName = property_getName(property);
2092//            if (propName) {
2093//                NSString *key = @(propName);
2094//                id value = [obj valueForKey:key];
2095//                [clone setValue:value forKey:key];
2096//                // NSLog(@"%@ %@", key, value);
2097//            }
2098//        }
2099//        free(properties);
2100//    }
2101    #endif
2102    return (clone) ? (void *)CFBridgingRetain(clone) : NULL;
2103}
2104
2105// MARK: - Free -
2106
2107static void bridge_free_instance (gravity_vm *vm, gravity_instance_t *i) {
2108    #pragma unused(vm)
2109    DEBUG_XDATA(@"\tBRIDGE FREE INSTANCE %@", i->xdata);
2110
2111    #if GRAVITY_BRIDGE_DEBUG_MEMORY
2112    NSLog(@"Free instance %p (%@)", i->xdata, NSStringFromClass([(__bridge id)i->xdata class]));
2113    #endif
2114
2115    if (!i->xdata) return;
2116
2117    #ifdef CREO_PROJECT
2118    set_runtime_instance(vm, (__bridge id)(i->xdata), nil);
2119    NSObject *obj = (__bridge NSObject *)(i->xdata);
2120    if ([obj respondsToSelector:@selector(removeFromSuperview)]) [(UIView*)obj removeFromSuperview];
2121    #endif
2122
2123    FREE_OBJC_VALUE(i->xdata);
2124}
2125
2126static void bridge_free_closure (gravity_vm *vm, gravity_closure_t *closure, bool is_property) {
2127    DEBUG_XDATA(@"\tBRIDGE FREE CLOSURE %p", closure->f);
2128
2129    #pragma unused(vm)
2130    if (closure->f->tag == EXEC_TYPE_SPECIAL) {
2131        assert(closure->f->index == GRAVITY_BRIDGE_INDEX);
2132        if (closure->f->xdata) objc_bridge_var_free((objc_bridge_var_t *)closure->f->xdata);
2133        closure->f->xdata = NULL;
2134        gravity_closure_t *getter = (gravity_closure_t *)closure->f->special[0];
2135        gravity_closure_t *setter = (closure->f->special[0] != closure->f->special[1]) ? (gravity_closure_t *)closure->f->special[1] : NULL;
2136        if (getter) bridge_free_closure(vm, getter, true);
2137        if (setter) bridge_free_closure(vm, setter, true);
2138    }
2139
2140    if (closure->f->tag == EXEC_TYPE_BRIDGED) {
2141        if (is_property) {
2142            objc_bridge_var_free((objc_bridge_var_t *)closure->f->xdata);
2143        } else {
2144            objc_bridge_func_free((objc_bridge_func_t *)closure->f->xdata);
2145        }
2146        closure->f->xdata = NULL;
2147    }
2148
2149    if (closure->f->xdata) {
2150        objc_bridge_var_free((objc_bridge_var_t *)closure->f->xdata);
2151    }
2152    gravity_function_t *f = closure->f;
2153    gravity_closure_free(NULL, closure);
2154    gravity_function_free(NULL, f);
2155}
2156
2157static void bridge_hash_iterate (gravity_hash_t *hashtable, gravity_value_t key, gravity_value_t value, void *data) {
2158    #pragma unused(hashtable, key)
2159    if (gravity_value_isobject(value)) {
2160        bridge_free((gravity_vm*)data, VALUE_AS_OBJECT(value));
2161    }
2162}
2163
2164static void bridge_free_class (gravity_vm *vm, gravity_class_t *c) {
2165    if (!c->xdata) return;
2166
2167    DEBUG_XDATA(@"BRIDGE FREE CLASS %s %p", c->identifier, c);
2168
2169    // free meta class first
2170    gravity_class_t *meta = gravity_class_get_meta(c);
2171    gravity_hash_iterate(meta->htable, bridge_hash_iterate, (void *)vm);
2172
2173    // then free real class
2174    gravity_hash_iterate(c->htable, bridge_hash_iterate, (void *)vm);
2175    FREE_OBJC_VALUE(c->xdata);
2176}
2177
2178void bridge_free (gravity_vm *vm, gravity_object_t *obj) {
2179    if (OBJECT_ISA_INSTANCE(obj)) {
2180        bridge_free_instance(vm, (gravity_instance_t *)obj);
2181    } else if (OBJECT_ISA_CLOSURE(obj)) {
2182        bridge_free_closure(vm, (gravity_closure_t *)obj, false);
2183    } else if (OBJECT_ISA_CLASS(obj)) {
2184        bridge_free_class(vm, (gravity_class_t *)obj);
2185    } else {
2186        // should never reach this point
2187        assert(0);
2188    }
2189}
2190
2191