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>
78 #define _assert(test) do { \
80 @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:[NSString stringWithFormat:@"_assert(%s):%s(%u):%s", #test, __FILE__, __LINE__, __FUNCTION__] userInfo:nil]; \
83 #define _trace() do { \
84 CFLog(kCFLogLevelNotice, CFSTR("_trace():%u"), __LINE__); \
87 /* Objective-C Handle<> {{{ */
88 template <typename Type_>
90 typedef _H<Type_> This_;
95 _finline void Retain_() {
100 _finline void Clear_() {
106 _finline _H(const This_ &rhs) :
107 value_(rhs.value_ == nil ? nil : [rhs.value_ retain])
111 _finline _H(Type_ *value = NULL, bool mended = false) :
122 _finline operator Type_ *() const {
126 _finline This_ &operator =(Type_ *value) {
127 if (value_ != value) {
137 /* APR Pool Helpers {{{ */
138 void *operator new(size_t size, apr_pool_t *pool) {
139 return apr_palloc(pool, size);
142 void *operator new [](size_t size, apr_pool_t *pool) {
143 return apr_palloc(pool, size);
152 apr_pool_create(&pool_, NULL);
156 apr_pool_destroy(pool_);
159 operator apr_pool_t *() const {
165 #define _pooled _H<NSAutoreleasePool> _pool([[NSAutoreleasePool alloc] init], true);
167 static JSContextRef Context_;
169 static JSClassRef Functor_;
170 static JSClassRef Instance_;
171 static JSClassRef Pointer_;
172 static JSClassRef Selector_;
174 static JSObjectRef Array_;
176 static JSStringRef name_;
177 static JSStringRef message_;
178 static JSStringRef length_;
180 static Class NSCFBoolean_;
182 static NSMutableDictionary *Bridge_;
185 CFHTTPMessageRef message_;
189 JSObjectRef CYMakeObject(JSContextRef context, id object) {
190 return JSObjectMake(context, Instance_, [object retain]);
193 @interface NSMethodSignature (Cycript)
194 - (NSString *) _typeString;
197 @interface NSObject (Cycript)
198 - (NSString *) cy$toJSON;
199 - (JSValueRef) cy$JSValueInContext:(JSContextRef)context;
202 @interface NSString (Cycript)
203 - (void *) cy$symbol;
206 @interface NSNumber (Cycript)
207 - (void *) cy$symbol;
210 @implementation NSObject (Cycript)
212 - (NSString *) cy$toJSON {
213 return [self description];
216 - (JSValueRef) cy$JSValueInContext:(JSContextRef)context {
217 return CYMakeObject(context, self);
222 @implementation WebUndefined (Cycript)
224 - (NSString *) cy$toJSON {
228 - (JSValueRef) cy$JSValueInContext:(JSContextRef)context {
229 return JSValueMakeUndefined(context);
234 @implementation NSArray (Cycript)
236 - (NSString *) cy$toJSON {
237 NSMutableString *json([[[NSMutableString alloc] init] autorelease]);
238 [json appendString:@"["];
241 for (id object in self) {
243 [json appendString:@","];
246 [json appendString:[object cy$toJSON]];
249 [json appendString:@"]"];
255 @implementation NSDictionary (Cycript)
257 - (NSString *) cy$toJSON {
258 NSMutableString *json([[[NSMutableString alloc] init] autorelease]);
259 [json appendString:@"("];
260 [json appendString:@"{"];
263 for (id key in self) {
265 [json appendString:@","];
268 [json appendString:[key cy$toJSON]];
269 [json appendString:@":"];
270 NSObject *object([self objectForKey:key]);
271 [json appendString:[object cy$toJSON]];
274 [json appendString:@"})"];
280 @implementation NSNumber (Cycript)
282 - (NSString *) cy$toJSON {
283 return [self class] != NSCFBoolean_ ? [self stringValue] : [self boolValue] ? @"true" : @"false";
286 - (JSValueRef) cy$JSValueInContext:(JSContextRef)context {
287 return [self class] != NSCFBoolean_ ? JSValueMakeNumber(context, [self doubleValue]) : JSValueMakeBoolean(context, [self boolValue]);
290 - (void *) cy$symbol {
291 return [self pointerValue];
296 @implementation NSString (Cycript)
298 - (NSString *) cy$toJSON {
299 CFMutableStringRef json(CFStringCreateMutableCopy(kCFAllocatorDefault, 0, (CFStringRef) self));
301 CFStringFindAndReplace(json, CFSTR("\\"), CFSTR("\\\\"), CFRangeMake(0, CFStringGetLength(json)), 0);
302 CFStringFindAndReplace(json, CFSTR("\""), CFSTR("\\\""), CFRangeMake(0, CFStringGetLength(json)), 0);
303 CFStringFindAndReplace(json, CFSTR("\t"), CFSTR("\\t"), CFRangeMake(0, CFStringGetLength(json)), 0);
304 CFStringFindAndReplace(json, CFSTR("\r"), CFSTR("\\r"), CFRangeMake(0, CFStringGetLength(json)), 0);
305 CFStringFindAndReplace(json, CFSTR("\n"), CFSTR("\\n"), CFRangeMake(0, CFStringGetLength(json)), 0);
307 CFStringInsert(json, 0, CFSTR("\""));
308 CFStringAppend(json, CFSTR("\""));
310 return [reinterpret_cast<const NSString *>(json) autorelease];
313 - (void *) cy$symbol {
314 return dlsym(RTLD_DEFAULT, [self UTF8String]);
319 @interface CYJSObject : NSDictionary {
321 JSContextRef context_;
324 - (id) initWithJSObject:(JSObjectRef)object inContext:(JSContextRef)context;
326 - (NSUInteger) count;
327 - (id) objectForKey:(id)key;
328 - (NSEnumerator *) keyEnumerator;
329 - (void) setObject:(id)object forKey:(id)key;
330 - (void) removeObjectForKey:(id)key;
334 @interface CYJSArray : NSArray {
336 JSContextRef context_;
339 - (id) initWithJSObject:(JSObjectRef)object inContext:(JSContextRef)context;
341 - (NSUInteger) count;
342 - (id) objectAtIndex:(NSUInteger)index;
346 JSContextRef JSGetContext() {
351 @catch (id error) { \
352 CYThrow(context, error, exception); \
356 void CYThrow(JSContextRef context, JSValueRef value);
358 id CYCastNSObject(JSContextRef context, JSObjectRef object) {
359 if (JSValueIsObjectOfClass(context, object, Instance_))
360 return reinterpret_cast<id>(JSObjectGetPrivate(object));
361 JSValueRef exception(NULL);
362 bool array(JSValueIsInstanceOfConstructor(context, object, Array_, &exception));
363 CYThrow(context, exception);
365 return [[[CYJSArray alloc] initWithJSObject:object inContext:context] autorelease];
366 return [[[CYJSObject alloc] initWithJSObject:object inContext:context] autorelease];
369 JSStringRef CYCopyJSString(id value) {
370 return JSStringCreateWithCFString(reinterpret_cast<CFStringRef>([value description]));
373 JSStringRef CYCopyJSString(const char *value) {
374 return JSStringCreateWithUTF8CString(value);
377 JSStringRef CYCopyJSString(JSStringRef value) {
378 return JSStringRetain(value);
381 JSStringRef CYCopyJSString(JSContextRef context, JSValueRef value) {
382 JSValueRef exception(NULL);
383 JSStringRef string(JSValueToStringCopy(context, value, &exception));
384 CYThrow(context, exception);
388 // XXX: this is not a safe handle
394 template <typename Arg0_>
395 CYString(Arg0_ arg0) {
396 string_ = CYCopyJSString(arg0);
399 template <typename Arg0_, typename Arg1_>
400 CYString(Arg0_ arg0, Arg1_ arg1) {
401 string_ = CYCopyJSString(arg0, arg1);
405 JSStringRelease(string_);
408 operator JSStringRef() const {
413 CFStringRef CYCopyCFString(JSStringRef value) {
414 return JSStringCopyCFString(kCFAllocatorDefault, value);
417 CFStringRef CYCopyCFString(JSContextRef context, JSValueRef value) {
418 return CYCopyCFString(CYString(context, value));
421 double CYCastDouble(JSContextRef context, JSValueRef value) {
422 JSValueRef exception(NULL);
423 double number(JSValueToNumber(context, value, &exception));
424 CYThrow(context, exception);
428 CFNumberRef CYCopyCFNumber(JSContextRef context, JSValueRef value) {
429 double number(CYCastDouble(context, value));
430 return CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &number);
433 NSString *CYCastNSString(JSStringRef value) {
434 return [reinterpret_cast<const NSString *>(CYCopyCFString(value)) autorelease];
437 CFTypeRef CYCopyCFType(JSContextRef context, JSValueRef value) {
438 switch (JSType type = JSValueGetType(context, value)) {
439 case kJSTypeUndefined:
440 return CFRetain([WebUndefined undefined]);
444 return CFRetain(JSValueToBoolean(context, value) ? kCFBooleanTrue : kCFBooleanFalse);
446 return CYCopyCFNumber(context, value);
448 return CYCopyCFString(context, value);
450 return CFRetain((CFTypeRef) CYCastNSObject(context, (JSObjectRef) value));
452 @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:[NSString stringWithFormat:@"JSValueGetType() == 0x%x", type] userInfo:nil];
456 NSArray *CYCastNSArray(JSPropertyNameArrayRef names) {
457 size_t size(JSPropertyNameArrayGetCount(names));
458 NSMutableArray *array([NSMutableArray arrayWithCapacity:size]);
459 for (size_t index(0); index != size; ++index)
460 [array addObject:CYCastNSString(JSPropertyNameArrayGetNameAtIndex(names, index))];
464 id CYCastNSObject(JSContextRef context, JSValueRef value) {
465 const NSObject *object(reinterpret_cast<const NSObject *>(CYCopyCFType(context, value)));
466 return object == nil ? nil : [object autorelease];
469 void CYThrow(JSContextRef context, JSValueRef value) {
472 @throw CYCastNSObject(context, value);
475 JSValueRef CYCastJSValue(JSContextRef context, id value) {
476 return value == nil ? JSValueMakeNull(context) : [value cy$JSValueInContext:context];
479 void CYThrow(JSContextRef context, id error, JSValueRef *exception) {
480 *exception = CYCastJSValue(context, error);
483 @implementation CYJSObject
485 - (id) initWithJSObject:(JSObjectRef)object inContext:(JSContextRef)context {
486 if ((self = [super init]) != nil) {
492 - (NSUInteger) count {
493 JSPropertyNameArrayRef names(JSObjectCopyPropertyNames(context_, object_));
494 size_t size(JSPropertyNameArrayGetCount(names));
495 JSPropertyNameArrayRelease(names);
499 - (id) objectForKey:(id)key {
500 JSValueRef exception(NULL);
501 JSValueRef value(JSObjectGetProperty(context_, object_, CYString(key), &exception));
502 CYThrow(context_, exception);
503 return CYCastNSObject(context_, value);
506 - (NSEnumerator *) keyEnumerator {
507 JSPropertyNameArrayRef names(JSObjectCopyPropertyNames(context_, object_));
508 NSEnumerator *enumerator([CYCastNSArray(names) objectEnumerator]);
509 JSPropertyNameArrayRelease(names);
513 - (void) setObject:(id)object forKey:(id)key {
514 JSValueRef exception(NULL);
515 JSObjectSetProperty(context_, object_, CYString(key), CYCastJSValue(context_, object), kJSPropertyAttributeNone, &exception);
516 CYThrow(context_, exception);
519 - (void) removeObjectForKey:(id)key {
520 JSValueRef exception(NULL);
521 // XXX: this returns a bool... throw exception, or ignore?
522 JSObjectDeleteProperty(context_, object_, CYString(key), &exception);
523 CYThrow(context_, exception);
528 @implementation CYJSArray
530 - (id) initWithJSObject:(JSObjectRef)object inContext:(JSContextRef)context {
531 if ((self = [super init]) != nil) {
537 - (NSUInteger) count {
538 JSValueRef exception(NULL);
539 JSValueRef value(JSObjectGetProperty(context_, object_, length_, &exception));
540 CYThrow(context_, exception);
541 return CYCastDouble(context_, value);
544 - (id) objectAtIndex:(NSUInteger)index {
545 JSValueRef exception(NULL);
546 JSValueRef value(JSObjectGetPropertyAtIndex(context_, object_, index, &exception));
547 CYThrow(context_, exception);
548 id object(CYCastNSObject(context_, value));
549 return object == nil ? [NSNull null] : object;
554 CFStringRef JSValueToJSONCopy(JSContextRef context, JSValueRef value) {
555 id object(CYCastNSObject(context, value));
556 return reinterpret_cast<CFStringRef>([(object == nil ? @"null" : [object cy$toJSON]) retain]);
559 static void OnData(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *value, void *info) {
561 case kCFSocketDataCallBack:
562 CFDataRef data(reinterpret_cast<CFDataRef>(value));
563 Client *client(reinterpret_cast<Client *>(info));
565 if (client->message_ == NULL)
566 client->message_ = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, TRUE);
568 if (!CFHTTPMessageAppendBytes(client->message_, CFDataGetBytePtr(data), CFDataGetLength(data)))
569 CFLog(kCFLogLevelError, CFSTR("CFHTTPMessageAppendBytes()"));
570 else if (CFHTTPMessageIsHeaderComplete(client->message_)) {
571 CFURLRef url(CFHTTPMessageCopyRequestURL(client->message_));
573 CFStringRef path(CFURLCopyStrictPath(url, &absolute));
574 CFRelease(client->message_);
576 CFStringRef code(CFURLCreateStringByReplacingPercentEscapes(kCFAllocatorDefault, path, CFSTR("")));
579 JSStringRef script(JSStringCreateWithCFString(code));
582 JSValueRef result(JSEvaluateScript(JSGetContext(), script, NULL, NULL, 0, NULL));
583 JSStringRelease(script);
585 CFHTTPMessageRef response(CFHTTPMessageCreateResponse(kCFAllocatorDefault, 200, NULL, kCFHTTPVersion1_1));
586 CFHTTPMessageSetHeaderFieldValue(response, CFSTR("Content-Type"), CFSTR("application/json; charset=utf-8"));
588 CFStringRef json(JSValueToJSONCopy(JSGetContext(), result));
589 CFDataRef body(CFStringCreateExternalRepresentation(kCFAllocatorDefault, json, kCFStringEncodingUTF8, NULL));
592 CFStringRef length(CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%u"), CFDataGetLength(body)));
593 CFHTTPMessageSetHeaderFieldValue(response, CFSTR("Content-Length"), length);
596 CFHTTPMessageSetBody(response, body);
599 CFDataRef serialized(CFHTTPMessageCopySerializedMessage(response));
602 CFSocketSendData(socket, NULL, serialized, 0);
603 CFRelease(serialized);
611 static void OnAccept(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *value, void *info) {
613 case kCFSocketAcceptCallBack:
614 Client *client(new Client());
616 client->message_ = NULL;
618 CFSocketContext context;
620 context.info = client;
621 context.retain = NULL;
622 context.release = NULL;
623 context.copyDescription = NULL;
625 client->socket_ = CFSocketCreateWithNative(kCFAllocatorDefault, *reinterpret_cast<const CFSocketNativeHandle *>(value), kCFSocketDataCallBack, &OnData, &context);
627 CFRunLoopAddSource(CFRunLoopGetCurrent(), CFSocketCreateRunLoopSource(kCFAllocatorDefault, client->socket_, 0), kCFRunLoopDefaultMode);
632 static JSValueRef Instance_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { _pooled
634 NSString *name(CYCastNSString(property));
642 static JSObjectRef Instance_callAsConstructor(JSContextRef context, JSObjectRef object, size_t count, const JSValueRef arguments[], JSValueRef *exception) { _pooled
644 id data(reinterpret_cast<jocData>(JSObjectGetPrivate(object)));
645 return CYMakeObject(context, [[data alloc] autorelease]);
654 void *operator new(size_t size) {
656 apr_pool_create(&pool, NULL);
657 void *data(apr_palloc(pool, size));
658 reinterpret_cast<ptrData *>(data)->pool_ = pool;
662 ptrData(void *value) :
668 struct ffiData : ptrData {
669 sig::Signature signature_;
672 ffiData(void (*value)(), const char *type) :
673 ptrData(reinterpret_cast<void *>(value))
675 sig::Parse(pool_, &signature_, type);
676 sig::sig_ffi_cif(pool_, &sig::ObjectiveC, &signature_, &cif_);
680 struct selData : ptrData {
687 static void Pointer_finalize(JSObjectRef object) {
688 ptrData *data(reinterpret_cast<ptrData *>(JSObjectGetPrivate(object)));
689 apr_pool_destroy(data->pool_);
692 static void Instance_finalize(JSObjectRef object) {
693 id data(reinterpret_cast<jocData>(JSObjectGetPrivate(object)));
697 JSObjectRef CYMakeFunction(JSContextRef context, void (*function)(), const char *type) {
698 ffiData *data(new ffiData(function, type));
699 return JSObjectMake(context, Functor_, data);
703 JSObjectRef CYMakeFunction(JSContextRef context, void *function, const char *type) {
704 return CYMakeFunction(context, reinterpret_cast<void (*)()>(function), type);
707 void CYSetProperty(JSContextRef context, JSObjectRef object, const char *name, JSValueRef value) {
708 JSValueRef exception(NULL);
709 JSObjectSetProperty(context, object, CYString(name), value, kJSPropertyAttributeNone, &exception);
710 CYThrow(context, exception);
713 char *CYPoolCString(apr_pool_t *pool, JSStringRef value) {
714 size_t size(JSStringGetMaximumUTF8CStringSize(value));
715 char *string(new(pool) char[size]);
716 JSStringGetUTF8CString(value, string, size);
717 JSStringRelease(value);
721 char *CYPoolCString(apr_pool_t *pool, JSContextRef context, JSValueRef value) {
722 return CYPoolCString(pool, CYString(context, value));
725 // XXX: this macro is unhygenic
726 #define CYCastCString(context, value) ({ \
727 JSValueRef exception(NULL); \
728 JSStringRef string(JSValueToStringCopy(context, value, &exception)); \
729 CYThrow(context, exception); \
730 size_t size(JSStringGetMaximumUTF8CStringSize(string)); \
731 char *utf8(reinterpret_cast<char *>(alloca(size))); \
732 JSStringGetUTF8CString(string, utf8, size); \
733 JSStringRelease(string); \
737 SEL CYCastSEL(JSContextRef context, JSValueRef value) {
738 if (JSValueIsNull(context, value))
740 else if (JSValueIsObjectOfClass(context, value, Selector_)) {
741 selData *data(reinterpret_cast<selData *>(JSObjectGetPrivate((JSObjectRef) value)));
742 return reinterpret_cast<SEL>(data->value_);
744 return sel_registerName(CYCastCString(context, value));
747 void *CYCastPointer(JSContextRef context, JSValueRef value) {
748 switch (JSValueGetType(context, value)) {
752 return dlsym(RTLD_DEFAULT, CYCastCString(context, value));
754 if (JSValueIsObjectOfClass(context, value, Pointer_)) {
755 ptrData *data(reinterpret_cast<ptrData *>(JSObjectGetPrivate((JSObjectRef) value)));
759 return reinterpret_cast<void *>(static_cast<uintptr_t>(CYCastDouble(context, value)));
763 void CYPoolFFI(apr_pool_t *pool, JSContextRef context, sig::Type *type, void *data, JSValueRef value) {
764 switch (type->primitive) {
766 *reinterpret_cast<bool *>(data) = JSValueToBoolean(context, value);
769 #define CYPoolFFI_(primitive, native) \
770 case sig::primitive ## _P: \
771 *reinterpret_cast<native *>(data) = CYCastDouble(context, value); \
774 CYPoolFFI_(uchar, unsigned char)
775 CYPoolFFI_(char, char)
776 CYPoolFFI_(ushort, unsigned short)
777 CYPoolFFI_(short, short)
778 CYPoolFFI_(ulong, unsigned long)
779 CYPoolFFI_(long, long)
780 CYPoolFFI_(uint, unsigned int)
782 CYPoolFFI_(ulonglong, unsigned long long)
783 CYPoolFFI_(longlong, long long)
784 CYPoolFFI_(float, float)
785 CYPoolFFI_(double, double)
788 case sig::typename_P:
789 *reinterpret_cast<id *>(data) = CYCastNSObject(context, value);
792 case sig::selector_P:
793 *reinterpret_cast<SEL *>(data) = CYCastSEL(context, value);
797 *reinterpret_cast<void **>(data) = CYCastPointer(context, value);
801 *reinterpret_cast<char **>(data) = CYPoolCString(pool, context, value);
811 NSLog(@"CYPoolFFI(%c)\n", type->primitive);
816 JSValueRef CYFromFFI(JSContextRef context, sig::Type *type, void *data) {
819 switch (type->primitive) {
821 value = JSValueMakeBoolean(context, *reinterpret_cast<bool *>(data));
824 #define CYFromFFI_(primitive, native) \
825 case sig::primitive ## _P: \
826 value = JSValueMakeNumber(context, *reinterpret_cast<native *>(data)); \
829 CYFromFFI_(uchar, unsigned char)
830 CYFromFFI_(char, char)
831 CYFromFFI_(ushort, unsigned short)
832 CYFromFFI_(short, short)
833 CYFromFFI_(ulong, unsigned long)
834 CYFromFFI_(long, long)
835 CYFromFFI_(uint, unsigned int)
837 CYFromFFI_(ulonglong, unsigned long long)
838 CYFromFFI_(longlong, long long)
839 CYFromFFI_(float, float)
840 CYFromFFI_(double, double)
843 case sig::typename_P: {
844 value = CYCastJSValue(context, *reinterpret_cast<id *>(data));
847 case sig::selector_P: {
848 if (SEL sel = *reinterpret_cast<SEL *>(data)) {
849 selData *data(new selData(sel));
850 value = JSObjectMake(context, Selector_, data);
854 case sig::pointer_P: {
855 if (void *pointer = *reinterpret_cast<void **>(data)) {
856 ptrData *data(new ptrData(pointer));
857 value = JSObjectMake(context, Pointer_, data);
861 case sig::string_P: {
862 if (char *utf8 = *reinterpret_cast<char **>(data))
863 value = JSValueMakeString(context, CYString(utf8));
871 value = JSValueMakeUndefined(context);
875 value = JSValueMakeNull(context);
879 NSLog(@"CYFromFFI(%c)\n", type->primitive);
886 static JSValueRef CYCallFunction(JSContextRef context, size_t count, const JSValueRef *arguments, JSValueRef *exception, sig::Signature *signature, ffi_cif *cif, void (*function)()) { _pooled
888 if (count != signature->count - 1)
889 @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"incorrect number of arguments to ffi function" userInfo:nil];
894 for (unsigned index(0); index != count; ++index) {
895 sig::Element *element(&signature->elements[index + 1]);
897 values[index] = new(pool) uint8_t[cif->arg_types[index]->size];
898 CYPoolFFI(pool, context, element->type, values[index], arguments[index]);
901 uint8_t value[cif->rtype->size];
902 ffi_call(cif, function, value, values);
904 return CYFromFFI(context, signature->elements[0].type, value);
908 static JSValueRef Global_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { _pooled
910 NSString *name(CYCastNSString(property));
911 if (Class _class = NSClassFromString(name))
912 return CYMakeObject(context, _class);
913 if (NSMutableArray *entry = [Bridge_ objectForKey:name])
914 switch ([[entry objectAtIndex:0] intValue]) {
916 return JSEvaluateScript(JSGetContext(), CYString([entry objectAtIndex:1]), NULL, NULL, 0, NULL);
918 return CYMakeFunction(context, [name cy$symbol], [[entry objectAtIndex:1] UTF8String]);
921 sig::Signature signature;
922 sig::Parse(pool, &signature, [[entry objectAtIndex:1] UTF8String]);
923 return CYFromFFI(context, signature.elements[0].type, [name cy$symbol]);
929 bool stret(ffi_type *ffi_type) {
930 return ffi_type->type == FFI_TYPE_STRUCT && (
931 ffi_type->size > OBJC_MAX_STRUCT_BY_VALUE ||
932 struct_forward_array[ffi_type->size] != 0
936 static JSValueRef $objc_msgSend(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { _pooled
941 @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"too few arguments to objc_msgSend" userInfo:nil];
943 id self(CYCastNSObject(context, arguments[0]));
945 return JSValueMakeNull(context);
947 SEL _cmd(CYCastSEL(context, arguments[1]));
948 NSMethodSignature *method([self methodSignatureForSelector:_cmd]);
950 @throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"unrecognized selector %s sent to object %p", sel_getName(_cmd), self] userInfo:nil];
952 type = [[method _typeString] UTF8String];
957 sig::Signature signature;
958 sig::Parse(pool, &signature, type);
961 sig::sig_ffi_cif(pool, &sig::ObjectiveC, &signature, &cif);
963 void (*function)() = stret(cif.rtype) ? reinterpret_cast<void (*)()>(&objc_msgSend_stret) : reinterpret_cast<void (*)()>(&objc_msgSend);
964 return CYCallFunction(context, count, arguments, exception, &signature, &cif, function);
967 static JSValueRef ffi_callAsFunction(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) {
968 ffiData *data(reinterpret_cast<ffiData *>(JSObjectGetPrivate(object)));
969 return CYCallFunction(context, count, arguments, exception, &data->signature_, &data->cif_, reinterpret_cast<void (*)()>(data->value_));
972 JSObjectRef ffi(JSContextRef context, JSObjectRef object, size_t count, const JSValueRef arguments[], JSValueRef *exception) {
975 @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"incorrect number of arguments to ffi constructor" userInfo:nil];
976 void *function(CYCastPointer(context, arguments[0]));
977 const char *type(CYCastCString(context, arguments[1]));
978 return CYMakeFunction(context, function, type);
982 JSValueRef Pointer_getProperty_value(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) {
983 ptrData *data(reinterpret_cast<ptrData *>(JSObjectGetPrivate(object)));
984 return JSValueMakeNumber(context, reinterpret_cast<uintptr_t>(data->value_));
987 static JSStaticValue Pointer_staticValues[2] = {
988 {"value", &Pointer_getProperty_value, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete},
989 {NULL, NULL, NULL, 0}
993 /*CYTokenBreak, CYTokenCase, CYTokenCatch, CYTokenContinue, CYTokenDefault,
994 CYTokenDelete, CYTokenDo, CYTokenElse, CYTokenFinally, CYTokenFor,
995 CYTokenFunction, CYTokenIf, CYTokenIn, CYTokenInstanceOf, CYTokenNew,
996 CYTokenReturn, CYTokenSwitch, CYTokenThis, CYTokenThrow, CYTokenTry,
997 CYTokenTypeOf, CYTokenVar, CYTokenVoid, CYTokenWhile, CYTokenWith,*/
999 CYTokenWord, CYTokenPunctuation, CYTokenLiteral,
1000 CYTokenSemiColon, CYTokenOpen, CYTokenClose,
1004 enum CYTokenType type_;
1008 struct CYExpression {
1015 CYRange(uint64_t lo, uint64_t hi) :
1020 bool operator [](uint8_t value) const {
1021 return (value >> 7) && (value >> 6 ? hi_ : lo_) >> (value & 0x3f) & 0x1;
1024 void operator()(uint8_t value) {
1027 (value >> 6 ? hi_ : lo_) |= uint64_t(0x1) << (value & 0x3f);
1031 CYRange WordStartRange_(0x1000000000LLU,0x7fffffe87fffffeLLU); // A-Za-z_$
1032 CYRange WordEndRange_(0x3ff001000000000LLU,0x7fffffe87fffffeLLU); // A-Za-z_$0-9
1033 CYRange NumberRange_(0x3ff400000000000LLU,0x100007e0100007eLLU); // 0-9.eExXA-Fa-f
1034 CYRange PunctuationRange_(0xfc00fc6200000001LLU,0x5000000040000000LLU); // -.,;<>=!+*/%&|^~?:
1045 CYParser(FILE *file) :
1048 data_(reinterpret_cast<char *>(malloc(capacity_))),
1055 // XXX: this will not deconstruct in constructor failures
1061 data_[capacity_ - 1] = ~'\0';
1064 if (fgets(data_, capacity_, file_) == NULL)
1068 if (data_[capacity_ - 1] != '\0') {
1069 size_ = strlen(data_);
1072 } else if (data_[capacity_ - 2] == '\n')
1073 size_ = capacity_ - 2;
1075 size_t capacity(capacity_ * 2);
1076 char *data(reinterpret_cast<char *>(realloc(data_, capacity)));
1077 _assert(data != NULL);
1079 size_ = capacity_ - 1;
1080 capacity_ = capacity;
1081 fgets(data_ + size_, capacity_ - size_, file_);
1088 _finline void ScanRange(const CYRange &range) {
1089 while (range[data_[++offset_]]);
1092 CYToken *CYParseToken(apr_pool_t *pool, bool expecting) {
1096 if (offset_ == size_ && (!expecting || !ReadLine()))
1098 next = data_[offset_];
1099 if (next != ' ' && next != '\t')
1105 size_t index(offset_);
1107 if (WordStartRange_[next]) {
1108 ScanRange(WordEndRange_);
1110 } else if (next == '"' || next == '\'') {
1112 } else if (next == '.') {
1113 char after(data_[offset_ + 1]);
1114 if (after >= '0' && next <= '9')
1117 } else if (next >= '0' && next <= '9') {
1119 ScanRange(NumberRange_);
1120 type = CYTokenLiteral;
1121 } else if (PunctuationRange_[next]) {
1123 ScanRange(PunctuationRange_);
1124 type = CYTokenPunctuation;
1125 } else if (next == '(' || next == '{' || next == '[') {
1128 } else if (next == ')' || next == '}' || next == ']') {
1130 type = CYTokenClose;
1131 } else if (next == ';') {
1133 type = CYTokenSemiColon;
1138 CYToken *token(new(pool) CYToken());
1139 token->type_ = type;
1140 token->value_ = apr_pstrndup(pool, data_ + index, offset_ - index);
1145 void CYConsole(FILE *fin, FILE *fout, FILE *ferr) {
1148 __gnu_cxx::stdio_filebuf<char> bin(fin, std::ios::in);
1149 std::istream sin(&bin);
1152 fputs(">>> ", fout);
1155 if (!std::getline(sin, line))
1158 JSStringRef script(JSStringCreateWithUTF8CString(line.c_str()));
1160 JSContextRef context(JSGetContext());
1162 JSValueRef exception(NULL);
1163 JSValueRef result(JSEvaluateScript(context, script, NULL, NULL, 0, &exception));
1164 JSStringRelease(script);
1166 if (exception != NULL)
1169 if (!JSValueIsUndefined(context, result)) {
1173 json = JSValueToJSONCopy(context, result);
1174 } @catch (id error) {
1175 CYThrow(context, error, &result);
1179 fputs([reinterpret_cast<const NSString *>(json) UTF8String], fout);
1188 MSInitialize { _pooled
1191 NSCFBoolean_ = objc_getClass("NSCFBoolean");
1193 pid_t pid(getpid());
1195 struct sockaddr_in address;
1196 address.sin_len = sizeof(address);
1197 address.sin_family = AF_INET;
1198 address.sin_addr.s_addr = INADDR_ANY;
1199 address.sin_port = htons(10000 + pid);
1201 CFDataRef data(CFDataCreate(kCFAllocatorDefault, reinterpret_cast<UInt8 *>(&address), sizeof(address)));
1203 CFSocketSignature signature;
1204 signature.protocolFamily = AF_INET;
1205 signature.socketType = SOCK_STREAM;
1206 signature.protocol = IPPROTO_TCP;
1207 signature.address = data;
1209 CFSocketRef socket(CFSocketCreateWithSocketSignature(kCFAllocatorDefault, &signature, kCFSocketAcceptCallBack, &OnAccept, NULL));
1210 CFRunLoopAddSource(CFRunLoopGetCurrent(), CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket, 0), kCFRunLoopDefaultMode);
1212 JSClassDefinition definition;
1214 definition = kJSClassDefinitionEmpty;
1215 definition.className = "Pointer";
1216 definition.staticValues = Pointer_staticValues;
1217 definition.finalize = &Pointer_finalize;
1218 Pointer_ = JSClassCreate(&definition);
1220 definition = kJSClassDefinitionEmpty;
1221 definition.className = "Functor";
1222 definition.parentClass = Pointer_;
1223 definition.callAsFunction = &ffi_callAsFunction;
1224 Functor_ = JSClassCreate(&definition);
1226 definition = kJSClassDefinitionEmpty;
1227 definition.className = "Selector";
1228 definition.parentClass = Pointer_;
1229 Selector_ = JSClassCreate(&definition);
1231 definition = kJSClassDefinitionEmpty;
1232 definition.className = "Instance_";
1233 definition.getProperty = &Instance_getProperty;
1234 definition.callAsConstructor = &Instance_callAsConstructor;
1235 definition.finalize = &Instance_finalize;
1236 Instance_ = JSClassCreate(&definition);
1238 definition = kJSClassDefinitionEmpty;
1239 definition.getProperty = &Global_getProperty;
1240 JSClassRef Global(JSClassCreate(&definition));
1242 JSContextRef context(JSGlobalContextCreate(Global));
1245 JSObjectRef global(JSContextGetGlobalObject(context));
1247 CYSetProperty(context, global, "ffi", JSObjectMakeConstructor(context, Functor_, &ffi));
1249 CYSetProperty(context, global, "objc_msgSend", JSObjectMakeFunctionWithCallback(context, CYString("objc_msgSend"), &$objc_msgSend));
1251 Bridge_ = [[NSMutableDictionary dictionaryWithContentsOfFile:@"/usr/lib/libcycript.plist"] retain];
1253 name_ = JSStringCreateWithUTF8CString("name");
1254 message_ = JSStringCreateWithUTF8CString("message");
1255 length_ = JSStringCreateWithUTF8CString("length");
1257 JSValueRef exception(NULL);
1258 JSValueRef value(JSObjectGetProperty(JSGetContext(), global, CYString("Array"), &exception));
1259 CYThrow(context, exception);
1260 Array_ = JSValueToObject(JSGetContext(), value, &exception);
1261 CYThrow(context, exception);