]> git.saurik.com Git - cycript.git/blame - Library.mm
Use _pooled in CYConsole.
[cycript.git] / Library.mm
CommitLineData
62ca2b82 1/* Cyrker - Remove Execution Server and Disassembler
c1582939
JF
2 * Copyright (C) 2009 Jay Freeman (saurik)
3*/
4
62ca2b82 5/* Modified BSD License {{{ */
c1582939
JF
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*/
62ca2b82 38/* }}} */
c1582939 39
8d9b5eed
JF
40#define _GNU_SOURCE
41
c1582939 42#include <substrate.h>
04450da0 43#include "Struct.hpp"
c1582939 44
ea2d184c
JF
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
c1582939
JF
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
8d9b5eed
JF
72#include <iostream>
73#include <ext/stdio_filebuf.h>
74
ea2d184c
JF
75#undef _assert
76#undef _trace
77
c1582939 78#define _assert(test) do { \
f5e9be24
JF
79 if (!(test)) \
80 @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:[NSString stringWithFormat:@"_assert(%s):%s(%u):%s", #test, __FILE__, __LINE__, __FUNCTION__] userInfo:nil]; \
c1582939
JF
81} while (false)
82
83#define _trace() do { \
62ca2b82 84 CFLog(kCFLogLevelNotice, CFSTR("_trace():%u"), __LINE__); \
c1582939
JF
85} while (false)
86
7ba62cfd
JF
87/* Objective-C Handle<> {{{ */
88template <typename Type_>
89class _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/* }}} */
707bcb93 137/* APR Pool Helpers {{{ */
b21525c7
JF
138void *operator new(size_t size, apr_pool_t *pool) {
139 return apr_palloc(pool, size);
140}
141
142void *operator new [](size_t size, apr_pool_t *pool) {
143 return apr_palloc(pool, size);
144}
145
707bcb93
JF
146class 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
ea2d184c
JF
167static JSContextRef Context_;
168
88c977fa
JF
169static JSClassRef Functor_;
170static JSClassRef Instance_;
171static JSClassRef Pointer_;
172static JSClassRef Selector_;
ea2d184c 173
c1582939 174static JSObjectRef Array_;
ea2d184c 175
62ca2b82
JF
176static JSStringRef name_;
177static JSStringRef message_;
c1582939 178static JSStringRef length_;
ea2d184c 179
c1582939
JF
180static Class NSCFBoolean_;
181
88c977fa
JF
182static NSMutableDictionary *Bridge_;
183
c1582939
JF
184struct Client {
185 CFHTTPMessageRef message_;
186 CFSocketRef socket_;
187};
188
0c862573 189JSObjectRef CYMakeObject(JSContextRef context, id object) {
88c977fa 190 return JSObjectMake(context, Instance_, [object retain]);
0c862573
JF
191}
192
107e3ed0 193@interface NSMethodSignature (Cycript)
7ba62cfd
JF
194- (NSString *) _typeString;
195@end
196
107e3ed0 197@interface NSObject (Cycript)
c1582939 198- (NSString *) cy$toJSON;
ea2d184c 199- (JSValueRef) cy$JSValueInContext:(JSContextRef)context;
c1582939
JF
200@end
201
107e3ed0 202@interface NSString (Cycript)
88c977fa
JF
203- (void *) cy$symbol;
204@end
205
107e3ed0 206@interface NSNumber (Cycript)
88c977fa
JF
207- (void *) cy$symbol;
208@end
209
107e3ed0 210@implementation NSObject (Cycript)
62ca2b82 211
c1582939 212- (NSString *) cy$toJSON {
62ca2b82
JF
213 return [self description];
214}
215
ea2d184c 216- (JSValueRef) cy$JSValueInContext:(JSContextRef)context {
0c862573 217 return CYMakeObject(context, self);
ea2d184c
JF
218}
219
62ca2b82 220@end
c1582939 221
107e3ed0 222@implementation WebUndefined (Cycript)
62ca2b82 223
c1582939
JF
224- (NSString *) cy$toJSON {
225 return @"undefined";
62ca2b82
JF
226}
227
228- (JSValueRef) cy$JSValueInContext:(JSContextRef)context {
229 return JSValueMakeUndefined(context);
230}
231
232@end
c1582939 233
107e3ed0 234@implementation NSArray (Cycript)
62ca2b82 235
c1582939
JF
236- (NSString *) cy$toJSON {
237 NSMutableString *json([[[NSMutableString alloc] init] autorelease]);
238 [json appendString:@"["];
239
240 bool comma(false);
62ca2b82 241 for (id object in self) {
c1582939
JF
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;
62ca2b82
JF
251}
252
253@end
254
107e3ed0 255@implementation NSDictionary (Cycript)
62ca2b82
JF
256
257- (NSString *) cy$toJSON {
258 NSMutableString *json([[[NSMutableString alloc] init] autorelease]);
7ba62cfd
JF
259 [json appendString:@"("];
260 [json appendString:@"{"];
62ca2b82
JF
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
c1582939 279
107e3ed0 280@implementation NSNumber (Cycript)
62ca2b82 281
c1582939
JF
282- (NSString *) cy$toJSON {
283 return [self class] != NSCFBoolean_ ? [self stringValue] : [self boolValue] ? @"true" : @"false";
62ca2b82
JF
284}
285
286- (JSValueRef) cy$JSValueInContext:(JSContextRef)context {
287 return [self class] != NSCFBoolean_ ? JSValueMakeNumber(context, [self doubleValue]) : JSValueMakeBoolean(context, [self boolValue]);
288}
289
88c977fa
JF
290- (void *) cy$symbol {
291 return [self pointerValue];
292}
293
62ca2b82 294@end
c1582939 295
107e3ed0 296@implementation NSString (Cycript)
62ca2b82 297
c1582939
JF
298- (NSString *) cy$toJSON {
299 CFMutableStringRef json(CFStringCreateMutableCopy(kCFAllocatorDefault, 0, (CFStringRef) self));
300
c1582939
JF
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
62ca2b82
JF
310 return [reinterpret_cast<const NSString *>(json) autorelease];
311}
312
88c977fa
JF
313- (void *) cy$symbol {
314 return dlsym(RTLD_DEFAULT, [self UTF8String]);
315}
316
62ca2b82
JF
317@end
318
b21525c7 319@interface CYJSObject : NSDictionary {
62ca2b82
JF
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
c1582939 333
b21525c7 334@interface CYJSArray : NSArray {
c1582939
JF
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
62ca2b82 346JSContextRef JSGetContext() {
ea2d184c 347 return Context_;
62ca2b82
JF
348}
349
0c862573
JF
350#define CYCatch \
351 @catch (id error) { \
352 CYThrow(context, error, exception); \
353 return NULL; \
354 }
355
ea2d184c
JF
356void CYThrow(JSContextRef context, JSValueRef value);
357
b21525c7 358id CYCastNSObject(JSContextRef context, JSObjectRef object) {
88c977fa 359 if (JSValueIsObjectOfClass(context, object, Instance_))
c1582939 360 return reinterpret_cast<id>(JSObjectGetPrivate(object));
ea2d184c
JF
361 JSValueRef exception(NULL);
362 bool array(JSValueIsInstanceOfConstructor(context, object, Array_, &exception));
363 CYThrow(context, exception);
364 if (array)
b21525c7
JF
365 return [[[CYJSArray alloc] initWithJSObject:object inContext:context] autorelease];
366 return [[[CYJSObject alloc] initWithJSObject:object inContext:context] autorelease];
62ca2b82
JF
367}
368
77e87a6c
JF
369JSStringRef CYCopyJSString(id value) {
370 return JSStringCreateWithCFString(reinterpret_cast<CFStringRef>([value description]));
62ca2b82
JF
371}
372
77e87a6c
JF
373JSStringRef CYCopyJSString(const char *value) {
374 return JSStringCreateWithUTF8CString(value);
375}
376
377JSStringRef CYCopyJSString(JSStringRef value) {
378 return JSStringRetain(value);
379}
380
381JSStringRef CYCopyJSString(JSContextRef context, JSValueRef value) {
62ca2b82 382 JSValueRef exception(NULL);
ea2d184c
JF
383 JSStringRef string(JSValueToStringCopy(context, value, &exception));
384 CYThrow(context, exception);
77e87a6c
JF
385 return string;
386}
387
388// XXX: this is not a safe handle
389class 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
413CFStringRef CYCopyCFString(JSStringRef value) {
414 return JSStringCopyCFString(kCFAllocatorDefault, value);
415}
416
417CFStringRef CYCopyCFString(JSContextRef context, JSValueRef value) {
418 return CYCopyCFString(CYString(context, value));
c1582939
JF
419}
420
f610e1a0 421double CYCastDouble(JSContextRef context, JSValueRef value) {
0c862573
JF
422 JSValueRef exception(NULL);
423 double number(JSValueToNumber(context, value, &exception));
424 CYThrow(context, exception);
f610e1a0
JF
425 return number;
426}
427
428CFNumberRef CYCopyCFNumber(JSContextRef context, JSValueRef value) {
429 double number(CYCastDouble(context, value));
0c862573
JF
430 return CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &number);
431}
432
62ca2b82
JF
433NSString *CYCastNSString(JSStringRef value) {
434 return [reinterpret_cast<const NSString *>(CYCopyCFString(value)) autorelease];
435}
436
ea2d184c 437CFTypeRef CYCopyCFType(JSContextRef context, JSValueRef value) {
f610e1a0 438 switch (JSType type = JSValueGetType(context, value)) {
c1582939 439 case kJSTypeUndefined:
62ca2b82 440 return CFRetain([WebUndefined undefined]);
c1582939
JF
441 case kJSTypeNull:
442 return nil;
c1582939 443 case kJSTypeBoolean:
ea2d184c 444 return CFRetain(JSValueToBoolean(context, value) ? kCFBooleanTrue : kCFBooleanFalse);
0c862573
JF
445 case kJSTypeNumber:
446 return CYCopyCFNumber(context, value);
62ca2b82 447 case kJSTypeString:
ea2d184c 448 return CYCopyCFString(context, value);
c1582939 449 case kJSTypeObject:
b21525c7 450 return CFRetain((CFTypeRef) CYCastNSObject(context, (JSObjectRef) value));
c1582939 451 default:
f5e9be24 452 @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:[NSString stringWithFormat:@"JSValueGetType() == 0x%x", type] userInfo:nil];
c1582939
JF
453 }
454}
455
62ca2b82
JF
456NSArray *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
ea2d184c
JF
464id CYCastNSObject(JSContextRef context, JSValueRef value) {
465 const NSObject *object(reinterpret_cast<const NSObject *>(CYCopyCFType(context, value)));
c1582939
JF
466 return object == nil ? nil : [object autorelease];
467}
468
ea2d184c 469void CYThrow(JSContextRef context, JSValueRef value) {
62ca2b82
JF
470 if (value == NULL)
471 return;
ea2d184c 472 @throw CYCastNSObject(context, value);
62ca2b82
JF
473}
474
ea2d184c
JF
475JSValueRef CYCastJSValue(JSContextRef context, id value) {
476 return value == nil ? JSValueMakeNull(context) : [value cy$JSValueInContext:context];
62ca2b82
JF
477}
478
7ba62cfd
JF
479void CYThrow(JSContextRef context, id error, JSValueRef *exception) {
480 *exception = CYCastJSValue(context, error);
481}
482
b21525c7 483@implementation CYJSObject
62ca2b82
JF
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);
7ba62cfd 501 JSValueRef value(JSObjectGetProperty(context_, object_, CYString(key), &exception));
62ca2b82
JF
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);
7ba62cfd 515 JSObjectSetProperty(context_, object_, CYString(key), CYCastJSValue(context_, object), kJSPropertyAttributeNone, &exception);
62ca2b82
JF
516 CYThrow(context_, exception);
517}
518
519- (void) removeObjectForKey:(id)key {
520 JSValueRef exception(NULL);
f610e1a0 521 // XXX: this returns a bool... throw exception, or ignore?
7ba62cfd 522 JSObjectDeleteProperty(context_, object_, CYString(key), &exception);
62ca2b82
JF
523 CYThrow(context_, exception);
524}
525
526@end
527
b21525c7 528@implementation CYJSArray
c1582939
JF
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 {
62ca2b82
JF
538 JSValueRef exception(NULL);
539 JSValueRef value(JSObjectGetProperty(context_, object_, length_, &exception));
540 CYThrow(context_, exception);
f610e1a0 541 return CYCastDouble(context_, value);
c1582939
JF
542}
543
544- (id) objectAtIndex:(NSUInteger)index {
62ca2b82
JF
545 JSValueRef exception(NULL);
546 JSValueRef value(JSObjectGetPropertyAtIndex(context_, object_, index, &exception));
547 CYThrow(context_, exception);
548 id object(CYCastNSObject(context_, value));
c1582939
JF
549 return object == nil ? [NSNull null] : object;
550}
551
552@end
553
ea2d184c
JF
554CFStringRef JSValueToJSONCopy(JSContextRef context, JSValueRef value) {
555 id object(CYCastNSObject(context, value));
62ca2b82 556 return reinterpret_cast<CFStringRef>([(object == nil ? @"null" : [object cy$toJSON]) retain]);
c1582939
JF
557}
558
559static 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
62ca2b82 582 JSValueRef result(JSEvaluateScript(JSGetContext(), script, NULL, NULL, 0, NULL));
c1582939
JF
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
62ca2b82 588 CFStringRef json(JSValueToJSONCopy(JSGetContext(), result));
c1582939
JF
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
611static 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
f610e1a0
JF
632static 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
c1582939
JF
638}
639
ea2d184c
JF
640typedef id jocData;
641
88c977fa 642static JSObjectRef Instance_callAsConstructor(JSContextRef context, JSObjectRef object, size_t count, const JSValueRef arguments[], JSValueRef *exception) { _pooled
0c862573
JF
643 @try {
644 id data(reinterpret_cast<jocData>(JSObjectGetPrivate(object)));
645 return CYMakeObject(context, [[data alloc] autorelease]);
646 } CYCatch
647}
648
04450da0 649struct ptrData {
b21525c7 650 apr_pool_t *pool_;
04450da0 651 void *value_;
b21525c7 652 sig::Type type_;
77e87a6c
JF
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
668struct 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
680struct selData : ptrData {
681 selData(SEL value) :
682 ptrData(value)
683 {
684 }
04450da0
JF
685};
686
88c977fa 687static void Pointer_finalize(JSObjectRef object) {
04450da0 688 ptrData *data(reinterpret_cast<ptrData *>(JSObjectGetPrivate(object)));
b21525c7 689 apr_pool_destroy(data->pool_);
04450da0
JF
690}
691
88c977fa 692static void Instance_finalize(JSObjectRef object) {
ea2d184c
JF
693 id data(reinterpret_cast<jocData>(JSObjectGetPrivate(object)));
694 [data release];
695}
696
88c977fa
JF
697JSObjectRef CYMakeFunction(JSContextRef context, void (*function)(), const char *type) {
698 ffiData *data(new ffiData(function, type));
699 return JSObjectMake(context, Functor_, data);
700}
701
702
703JSObjectRef CYMakeFunction(JSContextRef context, void *function, const char *type) {
704 return CYMakeFunction(context, reinterpret_cast<void (*)()>(function), type);
705}
706
ea2d184c
JF
707void CYSetProperty(JSContextRef context, JSObjectRef object, const char *name, JSValueRef value) {
708 JSValueRef exception(NULL);
7ba62cfd 709 JSObjectSetProperty(context, object, CYString(name), value, kJSPropertyAttributeNone, &exception);
ea2d184c
JF
710 CYThrow(context, exception);
711}
712
7ba62cfd
JF
713char *CYPoolCString(apr_pool_t *pool, JSStringRef value) {
714 size_t size(JSStringGetMaximumUTF8CStringSize(value));
b21525c7 715 char *string(new(pool) char[size]);
7ba62cfd
JF
716 JSStringGetUTF8CString(value, string, size);
717 JSStringRelease(value);
718 return string;
719}
720
77e87a6c
JF
721char *CYPoolCString(apr_pool_t *pool, JSContextRef context, JSValueRef value) {
722 return CYPoolCString(pool, CYString(context, value));
723}
724
f610e1a0 725// XXX: this macro is unhygenic
b21525c7
JF
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
7ba62cfd
JF
737SEL CYCastSEL(JSContextRef context, JSValueRef value) {
738 if (JSValueIsNull(context, value))
739 return NULL;
88c977fa 740 else if (JSValueIsObjectOfClass(context, value, Selector_)) {
77e87a6c
JF
741 selData *data(reinterpret_cast<selData *>(JSObjectGetPrivate((JSObjectRef) value)));
742 return reinterpret_cast<SEL>(data->value_);
743 } else
b21525c7
JF
744 return sel_registerName(CYCastCString(context, value));
745}
746
747void *CYCastPointer(JSContextRef context, JSValueRef value) {
748 switch (JSValueGetType(context, value)) {
749 case kJSTypeNull:
750 return NULL;
b21525c7
JF
751 case kJSTypeString:
752 return dlsym(RTLD_DEFAULT, CYCastCString(context, value));
b21525c7 753 case kJSTypeObject:
88c977fa 754 if (JSValueIsObjectOfClass(context, value, Pointer_)) {
b21525c7
JF
755 ptrData *data(reinterpret_cast<ptrData *>(JSObjectGetPrivate((JSObjectRef) value)));
756 return data->value_;
757 }
758 default:
f610e1a0 759 return reinterpret_cast<void *>(static_cast<uintptr_t>(CYCastDouble(context, value)));
7ba62cfd
JF
760 }
761}
762
43cb3d68 763void CYPoolFFI(apr_pool_t *pool, JSContextRef context, sig::Type *type, void *data, JSValueRef value) {
ea2d184c
JF
764 switch (type->primitive) {
765 case sig::boolean_P:
766 *reinterpret_cast<bool *>(data) = JSValueToBoolean(context, value);
767 break;
768
43cb3d68 769#define CYPoolFFI_(primitive, native) \
f610e1a0
JF
770 case sig::primitive ## _P: \
771 *reinterpret_cast<native *>(data) = CYCastDouble(context, value); \
772 break;
ea2d184c 773
43cb3d68
JF
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)
ea2d184c
JF
786
787 case sig::object_P:
788 case sig::typename_P:
7ba62cfd
JF
789 *reinterpret_cast<id *>(data) = CYCastNSObject(context, value);
790 break;
791
ea2d184c 792 case sig::selector_P:
7ba62cfd
JF
793 *reinterpret_cast<SEL *>(data) = CYCastSEL(context, value);
794 break;
ea2d184c 795
b21525c7
JF
796 case sig::pointer_P:
797 *reinterpret_cast<void **>(data) = CYCastPointer(context, value);
798 break;
ea2d184c 799
77e87a6c
JF
800 case sig::string_P:
801 *reinterpret_cast<char **>(data) = CYPoolCString(pool, context, value);
802 break;
7ba62cfd 803
ea2d184c
JF
804 case sig::struct_P:
805 goto fail;
806
807 case sig::void_P:
808 break;
809
810 default: fail:
43cb3d68 811 NSLog(@"CYPoolFFI(%c)\n", type->primitive);
ea2d184c
JF
812 _assert(false);
813 }
814}
815
43cb3d68 816JSValueRef CYFromFFI(JSContextRef context, sig::Type *type, void *data) {
ea2d184c
JF
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
7ba62cfd 847 case sig::selector_P: {
77e87a6c
JF
848 if (SEL sel = *reinterpret_cast<SEL *>(data)) {
849 selData *data(new selData(sel));
88c977fa 850 value = JSObjectMake(context, Selector_, data);
f610e1a0 851 } else goto null;
7ba62cfd
JF
852 } break;
853
854 case sig::pointer_P: {
04450da0 855 if (void *pointer = *reinterpret_cast<void **>(data)) {
77e87a6c 856 ptrData *data(new ptrData(pointer));
88c977fa 857 value = JSObjectMake(context, Pointer_, data);
f610e1a0 858 } else goto null;
7ba62cfd 859 } break;
ea2d184c
JF
860
861 case sig::string_P: {
f610e1a0
JF
862 if (char *utf8 = *reinterpret_cast<char **>(data))
863 value = JSValueMakeString(context, CYString(utf8));
864 else goto null;
ea2d184c
JF
865 } break;
866
867 case sig::struct_P:
868 goto fail;
869
870 case sig::void_P:
f610e1a0
JF
871 value = JSValueMakeUndefined(context);
872 break;
873
874 null:
875 value = JSValueMakeNull(context);
ea2d184c
JF
876 break;
877
878 default: fail:
879 NSLog(@"CYFromFFI(%c)\n", type->primitive);
880 _assert(false);
881 }
882
883 return value;
884}
885
0c862573 886static JSValueRef CYCallFunction(JSContextRef context, size_t count, const JSValueRef *arguments, JSValueRef *exception, sig::Signature *signature, ffi_cif *cif, void (*function)()) { _pooled
7ba62cfd 887 @try {
85a33bf5 888 if (count != signature->count - 1)
f5e9be24 889 @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"incorrect number of arguments to ffi function" userInfo:nil];
85a33bf5 890
7ba62cfd
JF
891 CYPool pool;
892 void *values[count];
ea2d184c 893
7ba62cfd
JF
894 for (unsigned index(0); index != count; ++index) {
895 sig::Element *element(&signature->elements[index + 1]);
77e87a6c 896 // XXX: alignment?
b21525c7 897 values[index] = new(pool) uint8_t[cif->arg_types[index]->size];
43cb3d68 898 CYPoolFFI(pool, context, element->type, values[index], arguments[index]);
7ba62cfd 899 }
ea2d184c 900
7ba62cfd
JF
901 uint8_t value[cif->rtype->size];
902 ffi_call(cif, function, value, values);
903
43cb3d68 904 return CYFromFFI(context, signature->elements[0].type, value);
0c862573 905 } CYCatch
7ba62cfd 906}
ea2d184c 907
f610e1a0 908static JSValueRef Global_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { _pooled
707bcb93 909 @try {
f610e1a0
JF
910 NSString *name(CYCastNSString(property));
911 if (Class _class = NSClassFromString(name))
707bcb93 912 return CYMakeObject(context, _class);
f610e1a0 913 if (NSMutableArray *entry = [Bridge_ objectForKey:name])
707bcb93
JF
914 switch ([[entry objectAtIndex:0] intValue]) {
915 case 0:
916 return JSEvaluateScript(JSGetContext(), CYString([entry objectAtIndex:1]), NULL, NULL, 0, NULL);
917 case 1:
f610e1a0 918 return CYMakeFunction(context, [name cy$symbol], [[entry objectAtIndex:1] UTF8String]);
707bcb93
JF
919 case 2:
920 CYPool pool;
921 sig::Signature signature;
922 sig::Parse(pool, &signature, [[entry objectAtIndex:1] UTF8String]);
f610e1a0 923 return CYFromFFI(context, signature.elements[0].type, [name cy$symbol]);
707bcb93
JF
924 }
925 return NULL;
926 } CYCatch
927}
928
04450da0
JF
929bool 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
7ba62cfd 936static JSValueRef $objc_msgSend(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { _pooled
7ba62cfd 937 const char *type;
ea2d184c
JF
938
939 @try {
85a33bf5 940 if (count < 2)
f5e9be24 941 @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"too few arguments to objc_msgSend" userInfo:nil];
85a33bf5 942
7ba62cfd
JF
943 id self(CYCastNSObject(context, arguments[0]));
944 if (self == nil)
945 return JSValueMakeNull(context);
7ba62cfd 946
85a33bf5 947 SEL _cmd(CYCastSEL(context, arguments[1]));
7ba62cfd 948 NSMethodSignature *method([self methodSignatureForSelector:_cmd]);
85a33bf5 949 if (method == nil)
f5e9be24 950 @throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"unrecognized selector %s sent to object %p", sel_getName(_cmd), self] userInfo:nil];
7ba62cfd
JF
951
952 type = [[method _typeString] UTF8String];
0c862573 953 } CYCatch
ea2d184c 954
7ba62cfd
JF
955 CYPool pool;
956
957 sig::Signature signature;
958 sig::Parse(pool, &signature, type);
ea2d184c 959
7ba62cfd 960 ffi_cif cif;
b21525c7 961 sig::sig_ffi_cif(pool, &sig::ObjectiveC, &signature, &cif);
7ba62cfd 962
04450da0 963 void (*function)() = stret(cif.rtype) ? reinterpret_cast<void (*)()>(&objc_msgSend_stret) : reinterpret_cast<void (*)()>(&objc_msgSend);
7ba62cfd
JF
964 return CYCallFunction(context, count, arguments, exception, &signature, &cif, function);
965}
966
967static 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)));
77e87a6c 969 return CYCallFunction(context, count, arguments, exception, &data->signature_, &data->cif_, reinterpret_cast<void (*)()>(data->value_));
ea2d184c
JF
970}
971
b21525c7
JF
972JSObjectRef ffi(JSContextRef context, JSObjectRef object, size_t count, const JSValueRef arguments[], JSValueRef *exception) {
973 @try {
974 if (count != 2)
f5e9be24 975 @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"incorrect number of arguments to ffi constructor" userInfo:nil];
88c977fa 976 void *function(CYCastPointer(context, arguments[0]));
b21525c7
JF
977 const char *type(CYCastCString(context, arguments[1]));
978 return CYMakeFunction(context, function, type);
0c862573 979 } CYCatch
ea2d184c
JF
980}
981
f610e1a0 982JSValueRef Pointer_getProperty_value(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) {
04450da0
JF
983 ptrData *data(reinterpret_cast<ptrData *>(JSObjectGetPrivate(object)));
984 return JSValueMakeNumber(context, reinterpret_cast<uintptr_t>(data->value_));
985}
986
88c977fa
JF
987static JSStaticValue Pointer_staticValues[2] = {
988 {"value", &Pointer_getProperty_value, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete},
04450da0
JF
989 {NULL, NULL, NULL, 0}
990};
991
8d9b5eed
JF
992void CYConsole(FILE *fin, FILE *fout, FILE *ferr) {
993 std::string line;
994
995 __gnu_cxx::stdio_filebuf<char> bin(fin, std::ios::in);
996 std::istream sin(&bin);
997
fd985644 998 for (;;) { _pooled
8d9b5eed
JF
999 fputs(">>> ", fout);
1000 fflush(fout);
1001
1002 if (!std::getline(sin, line))
1003 break;
1004
1005 JSStringRef script(JSStringCreateWithUTF8CString(line.c_str()));
1006
1007 JSContextRef context(JSGetContext());
1008
1009 JSValueRef exception(NULL);
1010 JSValueRef result(JSEvaluateScript(context, script, NULL, NULL, 0, &exception));
1011 JSStringRelease(script);
1012
1013 if (exception != NULL)
1014 result = exception;
1015
1016 if (!JSValueIsUndefined(context, result)) {
1017 CFStringRef json;
1018
1019 @try { json:
1020 json = JSValueToJSONCopy(context, result);
1021 } @catch (id error) {
1022 CYThrow(context, error, &result);
1023 goto json;
1024 }
1025
1026 fputs([reinterpret_cast<const NSString *>(json) UTF8String], fout);
1027 CFRelease(json);
1028
1029 fputs("\n", fout);
1030 fflush(fout);
1031 }
8d9b5eed
JF
1032 }
1033}
1034
7ba62cfd 1035MSInitialize { _pooled
ea2d184c
JF
1036 apr_initialize();
1037
c1582939
JF
1038 NSCFBoolean_ = objc_getClass("NSCFBoolean");
1039
1040 pid_t pid(getpid());
1041
1042 struct sockaddr_in address;
1043 address.sin_len = sizeof(address);
1044 address.sin_family = AF_INET;
1045 address.sin_addr.s_addr = INADDR_ANY;
1046 address.sin_port = htons(10000 + pid);
1047
1048 CFDataRef data(CFDataCreate(kCFAllocatorDefault, reinterpret_cast<UInt8 *>(&address), sizeof(address)));
1049
1050 CFSocketSignature signature;
1051 signature.protocolFamily = AF_INET;
1052 signature.socketType = SOCK_STREAM;
1053 signature.protocol = IPPROTO_TCP;
1054 signature.address = data;
1055
1056 CFSocketRef socket(CFSocketCreateWithSocketSignature(kCFAllocatorDefault, &signature, kCFSocketAcceptCallBack, &OnAccept, NULL));
1057 CFRunLoopAddSource(CFRunLoopGetCurrent(), CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket, 0), kCFRunLoopDefaultMode);
1058
1059 JSClassDefinition definition;
1060
1061 definition = kJSClassDefinitionEmpty;
88c977fa
JF
1062 definition.className = "Pointer";
1063 definition.staticValues = Pointer_staticValues;
1064 definition.finalize = &Pointer_finalize;
1065 Pointer_ = JSClassCreate(&definition);
c1582939
JF
1066
1067 definition = kJSClassDefinitionEmpty;
88c977fa
JF
1068 definition.className = "Functor";
1069 definition.parentClass = Pointer_;
1070 definition.callAsFunction = &ffi_callAsFunction;
1071 Functor_ = JSClassCreate(&definition);
7ba62cfd 1072
77e87a6c 1073 definition = kJSClassDefinitionEmpty;
88c977fa
JF
1074 definition.className = "Selector";
1075 definition.parentClass = Pointer_;
1076 Selector_ = JSClassCreate(&definition);
77e87a6c 1077
7ba62cfd 1078 definition = kJSClassDefinitionEmpty;
88c977fa
JF
1079 definition.className = "Instance_";
1080 definition.getProperty = &Instance_getProperty;
1081 definition.callAsConstructor = &Instance_callAsConstructor;
1082 definition.finalize = &Instance_finalize;
1083 Instance_ = JSClassCreate(&definition);
7ba62cfd
JF
1084
1085 definition = kJSClassDefinitionEmpty;
88c977fa
JF
1086 definition.getProperty = &Global_getProperty;
1087 JSClassRef Global(JSClassCreate(&definition));
c1582939 1088
88c977fa 1089 JSContextRef context(JSGlobalContextCreate(Global));
ea2d184c
JF
1090 Context_ = context;
1091
1092 JSObjectRef global(JSContextGetGlobalObject(context));
c1582939 1093
88c977fa 1094 CYSetProperty(context, global, "ffi", JSObjectMakeConstructor(context, Functor_, &ffi));
7ba62cfd
JF
1095
1096 CYSetProperty(context, global, "objc_msgSend", JSObjectMakeFunctionWithCallback(context, CYString("objc_msgSend"), &$objc_msgSend));
1097
107e3ed0 1098 Bridge_ = [[NSMutableDictionary dictionaryWithContentsOfFile:@"/usr/lib/libcycript.plist"] retain];
c1582939 1099
62ca2b82
JF
1100 name_ = JSStringCreateWithUTF8CString("name");
1101 message_ = JSStringCreateWithUTF8CString("message");
c1582939
JF
1102 length_ = JSStringCreateWithUTF8CString("length");
1103
62ca2b82 1104 JSValueRef exception(NULL);
7ba62cfd 1105 JSValueRef value(JSObjectGetProperty(JSGetContext(), global, CYString("Array"), &exception));
ea2d184c 1106 CYThrow(context, exception);
62ca2b82 1107 Array_ = JSValueToObject(JSGetContext(), value, &exception);
ea2d184c 1108 CYThrow(context, exception);
c1582939 1109}