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>
45 #include "sig/parse.hpp"
46 #include "sig/ffi_type.hpp"
48 #include <apr-1/apr_pools.h>
49 #include <apr-1/apr_strings.h>
53 #include <CoreFoundation/CoreFoundation.h>
54 #include <CoreFoundation/CFLogUtilities.h>
56 #include <CFNetwork/CFNetwork.h>
57 #include <Foundation/Foundation.h>
59 #include <JavaScriptCore/JSBase.h>
60 #include <JavaScriptCore/JSValueRef.h>
61 #include <JavaScriptCore/JSObjectRef.h>
62 #include <JavaScriptCore/JSContextRef.h>
63 #include <JavaScriptCore/JSStringRef.h>
64 #include <JavaScriptCore/JSStringRefCF.h>
66 #include <WebKit/WebScriptObject.h>
68 #include <sys/types.h>
69 #include <sys/socket.h>
70 #include <netinet/in.h>
73 #include <ext/stdio_filebuf.h>
80 #define _assert(test) do { \
82 @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:[NSString stringWithFormat:@"_assert(%s):%s(%u):%s", #test, __FILE__, __LINE__, __FUNCTION__] userInfo:nil]; \
85 #define _trace() do { \
86 CFLog(kCFLogLevelNotice, CFSTR("_trace():%u"), __LINE__); \
89 /* Objective-C Handle<> {{{ */
90 template <typename Type_>
92 typedef _H<Type_> This_;
97 _finline void Retain_() {
102 _finline void Clear_() {
108 _finline _H(const This_ &rhs) :
109 value_(rhs.value_ == nil ? nil : [rhs.value_ retain])
113 _finline _H(Type_ *value = NULL, bool mended = false) :
124 _finline operator Type_ *() const {
128 _finline This_ &operator =(Type_ *value) {
129 if (value_ != value) {
139 /* APR Pool Helpers {{{ */
140 void *operator new(size_t size, apr_pool_t *pool) {
141 return apr_palloc(pool, size);
144 void *operator new [](size_t size, apr_pool_t *pool) {
145 return apr_palloc(pool, size);
154 apr_pool_create(&pool_, NULL);
158 apr_pool_destroy(pool_);
161 operator apr_pool_t *() const {
167 #define _pooled _H<NSAutoreleasePool> _pool([[NSAutoreleasePool alloc] init], true);
169 static JSContextRef Context_;
171 static JSClassRef Functor_;
172 static JSClassRef Instance_;
173 static JSClassRef Pointer_;
174 static JSClassRef Selector_;
176 static JSObjectRef Array_;
178 static JSStringRef name_;
179 static JSStringRef message_;
180 static JSStringRef length_;
182 static Class NSCFBoolean_;
184 static NSMutableDictionary *Bridge_;
187 CFHTTPMessageRef message_;
191 JSObjectRef CYMakeObject(JSContextRef context, id object) {
192 return JSObjectMake(context, Instance_, [object retain]);
195 @interface NSMethodSignature (Cycript)
196 - (NSString *) _typeString;
199 @interface NSObject (Cycript)
200 - (NSString *) cy$toJSON;
201 - (JSValueRef) cy$JSValueInContext:(JSContextRef)context;
204 @interface NSString (Cycript)
205 - (void *) cy$symbol;
208 @interface NSNumber (Cycript)
209 - (void *) cy$symbol;
212 @implementation NSObject (Cycript)
214 - (NSString *) cy$toJSON {
215 return [self description];
218 - (JSValueRef) cy$JSValueInContext:(JSContextRef)context {
219 return CYMakeObject(context, self);
224 @implementation WebUndefined (Cycript)
226 - (NSString *) cy$toJSON {
230 - (JSValueRef) cy$JSValueInContext:(JSContextRef)context {
231 return JSValueMakeUndefined(context);
236 @implementation NSArray (Cycript)
238 - (NSString *) cy$toJSON {
239 NSMutableString *json([[[NSMutableString alloc] init] autorelease]);
240 [json appendString:@"["];
243 for (id object in self) {
245 [json appendString:@","];
248 [json appendString:[object cy$toJSON]];
251 [json appendString:@"]"];
257 @implementation NSDictionary (Cycript)
259 - (NSString *) cy$toJSON {
260 NSMutableString *json([[[NSMutableString alloc] init] autorelease]);
261 [json appendString:@"("];
262 [json appendString:@"{"];
265 for (id key in self) {
267 [json appendString:@","];
270 [json appendString:[key cy$toJSON]];
271 [json appendString:@":"];
272 NSObject *object([self objectForKey:key]);
273 [json appendString:[object cy$toJSON]];
276 [json appendString:@"})"];
282 @implementation NSNumber (Cycript)
284 - (NSString *) cy$toJSON {
285 return [self class] != NSCFBoolean_ ? [self stringValue] : [self boolValue] ? @"true" : @"false";
288 - (JSValueRef) cy$JSValueInContext:(JSContextRef)context {
289 return [self class] != NSCFBoolean_ ? JSValueMakeNumber(context, [self doubleValue]) : JSValueMakeBoolean(context, [self boolValue]);
292 - (void *) cy$symbol {
293 return [self pointerValue];
298 @implementation NSString (Cycript)
300 - (NSString *) cy$toJSON {
301 CFMutableStringRef json(CFStringCreateMutableCopy(kCFAllocatorDefault, 0, (CFStringRef) self));
303 CFStringFindAndReplace(json, CFSTR("\\"), CFSTR("\\\\"), CFRangeMake(0, CFStringGetLength(json)), 0);
304 CFStringFindAndReplace(json, CFSTR("\""), CFSTR("\\\""), CFRangeMake(0, CFStringGetLength(json)), 0);
305 CFStringFindAndReplace(json, CFSTR("\t"), CFSTR("\\t"), CFRangeMake(0, CFStringGetLength(json)), 0);
306 CFStringFindAndReplace(json, CFSTR("\r"), CFSTR("\\r"), CFRangeMake(0, CFStringGetLength(json)), 0);
307 CFStringFindAndReplace(json, CFSTR("\n"), CFSTR("\\n"), CFRangeMake(0, CFStringGetLength(json)), 0);
309 CFStringInsert(json, 0, CFSTR("\""));
310 CFStringAppend(json, CFSTR("\""));
312 return [reinterpret_cast<const NSString *>(json) autorelease];
315 - (void *) cy$symbol {
316 return dlsym(RTLD_DEFAULT, [self UTF8String]);
321 @interface CYJSObject : NSDictionary {
323 JSContextRef context_;
326 - (id) initWithJSObject:(JSObjectRef)object inContext:(JSContextRef)context;
328 - (NSUInteger) count;
329 - (id) objectForKey:(id)key;
330 - (NSEnumerator *) keyEnumerator;
331 - (void) setObject:(id)object forKey:(id)key;
332 - (void) removeObjectForKey:(id)key;
336 @interface CYJSArray : NSArray {
338 JSContextRef context_;
341 - (id) initWithJSObject:(JSObjectRef)object inContext:(JSContextRef)context;
343 - (NSUInteger) count;
344 - (id) objectAtIndex:(NSUInteger)index;
348 JSContextRef JSGetContext() {
353 @catch (id error) { \
354 CYThrow(context, error, exception); \
358 void CYThrow(JSContextRef context, JSValueRef value);
360 id CYCastNSObject(JSContextRef context, JSObjectRef object) {
361 if (JSValueIsObjectOfClass(context, object, Instance_))
362 return reinterpret_cast<id>(JSObjectGetPrivate(object));
363 JSValueRef exception(NULL);
364 bool array(JSValueIsInstanceOfConstructor(context, object, Array_, &exception));
365 CYThrow(context, exception);
367 return [[[CYJSArray alloc] initWithJSObject:object inContext:context] autorelease];
368 return [[[CYJSObject alloc] initWithJSObject:object inContext:context] autorelease];
371 JSStringRef CYCopyJSString(id value) {
372 return JSStringCreateWithCFString(reinterpret_cast<CFStringRef>([value description]));
375 JSStringRef CYCopyJSString(const char *value) {
376 return JSStringCreateWithUTF8CString(value);
379 JSStringRef CYCopyJSString(JSStringRef value) {
380 return JSStringRetain(value);
383 JSStringRef CYCopyJSString(JSContextRef context, JSValueRef value) {
384 JSValueRef exception(NULL);
385 JSStringRef string(JSValueToStringCopy(context, value, &exception));
386 CYThrow(context, exception);
390 // XXX: this is not a safe handle
396 template <typename Arg0_>
397 CYString(Arg0_ arg0) {
398 string_ = CYCopyJSString(arg0);
401 template <typename Arg0_, typename Arg1_>
402 CYString(Arg0_ arg0, Arg1_ arg1) {
403 string_ = CYCopyJSString(arg0, arg1);
407 JSStringRelease(string_);
410 operator JSStringRef() const {
415 CFStringRef CYCopyCFString(JSStringRef value) {
416 return JSStringCopyCFString(kCFAllocatorDefault, value);
419 CFStringRef CYCopyCFString(JSContextRef context, JSValueRef value) {
420 return CYCopyCFString(CYString(context, value));
423 double CYCastDouble(JSContextRef context, JSValueRef value) {
424 JSValueRef exception(NULL);
425 double number(JSValueToNumber(context, value, &exception));
426 CYThrow(context, exception);
430 CFNumberRef CYCopyCFNumber(JSContextRef context, JSValueRef value) {
431 double number(CYCastDouble(context, value));
432 return CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &number);
435 NSString *CYCastNSString(JSStringRef value) {
436 return [reinterpret_cast<const NSString *>(CYCopyCFString(value)) autorelease];
439 CFTypeRef CYCopyCFType(JSContextRef context, JSValueRef value) {
440 switch (JSType type = JSValueGetType(context, value)) {
441 case kJSTypeUndefined:
442 return CFRetain([WebUndefined undefined]);
446 return CFRetain(JSValueToBoolean(context, value) ? kCFBooleanTrue : kCFBooleanFalse);
448 return CYCopyCFNumber(context, value);
450 return CYCopyCFString(context, value);
452 return CFRetain((CFTypeRef) CYCastNSObject(context, (JSObjectRef) value));
454 @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:[NSString stringWithFormat:@"JSValueGetType() == 0x%x", type] userInfo:nil];
458 NSArray *CYCastNSArray(JSPropertyNameArrayRef names) {
459 size_t size(JSPropertyNameArrayGetCount(names));
460 NSMutableArray *array([NSMutableArray arrayWithCapacity:size]);
461 for (size_t index(0); index != size; ++index)
462 [array addObject:CYCastNSString(JSPropertyNameArrayGetNameAtIndex(names, index))];
466 id CYCastNSObject(JSContextRef context, JSValueRef value) {
467 const NSObject *object(reinterpret_cast<const NSObject *>(CYCopyCFType(context, value)));
468 return object == nil ? nil : [object autorelease];
471 void CYThrow(JSContextRef context, JSValueRef value) {
474 @throw CYCastNSObject(context, value);
477 JSValueRef CYCastJSValue(JSContextRef context, id value) {
478 return value == nil ? JSValueMakeNull(context) : [value cy$JSValueInContext:context];
481 void CYThrow(JSContextRef context, id error, JSValueRef *exception) {
482 *exception = CYCastJSValue(context, error);
485 @implementation CYJSObject
487 - (id) initWithJSObject:(JSObjectRef)object inContext:(JSContextRef)context {
488 if ((self = [super init]) != nil) {
494 - (NSUInteger) count {
495 JSPropertyNameArrayRef names(JSObjectCopyPropertyNames(context_, object_));
496 size_t size(JSPropertyNameArrayGetCount(names));
497 JSPropertyNameArrayRelease(names);
501 - (id) objectForKey:(id)key {
502 JSValueRef exception(NULL);
503 JSValueRef value(JSObjectGetProperty(context_, object_, CYString(key), &exception));
504 CYThrow(context_, exception);
505 return CYCastNSObject(context_, value);
508 - (NSEnumerator *) keyEnumerator {
509 JSPropertyNameArrayRef names(JSObjectCopyPropertyNames(context_, object_));
510 NSEnumerator *enumerator([CYCastNSArray(names) objectEnumerator]);
511 JSPropertyNameArrayRelease(names);
515 - (void) setObject:(id)object forKey:(id)key {
516 JSValueRef exception(NULL);
517 JSObjectSetProperty(context_, object_, CYString(key), CYCastJSValue(context_, object), kJSPropertyAttributeNone, &exception);
518 CYThrow(context_, exception);
521 - (void) removeObjectForKey:(id)key {
522 JSValueRef exception(NULL);
523 // XXX: this returns a bool... throw exception, or ignore?
524 JSObjectDeleteProperty(context_, object_, CYString(key), &exception);
525 CYThrow(context_, exception);
530 @implementation CYJSArray
532 - (id) initWithJSObject:(JSObjectRef)object inContext:(JSContextRef)context {
533 if ((self = [super init]) != nil) {
539 - (NSUInteger) count {
540 JSValueRef exception(NULL);
541 JSValueRef value(JSObjectGetProperty(context_, object_, length_, &exception));
542 CYThrow(context_, exception);
543 return CYCastDouble(context_, value);
546 - (id) objectAtIndex:(NSUInteger)index {
547 JSValueRef exception(NULL);
548 JSValueRef value(JSObjectGetPropertyAtIndex(context_, object_, index, &exception));
549 CYThrow(context_, exception);
550 id object(CYCastNSObject(context_, value));
551 return object == nil ? [NSNull null] : object;
556 CFStringRef JSValueToJSONCopy(JSContextRef context, JSValueRef value) {
557 id object(CYCastNSObject(context, value));
558 return reinterpret_cast<CFStringRef>([(object == nil ? @"null" : [object cy$toJSON]) retain]);
561 static void OnData(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *value, void *info) {
563 case kCFSocketDataCallBack:
564 CFDataRef data(reinterpret_cast<CFDataRef>(value));
565 Client *client(reinterpret_cast<Client *>(info));
567 if (client->message_ == NULL)
568 client->message_ = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, TRUE);
570 if (!CFHTTPMessageAppendBytes(client->message_, CFDataGetBytePtr(data), CFDataGetLength(data)))
571 CFLog(kCFLogLevelError, CFSTR("CFHTTPMessageAppendBytes()"));
572 else if (CFHTTPMessageIsHeaderComplete(client->message_)) {
573 CFURLRef url(CFHTTPMessageCopyRequestURL(client->message_));
575 CFStringRef path(CFURLCopyStrictPath(url, &absolute));
576 CFRelease(client->message_);
578 CFStringRef code(CFURLCreateStringByReplacingPercentEscapes(kCFAllocatorDefault, path, CFSTR("")));
581 JSStringRef script(JSStringCreateWithCFString(code));
584 JSValueRef result(JSEvaluateScript(JSGetContext(), script, NULL, NULL, 0, NULL));
585 JSStringRelease(script);
587 CFHTTPMessageRef response(CFHTTPMessageCreateResponse(kCFAllocatorDefault, 200, NULL, kCFHTTPVersion1_1));
588 CFHTTPMessageSetHeaderFieldValue(response, CFSTR("Content-Type"), CFSTR("application/json; charset=utf-8"));
590 CFStringRef json(JSValueToJSONCopy(JSGetContext(), result));
591 CFDataRef body(CFStringCreateExternalRepresentation(kCFAllocatorDefault, json, kCFStringEncodingUTF8, NULL));
594 CFStringRef length(CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%u"), CFDataGetLength(body)));
595 CFHTTPMessageSetHeaderFieldValue(response, CFSTR("Content-Length"), length);
598 CFHTTPMessageSetBody(response, body);
601 CFDataRef serialized(CFHTTPMessageCopySerializedMessage(response));
604 CFSocketSendData(socket, NULL, serialized, 0);
605 CFRelease(serialized);
613 static void OnAccept(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *value, void *info) {
615 case kCFSocketAcceptCallBack:
616 Client *client(new Client());
618 client->message_ = NULL;
620 CFSocketContext context;
622 context.info = client;
623 context.retain = NULL;
624 context.release = NULL;
625 context.copyDescription = NULL;
627 client->socket_ = CFSocketCreateWithNative(kCFAllocatorDefault, *reinterpret_cast<const CFSocketNativeHandle *>(value), kCFSocketDataCallBack, &OnData, &context);
629 CFRunLoopAddSource(CFRunLoopGetCurrent(), CFSocketCreateRunLoopSource(kCFAllocatorDefault, client->socket_, 0), kCFRunLoopDefaultMode);
634 static JSValueRef Instance_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { _pooled
636 NSString *name(CYCastNSString(property));
644 static JSObjectRef Instance_callAsConstructor(JSContextRef context, JSObjectRef object, size_t count, const JSValueRef arguments[], JSValueRef *exception) { _pooled
646 id data(reinterpret_cast<jocData>(JSObjectGetPrivate(object)));
647 return CYMakeObject(context, [[data alloc] autorelease]);
656 void *operator new(size_t size) {
658 apr_pool_create(&pool, NULL);
659 void *data(apr_palloc(pool, size));
660 reinterpret_cast<ptrData *>(data)->pool_ = pool;
664 ptrData(void *value) :
670 struct ffiData : ptrData {
671 sig::Signature signature_;
674 ffiData(void (*value)(), const char *type) :
675 ptrData(reinterpret_cast<void *>(value))
677 sig::Parse(pool_, &signature_, type);
678 sig::sig_ffi_cif(pool_, &sig::ObjectiveC, &signature_, &cif_);
682 struct selData : ptrData {
689 static void Pointer_finalize(JSObjectRef object) {
690 ptrData *data(reinterpret_cast<ptrData *>(JSObjectGetPrivate(object)));
691 apr_pool_destroy(data->pool_);
694 static void Instance_finalize(JSObjectRef object) {
695 id data(reinterpret_cast<jocData>(JSObjectGetPrivate(object)));
699 JSObjectRef CYMakeFunction(JSContextRef context, void (*function)(), const char *type) {
700 ffiData *data(new ffiData(function, type));
701 return JSObjectMake(context, Functor_, data);
705 JSObjectRef CYMakeFunction(JSContextRef context, void *function, const char *type) {
706 return CYMakeFunction(context, reinterpret_cast<void (*)()>(function), type);
709 void CYSetProperty(JSContextRef context, JSObjectRef object, const char *name, JSValueRef value) {
710 JSValueRef exception(NULL);
711 JSObjectSetProperty(context, object, CYString(name), value, kJSPropertyAttributeNone, &exception);
712 CYThrow(context, exception);
715 char *CYPoolCString(apr_pool_t *pool, JSStringRef value) {
716 size_t size(JSStringGetMaximumUTF8CStringSize(value));
717 char *string(new(pool) char[size]);
718 JSStringGetUTF8CString(value, string, size);
719 JSStringRelease(value);
723 char *CYPoolCString(apr_pool_t *pool, JSContextRef context, JSValueRef value) {
724 return CYPoolCString(pool, CYString(context, value));
727 // XXX: this macro is unhygenic
728 #define CYCastCString(context, value) ({ \
729 JSValueRef exception(NULL); \
730 JSStringRef string(JSValueToStringCopy(context, value, &exception)); \
731 CYThrow(context, exception); \
732 size_t size(JSStringGetMaximumUTF8CStringSize(string)); \
733 char *utf8(reinterpret_cast<char *>(alloca(size))); \
734 JSStringGetUTF8CString(string, utf8, size); \
735 JSStringRelease(string); \
739 SEL CYCastSEL(JSContextRef context, JSValueRef value) {
740 if (JSValueIsNull(context, value))
742 else if (JSValueIsObjectOfClass(context, value, Selector_)) {
743 selData *data(reinterpret_cast<selData *>(JSObjectGetPrivate((JSObjectRef) value)));
744 return reinterpret_cast<SEL>(data->value_);
746 return sel_registerName(CYCastCString(context, value));
749 void *CYCastPointer(JSContextRef context, JSValueRef value) {
750 switch (JSValueGetType(context, value)) {
754 return dlsym(RTLD_DEFAULT, CYCastCString(context, value));
756 if (JSValueIsObjectOfClass(context, value, Pointer_)) {
757 ptrData *data(reinterpret_cast<ptrData *>(JSObjectGetPrivate((JSObjectRef) value)));
761 return reinterpret_cast<void *>(static_cast<uintptr_t>(CYCastDouble(context, value)));
765 void CYPoolFFI(apr_pool_t *pool, JSContextRef context, sig::Type *type, void *data, JSValueRef value) {
766 switch (type->primitive) {
768 *reinterpret_cast<bool *>(data) = JSValueToBoolean(context, value);
771 #define CYPoolFFI_(primitive, native) \
772 case sig::primitive ## _P: \
773 *reinterpret_cast<native *>(data) = CYCastDouble(context, value); \
776 CYPoolFFI_(uchar, unsigned char)
777 CYPoolFFI_(char, char)
778 CYPoolFFI_(ushort, unsigned short)
779 CYPoolFFI_(short, short)
780 CYPoolFFI_(ulong, unsigned long)
781 CYPoolFFI_(long, long)
782 CYPoolFFI_(uint, unsigned int)
784 CYPoolFFI_(ulonglong, unsigned long long)
785 CYPoolFFI_(longlong, long long)
786 CYPoolFFI_(float, float)
787 CYPoolFFI_(double, double)
790 case sig::typename_P:
791 *reinterpret_cast<id *>(data) = CYCastNSObject(context, value);
794 case sig::selector_P:
795 *reinterpret_cast<SEL *>(data) = CYCastSEL(context, value);
799 *reinterpret_cast<void **>(data) = CYCastPointer(context, value);
803 *reinterpret_cast<char **>(data) = CYPoolCString(pool, context, value);
813 NSLog(@"CYPoolFFI(%c)\n", type->primitive);
818 JSValueRef CYFromFFI(JSContextRef context, sig::Type *type, void *data) {
821 switch (type->primitive) {
823 value = JSValueMakeBoolean(context, *reinterpret_cast<bool *>(data));
826 #define CYFromFFI_(primitive, native) \
827 case sig::primitive ## _P: \
828 value = JSValueMakeNumber(context, *reinterpret_cast<native *>(data)); \
831 CYFromFFI_(uchar, unsigned char)
832 CYFromFFI_(char, char)
833 CYFromFFI_(ushort, unsigned short)
834 CYFromFFI_(short, short)
835 CYFromFFI_(ulong, unsigned long)
836 CYFromFFI_(long, long)
837 CYFromFFI_(uint, unsigned int)
839 CYFromFFI_(ulonglong, unsigned long long)
840 CYFromFFI_(longlong, long long)
841 CYFromFFI_(float, float)
842 CYFromFFI_(double, double)
845 case sig::typename_P: {
846 value = CYCastJSValue(context, *reinterpret_cast<id *>(data));
849 case sig::selector_P: {
850 if (SEL sel = *reinterpret_cast<SEL *>(data)) {
851 selData *data(new selData(sel));
852 value = JSObjectMake(context, Selector_, data);
856 case sig::pointer_P: {
857 if (void *pointer = *reinterpret_cast<void **>(data)) {
858 ptrData *data(new ptrData(pointer));
859 value = JSObjectMake(context, Pointer_, data);
863 case sig::string_P: {
864 if (char *utf8 = *reinterpret_cast<char **>(data))
865 value = JSValueMakeString(context, CYString(utf8));
873 value = JSValueMakeUndefined(context);
877 value = JSValueMakeNull(context);
881 NSLog(@"CYFromFFI(%c)\n", type->primitive);
888 static JSValueRef CYCallFunction(JSContextRef context, size_t count, const JSValueRef *arguments, JSValueRef *exception, sig::Signature *signature, ffi_cif *cif, void (*function)()) { _pooled
890 if (count != signature->count - 1)
891 @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"incorrect number of arguments to ffi function" userInfo:nil];
896 for (unsigned index(0); index != count; ++index) {
897 sig::Element *element(&signature->elements[index + 1]);
899 values[index] = new(pool) uint8_t[cif->arg_types[index]->size];
900 CYPoolFFI(pool, context, element->type, values[index], arguments[index]);
903 uint8_t value[cif->rtype->size];
904 ffi_call(cif, function, value, values);
906 return CYFromFFI(context, signature->elements[0].type, value);
910 static JSValueRef Global_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { _pooled
912 NSString *name(CYCastNSString(property));
913 if (Class _class = NSClassFromString(name))
914 return CYMakeObject(context, _class);
915 if (NSMutableArray *entry = [Bridge_ objectForKey:name])
916 switch ([[entry objectAtIndex:0] intValue]) {
918 return JSEvaluateScript(JSGetContext(), CYString([entry objectAtIndex:1]), NULL, NULL, 0, NULL);
920 return CYMakeFunction(context, [name cy$symbol], [[entry objectAtIndex:1] UTF8String]);
923 sig::Signature signature;
924 sig::Parse(pool, &signature, [[entry objectAtIndex:1] UTF8String]);
925 return CYFromFFI(context, signature.elements[0].type, [name cy$symbol]);
931 bool stret(ffi_type *ffi_type) {
932 return ffi_type->type == FFI_TYPE_STRUCT && (
933 ffi_type->size > OBJC_MAX_STRUCT_BY_VALUE ||
934 struct_forward_array[ffi_type->size] != 0
938 static JSValueRef $objc_msgSend(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { _pooled
943 @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"too few arguments to objc_msgSend" userInfo:nil];
945 id self(CYCastNSObject(context, arguments[0]));
947 return JSValueMakeNull(context);
949 SEL _cmd(CYCastSEL(context, arguments[1]));
950 NSMethodSignature *method([self methodSignatureForSelector:_cmd]);
952 @throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"unrecognized selector %s sent to object %p", sel_getName(_cmd), self] userInfo:nil];
954 type = [[method _typeString] UTF8String];
959 sig::Signature signature;
960 sig::Parse(pool, &signature, type);
963 sig::sig_ffi_cif(pool, &sig::ObjectiveC, &signature, &cif);
965 void (*function)() = stret(cif.rtype) ? reinterpret_cast<void (*)()>(&objc_msgSend_stret) : reinterpret_cast<void (*)()>(&objc_msgSend);
966 return CYCallFunction(context, count, arguments, exception, &signature, &cif, function);
969 static JSValueRef ffi_callAsFunction(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) {
970 ffiData *data(reinterpret_cast<ffiData *>(JSObjectGetPrivate(object)));
971 return CYCallFunction(context, count, arguments, exception, &data->signature_, &data->cif_, reinterpret_cast<void (*)()>(data->value_));
974 JSObjectRef ffi(JSContextRef context, JSObjectRef object, size_t count, const JSValueRef arguments[], JSValueRef *exception) {
977 @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"incorrect number of arguments to ffi constructor" userInfo:nil];
978 void *function(CYCastPointer(context, arguments[0]));
979 const char *type(CYCastCString(context, arguments[1]));
980 return CYMakeFunction(context, function, type);
984 JSValueRef Pointer_getProperty_value(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) {
985 ptrData *data(reinterpret_cast<ptrData *>(JSObjectGetPrivate(object)));
986 return JSValueMakeNumber(context, reinterpret_cast<uintptr_t>(data->value_));
989 static JSStaticValue Pointer_staticValues[2] = {
990 {"value", &Pointer_getProperty_value, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete},
991 {NULL, NULL, NULL, 0}
995 CYTokenBreak, CYTokenCase, CYTokenCatch, CYTokenContinue, CYTokenDefault,
996 CYTokenDelete, CYTokenDo, CYTokenElse, CYTokenFinally, CYTokenFor,
997 CYTokenFunction, CYTokenIf, CYTokenIn, CYTokenInstanceOf, CYTokenNew,
998 CYTokenReturn, CYTokenSwitch, CYTokenThis, CYTokenThrow, CYTokenTry,
999 CYTokenTypeOf, CYTokenVar, CYTokenVoid, CYTokenWhile, CYTokenWith,
1001 CYTokenOpenBrace, CYTokenOpenParen, CYTokenOpenBracket,
1002 CYTokenCloseBrace, CYTokenCloseParen, CYTokenCloseBracket,
1004 CYTokenPeriod, CYTokenSemiColon, CYTokenComma, CYTokenLeft, CYTokenRight,
1005 CYTokenLeftEqual, CYTokenRightEqual, CYTokenEqualEqual, CYTokenExclamationEqual,
1006 CYTokenEqualEqualEqual, CYTokenExclamationEqualEqual, CYTokenPlus, CYTokenHyphen,
1007 CYTokenStar, CYTokenPercent, CYTokenPlusPlus, CYTokenHyphenHyphen, CYTokenLeftLeft,
1008 CYTokenRightRight, CYTokenRightRightRight, CYTokenAmpersand, CYTokenPipe,
1009 CYTokenCarrot, CYTokenExclamation, CYTokenTilde, CYTokenAmpersandAmpersand,
1010 CYTokenPipePipe, CYTokenQuestion, CYTokenColon, CYTokenEqual, CYTokenPlusEqual,
1011 CYTokenHyphenEqual, CYTokenStarEqual, CYTokenPercentEqual, CYTokenLeftLeftEqual,
1012 CYTokenRightRightEqual, CYTokenRightRightRightEqual, CYTokenAmpersandEqual,
1013 CYTokenPipeEqual, CYTokenCarrotEqual, CYTokenSlash, CYTokenSlashEqual,
1015 CYTokenIdentifier, CYTokenLiteral
1018 typedef std::map<const char *, CYTokenType> TokenMap;
1022 enum CYTokenType type_;
1028 struct CYExpression {
1035 CYRange(uint64_t lo, uint64_t hi) :
1040 bool operator [](uint8_t value) const {
1041 return !(value >> 7) && (value >> 6 ? hi_ : lo_) >> (value & 0x3f) & 0x1;
1044 void operator()(uint8_t value) {
1047 (value >> 6 ? hi_ : lo_) |= uint64_t(0x1) << (value & 0x3f);
1051 CYRange WordStartRange_(0x1000000000LLU,0x7fffffe87fffffeLLU); // A-Za-z_$
1052 CYRange WordEndRange_(0x3ff001000000000LLU,0x7fffffe87fffffeLLU); // A-Za-z_$0-9
1053 CYRange NumberRange_(0x3ff400000000000LLU,0x100007e0100007eLLU); // 0-9.eExXA-Fa-f
1054 CYRange PunctuationRange_(0xfc00fc6200000000LLU,0x5000000040000000LLU); // -.,;<>=!+*/%&|^~?:
1056 struct CStringMapLess :
1057 std::binary_function<const char *, const char *, bool>
1059 _finline bool operator ()(const char *lhs, const char *rhs) const {
1060 return strcmp(lhs, rhs) < 0;
1064 std::set<const char *, CStringMapLess> OperatorWords_;
1076 CYParser(FILE *fin, FILE *fout) :
1080 data_(reinterpret_cast<char *>(malloc(capacity_))),
1087 // XXX: this will not deconstruct in constructor failures
1091 bool ReadLine(const char *prompt) {
1093 data_[capacity_ - 1] = ~'\0';
1096 if (fout_ != NULL) {
1097 fputs(prompt, fout_);
1102 if (fgets(data_, capacity_, fin_) == NULL)
1106 if (data_[capacity_ - 1] != '\0') {
1107 size_ = strlen(data_);
1110 if (data_[size_ - 1] == '\n') {
1114 } else if (data_[capacity_ - 2] == '\n') {
1115 size_ = capacity_ - 2;
1117 data_[size_] = '\0';
1119 size_t capacity(capacity_ * 2);
1120 char *data(reinterpret_cast<char *>(realloc(data_, capacity)));
1121 _assert(data != NULL);
1123 size_ = capacity_ - 1;
1124 capacity_ = capacity;
1125 fgets(data_ + size_, capacity_ - size_, fin_);
1132 _finline void ScanRange(const CYRange &range) {
1133 while (range[data_[++offset_]]);
1136 CYToken *ParseToken(apr_pool_t *pool, const char *prompt) {
1140 if (offset_ == size_ && (prompt == NULL || !ReadLine(prompt)))
1142 next = data_[offset_];
1143 if (next != ' ' && next != '\t')
1149 size_t index(offset_);
1151 if (WordStartRange_[next]) {
1152 ScanRange(WordEndRange_);
1154 } else if (next == '.') {
1155 char after(data_[offset_ + 1]);
1156 if (after >= '0' && next <= '9')
1159 } else if (next >= '0' && next <= '9') {
1161 ScanRange(NumberRange_);
1162 type = CYTokenLiteral;
1163 } else if (PunctuationRange_[next]) {
1165 ScanRange(PunctuationRange_);
1166 type = CYTokenPunctuation;
1167 } else if (next == '"' || next == '\'') {
1169 char after(data_[++offset_]);
1170 if (after == '\\') {
1171 after = data_[offset_];
1172 _assert(after != '\0');
1175 _assert(offset_ < size_);
1177 } else if (after == next)
1182 type = CYTokenLiteral;
1183 } else if (next == '(' || next == '{' || next == '[') {
1186 } else if (next == ')' || next == '}' || next == ']') {
1188 type = CYTokenClose;
1189 } else if (next == ';') {
1191 type = CYTokenSemiColon;
1193 printf(":( %u\n", next);
1197 char *value(apr_pstrndup(pool, data_ + index, offset_ - index));
1199 if (type == CYTokenWord && OperatorWords_.find(value) != OperatorWords_.end())
1200 type = CYTokenPunctuation;
1202 CYToken *token(new(pool) CYToken());
1203 token->type_ = type;
1204 token->value_ = value;
1205 token->next_ = token;
1206 token->prev_ = &token->next_;
1210 CYToken *ParseExpression(apr_pool_t *pool, const char *prompt) {
1211 CYToken *token(ParseToken(pool, prompt));
1216 void CYConsole(FILE *fin, FILE *fout, FILE *ferr) {
1217 CYParser parser(fin, fout);
1221 CYToken *token(parser.ParseExpression(pool, ">>>"));
1225 CYToken *next(token);
1227 fputs(next->value_, fout);
1230 } while (next != token);
1233 JSStringRef script(JSStringCreateWithUTF8CString(line.c_str()));
1235 JSContextRef context(JSGetContext());
1237 JSValueRef exception(NULL);
1238 JSValueRef result(JSEvaluateScript(context, script, NULL, NULL, 0, &exception));
1239 JSStringRelease(script);
1241 if (exception != NULL)
1244 if (!JSValueIsUndefined(context, result)) {
1248 json = JSValueToJSONCopy(context, result);
1249 } @catch (id error) {
1250 CYThrow(context, error, &result);
1254 fputs([reinterpret_cast<const NSString *>(json) UTF8String], fout);
1264 MSInitialize { _pooled
1267 NSCFBoolean_ = objc_getClass("NSCFBoolean");
1269 pid_t pid(getpid());
1271 struct sockaddr_in address;
1272 address.sin_len = sizeof(address);
1273 address.sin_family = AF_INET;
1274 address.sin_addr.s_addr = INADDR_ANY;
1275 address.sin_port = htons(10000 + pid);
1277 CFDataRef data(CFDataCreate(kCFAllocatorDefault, reinterpret_cast<UInt8 *>(&address), sizeof(address)));
1279 CFSocketSignature signature;
1280 signature.protocolFamily = AF_INET;
1281 signature.socketType = SOCK_STREAM;
1282 signature.protocol = IPPROTO_TCP;
1283 signature.address = data;
1285 CFSocketRef socket(CFSocketCreateWithSocketSignature(kCFAllocatorDefault, &signature, kCFSocketAcceptCallBack, &OnAccept, NULL));
1286 CFRunLoopAddSource(CFRunLoopGetCurrent(), CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket, 0), kCFRunLoopDefaultMode);
1288 JSClassDefinition definition;
1290 definition = kJSClassDefinitionEmpty;
1291 definition.className = "Pointer";
1292 definition.staticValues = Pointer_staticValues;
1293 definition.finalize = &Pointer_finalize;
1294 Pointer_ = JSClassCreate(&definition);
1296 definition = kJSClassDefinitionEmpty;
1297 definition.className = "Functor";
1298 definition.parentClass = Pointer_;
1299 definition.callAsFunction = &ffi_callAsFunction;
1300 Functor_ = JSClassCreate(&definition);
1302 definition = kJSClassDefinitionEmpty;
1303 definition.className = "Selector";
1304 definition.parentClass = Pointer_;
1305 Selector_ = JSClassCreate(&definition);
1307 definition = kJSClassDefinitionEmpty;
1308 definition.className = "Instance_";
1309 definition.getProperty = &Instance_getProperty;
1310 definition.callAsConstructor = &Instance_callAsConstructor;
1311 definition.finalize = &Instance_finalize;
1312 Instance_ = JSClassCreate(&definition);
1314 definition = kJSClassDefinitionEmpty;
1315 definition.getProperty = &Global_getProperty;
1316 JSClassRef Global(JSClassCreate(&definition));
1318 JSContextRef context(JSGlobalContextCreate(Global));
1321 JSObjectRef global(JSContextGetGlobalObject(context));
1323 CYSetProperty(context, global, "ffi", JSObjectMakeConstructor(context, Functor_, &ffi));
1325 CYSetProperty(context, global, "objc_msgSend", JSObjectMakeFunctionWithCallback(context, CYString("objc_msgSend"), &$objc_msgSend));
1327 Bridge_ = [[NSMutableDictionary dictionaryWithContentsOfFile:@"/usr/lib/libcycript.plist"] retain];
1329 Tokens_.insert(TokenMap::value_type("break", CYTokenBreak));
1330 Tokens_.insert(TokenMap::value_type("case", CYTokenCase));
1331 Tokens_.insert(TokenMap::value_type("catch", CYTokenCatch));
1332 Tokens_.insert(TokenMap::value_type("continue", CYTokenContinue));
1333 Tokens_.insert(TokenMap::value_type("default", CYTokenDefault));
1334 Tokens_.insert(TokenMap::value_type("delete", CYTokenDelete));
1335 Tokens_.insert(TokenMap::value_type("do", CYTokenDo));
1336 Tokens_.insert(TokenMap::value_type("else", CYTokenElse));
1337 Tokens_.insert(TokenMap::value_type("finally", CYTokenFinally));
1338 Tokens_.insert(TokenMap::value_type("for", CYTokenFor));
1339 Tokens_.insert(TokenMap::value_type("function", CYTokenFunction));
1340 Tokens_.insert(TokenMap::value_type("if", CYTokenIf));
1341 Tokens_.insert(TokenMap::value_type("in", CYTokenIn));
1342 Tokens_.insert(TokenMap::value_type("instanceof", CYTokenInstanceOf));
1343 Tokens_.insert(TokenMap::value_type("new", CYTokenNew));
1344 Tokens_.insert(TokenMap::value_type("return", CYTokenReturn));
1345 Tokens_.insert(TokenMap::value_type("switch", CYTokenSwitch));
1346 Tokens_.insert(TokenMap::value_type("this", CYTokenThis));
1347 Tokens_.insert(TokenMap::value_type("throw", CYTokenThrow));
1348 Tokens_.insert(TokenMap::value_type("try", CYTokenTry));
1349 Tokens_.insert(TokenMap::value_type("typeof", CYTokenTypeOf));
1350 Tokens_.insert(TokenMap::value_type("var", CYTokenVar));
1351 Tokens_.insert(TokenMap::value_type("void", CYTokenVoid));
1352 Tokens_.insert(TokenMap::value_type("while", CYTokenWhile));
1353 Tokens_.insert(TokenMap::value_type("with", CYTokenWith));
1355 Tokens_.insert(TokenMap::value_type("&", CYTokenAmpersand));
1356 Tokens_.insert(TokenMap::value_type("&&", CYTokenAmpersandAmpersand));
1357 Tokens_.insert(TokenMap::value_type("&=", CYTokenAmpersandEqual));
1358 Tokens_.insert(TokenMap::value_type("^", CYTokenCarrot));
1359 Tokens_.insert(TokenMap::value_type("^=", CYTokenCarrotEqual));
1360 Tokens_.insert(TokenMap::value_type(":", CYTokenColon));
1361 Tokens_.insert(TokenMap::value_type(",", CYTokenComma));
1362 Tokens_.insert(TokenMap::value_type("=", CYTokenEqual));
1363 Tokens_.insert(TokenMap::value_type("==", CYTokenEqualEqual));
1364 Tokens_.insert(TokenMap::value_type("===", CYTokenEqualEqualEqual));
1365 Tokens_.insert(TokenMap::value_type("!", CYTokenExclamation));
1366 Tokens_.insert(TokenMap::value_type("!=", CYTokenExclamationEqual));
1367 Tokens_.insert(TokenMap::value_type("!==", CYTokenExclamationEqualEqual));
1368 Tokens_.insert(TokenMap::value_type("-", CYTokenHyphen));
1369 Tokens_.insert(TokenMap::value_type("-=", CYTokenHyphenEqual));
1370 Tokens_.insert(TokenMap::value_type("--", CYTokenHyphenHyphen));
1371 Tokens_.insert(TokenMap::value_type("<", CYTokenLeft));
1372 Tokens_.insert(TokenMap::value_type("<=", CYTokenLeftEqual));
1373 Tokens_.insert(TokenMap::value_type("<<", CYTokenLeftLeft));
1374 Tokens_.insert(TokenMap::value_type("<<=", CYTokenLeftLeftEqual));
1375 Tokens_.insert(TokenMap::value_type("%", CYTokenPercent));
1376 Tokens_.insert(TokenMap::value_type("%=", CYTokenPercentEqual));
1377 Tokens_.insert(TokenMap::value_type(".", CYTokenPeriod));
1378 Tokens_.insert(TokenMap::value_type("|", CYTokenPipe));
1379 Tokens_.insert(TokenMap::value_type("|=", CYTokenPipeEqual));
1380 Tokens_.insert(TokenMap::value_type("||", CYTokenPipePipe));
1381 Tokens_.insert(TokenMap::value_type("+", CYTokenPlus));
1382 Tokens_.insert(TokenMap::value_type("+=", CYTokenPlusEqual));
1383 Tokens_.insert(TokenMap::value_type("++", CYTokenPlusPlus));
1384 Tokens_.insert(TokenMap::value_type("?", CYTokenQuestion));
1385 Tokens_.insert(TokenMap::value_type(">", CYTokenRight));
1386 Tokens_.insert(TokenMap::value_type(">=", CYTokenRightEqual));
1387 Tokens_.insert(TokenMap::value_type(">>", CYTokenRightRight));
1388 Tokens_.insert(TokenMap::value_type(">>=", CYTokenRightRightEqual));
1389 Tokens_.insert(TokenMap::value_type(">>>", CYTokenRightRightRight));
1390 Tokens_.insert(TokenMap::value_type(">>>=", CYTokenRightRightRightEqual));
1391 Tokens_.insert(TokenMap::value_type(";", CYTokenSemiColon));
1392 Tokens_.insert(TokenMap::value_type("/", CYTokenSlash));
1393 Tokens_.insert(TokenMap::value_type("/=", CYTokenSlashEqual));
1394 Tokens_.insert(TokenMap::value_type("*", CYTokenStar));
1395 Tokens_.insert(TokenMap::value_type("*=", CYTokenStarEqual));
1396 Tokens_.insert(TokenMap::value_type("~", CYTokenTilde));
1398 name_ = JSStringCreateWithUTF8CString("name");
1399 message_ = JSStringCreateWithUTF8CString("message");
1400 length_ = JSStringCreateWithUTF8CString("length");
1402 JSValueRef exception(NULL);
1403 JSValueRef value(JSObjectGetProperty(JSGetContext(), global, CYString("Array"), &exception));
1404 CYThrow(context, exception);
1405 Array_ = JSValueToObject(JSGetContext(), value, &exception);
1406 CYThrow(context, exception);