]> git.saurik.com Git - cycript.git/blame - Tweak.mm
This is getting seriously hot.
[cycript.git] / Tweak.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
JF
39
40#include <substrate.h>
41
ea2d184c
JF
42#include "sig/parse.hpp"
43#include "sig/ffi_type.hpp"
44
45#include <apr-1/apr_pools.h>
46#include <apr-1/apr_strings.h>
47
c1582939
JF
48#include <unistd.h>
49
50#include <CoreFoundation/CoreFoundation.h>
51#include <CoreFoundation/CFLogUtilities.h>
52
53#include <CFNetwork/CFNetwork.h>
54#include <Foundation/Foundation.h>
55
56#include <JavaScriptCore/JSBase.h>
57#include <JavaScriptCore/JSValueRef.h>
58#include <JavaScriptCore/JSObjectRef.h>
59#include <JavaScriptCore/JSContextRef.h>
60#include <JavaScriptCore/JSStringRef.h>
61#include <JavaScriptCore/JSStringRefCF.h>
62
63#include <WebKit/WebScriptObject.h>
64
65#include <sys/types.h>
66#include <sys/socket.h>
67#include <netinet/in.h>
68
ea2d184c
JF
69#undef _assert
70#undef _trace
71
c1582939
JF
72/* XXX: bad _assert */
73#define _assert(test) do { \
74 if ((test)) break; \
62ca2b82
JF
75 CFLog(kCFLogLevelNotice, CFSTR("_assert(%s):%u"), #test, __LINE__); \
76 throw; \
c1582939
JF
77} while (false)
78
79#define _trace() do { \
62ca2b82 80 CFLog(kCFLogLevelNotice, CFSTR("_trace():%u"), __LINE__); \
c1582939
JF
81} while (false)
82
7ba62cfd
JF
83/* Objective-C Handle<> {{{ */
84template <typename Type_>
85class _H {
86 typedef _H<Type_> This_;
87
88 private:
89 Type_ *value_;
90
91 _finline void Retain_() {
92 if (value_ != nil)
93 [value_ retain];
94 }
95
96 _finline void Clear_() {
97 if (value_ != nil)
98 [value_ release];
99 }
100
101 public:
102 _finline _H(const This_ &rhs) :
103 value_(rhs.value_ == nil ? nil : [rhs.value_ retain])
104 {
105 }
106
107 _finline _H(Type_ *value = NULL, bool mended = false) :
108 value_(value)
109 {
110 if (!mended)
111 Retain_();
112 }
113
114 _finline ~_H() {
115 Clear_();
116 }
117
118 _finline operator Type_ *() const {
119 return value_;
120 }
121
122 _finline This_ &operator =(Type_ *value) {
123 if (value_ != value) {
124 Type_ *old(value_);
125 value_ = value;
126 Retain_();
127 if (old != nil)
128 [old release];
129 } return *this;
130 }
131};
132/* }}} */
133
134#define _pooled _H<NSAutoreleasePool> _pool([[NSAutoreleasePool alloc] init], true);
135
ea2d184c
JF
136static JSContextRef Context_;
137
138static JSClassRef ffi_;
c1582939 139static JSClassRef joc_;
7ba62cfd
JF
140static JSClassRef ptr_;
141static JSClassRef sel_;
ea2d184c 142
c1582939 143static JSObjectRef Array_;
ea2d184c 144
62ca2b82
JF
145static JSStringRef name_;
146static JSStringRef message_;
c1582939 147static JSStringRef length_;
ea2d184c 148
c1582939
JF
149static Class NSCFBoolean_;
150
151struct Client {
152 CFHTTPMessageRef message_;
153 CFSocketRef socket_;
154};
155
7ba62cfd
JF
156@interface NSMethodSignature (Cyrver)
157- (NSString *) _typeString;
158@end
159
c1582939
JF
160@interface NSObject (Cyrver)
161- (NSString *) cy$toJSON;
ea2d184c 162- (JSValueRef) cy$JSValueInContext:(JSContextRef)context;
c1582939
JF
163@end
164
165@implementation NSObject (Cyrver)
62ca2b82 166
c1582939 167- (NSString *) cy$toJSON {
62ca2b82
JF
168 return [self description];
169}
170
ea2d184c
JF
171- (JSValueRef) cy$JSValueInContext:(JSContextRef)context {
172 return JSObjectMake(context, joc_, [self retain]);
173}
174
62ca2b82 175@end
c1582939
JF
176
177@implementation WebUndefined (Cyrver)
62ca2b82 178
c1582939
JF
179- (NSString *) cy$toJSON {
180 return @"undefined";
62ca2b82
JF
181}
182
183- (JSValueRef) cy$JSValueInContext:(JSContextRef)context {
184 return JSValueMakeUndefined(context);
185}
186
187@end
c1582939
JF
188
189@implementation NSArray (Cyrver)
62ca2b82 190
c1582939
JF
191- (NSString *) cy$toJSON {
192 NSMutableString *json([[[NSMutableString alloc] init] autorelease]);
193 [json appendString:@"["];
194
195 bool comma(false);
62ca2b82 196 for (id object in self) {
c1582939
JF
197 if (comma)
198 [json appendString:@","];
199 else
200 comma = true;
201 [json appendString:[object cy$toJSON]];
202 }
203
204 [json appendString:@"]"];
205 return json;
62ca2b82
JF
206}
207
208@end
209
210@implementation NSDictionary (Cyrver)
211
212- (NSString *) cy$toJSON {
213 NSMutableString *json([[[NSMutableString alloc] init] autorelease]);
7ba62cfd
JF
214 [json appendString:@"("];
215 [json appendString:@"{"];
62ca2b82
JF
216
217 bool comma(false);
218 for (id key in self) {
219 if (comma)
220 [json appendString:@","];
221 else
222 comma = true;
223 [json appendString:[key cy$toJSON]];
224 [json appendString:@":"];
225 NSObject *object([self objectForKey:key]);
226 [json appendString:[object cy$toJSON]];
227 }
228
229 [json appendString:@"})"];
230 return json;
231}
232
233@end
c1582939
JF
234
235@implementation NSNumber (Cyrver)
62ca2b82 236
c1582939
JF
237- (NSString *) cy$toJSON {
238 return [self class] != NSCFBoolean_ ? [self stringValue] : [self boolValue] ? @"true" : @"false";
62ca2b82
JF
239}
240
241- (JSValueRef) cy$JSValueInContext:(JSContextRef)context {
242 return [self class] != NSCFBoolean_ ? JSValueMakeNumber(context, [self doubleValue]) : JSValueMakeBoolean(context, [self boolValue]);
243}
244
245@end
c1582939
JF
246
247@implementation NSString (Cyrver)
62ca2b82 248
c1582939
JF
249- (NSString *) cy$toJSON {
250 CFMutableStringRef json(CFStringCreateMutableCopy(kCFAllocatorDefault, 0, (CFStringRef) self));
251
c1582939
JF
252 CFStringFindAndReplace(json, CFSTR("\\"), CFSTR("\\\\"), CFRangeMake(0, CFStringGetLength(json)), 0);
253 CFStringFindAndReplace(json, CFSTR("\""), CFSTR("\\\""), CFRangeMake(0, CFStringGetLength(json)), 0);
254 CFStringFindAndReplace(json, CFSTR("\t"), CFSTR("\\t"), CFRangeMake(0, CFStringGetLength(json)), 0);
255 CFStringFindAndReplace(json, CFSTR("\r"), CFSTR("\\r"), CFRangeMake(0, CFStringGetLength(json)), 0);
256 CFStringFindAndReplace(json, CFSTR("\n"), CFSTR("\\n"), CFRangeMake(0, CFStringGetLength(json)), 0);
257
258 CFStringInsert(json, 0, CFSTR("\""));
259 CFStringAppend(json, CFSTR("\""));
260
62ca2b82
JF
261 return [reinterpret_cast<const NSString *>(json) autorelease];
262}
263
264@end
265
266@interface CY$JSObject : NSDictionary {
267 JSObjectRef object_;
268 JSContextRef context_;
269}
270
271- (id) initWithJSObject:(JSObjectRef)object inContext:(JSContextRef)context;
272
273- (NSUInteger) count;
274- (id) objectForKey:(id)key;
275- (NSEnumerator *) keyEnumerator;
276- (void) setObject:(id)object forKey:(id)key;
277- (void) removeObjectForKey:(id)key;
278
279@end
c1582939
JF
280
281@interface CY$JSArray : NSArray {
282 JSObjectRef object_;
283 JSContextRef context_;
284}
285
286- (id) initWithJSObject:(JSObjectRef)object inContext:(JSContextRef)context;
287
288- (NSUInteger) count;
289- (id) objectAtIndex:(NSUInteger)index;
290
291@end
292
62ca2b82 293JSContextRef JSGetContext() {
ea2d184c 294 return Context_;
62ca2b82
JF
295}
296
ea2d184c
JF
297void CYThrow(JSContextRef context, JSValueRef value);
298
299id JSObjectToNSObject(JSContextRef context, JSObjectRef object) {
300 if (JSValueIsObjectOfClass(context, object, joc_))
c1582939 301 return reinterpret_cast<id>(JSObjectGetPrivate(object));
ea2d184c
JF
302 JSValueRef exception(NULL);
303 bool array(JSValueIsInstanceOfConstructor(context, object, Array_, &exception));
304 CYThrow(context, exception);
305 if (array)
306 return [[[CY$JSArray alloc] initWithJSObject:object inContext:context] autorelease];
307 return [[[CY$JSObject alloc] initWithJSObject:object inContext:context] autorelease];
62ca2b82
JF
308}
309
310CFStringRef CYCopyCFString(JSStringRef value) {
311 return JSStringCopyCFString(kCFAllocatorDefault, value);
312}
313
ea2d184c 314CFStringRef CYCopyCFString(JSContextRef context, JSValueRef value) {
62ca2b82 315 JSValueRef exception(NULL);
ea2d184c
JF
316 JSStringRef string(JSValueToStringCopy(context, value, &exception));
317 CYThrow(context, exception);
62ca2b82
JF
318 CFStringRef object(CYCopyCFString(string));
319 JSStringRelease(string);
320 return object;
c1582939
JF
321}
322
62ca2b82
JF
323NSString *CYCastNSString(JSStringRef value) {
324 return [reinterpret_cast<const NSString *>(CYCopyCFString(value)) autorelease];
325}
326
ea2d184c
JF
327CFTypeRef CYCopyCFType(JSContextRef context, JSValueRef value) {
328 JSType type(JSValueGetType(context, value));
c1582939
JF
329
330 switch (type) {
331 case kJSTypeUndefined:
62ca2b82 332 return CFRetain([WebUndefined undefined]);
c1582939
JF
333 break;
334
335 case kJSTypeNull:
336 return nil;
337 break;
338
339 case kJSTypeBoolean:
ea2d184c 340 return CFRetain(JSValueToBoolean(context, value) ? kCFBooleanTrue : kCFBooleanFalse);
c1582939
JF
341 break;
342
343 case kJSTypeNumber: {
62ca2b82 344 JSValueRef exception(NULL);
ea2d184c
JF
345 double number(JSValueToNumber(context, value, &exception));
346 CYThrow(context, exception);
c1582939
JF
347 return CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &number);
348 } break;
349
62ca2b82 350 case kJSTypeString:
ea2d184c 351 return CYCopyCFString(context, value);
62ca2b82 352 break;
c1582939
JF
353
354 case kJSTypeObject:
ea2d184c 355 return CFRetain((CFTypeRef) JSObjectToNSObject(context, (JSObjectRef) value));
c1582939
JF
356 break;
357
358 default:
359 _assert(false);
c1582939
JF
360 break;
361 }
362}
363
62ca2b82
JF
364NSArray *CYCastNSArray(JSPropertyNameArrayRef names) {
365 size_t size(JSPropertyNameArrayGetCount(names));
366 NSMutableArray *array([NSMutableArray arrayWithCapacity:size]);
367 for (size_t index(0); index != size; ++index)
368 [array addObject:CYCastNSString(JSPropertyNameArrayGetNameAtIndex(names, index))];
369 return array;
370}
371
ea2d184c
JF
372id CYCastNSObject(JSContextRef context, JSValueRef value) {
373 const NSObject *object(reinterpret_cast<const NSObject *>(CYCopyCFType(context, value)));
c1582939
JF
374 return object == nil ? nil : [object autorelease];
375}
376
ea2d184c 377void CYThrow(JSContextRef context, JSValueRef value) {
62ca2b82
JF
378 if (value == NULL)
379 return;
ea2d184c 380 @throw CYCastNSObject(context, value);
62ca2b82
JF
381}
382
ea2d184c
JF
383JSValueRef CYCastJSValue(JSContextRef context, id value) {
384 return value == nil ? JSValueMakeNull(context) : [value cy$JSValueInContext:context];
62ca2b82
JF
385}
386
ea2d184c 387JSStringRef CYCopyJSString(id value) {
62ca2b82
JF
388 return JSStringCreateWithCFString(reinterpret_cast<CFStringRef>([value description]));
389}
390
ea2d184c
JF
391JSStringRef CYCopyJSString(const char *value) {
392 return JSStringCreateWithUTF8CString(value);
393}
394
7ba62cfd
JF
395JSStringRef CYCopyJSString(JSStringRef value) {
396 return JSStringRetain(value);
397}
398
399// XXX: this is not a safe handle
400class CYString {
401 private:
402 JSStringRef string_;
403
404 public:
405 template <typename Type_>
406 CYString(Type_ value) {
407 string_ = CYCopyJSString(value);
408 }
409
410 ~CYString() {
411 JSStringRelease(string_);
412 }
413
414 operator JSStringRef() const {
415 return string_;
416 }
417};
418
419void CYThrow(JSContextRef context, id error, JSValueRef *exception) {
420 *exception = CYCastJSValue(context, error);
421}
422
62ca2b82
JF
423@implementation CY$JSObject
424
425- (id) initWithJSObject:(JSObjectRef)object inContext:(JSContextRef)context {
426 if ((self = [super init]) != nil) {
427 object_ = object;
428 context_ = context;
429 } return self;
430}
431
432- (NSUInteger) count {
433 JSPropertyNameArrayRef names(JSObjectCopyPropertyNames(context_, object_));
434 size_t size(JSPropertyNameArrayGetCount(names));
435 JSPropertyNameArrayRelease(names);
436 return size;
437}
438
439- (id) objectForKey:(id)key {
440 JSValueRef exception(NULL);
7ba62cfd 441 JSValueRef value(JSObjectGetProperty(context_, object_, CYString(key), &exception));
62ca2b82
JF
442 CYThrow(context_, exception);
443 return CYCastNSObject(context_, value);
444}
445
446- (NSEnumerator *) keyEnumerator {
447 JSPropertyNameArrayRef names(JSObjectCopyPropertyNames(context_, object_));
448 NSEnumerator *enumerator([CYCastNSArray(names) objectEnumerator]);
449 JSPropertyNameArrayRelease(names);
450 return enumerator;
451}
452
453- (void) setObject:(id)object forKey:(id)key {
454 JSValueRef exception(NULL);
7ba62cfd 455 JSObjectSetProperty(context_, object_, CYString(key), CYCastJSValue(context_, object), kJSPropertyAttributeNone, &exception);
62ca2b82
JF
456 CYThrow(context_, exception);
457}
458
459- (void) removeObjectForKey:(id)key {
460 JSValueRef exception(NULL);
62ca2b82 461 // XXX: this returns a bool
7ba62cfd 462 JSObjectDeleteProperty(context_, object_, CYString(key), &exception);
62ca2b82
JF
463 CYThrow(context_, exception);
464}
465
466@end
467
c1582939
JF
468@implementation CY$JSArray
469
470- (id) initWithJSObject:(JSObjectRef)object inContext:(JSContextRef)context {
471 if ((self = [super init]) != nil) {
472 object_ = object;
473 context_ = context;
474 } return self;
475}
476
477- (NSUInteger) count {
62ca2b82
JF
478 JSValueRef exception(NULL);
479 JSValueRef value(JSObjectGetProperty(context_, object_, length_, &exception));
480 CYThrow(context_, exception);
481 double number(JSValueToNumber(context_, value, &exception));
482 CYThrow(context_, exception);
483 return number;
c1582939
JF
484}
485
486- (id) objectAtIndex:(NSUInteger)index {
62ca2b82
JF
487 JSValueRef exception(NULL);
488 JSValueRef value(JSObjectGetPropertyAtIndex(context_, object_, index, &exception));
489 CYThrow(context_, exception);
490 id object(CYCastNSObject(context_, value));
c1582939
JF
491 return object == nil ? [NSNull null] : object;
492}
493
494@end
495
ea2d184c
JF
496CFStringRef JSValueToJSONCopy(JSContextRef context, JSValueRef value) {
497 id object(CYCastNSObject(context, value));
62ca2b82 498 return reinterpret_cast<CFStringRef>([(object == nil ? @"null" : [object cy$toJSON]) retain]);
c1582939
JF
499}
500
501static void OnData(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *value, void *info) {
502 switch (type) {
503 case kCFSocketDataCallBack:
504 CFDataRef data(reinterpret_cast<CFDataRef>(value));
505 Client *client(reinterpret_cast<Client *>(info));
506
507 if (client->message_ == NULL)
508 client->message_ = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, TRUE);
509
510 if (!CFHTTPMessageAppendBytes(client->message_, CFDataGetBytePtr(data), CFDataGetLength(data)))
511 CFLog(kCFLogLevelError, CFSTR("CFHTTPMessageAppendBytes()"));
512 else if (CFHTTPMessageIsHeaderComplete(client->message_)) {
513 CFURLRef url(CFHTTPMessageCopyRequestURL(client->message_));
514 Boolean absolute;
515 CFStringRef path(CFURLCopyStrictPath(url, &absolute));
516 CFRelease(client->message_);
517
518 CFStringRef code(CFURLCreateStringByReplacingPercentEscapes(kCFAllocatorDefault, path, CFSTR("")));
519 CFRelease(path);
520
521 JSStringRef script(JSStringCreateWithCFString(code));
522 CFRelease(code);
523
62ca2b82 524 JSValueRef result(JSEvaluateScript(JSGetContext(), script, NULL, NULL, 0, NULL));
c1582939
JF
525 JSStringRelease(script);
526
527 CFHTTPMessageRef response(CFHTTPMessageCreateResponse(kCFAllocatorDefault, 200, NULL, kCFHTTPVersion1_1));
528 CFHTTPMessageSetHeaderFieldValue(response, CFSTR("Content-Type"), CFSTR("application/json; charset=utf-8"));
529
62ca2b82 530 CFStringRef json(JSValueToJSONCopy(JSGetContext(), result));
c1582939
JF
531 CFDataRef body(CFStringCreateExternalRepresentation(kCFAllocatorDefault, json, kCFStringEncodingUTF8, NULL));
532 CFRelease(json);
533
534 CFStringRef length(CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%u"), CFDataGetLength(body)));
535 CFHTTPMessageSetHeaderFieldValue(response, CFSTR("Content-Length"), length);
536 CFRelease(length);
537
538 CFHTTPMessageSetBody(response, body);
539 CFRelease(body);
540
541 CFDataRef serialized(CFHTTPMessageCopySerializedMessage(response));
542 CFRelease(response);
543
544 CFSocketSendData(socket, NULL, serialized, 0);
545 CFRelease(serialized);
546
547 CFRelease(url);
548 }
549 break;
550 }
551}
552
553static void OnAccept(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *value, void *info) {
554 switch (type) {
555 case kCFSocketAcceptCallBack:
556 Client *client(new Client());
557
558 client->message_ = NULL;
559
560 CFSocketContext context;
561 context.version = 0;
562 context.info = client;
563 context.retain = NULL;
564 context.release = NULL;
565 context.copyDescription = NULL;
566
567 client->socket_ = CFSocketCreateWithNative(kCFAllocatorDefault, *reinterpret_cast<const CFSocketNativeHandle *>(value), kCFSocketDataCallBack, &OnData, &context);
568
569 CFRunLoopAddSource(CFRunLoopGetCurrent(), CFSocketCreateRunLoopSource(kCFAllocatorDefault, client->socket_, 0), kCFRunLoopDefaultMode);
570 break;
571 }
572}
573
ea2d184c 574static JSValueRef joc_getProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef *exception) {
c1582939
JF
575 return NULL;
576}
577
ea2d184c
JF
578typedef id jocData;
579
580static void joc_finalize(JSObjectRef object) {
581 id data(reinterpret_cast<jocData>(JSObjectGetPrivate(object)));
582 [data release];
583}
584
585static JSValueRef obc_getProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef *exception) {
c1582939
JF
586 NSString *name([(NSString *) JSStringCopyCFString(kCFAllocatorDefault, propertyName) autorelease]);
587 if (Class _class = NSClassFromString(name))
ea2d184c 588 return JSObjectMake(context, joc_, [_class retain]);
c1582939
JF
589 return NULL;
590}
591
ea2d184c
JF
592void CYSetProperty(JSContextRef context, JSObjectRef object, const char *name, JSValueRef value) {
593 JSValueRef exception(NULL);
7ba62cfd 594 JSObjectSetProperty(context, object, CYString(name), value, kJSPropertyAttributeNone, &exception);
ea2d184c
JF
595 CYThrow(context, exception);
596}
597
598struct ffiData {
599 apr_pool_t *pool_;
600 void (*function_)();
601 const char *type_;
602 sig::Signature signature_;
603 ffi_cif cif_;
604};
605
7ba62cfd
JF
606char *CYPoolCString(apr_pool_t *pool, JSStringRef value) {
607 size_t size(JSStringGetMaximumUTF8CStringSize(value));
608 char *string(reinterpret_cast<char *>(apr_palloc(pool, size)));
609 JSStringGetUTF8CString(value, string, size);
610 JSStringRelease(value);
611 return string;
612}
613
614SEL CYCastSEL(JSContextRef context, JSValueRef value) {
615 if (JSValueIsNull(context, value))
616 return NULL;
617 else if (JSValueIsObjectOfClass(context, value, sel_))
618 return reinterpret_cast<SEL>(JSObjectGetPrivate((JSObjectRef) value));
619 else {
620 JSValueRef exception(NULL);
621 JSStringRef string(JSValueToStringCopy(context, value, &exception));
622 CYThrow(context, exception);
623 size_t size(JSStringGetMaximumUTF8CStringSize(string));
624 char utf8[size];
625 JSStringGetUTF8CString(string, utf8, size);
626 JSStringRelease(string);
627 return sel_registerName(utf8);
628 }
629}
630
631void CYToFFI(apr_pool_t *pool, JSContextRef context, sig::Type *type, void *data, JSValueRef value) {
ea2d184c
JF
632 switch (type->primitive) {
633 case sig::boolean_P:
634 *reinterpret_cast<bool *>(data) = JSValueToBoolean(context, value);
635 break;
636
637#define CYToFFI_(primitive, native) \
638 case sig::primitive ## _P: { \
7ba62cfd
JF
639 JSValueRef exception(NULL); \
640 double number(JSValueToNumber(context, value, &exception)); \
641 CYThrow(context, exception); \
642 *reinterpret_cast<native *>(data) = number; \
ea2d184c
JF
643 } break;
644
645 CYToFFI_(uchar, unsigned char)
646 CYToFFI_(char, char)
647 CYToFFI_(ushort, unsigned short)
648 CYToFFI_(short, short)
649 CYToFFI_(ulong, unsigned long)
650 CYToFFI_(long, long)
651 CYToFFI_(uint, unsigned int)
652 CYToFFI_(int, int)
653 CYToFFI_(ulonglong, unsigned long long)
654 CYToFFI_(longlong, long long)
655 CYToFFI_(float, float)
656 CYToFFI_(double, double)
657
658 case sig::object_P:
659 case sig::typename_P:
7ba62cfd
JF
660 *reinterpret_cast<id *>(data) = CYCastNSObject(context, value);
661 break;
662
ea2d184c 663 case sig::selector_P:
7ba62cfd
JF
664 *reinterpret_cast<SEL *>(data) = CYCastSEL(context, value);
665 break;
ea2d184c 666
7ba62cfd
JF
667 case sig::pointer_P: {
668 void *&pointer(*reinterpret_cast<void **>(data));
669 if (JSValueIsNull(context, value))
670 pointer = NULL;
671 else if (JSValueIsObjectOfClass(context, value, ptr_))
672 pointer = JSObjectGetPrivate((JSObjectRef) value);
673 else {
674 JSValueRef exception(NULL);
675 double number(JSValueToNumber(context, value, &exception));
676 CYThrow(context, exception);
677 pointer = reinterpret_cast<void *>(static_cast<uintptr_t>(number));
ea2d184c
JF
678 }
679 } break;
680
7ba62cfd
JF
681 case sig::string_P: {
682 JSValueRef exception(NULL);
683 JSStringRef string(JSValueToStringCopy(context, value, &exception));
684 CYThrow(context, exception);
685 size_t size(JSStringGetMaximumUTF8CStringSize(string));
686 char *utf8(reinterpret_cast<char *>(apr_palloc(pool, size)));
687 JSStringGetUTF8CString(string, utf8, size);
688 JSStringRelease(string);
689 *reinterpret_cast<char **>(data) = utf8;
690 } break;
691
ea2d184c
JF
692 case sig::struct_P:
693 goto fail;
694
695 case sig::void_P:
696 break;
697
698 default: fail:
699 NSLog(@"CYToFFI(%c)\n", type->primitive);
700 _assert(false);
701 }
702}
703
7ba62cfd 704JSValueRef CYFromFFI(apr_pool_t *pool, JSContextRef context, sig::Type *type, void *data) {
ea2d184c
JF
705 JSValueRef value;
706
707 switch (type->primitive) {
708 case sig::boolean_P:
709 value = JSValueMakeBoolean(context, *reinterpret_cast<bool *>(data));
710 break;
711
712#define CYFromFFI_(primitive, native) \
713 case sig::primitive ## _P: \
714 value = JSValueMakeNumber(context, *reinterpret_cast<native *>(data)); \
715 break;
716
717 CYFromFFI_(uchar, unsigned char)
718 CYFromFFI_(char, char)
719 CYFromFFI_(ushort, unsigned short)
720 CYFromFFI_(short, short)
721 CYFromFFI_(ulong, unsigned long)
722 CYFromFFI_(long, long)
723 CYFromFFI_(uint, unsigned int)
724 CYFromFFI_(int, int)
725 CYFromFFI_(ulonglong, unsigned long long)
726 CYFromFFI_(longlong, long long)
727 CYFromFFI_(float, float)
728 CYFromFFI_(double, double)
729
730 case sig::object_P:
731 case sig::typename_P: {
732 value = CYCastJSValue(context, *reinterpret_cast<id *>(data));
733 } break;
734
7ba62cfd
JF
735 case sig::selector_P: {
736 SEL sel(*reinterpret_cast<SEL *>(data));
737 value = sel == NULL ? JSValueMakeNull(context) : JSObjectMake(context, sel_, sel);
738 } break;
739
740 case sig::pointer_P: {
741 void *pointer(*reinterpret_cast<void **>(data));
742 value = pointer == NULL ? JSValueMakeNull(context) : JSObjectMake(context, ptr_, pointer);
743 } break;
ea2d184c
JF
744
745 case sig::string_P: {
746 char *utf8(*reinterpret_cast<char **>(data));
7ba62cfd 747 value = utf8 == NULL ? JSValueMakeNull(context) : JSValueMakeString(context, CYString(utf8));
ea2d184c
JF
748 } break;
749
750 case sig::struct_P:
751 goto fail;
752
753 case sig::void_P:
754 value = NULL;
755 break;
756
757 default: fail:
758 NSLog(@"CYFromFFI(%c)\n", type->primitive);
759 _assert(false);
760 }
761
762 return value;
763}
764
7ba62cfd
JF
765class CYPool {
766 private:
767 apr_pool_t *pool_;
ea2d184c 768
7ba62cfd
JF
769 public:
770 CYPool() {
771 apr_pool_create(&pool_, NULL);
772 }
ea2d184c 773
7ba62cfd
JF
774 ~CYPool() {
775 apr_pool_destroy(pool_);
776 }
ea2d184c 777
7ba62cfd
JF
778 operator apr_pool_t *() const {
779 return pool_;
780 }
781};
782
783static JSValueRef CYCallFunction(JSContextRef context, size_t count, const JSValueRef *arguments, JSValueRef *exception, sig::Signature *signature, ffi_cif *cif, void (*function)()) {
784 @try {
785 CYPool pool;
786 void *values[count];
ea2d184c 787
7ba62cfd
JF
788 for (unsigned index(0); index != count; ++index) {
789 sig::Element *element(&signature->elements[index + 1]);
790 values[index] = apr_palloc(pool, cif->arg_types[index]->size);
791 CYToFFI(pool, context, element->type, values[index], arguments[index]);
792 }
ea2d184c 793
7ba62cfd
JF
794 uint8_t value[cif->rtype->size];
795 ffi_call(cif, function, value, values);
796
797 return CYFromFFI(pool, context, signature->elements[0].type, value);
798 } @catch (id error) {
799 CYThrow(context, error, exception);
800 return NULL;
ea2d184c 801 }
7ba62cfd 802}
ea2d184c 803
7ba62cfd
JF
804static JSValueRef $objc_msgSend(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { _pooled
805 if (count < 2)
806 _assert(false);
807
808 const char *type;
ea2d184c
JF
809
810 @try {
7ba62cfd
JF
811 id self(CYCastNSObject(context, arguments[0]));
812 if (self == nil)
813 return JSValueMakeNull(context);
814 SEL _cmd(CYCastSEL(context, arguments[1]));
815
816 // XXX: what happens if you pass a NULL SEL?
817 NSMethodSignature *method([self methodSignatureForSelector:_cmd]);
818 // XXX: if the selector doesn't exist, throw an exception
819
820 type = [[method _typeString] UTF8String];
ea2d184c 821 } @catch (id error) {
7ba62cfd
JF
822 CYThrow(context, error, exception);
823 return NULL;
ea2d184c
JF
824 }
825
7ba62cfd
JF
826 CYPool pool;
827
828 sig::Signature signature;
829 sig::Parse(pool, &signature, type);
ea2d184c 830
7ba62cfd
JF
831 void (*function)() = reinterpret_cast<void (*)()>(&objc_msgSend);
832
833 ffi_cif cif;
834 sig::sig_ffi_cif(pool, &sig::sig_objc_ffi_type, &signature, &cif);
835
836 return CYCallFunction(context, count, arguments, exception, &signature, &cif, function);
837}
838
839static JSValueRef ffi_callAsFunction(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) {
840 ffiData *data(reinterpret_cast<ffiData *>(JSObjectGetPrivate(object)));
841 if (count != data->signature_.count - 1)
842 _assert(false);
843 return CYCallFunction(context, count, arguments, exception, &data->signature_, &data->cif_, data->function_);
ea2d184c
JF
844}
845
846static void ffi_finalize(JSObjectRef object) {
847 ffiData *data(reinterpret_cast<ffiData *>(JSObjectGetPrivate(object)));
848 apr_pool_destroy(data->pool_);
849}
850
851void CYSetFunction(JSContextRef context, JSObjectRef object, const char *name, void (*function)(), const char *type) {
852 apr_pool_t *pool;
853 apr_pool_create(&pool, NULL);
854
855 ffiData *data(reinterpret_cast<ffiData *>(apr_palloc(pool, sizeof(ffiData))));
856
857 data->pool_ = pool;
858 data->function_ = function;
859 data->type_ = apr_pstrdup(pool, type);
860
861 sig::Parse(pool, &data->signature_, type);
7ba62cfd 862 sig::sig_ffi_cif(pool, &sig::sig_objc_ffi_type, &data->signature_, &data->cif_);
ea2d184c
JF
863
864 JSObjectRef value(JSObjectMake(context, ffi_, data));
865 CYSetProperty(context, object, name, value);
866}
867
7ba62cfd 868MSInitialize { _pooled
ea2d184c
JF
869 apr_initialize();
870
c1582939
JF
871 NSCFBoolean_ = objc_getClass("NSCFBoolean");
872
873 pid_t pid(getpid());
874
875 struct sockaddr_in address;
876 address.sin_len = sizeof(address);
877 address.sin_family = AF_INET;
878 address.sin_addr.s_addr = INADDR_ANY;
879 address.sin_port = htons(10000 + pid);
880
881 CFDataRef data(CFDataCreate(kCFAllocatorDefault, reinterpret_cast<UInt8 *>(&address), sizeof(address)));
882
883 CFSocketSignature signature;
884 signature.protocolFamily = AF_INET;
885 signature.socketType = SOCK_STREAM;
886 signature.protocol = IPPROTO_TCP;
887 signature.address = data;
888
889 CFSocketRef socket(CFSocketCreateWithSocketSignature(kCFAllocatorDefault, &signature, kCFSocketAcceptCallBack, &OnAccept, NULL));
890 CFRunLoopAddSource(CFRunLoopGetCurrent(), CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket, 0), kCFRunLoopDefaultMode);
891
892 JSClassDefinition definition;
893
894 definition = kJSClassDefinitionEmpty;
895 definition.getProperty = &obc_getProperty;
896 JSClassRef obc(JSClassCreate(&definition));
897
ea2d184c 898 definition = kJSClassDefinitionEmpty;
7ba62cfd 899 definition.className = "ffi";
ea2d184c
JF
900 definition.callAsFunction = &ffi_callAsFunction;
901 definition.finalize = &ffi_finalize;
902 ffi_ = JSClassCreate(&definition);
903
c1582939 904 definition = kJSClassDefinitionEmpty;
7ba62cfd
JF
905 definition.className = "ptr";
906 ptr_ = JSClassCreate(&definition);
907
908 definition = kJSClassDefinitionEmpty;
909 definition.className = "sel";
910 sel_ = JSClassCreate(&definition);
911
912 definition = kJSClassDefinitionEmpty;
913 definition.className = "joc";
c1582939 914 definition.getProperty = &joc_getProperty;
ea2d184c 915 definition.finalize = &joc_finalize;
c1582939
JF
916 joc_ = JSClassCreate(&definition);
917
ea2d184c
JF
918 JSContextRef context(JSGlobalContextCreate(obc));
919 Context_ = context;
920
921 JSObjectRef global(JSContextGetGlobalObject(context));
c1582939 922
7ba62cfd
JF
923#define CYSetFunction_(name, type) \
924 CYSetFunction(context, global, #name, reinterpret_cast<void (*)()>(&name), type)
925
926 CYSetFunction_(class_createInstance, "@#L");
927 CYSetFunction_(class_getInstanceSize, "L#");
928 CYSetFunction_(class_getIvarLayout, "*#");
929 CYSetFunction_(class_getName, "*#");
930 CYSetFunction_(class_getSuperclass, "##");
931 CYSetFunction_(class_getVersion, "i#");
932 CYSetFunction_(class_isMetaClass, "B#");
933 CYSetFunction_(class_respondsToSelector, "B#:");
934 CYSetFunction_(class_setSuperclass, "###");
935 CYSetFunction_(class_setVersion, "v#i");
936 CYSetFunction_(objc_allocateClassPair, "##*L");
937 CYSetFunction_(objc_getClass, "#*");
938 CYSetFunction_(objc_getFutureClass, "#*");
939 CYSetFunction_(objc_getMetaClass, "@*");
940 CYSetFunction_(objc_getRequiredClass, "@*");
941 CYSetFunction_(objc_lookUpClass, "@*");
942 CYSetFunction_(objc_registerClassPair, "v#");
943 CYSetFunction_(objc_setFutureClass, "v#*");
944 CYSetFunction_(object_copy, "@@L");
945 CYSetFunction_(object_dispose, "@@");
946 CYSetFunction_(object_getClass, "#@");
947 CYSetFunction_(object_getClassName, "*@");
948 CYSetFunction_(object_setClass, "#@#");
949 CYSetFunction_(sel_getName, "*:");
950 CYSetFunction_(sel_getUid, ":*");
951 CYSetFunction_(sel_isEqual, "B::");
952 CYSetFunction_(sel_registerName, ":*");
953
954 CYSetProperty(context, global, "objc_msgSend", JSObjectMakeFunctionWithCallback(context, CYString("objc_msgSend"), &$objc_msgSend));
955
956 CYSetProperty(context, global, "YES", JSValueMakeBoolean(context, true));
957 CYSetProperty(context, global, "NO", JSValueMakeBoolean(context, true));
958 CYSetProperty(context, global, "nil", JSValueMakeNull(context));
c1582939 959
62ca2b82
JF
960 name_ = JSStringCreateWithUTF8CString("name");
961 message_ = JSStringCreateWithUTF8CString("message");
c1582939
JF
962 length_ = JSStringCreateWithUTF8CString("length");
963
62ca2b82 964 JSValueRef exception(NULL);
7ba62cfd 965 JSValueRef value(JSObjectGetProperty(JSGetContext(), global, CYString("Array"), &exception));
ea2d184c 966 CYThrow(context, exception);
62ca2b82 967 Array_ = JSValueToObject(JSGetContext(), value, &exception);
ea2d184c 968 CYThrow(context, exception);
c1582939 969}