X-Git-Url: https://git.saurik.com/cycript.git/blobdiff_plain/5999c31517bb72b55257d25f482edc566adec045..283e7e33dbc363c7bba72e3838b5e0d61c92bf2b:/Library.mm diff --git a/Library.mm b/Library.mm index 8135429..0083fb5 100644 --- a/Library.mm +++ b/Library.mm @@ -1,4 +1,4 @@ -/* Cyrker - Remove Execution Server and Disassembler +/* Cycript - Remove Execution Server and Disassembler * Copyright (C) 2009 Jay Freeman (saurik) */ @@ -40,39 +40,33 @@ #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 #include #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" @@ -88,33 +82,327 @@ CFLog(kCFLogLevelNotice, CFSTR("_trace():%u"), __LINE__); \ } while (false) - -#define _pooled _H _pool([[NSAutoreleasePool alloc] init], true); - -static JSContextRef Context_; +#define CYPoolTry { \ + id _saved(nil); \ + NSAutoreleasePool *_pool([[NSAutoreleasePool alloc] init]); \ + @try +#define CYPoolCatch(value) \ + @catch (NSException *error) { \ + _saved = [error retain]; \ + @throw; \ + return value; \ + } @finally { \ + [_pool release]; \ + if (_saved != nil) \ + [_saved autorelease]; \ + } \ +} + +static JSGlobalContextRef Context_; +static JSObjectRef System_; static JSClassRef Functor_; static JSClassRef Instance_; static JSClassRef Pointer_; +static JSClassRef Runtime_; static JSClassRef Selector_; +static JSClassRef Struct_; static JSObjectRef Array_; +static JSObjectRef Function_; -static JSStringRef name_; -static JSStringRef message_; static JSStringRef length_; +static JSStringRef message_; +static JSStringRef name_; +static JSStringRef toCYON_; +static JSStringRef toJSON_; static Class NSCFBoolean_; -static NSMutableDictionary *Bridge_; +static NSArray *Bridge_; + +struct CYData { + apr_pool_t *pool_; + + virtual ~CYData() { + } + + 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;; + } + + static void Finalize(JSObjectRef object) { + CYData *data(reinterpret_cast(JSObjectGetPrivate(object))); + data->~CYData(); + apr_pool_destroy(data->pool_); + } +}; + +struct Pointer_privateData : + CYData +{ + void *value_; + sig::Type type_; + + Pointer_privateData() { + } + + Pointer_privateData(void *value) : + value_(value) + { + } +}; + +struct Selector_privateData : Pointer_privateData { + Selector_privateData(SEL value) : + Pointer_privateData(value) + { + } + + SEL GetValue() const { + return reinterpret_cast(value_); + } +}; + +struct Instance_privateData : + Pointer_privateData +{ + bool transient_; + + Instance_privateData(id value, bool transient) : + Pointer_privateData(value) + { + } + + virtual ~Instance_privateData() { + if (!transient_) + [GetValue() release]; + } + + id GetValue() const { + return reinterpret_cast(value_); + } +}; + +namespace sig { + +void Copy(apr_pool_t *pool, Type &lhs, Type &rhs); + +void Copy(apr_pool_t *pool, Element &lhs, Element &rhs) { + lhs.name = apr_pstrdup(pool, rhs.name); + if (rhs.type == NULL) + lhs.type = NULL; + else { + lhs.type = new(pool) Type; + Copy(pool, *lhs.type, *rhs.type); + } + lhs.offset = rhs.offset; +} + +void Copy(apr_pool_t *pool, Signature &lhs, Signature &rhs) { + size_t count(rhs.count); + lhs.count = count; + lhs.elements = new(pool) Element[count]; + for (size_t index(0); index != count; ++index) + Copy(pool, lhs.elements[index], rhs.elements[index]); +} + +void Copy(apr_pool_t *pool, Type &lhs, Type &rhs) { + lhs.primitive = rhs.primitive; + lhs.name = apr_pstrdup(pool, rhs.name); + lhs.flags = rhs.flags; + + if (sig::IsAggregate(rhs.primitive)) + Copy(pool, lhs.data.signature, rhs.data.signature); + else { + if (rhs.data.data.type != NULL) { + lhs.data.data.type = new(pool) Type; + Copy(pool, *lhs.data.data.type, *rhs.data.data.type); + } + + lhs.data.data.size = rhs.data.data.size; + } +} + +void Copy(apr_pool_t *pool, ffi_type &lhs, ffi_type &rhs) { + lhs.size = rhs.size; + lhs.alignment = rhs.alignment; + lhs.type = rhs.type; + if (rhs.elements == NULL) + lhs.elements = NULL; + else { + size_t count(0); + while (rhs.elements[count] != NULL) + ++count; + + lhs.elements = new(pool) ffi_type *[count + 1]; + lhs.elements[count] = NULL; + + for (size_t index(0); index != count; ++index) { + // XXX: if these are libffi native then you can just take them + ffi_type *ffi(new(pool) ffi_type); + lhs.elements[index] = ffi; + sig::Copy(pool, *ffi, *rhs.elements[index]); + } + } +} + +} + +struct CStringMapLess : + std::binary_function +{ + _finline bool operator ()(const char *lhs, const char *rhs) const { + return strcmp(lhs, rhs) < 0; + } +}; + +struct Type_privateData { + sig::Type type_; + ffi_type ffi_; + + Type_privateData(apr_pool_t *pool, sig::Type *type, ffi_type *ffi) { + sig::Copy(pool, type_, *type); + sig::Copy(pool, ffi_, *ffi); + } +}; + +struct Struct_privateData : + Pointer_privateData +{ + JSObjectRef owner_; + Type_privateData *type_; + + Struct_privateData() { + } +}; + +typedef std::map TypeMap; +static TypeMap Types_; + +JSObjectRef CYMakeStruct(JSContextRef context, void *data, sig::Type *type, ffi_type *ffi, JSObjectRef owner) { + Struct_privateData *internal(new Struct_privateData()); + apr_pool_t *pool(internal->pool_); + Type_privateData *typical(new(pool) Type_privateData(pool, type, ffi)); + internal->type_ = typical; + + if (owner != NULL) { + internal->owner_ = owner; + internal->value_ = data; + } else { + internal->owner_ = NULL; + + size_t size(typical->ffi_.size); + void *copy(apr_palloc(internal->pool_, size)); + memcpy(copy, data, size); + internal->value_ = copy; + } + + return JSObjectMake(context, Struct_, internal); +} + +void Structor_(apr_pool_t *pool, const char *name, const char *types, sig::Type *type) { + CYPoolTry { + if (NSMutableArray *entry = [[Bridge_ objectAtIndex:2] objectForKey:[NSString stringWithUTF8String:name]]) { + switch ([[entry objectAtIndex:0] intValue]) { + case 0: + static CYPool Pool_; + sig::Parse(Pool_, &type->data.signature, [[entry objectAtIndex:1] UTF8String], &Structor_); + break; + } + } + } CYPoolCatch() +} + +struct Functor_privateData : + Pointer_privateData +{ + sig::Signature signature_; + ffi_cif cif_; + + Functor_privateData(const char *type, void (*value)()) : + Pointer_privateData(reinterpret_cast(value)) + { + sig::Parse(pool_, &signature_, type, &Structor_); + sig::sig_ffi_cif(pool_, &sig::ObjectiveC, &signature_, &cif_); + } +}; -struct Client { - CFHTTPMessageRef message_; - CFSocketRef socket_; +struct ffoData : + Functor_privateData +{ + JSContextRef context_; + JSObjectRef function_; + + ffoData(const char *type) : + Functor_privateData(type, NULL) + { + } }; -JSObjectRef CYMakeObject(JSContextRef context, id object) { - return JSObjectMake(context, Instance_, [object retain]); +JSObjectRef CYMakeInstance(JSContextRef context, id object, bool transient) { + if (!transient) + object = [object retain]; + Instance_privateData *data(new Instance_privateData(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:NSInternalInconsistencyException 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); +} + +size_t CYCastIndex(const char *value) { + if (value[0] == '0') { + if (value[1] == '\0') + return 0; + } else { + char *end; + size_t index(strtoul(value, &end, 10)); + if (value + strlen(value) == end) + return index; + } + + return _not(size_t); +} + +size_t CYCastIndex(NSString *value) { + return CYCastIndex([value UTF8String]); } @interface NSMethodSignature (Cycript) @@ -122,8 +410,19 @@ JSObjectRef CYMakeObject(JSContextRef context, id object) { @end @interface NSObject (Cycript) -- (NSString *) cy$toJSON; -- (JSValueRef) cy$JSValueInContext:(JSContextRef)context; + +- (JSType) cy$JSType; + +- (NSObject *) cy$toJSON:(NSString *)key; +- (NSString *) cy$toCYON; +- (NSString *) cy$toKey; + +- (JSValueRef) cy$JSValueInContext:(JSContextRef)context transient:(bool)transient; + +- (NSObject *) cy$getProperty:(NSString *)name; +- (bool) cy$setProperty:(NSString *)name to:(NSObject *)value; +- (bool) cy$deleteProperty:(NSString *)name; + @end @interface NSString (Cycript) @@ -134,33 +433,171 @@ JSObjectRef CYMakeObject(JSContextRef context, id object) { - (void *) cy$symbol; @end +struct PropertyAttributes { + CYPool pool_; + + const char *name; + + const char *variable; + + const char *getter_; + const char *setter_; + + bool readonly; + bool copy; + bool retain; + bool nonatomic; + bool dynamic; + bool weak; + bool garbage; + + PropertyAttributes(objc_property_t property) : + variable(NULL), + getter_(NULL), + setter_(NULL), + readonly(false), + copy(false), + retain(false), + nonatomic(false), + dynamic(false), + weak(false), + garbage(false) + { + name = property_getName(property); + const char *attributes(property_getAttributes(property)); + + for (char *state, *token(apr_strtok(apr_pstrdup(pool_, attributes), ",", &state)); token != NULL; token = apr_strtok(NULL, ",", &state)) { + switch (*token) { + case 'R': readonly = true; break; + case 'C': copy = true; break; + case '&': retain = true; break; + case 'N': nonatomic = true; break; + case 'G': getter_ = token + 1; break; + case 'S': setter_ = token + 1; break; + case 'V': variable = token + 1; break; + } + } + + /*if (variable == NULL) { + variable = property_getName(property); + size_t size(strlen(variable)); + char *name(new(pool_) char[size + 2]); + name[0] = '_'; + memcpy(name + 1, variable, size); + name[size + 1] = '\0'; + variable = name; + }*/ + } + + const char *Getter() { + if (getter_ == NULL) + getter_ = apr_pstrdup(pool_, name); + return getter_; + } + + const char *Setter() { + if (setter_ == NULL && !readonly) { + size_t length(strlen(name)); + + char *temp(new(pool_) char[length + 5]); + temp[0] = 's'; + temp[1] = 'e'; + temp[2] = 't'; + + if (length != 0) { + temp[3] = toupper(name[0]); + memcpy(temp + 4, name + 1, length - 1); + } + + temp[length + 3] = ':'; + temp[length + 4] = '\0'; + setter_ = temp; + } + + return setter_; + } + +}; + @implementation NSObject (Cycript) -- (NSString *) cy$toJSON { +- (JSType) cy$JSType { + return kJSTypeObject; +} + +- (NSObject *) cy$toJSON:(NSString *)key { return [self description]; } -- (JSValueRef) cy$JSValueInContext:(JSContextRef)context { - return CYMakeObject(context, self); +- (NSString *) cy$toCYON { + return [[self cy$toJSON:@""] cy$toCYON]; +} + +- (NSString *) cy$toKey { + return [self cy$toCYON]; +} + +- (JSValueRef) cy$JSValueInContext:(JSContextRef)context transient:(bool)transient { + return CYMakeInstance(context, self, transient); +} + +- (NSObject *) cy$getProperty:(NSString *)name { + /*if (![name isEqualToString:@"prototype"]) + NSLog(@"get:%@", name);*/ + return nil; +} + +- (bool) cy$setProperty:(NSString *)name to:(NSObject *)value { + //NSLog(@"set:%@", name); + return false; +} + +- (bool) cy$deleteProperty:(NSString *)name { + //NSLog(@"delete:%@", name); + return false; } @end @implementation WebUndefined (Cycript) -- (NSString *) cy$toJSON { +- (JSType) cy$JSType { + return kJSTypeUndefined; +} + +- (NSObject *) cy$toJSON:(NSString *)key { + return self; +} + +- (NSString *) cy$toCYON { return @"undefined"; } -- (JSValueRef) cy$JSValueInContext:(JSContextRef)context { - return JSValueMakeUndefined(context); +- (JSValueRef) cy$JSValueInContext:(JSContextRef)context transient:(bool)transient { + return CYJSUndefined(context); +} + +@end + +@implementation NSNull (Cycript) + +- (JSType) cy$JSType { + return kJSTypeNull; +} + +- (NSObject *) cy$toJSON:(NSString *)key { + return self; +} + +- (NSString *) cy$toCYON { + return @"null"; } @end @implementation NSArray (Cycript) -- (NSString *) cy$toJSON { +- (NSString *) cy$toCYON { NSMutableString *json([[[NSMutableString alloc] init] autorelease]); [json appendString:@"["]; @@ -170,20 +607,59 @@ JSObjectRef CYMakeObject(JSContextRef context, id object) { [json appendString:@","]; else comma = true; - [json appendString:[object cy$toJSON]]; + if ([object cy$JSType] != kJSTypeUndefined) + [json appendString:[object cy$toCYON]]; + else { + [json appendString:@","]; + comma = false; + } } [json appendString:@"]"]; return json; } +- (NSObject *) cy$getProperty:(NSString *)name { + if ([name isEqualToString:@"length"]) + return [NSNumber numberWithUnsignedInteger:[self count]]; + + size_t index(CYCastIndex(name)); + if (index == _not(size_t) || index >= [self count]) + return [super cy$getProperty:name]; + else + return [self objectAtIndex:index]; +} + +@end + +@implementation NSMutableArray (Cycript) + +- (bool) cy$setProperty:(NSString *)name to:(NSObject *)value { + size_t index(CYCastIndex(name)); + if (index == _not(size_t) || index >= [self count]) + return [super cy$setProperty:name to:value]; + else { + [self replaceObjectAtIndex:index withObject:(value ?: [NSNull null])]; + return true; + } +} + +- (bool) cy$deleteProperty:(NSString *)name { + size_t index(CYCastIndex(name)); + if (index == _not(size_t) || index >= [self count]) + return [super cy$deleteProperty:name]; + else { + [self removeObjectAtIndex:index]; + return true; + } +} + @end @implementation NSDictionary (Cycript) -- (NSString *) cy$toJSON { +- (NSString *) cy$toCYON { NSMutableString *json([[[NSMutableString alloc] init] autorelease]); - [json appendString:@"("]; [json appendString:@"{"]; bool comma(false); @@ -192,26 +668,57 @@ JSObjectRef CYMakeObject(JSContextRef context, id object) { [json appendString:@","]; else comma = true; - [json appendString:[key cy$toJSON]]; + [json appendString:[key cy$toKey]]; [json appendString:@":"]; NSObject *object([self objectForKey:key]); - [json appendString:[object cy$toJSON]]; + [json appendString:[object cy$toCYON]]; } - [json appendString:@"})"]; + [json appendString:@"}"]; return json; } +- (NSObject *) cy$getProperty:(NSString *)name { + return [self objectForKey:name]; +} + +@end + +@implementation NSMutableDictionary (Cycript) + +- (bool) cy$setProperty:(NSString *)name to:(NSObject *)value { + [self setObject:(value ?: [NSNull null]) forKey:name]; + return true; +} + +- (bool) cy$deleteProperty:(NSString *)name { + if ([self objectForKey:name] == nil) + return false; + else { + [self removeObjectForKey:name]; + return true; + } +} + @end @implementation NSNumber (Cycript) -- (NSString *) cy$toJSON { - return [self class] != NSCFBoolean_ ? [self stringValue] : [self boolValue] ? @"true" : @"false"; +- (JSType) cy$JSType { + // XXX: this just seems stupid + return [self class] == NSCFBoolean_ ? kJSTypeBoolean : kJSTypeNumber; +} + +- (NSObject *) cy$toJSON:(NSString *)key { + return self; +} + +- (NSString *) cy$toCYON { + return [self cy$JSType] != kJSTypeBoolean ? [self stringValue] : [self boolValue] ? @"true" : @"false"; } -- (JSValueRef) cy$JSValueInContext:(JSContextRef)context { - return [self class] != NSCFBoolean_ ? JSValueMakeNumber(context, [self doubleValue]) : JSValueMakeBoolean(context, [self boolValue]); +- (JSValueRef) cy$JSValueInContext:(JSContextRef)context transient:(bool)transient { + return [self cy$JSType] != kJSTypeBoolean ? CYCastJSValue(context, [self doubleValue]) : CYCastJSValue(context, [self boolValue]); } - (void *) cy$symbol { @@ -222,7 +729,16 @@ JSObjectRef CYMakeObject(JSContextRef context, id object) { @implementation NSString (Cycript) -- (NSString *) cy$toJSON { +- (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); @@ -237,8 +753,33 @@ JSObjectRef CYMakeObject(JSContextRef context, id object) { return [reinterpret_cast(json) autorelease]; } +- (NSString *) cy$toKey { + const char *value([self UTF8String]); + size_t size(strlen(value)); + + if (size == 0) + goto cyon; + + if (DigitRange_[value[0]]) { + if (CYCastIndex(self) == _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 { - return dlsym(RTLD_DEFAULT, [self UTF8String]); + CYPool pool; + return dlsym(RTLD_DEFAULT, CYPoolCString(pool, self)); } @end @@ -250,6 +791,8 @@ JSObjectRef CYMakeObject(JSContextRef context, id object) { - (id) initWithJSObject:(JSObjectRef)object inContext:(JSContextRef)context; +- (NSString *) cy$toJSON:(NSString *)key; + - (NSUInteger) count; - (id) objectForKey:(id)key; - (NSEnumerator *) keyEnumerator; @@ -270,10 +813,16 @@ JSObjectRef CYMakeObject(JSContextRef context, id object) { @end -JSContextRef JSGetContext() { +CYRange DigitRange_ (0x3ff000000000000LLU, 0x000000000000000LLU); // 0-9 +CYRange WordStartRange_(0x000001000000000LLU, 0x7fffffe87fffffeLLU); // A-Za-z_$ +CYRange WordEndRange_ (0x3ff001000000000LLU, 0x7fffffe87fffffeLLU); // A-Za-z_$0-9 + +JSGlobalContextRef CYGetJSContext() { return Context_; } +#define CYTry \ + @try #define CYCatch \ @catch (id error) { \ CYThrow(context, error, exception); \ @@ -282,54 +831,101 @@ JSContextRef JSGetContext() { 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 (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) { + if (JSValueIsObjectOfClass(context, object, Instance_)) { + Instance_privateData *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_() { + if (string_ != NULL) + 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 { @@ -345,6 +941,18 @@ CFStringRef CYCopyCFString(JSContextRef context, JSValueRef value) { return CYCopyCFString(CYJSString(context, value)); } +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)); @@ -357,56 +965,158 @@ CFNumberRef CYCopyCFNumber(JSContextRef context, JSValueRef value) { return CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &number); } -NSString *CYCastNSString(JSStringRef value) { - return [reinterpret_cast(CYCopyCFString(value)) autorelease]; +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)); } -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, 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, bool transient = true) { + return value == nil ? CYJSNull(context) : [value cy$JSValueInContext:context transient:transient]; +} + +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, size_t index) { + JSValueRef exception(NULL); + JSValueRef value(JSObjectGetPropertyAtIndex(context, object, index, &exception)); + CYThrow(context, exception); + return value; +} + +JSValueRef CYGetProperty(JSContextRef context, JSObjectRef object, JSStringRef name) { + JSValueRef exception(NULL); + JSValueRef value(JSObjectGetProperty(context, object, name, &exception)); + CYThrow(context, exception); + return value; } -JSValueRef CYCastJSValue(JSContextRef context, id value) { - return value == nil ? JSValueMakeNull(context) : [value cy$JSValueInContext:context]; +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) { + 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 { @@ -416,6 +1126,28 @@ void CYThrow(JSContextRef context, id error, JSValueRef *exception) { } return self; } +- (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)) + return [super cy$toCYON]; + else { + JSValueRef value(CYCallAsFunction(context_, (JSObjectRef) toCYON, object_, 0, NULL)); + return CYCastNSString(NULL, CYJSString(context_, value)); + } +} + - (NSUInteger) count { JSPropertyNameArrayRef names(JSObjectCopyPropertyNames(context_, object_)); size_t size(JSPropertyNameArrayGetCount(names)); @@ -424,10 +1156,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 { @@ -438,9 +1167,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 { @@ -462,232 +1189,191 @@ 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)); - return reinterpret_cast([(object == nil ? @"null" : [object cy$toJSON]) retain]); +CFStringRef CYCopyCYONString(JSContextRef context, JSValueRef value, JSValueRef *exception) { + CYTry { + CYPoolTry { + id object(CYCastNSObject(NULL, context, value) ?: [NSNull null]); + return reinterpret_cast([[object cy$toCYON] retain]); + } CYPoolCatch(NULL) + } CYCatch } -static void OnData(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *value, void *info) { - switch (type) { - case kCFSocketDataCallBack: - CFDataRef data(reinterpret_cast(value)); - Client *client(reinterpret_cast(info)); - - if (client->message_ == NULL) - client->message_ = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, TRUE); - - if (!CFHTTPMessageAppendBytes(client->message_, CFDataGetBytePtr(data), CFDataGetLength(data))) - CFLog(kCFLogLevelError, CFSTR("CFHTTPMessageAppendBytes()")); - else if (CFHTTPMessageIsHeaderComplete(client->message_)) { - CFURLRef url(CFHTTPMessageCopyRequestURL(client->message_)); - Boolean absolute; - CFStringRef path(CFURLCopyStrictPath(url, &absolute)); - CFRelease(client->message_); - - CFStringRef code(CFURLCreateStringByReplacingPercentEscapes(kCFAllocatorDefault, path, CFSTR(""))); - CFRelease(path); - - JSStringRef script(JSStringCreateWithCFString(code)); - CFRelease(code); - - 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(JSGetContext(), result)); - CFDataRef body(CFStringCreateExternalRepresentation(kCFAllocatorDefault, json, kCFStringEncodingUTF8, NULL)); - CFRelease(json); - - CFStringRef length(CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%u"), CFDataGetLength(body))); - CFHTTPMessageSetHeaderFieldValue(response, CFSTR("Content-Length"), length); - CFRelease(length); - - CFHTTPMessageSetBody(response, body); - CFRelease(body); - - CFDataRef serialized(CFHTTPMessageCopySerializedMessage(response)); - CFRelease(response); - - CFSocketSendData(socket, NULL, serialized, 0); - CFRelease(serialized); - - CFRelease(url); - } - break; - } +const char *CYPoolCYONString(apr_pool_t *pool, JSContextRef context, JSValueRef value, JSValueRef *exception) { + if (NSString *json = (NSString *) CYCopyCYONString(context, value, exception)) { + const char *string(CYPoolCString(pool, json)); + [json release]; + return string; + } else return NULL; } -static void OnAccept(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *value, void *info) { - switch (type) { - case kCFSocketAcceptCallBack: - Client *client(new Client()); - - client->message_ = NULL; +static JSValueRef Instance_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { + CYPool pool; - CFSocketContext context; - context.version = 0; - context.info = client; - context.retain = NULL; - context.release = NULL; - context.copyDescription = NULL; + CYTry { + NSString *self(CYCastNSObject(pool, context, object)); + NSString *name(CYCastNSString(pool, property)); - client->socket_ = CFSocketCreateWithNative(kCFAllocatorDefault, *reinterpret_cast(value), kCFSocketDataCallBack, &OnData, &context); + CYPoolTry { + if (NSObject *data = [self cy$getProperty:name]) + return CYCastJSValue(context, data); + } CYPoolCatch(NULL) - CFRunLoopAddSource(CFRunLoopGetCurrent(), CFSocketCreateRunLoopSource(kCFAllocatorDefault, client->socket_, 0), kCFRunLoopDefaultMode); - break; - } -} + if (objc_property_t property = class_getProperty(object_getClass(self), [name UTF8String])) { + PropertyAttributes attributes(property); + SEL sel(sel_registerName(attributes.Getter())); + return CYSendMessage(pool, context, self, sel, 0, NULL, exception); + } -static JSValueRef Instance_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { _pooled - @try { - NSString *name(CYCastNSString(property)); - NSLog(@"%@", name); return NULL; } CYCatch } -typedef id jocData; +static bool Instance_setProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef *exception) { + CYPool pool; + + CYTry { + NSString *self(CYCastNSObject(pool, context, object)); + NSString *name(CYCastNSString(pool, property)); + NSString *data(CYCastNSObject(pool, context, value)); + + CYPoolTry { + if ([self cy$setProperty:name to:data]) + return true; + } CYPoolCatch(NULL) + + if (objc_property_t property = class_getProperty(object_getClass(self), [name UTF8String])) { + 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, exception); + return true; + } + } -static JSObjectRef Instance_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]); + 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) - { - } -}; - -struct ffiData : ptrData { - sig::Signature signature_; - ffi_cif cif_; - - ffiData(void (*value)(), const char *type) : - ptrData(reinterpret_cast(value)) - { - sig::Parse(pool_, &signature_, type); - sig::sig_ffi_cif(pool_, &sig::ObjectiveC, &signature_, &cif_); - } -}; - -struct selData : ptrData { - selData(SEL value) : - ptrData(value) - { - } -}; - -static void Pointer_finalize(JSObjectRef object) { - ptrData *data(reinterpret_cast(JSObjectGetPrivate(object))); - apr_pool_destroy(data->pool_); +static bool Instance_deleteProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { + CYTry { + CYPoolTry { + NSString *self(CYCastNSObject(NULL, context, object)); + NSString *name(CYCastNSString(NULL, property)); + return [self cy$deleteProperty:name]; + } CYPoolCatch(NULL) + } CYCatch } -static void Instance_finalize(JSObjectRef object) { - id data(reinterpret_cast(JSObjectGetPrivate(object))); - [data release]; +static JSObjectRef Instance_callAsConstructor(JSContextRef context, JSObjectRef object, size_t count, const JSValueRef arguments[], JSValueRef *exception) { + CYTry { + Instance_privateData *data(reinterpret_cast(JSObjectGetPrivate(object))); + return CYMakeInstance(context, [data->GetValue() alloc], true); + } CYCatch } -JSObjectRef CYMakeFunction(JSContextRef context, void (*function)(), const char *type) { - ffiData *data(new ffiData(function, type)); - return JSObjectMake(context, Functor_, data); +JSObjectRef CYMakeSelector(JSContextRef context, SEL sel) { + Selector_privateData *data(new Selector_privateData(sel)); + return JSObjectMake(context, Selector_, data); } - -JSObjectRef CYMakeFunction(JSContextRef context, void *function, const char *type) { - return CYMakeFunction(context, reinterpret_cast(function), type); +JSObjectRef CYMakePointer(JSContextRef context, void *pointer) { + Pointer_privateData *data(new Pointer_privateData(pointer)); + return JSObjectMake(context, Pointer_, data); } -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); +JSObjectRef CYMakeFunctor(JSContextRef context, void (*function)(), const char *type) { + Functor_privateData *data(new Functor_privateData(type, function)); + return JSObjectMake(context, Functor_, data); } -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, size_t *length = NULL) { + if (pool == NULL) { + const char *string([CYCastNSString(NULL, value) UTF8String]); + if (length != NULL) + *length = strlen(string); + return string; + } else { + size_t size(JSStringGetMaximumUTF8CStringSize(value)); + char *string(new(pool) char[size]); + JSStringGetUTF8CString(value, string, size); + // XXX: this is ironic + if (length != NULL) + *length = strlen(string); + return string; + } } -char *CYPoolCString(apr_pool_t *pool, JSContextRef context, JSValueRef value) { - return CYPoolCString(pool, CYJSString(context, value)); +const char *CYPoolCString(apr_pool_t *pool, JSContextRef context, JSValueRef value, size_t *length = NULL) { + if (!JSValueIsNull(context, value)) + return CYPoolCString(pool, CYJSString(context, value), length); + else { + if (length != NULL) + *length = 0; + return NULL; + } } // 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; \ }) -SEL CYCastSEL(JSContextRef context, JSValueRef value) { - if (JSValueIsNull(context, value)) - return NULL; - else if (JSValueIsObjectOfClass(context, value, Selector_)) { - selData *data(reinterpret_cast(JSObjectGetPrivate((JSObjectRef) value))); - return reinterpret_cast(data->value_); - } else - 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; - case kJSTypeString: + /*case kJSTypeString: return dlsym(RTLD_DEFAULT, CYCastCString(context, value)); case kJSTypeObject: if (JSValueIsObjectOfClass(context, value, Pointer_)) { - ptrData *data(reinterpret_cast(JSObjectGetPrivate((JSObjectRef) value))); + Pointer_privateData *data(reinterpret_cast(JSObjectGetPrivate((JSObjectRef) value))); return data->value_; - } + }*/ default: - return reinterpret_cast(static_cast(CYCastDouble(context, value))); + double number(CYCastDouble(context, value)); + if (std::isnan(number)) + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"cannot convert value to pointer" userInfo:nil]; + return reinterpret_cast(static_cast(static_cast(number))); } } -void CYPoolFFI(apr_pool_t *pool, JSContextRef context, sig::Type *type, void *data, JSValueRef value) { +template +_finline Type_ CYCastPointer(JSContextRef context, JSValueRef value) { + return reinterpret_cast(CYCastPointer_(context, value)); +} + +SEL CYCastSEL(JSContextRef context, JSValueRef value) { + if (JSValueIsObjectOfClass(context, value, Selector_)) { + Selector_privateData *data(reinterpret_cast(JSObjectGetPrivate((JSObjectRef) value))); + return reinterpret_cast(data->value_); + } else + return CYCastPointer(context, value); +} + +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(data) = JSValueToBoolean(context, value); @@ -713,7 +1399,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: @@ -721,36 +1407,61 @@ 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: - goto fail; + case sig::struct_P: { + uint8_t *base(reinterpret_cast(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: fail: + default: NSLog(@"CYPoolFFI(%c)\n", type->primitive); _assert(false); } } -JSValueRef CYFromFFI(JSContextRef context, sig::Type *type, void *data) { +JSValueRef CYFromFFI(JSContextRef context, sig::Type *type, ffi_type *ffi, void *data, JSObjectRef owner = NULL) { JSValueRef value; 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) @@ -767,42 +1478,44 @@ 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; + 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::typename_P: + value = CYMakeInstance(context, *reinterpret_cast(data), true); + 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::selector_P: + if (SEL sel = *reinterpret_cast(data)) + value = CYMakeSelector(context, sel); + else goto null; + break; - case sig::string_P: { + 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; + value = CYMakeStruct(context, data, type, ffi, owner); + break; case sig::void_P: - value = JSValueMakeUndefined(context); + value = CYJSUndefined(context); break; null: - value = JSValueMakeNull(context); + value = CYJSNull(context); break; - default: fail: + default: NSLog(@"CYFromFFI(%c)\n", type->primitive); _assert(false); } @@ -810,44 +1523,181 @@ 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 - @try { - if (count != signature->count - 1) +bool Index_(apr_pool_t *pool, Struct_privateData *internal, JSStringRef property, ssize_t &index, uint8_t *&base) { + Type_privateData *typical(internal->type_); + + size_t length; + const char *name(CYPoolCString(pool, property, &length)); + double number(CYCastDouble(name, length)); + + size_t count(typical->type_.data.signature.count); + + if (std::isnan(number)) { + if (property == NULL) + return false; + + sig::Element *elements(typical->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(number); + if (index != number || index < 0 || static_cast(index) >= count) + return false; + } + + base: + base = reinterpret_cast(internal->value_); + for (ssize_t local(0); local != index; ++local) + base += typical->ffi_.elements[local]->size; + + return true; +} + +static JSValueRef Struct_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { + CYPool pool; + Struct_privateData *internal(reinterpret_cast(JSObjectGetPrivate(object))); + Type_privateData *typical(internal->type_); + + ssize_t index; + uint8_t *base; + + if (!Index_(pool, internal, property, index, base)) + return NULL; + + CYTry { + return CYFromFFI(context, typical->type_.data.signature.elements[index].type, typical->ffi_.elements[index], base, object); + } CYCatch +} + +static bool Struct_setProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef *exception) { + CYPool pool; + Struct_privateData *internal(reinterpret_cast(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->ffi_.elements[index], base, value); + return true; + } CYCatch +} + +static void Struct_getPropertyNames(JSContextRef context, JSObjectRef object, JSPropertyNameAccumulatorRef names) { + Struct_privateData *internal(reinterpret_cast(JSObjectGetPrivate(object))); + Type_privateData *typical(internal->type_); + + size_t count(typical->type_.data.signature.count); + sig::Element *elements(typical->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, 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]; - CYPool pool; - void *values[count]; + size_t size(setups + count); + void *values[size]; + memcpy(values, setup, sizeof(void *) * setups); - for (unsigned index(0); index != count; ++index) { + 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[cif->arg_types[index]->size]; - CYPoolFFI(pool, context, element->type, values[index], arguments[index]); + 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, value); + return CYFromFFI(context, signature->elements[0].type, cif->rtype, value); } CYCatch } -static JSValueRef Global_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { _pooled - @try { - NSString *name(CYCastNSString(property)); +void Closure_(ffi_cif *cif, void *result, void **arguments, void *arg) { + 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, data->cif_.arg_types[index], arguments[index]); + + JSValueRef value(CYCallAsFunction(context, data->function_, NULL, count, values)); + CYPoolFFI(NULL, context, data->signature_.elements[0].type, data->cif_.rtype, 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 Runtime_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { + CYTry { + CYPool pool; + NSString *name(CYCastNSString(pool, property)); if (Class _class = NSClassFromString(name)) - return CYMakeObject(context, _class); - if (NSMutableArray *entry = [Bridge_ objectForKey:name]) + return CYMakeInstance(context, _class, true); + if (NSMutableArray *entry = [[Bridge_ objectAtIndex:0] 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; + // XXX: this is horrendously inefficient sig::Signature signature; - sig::Parse(pool, &signature, [[entry objectAtIndex:1] UTF8String]); - return CYFromFFI(context, signature.elements[0].type, [name cy$symbol]); + 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]); } return NULL; } CYCatch @@ -860,55 +1710,222 @@ 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 - const char *type; +extern "C" { + int *_NSGetArgc(void); + char ***_NSGetArgv(void); + int UIApplicationMain(int argc, char *argv[], NSString *principalClassName, NSString *delegateClassName); +} - @try { - if (count < 2) - @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"too few arguments to objc_msgSend" userInfo:nil]; +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 +} - id self(CYCastNSObject(context, arguments[0])); - if (self == nil) - return JSValueMakeNull(context); +static JSValueRef CYApplicationMain(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { + CYTry { + CYPool pool; - 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]; + int argc(CYCastDouble(context, arguments[0])); + char **argv(CYCastPointer(context, arguments[1])); + NSString *principal(CYCastNSObject(pool, context, arguments[2])); + NSString *delegate(CYCastNSObject(pool, context, arguments[3])); - type = [[method _typeString] UTF8String]; + argc = *_NSGetArgc() - 1; + argv = *_NSGetArgv() + 1; + for (int i(0); i != argc; ++i) + NSLog(@"argv[%i]=%s", i, argv[i]); + + _pooled + return CYCastJSValue(context, UIApplicationMain(argc, argv, principal, delegate)); } CYCatch +} - CYPool pool; +JSValueRef CYSendMessage(apr_pool_t *pool, JSContextRef context, id self, SEL _cmd, size_t count, const JSValueRef arguments[], JSValueRef *exception) { + const char *type; + + Class _class(object_getClass(self)); + if (Method method = class_getInstanceMethod(_class, _cmd)) + type = method_getTypeEncoding(method); + else { + 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) + } + + void *setup[2]; + setup[0] = &self; + setup[1] = &_cmd; sig::Signature signature; - sig::Parse(pool, &signature, type); + 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(&objc_msgSend_stret) : reinterpret_cast(&objc_msgSend); - return CYCallFunction(context, count, arguments, exception, &signature, &cif, function); + return CYCallFunction(pool, context, 2, setup, count, arguments, exception, &signature, &cif, function); +} + +static JSValueRef $objc_msgSend(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { + CYPool pool; + + id self; + SEL _cmd; + + CYTry { + if (count < 2) + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"too few arguments to objc_msgSend" userInfo:nil]; + + self = CYCastNSObject(pool, context, arguments[0]); + if (self == nil) + return CYJSNull(context); + + _cmd = CYCastSEL(context, arguments[1]); + } CYCatch + + return CYSendMessage(pool, context, self, _cmd, count - 2, arguments + 2, 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; + memcpy(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) { + CYPool pool; + Functor_privateData *data(reinterpret_cast(JSObjectGetPrivate(object))); + return CYCallFunction(pool, context, 0, NULL, count, arguments, exception, &data->signature_, &data->cif_, reinterpret_cast(data->value_)); } -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_, reinterpret_cast(data->value_)); +JSObjectRef Selector_new(JSContextRef context, JSObjectRef object, size_t count, const JSValueRef arguments[], JSValueRef *exception) { + CYTry { + 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 ffi(JSContextRef context, JSObjectRef object, size_t count, const JSValueRef arguments[], JSValueRef *exception) { - @try { +JSObjectRef Functor_new(JSContextRef context, JSObjectRef object, size_t count, const JSValueRef arguments[], JSValueRef *exception) { + CYTry { 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_)); + Pointer_privateData *data(reinterpret_cast(JSObjectGetPrivate(object))); + return CYCastJSValue(context, reinterpret_cast(data->value_)); +} + +JSValueRef Selector_getProperty_prototype(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { + return Function_; +} + +static JSValueRef Pointer_callAsFunction_valueOf(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { + CYTry { + Pointer_privateData *data(reinterpret_cast(JSObjectGetPrivate(_this))); + return CYCastJSValue(context, reinterpret_cast(data->value_)); + } CYCatch +} + +static JSValueRef Pointer_callAsFunction_toJSON(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { + return Pointer_callAsFunction_valueOf(context, object, _this, count, arguments, exception); +} + +static JSValueRef Pointer_callAsFunction_toCYON(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { + CYTry { + Pointer_privateData *data(reinterpret_cast(JSObjectGetPrivate(_this))); + char string[32]; + sprintf(string, "%p", data->value_); + return CYCastJSValue(context, string); + } CYCatch +} + +static JSValueRef Instance_callAsFunction_toCYON(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { + CYTry { + Instance_privateData *data(reinterpret_cast(JSObjectGetPrivate(_this))); + CYPoolTry { + return CYCastJSValue(context, CYJSString([data->GetValue() cy$toCYON])); + } CYPoolCatch(NULL) + } CYCatch +} + +static JSValueRef Instance_callAsFunction_toJSON(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { + CYTry { + Instance_privateData *data(reinterpret_cast(JSObjectGetPrivate(_this))); + CYPoolTry { + NSString *key(count == 0 ? nil : CYCastNSString(NULL, CYJSString(context, arguments[0]))); + return CYCastJSValue(context, CYJSString([data->GetValue() cy$toJSON:key])); + } CYPoolCatch(NULL) + } CYCatch +} + +static JSValueRef Instance_callAsFunction_toString(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { + CYTry { + Instance_privateData *data(reinterpret_cast(JSObjectGetPrivate(_this))); + CYPoolTry { + return CYCastJSValue(context, CYJSString([data->GetValue() description])); + } CYPoolCatch(NULL) + } CYCatch +} + +static JSValueRef Selector_callAsFunction_toString(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { + CYTry { + Selector_privateData *data(reinterpret_cast(JSObjectGetPrivate(_this))); + return CYCastJSValue(context, sel_getName(data->GetValue())); + } CYCatch +} + +static JSValueRef Selector_callAsFunction_toJSON(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { + return Selector_callAsFunction_toString(context, object, _this, count, arguments, exception); +} + +static JSValueRef Selector_callAsFunction_toCYON(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { + CYTry { + Selector_privateData *data(reinterpret_cast(JSObjectGetPrivate(_this))); + const char *name(sel_getName(data->GetValue())); + CYPoolTry { + return CYCastJSValue(context, CYJSString([NSString stringWithFormat:@"@selector(%s)", name])); + } CYPoolCatch(NULL) + } CYCatch +} + +static JSValueRef Selector_callAsFunction_type(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { + CYTry { + if (count != 2) + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"incorrect number of arguments to Selector.type" userInfo:nil]; + CYPool pool; + Selector_privateData *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_ objectAtIndex:1] objectForKey:CYCastNSString(pool, sel_getName(sel))]) + return CYCastJSValue(context, CYJSString(type)); + else + return CYJSNull(context); + } CYCatch } static JSStaticValue Pointer_staticValues[2] = { @@ -916,7 +1933,37 @@ static JSStaticValue Pointer_staticValues[2] = { {NULL, NULL, NULL, 0} }; +static JSStaticFunction Pointer_staticFunctions[4] = { + {"toCYON", &Pointer_callAsFunction_toCYON, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"toJSON", &Pointer_callAsFunction_toJSON, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"valueOf", &Pointer_callAsFunction_valueOf, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {NULL, NULL, 0} +}; + +/*static JSStaticValue Selector_staticValues[2] = { + {"prototype", &Selector_getProperty_prototype, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete}, + {NULL, NULL, NULL, 0} +};*/ + +static JSStaticFunction Instance_staticFunctions[4] = { + {"toCYON", &Instance_callAsFunction_toCYON, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"toJSON", &Instance_callAsFunction_toJSON, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"toString", &Instance_callAsFunction_toString, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {NULL, NULL, 0} +}; + +static JSStaticFunction Selector_staticFunctions[5] = { + {"toCYON", &Selector_callAsFunction_toCYON, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"toJSON", &Selector_callAsFunction_toJSON, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"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), + size_(0), filename_(filename), source_(NULL) { @@ -927,92 +1974,116 @@ CYDriver::~CYDriver() { ScannerDestroy(); } -extern int cydebug; +void cy::parser::error(const cy::parser::location_type &location, const std::string &message) { + CYDriver::Error error; + error.location_ = location; + error.message_ = message; + driver.errors_.push_back(error); +} -void cy::parser::error(const cy::parser::location_type &loc, const std::string &msg) { - std::cerr << loc << ": " << msg << std::endl; +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); } -void CYConsole(FILE *fin, FILE *fout, FILE *ferr) { - cydebug = 1; - CYDriver driver(""); - cy::parser parser(driver); - if (parser.parse() == 0) - driver.source_->Part(std::cout); +JSObjectRef CYGetGlobalObject(JSContextRef context) { + return JSContextGetGlobalObject(context); } MSInitialize { _pooled apr_initialize(); - NSCFBoolean_ = objc_getClass("NSCFBoolean"); - - pid_t pid(getpid()); - - struct sockaddr_in address; - address.sin_len = sizeof(address); - address.sin_family = AF_INET; - address.sin_addr.s_addr = INADDR_ANY; - address.sin_port = htons(10000 + pid); + Bridge_ = [[NSMutableArray arrayWithContentsOfFile:@"/usr/lib/libcycript.plist"] retain]; - CFDataRef data(CFDataCreate(kCFAllocatorDefault, reinterpret_cast(&address), sizeof(address))); - - CFSocketSignature signature; - signature.protocolFamily = AF_INET; - signature.socketType = SOCK_STREAM; - signature.protocol = IPPROTO_TCP; - signature.address = data; - - CFSocketRef socket(CFSocketCreateWithSocketSignature(kCFAllocatorDefault, &signature, kCFSocketAcceptCallBack, &OnAccept, NULL)); - CFRunLoopAddSource(CFRunLoopGetCurrent(), CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket, 0), kCFRunLoopDefaultMode); + NSCFBoolean_ = objc_getClass("NSCFBoolean"); JSClassDefinition definition; definition = kJSClassDefinitionEmpty; definition.className = "Pointer"; definition.staticValues = Pointer_staticValues; - definition.finalize = &Pointer_finalize; + definition.staticFunctions = Pointer_staticFunctions; + definition.finalize = &CYData::Finalize; Pointer_ = JSClassCreate(&definition); definition = kJSClassDefinitionEmpty; definition.className = "Functor"; - definition.parentClass = Pointer_; - definition.callAsFunction = &ffi_callAsFunction; + definition.staticValues = Pointer_staticValues; + definition.staticFunctions = Pointer_staticFunctions; + definition.callAsFunction = &Functor_callAsFunction; + definition.finalize = &CYData::Finalize; Functor_ = JSClassCreate(&definition); + definition = kJSClassDefinitionEmpty; + definition.className = "Struct"; + definition.getProperty = &Struct_getProperty; + definition.setProperty = &Struct_setProperty; + definition.getPropertyNames = &Struct_getPropertyNames; + definition.finalize = &CYData::Finalize; + Struct_ = JSClassCreate(&definition); + definition = kJSClassDefinitionEmpty; definition.className = "Selector"; - definition.parentClass = Pointer_; + definition.staticValues = Pointer_staticValues; + //definition.staticValues = Selector_staticValues; + definition.staticFunctions = Selector_staticFunctions; + definition.callAsFunction = &Selector_callAsFunction; + definition.finalize = &CYData::Finalize; Selector_ = JSClassCreate(&definition); definition = kJSClassDefinitionEmpty; - definition.className = "Instance_"; + definition.className = "Instance"; + definition.staticValues = Pointer_staticValues; + definition.staticFunctions = Instance_staticFunctions; definition.getProperty = &Instance_getProperty; + definition.setProperty = &Instance_setProperty; + definition.deleteProperty = &Instance_deleteProperty; definition.callAsConstructor = &Instance_callAsConstructor; - definition.finalize = &Instance_finalize; + definition.finalize = &CYData::Finalize; Instance_ = JSClassCreate(&definition); definition = kJSClassDefinitionEmpty; - definition.getProperty = &Global_getProperty; + definition.className = "Runtime"; + definition.getProperty = &Runtime_getProperty; + Runtime_ = 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)); + JSObjectRef global(CYGetGlobalObject(context)); - CYSetProperty(context, global, "ffi", JSObjectMakeConstructor(context, Functor_, &ffi)); + JSObjectSetPrototype(context, global, JSObjectMake(context, Runtime_, NULL)); + CYSetProperty(context, global, CYJSString("ObjectiveC"), JSObjectMake(context, Runtime_, NULL)); - CYSetProperty(context, global, "objc_msgSend", JSObjectMakeFunctionWithCallback(context, CYJSString("objc_msgSend"), &$objc_msgSend)); + CYSetProperty(context, global, CYJSString("Selector"), JSObjectMakeConstructor(context, Selector_, &Selector_new)); + CYSetProperty(context, global, CYJSString("Functor"), JSObjectMakeConstructor(context, Functor_, &Functor_new)); - Bridge_ = [[NSMutableDictionary dictionaryWithContentsOfFile:@"/usr/lib/libcycript.plist"] retain]; + 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)); - name_ = JSStringCreateWithUTF8CString("name"); - message_ = JSStringCreateWithUTF8CString("message"); length_ = JSStringCreateWithUTF8CString("length"); + message_ = JSStringCreateWithUTF8CString("message"); + name_ = JSStringCreateWithUTF8CString("name"); + toCYON_ = JSStringCreateWithUTF8CString("toCYON"); + toJSON_ = JSStringCreateWithUTF8CString("toJSON"); - 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"))); }