X-Git-Url: https://git.saurik.com/cycript.git/blobdiff_plain/c1582939561770d8655b9378ec80de362bad89b4..5a36c6cf6ef94d7f7c6b55c8acf05e81ea0c64b3:/Tweak.mm diff --git a/Tweak.mm b/Tweak.mm index 268862f..0efe892 100644 --- a/Tweak.mm +++ b/Tweak.mm @@ -1,7 +1,8 @@ -/* Cyrker - Remove Execution Server and Disassembler +/* Cyrker - Remove Execution Server and Disassembler * Copyright (C) 2009 Jay Freeman (saurik) */ +/* Modified BSD License {{{ */ /* * Redistribution and use in source and binary * forms, with or without modification, are permitted @@ -34,8 +35,16 @@ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +/* }}} */ #include +#include "Struct.hpp" + +#include "sig/parse.hpp" +#include "sig/ffi_type.hpp" + +#include +#include #include @@ -58,20 +67,94 @@ #include #include +#undef _assert +#undef _trace + /* XXX: bad _assert */ #define _assert(test) do { \ if ((test)) break; \ - CFLog(kCFLogLevelNotice, CFSTR("_assert(%u)"), __LINE__); \ + CFLog(kCFLogLevelNotice, CFSTR("_assert(%s):%u"), #test, __LINE__); \ + throw; \ } while (false) #define _trace() do { \ - CFLog(kCFLogLevelNotice, CFSTR("_trace(%u)"), __LINE__); \ + CFLog(kCFLogLevelNotice, CFSTR("_trace():%u"), __LINE__); \ } while (false) +/* Objective-C Handle<> {{{ */ +template +class _H { + typedef _H This_; + + private: + Type_ *value_; + + _finline void Retain_() { + if (value_ != nil) + [value_ retain]; + } + + _finline void Clear_() { + if (value_ != nil) + [value_ release]; + } + + public: + _finline _H(const This_ &rhs) : + value_(rhs.value_ == nil ? nil : [rhs.value_ retain]) + { + } + + _finline _H(Type_ *value = NULL, bool mended = false) : + value_(value) + { + if (!mended) + Retain_(); + } + + _finline ~_H() { + Clear_(); + } + + _finline operator Type_ *() const { + return value_; + } + + _finline This_ &operator =(Type_ *value) { + if (value_ != value) { + Type_ *old(value_); + value_ = value; + Retain_(); + if (old != nil) + [old release]; + } return *this; + } +}; +/* }}} */ + +#define _pooled _H _pool([[NSAutoreleasePool alloc] init], true); + +void *operator new(size_t size, apr_pool_t *pool) { + return apr_palloc(pool, size); +} + +void *operator new [](size_t size, apr_pool_t *pool) { + return apr_palloc(pool, size); +} + static JSContextRef Context_; + +static JSClassRef ffi_; static JSClassRef joc_; +static JSClassRef ptr_; +static JSClassRef sel_; + static JSObjectRef Array_; + +static JSStringRef name_; +static JSStringRef message_; static JSStringRef length_; + static Class NSCFBoolean_; struct Client { @@ -79,27 +162,51 @@ struct Client { CFSocketRef socket_; }; +JSObjectRef CYMakeObject(JSContextRef context, id object) { + return JSObjectMake(context, joc_, [object retain]); +} + +@interface NSMethodSignature (Cyrver) +- (NSString *) _typeString; +@end + @interface NSObject (Cyrver) - (NSString *) cy$toJSON; +- (JSValueRef) cy$JSValueInContext:(JSContextRef)context; @end @implementation NSObject (Cyrver) + - (NSString *) cy$toJSON { - return [NSString stringWithFormat:@"<%@>", [self description]]; -} @end + return [self description]; +} + +- (JSValueRef) cy$JSValueInContext:(JSContextRef)context { + return CYMakeObject(context, self); +} + +@end @implementation WebUndefined (Cyrver) + - (NSString *) cy$toJSON { return @"undefined"; -} @end +} + +- (JSValueRef) cy$JSValueInContext:(JSContextRef)context { + return JSValueMakeUndefined(context); +} + +@end @implementation NSArray (Cyrver) + - (NSString *) cy$toJSON { NSMutableString *json([[[NSMutableString alloc] init] autorelease]); [json appendString:@"["]; bool comma(false); - for (NSObject *object in self) { + for (id object in self) { if (comma) [json appendString:@","]; else @@ -109,18 +216,52 @@ struct Client { [json appendString:@"]"]; return json; -} @end +} + +@end + +@implementation NSDictionary (Cyrver) + +- (NSString *) cy$toJSON { + NSMutableString *json([[[NSMutableString alloc] init] autorelease]); + [json appendString:@"("]; + [json appendString:@"{"]; + + bool comma(false); + for (id key in self) { + if (comma) + [json appendString:@","]; + else + comma = true; + [json appendString:[key cy$toJSON]]; + [json appendString:@":"]; + NSObject *object([self objectForKey:key]); + [json appendString:[object cy$toJSON]]; + } + + [json appendString:@"})"]; + return json; +} + +@end @implementation NSNumber (Cyrver) + - (NSString *) cy$toJSON { return [self class] != NSCFBoolean_ ? [self stringValue] : [self boolValue] ? @"true" : @"false"; -} @end +} + +- (JSValueRef) cy$JSValueInContext:(JSContextRef)context { + return [self class] != NSCFBoolean_ ? JSValueMakeNumber(context, [self doubleValue]) : JSValueMakeBoolean(context, [self boolValue]); +} + +@end @implementation NSString (Cyrver) + - (NSString *) cy$toJSON { CFMutableStringRef json(CFStringCreateMutableCopy(kCFAllocatorDefault, 0, (CFStringRef) self)); - /* XXX: I can't believe there isn't a safe helper function for this... */ 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); @@ -130,10 +271,27 @@ struct Client { CFStringInsert(json, 0, CFSTR("\"")); CFStringAppend(json, CFSTR("\"")); - return (NSString *) json; -} @end + return [reinterpret_cast(json) autorelease]; +} + +@end + +@interface CYJSObject : NSDictionary { + JSObjectRef object_; + JSContextRef context_; +} + +- (id) initWithJSObject:(JSObjectRef)object inContext:(JSContextRef)context; -@interface CY$JSArray : NSArray { +- (NSUInteger) count; +- (id) objectForKey:(id)key; +- (NSEnumerator *) keyEnumerator; +- (void) setObject:(id)object forKey:(id)key; +- (void) removeObjectForKey:(id)key; + +@end + +@interface CYJSArray : NSArray { JSObjectRef object_; JSContextRef context_; } @@ -145,61 +303,177 @@ struct Client { @end -static id JSObjectToNSObject(JSContextRef ctx, JSObjectRef object) { - if (JSValueIsObjectOfClass(ctx, object, joc_)) - return reinterpret_cast(JSObjectGetPrivate(object)); - else if (JSValueIsInstanceOfConstructor(ctx, object, Array_, NULL)) - return [[[CY$JSArray alloc] initWithJSObject:object inContext:ctx] autorelease]; - else - return @"Hello"; - //return [[[CY$JSObject alloc] initWithJSObject:object inContext:ctx] autorelease]; +JSContextRef JSGetContext() { + return Context_; } -static CFTypeRef JSValueToCFTypeCopy(JSContextRef ctx, JSValueRef value) { - JSType type(JSValueGetType(ctx, value)); +#define CYCatch \ + @catch (id error) { \ + CYThrow(context, error, exception); \ + return NULL; \ + } - switch (type) { - case kJSTypeUndefined: - return [WebUndefined undefined]; - break; +void CYThrow(JSContextRef context, JSValueRef value); - case kJSTypeNull: - return nil; - break; +id CYCastNSObject(JSContextRef context, JSObjectRef object) { + if (JSValueIsObjectOfClass(context, object, joc_)) + return reinterpret_cast(JSObjectGetPrivate(object)); + JSValueRef exception(NULL); + bool array(JSValueIsInstanceOfConstructor(context, object, Array_, &exception)); + CYThrow(context, exception); + if (array) + return [[[CYJSArray alloc] initWithJSObject:object inContext:context] autorelease]; + return [[[CYJSObject alloc] initWithJSObject:object inContext:context] autorelease]; +} - case kJSTypeBoolean: - return CFRetain(JSValueToBoolean(ctx, value) ? kCFBooleanTrue : kCFBooleanFalse); - break; +CFStringRef CYCopyCFString(JSStringRef value) { + return JSStringCopyCFString(kCFAllocatorDefault, value); +} - case kJSTypeNumber: { - double number(JSValueToNumber(ctx, value, NULL)); - return CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &number); - } break; +CFStringRef CYCopyCFString(JSContextRef context, JSValueRef value) { + JSValueRef exception(NULL); + JSStringRef string(JSValueToStringCopy(context, value, &exception)); + CYThrow(context, exception); + CFStringRef object(CYCopyCFString(string)); + JSStringRelease(string); + return object; +} - case kJSTypeString: { - JSStringRef string(JSValueToStringCopy(ctx, value, NULL)); - CFStringRef object(JSStringCopyCFString(kCFAllocatorDefault, string)); - JSStringRelease(string); - return object; - } break; +CFNumberRef CYCopyCFNumber(JSContextRef context, JSValueRef value) { + JSValueRef exception(NULL); + double number(JSValueToNumber(context, value, &exception)); + CYThrow(context, exception); + return CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &number); +} - case kJSTypeObject: - return CFRetain((CFTypeRef) JSObjectToNSObject(ctx, (JSObjectRef) value)); - break; +NSString *CYCastNSString(JSStringRef value) { + return [reinterpret_cast(CYCopyCFString(value)) autorelease]; +} +CFTypeRef CYCopyCFType(JSContextRef context, JSValueRef value) { + switch (JSValueGetType(context, value)) { + case kJSTypeUndefined: + return CFRetain([WebUndefined undefined]); + case kJSTypeNull: + return nil; + case kJSTypeBoolean: + return CFRetain(JSValueToBoolean(context, value) ? kCFBooleanTrue : kCFBooleanFalse); + case kJSTypeNumber: + return CYCopyCFNumber(context, value); + case kJSTypeString: + return CYCopyCFString(context, value); + case kJSTypeObject: + return CFRetain((CFTypeRef) CYCastNSObject(context, (JSObjectRef) value)); default: _assert(false); - return NULL; - break; } } -static id JSValueToNSObject(JSContextRef ctx, JSValueRef value) { - id object((id) JSValueToCFTypeCopy(ctx, value)); +NSArray *CYCastNSArray(JSPropertyNameArrayRef names) { + size_t size(JSPropertyNameArrayGetCount(names)); + NSMutableArray *array([NSMutableArray arrayWithCapacity:size]); + for (size_t index(0); index != size; ++index) + [array addObject:CYCastNSString(JSPropertyNameArrayGetNameAtIndex(names, index))]; + return array; +} + +id CYCastNSObject(JSContextRef context, JSValueRef value) { + const NSObject *object(reinterpret_cast(CYCopyCFType(context, value))); return object == nil ? nil : [object autorelease]; } -@implementation CY$JSArray +void CYThrow(JSContextRef context, JSValueRef value) { + if (value == NULL) + return; + @throw CYCastNSObject(context, value); +} + +JSValueRef CYCastJSValue(JSContextRef context, id value) { + return value == nil ? JSValueMakeNull(context) : [value cy$JSValueInContext:context]; +} + +JSStringRef CYCopyJSString(id value) { + return JSStringCreateWithCFString(reinterpret_cast([value description])); +} + +JSStringRef CYCopyJSString(const char *value) { + return JSStringCreateWithUTF8CString(value); +} + +JSStringRef CYCopyJSString(JSStringRef value) { + return JSStringRetain(value); +} + +// XXX: this is not a safe handle +class CYString { + private: + JSStringRef string_; + + public: + template + CYString(Type_ value) { + string_ = CYCopyJSString(value); + } + + ~CYString() { + JSStringRelease(string_); + } + + operator JSStringRef() const { + return string_; + } +}; + +void CYThrow(JSContextRef context, id error, JSValueRef *exception) { + *exception = CYCastJSValue(context, error); +} + +@implementation CYJSObject + +- (id) initWithJSObject:(JSObjectRef)object inContext:(JSContextRef)context { + if ((self = [super init]) != nil) { + object_ = object; + context_ = context; + } return self; +} + +- (NSUInteger) count { + JSPropertyNameArrayRef names(JSObjectCopyPropertyNames(context_, object_)); + size_t size(JSPropertyNameArrayGetCount(names)); + JSPropertyNameArrayRelease(names); + return size; +} + +- (id) objectForKey:(id)key { + JSValueRef exception(NULL); + JSValueRef value(JSObjectGetProperty(context_, object_, CYString(key), &exception)); + CYThrow(context_, exception); + return CYCastNSObject(context_, value); +} + +- (NSEnumerator *) keyEnumerator { + JSPropertyNameArrayRef names(JSObjectCopyPropertyNames(context_, object_)); + NSEnumerator *enumerator([CYCastNSArray(names) objectEnumerator]); + JSPropertyNameArrayRelease(names); + return enumerator; +} + +- (void) setObject:(id)object forKey:(id)key { + JSValueRef exception(NULL); + JSObjectSetProperty(context_, object_, CYString(key), CYCastJSValue(context_, object), kJSPropertyAttributeNone, &exception); + CYThrow(context_, exception); +} + +- (void) removeObjectForKey:(id)key { + JSValueRef exception(NULL); + // XXX: this returns a bool + JSObjectDeleteProperty(context_, object_, CYString(key), &exception); + CYThrow(context_, exception); +} + +@end + +@implementation CYJSArray - (id) initWithJSObject:(JSObjectRef)object inContext:(JSContextRef)context { if ((self = [super init]) != nil) { @@ -209,20 +483,27 @@ static id JSValueToNSObject(JSContextRef ctx, JSValueRef value) { } - (NSUInteger) count { - return JSValueToNumber(context_, JSObjectGetProperty(context_, object_, length_, NULL), NULL); + JSValueRef exception(NULL); + JSValueRef value(JSObjectGetProperty(context_, object_, length_, &exception)); + CYThrow(context_, exception); + double number(JSValueToNumber(context_, value, &exception)); + CYThrow(context_, exception); + return number; } - (id) objectAtIndex:(NSUInteger)index { - JSValueRef value(JSObjectGetPropertyAtIndex(context_, object_, index, NULL)); - id object(JSValueToNSObject(context_, value)); + JSValueRef exception(NULL); + JSValueRef value(JSObjectGetPropertyAtIndex(context_, object_, index, &exception)); + CYThrow(context_, exception); + id object(CYCastNSObject(context_, value)); return object == nil ? [NSNull null] : object; } @end -static CFStringRef JSValueToJSONCopy(JSContextRef ctx, JSValueRef value) { - id object(JSValueToNSObject(ctx, value)); - return (CFStringRef) [(object == nil ? @"null" : [object cy$toJSON]) retain]; +CFStringRef JSValueToJSONCopy(JSContextRef context, JSValueRef value) { + id object(CYCastNSObject(context, value)); + return reinterpret_cast([(object == nil ? @"null" : [object cy$toJSON]) retain]); } static void OnData(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *value, void *info) { @@ -248,13 +529,13 @@ static void OnData(CFSocketRef socket, CFSocketCallBackType type, CFDataRef addr JSStringRef script(JSStringCreateWithCFString(code)); CFRelease(code); - JSValueRef result(JSEvaluateScript(Context_, script, NULL, NULL, 0, NULL)); + JSValueRef result(JSEvaluateScript(JSGetContext(), script, NULL, NULL, 0, NULL)); JSStringRelease(script); CFHTTPMessageRef response(CFHTTPMessageCreateResponse(kCFAllocatorDefault, 200, NULL, kCFHTTPVersion1_1)); CFHTTPMessageSetHeaderFieldValue(response, CFSTR("Content-Type"), CFSTR("application/json; charset=utf-8")); - CFStringRef json(JSValueToJSONCopy(Context_, result)); + CFStringRef json(JSValueToJSONCopy(JSGetContext(), result)); CFDataRef body(CFStringCreateExternalRepresentation(kCFAllocatorDefault, json, kCFStringEncodingUTF8, NULL)); CFRelease(json); @@ -298,19 +579,359 @@ static void OnAccept(CFSocketRef socket, CFSocketCallBackType type, CFDataRef ad } } -static JSValueRef joc_getProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef *exception) { +static JSValueRef joc_getProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef *exception) { return NULL; } -static JSValueRef obc_getProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef *exception) { +typedef id jocData; + +static JSObjectRef joc_callAsConstructor(JSContextRef context, JSObjectRef object, size_t count, const JSValueRef arguments[], JSValueRef *exception) { _pooled + @try { + id data(reinterpret_cast(JSObjectGetPrivate(object))); + return CYMakeObject(context, [[data alloc] autorelease]); + } CYCatch +} + +struct ptrData { + apr_pool_t *pool_; + void *value_; + sig::Type type_; +}; + +static void ptr_finalize(JSObjectRef object) { + ptrData *data(reinterpret_cast(JSObjectGetPrivate(object))); + apr_pool_destroy(data->pool_); +} + +static void joc_finalize(JSObjectRef object) { + id data(reinterpret_cast(JSObjectGetPrivate(object))); + [data release]; +} + +static JSValueRef obc_getProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef *exception) { _pooled NSString *name([(NSString *) JSStringCopyCFString(kCFAllocatorDefault, propertyName) autorelease]); if (Class _class = NSClassFromString(name)) - return JSObjectMake(ctx, joc_, _class); + return CYMakeObject(context, _class); return NULL; } -extern "C" void TweakInitialize() { - NSAutoreleasePool *pool([[NSAutoreleasePool alloc] init]); +void CYSetProperty(JSContextRef context, JSObjectRef object, const char *name, JSValueRef value) { + JSValueRef exception(NULL); + JSObjectSetProperty(context, object, CYString(name), value, kJSPropertyAttributeNone, &exception); + CYThrow(context, exception); +} + +struct ffiData { + apr_pool_t *pool_; + void (*function_)(); + const char *type_; + sig::Signature signature_; + ffi_cif cif_; +}; + +char *CYPoolCString(apr_pool_t *pool, JSStringRef value) { + size_t size(JSStringGetMaximumUTF8CStringSize(value)); + char *string(new(pool) char[size]); + JSStringGetUTF8CString(value, string, size); + JSStringRelease(value); + return string; +} + +// XXX: this macro is dangerous +#define CYCastCString(context, value) ({ \ + JSValueRef exception(NULL); \ + JSStringRef string(JSValueToStringCopy(context, value, &exception)); \ + CYThrow(context, exception); \ + size_t size(JSStringGetMaximumUTF8CStringSize(string)); \ + char *utf8(reinterpret_cast(alloca(size))); \ + JSStringGetUTF8CString(string, utf8, size); \ + JSStringRelease(string); \ + utf8; \ +}) + +SEL CYCastSEL(JSContextRef context, JSValueRef value) { + if (JSValueIsNull(context, value)) + return NULL; + else if (JSValueIsObjectOfClass(context, value, sel_)) + return reinterpret_cast(JSObjectGetPrivate((JSObjectRef) value)); + else + return sel_registerName(CYCastCString(context, value)); +} + +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, ptr_)) { + ptrData *data(reinterpret_cast(JSObjectGetPrivate((JSObjectRef) value))); + return data->value_; + } + default: + JSValueRef exception(NULL); + double number(JSValueToNumber(context, value, &exception)); + CYThrow(context, exception); + return reinterpret_cast(static_cast(number)); + } +} + +void CYPoolFFI(apr_pool_t *pool, JSContextRef context, sig::Type *type, void *data, JSValueRef value) { + switch (type->primitive) { + case sig::boolean_P: + *reinterpret_cast(data) = JSValueToBoolean(context, value); + break; + +#define CYPoolFFI_(primitive, native) \ + case sig::primitive ## _P: { \ + JSValueRef exception(NULL); \ + double number(JSValueToNumber(context, value, &exception)); \ + CYThrow(context, exception); \ + *reinterpret_cast(data) = number; \ + } 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(data) = CYCastNSObject(context, value); + break; + + case sig::selector_P: + *reinterpret_cast(data) = CYCastSEL(context, value); + break; + + case sig::pointer_P: + *reinterpret_cast(data) = CYCastPointer(context, value); + break; + + case sig::string_P: { + JSValueRef exception(NULL); + JSStringRef string(JSValueToStringCopy(context, value, &exception)); + CYThrow(context, exception); + size_t size(JSStringGetMaximumUTF8CStringSize(string)); + char *utf8(new(pool) char[size]); + JSStringGetUTF8CString(string, utf8, size); + JSStringRelease(string); + *reinterpret_cast(data) = utf8; + } break; + + case sig::struct_P: + goto fail; + + case sig::void_P: + break; + + default: fail: + NSLog(@"CYPoolFFI(%c)\n", type->primitive); + _assert(false); + } +} + +JSValueRef CYFromFFI(JSContextRef context, sig::Type *type, void *data) { + JSValueRef value; + + switch (type->primitive) { + case sig::boolean_P: + value = JSValueMakeBoolean(context, *reinterpret_cast(data)); + break; + +#define CYFromFFI_(primitive, native) \ + case sig::primitive ## _P: \ + value = JSValueMakeNumber(context, *reinterpret_cast(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: + case sig::typename_P: { + value = CYCastJSValue(context, *reinterpret_cast(data)); + } break; + + case sig::selector_P: { + SEL sel(*reinterpret_cast(data)); + value = sel == NULL ? JSValueMakeNull(context) : JSObjectMake(context, sel_, sel); + } break; + + case sig::pointer_P: { + if (void *pointer = *reinterpret_cast(data)) { + apr_pool_t *pool; + apr_pool_create(&pool, NULL); + ptrData *data(new(pool) ptrData()); + data->pool_ = pool; + data->value_ = pointer; + value = JSObjectMake(context, ptr_, data); + } else value = JSValueMakeNull(context); + } break; + + case sig::string_P: { + char *utf8(*reinterpret_cast(data)); + value = utf8 == NULL ? JSValueMakeNull(context) : JSValueMakeString(context, CYString(utf8)); + } break; + + case sig::struct_P: + goto fail; + + case sig::void_P: + value = NULL; + break; + + default: fail: + NSLog(@"CYFromFFI(%c)\n", type->primitive); + _assert(false); + } + + return value; +} + +class CYPool { + private: + apr_pool_t *pool_; + + public: + CYPool() { + apr_pool_create(&pool_, NULL); + } + + ~CYPool() { + apr_pool_destroy(pool_); + } + + operator apr_pool_t *() const { + return pool_; + } +}; + +static JSValueRef CYCallFunction(JSContextRef context, size_t count, const JSValueRef *arguments, JSValueRef *exception, sig::Signature *signature, ffi_cif *cif, void (*function)()) { _pooled + @try { + if (count != signature->count - 1) + [NSException raise:NSInvalidArgumentException format:@"incorrect number of arguments to ffi function"]; + + CYPool pool; + void *values[count]; + + for (unsigned index(0); index != count; ++index) { + sig::Element *element(&signature->elements[index + 1]); + values[index] = new(pool) uint8_t[cif->arg_types[index]->size]; + CYPoolFFI(pool, context, element->type, values[index], arguments[index]); + } + + uint8_t value[cif->rtype->size]; + ffi_call(cif, function, value, values); + + return CYFromFFI(context, signature->elements[0].type, value); + } CYCatch +} + +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 + ); +} + +static JSValueRef $objc_msgSend(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { _pooled + const char *type; + + @try { + if (count < 2) + [NSException raise:NSInvalidArgumentException format:@"too few arguments to objc_msgSend"]; + + id self(CYCastNSObject(context, arguments[0])); + if (self == nil) + return JSValueMakeNull(context); + + SEL _cmd(CYCastSEL(context, arguments[1])); + NSMethodSignature *method([self methodSignatureForSelector:_cmd]); + if (method == nil) + [NSException raise:NSInvalidArgumentException format:@"unrecognized selector %s sent to object %p", sel_getName(_cmd), self]; + + type = [[method _typeString] UTF8String]; + } CYCatch + + CYPool pool; + + sig::Signature signature; + sig::Parse(pool, &signature, type); + + ffi_cif cif; + sig::sig_ffi_cif(pool, &sig::ObjectiveC, &signature, &cif); + + void (*function)() = stret(cif.rtype) ? reinterpret_cast(&objc_msgSend_stret) : reinterpret_cast(&objc_msgSend); + return CYCallFunction(context, count, arguments, exception, &signature, &cif, function); +} + +static JSValueRef ffi_callAsFunction(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { + ffiData *data(reinterpret_cast(JSObjectGetPrivate(object))); + return CYCallFunction(context, count, arguments, exception, &data->signature_, &data->cif_, data->function_); +} + +static void ffi_finalize(JSObjectRef object) { + ffiData *data(reinterpret_cast(JSObjectGetPrivate(object))); + apr_pool_destroy(data->pool_); +} + +JSObjectRef CYMakeFunction(JSContextRef context, void (*function)(), const char *type) { + apr_pool_t *pool; + apr_pool_create(&pool, NULL); + + ffiData *data(new(pool) ffiData()); + + data->pool_ = pool; + data->function_ = function; + data->type_ = apr_pstrdup(pool, type); + + sig::Parse(pool, &data->signature_, type); + sig::sig_ffi_cif(pool, &sig::ObjectiveC, &data->signature_, &data->cif_); + + return JSObjectMake(context, ffi_, data); +} + +JSObjectRef ffi(JSContextRef context, JSObjectRef object, size_t count, const JSValueRef arguments[], JSValueRef *exception) { + @try { + if (count != 2) + [NSException raise:NSInvalidArgumentException format:@"incorrect number of arguments to ffi constructor"]; + void (*function)() = reinterpret_cast(CYCastPointer(context, arguments[0])); + const char *type(CYCastCString(context, arguments[1])); + return CYMakeFunction(context, function, type); + } CYCatch +} + +JSValueRef ptr_getProperty_value(JSContextRef context, JSObjectRef object, JSStringRef name, JSValueRef *exception) { + ptrData *data(reinterpret_cast(JSObjectGetPrivate(object))); + return JSValueMakeNumber(context, reinterpret_cast(data->value_)); +} + +static JSStaticValue ptr_staticValues[2] = { + {"value", &ptr_getProperty_value, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete}, + {NULL, NULL, NULL, 0} +}; + +MSInitialize { _pooled + apr_initialize(); NSCFBoolean_ = objc_getClass("NSCFBoolean"); @@ -340,18 +961,131 @@ extern "C" void TweakInitialize() { JSClassRef obc(JSClassCreate(&definition)); definition = kJSClassDefinitionEmpty; - definition.getProperty = &joc_getProperty; - joc_ = JSClassCreate(&definition); + definition.className = "ffi"; + definition.callAsFunction = &ffi_callAsFunction; + definition.finalize = &ffi_finalize; + ffi_ = JSClassCreate(&definition); - Context_ = JSGlobalContextCreate(obc); + definition = kJSClassDefinitionEmpty; + definition.className = "ptr"; + definition.staticValues = ptr_staticValues; + definition.finalize = &ptr_finalize; + ptr_ = JSClassCreate(&definition); - JSObjectRef global(JSContextGetGlobalObject(Context_)); + definition = kJSClassDefinitionEmpty; + definition.className = "sel"; + sel_ = JSClassCreate(&definition); - length_ = JSStringCreateWithUTF8CString("length"); + definition = kJSClassDefinitionEmpty; + definition.className = "joc"; + definition.getProperty = &joc_getProperty; + definition.callAsConstructor = &joc_callAsConstructor; + definition.finalize = &joc_finalize; + joc_ = JSClassCreate(&definition); - JSStringRef name(JSStringCreateWithUTF8CString("Array")); - Array_ = JSValueToObject(Context_, JSObjectGetProperty(Context_, global, name, NULL), NULL); - JSStringRelease(name); + JSContextRef context(JSGlobalContextCreate(obc)); + Context_ = context; + + JSObjectRef global(JSContextGetGlobalObject(context)); + + CYSetProperty(context, global, "ffi", JSObjectMakeConstructor(context, ffi_, &ffi)); + CYSetProperty(context, global, "obc", JSObjectMake(context, obc, NULL)); + +#define CYSetFunction_(name, type) \ + CYSetProperty(context, global, #name, CYMakeFunction(context, reinterpret_cast(&name), type)) + + CYSetFunction_(class_addIvar, "B#*LC*"); + CYSetFunction_(class_addMethod, "B#:^?*"); + CYSetFunction_(class_addProtocol, "B#@"); + CYSetFunction_(class_conformsToProtocol, "B#@"); + CYSetFunction_(class_copyIvarList, "^^{objc_ivar=}#^I"); + CYSetFunction_(class_copyMethodList, "^^{objc_method=}#^I"); + CYSetFunction_(class_copyPropertyList, "^^{objc_property=}#^I"); + CYSetFunction_(class_copyProtocolList, "^@#^I"); + CYSetFunction_(class_createInstance, "@#L"); + CYSetFunction_(class_getClassMethod, "^{objc_method=}#:"); + CYSetFunction_(class_getClassVariable, "^{objc_ivar=}#*"); + CYSetFunction_(class_getInstanceMethod, "^{objc_method=}#:"); + CYSetFunction_(class_getInstanceSize, "L#"); + CYSetFunction_(class_getInstanceVariable, "^{objc_ivar=}#*"); + CYSetFunction_(class_getIvarLayout, "*#"); + CYSetFunction_(class_getMethodImplementation, "^?#:"); + CYSetFunction_(class_getMethodImplementation_stret, "^?#:"); + CYSetFunction_(class_getName, "*#"); + CYSetFunction_(class_getProperty, "^{objc_property=}#*"); + CYSetFunction_(class_getSuperclass, "##"); + CYSetFunction_(class_getVersion, "i#"); + CYSetFunction_(class_getWeakIvarLayout, "*#"); + CYSetFunction_(class_isMetaClass, "B#"); + CYSetFunction_(class_replaceMethod, "^?#:^?*"); + CYSetFunction_(class_respondsToSelector, "B#:"); + CYSetFunction_(class_setIvarLayout, "v#*"); + CYSetFunction_(class_setSuperclass, "###"); + CYSetFunction_(class_setVersion, "v#i"); + CYSetFunction_(class_setWeakIvarLayout, "v#*"); + CYSetFunction_(ivar_getName, "*^{objc_ivar=}"); + CYSetFunction_(ivar_getOffset, "i^{objc_ivar=}"); + CYSetFunction_(ivar_getTypeEncoding, "*^{objc_ivar=}"); + CYSetFunction_(method_copyArgumentType, "^c^{objc_method=}I"); + CYSetFunction_(method_copyReturnType, "^c^{objc_method=}"); + CYSetFunction_(method_exchangeImplementations, "v^{objc_method=}^{objc_method=}"); + CYSetFunction_(method_getArgumentType, "v^{objc_method=}I^cL"); + CYSetFunction_(method_getImplementation, "^?^{objc_method=}"); + CYSetFunction_(method_getName, ":^{objc_method=}"); + CYSetFunction_(method_getNumberOfArguments, "I^{objc_method=}"); + CYSetFunction_(method_getReturnType, "v^{objc_method=}^cL"); + CYSetFunction_(method_getTypeEncoding, "*^{objc_method=}"); + CYSetFunction_(method_setImplementation, "^?^{objc_method=}^?"); + CYSetFunction_(objc_allocateClassPair, "##*L"); + CYSetFunction_(objc_copyProtocolList, "^@^I"); + CYSetFunction_(objc_duplicateClass, "##*L"); + CYSetFunction_(objc_getClass, "#*"); + CYSetFunction_(objc_getClassList, "i^#i"); + CYSetFunction_(objc_getFutureClass, "#*"); + CYSetFunction_(objc_getMetaClass, "@*"); + CYSetFunction_(objc_getProtocol, "@*"); + CYSetFunction_(objc_getRequiredClass, "@*"); + CYSetFunction_(objc_lookUpClass, "@*"); + CYSetFunction_(objc_registerClassPair, "v#"); + CYSetFunction_(objc_setFutureClass, "v#*"); + CYSetFunction_(object_copy, "@@L"); + CYSetFunction_(object_dispose, "@@"); + CYSetFunction_(object_getClass, "#@"); + CYSetFunction_(object_getClassName, "*@"); + CYSetFunction_(object_getIndexedIvars, "^v@"); + CYSetFunction_(object_getInstanceVariable, "^{objc_ivar=}@*^^v"); + CYSetFunction_(object_getIvar, "@@^{objc_ivar=}"); + CYSetFunction_(object_setClass, "#@#"); + CYSetFunction_(object_setInstanceVariable, "^{objc_ivar=}@*^v"); + CYSetFunction_(object_setIvar, "v@^{objc_ivar=}@"); + CYSetFunction_(property_getAttributes, "*^{objc_property=}"); + CYSetFunction_(property_getName, "*^{objc_property=}"); + CYSetFunction_(protocol_conformsToProtocol, "B@@"); + CYSetFunction_(protocol_copyMethodDescriptionList, "^{objc_method_description=:*}@BB^I"); + CYSetFunction_(protocol_copyPropertyList, "^{objc_property=}@^I"); + CYSetFunction_(protocol_copyProtocolList, "^@@^I"); + CYSetFunction_(protocol_getMethodDescription, "{objc_method_description=:*}@:BB"); + CYSetFunction_(protocol_getName, "*@"); + CYSetFunction_(protocol_getProperty, "^{objc_property=}@*BB"); + CYSetFunction_(protocol_isEqual, "B@@"); + CYSetFunction_(sel_getName, "*:"); + CYSetFunction_(sel_getUid, ":*"); + CYSetFunction_(sel_isEqual, "B::"); + CYSetFunction_(sel_registerName, ":*"); + + CYSetProperty(context, global, "objc_msgSend", JSObjectMakeFunctionWithCallback(context, CYString("objc_msgSend"), &$objc_msgSend)); + + CYSetProperty(context, global, "YES", JSValueMakeBoolean(context, true)); + CYSetProperty(context, global, "NO", JSValueMakeBoolean(context, false)); + CYSetProperty(context, global, "nil", JSValueMakeNull(context)); + + name_ = JSStringCreateWithUTF8CString("name"); + message_ = JSStringCreateWithUTF8CString("message"); + length_ = JSStringCreateWithUTF8CString("length"); - [pool release]; + JSValueRef exception(NULL); + JSValueRef value(JSObjectGetProperty(JSGetContext(), global, CYString("Array"), &exception)); + CYThrow(context, exception); + Array_ = JSValueToObject(JSGetContext(), value, &exception); + CYThrow(context, exception); }