1 /* Cyrker - Remove Execution Server and Disassembler
2 * Copyright (C) 2009 Jay Freeman (saurik)
5 /* Modified BSD License {{{ */
7 * Redistribution and use in source and binary
8 * forms, with or without modification, are permitted
9 * provided that the following conditions are met:
11 * 1. Redistributions of source code must retain the
12 * above copyright notice, this list of conditions
13 * and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the
15 * above copyright notice, this list of conditions
16 * and the following disclaimer in the documentation
17 * and/or other materials provided with the
19 * 3. The name of the author may not be used to endorse
20 * or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS''
24 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
25 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
26 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
28 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
29 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
30 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
33 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
34 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
35 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
36 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42 #include <substrate.h>
43 #include "cycript.hpp"
45 #include "sig/parse.hpp"
46 #include "sig/ffi_type.hpp"
48 #include "Pooling.hpp"
53 #include <CoreFoundation/CoreFoundation.h>
54 #include <CoreFoundation/CFLogUtilities.h>
56 #include <CFNetwork/CFNetwork.h>
58 #include <WebKit/WebScriptObject.h>
60 #include <sys/types.h>
61 #include <sys/socket.h>
62 #include <netinet/in.h>
66 #include <ext/stdio_filebuf.h>
71 #include "Cycript.tab.hh"
76 #define _assert(test) do { \
78 @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:[NSString stringWithFormat:@"_assert(%s):%s(%u):%s", #test, __FILE__, __LINE__, __FUNCTION__] userInfo:nil]; \
81 #define _trace() do { \
82 CFLog(kCFLogLevelNotice, CFSTR("_trace():%u"), __LINE__); \
85 static JSGlobalContextRef Context_;
86 static JSObjectRef System_;
88 static JSClassRef Functor_;
89 static JSClassRef Instance_;
90 static JSClassRef Pointer_;
91 static JSClassRef Selector_;
93 static JSObjectRef Array_;
94 static JSObjectRef Function_;
96 static JSStringRef name_;
97 static JSStringRef message_;
98 static JSStringRef length_;
100 static Class NSCFBoolean_;
102 static NSMutableDictionary *Bridge_;
105 CFHTTPMessageRef message_;
114 void *operator new(size_t size) {
116 apr_pool_create(&pool, NULL);
117 void *data(apr_palloc(pool, size));
118 reinterpret_cast<ptrData *>(data)->pool_ = pool;
122 ptrData(void *value) :
131 struct ffiData : ptrData {
132 sig::Signature signature_;
135 ffiData(const char *type, void (*value)()) :
136 ptrData(reinterpret_cast<void *>(value))
138 sig::Parse(pool_, &signature_, type);
139 sig::sig_ffi_cif(pool_, &sig::ObjectiveC, &signature_, &cif_);
143 struct ffoData : ffiData {
144 JSContextRef context_;
145 JSObjectRef function_;
147 ffoData(const char *type) :
153 struct selData : ptrData {
159 SEL GetValue() const {
160 return reinterpret_cast<SEL>(value_);
164 struct jocData : ptrData {
167 jocData(id value, bool transient) :
174 [GetValue() release];
177 id GetValue() const {
178 return reinterpret_cast<id>(value_);
182 JSObjectRef CYMakeInstance(JSContextRef context, id object, bool transient = true) {
184 object = [object retain];
185 jocData *data(new jocData(object, transient));
186 return JSObjectMake(context, Instance_, data);
189 const char *CYPoolCString(apr_pool_t *pool, NSString *value) {
191 return [value UTF8String];
193 size_t size([value maximumLengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1);
194 char *string(new(pool) char[size]);
195 if (![value getCString:string maxLength:size encoding:NSUTF8StringEncoding])
196 @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"[NSString getCString:maxLength:encoding:] == NO" userInfo:nil];
201 JSValueRef CYCastJSValue(JSContextRef context, bool value) {
202 return JSValueMakeBoolean(context, value);
205 JSValueRef CYCastJSValue(JSContextRef context, double value) {
206 return JSValueMakeNumber(context, value);
209 #define CYCastJSValue_(Type_) \
210 JSValueRef CYCastJSValue(JSContextRef context, Type_ value) { \
211 return JSValueMakeNumber(context, static_cast<double>(value)); \
215 CYCastJSValue_(unsigned int)
216 CYCastJSValue_(long int)
217 CYCastJSValue_(long unsigned int)
218 CYCastJSValue_(long long int)
219 CYCastJSValue_(long long unsigned int)
221 JSValueRef CYJSUndefined(JSContextRef context) {
222 return JSValueMakeUndefined(context);
225 @interface NSMethodSignature (Cycript)
226 - (NSString *) _typeString;
229 @interface NSObject (Cycript)
230 - (bool) cy$isUndefined;
231 - (NSString *) cy$toJSON;
232 - (JSValueRef) cy$JSValueInContext:(JSContextRef)context;
235 @interface NSString (Cycript)
236 - (void *) cy$symbol;
239 @interface NSNumber (Cycript)
240 - (void *) cy$symbol;
243 @implementation NSObject (Cycript)
245 - (bool) cy$isUndefined {
249 - (NSString *) cy$toJSON {
250 return [self description];
253 - (JSValueRef) cy$JSValueInContext:(JSContextRef)context {
254 return CYMakeInstance(context, self);
259 @implementation WebUndefined (Cycript)
261 - (bool) cy$isUndefined {
265 - (NSString *) cy$toJSON {
269 - (JSValueRef) cy$JSValueInContext:(JSContextRef)context {
270 return CYJSUndefined(context);
275 @implementation NSNull (Cycript)
277 - (NSString *) cy$toJSON {
283 @implementation NSArray (Cycript)
285 - (NSString *) cy$toJSON {
286 NSMutableString *json([[[NSMutableString alloc] init] autorelease]);
287 [json appendString:@"["];
290 for (id object in self) {
292 [json appendString:@","];
295 if (![object cy$isUndefined])
296 [json appendString:[object cy$toJSON]];
298 [json appendString:@","];
303 [json appendString:@"]"];
309 @implementation NSDictionary (Cycript)
311 - (NSString *) cy$toJSON {
312 NSMutableString *json([[[NSMutableString alloc] init] autorelease]);
313 [json appendString:@"({"];
316 for (id key in self) {
318 [json appendString:@","];
321 [json appendString:[key cy$toJSON]];
322 [json appendString:@":"];
323 NSObject *object([self objectForKey:key]);
324 [json appendString:[object cy$toJSON]];
327 [json appendString:@"})"];
333 @implementation NSNumber (Cycript)
335 - (NSString *) cy$toJSON {
336 return [self class] != NSCFBoolean_ ? [self stringValue] : [self boolValue] ? @"true" : @"false";
339 - (JSValueRef) cy$JSValueInContext:(JSContextRef)context {
340 return [self class] != NSCFBoolean_ ? CYCastJSValue(context, [self doubleValue]) : CYCastJSValue(context, [self boolValue]);
343 - (void *) cy$symbol {
344 return [self pointerValue];
349 @implementation NSString (Cycript)
351 - (NSString *) cy$toJSON {
352 CFMutableStringRef json(CFStringCreateMutableCopy(kCFAllocatorDefault, 0, (CFStringRef) self));
354 CFStringFindAndReplace(json, CFSTR("\\"), CFSTR("\\\\"), CFRangeMake(0, CFStringGetLength(json)), 0);
355 CFStringFindAndReplace(json, CFSTR("\""), CFSTR("\\\""), CFRangeMake(0, CFStringGetLength(json)), 0);
356 CFStringFindAndReplace(json, CFSTR("\t"), CFSTR("\\t"), CFRangeMake(0, CFStringGetLength(json)), 0);
357 CFStringFindAndReplace(json, CFSTR("\r"), CFSTR("\\r"), CFRangeMake(0, CFStringGetLength(json)), 0);
358 CFStringFindAndReplace(json, CFSTR("\n"), CFSTR("\\n"), CFRangeMake(0, CFStringGetLength(json)), 0);
360 CFStringInsert(json, 0, CFSTR("\""));
361 CFStringAppend(json, CFSTR("\""));
363 return [reinterpret_cast<const NSString *>(json) autorelease];
366 - (void *) cy$symbol {
368 return dlsym(RTLD_DEFAULT, CYPoolCString(pool, self));
373 @interface CYJSObject : NSDictionary {
375 JSContextRef context_;
378 - (id) initWithJSObject:(JSObjectRef)object inContext:(JSContextRef)context;
380 - (NSUInteger) count;
381 - (id) objectForKey:(id)key;
382 - (NSEnumerator *) keyEnumerator;
383 - (void) setObject:(id)object forKey:(id)key;
384 - (void) removeObjectForKey:(id)key;
388 @interface CYJSArray : NSArray {
390 JSContextRef context_;
393 - (id) initWithJSObject:(JSObjectRef)object inContext:(JSContextRef)context;
395 - (NSUInteger) count;
396 - (id) objectAtIndex:(NSUInteger)index;
400 CYRange WordStartRange_(0x1000000000LLU,0x7fffffe87fffffeLLU); // A-Za-z_$
401 CYRange WordEndRange_(0x3ff001000000000LLU,0x7fffffe87fffffeLLU); // A-Za-z_$0-9
403 JSGlobalContextRef CYGetJSContext() {
408 @catch (id error) { \
409 NSLog(@"e:%@", error); \
410 CYThrow(context, error, exception); \
414 void CYThrow(JSContextRef context, JSValueRef value);
416 apr_status_t CYPoolRelease_(void *data) {
417 id object(reinterpret_cast<id>(data));
422 id CYPoolRelease(apr_pool_t *pool, id object) {
424 return [object autorelease];
426 apr_pool_cleanup_register(pool, object, &CYPoolRelease_, &apr_pool_cleanup_null);
431 id CYCastNSObject(apr_pool_t *pool, JSContextRef context, JSObjectRef object) {
432 if (JSValueIsObjectOfClass(context, object, Instance_)) {
433 jocData *data(reinterpret_cast<jocData *>(JSObjectGetPrivate(object)));
434 return data->GetValue();
437 JSValueRef exception(NULL);
438 bool array(JSValueIsInstanceOfConstructor(context, object, Array_, &exception));
439 CYThrow(context, exception);
440 id value(array ? [CYJSArray alloc] : [CYJSObject alloc]);
441 return CYPoolRelease(pool, [value initWithJSObject:object inContext:context]);
444 JSStringRef CYCopyJSString(id value) {
445 return value == NULL ? NULL : JSStringCreateWithCFString(reinterpret_cast<CFStringRef>([value description]));
448 JSStringRef CYCopyJSString(const char *value) {
449 return value == NULL ? NULL : JSStringCreateWithUTF8CString(value);
452 JSStringRef CYCopyJSString(JSStringRef value) {
453 return value == NULL ? NULL : JSStringRetain(value);
456 JSStringRef CYCopyJSString(JSContextRef context, JSValueRef value) {
457 if (JSValueIsNull(context, value))
459 JSValueRef exception(NULL);
460 JSStringRef string(JSValueToStringCopy(context, value, &exception));
461 CYThrow(context, exception);
470 JSStringRelease(string_);
474 CYJSString(const CYJSString &rhs) :
475 string_(CYCopyJSString(rhs.string_))
479 template <typename Arg0_>
480 CYJSString(Arg0_ arg0) :
481 string_(CYCopyJSString(arg0))
485 template <typename Arg0_, typename Arg1_>
486 CYJSString(Arg0_ arg0, Arg1_ arg1) :
487 string_(CYCopyJSString(arg0, arg1))
491 CYJSString &operator =(const CYJSString &rhs) {
493 string_ = CYCopyJSString(rhs.string_);
506 operator JSStringRef() const {
511 CFStringRef CYCopyCFString(JSStringRef value) {
512 return JSStringCopyCFString(kCFAllocatorDefault, value);
515 CFStringRef CYCopyCFString(JSContextRef context, JSValueRef value) {
516 return CYCopyCFString(CYJSString(context, value));
519 double CYCastDouble(JSContextRef context, JSValueRef value) {
520 JSValueRef exception(NULL);
521 double number(JSValueToNumber(context, value, &exception));
522 CYThrow(context, exception);
526 CFNumberRef CYCopyCFNumber(JSContextRef context, JSValueRef value) {
527 double number(CYCastDouble(context, value));
528 return CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &number);
531 NSString *CYCastNSString(apr_pool_t *pool, JSStringRef value) {
532 return CYPoolRelease(pool, reinterpret_cast<const NSString *>(CYCopyCFString(value)));
535 bool CYCastBool(JSContextRef context, JSValueRef value) {
536 return JSValueToBoolean(context, value);
539 CFTypeRef CYCFType(apr_pool_t *pool, JSContextRef context, JSValueRef value, bool cast) {
543 switch (JSType type = JSValueGetType(context, value)) {
544 case kJSTypeUndefined:
545 object = [WebUndefined undefined];
554 object = CYCastBool(context, value) ? kCFBooleanTrue : kCFBooleanFalse;
559 object = CYCopyCFNumber(context, value);
564 object = CYCopyCFString(context, value);
569 // XXX: this might could be more efficient
570 object = (CFTypeRef) CYCastNSObject(pool, context, (JSObjectRef) value);
575 @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:[NSString stringWithFormat:@"JSValueGetType() == 0x%x", type] userInfo:nil];
582 return CYPoolRelease(pool, (id) object);
584 return CFRetain(object);
587 CFTypeRef CYCastCFType(apr_pool_t *pool, JSContextRef context, JSValueRef value) {
588 return CYCFType(pool, context, value, true);
591 CFTypeRef CYCopyCFType(apr_pool_t *pool, JSContextRef context, JSValueRef value) {
592 return CYCFType(pool, context, value, false);
595 NSArray *CYCastNSArray(JSPropertyNameArrayRef names) {
597 size_t size(JSPropertyNameArrayGetCount(names));
598 NSMutableArray *array([NSMutableArray arrayWithCapacity:size]);
599 for (size_t index(0); index != size; ++index)
600 [array addObject:CYCastNSString(pool, JSPropertyNameArrayGetNameAtIndex(names, index))];
604 id CYCastNSObject(apr_pool_t *pool, JSContextRef context, JSValueRef value) {
605 return reinterpret_cast<const NSObject *>(CYCastCFType(pool, context, value));
608 void CYThrow(JSContextRef context, JSValueRef value) {
611 @throw CYCastNSObject(NULL, context, value);
614 JSValueRef CYJSNull(JSContextRef context) {
615 return JSValueMakeNull(context);
618 JSValueRef CYCastJSValue(JSContextRef context, JSStringRef value) {
619 return value == NULL ? CYJSNull(context) : JSValueMakeString(context, value);
622 JSValueRef CYCastJSValue(JSContextRef context, const char *value) {
623 return CYCastJSValue(context, CYJSString(value));
626 JSValueRef CYCastJSValue(JSContextRef context, id value) {
627 return value == nil ? CYJSNull(context) : [value cy$JSValueInContext:context];
630 JSObjectRef CYCastJSObject(JSContextRef context, JSValueRef value) {
631 JSValueRef exception(NULL);
632 JSObjectRef object(JSValueToObject(context, value, &exception));
633 CYThrow(context, exception);
637 JSValueRef CYGetProperty(JSContextRef context, JSObjectRef object, JSStringRef name) {
638 JSValueRef exception(NULL);
639 JSValueRef value(JSObjectGetProperty(context, object, name, &exception));
640 CYThrow(context, exception);
644 void CYSetProperty(JSContextRef context, JSObjectRef object, JSStringRef name, JSValueRef value) {
645 JSValueRef exception(NULL);
646 JSObjectSetProperty(context, object, name, value, kJSPropertyAttributeNone, &exception);
647 CYThrow(context, exception);
650 void CYThrow(JSContextRef context, id error, JSValueRef *exception) {
651 *exception = CYCastJSValue(context, error);
654 @implementation CYJSObject
656 - (id) initWithJSObject:(JSObjectRef)object inContext:(JSContextRef)context {
657 if ((self = [super init]) != nil) {
663 - (NSUInteger) count {
664 JSPropertyNameArrayRef names(JSObjectCopyPropertyNames(context_, object_));
665 size_t size(JSPropertyNameArrayGetCount(names));
666 JSPropertyNameArrayRelease(names);
670 - (id) objectForKey:(id)key {
671 return CYCastNSObject(NULL, context_, CYGetProperty(context_, object_, CYJSString(key))) ?: [NSNull null];
674 - (NSEnumerator *) keyEnumerator {
675 JSPropertyNameArrayRef names(JSObjectCopyPropertyNames(context_, object_));
676 NSEnumerator *enumerator([CYCastNSArray(names) objectEnumerator]);
677 JSPropertyNameArrayRelease(names);
681 - (void) setObject:(id)object forKey:(id)key {
682 CYSetProperty(context_, object_, CYJSString(key), CYCastJSValue(context_, object));
685 - (void) removeObjectForKey:(id)key {
686 JSValueRef exception(NULL);
687 // XXX: this returns a bool... throw exception, or ignore?
688 JSObjectDeleteProperty(context_, object_, CYJSString(key), &exception);
689 CYThrow(context_, exception);
694 @implementation CYJSArray
696 - (id) initWithJSObject:(JSObjectRef)object inContext:(JSContextRef)context {
697 if ((self = [super init]) != nil) {
703 - (NSUInteger) count {
704 return CYCastDouble(context_, CYGetProperty(context_, object_, length_));
707 - (id) objectAtIndex:(NSUInteger)index {
708 JSValueRef exception(NULL);
709 JSValueRef value(JSObjectGetPropertyAtIndex(context_, object_, index, &exception));
710 CYThrow(context_, exception);
711 return CYCastNSObject(NULL, context_, value) ?: [NSNull null];
716 CFStringRef CYCopyJSONString(JSContextRef context, JSValueRef value) { _pooled
717 id object(CYCastNSObject(NULL, context, value));
718 return reinterpret_cast<CFStringRef>([(object == nil ? @"null" : [object cy$toJSON]) retain]);
721 const char *CYPoolJSONString(apr_pool_t *pool, JSContextRef context, JSValueRef value) {
722 NSString *json((NSString *) CYCopyJSONString(context, value));
723 const char *string(CYPoolCString(pool, json));
728 static void OnData(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *value, void *info) {
730 case kCFSocketDataCallBack:
731 CFDataRef data(reinterpret_cast<CFDataRef>(value));
732 Client *client(reinterpret_cast<Client *>(info));
734 if (client->message_ == NULL)
735 client->message_ = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, TRUE);
737 if (!CFHTTPMessageAppendBytes(client->message_, CFDataGetBytePtr(data), CFDataGetLength(data)))
738 CFLog(kCFLogLevelError, CFSTR("CFHTTPMessageAppendBytes()"));
739 else if (CFHTTPMessageIsHeaderComplete(client->message_)) {
740 CFURLRef url(CFHTTPMessageCopyRequestURL(client->message_));
742 CFStringRef path(CFURLCopyStrictPath(url, &absolute));
743 CFRelease(client->message_);
745 CFStringRef code(CFURLCreateStringByReplacingPercentEscapes(kCFAllocatorDefault, path, CFSTR("")));
748 JSStringRef script(JSStringCreateWithCFString(code));
751 JSValueRef result(JSEvaluateScript(CYGetJSContext(), script, NULL, NULL, 0, NULL));
752 JSStringRelease(script);
754 CFHTTPMessageRef response(CFHTTPMessageCreateResponse(kCFAllocatorDefault, 200, NULL, kCFHTTPVersion1_1));
755 CFHTTPMessageSetHeaderFieldValue(response, CFSTR("Content-Type"), CFSTR("application/json; charset=utf-8"));
757 CFStringRef json(CYCopyJSONString(CYGetJSContext(), result));
758 CFDataRef body(CFStringCreateExternalRepresentation(kCFAllocatorDefault, json, kCFStringEncodingUTF8, NULL));
761 CFStringRef length(CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%u"), CFDataGetLength(body)));
762 CFHTTPMessageSetHeaderFieldValue(response, CFSTR("Content-Length"), length);
765 CFHTTPMessageSetBody(response, body);
768 CFDataRef serialized(CFHTTPMessageCopySerializedMessage(response));
771 CFSocketSendData(socket, NULL, serialized, 0);
772 CFRelease(serialized);
780 static void OnAccept(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *value, void *info) {
782 case kCFSocketAcceptCallBack:
783 Client *client(new Client());
785 client->message_ = NULL;
787 CFSocketContext context;
789 context.info = client;
790 context.retain = NULL;
791 context.release = NULL;
792 context.copyDescription = NULL;
794 client->socket_ = CFSocketCreateWithNative(kCFAllocatorDefault, *reinterpret_cast<const CFSocketNativeHandle *>(value), kCFSocketDataCallBack, &OnData, &context);
796 CFRunLoopAddSource(CFRunLoopGetCurrent(), CFSocketCreateRunLoopSource(kCFAllocatorDefault, client->socket_, 0), kCFRunLoopDefaultMode);
801 static JSValueRef Instance_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) {
804 NSString *name(CYCastNSString(pool, property));
805 NSLog(@"get:%@", name);
810 static bool Instance_setProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef *exception) {
813 NSString *name(CYCastNSString(pool, property));
814 NSLog(@"set:%@", name);
819 static bool Instance_deleteProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) {
822 NSString *name(CYCastNSString(pool, property));
823 NSLog(@"delete:%@", name);
828 static JSObjectRef Instance_callAsConstructor(JSContextRef context, JSObjectRef object, size_t count, const JSValueRef arguments[], JSValueRef *exception) {
830 jocData *data(reinterpret_cast<jocData *>(JSObjectGetPrivate(object)));
831 return CYMakeInstance(context, [data->GetValue() alloc]);
835 JSObjectRef CYMakeSelector(JSContextRef context, SEL sel) {
836 selData *data(new selData(sel));
837 return JSObjectMake(context, Selector_, data);
840 JSObjectRef CYMakePointer(JSContextRef context, void *pointer) {
841 ptrData *data(new ptrData(pointer));
842 return JSObjectMake(context, Pointer_, data);
845 static void Pointer_finalize(JSObjectRef object) {
846 ptrData *data(reinterpret_cast<ptrData *>(JSObjectGetPrivate(object)));
848 apr_pool_destroy(data->pool_);
851 JSObjectRef CYMakeFunctor(JSContextRef context, void (*function)(), const char *type) {
852 ffiData *data(new ffiData(type, function));
853 return JSObjectMake(context, Functor_, data);
856 const char *CYPoolCString(apr_pool_t *pool, JSStringRef value) {
858 return [CYCastNSString(NULL, value) UTF8String];
860 size_t size(JSStringGetMaximumUTF8CStringSize(value));
861 char *string(new(pool) char[size]);
862 JSStringGetUTF8CString(value, string, size);
867 const char *CYPoolCString(apr_pool_t *pool, JSContextRef context, JSValueRef value) {
868 if (JSValueIsNull(context, value))
870 return CYPoolCString(pool, CYJSString(context, value));
873 // XXX: this macro is unhygenic
874 #define CYCastCString(context, value) ({ \
878 else if (JSStringRef string = CYCopyJSString(context, value)) { \
879 size_t size(JSStringGetMaximumUTF8CStringSize(string)); \
880 utf8 = reinterpret_cast<char *>(alloca(size)); \
881 JSStringGetUTF8CString(string, utf8, size); \
882 JSStringRelease(string); \
888 SEL CYCastSEL(JSContextRef context, JSValueRef value) {
889 if (JSValueIsNull(context, value))
891 else if (JSValueIsObjectOfClass(context, value, Selector_)) {
892 selData *data(reinterpret_cast<selData *>(JSObjectGetPrivate((JSObjectRef) value)));
893 return reinterpret_cast<SEL>(data->value_);
895 return sel_registerName(CYCastCString(context, value));
898 void *CYCastPointer_(JSContextRef context, JSValueRef value) {
899 switch (JSValueGetType(context, value)) {
903 return dlsym(RTLD_DEFAULT, CYCastCString(context, value));
905 if (JSValueIsObjectOfClass(context, value, Pointer_)) {
906 ptrData *data(reinterpret_cast<ptrData *>(JSObjectGetPrivate((JSObjectRef) value)));
910 return reinterpret_cast<void *>(static_cast<uintptr_t>(CYCastDouble(context, value)));
914 template <typename Type_>
915 _finline Type_ CYCastPointer(JSContextRef context, JSValueRef value) {
916 return reinterpret_cast<Type_>(CYCastPointer_(context, value));
919 void CYPoolFFI(apr_pool_t *pool, JSContextRef context, sig::Type *type, void *data, JSValueRef value) {
920 switch (type->primitive) {
922 *reinterpret_cast<bool *>(data) = JSValueToBoolean(context, value);
925 #define CYPoolFFI_(primitive, native) \
926 case sig::primitive ## _P: \
927 *reinterpret_cast<native *>(data) = CYCastDouble(context, value); \
930 CYPoolFFI_(uchar, unsigned char)
931 CYPoolFFI_(char, char)
932 CYPoolFFI_(ushort, unsigned short)
933 CYPoolFFI_(short, short)
934 CYPoolFFI_(ulong, unsigned long)
935 CYPoolFFI_(long, long)
936 CYPoolFFI_(uint, unsigned int)
938 CYPoolFFI_(ulonglong, unsigned long long)
939 CYPoolFFI_(longlong, long long)
940 CYPoolFFI_(float, float)
941 CYPoolFFI_(double, double)
944 case sig::typename_P:
945 *reinterpret_cast<id *>(data) = CYCastNSObject(pool, context, value);
948 case sig::selector_P:
949 *reinterpret_cast<SEL *>(data) = CYCastSEL(context, value);
953 *reinterpret_cast<void **>(data) = CYCastPointer<void *>(context, value);
957 *reinterpret_cast<const char **>(data) = CYPoolCString(pool, context, value);
967 NSLog(@"CYPoolFFI(%c)\n", type->primitive);
972 JSValueRef CYFromFFI(JSContextRef context, sig::Type *type, void *data) {
975 switch (type->primitive) {
977 value = CYCastJSValue(context, *reinterpret_cast<bool *>(data));
980 #define CYFromFFI_(primitive, native) \
981 case sig::primitive ## _P: \
982 value = CYCastJSValue(context, *reinterpret_cast<native *>(data)); \
985 CYFromFFI_(uchar, unsigned char)
986 CYFromFFI_(char, char)
987 CYFromFFI_(ushort, unsigned short)
988 CYFromFFI_(short, short)
989 CYFromFFI_(ulong, unsigned long)
990 CYFromFFI_(long, long)
991 CYFromFFI_(uint, unsigned int)
993 CYFromFFI_(ulonglong, unsigned long long)
994 CYFromFFI_(longlong, long long)
995 CYFromFFI_(float, float)
996 CYFromFFI_(double, double)
999 value = CYCastJSValue(context, *reinterpret_cast<id *>(data));
1002 case sig::typename_P:
1003 value = CYMakeInstance(context, *reinterpret_cast<Class *>(data));
1006 case sig::selector_P:
1007 if (SEL sel = *reinterpret_cast<SEL *>(data))
1008 value = CYMakeSelector(context, sel);
1012 case sig::pointer_P:
1013 if (void *pointer = *reinterpret_cast<void **>(data))
1014 value = CYMakePointer(context, pointer);
1019 if (char *utf8 = *reinterpret_cast<char **>(data))
1020 value = CYCastJSValue(context, utf8);
1028 value = CYJSUndefined(context);
1032 value = CYJSNull(context);
1036 NSLog(@"CYFromFFI(%c)\n", type->primitive);
1043 static JSValueRef CYCallFunction(JSContextRef context, size_t count, const JSValueRef *arguments, JSValueRef *exception, sig::Signature *signature, ffi_cif *cif, void (*function)()) {
1045 if (count != signature->count - 1)
1046 @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"incorrect number of arguments to ffi function" userInfo:nil];
1049 void *values[count];
1051 for (unsigned index(0); index != count; ++index) {
1052 sig::Element *element(&signature->elements[index + 1]);
1054 values[index] = new(pool) uint8_t[cif->arg_types[index]->size];
1055 CYPoolFFI(pool, context, element->type, values[index], arguments[index]);
1058 uint8_t value[cif->rtype->size];
1059 ffi_call(cif, function, value, values);
1061 return CYFromFFI(context, signature->elements[0].type, value);
1065 void Closure_(ffi_cif *cif, void *result, void **arguments, void *arg) {
1066 NSLog(@"Closure()");
1067 ffoData *data(reinterpret_cast<ffoData *>(arg));
1069 JSContextRef context(data->context_);
1071 size_t count(data->cif_.nargs);
1072 JSValueRef values[count];
1074 for (size_t index(0); index != count; ++index)
1075 values[index] = CYFromFFI(context, data->signature_.elements[1 + index].type, arguments[index]);
1077 JSValueRef exception(NULL);
1078 JSValueRef value(JSObjectCallAsFunction(context, data->function_, NULL, count, values, &exception));
1079 CYThrow(context, exception);
1081 CYPoolFFI(NULL, context, data->signature_.elements[0].type, result, value);
1084 JSObjectRef CYMakeFunctor(JSContextRef context, JSObjectRef function, const char *type) {
1085 // XXX: in case of exceptions this will leak
1086 ffoData *data(new ffoData(type));
1088 ffi_closure *closure;
1089 _syscall(closure = (ffi_closure *) mmap(
1090 NULL, sizeof(ffi_closure),
1091 PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE,
1095 ffi_status status(ffi_prep_closure(closure, &data->cif_, &Closure_, data));
1096 _assert(status == FFI_OK);
1098 _syscall(mprotect(closure, sizeof(*closure), PROT_READ | PROT_EXEC));
1100 data->value_ = closure;
1102 data->context_ = CYGetJSContext();
1103 data->function_ = function;
1105 return JSObjectMake(context, Functor_, data);
1108 static JSValueRef Global_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) {
1111 NSString *name(CYCastNSString(pool, property));
1112 if (Class _class = NSClassFromString(name))
1113 return CYMakeInstance(context, _class);
1114 if (NSMutableArray *entry = [Bridge_ objectForKey:name])
1115 switch ([[entry objectAtIndex:0] intValue]) {
1117 return JSEvaluateScript(CYGetJSContext(), CYJSString([entry objectAtIndex:1]), NULL, NULL, 0, NULL);
1119 return CYMakeFunctor(context, reinterpret_cast<void (*)()>([name cy$symbol]), CYPoolCString(pool, [entry objectAtIndex:1]));
1121 sig::Signature signature;
1122 sig::Parse(pool, &signature, CYPoolCString(pool, [entry objectAtIndex:1]));
1123 return CYFromFFI(context, signature.elements[0].type, [name cy$symbol]);
1129 bool stret(ffi_type *ffi_type) {
1130 return ffi_type->type == FFI_TYPE_STRUCT && (
1131 ffi_type->size > OBJC_MAX_STRUCT_BY_VALUE ||
1132 struct_forward_array[ffi_type->size] != 0
1137 int *_NSGetArgc(void);
1138 char ***_NSGetArgv(void);
1139 int UIApplicationMain(int argc, char *argv[], NSString *principalClassName, NSString *delegateClassName);
1142 static JSValueRef System_print(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) {
1144 NSLog(@"%s", CYCastCString(context, arguments[0]));
1145 return CYJSUndefined(context);
1149 static JSValueRef CYApplicationMain(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) {
1152 NSString *name(CYCastNSObject(pool, context, arguments[0]));
1153 int argc(*_NSGetArgc());
1154 char **argv(*_NSGetArgv());
1155 for (int i(0); i != argc; ++i)
1156 NSLog(@"argv[%i]=%s", i, argv[i]);
1158 return CYCastJSValue(context, UIApplicationMain(argc, argv, name, name));
1162 static JSValueRef $objc_msgSend(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) {
1169 @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"too few arguments to objc_msgSend" userInfo:nil];
1171 id self(CYCastNSObject(pool, context, arguments[0]));
1173 return CYJSNull(context);
1175 SEL _cmd(CYCastSEL(context, arguments[1]));
1177 Class _class(object_getClass(self));
1178 if (Method method = class_getInstanceMethod(_class, _cmd))
1179 type = method_getTypeEncoding(method);
1181 NSMethodSignature *method([self methodSignatureForSelector:_cmd]);
1183 @throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"unrecognized selector %s sent to object %p", sel_getName(_cmd), self] userInfo:nil];
1184 type = CYPoolCString(pool, [method _typeString]);
1188 sig::Signature signature;
1189 sig::Parse(pool, &signature, type);
1192 sig::sig_ffi_cif(pool, &sig::ObjectiveC, &signature, &cif);
1194 void (*function)() = stret(cif.rtype) ? reinterpret_cast<void (*)()>(&objc_msgSend_stret) : reinterpret_cast<void (*)()>(&objc_msgSend);
1195 return CYCallFunction(context, count, arguments, exception, &signature, &cif, function);
1198 static JSValueRef Selector_callAsFunction(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) {
1199 JSValueRef setup[count + 2];
1202 memmove(setup + 2, arguments, sizeof(JSValueRef) * count);
1203 return $objc_msgSend(context, NULL, NULL, count + 2, setup, exception);
1206 static JSValueRef Functor_callAsFunction(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) {
1207 ffiData *data(reinterpret_cast<ffiData *>(JSObjectGetPrivate(object)));
1208 return CYCallFunction(context, count, arguments, exception, &data->signature_, &data->cif_, reinterpret_cast<void (*)()>(data->value_));
1211 JSObjectRef Selector_new(JSContextRef context, JSObjectRef object, size_t count, const JSValueRef arguments[], JSValueRef *exception) {
1214 @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"incorrect number of arguments to Selector constructor" userInfo:nil];
1215 const char *name(CYCastCString(context, arguments[0]));
1216 return CYMakeSelector(context, sel_registerName(name));
1220 JSObjectRef Functor_new(JSContextRef context, JSObjectRef object, size_t count, const JSValueRef arguments[], JSValueRef *exception) {
1223 @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"incorrect number of arguments to Functor constructor" userInfo:nil];
1224 const char *type(CYCastCString(context, arguments[1]));
1225 JSValueRef exception(NULL);
1226 if (JSValueIsInstanceOfConstructor(context, arguments[0], Function_, &exception)) {
1227 JSObjectRef function(CYCastJSObject(context, arguments[0]));
1228 return CYMakeFunctor(context, function, type);
1229 } else if (exception != NULL) {
1232 void (*function)()(CYCastPointer<void (*)()>(context, arguments[0]));
1233 return CYMakeFunctor(context, function, type);
1238 JSValueRef Pointer_getProperty_value(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) {
1239 ptrData *data(reinterpret_cast<ptrData *>(JSObjectGetPrivate(object)));
1240 return CYCastJSValue(context, reinterpret_cast<uintptr_t>(data->value_));
1243 JSValueRef Selector_getProperty_prototype(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) {
1247 static JSValueRef Instance_callAsFunction_toString(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { _pooled
1249 jocData *data(reinterpret_cast<jocData *>(JSObjectGetPrivate(_this)));
1250 return CYCastJSValue(context, CYJSString([data->GetValue() description]));
1254 static JSValueRef Selector_callAsFunction_toString(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) {
1256 selData *data(reinterpret_cast<selData *>(JSObjectGetPrivate(_this)));
1257 return CYCastJSValue(context, sel_getName(data->GetValue()));
1261 static JSValueRef Selector_callAsFunction_type(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) {
1264 @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"incorrect number of arguments to Selector.type" userInfo:nil];
1266 selData *data(reinterpret_cast<selData *>(JSObjectGetPrivate(_this)));
1267 Class _class(CYCastNSObject(pool, context, arguments[0]));
1268 bool instance(CYCastBool(context, arguments[1]));
1269 SEL sel(data->GetValue());
1270 if (Method method = (*(instance ? &class_getInstanceMethod : class_getClassMethod))(_class, sel))
1271 return CYCastJSValue(context, method_getTypeEncoding(method));
1272 else if (NSString *type = [Bridge_ objectForKey:CYPoolRelease(pool, [[NSString alloc] initWithFormat:@":%s", sel_getName(sel)])])
1273 return CYCastJSValue(context, CYJSString(type));
1275 return CYJSNull(context);
1279 static JSStaticValue Pointer_staticValues[2] = {
1280 {"value", &Pointer_getProperty_value, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete},
1281 {NULL, NULL, NULL, 0}
1284 /*static JSStaticValue Selector_staticValues[2] = {
1285 {"prototype", &Selector_getProperty_prototype, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete},
1286 {NULL, NULL, NULL, 0}
1289 static JSStaticFunction Instance_staticFunctions[2] = {
1290 {"toString", &Instance_callAsFunction_toString, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete},
1294 static JSStaticFunction Selector_staticFunctions[3] = {
1295 {"toString", &Selector_callAsFunction_toString, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete},
1296 {"type", &Selector_callAsFunction_type, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete},
1300 CYDriver::CYDriver(const std::string &filename) :
1304 filename_(filename),
1310 CYDriver::~CYDriver() {
1314 void cy::parser::error(const cy::parser::location_type &location, const std::string &message) {
1315 CYDriver::Error error;
1316 error.location_ = location;
1317 error.message_ = message;
1318 driver.errors_.push_back(error);
1321 void CYSetArgs(int argc, const char *argv[]) {
1322 JSContextRef context(CYGetJSContext());
1323 JSValueRef args[argc];
1324 for (int i(0); i != argc; ++i)
1325 args[i] = CYCastJSValue(context, argv[i]);
1326 JSValueRef exception(NULL);
1327 JSObjectRef array(JSObjectMakeArray(context, argc, args, &exception));
1328 CYThrow(context, exception);
1329 CYSetProperty(context, System_, CYJSString("args"), array);
1332 MSInitialize { _pooled
1335 NSCFBoolean_ = objc_getClass("NSCFBoolean");
1337 pid_t pid(getpid());
1339 struct sockaddr_in address;
1340 address.sin_len = sizeof(address);
1341 address.sin_family = AF_INET;
1342 address.sin_addr.s_addr = INADDR_ANY;
1343 address.sin_port = htons(10000 + pid);
1345 CFDataRef data(CFDataCreate(kCFAllocatorDefault, reinterpret_cast<UInt8 *>(&address), sizeof(address)));
1347 CFSocketSignature signature;
1348 signature.protocolFamily = AF_INET;
1349 signature.socketType = SOCK_STREAM;
1350 signature.protocol = IPPROTO_TCP;
1351 signature.address = data;
1353 CFSocketRef socket(CFSocketCreateWithSocketSignature(kCFAllocatorDefault, &signature, kCFSocketAcceptCallBack, &OnAccept, NULL));
1354 CFRunLoopAddSource(CFRunLoopGetCurrent(), CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket, 0), kCFRunLoopDefaultMode);
1356 JSClassDefinition definition;
1358 definition = kJSClassDefinitionEmpty;
1359 definition.className = "Pointer";
1360 definition.staticValues = Pointer_staticValues;
1361 definition.finalize = &Pointer_finalize;
1362 Pointer_ = JSClassCreate(&definition);
1364 definition = kJSClassDefinitionEmpty;
1365 definition.className = "Functor";
1366 definition.parentClass = Pointer_;
1367 definition.callAsFunction = &Functor_callAsFunction;
1368 Functor_ = JSClassCreate(&definition);
1370 definition = kJSClassDefinitionEmpty;
1371 definition.className = "Selector";
1372 definition.parentClass = Pointer_;
1373 //definition.staticValues = Selector_staticValues;
1374 definition.staticFunctions = Selector_staticFunctions;
1375 definition.callAsFunction = &Selector_callAsFunction;
1376 Selector_ = JSClassCreate(&definition);
1378 definition = kJSClassDefinitionEmpty;
1379 definition.className = "Instance";
1380 definition.parentClass = Pointer_;
1381 definition.staticFunctions = Instance_staticFunctions;
1382 definition.getProperty = &Instance_getProperty;
1383 definition.setProperty = &Instance_setProperty;
1384 definition.deleteProperty = &Instance_deleteProperty;
1385 definition.callAsConstructor = &Instance_callAsConstructor;
1386 Instance_ = JSClassCreate(&definition);
1388 definition = kJSClassDefinitionEmpty;
1389 definition.getProperty = &Global_getProperty;
1390 JSClassRef Global(JSClassCreate(&definition));
1392 JSGlobalContextRef context(JSGlobalContextCreate(Global));
1395 JSObjectRef global(JSContextGetGlobalObject(context));
1397 CYSetProperty(context, global, CYJSString("Selector"), JSObjectMakeConstructor(context, Selector_, &Selector_new));
1398 CYSetProperty(context, global, CYJSString("Functor"), JSObjectMakeConstructor(context, Functor_, &Functor_new));
1400 CYSetProperty(context, global, CYJSString("CYApplicationMain"), JSObjectMakeFunctionWithCallback(context, CYJSString("CYApplicationMain"), &CYApplicationMain));
1401 CYSetProperty(context, global, CYJSString("objc_msgSend"), JSObjectMakeFunctionWithCallback(context, CYJSString("objc_msgSend"), &$objc_msgSend));
1403 System_ = JSObjectMake(context, NULL, NULL);
1404 CYSetProperty(context, global, CYJSString("system"), System_);
1405 CYSetProperty(context, System_, CYJSString("args"), CYJSNull(context));
1406 CYSetProperty(context, System_, CYJSString("global"), global);
1408 CYSetProperty(context, System_, CYJSString("print"), JSObjectMakeFunctionWithCallback(context, CYJSString("print"), &System_print));
1410 Bridge_ = [[NSMutableDictionary dictionaryWithContentsOfFile:@"/usr/lib/libcycript.plist"] retain];
1412 name_ = JSStringCreateWithUTF8CString("name");
1413 message_ = JSStringCreateWithUTF8CString("message");
1414 length_ = JSStringCreateWithUTF8CString("length");
1416 Array_ = CYCastJSObject(context, CYGetProperty(context, global, CYJSString("Array")));
1417 Function_ = CYCastJSObject(context, CYGetProperty(context, global, CYJSString("Function")));