X-Git-Url: https://git.saurik.com/cycript.git/blobdiff_plain/5befe15eb839184507218ab3feb9e375edf335c3..478d4ed0925f2e61fbcdc4672104620342c52c4c:/Library.mm?ds=sidebyside diff --git a/Library.mm b/Library.mm index 2dc719e..4213e5b 100644 --- a/Library.mm +++ b/Library.mm @@ -40,12 +40,13 @@ #define _GNU_SOURCE #include -#include "Struct.hpp" +#include "cycript.hpp" #include "sig/parse.hpp" #include "sig/ffi_type.hpp" #include "Pooling.hpp" +#include "Struct.hpp" #include @@ -53,26 +54,18 @@ #include #include -#include - -#include -#include -#include -#include -#include -#include #include #include #include #include +#include #include #include #include #include -#include #include "Parser.hpp" #include "Cycript.tab.hh" @@ -89,10 +82,8 @@ CFLog(kCFLogLevelNotice, CFSTR("_trace():%u"), __LINE__); \ } while (false) - -#define _pooled _H _pool([[NSAutoreleasePool alloc] init], true); - -static JSContextRef Context_; +static JSGlobalContextRef Context_; +static JSObjectRef System_; static JSClassRef Functor_; static JSClassRef Instance_; @@ -100,6 +91,7 @@ static JSClassRef Pointer_; static JSClassRef Selector_; static JSObjectRef Array_; +static JSObjectRef Function_; static JSStringRef name_; static JSStringRef message_; @@ -114,8 +106,120 @@ struct Client { CFSocketRef socket_; }; -JSObjectRef CYMakeObject(JSContextRef context, id object) { - return JSObjectMake(context, Instance_, [object retain]); +struct ptrData { + apr_pool_t *pool_; + void *value_; + sig::Type type_; + + void *operator new(size_t size) { + apr_pool_t *pool; + apr_pool_create(&pool, NULL); + void *data(apr_palloc(pool, size)); + reinterpret_cast(data)->pool_ = pool; + return data;; + } + + ptrData(void *value) : + value_(value) + { + } + + virtual ~ptrData() { + } +}; + +struct ffiData : ptrData { + sig::Signature signature_; + ffi_cif cif_; + + ffiData(const char *type, void (*value)()) : + ptrData(reinterpret_cast(value)) + { + sig::Parse(pool_, &signature_, type); + sig::sig_ffi_cif(pool_, &sig::ObjectiveC, &signature_, &cif_); + } +}; + +struct ffoData : ffiData { + JSContextRef context_; + JSObjectRef function_; + + ffoData(const char *type) : + ffiData(type, NULL) + { + } +}; + +struct selData : ptrData { + selData(SEL value) : + ptrData(value) + { + } + + SEL GetValue() const { + return reinterpret_cast(value_); + } +}; + +struct jocData : ptrData { + bool transient_; + + jocData(id value, bool transient) : + ptrData(value) + { + } + + virtual ~jocData() { + if (!transient_) + [GetValue() release]; + } + + id GetValue() const { + return reinterpret_cast(value_); + } +}; + +JSObjectRef CYMakeInstance(JSContextRef context, id object, bool transient = true) { + if (!transient) + object = [object retain]; + jocData *data(new jocData(object, transient)); + return JSObjectMake(context, Instance_, data); +} + +const char *CYPoolCString(apr_pool_t *pool, NSString *value) { + if (pool == NULL) + return [value UTF8String]; + else { + size_t size([value maximumLengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1); + char *string(new(pool) char[size]); + if (![value getCString:string maxLength:size encoding:NSUTF8StringEncoding]) + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"[NSString getCString:maxLength:encoding:] == NO" userInfo:nil]; + return string; + } +} + +JSValueRef CYCastJSValue(JSContextRef context, bool value) { + return JSValueMakeBoolean(context, value); +} + +JSValueRef CYCastJSValue(JSContextRef context, double value) { + return JSValueMakeNumber(context, value); +} + +#define CYCastJSValue_(Type_) \ + JSValueRef CYCastJSValue(JSContextRef context, Type_ value) { \ + return JSValueMakeNumber(context, static_cast(value)); \ + } + +CYCastJSValue_(int) +CYCastJSValue_(unsigned int) +CYCastJSValue_(long int) +CYCastJSValue_(long unsigned int) +CYCastJSValue_(long long int) +CYCastJSValue_(long long unsigned int) + +JSValueRef CYJSUndefined(JSContextRef context) { + return JSValueMakeUndefined(context); } @interface NSMethodSignature (Cycript) @@ -123,6 +227,7 @@ JSObjectRef CYMakeObject(JSContextRef context, id object) { @end @interface NSObject (Cycript) +- (bool) cy$isUndefined; - (NSString *) cy$toJSON; - (JSValueRef) cy$JSValueInContext:(JSContextRef)context; @end @@ -137,24 +242,40 @@ JSObjectRef CYMakeObject(JSContextRef context, id object) { @implementation NSObject (Cycript) +- (bool) cy$isUndefined { + return false; +} + - (NSString *) cy$toJSON { return [self description]; } - (JSValueRef) cy$JSValueInContext:(JSContextRef)context { - return CYMakeObject(context, self); + return CYMakeInstance(context, self); } @end @implementation WebUndefined (Cycript) +- (bool) cy$isUndefined { + return true; +} + - (NSString *) cy$toJSON { return @"undefined"; } - (JSValueRef) cy$JSValueInContext:(JSContextRef)context { - return JSValueMakeUndefined(context); + return CYJSUndefined(context); +} + +@end + +@implementation NSNull (Cycript) + +- (NSString *) cy$toJSON { + return @"null"; } @end @@ -171,7 +292,12 @@ JSObjectRef CYMakeObject(JSContextRef context, id object) { [json appendString:@","]; else comma = true; - [json appendString:[object cy$toJSON]]; + if (![object cy$isUndefined]) + [json appendString:[object cy$toJSON]]; + else { + [json appendString:@","]; + comma = false; + } } [json appendString:@"]"]; @@ -184,8 +310,7 @@ JSObjectRef CYMakeObject(JSContextRef context, id object) { - (NSString *) cy$toJSON { NSMutableString *json([[[NSMutableString alloc] init] autorelease]); - [json appendString:@"("]; - [json appendString:@"{"]; + [json appendString:@"({"]; bool comma(false); for (id key in self) { @@ -212,7 +337,7 @@ JSObjectRef CYMakeObject(JSContextRef context, id object) { } - (JSValueRef) cy$JSValueInContext:(JSContextRef)context { - return [self class] != NSCFBoolean_ ? JSValueMakeNumber(context, [self doubleValue]) : JSValueMakeBoolean(context, [self boolValue]); + return [self class] != NSCFBoolean_ ? CYCastJSValue(context, [self doubleValue]) : CYCastJSValue(context, [self boolValue]); } - (void *) cy$symbol { @@ -239,7 +364,8 @@ JSObjectRef CYMakeObject(JSContextRef context, id object) { } - (void *) cy$symbol { - return dlsym(RTLD_DEFAULT, [self UTF8String]); + CYPool pool; + return dlsym(RTLD_DEFAULT, CYPoolCString(pool, self)); } @end @@ -271,66 +397,110 @@ JSObjectRef CYMakeObject(JSContextRef context, id object) { @end -JSContextRef JSGetContext() { +CYRange WordStartRange_(0x1000000000LLU,0x7fffffe87fffffeLLU); // A-Za-z_$ +CYRange WordEndRange_(0x3ff001000000000LLU,0x7fffffe87fffffeLLU); // A-Za-z_$0-9 + +JSGlobalContextRef CYGetJSContext() { return Context_; } #define CYCatch \ @catch (id error) { \ + NSLog(@"e:%@", error); \ CYThrow(context, error, exception); \ return NULL; \ } void CYThrow(JSContextRef context, JSValueRef value); -id CYCastNSObject(JSContextRef context, JSObjectRef object) { - if (JSValueIsObjectOfClass(context, object, Instance_)) - return reinterpret_cast(JSObjectGetPrivate(object)); +apr_status_t CYPoolRelease_(void *data) { + id object(reinterpret_cast(data)); + [object release]; + return APR_SUCCESS; +} + +id CYPoolRelease(apr_pool_t *pool, id object) { + if (pool == NULL) + return [object autorelease]; + else { + apr_pool_cleanup_register(pool, object, &CYPoolRelease_, &apr_pool_cleanup_null); + return object; + } +} + +id CYCastNSObject(apr_pool_t *pool, JSContextRef context, JSObjectRef object) { + if (JSValueIsObjectOfClass(context, object, Instance_)) { + jocData *data(reinterpret_cast(JSObjectGetPrivate(object))); + return data->GetValue(); + } + 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]; + id value(array ? [CYJSArray alloc] : [CYJSObject alloc]); + return CYPoolRelease(pool, [value initWithJSObject:object inContext:context]); } JSStringRef CYCopyJSString(id value) { - return JSStringCreateWithCFString(reinterpret_cast([value description])); + return value == NULL ? NULL : JSStringCreateWithCFString(reinterpret_cast([value description])); } JSStringRef CYCopyJSString(const char *value) { - return JSStringCreateWithUTF8CString(value); + return value == NULL ? NULL : JSStringCreateWithUTF8CString(value); } JSStringRef CYCopyJSString(JSStringRef value) { - return JSStringRetain(value); + return value == NULL ? NULL : JSStringRetain(value); } JSStringRef CYCopyJSString(JSContextRef context, JSValueRef value) { + if (JSValueIsNull(context, value)) + return NULL; JSValueRef exception(NULL); JSStringRef string(JSValueToStringCopy(context, value, &exception)); CYThrow(context, exception); return string; } -// XXX: this is not a safe handle class CYJSString { private: JSStringRef string_; + void Clear_() { + JSStringRelease(string_); + } + public: + CYJSString(const CYJSString &rhs) : + string_(CYCopyJSString(rhs.string_)) + { + } + template - CYJSString(Arg0_ arg0) { - string_ = CYCopyJSString(arg0); + CYJSString(Arg0_ arg0) : + string_(CYCopyJSString(arg0)) + { } template - CYJSString(Arg0_ arg0, Arg1_ arg1) { - string_ = CYCopyJSString(arg0, arg1); + CYJSString(Arg0_ arg0, Arg1_ arg1) : + string_(CYCopyJSString(arg0, arg1)) + { + } + + CYJSString &operator =(const CYJSString &rhs) { + Clear_(); + string_ = CYCopyJSString(rhs.string_); + return *this; } ~CYJSString() { - JSStringRelease(string_); + Clear_(); + } + + void Clear() { + Clear_(); + string_ = NULL; } operator JSStringRef() const { @@ -358,50 +528,123 @@ CFNumberRef CYCopyCFNumber(JSContextRef context, JSValueRef value) { return CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &number); } -NSString *CYCastNSString(JSStringRef value) { - return [reinterpret_cast(CYCopyCFString(value)) autorelease]; +NSString *CYCastNSString(apr_pool_t *pool, JSStringRef value) { + return CYPoolRelease(pool, reinterpret_cast(CYCopyCFString(value))); } -CFTypeRef CYCopyCFType(JSContextRef context, JSValueRef 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: - return CFRetain([WebUndefined undefined]); + object = [WebUndefined undefined]; + copy = false; + break; + case kJSTypeNull: - return nil; + return NULL; + break; + case kJSTypeBoolean: - return CFRetain(JSValueToBoolean(context, value) ? kCFBooleanTrue : kCFBooleanFalse); + object = CYCastBool(context, value) ? kCFBooleanTrue : kCFBooleanFalse; + copy = false; + break; + case kJSTypeNumber: - return CYCopyCFNumber(context, value); + object = CYCopyCFNumber(context, value); + copy = true; + break; + case kJSTypeString: - return CYCopyCFString(context, value); + object = CYCopyCFString(context, value); + copy = true; + break; + case kJSTypeObject: - return CFRetain((CFTypeRef) CYCastNSObject(context, (JSObjectRef) value)); + // 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, (id) 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(JSPropertyNameArrayGetNameAtIndex(names, index))]; + [array addObject:CYCastNSString(pool, 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]; +id CYCastNSObject(apr_pool_t *pool, JSContextRef context, JSValueRef value) { + return reinterpret_cast(CYCastCFType(pool, context, value)); } void CYThrow(JSContextRef context, JSValueRef value) { if (value == NULL) return; - @throw CYCastNSObject(context, value); + @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) { - return value == nil ? JSValueMakeNull(context) : [value cy$JSValueInContext:context]; + return value == nil ? CYJSNull(context) : [value cy$JSValueInContext:context]; +} + +JSObjectRef CYCastJSObject(JSContextRef context, JSValueRef value) { + JSValueRef exception(NULL); + JSObjectRef object(JSValueToObject(context, value, &exception)); + CYThrow(context, exception); + return object; +} + +JSValueRef CYGetProperty(JSContextRef context, JSObjectRef object, JSStringRef name) { + JSValueRef exception(NULL); + JSValueRef value(JSObjectGetProperty(context, object, name, &exception)); + CYThrow(context, exception); + return value; +} + +void CYSetProperty(JSContextRef context, JSObjectRef object, JSStringRef name, JSValueRef value) { + JSValueRef exception(NULL); + JSObjectSetProperty(context, object, name, value, kJSPropertyAttributeNone, &exception); + CYThrow(context, exception); } void CYThrow(JSContextRef context, id error, JSValueRef *exception) { @@ -425,10 +668,7 @@ void CYThrow(JSContextRef context, id error, JSValueRef *exception) { } - (id) objectForKey:(id)key { - JSValueRef exception(NULL); - JSValueRef value(JSObjectGetProperty(context_, object_, CYJSString(key), &exception)); - CYThrow(context_, exception); - return CYCastNSObject(context_, value); + return CYCastNSObject(NULL, context_, CYGetProperty(context_, object_, CYJSString(key))) ?: [NSNull null]; } - (NSEnumerator *) keyEnumerator { @@ -439,9 +679,7 @@ void CYThrow(JSContextRef context, id error, JSValueRef *exception) { } - (void) setObject:(id)object forKey:(id)key { - JSValueRef exception(NULL); - JSObjectSetProperty(context_, object_, CYJSString(key), CYCastJSValue(context_, object), kJSPropertyAttributeNone, &exception); - CYThrow(context_, exception); + CYSetProperty(context_, object_, CYJSString(key), CYCastJSValue(context_, object)); } - (void) removeObjectForKey:(id)key { @@ -463,27 +701,30 @@ void CYThrow(JSContextRef context, id error, JSValueRef *exception) { } - (NSUInteger) count { - JSValueRef exception(NULL); - JSValueRef value(JSObjectGetProperty(context_, object_, length_, &exception)); - CYThrow(context_, exception); - return CYCastDouble(context_, value); + return CYCastDouble(context_, CYGetProperty(context_, object_, length_)); } - (id) objectAtIndex:(NSUInteger)index { JSValueRef exception(NULL); JSValueRef value(JSObjectGetPropertyAtIndex(context_, object_, index, &exception)); CYThrow(context_, exception); - id object(CYCastNSObject(context_, value)); - return object == nil ? [NSNull null] : object; + return CYCastNSObject(NULL, context_, value) ?: [NSNull null]; } @end -CFStringRef JSValueToJSONCopy(JSContextRef context, JSValueRef value) { - id object(CYCastNSObject(context, value)); +CFStringRef CYCopyJSONString(JSContextRef context, JSValueRef value) { _pooled + id object(CYCastNSObject(NULL, context, value)); return reinterpret_cast([(object == nil ? @"null" : [object cy$toJSON]) retain]); } +const char *CYPoolJSONString(apr_pool_t *pool, JSContextRef context, JSValueRef value) { + NSString *json((NSString *) CYCopyJSONString(context, value)); + const char *string(CYPoolCString(pool, json)); + [json release]; + return string; +} + static void OnData(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *value, void *info) { switch (type) { case kCFSocketDataCallBack: @@ -507,13 +748,13 @@ static void OnData(CFSocketRef socket, CFSocketCallBackType type, CFDataRef addr JSStringRef script(JSStringCreateWithCFString(code)); CFRelease(code); - JSValueRef result(JSEvaluateScript(JSGetContext(), script, NULL, NULL, 0, NULL)); + JSValueRef result(JSEvaluateScript(CYGetJSContext(), 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(JSGetContext(), result)); + CFStringRef json(CYCopyJSONString(CYGetJSContext(), result)); CFDataRef body(CFStringCreateExternalRepresentation(kCFAllocatorDefault, json, kCFStringEncodingUTF8, NULL)); CFRelease(json); @@ -557,108 +798,90 @@ static void OnAccept(CFSocketRef socket, CFSocketCallBackType type, CFDataRef ad } } -static JSValueRef Instance_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { _pooled +static JSValueRef Instance_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { @try { - NSString *name(CYCastNSString(property)); - NSLog(@"%@", name); + CYPool pool; + NSString *name(CYCastNSString(pool, property)); + NSLog(@"get:%@", name); return NULL; } CYCatch } -typedef id jocData; - -static JSObjectRef Instance_callAsConstructor(JSContextRef context, JSObjectRef object, size_t count, const JSValueRef arguments[], JSValueRef *exception) { _pooled +static bool Instance_setProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef *exception) { @try { - id data(reinterpret_cast(JSObjectGetPrivate(object))); - return CYMakeObject(context, [[data alloc] autorelease]); + CYPool pool; + NSString *name(CYCastNSString(pool, property)); + NSLog(@"set:%@", name); + return false; } CYCatch } -struct ptrData { - apr_pool_t *pool_; - void *value_; - sig::Type type_; - - void *operator new(size_t size) { - apr_pool_t *pool; - apr_pool_create(&pool, NULL); - void *data(apr_palloc(pool, size)); - reinterpret_cast(data)->pool_ = pool; - return data;; - } - - ptrData(void *value) : - value_(value) - { - } -}; +static bool Instance_deleteProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { + @try { + CYPool pool; + NSString *name(CYCastNSString(pool, property)); + NSLog(@"delete:%@", name); + return false; + } CYCatch +} -struct ffiData : ptrData { - sig::Signature signature_; - ffi_cif cif_; +static JSObjectRef Instance_callAsConstructor(JSContextRef context, JSObjectRef object, size_t count, const JSValueRef arguments[], JSValueRef *exception) { + @try { + jocData *data(reinterpret_cast(JSObjectGetPrivate(object))); + return CYMakeInstance(context, [data->GetValue() alloc]); + } CYCatch +} - ffiData(void (*value)(), const char *type) : - ptrData(reinterpret_cast(value)) - { - sig::Parse(pool_, &signature_, type); - sig::sig_ffi_cif(pool_, &sig::ObjectiveC, &signature_, &cif_); - } -}; +JSObjectRef CYMakeSelector(JSContextRef context, SEL sel) { + selData *data(new selData(sel)); + return JSObjectMake(context, Selector_, data); +} -struct selData : ptrData { - selData(SEL value) : - ptrData(value) - { - } -}; +JSObjectRef CYMakePointer(JSContextRef context, void *pointer) { + ptrData *data(new ptrData(pointer)); + return JSObjectMake(context, Pointer_, data); +} static void Pointer_finalize(JSObjectRef object) { ptrData *data(reinterpret_cast(JSObjectGetPrivate(object))); + data->~ptrData(); apr_pool_destroy(data->pool_); } -static void Instance_finalize(JSObjectRef object) { - id data(reinterpret_cast(JSObjectGetPrivate(object))); - [data release]; -} - -JSObjectRef CYMakeFunction(JSContextRef context, void (*function)(), const char *type) { - ffiData *data(new ffiData(function, type)); +JSObjectRef CYMakeFunctor(JSContextRef context, void (*function)(), const char *type) { + ffiData *data(new ffiData(type, function)); return JSObjectMake(context, Functor_, data); } - -JSObjectRef CYMakeFunction(JSContextRef context, void *function, const char *type) { - return CYMakeFunction(context, reinterpret_cast(function), type); -} - -void CYSetProperty(JSContextRef context, JSObjectRef object, const char *name, JSValueRef value) { - JSValueRef exception(NULL); - JSObjectSetProperty(context, object, CYJSString(name), value, kJSPropertyAttributeNone, &exception); - CYThrow(context, exception); -} - -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; +const char *CYPoolCString(apr_pool_t *pool, JSStringRef value) { + if (pool == NULL) + return [CYCastNSString(NULL, value) UTF8String]; + else { + size_t size(JSStringGetMaximumUTF8CStringSize(value)); + char *string(new(pool) char[size]); + JSStringGetUTF8CString(value, string, size); + return string; + } } -char *CYPoolCString(apr_pool_t *pool, JSContextRef context, JSValueRef value) { +const char *CYPoolCString(apr_pool_t *pool, JSContextRef context, JSValueRef value) { + if (JSValueIsNull(context, value)) + return NULL; return CYPoolCString(pool, CYJSString(context, value)); } // XXX: this macro is unhygenic #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); \ + char *utf8; \ + if (value == NULL) \ + utf8 = NULL; \ + else if (JSStringRef string = CYCopyJSString(context, value)) { \ + size_t size(JSStringGetMaximumUTF8CStringSize(string)); \ + utf8 = reinterpret_cast(alloca(size)); \ + JSStringGetUTF8CString(string, utf8, size); \ + JSStringRelease(string); \ + } else \ + utf8 = NULL; \ utf8; \ }) @@ -672,7 +895,7 @@ SEL CYCastSEL(JSContextRef context, JSValueRef value) { return sel_registerName(CYCastCString(context, value)); } -void *CYCastPointer(JSContextRef context, JSValueRef value) { +void *CYCastPointer_(JSContextRef context, JSValueRef value) { switch (JSValueGetType(context, value)) { case kJSTypeNull: return NULL; @@ -688,6 +911,11 @@ void *CYCastPointer(JSContextRef context, JSValueRef value) { } } +template +_finline Type_ CYCastPointer(JSContextRef context, JSValueRef value) { + return reinterpret_cast(CYCastPointer_(context, value)); +} + void CYPoolFFI(apr_pool_t *pool, JSContextRef context, sig::Type *type, void *data, JSValueRef value) { switch (type->primitive) { case sig::boolean_P: @@ -714,7 +942,7 @@ void CYPoolFFI(apr_pool_t *pool, JSContextRef context, sig::Type *type, void *da case sig::object_P: case sig::typename_P: - *reinterpret_cast(data) = CYCastNSObject(context, value); + *reinterpret_cast(data) = CYCastNSObject(pool, context, value); break; case sig::selector_P: @@ -722,11 +950,11 @@ void CYPoolFFI(apr_pool_t *pool, JSContextRef context, sig::Type *type, void *da break; case sig::pointer_P: - *reinterpret_cast(data) = CYCastPointer(context, value); + *reinterpret_cast(data) = CYCastPointer(context, value); break; case sig::string_P: - *reinterpret_cast(data) = CYPoolCString(pool, context, value); + *reinterpret_cast(data) = CYPoolCString(pool, context, value); break; case sig::struct_P: @@ -746,12 +974,12 @@ JSValueRef CYFromFFI(JSContextRef context, sig::Type *type, void *data) { switch (type->primitive) { case sig::boolean_P: - value = JSValueMakeBoolean(context, *reinterpret_cast(data)); + value = CYCastJSValue(context, *reinterpret_cast(data)); break; #define CYFromFFI_(primitive, native) \ case sig::primitive ## _P: \ - value = JSValueMakeNumber(context, *reinterpret_cast(data)); \ + value = CYCastJSValue(context, *reinterpret_cast(data)); \ break; CYFromFFI_(uchar, unsigned char) @@ -768,39 +996,40 @@ JSValueRef CYFromFFI(JSContextRef context, sig::Type *type, void *data) { CYFromFFI_(double, double) case sig::object_P: - case sig::typename_P: { value = CYCastJSValue(context, *reinterpret_cast(data)); - } break; - - case sig::selector_P: { - if (SEL sel = *reinterpret_cast(data)) { - selData *data(new selData(sel)); - value = JSObjectMake(context, Selector_, data); - } else goto null; - } break; - - case sig::pointer_P: { - if (void *pointer = *reinterpret_cast(data)) { - ptrData *data(new ptrData(pointer)); - value = JSObjectMake(context, Pointer_, data); - } else goto null; - } break; - - case sig::string_P: { + break; + + case sig::typename_P: + value = CYMakeInstance(context, *reinterpret_cast(data)); + break; + + case sig::selector_P: + if (SEL sel = *reinterpret_cast(data)) + value = CYMakeSelector(context, sel); + else goto null; + break; + + case sig::pointer_P: + if (void *pointer = *reinterpret_cast(data)) + value = CYMakePointer(context, pointer); + else goto null; + break; + + case sig::string_P: if (char *utf8 = *reinterpret_cast(data)) - value = JSValueMakeString(context, CYJSString(utf8)); + value = CYCastJSValue(context, utf8); else goto null; - } break; + break; case sig::struct_P: goto fail; case sig::void_P: - value = JSValueMakeUndefined(context); + value = CYJSUndefined(context); break; null: - value = JSValueMakeNull(context); + value = CYJSNull(context); break; default: fail: @@ -811,7 +1040,7 @@ JSValueRef CYFromFFI(JSContextRef context, sig::Type *type, void *data) { return value; } -static JSValueRef CYCallFunction(JSContextRef context, size_t count, const JSValueRef *arguments, JSValueRef *exception, sig::Signature *signature, ffi_cif *cif, void (*function)()) { _pooled +static JSValueRef CYCallFunction(JSContextRef context, size_t count, const JSValueRef *arguments, JSValueRef *exception, sig::Signature *signature, ffi_cif *cif, void (*function)()) { @try { if (count != signature->count - 1) @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"incorrect number of arguments to ffi function" userInfo:nil]; @@ -833,21 +1062,64 @@ static JSValueRef CYCallFunction(JSContextRef context, size_t count, const JSVal } CYCatch } -static JSValueRef Global_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { _pooled +void Closure_(ffi_cif *cif, void *result, void **arguments, void *arg) { + NSLog(@"Closure()"); + ffoData *data(reinterpret_cast(arg)); + + JSContextRef context(data->context_); + + size_t count(data->cif_.nargs); + JSValueRef values[count]; + + for (size_t index(0); index != count; ++index) + values[index] = CYFromFFI(context, data->signature_.elements[1 + index].type, arguments[index]); + + JSValueRef exception(NULL); + JSValueRef value(JSObjectCallAsFunction(context, data->function_, NULL, count, values, &exception)); + CYThrow(context, exception); + + CYPoolFFI(NULL, context, data->signature_.elements[0].type, result, value); +} + +JSObjectRef CYMakeFunctor(JSContextRef context, JSObjectRef function, const char *type) { + // XXX: in case of exceptions this will leak + ffoData *data(new ffoData(type)); + + ffi_closure *closure; + _syscall(closure = (ffi_closure *) mmap( + NULL, sizeof(ffi_closure), + PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, + -1, 0 + )); + + ffi_status status(ffi_prep_closure(closure, &data->cif_, &Closure_, data)); + _assert(status == FFI_OK); + + _syscall(mprotect(closure, sizeof(*closure), PROT_READ | PROT_EXEC)); + + data->value_ = closure; + + data->context_ = CYGetJSContext(); + data->function_ = function; + + return JSObjectMake(context, Functor_, data); +} + +static JSValueRef Global_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { @try { - NSString *name(CYCastNSString(property)); + CYPool pool; + NSString *name(CYCastNSString(pool, property)); if (Class _class = NSClassFromString(name)) - return CYMakeObject(context, _class); + return CYMakeInstance(context, _class); if (NSMutableArray *entry = [Bridge_ objectForKey:name]) switch ([[entry objectAtIndex:0] intValue]) { case 0: - return JSEvaluateScript(JSGetContext(), CYJSString([entry objectAtIndex:1]), NULL, NULL, 0, NULL); + return JSEvaluateScript(CYGetJSContext(), CYJSString([entry objectAtIndex:1]), NULL, NULL, 0, NULL); case 1: - return CYMakeFunction(context, [name cy$symbol], [[entry objectAtIndex:1] UTF8String]); + return CYMakeFunctor(context, reinterpret_cast([name cy$symbol]), CYPoolCString(pool, [entry objectAtIndex:1])); case 2: - CYPool pool; sig::Signature signature; - sig::Parse(pool, &signature, [[entry objectAtIndex:1] UTF8String]); + sig::Parse(pool, &signature, CYPoolCString(pool, [entry objectAtIndex:1])); return CYFromFFI(context, signature.elements[0].type, [name cy$symbol]); } return NULL; @@ -861,27 +1133,58 @@ bool stret(ffi_type *ffi_type) { ); } -static JSValueRef $objc_msgSend(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { _pooled +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) { + @try { + NSLog(@"%s", CYCastCString(context, arguments[0])); + return CYJSUndefined(context); + } CYCatch +} + +static JSValueRef CYApplicationMain(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { + @try { + CYPool pool; + NSString *name(CYCastNSObject(pool, context, arguments[0])); + int argc(*_NSGetArgc()); + char **argv(*_NSGetArgv()); + for (int i(0); i != argc; ++i) + NSLog(@"argv[%i]=%s", i, argv[i]); + _pooled + return CYCastJSValue(context, UIApplicationMain(argc, argv, name, name)); + } CYCatch +} + +static JSValueRef $objc_msgSend(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { const char *type; + CYPool pool; + @try { if (count < 2) @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"too few arguments to objc_msgSend" userInfo:nil]; - id self(CYCastNSObject(context, arguments[0])); + id self(CYCastNSObject(pool, context, arguments[0])); if (self == nil) - return JSValueMakeNull(context); + return CYJSNull(context); SEL _cmd(CYCastSEL(context, arguments[1])); - 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 = [[method _typeString] UTF8String]; + Class _class(object_getClass(self)); + if (Method method = class_getInstanceMethod(_class, _cmd)) + type = method_getTypeEncoding(method); + else { _pooled + 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]); + } } CYCatch - CYPool pool; - sig::Signature signature; sig::Parse(pool, &signature, type); @@ -892,24 +1195,85 @@ static JSValueRef $objc_msgSend(JSContextRef context, JSObjectRef object, JSObje 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) { +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; + memmove(setup + 2, arguments, sizeof(JSValueRef) * count); + return $objc_msgSend(context, NULL, NULL, count + 2, setup, exception); +} + +static JSValueRef Functor_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_, reinterpret_cast(data->value_)); } -JSObjectRef ffi(JSContextRef context, JSObjectRef object, size_t count, const JSValueRef arguments[], JSValueRef *exception) { +JSObjectRef Selector_new(JSContextRef context, JSObjectRef object, size_t count, const JSValueRef arguments[], JSValueRef *exception) { + @try { + if (count != 1) + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"incorrect number of arguments to Selector constructor" userInfo:nil]; + const char *name(CYCastCString(context, arguments[0])); + return CYMakeSelector(context, sel_registerName(name)); + } CYCatch +} + +JSObjectRef Functor_new(JSContextRef context, JSObjectRef object, size_t count, const JSValueRef arguments[], JSValueRef *exception) { @try { if (count != 2) - @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"incorrect number of arguments to ffi constructor" userInfo:nil]; - void *function(CYCastPointer(context, arguments[0])); + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"incorrect number of arguments to Functor constructor" userInfo:nil]; const char *type(CYCastCString(context, arguments[1])); - return CYMakeFunction(context, function, type); + JSValueRef exception(NULL); + if (JSValueIsInstanceOfConstructor(context, arguments[0], Function_, &exception)) { + JSObjectRef function(CYCastJSObject(context, arguments[0])); + return CYMakeFunctor(context, function, type); + } else if (exception != NULL) { + return NULL; + } else { + void (*function)()(CYCastPointer(context, arguments[0])); + return CYMakeFunctor(context, function, type); + } } CYCatch } JSValueRef Pointer_getProperty_value(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { ptrData *data(reinterpret_cast(JSObjectGetPrivate(object))); - return JSValueMakeNumber(context, reinterpret_cast(data->value_)); + return CYCastJSValue(context, reinterpret_cast(data->value_)); +} + +JSValueRef Selector_getProperty_prototype(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { + return Function_; +} + +static JSValueRef Instance_callAsFunction_toString(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { _pooled + @try { + jocData *data(reinterpret_cast(JSObjectGetPrivate(_this))); + return CYCastJSValue(context, CYJSString([data->GetValue() description])); + } CYCatch +} + +static JSValueRef Selector_callAsFunction_toString(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { + @try { + selData *data(reinterpret_cast(JSObjectGetPrivate(_this))); + return CYCastJSValue(context, sel_getName(data->GetValue())); + } CYCatch +} + +static JSValueRef Selector_callAsFunction_type(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { + @try { + if (count != 2) + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"incorrect number of arguments to Selector.type" userInfo:nil]; + CYPool pool; + selData *data(reinterpret_cast(JSObjectGetPrivate(_this))); + Class _class(CYCastNSObject(pool, context, arguments[0])); + bool instance(CYCastBool(context, arguments[1])); + SEL sel(data->GetValue()); + if (Method method = (*(instance ? &class_getInstanceMethod : class_getClassMethod))(_class, sel)) + return CYCastJSValue(context, method_getTypeEncoding(method)); + else if (NSString *type = [Bridge_ objectForKey:CYPoolRelease(pool, [[NSString alloc] initWithFormat:@":%s", sel_getName(sel)])]) + return CYCastJSValue(context, CYJSString(type)); + else + return CYJSNull(context); + } CYCatch } static JSStaticValue Pointer_staticValues[2] = { @@ -917,6 +1281,22 @@ static JSStaticValue Pointer_staticValues[2] = { {NULL, NULL, NULL, 0} }; +/*static JSStaticValue Selector_staticValues[2] = { + {"prototype", &Selector_getProperty_prototype, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete}, + {NULL, NULL, NULL, 0} +};*/ + +static JSStaticFunction Instance_staticFunctions[2] = { + {"toString", &Instance_callAsFunction_toString, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {NULL, NULL, 0} +}; + +static JSStaticFunction Selector_staticFunctions[3] = { + {"toString", &Selector_callAsFunction_toString, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"type", &Selector_callAsFunction_type, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {NULL, NULL, 0} +}; + CYDriver::CYDriver(const std::string &filename) : state_(CYClear), data_(NULL), @@ -938,91 +1318,15 @@ void cy::parser::error(const cy::parser::location_type &location, const std::str driver.errors_.push_back(error); } -void CYConsole(FILE *fin, FILE *fout, FILE *ferr) { - std::string line; - - __gnu_cxx::stdio_filebuf bin(fin, std::ios::in); - std::istream sin(&bin); - - restart: while (!feof(fin)) { _pooled - fputs("cy# ", fout); - fflush(fout); - - std::string command; - std::vector lines; - - read: - if (!std::getline(sin, line)) - break; - - lines.push_back(line); - command += line; - - CYDriver driver(""); - cy::parser parser(driver); - - driver.data_ = command.c_str(); - driver.size_ = command.size(); - - if (parser.parse() != 0) { - for (CYDriver::Errors::const_iterator i(driver.errors_.begin()); i != driver.errors_.end(); ++i) { - cy::position begin(i->location_.begin); - if (begin.line != lines.size() || begin.column - 1 != lines.back().size()) { - std::cerr << i->message_ << std::endl; - goto restart; - } - } - - driver.errors_.clear(); - - fputs("cy> ", fout); - fflush(fout); - - command += '\n'; - goto read; - } - - if (driver.source_ == NULL) - goto restart; - - std::ostringstream str; - driver.source_->Show(str); - - std::string code(str.str()); - std::cout << code << std::endl; - - JSStringRef script(JSStringCreateWithUTF8CString(code.c_str())); - - JSContextRef context(JSGetContext()); - - JSValueRef exception(NULL); - JSValueRef result(JSEvaluateScript(context, script, NULL, NULL, 0, &exception)); - JSStringRelease(script); - - if (exception != NULL) - result = exception; - - if (JSValueIsUndefined(context, result)) - goto restart; - - CFStringRef json; - - @try { json: - json = JSValueToJSONCopy(context, result); - } @catch (id error) { - CYThrow(context, error, &result); - goto json; - } - - fputs([reinterpret_cast(json) UTF8String], fout); - CFRelease(json); - - fputs("\n", fout); - fflush(fout); - } - - fputs("\n", fout); - fflush(fout); +void CYSetArgs(int argc, const char *argv[]) { + JSContextRef context(CYGetJSContext()); + JSValueRef args[argc]; + for (int i(0); i != argc; ++i) + args[i] = CYCastJSValue(context, argv[i]); + JSValueRef exception(NULL); + JSObjectRef array(JSObjectMakeArray(context, argc, args, &exception)); + CYThrow(context, exception); + CYSetProperty(context, System_, CYJSString("args"), array); } MSInitialize { _pooled @@ -1060,33 +1364,48 @@ MSInitialize { _pooled definition = kJSClassDefinitionEmpty; definition.className = "Functor"; definition.parentClass = Pointer_; - definition.callAsFunction = &ffi_callAsFunction; + definition.callAsFunction = &Functor_callAsFunction; Functor_ = JSClassCreate(&definition); definition = kJSClassDefinitionEmpty; definition.className = "Selector"; definition.parentClass = Pointer_; + //definition.staticValues = Selector_staticValues; + definition.staticFunctions = Selector_staticFunctions; + definition.callAsFunction = &Selector_callAsFunction; Selector_ = JSClassCreate(&definition); definition = kJSClassDefinitionEmpty; - definition.className = "Instance_"; + definition.className = "Instance"; + definition.parentClass = Pointer_; + definition.staticFunctions = Instance_staticFunctions; definition.getProperty = &Instance_getProperty; + definition.setProperty = &Instance_setProperty; + definition.deleteProperty = &Instance_deleteProperty; definition.callAsConstructor = &Instance_callAsConstructor; - definition.finalize = &Instance_finalize; Instance_ = JSClassCreate(&definition); definition = kJSClassDefinitionEmpty; definition.getProperty = &Global_getProperty; JSClassRef Global(JSClassCreate(&definition)); - JSContextRef context(JSGlobalContextCreate(Global)); + JSGlobalContextRef context(JSGlobalContextCreate(Global)); Context_ = context; JSObjectRef global(JSContextGetGlobalObject(context)); - CYSetProperty(context, global, "ffi", JSObjectMakeConstructor(context, Functor_, &ffi)); + CYSetProperty(context, global, CYJSString("Selector"), JSObjectMakeConstructor(context, Selector_, &Selector_new)); + CYSetProperty(context, global, CYJSString("Functor"), JSObjectMakeConstructor(context, Functor_, &Functor_new)); - CYSetProperty(context, global, "objc_msgSend", JSObjectMakeFunctionWithCallback(context, CYJSString("objc_msgSend"), &$objc_msgSend)); + CYSetProperty(context, global, CYJSString("CYApplicationMain"), JSObjectMakeFunctionWithCallback(context, CYJSString("CYApplicationMain"), &CYApplicationMain)); + CYSetProperty(context, global, CYJSString("objc_msgSend"), JSObjectMakeFunctionWithCallback(context, CYJSString("objc_msgSend"), &$objc_msgSend)); + + System_ = JSObjectMake(context, NULL, NULL); + CYSetProperty(context, global, CYJSString("system"), System_); + CYSetProperty(context, System_, CYJSString("args"), CYJSNull(context)); + CYSetProperty(context, System_, CYJSString("global"), global); + + CYSetProperty(context, System_, CYJSString("print"), JSObjectMakeFunctionWithCallback(context, CYJSString("print"), &System_print)); Bridge_ = [[NSMutableDictionary dictionaryWithContentsOfFile:@"/usr/lib/libcycript.plist"] retain]; @@ -1094,9 +1413,6 @@ MSInitialize { _pooled message_ = JSStringCreateWithUTF8CString("message"); length_ = JSStringCreateWithUTF8CString("length"); - JSValueRef exception(NULL); - JSValueRef value(JSObjectGetProperty(JSGetContext(), global, CYJSString("Array"), &exception)); - CYThrow(context, exception); - Array_ = JSValueToObject(JSGetContext(), value, &exception); - CYThrow(context, exception); + Array_ = CYCastJSObject(context, CYGetProperty(context, global, CYJSString("Array"))); + Function_ = CYCastJSObject(context, CYGetProperty(context, global, CYJSString("Function"))); }