+@end
+/* }}} */
+/* Bridge: NSObject {{{ */
+@implementation NSObject (Cycript)
+
+- (JSValueRef) cy$JSValueInContext:(JSContextRef)context {
+ return CYMakeInstance(context, self, false);
+}
+
+- (JSType) cy$JSType {
+ return kJSTypeObject;
+}
+
+- (NSObject *) cy$toJSON:(NSString *)key {
+ return [self description];
+}
+
+- (NSString *) cy$toCYON {
+ return [[self cy$toJSON:@""] cy$toCYON];
+}
+
+- (NSString *) cy$toKey {
+ return [self cy$toCYON];
+}
+
+- (bool) cy$hasProperty:(NSString *)name {
+ return false;
+}
+
+- (NSObject *) cy$getProperty:(NSString *)name {
+ return nil;
+}
+
+- (bool) cy$setProperty:(NSString *)name to:(NSObject *)value {
+ return false;
+}
+
+- (bool) cy$deleteProperty:(NSString *)name {
+ return false;
+}
+
+@end
+/* }}} */
+/* Bridge: NSProxy {{{ */
+@implementation NSProxy (Cycript)
+
+- (NSObject *) cy$toJSON:(NSString *)key {
+ return [self description];
+}
+
+- (NSString *) cy$toCYON {
+ return [[self cy$toJSON:@""] cy$toCYON];
+}
+
+@end
+/* }}} */
+/* Bridge: NSString {{{ */
+@implementation NSString (Cycript)
+
+- (JSType) cy$JSType {
+ return kJSTypeString;
+}
+
+- (NSObject *) cy$toJSON:(NSString *)key {
+ return self;
+}
+
+- (NSString *) cy$toCYON {
+ // XXX: this should use the better code from Output.cpp
+ CFMutableStringRef json(CFStringCreateMutableCopy(kCFAllocatorDefault, 0, (CFStringRef) self));
+
+ CFStringFindAndReplace(json, CFSTR("\\"), CFSTR("\\\\"), CFRangeMake(0, CFStringGetLength(json)), 0);
+ CFStringFindAndReplace(json, CFSTR("\""), CFSTR("\\\""), CFRangeMake(0, CFStringGetLength(json)), 0);
+ CFStringFindAndReplace(json, CFSTR("\t"), CFSTR("\\t"), CFRangeMake(0, CFStringGetLength(json)), 0);
+ CFStringFindAndReplace(json, CFSTR("\r"), CFSTR("\\r"), CFRangeMake(0, CFStringGetLength(json)), 0);
+ CFStringFindAndReplace(json, CFSTR("\n"), CFSTR("\\n"), CFRangeMake(0, CFStringGetLength(json)), 0);
+
+ CFStringInsert(json, 0, CFSTR("\""));
+ CFStringAppend(json, CFSTR("\""));
+
+ return [reinterpret_cast<const NSString *>(json) autorelease];
+}
+
+- (NSString *) cy$toKey {
+ const char *value([self UTF8String]);
+ size_t size(strlen(value));
+
+ if (size == 0)
+ goto cyon;
+
+ if (DigitRange_[value[0]]) {
+ size_t index(CYGetIndex(NULL, self));
+ if (index == _not(size_t))
+ goto cyon;
+ } else {
+ if (!WordStartRange_[value[0]])
+ goto cyon;
+ for (size_t i(1); i != size; ++i)
+ if (!WordEndRange_[value[i]])
+ goto cyon;
+ }
+
+ return self;
+
+ cyon:
+ return [self cy$toCYON];
+}
+
+- (void *) cy$symbol {
+ CYPool pool;
+ return dlsym(RTLD_DEFAULT, CYPoolCString(pool, self));
+}
+
+@end
+/* }}} */
+/* Bridge: WebUndefined {{{ */
+@implementation WebUndefined (Cycript)
+
+- (JSType) cy$JSType {
+ return kJSTypeUndefined;
+}
+
+- (NSObject *) cy$toJSON:(NSString *)key {
+ return self;
+}
+
+- (NSString *) cy$toCYON {
+ return @"undefined";
+}
+
+- (JSValueRef) cy$JSValueInContext:(JSContextRef)context {
+ return CYJSUndefined(context);
+}
+
+@end
+/* }}} */
+
+@interface CYJSObject : NSMutableDictionary {
+ JSObjectRef object_;
+ JSContextRef context_;
+}
+
+- (id) initWithJSObject:(JSObjectRef)object inContext:(JSContextRef)context;
+
+- (NSString *) cy$toJSON:(NSString *)key;
+
+- (NSUInteger) count;
+- (id) objectForKey:(id)key;
+- (NSEnumerator *) keyEnumerator;
+- (void) setObject:(id)object forKey:(id)key;
+- (void) removeObjectForKey:(id)key;
+
+@end
+
+@interface CYJSArray : NSMutableArray {
+ JSObjectRef object_;
+ JSContextRef context_;
+}
+
+- (id) initWithJSObject:(JSObjectRef)object inContext:(JSContextRef)context;
+
+- (NSUInteger) count;
+- (id) objectAtIndex:(NSUInteger)index;
+
+- (void) addObject:(id)anObject;
+- (void) insertObject:(id)anObject atIndex:(NSUInteger)index;
+- (void) removeLastObject;
+- (void) removeObjectAtIndex:(NSUInteger)index;
+- (void) replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject;
+
+@end
+
+CYRange DigitRange_ (0x3ff000000000000LLU, 0x000000000000000LLU); // 0-9
+CYRange WordStartRange_(0x000001000000000LLU, 0x7fffffe87fffffeLLU); // A-Za-z_$
+CYRange WordEndRange_ (0x3ff001000000000LLU, 0x7fffffe87fffffeLLU); // A-Za-z_$0-9
+
+#define CYTry \
+ @try
+#define CYCatch \
+ @catch (id error) { \
+ CYThrow(context, error, exception); \
+ return NULL; \
+ }
+
+apr_status_t CYPoolRelease_(void *data) {
+ id object(reinterpret_cast<id>(data));
+ [object release];
+ return APR_SUCCESS;
+}
+
+id CYPoolRelease(apr_pool_t *pool, id object) {
+ if (object == nil)
+ return nil;
+ else if (pool == NULL)
+ return [object autorelease];
+ else {
+ apr_pool_cleanup_register(pool, object, &CYPoolRelease_, &apr_pool_cleanup_null);
+ return object;
+ }
+}
+
+CFTypeRef CYPoolRelease(apr_pool_t *pool, CFTypeRef object) {
+ return (CFTypeRef) CYPoolRelease(pool, (id) object);
+}
+
+id CYCastNSObject_(apr_pool_t *pool, JSContextRef context, JSObjectRef object) {
+ JSValueRef exception(NULL);
+ bool array(JSValueIsInstanceOfConstructor(context, object, Array_, &exception));
+ CYThrow(context, exception);
+ id value(array ? [CYJSArray alloc] : [CYJSObject alloc]);
+ return CYPoolRelease(pool, [value initWithJSObject:object inContext:context]);
+}
+
+id CYCastNSObject(apr_pool_t *pool, JSContextRef context, JSObjectRef object) {
+ if (!JSValueIsObjectOfClass(context, object, Instance_))
+ return CYCastNSObject_(pool, context, object);
+ else {
+ Instance *internal(reinterpret_cast<Instance *>(JSObjectGetPrivate(object)));
+ return internal->GetValue();
+ }
+}
+
+double CYCastDouble(const char *value, size_t size) {
+ char *end;
+ double number(strtod(value, &end));
+ if (end != value + size)
+ return NAN;
+ return number;
+}
+
+double CYCastDouble(const char *value) {
+ return CYCastDouble(value, strlen(value));
+}
+
+double CYCastDouble(JSContextRef context, JSValueRef value) {
+ JSValueRef exception(NULL);
+ double number(JSValueToNumber(context, value, &exception));
+ CYThrow(context, exception);
+ return number;
+}
+
+CFNumberRef CYCopyCFNumber(JSContextRef context, JSValueRef value) {
+ double number(CYCastDouble(context, value));
+ return CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &number);
+}
+
+CFStringRef CYCopyCFString(const char *value) {
+ return CFStringCreateWithCString(kCFAllocatorDefault, value, kCFStringEncodingUTF8);
+}
+
+NSString *CYCastNSString(apr_pool_t *pool, const char *value) {
+ return (NSString *) CYPoolRelease(pool, CYCopyCFString(value));
+}
+
+NSString *CYCastNSString(apr_pool_t *pool, JSStringRef value) {
+ return (NSString *) CYPoolRelease(pool, CYCopyCFString(value));
+}
+
+bool CYCastBool(JSContextRef context, JSValueRef value) {
+ return JSValueToBoolean(context, value);
+}
+
+CFTypeRef CYCFType(apr_pool_t *pool, JSContextRef context, JSValueRef value, bool cast) {
+ CFTypeRef object;
+ bool copy;
+
+ switch (JSType type = JSValueGetType(context, value)) {
+ case kJSTypeUndefined:
+ object = [WebUndefined undefined];
+ copy = false;
+ break;
+
+ case kJSTypeNull:
+ return NULL;
+ break;
+
+ case kJSTypeBoolean:
+ object = CYCastBool(context, value) ? kCFBooleanTrue : kCFBooleanFalse;
+ copy = false;
+ break;
+
+ case kJSTypeNumber:
+ object = CYCopyCFNumber(context, value);
+ copy = true;
+ break;
+
+ case kJSTypeString:
+ object = CYCopyCFString(context, value);
+ copy = true;
+ break;
+
+ case kJSTypeObject:
+ // XXX: this might could be more efficient
+ object = (CFTypeRef) CYCastNSObject(pool, context, (JSObjectRef) value);
+ copy = false;
+ break;
+
+ default:
+ @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:[NSString stringWithFormat:@"JSValueGetType() == 0x%x", type] userInfo:nil];
+ break;
+ }
+
+ if (cast != copy)
+ return object;
+ else if (copy)
+ return CYPoolRelease(pool, object);
+ else
+ return CFRetain(object);
+}
+
+CFTypeRef CYCastCFType(apr_pool_t *pool, JSContextRef context, JSValueRef value) {
+ return CYCFType(pool, context, value, true);
+}
+
+CFTypeRef CYCopyCFType(apr_pool_t *pool, JSContextRef context, JSValueRef value) {
+ return CYCFType(pool, context, value, false);
+}
+
+NSArray *CYCastNSArray(JSPropertyNameArrayRef names) {
+ CYPool pool;
+ size_t size(JSPropertyNameArrayGetCount(names));
+ NSMutableArray *array([NSMutableArray arrayWithCapacity:size]);
+ for (size_t index(0); index != size; ++index)
+ [array addObject:CYCastNSString(pool, JSPropertyNameArrayGetNameAtIndex(names, index))];
+ return array;
+}
+
+id CYCastNSObject(apr_pool_t *pool, JSContextRef context, JSValueRef value) {
+ return reinterpret_cast<const NSObject *>(CYCastCFType(pool, context, value));
+}
+
+void CYThrow(JSContextRef context, JSValueRef value) {
+ if (value == NULL)
+ return;
+ @throw CYCastNSObject(NULL, context, value);
+}
+
+JSValueRef CYJSNull(JSContextRef context) {
+ return JSValueMakeNull(context);
+}
+
+JSValueRef CYCastJSValue(JSContextRef context, JSStringRef value) {
+ return value == NULL ? CYJSNull(context) : JSValueMakeString(context, value);
+}
+
+JSValueRef CYCastJSValue(JSContextRef context, const char *value) {
+ return CYCastJSValue(context, CYJSString(value));
+}
+
+JSValueRef CYCastJSValue(JSContextRef context, id value) {
+ if (value == nil)
+ return CYJSNull(context);
+ else if ([value respondsToSelector:@selector(cy$JSValueInContext:)])
+ return [value cy$JSValueInContext:context];
+ else
+ return CYMakeInstance(context, value, false);
+}
+
+JSObjectRef CYCastJSObject(JSContextRef context, JSValueRef value) {
+ JSValueRef exception(NULL);
+ JSObjectRef object(JSValueToObject(context, value, &exception));
+ CYThrow(context, exception);
+ return object;
+}
+
+void CYThrow(JSContextRef context, id error, JSValueRef *exception) {
+ if (exception == NULL)
+ throw error;
+ *exception = CYCastJSValue(context, error);
+}
+
+JSValueRef CYCallAsFunction(JSContextRef context, JSObjectRef function, JSObjectRef _this, size_t count, JSValueRef arguments[]) {
+ JSValueRef exception(NULL);
+ JSValueRef value(JSObjectCallAsFunction(context, function, _this, count, arguments, &exception));
+ CYThrow(context, exception);
+ return value;
+}
+
+bool CYIsCallable(JSContextRef context, JSValueRef value) {
+ // XXX: this isn't actually correct
+ return value != NULL && JSValueIsObject(context, value);
+}
+
+@implementation CYJSObject
+
+- (id) initWithJSObject:(JSObjectRef)object inContext:(JSContextRef)context {
+ if ((self = [super init]) != nil) {
+ object_ = object;
+ context_ = context;
+ JSValueProtect(context_, object_);
+ } return self;
+}
+
+- (void) dealloc {
+ JSValueUnprotect(context_, object_);
+ [super dealloc];
+}
+
+- (NSObject *) cy$toJSON:(NSString *)key {
+ JSValueRef toJSON(CYGetProperty(context_, object_, toJSON_));
+ if (!CYIsCallable(context_, toJSON))
+ return [super cy$toJSON:key];
+ else {
+ JSValueRef arguments[1] = {CYCastJSValue(context_, key)};
+ JSValueRef value(CYCallAsFunction(context_, (JSObjectRef) toJSON, object_, 1, arguments));
+ // XXX: do I really want an NSNull here?!
+ return CYCastNSObject(NULL, context_, value) ?: [NSNull null];
+ }
+}
+
+- (NSString *) cy$toCYON {
+ JSValueRef toCYON(CYGetProperty(context_, object_, toCYON_));
+ if (!CYIsCallable(context_, toCYON)) super:
+ return [super cy$toCYON];
+ else if (JSValueRef value = CYCallAsFunction(context_, (JSObjectRef) toCYON, object_, 0, NULL))
+ return CYCastNSString(NULL, CYJSString(context_, value));
+ else goto super;
+}
+
+- (NSUInteger) count {
+ JSPropertyNameArrayRef names(JSObjectCopyPropertyNames(context_, object_));
+ size_t size(JSPropertyNameArrayGetCount(names));
+ JSPropertyNameArrayRelease(names);
+ return size;
+}
+
+- (id) objectForKey:(id)key {
+ JSValueRef value(CYGetProperty(context_, object_, CYJSString(key)));
+ if (JSValueIsUndefined(context_, value))
+ return nil;
+ return CYCastNSObject(NULL, context_, value) ?: [NSNull null];
+}
+
+- (NSEnumerator *) keyEnumerator {
+ JSPropertyNameArrayRef names(JSObjectCopyPropertyNames(context_, object_));
+ NSEnumerator *enumerator([CYCastNSArray(names) objectEnumerator]);
+ JSPropertyNameArrayRelease(names);
+ return enumerator;
+}
+
+- (void) setObject:(id)object forKey:(id)key {
+ CYSetProperty(context_, object_, CYJSString(key), CYCastJSValue(context_, object));
+}
+
+- (void) removeObjectForKey:(id)key {
+ JSValueRef exception(NULL);
+ (void) JSObjectDeleteProperty(context_, object_, CYJSString(key), &exception);
+ CYThrow(context_, exception);
+}
+
+@end
+
+@implementation CYJSArray
+
+- (id) initWithJSObject:(JSObjectRef)object inContext:(JSContextRef)context {
+ if ((self = [super init]) != nil) {
+ object_ = object;
+ context_ = context;
+ JSValueProtect(context_, object_);
+ } return self;
+}
+
+- (void) dealloc {
+ JSValueUnprotect(context_, object_);
+ [super dealloc];
+}
+
+- (NSUInteger) count {
+ return CYCastDouble(context_, CYGetProperty(context_, object_, length_));
+}
+
+- (id) objectAtIndex:(NSUInteger)index {
+ size_t bounds([self count]);
+ if (index >= bounds)
+ @throw [NSException exceptionWithName:NSRangeException reason:[NSString stringWithFormat:@"*** -[CYJSArray objectAtIndex:]: index (%zu) beyond bounds (%zu)", index, bounds] userInfo:nil];
+ JSValueRef exception(NULL);
+ JSValueRef value(JSObjectGetPropertyAtIndex(context_, object_, index, &exception));
+ CYThrow(context_, exception);
+ return CYCastNSObject(NULL, context_, value) ?: [NSNull null];
+}
+
+- (void) addObject:(id)object {
+ JSValueRef exception(NULL);
+ JSValueRef arguments[1];
+ arguments[0] = CYCastJSValue(context_, object);
+ JSObjectCallAsFunction(context_, Array_push_, object_, 1, arguments, &exception);
+ CYThrow(context_, exception);
+}
+
+- (void) insertObject:(id)object atIndex:(NSUInteger)index {
+ size_t bounds([self count] + 1);
+ if (index >= bounds)
+ @throw [NSException exceptionWithName:NSRangeException reason:[NSString stringWithFormat:@"*** -[CYJSArray insertObject:atIndex:]: index (%zu) beyond bounds (%zu)", index, bounds] userInfo:nil];
+ JSValueRef exception(NULL);
+ JSValueRef arguments[3];
+ arguments[0] = CYCastJSValue(context_, index);
+ arguments[1] = CYCastJSValue(context_, 0);
+ arguments[2] = CYCastJSValue(context_, object);
+ JSObjectCallAsFunction(context_, Array_splice_, object_, 3, arguments, &exception);
+ CYThrow(context_, exception);
+}
+
+- (void) removeLastObject {
+ JSValueRef exception(NULL);
+ JSObjectCallAsFunction(context_, Array_pop_, object_, 0, NULL, &exception);
+ CYThrow(context_, exception);
+}
+
+- (void) removeObjectAtIndex:(NSUInteger)index {
+ size_t bounds([self count]);
+ if (index >= bounds)
+ @throw [NSException exceptionWithName:NSRangeException reason:[NSString stringWithFormat:@"*** -[CYJSArray removeObjectAtIndex:]: index (%zu) beyond bounds (%zu)", index, bounds] userInfo:nil];
+ JSValueRef exception(NULL);
+ JSValueRef arguments[2];
+ arguments[0] = CYCastJSValue(context_, index);
+ arguments[1] = CYCastJSValue(context_, 1);
+ JSObjectCallAsFunction(context_, Array_splice_, object_, 2, arguments, &exception);
+ CYThrow(context_, exception);
+}
+
+- (void) replaceObjectAtIndex:(NSUInteger)index withObject:(id)object {
+ size_t bounds([self count]);
+ if (index >= bounds)
+ @throw [NSException exceptionWithName:NSRangeException reason:[NSString stringWithFormat:@"*** -[CYJSArray replaceObjectAtIndex:withObject:]: index (%zu) beyond bounds (%zu)", index, bounds] userInfo:nil];
+ CYSetProperty(context_, object_, index, CYCastJSValue(context_, object));
+}
+
+@end
+
+NSString *CYCopyNSCYON(id value) {
+ NSString *string;
+
+ if (value == nil)
+ string = @"nil";
+ else {
+ Class _class(object_getClass(value));
+ SEL sel(@selector(cy$toCYON));
+
+ if (Method toCYON = class_getInstanceMethod(_class, sel))
+ string = reinterpret_cast<NSString *(*)(id, SEL)>(method_getImplementation(toCYON))(value, sel);
+ else if (Method methodSignatureForSelector = class_getInstanceMethod(_class, @selector(methodSignatureForSelector:))) {
+ if (reinterpret_cast<NSMethodSignature *(*)(id, SEL, SEL)>(method_getImplementation(methodSignatureForSelector))(value, @selector(methodSignatureForSelector:), sel) != nil)
+ string = [value cy$toCYON];
+ else goto fail;
+ } else fail: {
+ if (value == NSZombie_)
+ string = @"_NSZombie_";
+ else if (_class == NSZombie_)
+ string = [NSString stringWithFormat:@"<_NSZombie_: %p>", value];
+ // XXX: frowny /in/ the pants
+ else if (value == NSMessageBuilder_ || value == Object_)
+ string = nil;
+ else
+ string = [NSString stringWithFormat:@"%@", value];
+ }
+
+ // XXX: frowny pants
+ if (string == nil)
+ string = @"undefined";
+ }
+
+ return [string retain];
+}
+
+NSString *CYCopyNSCYON(JSContextRef context, JSValueRef value, JSValueRef *exception) {
+ if (JSValueIsNull(context, value))
+ return [@"null" retain];
+
+ CYTry {
+ CYPoolTry {
+ return CYCopyNSCYON(CYCastNSObject(NULL, context, value));
+ } CYPoolCatch(NULL)
+ } CYCatch
+}
+
+NSString *CYPoolNSCYON(apr_pool_t *pool, id value) {
+ return CYPoolRelease(pool, static_cast<id>(CYCopyNSCYON(value)));
+}
+
+const char *CYPoolCCYON(apr_pool_t *pool, JSContextRef context, JSValueRef value, JSValueRef *exception) {
+ if (NSString *json = CYCopyNSCYON(context, value, exception)) {
+ const char *string(CYPoolCString(pool, json));
+ [json release];
+ return string;
+ } else return NULL;
+}
+
+// XXX: use objc_getAssociatedObject and objc_setAssociatedObject on 10.6
+struct CYInternal :
+ CYData
+{
+ JSObjectRef object_;
+
+ CYInternal() :
+ object_(NULL)
+ {
+ }
+
+ ~CYInternal() {
+ // XXX: delete object_? ;(
+ }
+
+ static CYInternal *Get(id self) {
+ CYInternal *internal(NULL);
+ if (object_getInstanceVariable(self, "cy$internal_", reinterpret_cast<void **>(&internal)) == NULL) {
+ // XXX: do something epic? ;P
+ }
+
+ return internal;
+ }
+
+ static CYInternal *Set(id self) {
+ CYInternal *internal(NULL);
+ if (Ivar ivar = object_getInstanceVariable(self, "cy$internal_", reinterpret_cast<void **>(&internal))) {
+ if (internal == NULL) {
+ internal = new CYInternal();
+ object_setIvar(self, ivar, reinterpret_cast<id>(internal));
+ }
+ } else {
+ // XXX: do something epic? ;P
+ }
+
+ return internal;
+ }
+
+ bool HasProperty(JSContextRef context, JSStringRef name) {
+ if (object_ == NULL)
+ return false;
+ return JSObjectHasProperty(context, object_, name);
+ }
+
+ JSValueRef GetProperty(JSContextRef context, JSStringRef name) {
+ if (object_ == NULL)
+ return NULL;
+ return CYGetProperty(context, object_, name);
+ }
+
+ void SetProperty(JSContextRef context, JSStringRef name, JSValueRef value) {
+ if (object_ == NULL)
+ object_ = JSObjectMake(context, NULL, NULL);
+ CYSetProperty(context, object_, name, value);
+ }
+};
+
+static JSObjectRef CYMakeSelector(JSContextRef context, SEL sel) {
+ Selector_privateData *internal(new Selector_privateData(sel));
+ return JSObjectMake(context, Selector_, internal);
+}
+
+static JSObjectRef CYMakePointer(JSContextRef context, void *pointer, sig::Type *type, ffi_type *ffi, JSObjectRef owner) {
+ Pointer *internal(new Pointer(pointer, context, owner, type));
+ return JSObjectMake(context, Pointer_, internal);
+}
+
+static JSObjectRef CYMakeFunctor(JSContextRef context, void (*function)(), const char *type) {
+ Functor_privateData *internal(new Functor_privateData(type, function));
+ return JSObjectMake(context, Functor_, internal);
+}
+
+static const char *CYPoolCString(apr_pool_t *pool, JSStringRef value) {
+ if (pool == NULL) {
+ // XXX: this could be much more efficient
+ const char *string([CYCastNSString(NULL, value) UTF8String]);
+ return string;
+ } else {
+ size_t size(JSStringGetMaximumUTF8CStringSize(value));
+ char *string(new(pool) char[size]);
+ JSStringGetUTF8CString(value, string, size);
+ return string;
+ }
+}
+
+static const char *CYPoolCString(apr_pool_t *pool, JSContextRef context, JSValueRef value) {
+ return JSValueIsNull(context, value) ? NULL : CYPoolCString(pool, CYJSString(context, value));
+}
+
+static bool CYGetOffset(apr_pool_t *pool, JSStringRef value, ssize_t &index) {
+ return CYGetOffset(CYPoolCString(pool, value), index);
+}
+
+// XXX: this macro is unhygenic
+#define CYCastCString(context, value) ({ \
+ char *utf8; \
+ if (value == NULL) \
+ utf8 = NULL; \
+ else if (JSStringRef string = CYCopyJSString(context, value)) { \
+ size_t size(JSStringGetMaximumUTF8CStringSize(string)); \
+ utf8 = reinterpret_cast<char *>(alloca(size)); \
+ JSStringGetUTF8CString(string, utf8, size); \
+ JSStringRelease(string); \
+ } else \
+ utf8 = NULL; \
+ utf8; \
+})
+
+static void *CYCastPointer_(JSContextRef context, JSValueRef value) {
+ switch (JSValueGetType(context, value)) {
+ case kJSTypeNull:
+ return NULL;
+ /*case kJSTypeString:
+ return dlsym(RTLD_DEFAULT, CYCastCString(context, value));
+ case kJSTypeObject:
+ if (JSValueIsObjectOfClass(context, value, Pointer_)) {
+ Pointer *internal(reinterpret_cast<Pointer *>(JSObjectGetPrivate((JSObjectRef) value)));
+ return internal->value_;
+ }*/
+ default:
+ double number(CYCastDouble(context, value));
+ if (std::isnan(number))
+ @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"cannot convert value to pointer" userInfo:nil];
+ return reinterpret_cast<void *>(static_cast<uintptr_t>(static_cast<long long>(number)));
+ }
+}
+
+template <typename Type_>
+static _finline Type_ CYCastPointer(JSContextRef context, JSValueRef value) {
+ return reinterpret_cast<Type_>(CYCastPointer_(context, value));
+}
+
+static SEL CYCastSEL(JSContextRef context, JSValueRef value) {
+ if (JSValueIsObjectOfClass(context, value, Selector_)) {
+ Selector_privateData *internal(reinterpret_cast<Selector_privateData *>(JSObjectGetPrivate((JSObjectRef) value)));
+ return reinterpret_cast<SEL>(internal->value_);
+ } else
+ return CYCastPointer<SEL>(context, value);
+}
+
+static void CYPoolFFI(apr_pool_t *pool, JSContextRef context, sig::Type *type, ffi_type *ffi, void *data, JSValueRef value) {
+ switch (type->primitive) {
+ case sig::boolean_P:
+ *reinterpret_cast<bool *>(data) = JSValueToBoolean(context, value);
+ break;
+
+#define CYPoolFFI_(primitive, native) \
+ case sig::primitive ## _P: \
+ *reinterpret_cast<native *>(data) = CYCastDouble(context, value); \
+ break;
+
+ CYPoolFFI_(uchar, unsigned char)
+ CYPoolFFI_(char, char)
+ CYPoolFFI_(ushort, unsigned short)
+ CYPoolFFI_(short, short)
+ CYPoolFFI_(ulong, unsigned long)
+ CYPoolFFI_(long, long)
+ CYPoolFFI_(uint, unsigned int)
+ CYPoolFFI_(int, int)
+ CYPoolFFI_(ulonglong, unsigned long long)
+ CYPoolFFI_(longlong, long long)
+ CYPoolFFI_(float, float)
+ CYPoolFFI_(double, double)
+
+ case sig::object_P:
+ case sig::typename_P:
+ *reinterpret_cast<id *>(data) = CYCastNSObject(pool, context, value);
+ break;
+
+ case sig::selector_P:
+ *reinterpret_cast<SEL *>(data) = CYCastSEL(context, value);
+ break;
+
+ case sig::pointer_P:
+ *reinterpret_cast<void **>(data) = CYCastPointer<void *>(context, value);
+ break;
+
+ case sig::string_P:
+ *reinterpret_cast<const char **>(data) = CYPoolCString(pool, context, value);
+ break;
+
+ case sig::struct_P: {
+ uint8_t *base(reinterpret_cast<uint8_t *>(data));
+ JSObjectRef aggregate(JSValueIsObject(context, value) ? (JSObjectRef) value : NULL);
+ for (size_t index(0); index != type->data.signature.count; ++index) {
+ sig::Element *element(&type->data.signature.elements[index]);
+ ffi_type *field(ffi->elements[index]);
+
+ JSValueRef rhs;
+ if (aggregate == NULL)
+ rhs = value;
+ else {
+ rhs = CYGetProperty(context, aggregate, index);
+ if (JSValueIsUndefined(context, rhs)) {
+ if (element->name != NULL)
+ rhs = CYGetProperty(context, aggregate, CYJSString(element->name));
+ else
+ goto undefined;
+ if (JSValueIsUndefined(context, rhs)) undefined:
+ @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"unable to extract structure value" userInfo:nil];
+ }
+ }
+
+ CYPoolFFI(pool, context, element->type, field, base, rhs);
+ // XXX: alignment?
+ base += field->size;
+ }
+ } break;
+
+ case sig::void_P:
+ break;
+
+ default:
+ NSLog(@"CYPoolFFI(%c)\n", type->primitive);
+ _assert(false);
+ }
+}
+
+static JSValueRef CYFromFFI(JSContextRef context, sig::Type *type, ffi_type *ffi, void *data, bool initialize = false, JSObjectRef owner = NULL) {
+ JSValueRef value;
+
+ switch (type->primitive) {
+ case sig::boolean_P:
+ value = CYCastJSValue(context, *reinterpret_cast<bool *>(data));
+ break;
+
+#define CYFromFFI_(primitive, native) \
+ case sig::primitive ## _P: \
+ value = CYCastJSValue(context, *reinterpret_cast<native *>(data)); \
+ break;
+
+ CYFromFFI_(uchar, unsigned char)
+ CYFromFFI_(char, char)
+ CYFromFFI_(ushort, unsigned short)
+ CYFromFFI_(short, short)
+ CYFromFFI_(ulong, unsigned long)
+ CYFromFFI_(long, long)
+ CYFromFFI_(uint, unsigned int)
+ CYFromFFI_(int, int)
+ CYFromFFI_(ulonglong, unsigned long long)
+ CYFromFFI_(longlong, long long)
+ CYFromFFI_(float, float)
+ CYFromFFI_(double, double)
+
+ case sig::object_P: {
+ if (id object = *reinterpret_cast<id *>(data)) {
+ value = CYCastJSValue(context, object);
+ if (initialize)
+ [object release];
+ } else goto null;
+ } break;
+
+ case sig::typename_P:
+ value = CYMakeInstance(context, *reinterpret_cast<Class *>(data), true);
+ break;
+
+ case sig::selector_P:
+ if (SEL sel = *reinterpret_cast<SEL *>(data))
+ value = CYMakeSelector(context, sel);
+ else goto null;
+ break;
+
+ case sig::pointer_P:
+ if (void *pointer = *reinterpret_cast<void **>(data))
+ value = CYMakePointer(context, pointer, type->data.data.type, ffi, owner);
+ else goto null;
+ break;
+
+ case sig::string_P:
+ if (char *utf8 = *reinterpret_cast<char **>(data))
+ value = CYCastJSValue(context, utf8);
+ else goto null;
+ break;
+
+ case sig::struct_P:
+ value = CYMakeStruct(context, data, type, ffi, owner);
+ break;
+
+ case sig::void_P:
+ value = CYJSUndefined(context);
+ break;
+
+ null:
+ value = CYJSNull(context);
+ break;
+
+ default:
+ NSLog(@"CYFromFFI(%c)\n", type->primitive);
+ _assert(false);
+ }
+
+ return value;
+}
+
+static bool CYImplements(id object, Class _class, SEL selector, bool devoid) {
+ if (Method method = class_getInstanceMethod(_class, selector)) {
+ if (!devoid)
+ return true;
+ char type[16];
+ method_getReturnType(method, type, sizeof(type));
+ if (type[0] != 'v')
+ return true;
+ }
+
+ // XXX: possibly use a more "awesome" check?
+ return false;
+}
+
+static const char *CYPoolTypeEncoding(apr_pool_t *pool, Class _class, SEL sel, Method method) {
+ if (method != NULL)
+ return method_getTypeEncoding(method);
+ else if (NSString *type = [[Bridge_ objectAtIndex:1] objectForKey:CYCastNSString(pool, sel_getName(sel))])
+ return CYPoolCString(pool, type);
+ else
+ return NULL;
+}
+
+static void FunctionClosure_(ffi_cif *cif, void *result, void **arguments, void *arg) {
+ Closure_privateData *internal(reinterpret_cast<Closure_privateData *>(arg));
+
+ JSContextRef context(internal->context_);
+
+ size_t count(internal->cif_.nargs);
+ JSValueRef values[count];
+
+ for (size_t index(0); index != count; ++index)
+ values[index] = CYFromFFI(context, internal->signature_.elements[1 + index].type, internal->cif_.arg_types[index], arguments[index]);
+
+ JSValueRef value(CYCallAsFunction(context, internal->function_, NULL, count, values));
+ CYPoolFFI(NULL, context, internal->signature_.elements[0].type, internal->cif_.rtype, result, value);
+}
+
+static void MessageClosure_(ffi_cif *cif, void *result, void **arguments, void *arg) {
+ Closure_privateData *internal(reinterpret_cast<Closure_privateData *>(arg));
+
+ JSContextRef context(internal->context_);
+
+ size_t count(internal->cif_.nargs);
+ JSValueRef values[count];
+
+ for (size_t index(0); index != count; ++index)
+ values[index] = CYFromFFI(context, internal->signature_.elements[1 + index].type, internal->cif_.arg_types[index], arguments[index]);
+
+ JSObjectRef _this(CYCastJSObject(context, values[0]));
+
+ JSValueRef value(CYCallAsFunction(context, internal->function_, _this, count - 2, values + 2));
+ CYPoolFFI(NULL, context, internal->signature_.elements[0].type, internal->cif_.rtype, result, value);
+}
+
+static Closure_privateData *CYMakeFunctor_(JSContextRef context, JSObjectRef function, const char *type, void (*callback)(ffi_cif *, void *, void **, void *)) {
+ // XXX: in case of exceptions this will leak
+ // XXX: in point of fact, this may /need/ to leak :(
+ Closure_privateData *internal(new Closure_privateData(CYGetJSContext(), function, type));
+
+ ffi_closure *closure((ffi_closure *) _syscall(mmap(
+ NULL, sizeof(ffi_closure),
+ PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE,
+ -1, 0
+ )));
+
+ ffi_status status(ffi_prep_closure(closure, &internal->cif_, callback, internal));
+ _assert(status == FFI_OK);
+
+ _syscall(mprotect(closure, sizeof(*closure), PROT_READ | PROT_EXEC));
+
+ internal->value_ = closure;
+
+ return internal;
+}
+
+static JSObjectRef CYMakeFunctor(JSContextRef context, JSObjectRef function, const char *type) {
+ Closure_privateData *internal(CYMakeFunctor_(context, function, type, &FunctionClosure_));
+ return JSObjectMake(context, Functor_, internal);
+}
+
+static JSObjectRef CYMakeFunctor(JSContextRef context, JSValueRef value, const char *type) {
+ JSValueRef exception(NULL);
+ bool function(JSValueIsInstanceOfConstructor(context, value, Function_, &exception));
+ CYThrow(context, exception);
+
+ if (function) {
+ JSObjectRef function(CYCastJSObject(context, value));
+ return CYMakeFunctor(context, function, type);
+ } else {
+ void (*function)()(CYCastPointer<void (*)()>(context, value));
+ return CYMakeFunctor(context, function, type);
+ }
+}
+
+static JSObjectRef CYMakeMessage(JSContextRef context, SEL sel, IMP imp, const char *type) {
+ Message_privateData *internal(new Message_privateData(sel, type, imp));
+ return JSObjectMake(context, Message_, internal);
+}
+
+static IMP CYMakeMessage(JSContextRef context, JSValueRef value, const char *type) {
+ JSObjectRef function(CYCastJSObject(context, value));
+ Closure_privateData *internal(CYMakeFunctor_(context, function, type, &MessageClosure_));
+ return reinterpret_cast<IMP>(internal->GetValue());
+}
+
+static bool Messages_hasProperty(JSContextRef context, JSObjectRef object, JSStringRef property) {
+ Messages *internal(reinterpret_cast<Messages *>(JSObjectGetPrivate(object)));
+ Class _class(internal->GetValue());
+
+ CYPool pool;
+ const char *name(CYPoolCString(pool, property));
+
+ if (SEL sel = sel_getUid(name))
+ if (class_getInstanceMethod(_class, sel) != NULL)
+ return true;
+
+ return false;
+}
+
+static JSValueRef Messages_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) {
+ Messages *internal(reinterpret_cast<Messages *>(JSObjectGetPrivate(object)));
+ Class _class(internal->GetValue());
+
+ CYPool pool;
+ const char *name(CYPoolCString(pool, property));
+
+ if (SEL sel = sel_getUid(name))
+ if (Method method = class_getInstanceMethod(_class, sel))
+ return CYMakeMessage(context, sel, method_getImplementation(method), method_getTypeEncoding(method));
+
+ return NULL;
+}
+
+static bool Messages_setProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef *exception) {
+ Messages *internal(reinterpret_cast<Messages *>(JSObjectGetPrivate(object)));
+ Class _class(internal->GetValue());
+
+ CYPool pool;
+ const char *name(CYPoolCString(pool, property));
+
+ SEL sel(sel_registerName(name));
+
+ Method method(class_getInstanceMethod(_class, sel));
+
+ const char *type;
+ IMP imp;
+
+ if (JSValueIsObjectOfClass(context, value, Message_)) {
+ Message_privateData *message(reinterpret_cast<Message_privateData *>(JSObjectGetPrivate((JSObjectRef) value)));
+ type = sig::Unparse(pool, &message->signature_);
+ imp = reinterpret_cast<IMP>(message->GetValue());
+ } else {
+ type = CYPoolTypeEncoding(pool, _class, sel, method);
+ imp = CYMakeMessage(context, value, type);
+ }
+
+ if (method != NULL)
+ method_setImplementation(method, imp);
+ else
+ class_replaceMethod(_class, sel, imp, type);
+
+ return true;
+}
+
+#if !__OBJC2__
+static bool Messages_deleteProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) {
+ Messages *internal(reinterpret_cast<Messages *>(JSObjectGetPrivate(object)));
+ Class _class(internal->GetValue());
+
+ CYPool pool;
+ const char *name(CYPoolCString(pool, property));
+
+ if (SEL sel = sel_getUid(name))
+ if (Method method = class_getInstanceMethod(_class, sel)) {
+ objc_method_list list = {NULL, 1, {method}};
+ class_removeMethods(_class, &list);
+ return true;
+ }
+
+ return false;
+}
+#endif
+
+static void Messages_getPropertyNames(JSContextRef context, JSObjectRef object, JSPropertyNameAccumulatorRef names) {
+ Messages *internal(reinterpret_cast<Messages *>(JSObjectGetPrivate(object)));
+ Class _class(internal->GetValue());
+
+ unsigned int size;
+ Method *data(class_copyMethodList(_class, &size));
+ for (size_t i(0); i != size; ++i)
+ JSPropertyNameAccumulatorAddName(names, CYJSString(sel_getName(method_getName(data[i]))));
+ free(data);
+}
+
+static bool Instance_hasProperty(JSContextRef context, JSObjectRef object, JSStringRef property) {
+ Instance *internal(reinterpret_cast<Instance *>(JSObjectGetPrivate(object)));
+ id self(internal->GetValue());
+
+ if (JSStringIsEqualToUTF8CString(property, "$cyi"))
+ return true;
+
+ CYPool pool;
+ NSString *name(CYCastNSString(pool, property));
+
+ if (CYInternal *internal = CYInternal::Get(self))
+ if (internal->HasProperty(context, property))
+ return true;
+
+ Class _class(object_getClass(self));
+
+ CYPoolTry {
+ // XXX: this is an evil hack to deal with NSProxy; fix elsewhere
+ if (CYImplements(self, _class, @selector(cy$hasProperty:), false))
+ if ([self cy$hasProperty:name])
+ return true;
+ } CYPoolCatch(false)
+
+ const char *string(CYPoolCString(pool, name));
+
+ if (class_getProperty(_class, string) != NULL)
+ return true;
+
+ if (SEL sel = sel_getUid(string))
+ if (CYImplements(self, _class, sel, true))
+ return true;
+
+ return false;
+}
+
+static JSValueRef Instance_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) {
+ Instance *internal(reinterpret_cast<Instance *>(JSObjectGetPrivate(object)));
+ id self(internal->GetValue());
+
+ if (JSStringIsEqualToUTF8CString(property, "$cyi"))
+ return Internal::Make(context, self, object);
+
+ CYTry {
+ CYPool pool;
+ NSString *name(CYCastNSString(pool, property));
+
+ if (CYInternal *internal = CYInternal::Get(self))
+ if (JSValueRef value = internal->GetProperty(context, property))
+ return value;
+
+ CYPoolTry {
+ if (NSObject *data = [self cy$getProperty:name])
+ return CYCastJSValue(context, data);
+ } CYPoolCatch(NULL)
+
+ const char *string(CYPoolCString(pool, name));
+ Class _class(object_getClass(self));
+
+ if (objc_property_t property = class_getProperty(_class, string)) {
+ PropertyAttributes attributes(property);
+ SEL sel(sel_registerName(attributes.Getter()));
+ return CYSendMessage(pool, context, self, sel, 0, NULL, false, exception);
+ }
+
+ if (SEL sel = sel_getUid(string))
+ if (CYImplements(self, _class, sel, true))
+ return CYSendMessage(pool, context, self, sel, 0, NULL, false, exception);
+
+ return NULL;
+ } CYCatch
+}
+
+static bool Instance_setProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef *exception) {
+ Instance *internal(reinterpret_cast<Instance *>(JSObjectGetPrivate(object)));
+ id self(internal->GetValue());
+
+ CYPool pool;
+
+ CYTry {
+ NSString *name(CYCastNSString(pool, property));
+ NSString *data(CYCastNSObject(pool, context, value));
+
+ CYPoolTry {
+ if ([self cy$setProperty:name to:data])
+ return true;
+ } CYPoolCatch(NULL)
+
+ const char *string(CYPoolCString(pool, name));
+ Class _class(object_getClass(self));
+
+ if (objc_property_t property = class_getProperty(_class, string)) {
+ PropertyAttributes attributes(property);
+ if (const char *setter = attributes.Setter()) {
+ SEL sel(sel_registerName(setter));
+ JSValueRef arguments[1] = {value};
+ CYSendMessage(pool, context, self, sel, 1, arguments, false, exception);
+ return true;
+ }
+ }
+
+ size_t length(strlen(string));
+
+ char set[length + 5];
+
+ set[0] = 's';
+ set[1] = 'e';
+ set[2] = 't';
+
+ if (string[0] != '\0') {
+ set[3] = toupper(string[0]);
+ memcpy(set + 4, string + 1, length - 1);
+ }
+
+ set[length + 3] = ':';
+ set[length + 4] = '\0';
+
+ if (SEL sel = sel_getUid(set))
+ if (CYImplements(self, _class, sel, false)) {
+ JSValueRef arguments[1] = {value};
+ CYSendMessage(pool, context, self, sel, 1, arguments, false, exception);
+ }
+
+ if (CYInternal *internal = CYInternal::Set(self)) {
+ internal->SetProperty(context, property, value);
+ return true;
+ }
+
+ return false;
+ } CYCatch
+}
+
+static bool Instance_deleteProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) {
+ Instance *internal(reinterpret_cast<Instance *>(JSObjectGetPrivate(object)));
+ id self(internal->GetValue());
+
+ CYTry {
+ CYPoolTry {
+ NSString *name(CYCastNSString(NULL, property));
+ return [self cy$deleteProperty:name];
+ } CYPoolCatch(NULL)
+ } CYCatch
+}
+
+static void Instance_getPropertyNames(JSContextRef context, JSObjectRef object, JSPropertyNameAccumulatorRef names) {
+ Instance *internal(reinterpret_cast<Instance *>(JSObjectGetPrivate(object)));
+ id self(internal->GetValue());
+
+ CYPool pool;
+ Class _class(object_getClass(self));
+
+ {
+ unsigned int size;
+ objc_property_t *data(class_copyPropertyList(_class, &size));
+ for (size_t i(0); i != size; ++i)
+ JSPropertyNameAccumulatorAddName(names, CYJSString(property_getName(data[i])));
+ free(data);
+ }
+}
+
+static JSObjectRef Instance_callAsConstructor(JSContextRef context, JSObjectRef object, size_t count, const JSValueRef arguments[], JSValueRef *exception) {
+ CYTry {
+ Instance *internal(reinterpret_cast<Instance *>(JSObjectGetPrivate(object)));
+ JSObjectRef value(Instance::Make(context, [internal->GetValue() alloc], Instance::Uninitialized));
+ return value;
+ } CYCatch
+}
+
+static bool CYIsClass(id self) {
+ // XXX: this is a lame object_isClass
+ return class_getInstanceMethod(object_getClass(self), @selector(alloc)) != NULL;
+}
+
+static bool Instance_hasInstance(JSContextRef context, JSObjectRef constructor, JSValueRef instance, JSValueRef *exception) {
+ Instance *internal(reinterpret_cast<Instance *>(JSObjectGetPrivate((JSObjectRef) constructor)));
+ Class _class(internal->GetValue());
+ if (!CYIsClass(_class))
+ return false;
+
+ if (JSValueIsObjectOfClass(context, instance, Instance_)) {
+ Instance *linternal(reinterpret_cast<Instance *>(JSObjectGetPrivate((JSObjectRef) instance)));
+ // XXX: this isn't always safe
+ CYTry {
+ return [linternal->GetValue() isKindOfClass:_class];
+ } CYCatch
+ }
+
+ return false;
+}
+
+static bool Internal_hasProperty(JSContextRef context, JSObjectRef object, JSStringRef property) {
+ Internal *internal(reinterpret_cast<Internal *>(JSObjectGetPrivate(object)));
+ CYPool pool;
+
+ id self(internal->GetValue());
+ const char *name(CYPoolCString(pool, property));
+
+ if (object_getInstanceVariable(self, name, NULL) != NULL)
+ return true;
+
+ return false;
+}
+
+static JSValueRef Internal_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) {
+ Internal *internal(reinterpret_cast<Internal *>(JSObjectGetPrivate(object)));
+ CYPool pool;
+
+ CYTry {
+ id self(internal->GetValue());
+ const char *name(CYPoolCString(pool, property));
+
+ if (Ivar ivar = object_getInstanceVariable(self, name, NULL)) {
+ Type_privateData type(pool, ivar_getTypeEncoding(ivar));
+ return CYFromFFI(context, type.type_, type.GetFFI(), reinterpret_cast<uint8_t *>(self) + ivar_getOffset(ivar));
+ }
+
+ return NULL;
+ } CYCatch
+}
+
+static bool Internal_setProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef *exception) {
+ Internal *internal(reinterpret_cast<Internal *>(JSObjectGetPrivate(object)));
+ CYPool pool;
+
+ CYTry {
+ id self(internal->GetValue());
+ const char *name(CYPoolCString(pool, property));
+
+ if (Ivar ivar = object_getInstanceVariable(self, name, NULL)) {
+ Type_privateData type(pool, ivar_getTypeEncoding(ivar));
+ CYPoolFFI(pool, context, type.type_, type.GetFFI(), reinterpret_cast<uint8_t *>(self) + ivar_getOffset(ivar), value);
+ return true;
+ }
+
+ return false;
+ } CYCatch
+}
+
+static void Internal_getPropertyNames_(Class _class, JSPropertyNameAccumulatorRef names) {
+ if (Class super = class_getSuperclass(_class))
+ Internal_getPropertyNames_(super, names);
+
+ unsigned int size;
+ Ivar *data(class_copyIvarList(_class, &size));
+ for (size_t i(0); i != size; ++i)
+ JSPropertyNameAccumulatorAddName(names, CYJSString(ivar_getName(data[i])));
+ free(data);
+}
+
+static void Internal_getPropertyNames(JSContextRef context, JSObjectRef object, JSPropertyNameAccumulatorRef names) {
+ Internal *internal(reinterpret_cast<Internal *>(JSObjectGetPrivate(object)));
+ CYPool pool;
+
+ id self(internal->GetValue());
+ Class _class(object_getClass(self));
+
+ Internal_getPropertyNames_(_class, names);
+}
+
+static JSValueRef Internal_callAsFunction_$cya(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) {
+ Internal *internal(reinterpret_cast<Internal *>(JSObjectGetPrivate(object)));
+ return internal->GetOwner();
+}
+
+static bool Index_(apr_pool_t *pool, Struct_privateData *internal, JSStringRef property, ssize_t &index, uint8_t *&base) {
+ Type_privateData *typical(internal->type_);
+ sig::Type *type(typical->type_);
+ if (type == NULL)
+ return false;
+
+ const char *name(CYPoolCString(pool, property));
+ size_t length(strlen(name));
+ double number(CYCastDouble(name, length));
+
+ size_t count(type->data.signature.count);
+
+ if (std::isnan(number)) {
+ if (property == NULL)
+ return false;
+
+ sig::Element *elements(type->data.signature.elements);
+
+ for (size_t local(0); local != count; ++local) {
+ sig::Element *element(&elements[local]);
+ if (element->name != NULL && strcmp(name, element->name) == 0) {
+ index = local;
+ goto base;
+ }
+ }
+
+ return false;
+ } else {
+ index = static_cast<ssize_t>(number);
+ if (index != number || index < 0 || static_cast<size_t>(index) >= count)
+ return false;
+ }
+
+ base:
+ ffi_type **elements(typical->GetFFI()->elements);
+
+ base = reinterpret_cast<uint8_t *>(internal->value_);
+ for (ssize_t local(0); local != index; ++local)
+ base += elements[local]->size;
+
+ return true;
+}
+
+static JSValueRef Pointer_getIndex(JSContextRef context, JSObjectRef object, size_t index, JSValueRef *exception) {
+ Pointer *internal(reinterpret_cast<Pointer *>(JSObjectGetPrivate(object)));
+ Type_privateData *typical(internal->type_);
+
+ ffi_type *ffi(typical->GetFFI());
+
+ uint8_t *base(reinterpret_cast<uint8_t *>(internal->value_));
+ base += ffi->size * index;
+
+ JSObjectRef owner(internal->GetOwner() ?: object);
+
+ CYTry {
+ return CYFromFFI(context, typical->type_, ffi, base, false, owner);
+ } CYCatch
+}
+
+static JSValueRef Pointer_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) {
+ CYPool pool;
+ Pointer *internal(reinterpret_cast<Pointer *>(JSObjectGetPrivate(object)));
+ Type_privateData *typical(internal->type_);
+
+ if (typical->type_ == NULL)
+ return NULL;
+
+ ssize_t offset;
+ if (!CYGetOffset(pool, property, offset))
+ return NULL;
+
+ return Pointer_getIndex(context, object, offset, exception);
+}
+
+static JSValueRef Pointer_getProperty_$cyi(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) {
+ return Pointer_getIndex(context, object, 0, exception);
+}
+
+static bool Pointer_setIndex(JSContextRef context, JSObjectRef object, size_t index, JSValueRef value, JSValueRef *exception) {
+ Pointer *internal(reinterpret_cast<Pointer *>(JSObjectGetPrivate(object)));
+ Type_privateData *typical(internal->type_);
+
+ ffi_type *ffi(typical->GetFFI());
+
+ uint8_t *base(reinterpret_cast<uint8_t *>(internal->value_));
+ base += ffi->size * index;
+
+ CYTry {
+ CYPoolFFI(NULL, context, typical->type_, ffi, base, value);
+ return true;
+ } CYCatch
+}
+
+static bool Pointer_setProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef *exception) {
+ CYPool pool;
+ Pointer *internal(reinterpret_cast<Pointer *>(JSObjectGetPrivate(object)));
+ Type_privateData *typical(internal->type_);
+
+ if (typical->type_ == NULL)
+ return NULL;
+
+ ssize_t offset;
+ if (!CYGetOffset(pool, property, offset))
+ return NULL;
+
+ return Pointer_setIndex(context, object, offset, value, exception);
+}
+
+static bool Pointer_setProperty_$cyi(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef *exception) {
+ return Pointer_setIndex(context, object, 0, value, exception);
+}
+
+static JSValueRef Struct_callAsFunction_$cya(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) {
+ Struct_privateData *internal(reinterpret_cast<Struct_privateData *>(JSObjectGetPrivate(_this)));
+ Type_privateData *typical(internal->type_);
+ return CYMakePointer(context, internal->value_, typical->type_, typical->ffi_, _this);
+}
+
+static JSValueRef Struct_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) {
+ CYPool pool;
+ Struct_privateData *internal(reinterpret_cast<Struct_privateData *>(JSObjectGetPrivate(object)));
+ Type_privateData *typical(internal->type_);
+
+ ssize_t index;
+ uint8_t *base;
+
+ if (!Index_(pool, internal, property, index, base))
+ return NULL;
+
+ JSObjectRef owner(internal->GetOwner() ?: object);
+
+ CYTry {
+ return CYFromFFI(context, typical->type_->data.signature.elements[index].type, typical->GetFFI()->elements[index], base, false, owner);
+ } CYCatch
+}
+
+static bool Struct_setProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef *exception) {
+ CYPool pool;
+ Struct_privateData *internal(reinterpret_cast<Struct_privateData *>(JSObjectGetPrivate(object)));
+ Type_privateData *typical(internal->type_);
+
+ ssize_t index;
+ uint8_t *base;
+
+ if (!Index_(pool, internal, property, index, base))
+ return false;
+
+ CYTry {
+ CYPoolFFI(NULL, context, typical->type_->data.signature.elements[index].type, typical->GetFFI()->elements[index], base, value);
+ return true;
+ } CYCatch
+}
+
+static void Struct_getPropertyNames(JSContextRef context, JSObjectRef object, JSPropertyNameAccumulatorRef names) {
+ Struct_privateData *internal(reinterpret_cast<Struct_privateData *>(JSObjectGetPrivate(object)));
+ Type_privateData *typical(internal->type_);
+ sig::Type *type(typical->type_);
+
+ if (type == NULL)
+ return;
+
+ size_t count(type->data.signature.count);
+ sig::Element *elements(type->data.signature.elements);
+
+ char number[32];
+
+ for (size_t index(0); index != count; ++index) {
+ const char *name;
+ name = elements[index].name;
+
+ if (name == NULL) {
+ sprintf(number, "%lu", index);
+ name = number;
+ }
+
+ JSPropertyNameAccumulatorAddName(names, CYJSString(name));
+ }
+}
+
+JSValueRef CYCallFunction(apr_pool_t *pool, JSContextRef context, size_t setups, void *setup[], size_t count, const JSValueRef arguments[], bool initialize, JSValueRef *exception, sig::Signature *signature, ffi_cif *cif, void (*function)()) {
+ CYTry {
+ if (setups + count != signature->count - 1)
+ @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"incorrect number of arguments to ffi function" userInfo:nil];
+
+ size_t size(setups + count);
+ void *values[size];
+ memcpy(values, setup, sizeof(void *) * setups);
+
+ for (size_t index(setups); index != size; ++index) {
+ sig::Element *element(&signature->elements[index + 1]);
+ ffi_type *ffi(cif->arg_types[index]);
+ // XXX: alignment?
+ values[index] = new(pool) uint8_t[ffi->size];
+ CYPoolFFI(pool, context, element->type, ffi, values[index], arguments[index - setups]);
+ }
+
+ uint8_t value[cif->rtype->size];
+ ffi_call(cif, function, value, values);
+
+ return CYFromFFI(context, signature->elements[0].type, cif->rtype, value, initialize);
+ } CYCatch
+}
+
+static JSValueRef ObjectiveC_Classes_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) {
+ CYTry {
+ CYPool pool;
+ NSString *name(CYCastNSString(pool, property));
+ if (Class _class = NSClassFromString(name))
+ return CYMakeInstance(context, _class, true);
+ return NULL;
+ } CYCatch
+}
+
+static void ObjectiveC_Classes_getPropertyNames(JSContextRef context, JSObjectRef object, JSPropertyNameAccumulatorRef names) {
+ size_t size(objc_getClassList(NULL, 0));
+ Class *data(reinterpret_cast<Class *>(malloc(sizeof(Class) * size)));
+
+ get:
+ size_t writ(objc_getClassList(data, size));
+ if (size < writ) {
+ size = writ;
+ if (Class *copy = reinterpret_cast<Class *>(realloc(data, sizeof(Class) * writ))) {
+ data = copy;
+ goto get;
+ } else goto done;
+ }
+
+ for (size_t i(0); i != writ; ++i)
+ JSPropertyNameAccumulatorAddName(names, CYJSString(class_getName(data[i])));
+
+ done:
+ free(data);
+}
+
+static JSValueRef ObjectiveC_Image_Classes_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) {
+ const char *internal(reinterpret_cast<const char *>(JSObjectGetPrivate(object)));
+
+ CYTry {
+ CYPool pool;
+ const char *name(CYPoolCString(pool, property));
+ unsigned int size;
+ const char **data(objc_copyClassNamesForImage(internal, &size));
+ JSValueRef value;
+ for (size_t i(0); i != size; ++i)
+ if (strcmp(name, data[i]) == 0) {
+ if (Class _class = objc_getClass(name)) {
+ value = CYMakeInstance(context, _class, true);
+ goto free;
+ } else
+ break;
+ }
+ value = NULL;
+ free:
+ free(data);
+ return value;
+ } CYCatch
+}
+
+static void ObjectiveC_Image_Classes_getPropertyNames(JSContextRef context, JSObjectRef object, JSPropertyNameAccumulatorRef names) {
+ const char *internal(reinterpret_cast<const char *>(JSObjectGetPrivate(object)));
+ unsigned int size;
+ const char **data(objc_copyClassNamesForImage(internal, &size));
+ for (size_t i(0); i != size; ++i)
+ JSPropertyNameAccumulatorAddName(names, CYJSString(data[i]));
+ free(data);
+}
+
+static JSValueRef ObjectiveC_Images_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) {
+ CYTry {
+ CYPool pool;
+ const char *name(CYPoolCString(pool, property));
+ unsigned int size;
+ const char **data(objc_copyImageNames(&size));
+ for (size_t i(0); i != size; ++i)
+ if (strcmp(name, data[i]) == 0) {
+ name = data[i];
+ goto free;
+ }
+ name = NULL;
+ free:
+ free(data);
+ if (name == NULL)
+ return NULL;
+ JSObjectRef value(JSObjectMake(context, NULL, NULL));
+ CYSetProperty(context, value, CYJSString("classes"), JSObjectMake(context, ObjectiveC_Image_Classes_, const_cast<char *>(name)));
+ return value;
+ } CYCatch
+}
+
+static void ObjectiveC_Images_getPropertyNames(JSContextRef context, JSObjectRef object, JSPropertyNameAccumulatorRef names) {
+ unsigned int size;
+ const char **data(objc_copyImageNames(&size));
+ for (size_t i(0); i != size; ++i)
+ JSPropertyNameAccumulatorAddName(names, CYJSString(data[i]));
+ free(data);
+}
+
+static JSValueRef ObjectiveC_Protocols_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) {
+ CYTry {
+ CYPool pool;
+ NSString *name(CYCastNSString(pool, property));
+ if (Protocol *protocol = NSProtocolFromString(name))
+ return CYMakeInstance(context, protocol, true);
+ return NULL;
+ } CYCatch
+}
+
+static void ObjectiveC_Protocols_getPropertyNames(JSContextRef context, JSObjectRef object, JSPropertyNameAccumulatorRef names) {
+ unsigned int size;
+ Protocol **data(objc_copyProtocolList(&size));
+ for (size_t i(0); i != size; ++i)
+ JSPropertyNameAccumulatorAddName(names, CYJSString(protocol_getName(data[i])));
+ free(data);
+}
+
+static JSObjectRef CYMakeType(JSContextRef context, const char *type) {
+ Type_privateData *internal(new Type_privateData(NULL, type));
+ return JSObjectMake(context, Type_, internal);
+}
+
+static JSObjectRef CYMakeType(JSContextRef context, sig::Type *type) {
+ Type_privateData *internal(new Type_privateData(type));
+ return JSObjectMake(context, Type_, internal);
+}
+
+static JSValueRef Runtime_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) {
+ if (JSStringIsEqualToUTF8CString(property, "nil"))
+ return Instance::Make(context, nil);
+
+ CYTry {
+ CYPool pool;
+ NSString *name(CYCastNSString(pool, property));
+ if (Class _class = NSClassFromString(name))
+ return CYMakeInstance(context, _class, true);
+ if (NSMutableArray *entry = [[Bridge_ objectAtIndex:0] objectForKey:name])
+ switch ([[entry objectAtIndex:0] intValue]) {
+ case 0:
+ return JSEvaluateScript(CYGetJSContext(), CYJSString([entry objectAtIndex:1]), NULL, NULL, 0, NULL);
+ case 1:
+ return CYMakeFunctor(context, reinterpret_cast<void (*)()>([name cy$symbol]), CYPoolCString(pool, [entry objectAtIndex:1]));
+ case 2:
+ // XXX: this is horrendously inefficient
+ sig::Signature signature;
+ sig::Parse(pool, &signature, CYPoolCString(pool, [entry objectAtIndex:1]), &Structor_);
+ ffi_cif cif;
+ sig::sig_ffi_cif(pool, &sig::ObjectiveC, &signature, &cif);
+ return CYFromFFI(context, signature.elements[0].type, cif.rtype, [name cy$symbol]);
+ }
+ if (NSMutableArray *entry = [[Bridge_ objectAtIndex:2] objectForKey:name])
+ switch ([[entry objectAtIndex:0] intValue]) {
+ // XXX: implement case 0
+ case 1:
+ return CYMakeType(context, CYPoolCString(pool, [entry objectAtIndex:1]));
+ }
+ return NULL;
+ } CYCatch
+}
+
+static bool stret(ffi_type *ffi_type) {
+ return ffi_type->type == FFI_TYPE_STRUCT && (
+ ffi_type->size > OBJC_MAX_STRUCT_BY_VALUE ||
+ struct_forward_array[ffi_type->size] != 0
+ );
+}
+
+extern "C" {
+ int *_NSGetArgc(void);
+ char ***_NSGetArgv(void);
+ int UIApplicationMain(int argc, char *argv[], NSString *principalClassName, NSString *delegateClassName);
+}
+
+static JSValueRef System_print(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) {
+ CYTry {
+ NSLog(@"%s", CYCastCString(context, arguments[0]));
+ return CYJSUndefined(context);
+ } CYCatch
+}
+
+JSValueRef CYSendMessage(apr_pool_t *pool, JSContextRef context, id self, SEL _cmd, size_t count, const JSValueRef arguments[], bool initialize, JSValueRef *exception) {
+ const char *type;
+
+ Class _class(object_getClass(self));
+ if (Method method = class_getInstanceMethod(_class, _cmd))
+ type = method_getTypeEncoding(method);
+ else {
+ CYTry {
+ CYPoolTry {
+ NSMethodSignature *method([self methodSignatureForSelector:_cmd]);
+ if (method == nil)
+ @throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"unrecognized selector %s sent to object %p", sel_getName(_cmd), self] userInfo:nil];
+ type = CYPoolCString(pool, [method _typeString]);
+ } CYPoolCatch(NULL)
+ } CYCatch
+ }
+
+ void *setup[2];
+ setup[0] = &self;
+ setup[1] = &_cmd;
+
+ sig::Signature signature;
+ sig::Parse(pool, &signature, type, &Structor_);
+
+ ffi_cif cif;
+ sig::sig_ffi_cif(pool, &sig::ObjectiveC, &signature, &cif);
+
+ void (*function)() = stret(cif.rtype) ? reinterpret_cast<void (*)()>(&objc_msgSend_stret) : reinterpret_cast<void (*)()>(&objc_msgSend);
+ return CYCallFunction(pool, context, 2, setup, count, arguments, initialize, exception, &signature, &cif, function);
+}
+
+static size_t Nonce_(0);
+
+static JSValueRef $cyq(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) {
+ char name[16];
+ sprintf(name, "%s%zu", CYCastCString(context, arguments[0]), Nonce_++);
+ return CYCastJSValue(context, name);
+}
+
+static JSValueRef $objc_msgSend(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) {
+ CYPool pool;
+
+ bool uninitialized;
+
+ id self;
+ SEL _cmd;
+
+ CYTry {
+ if (count < 2)
+ @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"too few arguments to objc_msgSend" userInfo:nil];
+
+ if (JSValueIsObjectOfClass(context, arguments[0], Instance_)) {
+ Instance *internal(reinterpret_cast<Instance *>(JSObjectGetPrivate((JSObjectRef) arguments[0])));
+ self = internal->GetValue();
+ uninitialized = internal->IsUninitialized();
+ if (uninitialized)
+ internal->value_ = nil;
+ } else {
+ self = CYCastNSObject(pool, context, arguments[0]);
+ uninitialized = false;
+ }
+
+ if (self == nil)
+ return CYJSNull(context);
+
+ _cmd = CYCastSEL(context, arguments[1]);
+ } CYCatch
+
+ return CYSendMessage(pool, context, self, _cmd, count - 2, arguments + 2, uninitialized, exception);
+}
+
+/* Hook: objc_registerClassPair {{{ */
+// XXX: replace this with associated objects
+
+MSHook(void, CYDealloc, id self, SEL sel) {
+ CYInternal *internal;
+ object_getInstanceVariable(self, "cy$internal_", reinterpret_cast<void **>(&internal));
+ if (internal != NULL)
+ delete internal;
+ _CYDealloc(self, sel);
+}
+
+MSHook(void, objc_registerClassPair, Class _class) {
+ Class super(class_getSuperclass(_class));
+ if (super == NULL || class_getInstanceVariable(super, "cy$internal_") == NULL) {
+ class_addIvar(_class, "cy$internal_", sizeof(CYInternal *), log2(sizeof(CYInternal *)), "^{CYInternal}");
+ MSHookMessage(_class, @selector(dealloc), MSHake(CYDealloc));
+ }
+
+ _objc_registerClassPair(_class);
+}
+
+static JSValueRef objc_registerClassPair_(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) {
+ CYTry {
+ if (count != 1)
+ @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"incorrect number of arguments to objc_registerClassPair" userInfo:nil];
+ CYPool pool;
+ Class _class(CYCastNSObject(pool, context, arguments[0]));
+ $objc_registerClassPair(_class);
+ return CYJSUndefined(context);
+ } CYCatch
+}
+/* }}} */
+
+static JSValueRef Selector_callAsFunction(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) {
+ JSValueRef setup[count + 2];
+ setup[0] = _this;
+ setup[1] = object;
+ memcpy(setup + 2, arguments, sizeof(JSValueRef) * count);
+ return $objc_msgSend(context, NULL, NULL, count + 2, setup, exception);
+}
+
+static JSValueRef Message_callAsFunction(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) {
+ CYPool pool;
+ Message_privateData *internal(reinterpret_cast<Message_privateData *>(JSObjectGetPrivate(object)));
+
+ // XXX: handle Instance::Uninitialized?
+ id self(CYCastNSObject(pool, context, _this));
+
+ void *setup[2];
+ setup[0] = &self;
+ setup[1] = &internal->sel_;
+
+ return CYCallFunction(pool, context, 2, setup, count, arguments, false, exception, &internal->signature_, &internal->cif_, internal->GetValue());