]> git.saurik.com Git - cycript.git/blob - Library.mm
bb424d47cbc4a0d62054633303f23ee220c58b56
[cycript.git] / Library.mm
1 /* Cyrker - Remove Execution Server and Disassembler
2 * Copyright (C) 2009 Jay Freeman (saurik)
3 */
4
5 /* Modified BSD License {{{ */
6 /*
7 * Redistribution and use in source and binary
8 * forms, with or without modification, are permitted
9 * provided that the following conditions are met:
10 *
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
18 * distribution.
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.
22 *
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.
37 */
38 /* }}} */
39
40 #define _GNU_SOURCE
41
42 #include <substrate.h>
43 #include "Struct.hpp"
44
45 #include "sig/parse.hpp"
46 #include "sig/ffi_type.hpp"
47
48 #include <apr-1/apr_pools.h>
49 #include <apr-1/apr_strings.h>
50
51 #include <unistd.h>
52
53 #include <CoreFoundation/CoreFoundation.h>
54 #include <CoreFoundation/CFLogUtilities.h>
55
56 #include <CFNetwork/CFNetwork.h>
57 #include <Foundation/Foundation.h>
58
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>
65
66 #include <WebKit/WebScriptObject.h>
67
68 #include <sys/types.h>
69 #include <sys/socket.h>
70 #include <netinet/in.h>
71
72 #include <iostream>
73 #include <ext/stdio_filebuf.h>
74
75 #undef _assert
76 #undef _trace
77
78 #define _assert(test) do { \
79 if (!(test)) \
80 @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:[NSString stringWithFormat:@"_assert(%s):%s(%u):%s", #test, __FILE__, __LINE__, __FUNCTION__] userInfo:nil]; \
81 } while (false)
82
83 #define _trace() do { \
84 CFLog(kCFLogLevelNotice, CFSTR("_trace():%u"), __LINE__); \
85 } while (false)
86
87 /* Objective-C Handle<> {{{ */
88 template <typename Type_>
89 class _H {
90 typedef _H<Type_> This_;
91
92 private:
93 Type_ *value_;
94
95 _finline void Retain_() {
96 if (value_ != nil)
97 [value_ retain];
98 }
99
100 _finline void Clear_() {
101 if (value_ != nil)
102 [value_ release];
103 }
104
105 public:
106 _finline _H(const This_ &rhs) :
107 value_(rhs.value_ == nil ? nil : [rhs.value_ retain])
108 {
109 }
110
111 _finline _H(Type_ *value = NULL, bool mended = false) :
112 value_(value)
113 {
114 if (!mended)
115 Retain_();
116 }
117
118 _finline ~_H() {
119 Clear_();
120 }
121
122 _finline operator Type_ *() const {
123 return value_;
124 }
125
126 _finline This_ &operator =(Type_ *value) {
127 if (value_ != value) {
128 Type_ *old(value_);
129 value_ = value;
130 Retain_();
131 if (old != nil)
132 [old release];
133 } return *this;
134 }
135 };
136 /* }}} */
137 /* APR Pool Helpers {{{ */
138 void *operator new(size_t size, apr_pool_t *pool) {
139 return apr_palloc(pool, size);
140 }
141
142 void *operator new [](size_t size, apr_pool_t *pool) {
143 return apr_palloc(pool, size);
144 }
145
146 class CYPool {
147 private:
148 apr_pool_t *pool_;
149
150 public:
151 CYPool() {
152 apr_pool_create(&pool_, NULL);
153 }
154
155 ~CYPool() {
156 apr_pool_destroy(pool_);
157 }
158
159 operator apr_pool_t *() const {
160 return pool_;
161 }
162 };
163 /* }}} */
164
165 #define _pooled _H<NSAutoreleasePool> _pool([[NSAutoreleasePool alloc] init], true);
166
167 static JSContextRef Context_;
168
169 static JSClassRef Functor_;
170 static JSClassRef Instance_;
171 static JSClassRef Pointer_;
172 static JSClassRef Selector_;
173
174 static JSObjectRef Array_;
175
176 static JSStringRef name_;
177 static JSStringRef message_;
178 static JSStringRef length_;
179
180 static Class NSCFBoolean_;
181
182 static NSMutableDictionary *Bridge_;
183
184 struct Client {
185 CFHTTPMessageRef message_;
186 CFSocketRef socket_;
187 };
188
189 JSObjectRef CYMakeObject(JSContextRef context, id object) {
190 return JSObjectMake(context, Instance_, [object retain]);
191 }
192
193 @interface NSMethodSignature (Cycript)
194 - (NSString *) _typeString;
195 @end
196
197 @interface NSObject (Cycript)
198 - (NSString *) cy$toJSON;
199 - (JSValueRef) cy$JSValueInContext:(JSContextRef)context;
200 @end
201
202 @interface NSString (Cycript)
203 - (void *) cy$symbol;
204 @end
205
206 @interface NSNumber (Cycript)
207 - (void *) cy$symbol;
208 @end
209
210 @implementation NSObject (Cycript)
211
212 - (NSString *) cy$toJSON {
213 return [self description];
214 }
215
216 - (JSValueRef) cy$JSValueInContext:(JSContextRef)context {
217 return CYMakeObject(context, self);
218 }
219
220 @end
221
222 @implementation WebUndefined (Cycript)
223
224 - (NSString *) cy$toJSON {
225 return @"undefined";
226 }
227
228 - (JSValueRef) cy$JSValueInContext:(JSContextRef)context {
229 return JSValueMakeUndefined(context);
230 }
231
232 @end
233
234 @implementation NSArray (Cycript)
235
236 - (NSString *) cy$toJSON {
237 NSMutableString *json([[[NSMutableString alloc] init] autorelease]);
238 [json appendString:@"["];
239
240 bool comma(false);
241 for (id object in self) {
242 if (comma)
243 [json appendString:@","];
244 else
245 comma = true;
246 [json appendString:[object cy$toJSON]];
247 }
248
249 [json appendString:@"]"];
250 return json;
251 }
252
253 @end
254
255 @implementation NSDictionary (Cycript)
256
257 - (NSString *) cy$toJSON {
258 NSMutableString *json([[[NSMutableString alloc] init] autorelease]);
259 [json appendString:@"("];
260 [json appendString:@"{"];
261
262 bool comma(false);
263 for (id key in self) {
264 if (comma)
265 [json appendString:@","];
266 else
267 comma = true;
268 [json appendString:[key cy$toJSON]];
269 [json appendString:@":"];
270 NSObject *object([self objectForKey:key]);
271 [json appendString:[object cy$toJSON]];
272 }
273
274 [json appendString:@"})"];
275 return json;
276 }
277
278 @end
279
280 @implementation NSNumber (Cycript)
281
282 - (NSString *) cy$toJSON {
283 return [self class] != NSCFBoolean_ ? [self stringValue] : [self boolValue] ? @"true" : @"false";
284 }
285
286 - (JSValueRef) cy$JSValueInContext:(JSContextRef)context {
287 return [self class] != NSCFBoolean_ ? JSValueMakeNumber(context, [self doubleValue]) : JSValueMakeBoolean(context, [self boolValue]);
288 }
289
290 - (void *) cy$symbol {
291 return [self pointerValue];
292 }
293
294 @end
295
296 @implementation NSString (Cycript)
297
298 - (NSString *) cy$toJSON {
299 CFMutableStringRef json(CFStringCreateMutableCopy(kCFAllocatorDefault, 0, (CFStringRef) self));
300
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);
306
307 CFStringInsert(json, 0, CFSTR("\""));
308 CFStringAppend(json, CFSTR("\""));
309
310 return [reinterpret_cast<const NSString *>(json) autorelease];
311 }
312
313 - (void *) cy$symbol {
314 return dlsym(RTLD_DEFAULT, [self UTF8String]);
315 }
316
317 @end
318
319 @interface CYJSObject : NSDictionary {
320 JSObjectRef object_;
321 JSContextRef context_;
322 }
323
324 - (id) initWithJSObject:(JSObjectRef)object inContext:(JSContextRef)context;
325
326 - (NSUInteger) count;
327 - (id) objectForKey:(id)key;
328 - (NSEnumerator *) keyEnumerator;
329 - (void) setObject:(id)object forKey:(id)key;
330 - (void) removeObjectForKey:(id)key;
331
332 @end
333
334 @interface CYJSArray : NSArray {
335 JSObjectRef object_;
336 JSContextRef context_;
337 }
338
339 - (id) initWithJSObject:(JSObjectRef)object inContext:(JSContextRef)context;
340
341 - (NSUInteger) count;
342 - (id) objectAtIndex:(NSUInteger)index;
343
344 @end
345
346 JSContextRef JSGetContext() {
347 return Context_;
348 }
349
350 #define CYCatch \
351 @catch (id error) { \
352 CYThrow(context, error, exception); \
353 return NULL; \
354 }
355
356 void CYThrow(JSContextRef context, JSValueRef value);
357
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);
364 if (array)
365 return [[[CYJSArray alloc] initWithJSObject:object inContext:context] autorelease];
366 return [[[CYJSObject alloc] initWithJSObject:object inContext:context] autorelease];
367 }
368
369 JSStringRef CYCopyJSString(id value) {
370 return JSStringCreateWithCFString(reinterpret_cast<CFStringRef>([value description]));
371 }
372
373 JSStringRef CYCopyJSString(const char *value) {
374 return JSStringCreateWithUTF8CString(value);
375 }
376
377 JSStringRef CYCopyJSString(JSStringRef value) {
378 return JSStringRetain(value);
379 }
380
381 JSStringRef CYCopyJSString(JSContextRef context, JSValueRef value) {
382 JSValueRef exception(NULL);
383 JSStringRef string(JSValueToStringCopy(context, value, &exception));
384 CYThrow(context, exception);
385 return string;
386 }
387
388 // XXX: this is not a safe handle
389 class CYString {
390 private:
391 JSStringRef string_;
392
393 public:
394 template <typename Arg0_>
395 CYString(Arg0_ arg0) {
396 string_ = CYCopyJSString(arg0);
397 }
398
399 template <typename Arg0_, typename Arg1_>
400 CYString(Arg0_ arg0, Arg1_ arg1) {
401 string_ = CYCopyJSString(arg0, arg1);
402 }
403
404 ~CYString() {
405 JSStringRelease(string_);
406 }
407
408 operator JSStringRef() const {
409 return string_;
410 }
411 };
412
413 CFStringRef CYCopyCFString(JSStringRef value) {
414 return JSStringCopyCFString(kCFAllocatorDefault, value);
415 }
416
417 CFStringRef CYCopyCFString(JSContextRef context, JSValueRef value) {
418 return CYCopyCFString(CYString(context, value));
419 }
420
421 double CYCastDouble(JSContextRef context, JSValueRef value) {
422 JSValueRef exception(NULL);
423 double number(JSValueToNumber(context, value, &exception));
424 CYThrow(context, exception);
425 return number;
426 }
427
428 CFNumberRef CYCopyCFNumber(JSContextRef context, JSValueRef value) {
429 double number(CYCastDouble(context, value));
430 return CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &number);
431 }
432
433 NSString *CYCastNSString(JSStringRef value) {
434 return [reinterpret_cast<const NSString *>(CYCopyCFString(value)) autorelease];
435 }
436
437 CFTypeRef CYCopyCFType(JSContextRef context, JSValueRef value) {
438 switch (JSType type = JSValueGetType(context, value)) {
439 case kJSTypeUndefined:
440 return CFRetain([WebUndefined undefined]);
441 case kJSTypeNull:
442 return nil;
443 case kJSTypeBoolean:
444 return CFRetain(JSValueToBoolean(context, value) ? kCFBooleanTrue : kCFBooleanFalse);
445 case kJSTypeNumber:
446 return CYCopyCFNumber(context, value);
447 case kJSTypeString:
448 return CYCopyCFString(context, value);
449 case kJSTypeObject:
450 return CFRetain((CFTypeRef) CYCastNSObject(context, (JSObjectRef) value));
451 default:
452 @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:[NSString stringWithFormat:@"JSValueGetType() == 0x%x", type] userInfo:nil];
453 }
454 }
455
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))];
461 return array;
462 }
463
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];
467 }
468
469 void CYThrow(JSContextRef context, JSValueRef value) {
470 if (value == NULL)
471 return;
472 @throw CYCastNSObject(context, value);
473 }
474
475 JSValueRef CYCastJSValue(JSContextRef context, id value) {
476 return value == nil ? JSValueMakeNull(context) : [value cy$JSValueInContext:context];
477 }
478
479 void CYThrow(JSContextRef context, id error, JSValueRef *exception) {
480 *exception = CYCastJSValue(context, error);
481 }
482
483 @implementation CYJSObject
484
485 - (id) initWithJSObject:(JSObjectRef)object inContext:(JSContextRef)context {
486 if ((self = [super init]) != nil) {
487 object_ = object;
488 context_ = context;
489 } return self;
490 }
491
492 - (NSUInteger) count {
493 JSPropertyNameArrayRef names(JSObjectCopyPropertyNames(context_, object_));
494 size_t size(JSPropertyNameArrayGetCount(names));
495 JSPropertyNameArrayRelease(names);
496 return size;
497 }
498
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);
504 }
505
506 - (NSEnumerator *) keyEnumerator {
507 JSPropertyNameArrayRef names(JSObjectCopyPropertyNames(context_, object_));
508 NSEnumerator *enumerator([CYCastNSArray(names) objectEnumerator]);
509 JSPropertyNameArrayRelease(names);
510 return enumerator;
511 }
512
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);
517 }
518
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);
524 }
525
526 @end
527
528 @implementation CYJSArray
529
530 - (id) initWithJSObject:(JSObjectRef)object inContext:(JSContextRef)context {
531 if ((self = [super init]) != nil) {
532 object_ = object;
533 context_ = context;
534 } return self;
535 }
536
537 - (NSUInteger) count {
538 JSValueRef exception(NULL);
539 JSValueRef value(JSObjectGetProperty(context_, object_, length_, &exception));
540 CYThrow(context_, exception);
541 return CYCastDouble(context_, value);
542 }
543
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;
550 }
551
552 @end
553
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]);
557 }
558
559 static void OnData(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *value, void *info) {
560 switch (type) {
561 case kCFSocketDataCallBack:
562 CFDataRef data(reinterpret_cast<CFDataRef>(value));
563 Client *client(reinterpret_cast<Client *>(info));
564
565 if (client->message_ == NULL)
566 client->message_ = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, TRUE);
567
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_));
572 Boolean absolute;
573 CFStringRef path(CFURLCopyStrictPath(url, &absolute));
574 CFRelease(client->message_);
575
576 CFStringRef code(CFURLCreateStringByReplacingPercentEscapes(kCFAllocatorDefault, path, CFSTR("")));
577 CFRelease(path);
578
579 JSStringRef script(JSStringCreateWithCFString(code));
580 CFRelease(code);
581
582 JSValueRef result(JSEvaluateScript(JSGetContext(), script, NULL, NULL, 0, NULL));
583 JSStringRelease(script);
584
585 CFHTTPMessageRef response(CFHTTPMessageCreateResponse(kCFAllocatorDefault, 200, NULL, kCFHTTPVersion1_1));
586 CFHTTPMessageSetHeaderFieldValue(response, CFSTR("Content-Type"), CFSTR("application/json; charset=utf-8"));
587
588 CFStringRef json(JSValueToJSONCopy(JSGetContext(), result));
589 CFDataRef body(CFStringCreateExternalRepresentation(kCFAllocatorDefault, json, kCFStringEncodingUTF8, NULL));
590 CFRelease(json);
591
592 CFStringRef length(CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%u"), CFDataGetLength(body)));
593 CFHTTPMessageSetHeaderFieldValue(response, CFSTR("Content-Length"), length);
594 CFRelease(length);
595
596 CFHTTPMessageSetBody(response, body);
597 CFRelease(body);
598
599 CFDataRef serialized(CFHTTPMessageCopySerializedMessage(response));
600 CFRelease(response);
601
602 CFSocketSendData(socket, NULL, serialized, 0);
603 CFRelease(serialized);
604
605 CFRelease(url);
606 }
607 break;
608 }
609 }
610
611 static void OnAccept(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *value, void *info) {
612 switch (type) {
613 case kCFSocketAcceptCallBack:
614 Client *client(new Client());
615
616 client->message_ = NULL;
617
618 CFSocketContext context;
619 context.version = 0;
620 context.info = client;
621 context.retain = NULL;
622 context.release = NULL;
623 context.copyDescription = NULL;
624
625 client->socket_ = CFSocketCreateWithNative(kCFAllocatorDefault, *reinterpret_cast<const CFSocketNativeHandle *>(value), kCFSocketDataCallBack, &OnData, &context);
626
627 CFRunLoopAddSource(CFRunLoopGetCurrent(), CFSocketCreateRunLoopSource(kCFAllocatorDefault, client->socket_, 0), kCFRunLoopDefaultMode);
628 break;
629 }
630 }
631
632 static JSValueRef Instance_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { _pooled
633 @try {
634 NSString *name(CYCastNSString(property));
635 NSLog(@"%@", name);
636 return NULL;
637 } CYCatch
638 }
639
640 typedef id jocData;
641
642 static JSObjectRef Instance_callAsConstructor(JSContextRef context, JSObjectRef object, size_t count, const JSValueRef arguments[], JSValueRef *exception) { _pooled
643 @try {
644 id data(reinterpret_cast<jocData>(JSObjectGetPrivate(object)));
645 return CYMakeObject(context, [[data alloc] autorelease]);
646 } CYCatch
647 }
648
649 struct ptrData {
650 apr_pool_t *pool_;
651 void *value_;
652 sig::Type type_;
653
654 void *operator new(size_t size) {
655 apr_pool_t *pool;
656 apr_pool_create(&pool, NULL);
657 void *data(apr_palloc(pool, size));
658 reinterpret_cast<ptrData *>(data)->pool_ = pool;
659 return data;;
660 }
661
662 ptrData(void *value) :
663 value_(value)
664 {
665 }
666 };
667
668 struct ffiData : ptrData {
669 sig::Signature signature_;
670 ffi_cif cif_;
671
672 ffiData(void (*value)(), const char *type) :
673 ptrData(reinterpret_cast<void *>(value))
674 {
675 sig::Parse(pool_, &signature_, type);
676 sig::sig_ffi_cif(pool_, &sig::ObjectiveC, &signature_, &cif_);
677 }
678 };
679
680 struct selData : ptrData {
681 selData(SEL value) :
682 ptrData(value)
683 {
684 }
685 };
686
687 static void Pointer_finalize(JSObjectRef object) {
688 ptrData *data(reinterpret_cast<ptrData *>(JSObjectGetPrivate(object)));
689 apr_pool_destroy(data->pool_);
690 }
691
692 static void Instance_finalize(JSObjectRef object) {
693 id data(reinterpret_cast<jocData>(JSObjectGetPrivate(object)));
694 [data release];
695 }
696
697 JSObjectRef CYMakeFunction(JSContextRef context, void (*function)(), const char *type) {
698 ffiData *data(new ffiData(function, type));
699 return JSObjectMake(context, Functor_, data);
700 }
701
702
703 JSObjectRef CYMakeFunction(JSContextRef context, void *function, const char *type) {
704 return CYMakeFunction(context, reinterpret_cast<void (*)()>(function), type);
705 }
706
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);
711 }
712
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);
718 return string;
719 }
720
721 char *CYPoolCString(apr_pool_t *pool, JSContextRef context, JSValueRef value) {
722 return CYPoolCString(pool, CYString(context, value));
723 }
724
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); \
734 utf8; \
735 })
736
737 SEL CYCastSEL(JSContextRef context, JSValueRef value) {
738 if (JSValueIsNull(context, value))
739 return NULL;
740 else if (JSValueIsObjectOfClass(context, value, Selector_)) {
741 selData *data(reinterpret_cast<selData *>(JSObjectGetPrivate((JSObjectRef) value)));
742 return reinterpret_cast<SEL>(data->value_);
743 } else
744 return sel_registerName(CYCastCString(context, value));
745 }
746
747 void *CYCastPointer(JSContextRef context, JSValueRef value) {
748 switch (JSValueGetType(context, value)) {
749 case kJSTypeNull:
750 return NULL;
751 case kJSTypeString:
752 return dlsym(RTLD_DEFAULT, CYCastCString(context, value));
753 case kJSTypeObject:
754 if (JSValueIsObjectOfClass(context, value, Pointer_)) {
755 ptrData *data(reinterpret_cast<ptrData *>(JSObjectGetPrivate((JSObjectRef) value)));
756 return data->value_;
757 }
758 default:
759 return reinterpret_cast<void *>(static_cast<uintptr_t>(CYCastDouble(context, value)));
760 }
761 }
762
763 void CYPoolFFI(apr_pool_t *pool, JSContextRef context, sig::Type *type, void *data, JSValueRef value) {
764 switch (type->primitive) {
765 case sig::boolean_P:
766 *reinterpret_cast<bool *>(data) = JSValueToBoolean(context, value);
767 break;
768
769 #define CYPoolFFI_(primitive, native) \
770 case sig::primitive ## _P: \
771 *reinterpret_cast<native *>(data) = CYCastDouble(context, value); \
772 break;
773
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)
781 CYPoolFFI_(int, int)
782 CYPoolFFI_(ulonglong, unsigned long long)
783 CYPoolFFI_(longlong, long long)
784 CYPoolFFI_(float, float)
785 CYPoolFFI_(double, double)
786
787 case sig::object_P:
788 case sig::typename_P:
789 *reinterpret_cast<id *>(data) = CYCastNSObject(context, value);
790 break;
791
792 case sig::selector_P:
793 *reinterpret_cast<SEL *>(data) = CYCastSEL(context, value);
794 break;
795
796 case sig::pointer_P:
797 *reinterpret_cast<void **>(data) = CYCastPointer(context, value);
798 break;
799
800 case sig::string_P:
801 *reinterpret_cast<char **>(data) = CYPoolCString(pool, context, value);
802 break;
803
804 case sig::struct_P:
805 goto fail;
806
807 case sig::void_P:
808 break;
809
810 default: fail:
811 NSLog(@"CYPoolFFI(%c)\n", type->primitive);
812 _assert(false);
813 }
814 }
815
816 JSValueRef CYFromFFI(JSContextRef context, sig::Type *type, void *data) {
817 JSValueRef value;
818
819 switch (type->primitive) {
820 case sig::boolean_P:
821 value = JSValueMakeBoolean(context, *reinterpret_cast<bool *>(data));
822 break;
823
824 #define CYFromFFI_(primitive, native) \
825 case sig::primitive ## _P: \
826 value = JSValueMakeNumber(context, *reinterpret_cast<native *>(data)); \
827 break;
828
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)
836 CYFromFFI_(int, int)
837 CYFromFFI_(ulonglong, unsigned long long)
838 CYFromFFI_(longlong, long long)
839 CYFromFFI_(float, float)
840 CYFromFFI_(double, double)
841
842 case sig::object_P:
843 case sig::typename_P: {
844 value = CYCastJSValue(context, *reinterpret_cast<id *>(data));
845 } break;
846
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);
851 } else goto null;
852 } break;
853
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);
858 } else goto null;
859 } break;
860
861 case sig::string_P: {
862 if (char *utf8 = *reinterpret_cast<char **>(data))
863 value = JSValueMakeString(context, CYString(utf8));
864 else goto null;
865 } break;
866
867 case sig::struct_P:
868 goto fail;
869
870 case sig::void_P:
871 value = JSValueMakeUndefined(context);
872 break;
873
874 null:
875 value = JSValueMakeNull(context);
876 break;
877
878 default: fail:
879 NSLog(@"CYFromFFI(%c)\n", type->primitive);
880 _assert(false);
881 }
882
883 return value;
884 }
885
886 static JSValueRef CYCallFunction(JSContextRef context, size_t count, const JSValueRef *arguments, JSValueRef *exception, sig::Signature *signature, ffi_cif *cif, void (*function)()) { _pooled
887 @try {
888 if (count != signature->count - 1)
889 @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"incorrect number of arguments to ffi function" userInfo:nil];
890
891 CYPool pool;
892 void *values[count];
893
894 for (unsigned index(0); index != count; ++index) {
895 sig::Element *element(&signature->elements[index + 1]);
896 // XXX: alignment?
897 values[index] = new(pool) uint8_t[cif->arg_types[index]->size];
898 CYPoolFFI(pool, context, element->type, values[index], arguments[index]);
899 }
900
901 uint8_t value[cif->rtype->size];
902 ffi_call(cif, function, value, values);
903
904 return CYFromFFI(context, signature->elements[0].type, value);
905 } CYCatch
906 }
907
908 static JSValueRef Global_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { _pooled
909 @try {
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]) {
915 case 0:
916 return JSEvaluateScript(JSGetContext(), CYString([entry objectAtIndex:1]), NULL, NULL, 0, NULL);
917 case 1:
918 return CYMakeFunction(context, [name cy$symbol], [[entry objectAtIndex:1] UTF8String]);
919 case 2:
920 CYPool pool;
921 sig::Signature signature;
922 sig::Parse(pool, &signature, [[entry objectAtIndex:1] UTF8String]);
923 return CYFromFFI(context, signature.elements[0].type, [name cy$symbol]);
924 }
925 return NULL;
926 } CYCatch
927 }
928
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
933 );
934 }
935
936 static JSValueRef $objc_msgSend(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { _pooled
937 const char *type;
938
939 @try {
940 if (count < 2)
941 @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"too few arguments to objc_msgSend" userInfo:nil];
942
943 id self(CYCastNSObject(context, arguments[0]));
944 if (self == nil)
945 return JSValueMakeNull(context);
946
947 SEL _cmd(CYCastSEL(context, arguments[1]));
948 NSMethodSignature *method([self methodSignatureForSelector:_cmd]);
949 if (method == nil)
950 @throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"unrecognized selector %s sent to object %p", sel_getName(_cmd), self] userInfo:nil];
951
952 type = [[method _typeString] UTF8String];
953 } CYCatch
954
955 CYPool pool;
956
957 sig::Signature signature;
958 sig::Parse(pool, &signature, type);
959
960 ffi_cif cif;
961 sig::sig_ffi_cif(pool, &sig::ObjectiveC, &signature, &cif);
962
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);
965 }
966
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_));
970 }
971
972 JSObjectRef ffi(JSContextRef context, JSObjectRef object, size_t count, const JSValueRef arguments[], JSValueRef *exception) {
973 @try {
974 if (count != 2)
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);
979 } CYCatch
980 }
981
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_));
985 }
986
987 static JSStaticValue Pointer_staticValues[2] = {
988 {"value", &Pointer_getProperty_value, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete},
989 {NULL, NULL, NULL, 0}
990 };
991
992 enum CYTokenType {
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,*/
998
999 CYTokenWord, CYTokenPunctuation, CYTokenLiteral,
1000 CYTokenSemiColon, CYTokenOpen, CYTokenClose,
1001 };
1002
1003 struct CYToken {
1004 enum CYTokenType type_;
1005 char *value_;
1006 };
1007
1008 struct CYExpression {
1009 };
1010
1011 struct CYRange {
1012 uint64_t lo_;
1013 uint64_t hi_;
1014
1015 CYRange(uint64_t lo, uint64_t hi) :
1016 lo_(lo), hi_(hi)
1017 {
1018 }
1019
1020 bool operator [](uint8_t value) const {
1021 return (value >> 7) && (value >> 6 ? hi_ : lo_) >> (value & 0x3f) & 0x1;
1022 }
1023
1024 void operator()(uint8_t value) {
1025 if (value >> 7)
1026 return;
1027 (value >> 6 ? hi_ : lo_) |= uint64_t(0x1) << (value & 0x3f);
1028 }
1029 };
1030
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); // -.,;<>=!+*/%&|^~?:
1035
1036 struct CYParser {
1037 FILE *file_;
1038
1039 size_t capacity_;
1040 char *data_;
1041
1042 size_t offset_;
1043 size_t size_;
1044
1045 CYParser(FILE *file) :
1046 file_(file),
1047 capacity_(1024),
1048 data_(reinterpret_cast<char *>(malloc(capacity_))),
1049 offset_(0),
1050 size_(0)
1051 {
1052 }
1053
1054 ~CYParser() {
1055 // XXX: this will not deconstruct in constructor failures
1056 free(data_);
1057 }
1058
1059 bool ReadLine() {
1060 offset_ = 0;
1061 data_[capacity_ - 1] = ~'\0';
1062
1063 start:
1064 if (fgets(data_, capacity_, file_) == NULL)
1065 return false;
1066
1067 check:
1068 if (data_[capacity_ - 1] != '\0') {
1069 size_ = strlen(data_);
1070 if (size_ == 0)
1071 goto start;
1072 } else if (data_[capacity_ - 2] == '\n')
1073 size_ = capacity_ - 2;
1074 else {
1075 size_t capacity(capacity_ * 2);
1076 char *data(reinterpret_cast<char *>(realloc(data_, capacity)));
1077 _assert(data != NULL);
1078 data_ = data;
1079 size_ = capacity_ - 1;
1080 capacity_ = capacity;
1081 fgets(data_ + size_, capacity_ - size_, file_);
1082 goto check;
1083 }
1084
1085 return true;
1086 }
1087
1088 _finline void ScanRange(const CYRange &range) {
1089 while (range[data_[++offset_]]);
1090 }
1091
1092 CYToken *CYParseToken(apr_pool_t *pool, bool expecting) {
1093 char next;
1094
1095 for (;;) {
1096 if (offset_ == size_ && (!expecting || !ReadLine()))
1097 return false;
1098 next = data_[offset_];
1099 if (next != ' ' && next != '\t')
1100 break;
1101 ++offset_;
1102 }
1103
1104 CYTokenType type;
1105 size_t index(offset_);
1106
1107 if (WordStartRange_[next]) {
1108 ScanRange(WordEndRange_);
1109 type = CYTokenWord;
1110 } else if (next == '"' || next == '\'') {
1111 _assert(false);
1112 } else if (next == '.') {
1113 char after(data_[offset_ + 1]);
1114 if (after >= '0' && next <= '9')
1115 goto number;
1116 goto punctuation;
1117 } else if (next >= '0' && next <= '9') {
1118 number:
1119 ScanRange(NumberRange_);
1120 type = CYTokenLiteral;
1121 } else if (PunctuationRange_[next]) {
1122 punctuation:
1123 ScanRange(PunctuationRange_);
1124 type = CYTokenPunctuation;
1125 } else if (next == '(' || next == '{' || next == '[') {
1126 ++offset_;
1127 type = CYTokenOpen;
1128 } else if (next == ')' || next == '}' || next == ']') {
1129 ++offset_;
1130 type = CYTokenClose;
1131 } else if (next == ';') {
1132 ++offset_;
1133 type = CYTokenSemiColon;
1134 } else {
1135 _assert(false);
1136 }
1137
1138 CYToken *token(new(pool) CYToken());
1139 token->type_ = type;
1140 token->value_ = apr_pstrndup(pool, data_ + index, offset_ - index);
1141 return token;
1142 }
1143 };
1144
1145 void CYConsole(FILE *fin, FILE *fout, FILE *ferr) {
1146 std::string line;
1147
1148 __gnu_cxx::stdio_filebuf<char> bin(fin, std::ios::in);
1149 std::istream sin(&bin);
1150
1151 for (;;) { _pooled
1152 fputs(">>> ", fout);
1153 fflush(fout);
1154
1155 if (!std::getline(sin, line))
1156 break;
1157
1158 JSStringRef script(JSStringCreateWithUTF8CString(line.c_str()));
1159
1160 JSContextRef context(JSGetContext());
1161
1162 JSValueRef exception(NULL);
1163 JSValueRef result(JSEvaluateScript(context, script, NULL, NULL, 0, &exception));
1164 JSStringRelease(script);
1165
1166 if (exception != NULL)
1167 result = exception;
1168
1169 if (!JSValueIsUndefined(context, result)) {
1170 CFStringRef json;
1171
1172 @try { json:
1173 json = JSValueToJSONCopy(context, result);
1174 } @catch (id error) {
1175 CYThrow(context, error, &result);
1176 goto json;
1177 }
1178
1179 fputs([reinterpret_cast<const NSString *>(json) UTF8String], fout);
1180 CFRelease(json);
1181
1182 fputs("\n", fout);
1183 fflush(fout);
1184 }
1185 }
1186 }
1187
1188 MSInitialize { _pooled
1189 apr_initialize();
1190
1191 NSCFBoolean_ = objc_getClass("NSCFBoolean");
1192
1193 pid_t pid(getpid());
1194
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);
1200
1201 CFDataRef data(CFDataCreate(kCFAllocatorDefault, reinterpret_cast<UInt8 *>(&address), sizeof(address)));
1202
1203 CFSocketSignature signature;
1204 signature.protocolFamily = AF_INET;
1205 signature.socketType = SOCK_STREAM;
1206 signature.protocol = IPPROTO_TCP;
1207 signature.address = data;
1208
1209 CFSocketRef socket(CFSocketCreateWithSocketSignature(kCFAllocatorDefault, &signature, kCFSocketAcceptCallBack, &OnAccept, NULL));
1210 CFRunLoopAddSource(CFRunLoopGetCurrent(), CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket, 0), kCFRunLoopDefaultMode);
1211
1212 JSClassDefinition definition;
1213
1214 definition = kJSClassDefinitionEmpty;
1215 definition.className = "Pointer";
1216 definition.staticValues = Pointer_staticValues;
1217 definition.finalize = &Pointer_finalize;
1218 Pointer_ = JSClassCreate(&definition);
1219
1220 definition = kJSClassDefinitionEmpty;
1221 definition.className = "Functor";
1222 definition.parentClass = Pointer_;
1223 definition.callAsFunction = &ffi_callAsFunction;
1224 Functor_ = JSClassCreate(&definition);
1225
1226 definition = kJSClassDefinitionEmpty;
1227 definition.className = "Selector";
1228 definition.parentClass = Pointer_;
1229 Selector_ = JSClassCreate(&definition);
1230
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);
1237
1238 definition = kJSClassDefinitionEmpty;
1239 definition.getProperty = &Global_getProperty;
1240 JSClassRef Global(JSClassCreate(&definition));
1241
1242 JSContextRef context(JSGlobalContextCreate(Global));
1243 Context_ = context;
1244
1245 JSObjectRef global(JSContextGetGlobalObject(context));
1246
1247 CYSetProperty(context, global, "ffi", JSObjectMakeConstructor(context, Functor_, &ffi));
1248
1249 CYSetProperty(context, global, "objc_msgSend", JSObjectMakeFunctionWithCallback(context, CYString("objc_msgSend"), &$objc_msgSend));
1250
1251 Bridge_ = [[NSMutableDictionary dictionaryWithContentsOfFile:@"/usr/lib/libcycript.plist"] retain];
1252
1253 name_ = JSStringCreateWithUTF8CString("name");
1254 message_ = JSStringCreateWithUTF8CString("message");
1255 length_ = JSStringCreateWithUTF8CString("length");
1256
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);
1262 }