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