/* }}} */
#include <substrate.h>
+
#include <dlfcn.h>
+#include <iconv.h>
#include "cycript.hpp"
#include <ext/stdio_filebuf.h>
#include <set>
#include <map>
-
+#include <iomanip>
#include <sstream>
#include <cmath>
#define _assert(test) do { \
if (!(test)) \
- _throw(NSInternalInconsistencyException, "*** _assert(%s):%s(%u):%s", #test, __FILE__, __LINE__, __FUNCTION__); \
+ _throw(NSInternalInconsistencyException, "*** _assert(%s):%s(%u):%s [errno=%d]", #test, __FILE__, __LINE__, __FUNCTION__, errno); \
} while (false)
#define _trace() do { \
fprintf(stderr, "_trace():%u\n", __LINE__); \
} while (false)
+#define CYTry \
+ @try
+#define CYCatch \
+ @catch (NSException *error) { \
+ CYThrow(context, error, exception); \
+ return NULL; \
+ }
+
+#if 0
+#define CYTry \
+ try
+#define CYCatch \
+ catch (NSException *error) { \
+ CYThrow(context, error, exception); \
+ return NULL; \
+ } catch (...) { \
+ *exception = CYCastJSValue(context, "catch(...)"); \
+ return NULL; \
+ }
+#endif
+
#ifdef __OBJC__
#define CYPoolTry { \
id _saved(nil); \
JSValueRef CYCallFunction(apr_pool_t *pool, JSContextRef context, size_t setups, void *setup[], size_t count, const JSValueRef arguments[], bool initialize, JSValueRef *exception, sig::Signature *signature, ffi_cif *cif, void (*function)());
JSValueRef CYSendMessage(apr_pool_t *pool, JSContextRef context, id self, SEL _cmd, size_t count, const JSValueRef arguments[], bool initialize, JSValueRef *exception);
+struct CYUTF8String {
+ const char *data;
+ size_t size;
+
+ CYUTF8String(const char *data, size_t size) :
+ data(data),
+ size(size)
+ {
+ }
+};
+
+struct CYUTF16String {
+ const uint16_t *data;
+ size_t size;
+
+ CYUTF16String(const uint16_t *data, size_t size) :
+ data(data),
+ size(size)
+ {
+ }
+};
+
/* JavaScript Properties {{{ */
JSValueRef CYGetProperty(JSContextRef context, JSObjectRef object, size_t index) {
JSValueRef exception(NULL);
/* }}} */
#ifdef __OBJC__
+/* Objective-C Pool Release {{{ */
+apr_status_t CYPoolRelease_(void *data) {
+ id object(reinterpret_cast<id>(data));
+ [object release];
+ return APR_SUCCESS;
+}
+
+id CYPoolRelease_(apr_pool_t *pool, id object) {
+ if (object == nil)
+ return nil;
+ else if (pool == NULL)
+ return [object autorelease];
+ else {
+ apr_pool_cleanup_register(pool, object, &CYPoolRelease_, &apr_pool_cleanup_null);
+ return object;
+ }
+}
+
+template <typename Type_>
+Type_ CYPoolRelease(apr_pool_t *pool, Type_ object) {
+ return (Type_) CYPoolRelease_(pool, (id) object);
+}
+/* }}} */
/* Objective-C Strings {{{ */
const char *CYPoolCString(apr_pool_t *pool, NSString *value) {
if (pool == NULL)
return CYCopyJSString_(string);
}
+NSString *CYCopyNSString(const CYUTF8String &value) {
#ifdef __APPLE__
-CFStringRef CYCopyCFString(JSStringRef value) {
- return JSStringCopyCFString(kCFAllocatorDefault, value);
-}
-
-CFStringRef CYCopyCFString(const char *value) {
- return CFStringCreateWithCString(kCFAllocatorDefault, value, kCFStringEncodingUTF8);
-}
-
-template <typename Type_>
-NSString *CYCopyNSString(Type_ value) {
- return (NSString *) CYCopyCFString(value);
-}
+ return (NSString *) CFStringCreateWithBytes(kCFAllocatorDefault, reinterpret_cast<const UInt8 *>(value.data), value.size, kCFStringEncodingUTF8, true);
#else
-NSString *CYCopyNSString(const char *value) {
- return [NSString stringWithUTF8String:value];
+ return [[NSString alloc] initWithBytes:value.data length:value.size encoding:NSUTF8StringEncoding];
+#endif
}
NSString *CYCopyNSString(JSStringRef value) {
+#ifdef __APPLE__
+ return (NSString *) JSStringCopyCFString(kCFAllocatorDefault, value);
+#else
return CYCopyNSString(CYCastCString_(value));
-}
#endif
+}
NSString *CYCopyNSString(JSContextRef context, JSValueRef value) {
return CYCopyNSString(CYJSString(context, value));
}
-/* }}} */
-/* Objective-C Pool Release {{{ */
-apr_status_t CYPoolRelease_(void *data) {
- id object(reinterpret_cast<id>(data));
- [object release];
- return APR_SUCCESS;
+
+NSString *CYCastNSString(apr_pool_t *pool, const CYUTF8String &value) {
+ return CYPoolRelease(pool, CYCopyNSString(value));
}
-id CYPoolRelease_(apr_pool_t *pool, id object) {
- if (object == nil)
- return nil;
- else if (pool == NULL)
- return [object autorelease];
- else {
- apr_pool_cleanup_register(pool, object, &CYPoolRelease_, &apr_pool_cleanup_null);
- return object;
- }
+NSString *CYCastNSString(apr_pool_t *pool, SEL sel) {
+ const char *name(sel_getName(sel));
+ return CYPoolRelease(pool, CYCopyNSString(CYUTF8String(name, strlen(name))));
}
-template <typename Type_>
-Type_ CYPoolRelease(apr_pool_t *pool, Type_ object) {
- return (Type_) CYPoolRelease_(pool, (id) object);
+NSString *CYCastNSString(apr_pool_t *pool, JSStringRef value) {
+ return CYPoolRelease(pool, CYCopyNSString(value));
}
/* }}} */
#endif
+CYUTF8String CYCastUTF8String(NSString *value) {
+ NSData *data([value dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:NO]);
+ return CYUTF8String(reinterpret_cast<const char *>([data bytes]), [data length]);
+}
+
+/* JavaScript Stringify {{{ */
+void CYStringify(std::ostringstream &str, const char *data, size_t size) {
+ unsigned quot(0), apos(0);
+ for (const char *value(data), *end(data + size); value != end; ++value)
+ if (*value == '"')
+ ++quot;
+ else if (*value == '\'')
+ ++apos;
+
+ bool single(quot > apos);
+
+ str << (single ? '\'' : '"');
+
+ for (const char *value(data), *end(data + size); value != end; ++value)
+ switch (*value) {
+ case '\\': str << "\\\\"; break;
+ case '\b': str << "\\b"; break;
+ case '\f': str << "\\f"; break;
+ case '\n': str << "\\n"; break;
+ case '\r': str << "\\r"; break;
+ case '\t': str << "\\t"; break;
+ case '\v': str << "\\v"; break;
+
+ case '"':
+ if (!single)
+ str << "\\\"";
+ else goto simple;
+ break;
+
+ case '\'':
+ if (single)
+ str << "\\'";
+ else goto simple;
+ break;
+
+ default:
+ if (*value < 0x20 || *value >= 0x7f)
+ str << "\\x" << std::setbase(16) << std::setw(2) << std::setfill('0') << unsigned(*value);
+ else simple:
+ str << *value;
+ }
+
+ str << (single ? '\'' : '"');
+}
+/* }}} */
+
static JSGlobalContextRef Context_;
static JSObjectRef System_;
static JSObjectRef ObjectiveC_;
return JSValueMakeUndefined(context);
}
-size_t CYGetIndex(const char *value) {
- if (value[0] != '0') {
+size_t CYGetIndex(const CYUTF8String &value) {
+ if (value.data[0] != '0') {
char *end;
- size_t index(strtoul(value, &end, 10));
- if (value + strlen(value) == end)
+ size_t index(strtoul(value.data, &end, 10));
+ if (value.data + value.size == end)
return index;
- } else if (value[1] == '\0')
+ } else if (value.data[1] == '\0')
return 0;
return _not(size_t);
}
// XXX: fix this
-static const char *CYPoolCString(apr_pool_t *pool, JSStringRef value);
+static CYUTF8String CYPoolUTF8String(apr_pool_t *pool, JSStringRef value);
size_t CYGetIndex(apr_pool_t *pool, JSStringRef value) {
- return CYGetIndex(CYPoolCString(pool, value));
+ return CYGetIndex(CYPoolUTF8String(pool, value));
}
bool CYGetOffset(const char *value, ssize_t &index) {
}
#ifdef __OBJC__
-size_t CYGetIndex(apr_pool_t *pool, NSString *value) {
- return CYGetIndex(CYPoolCString(pool, value));
+size_t CYGetIndex(NSString *value) {
+ return CYGetIndex(CYCastUTF8String(value));
}
bool CYGetOffset(apr_pool_t *pool, NSString *value, ssize_t &index) {
#endif
#ifdef __OBJC__
-NSString *CYPoolNSCYON(apr_pool_t *pool, id value);
-
@interface NSMethodSignature (Cycript)
- (NSString *) _typeString;
@end
@end
#endif
+#ifdef __OBJC__
+NSString *CYCastNSCYON(id value) {
+ NSString *string;
+
+ if (value == nil)
+ string = @"nil";
+ else {
+ Class _class(object_getClass(value));
+ SEL sel(@selector(cy$toCYON));
+
+ if (objc_method *toCYON = class_getInstanceMethod(_class, sel))
+ string = reinterpret_cast<NSString *(*)(id, SEL)>(method_getImplementation(toCYON))(value, sel);
+ else if (objc_method *methodSignatureForSelector = class_getInstanceMethod(_class, @selector(methodSignatureForSelector:))) {
+ if (reinterpret_cast<NSMethodSignature *(*)(id, SEL, SEL)>(method_getImplementation(methodSignatureForSelector))(value, @selector(methodSignatureForSelector:), sel) != nil)
+ string = [value cy$toCYON];
+ else goto fail;
+ } else fail: {
+ if (value == NSZombie_)
+ string = @"_NSZombie_";
+ else if (_class == NSZombie_)
+ string = [NSString stringWithFormat:@"<_NSZombie_: %p>", value];
+ // XXX: frowny /in/ the pants
+ else if (value == NSMessageBuilder_ || value == Object_)
+ string = nil;
+ else
+ string = [NSString stringWithFormat:@"%@", value];
+ }
+
+ // XXX: frowny pants
+ if (string == nil)
+ string = @"undefined";
+ }
+
+ return string;
+}
+#endif
+
#ifdef __OBJC__
#ifdef __APPLE__
struct PropertyAttributes {
else
comma = true;
if (object == nil || [object cy$JSType] != kJSTypeUndefined)
- [json appendString:CYPoolNSCYON(NULL, object)];
+ [json appendString:CYCastNSCYON(object)];
else {
[json appendString:@","];
comma = false;
if ([name isEqualToString:@"length"])
return true;
- size_t index(CYGetIndex(NULL, name));
+ size_t index(CYGetIndex(name));
if (index == _not(size_t) || index >= [self count])
return [super cy$hasProperty:name];
else
#endif
}
- size_t index(CYGetIndex(NULL, name));
+ size_t index(CYGetIndex(name));
if (index == _not(size_t) || index >= [self count])
return [super cy$getProperty:name];
else
[json appendString:[key cy$toKey]];
[json appendString:@":"];
NSObject *object([self objectForKey:key]);
- [json appendString:CYPoolNSCYON(NULL, object)];
+ [json appendString:CYCastNSCYON(object)];
}
[json appendString:@"}"];
return true;
}
- size_t index(CYGetIndex(NULL, name));
+ size_t index(CYGetIndex(name));
if (index == _not(size_t))
return [super cy$setProperty:name to:value];
}
- (bool) cy$deleteProperty:(NSString *)name {
- size_t index(CYGetIndex(NULL, name));
+ size_t index(CYGetIndex(name));
if (index == _not(size_t) || index >= [self count])
return [super cy$deleteProperty:name];
[self replaceObjectAtIndex:index withObject:[WebUndefined undefined]];
}
- (NSString *) cy$toCYON {
- // XXX: this should use the better code from Output.cpp
-
- NSMutableString *json([self mutableCopy]);
-
- [json replaceOccurrencesOfString:@"\\" withString:@"\\\\" options:NSLiteralSearch range:NSMakeRange(0, [json length])];
- [json replaceOccurrencesOfString:@"\"" withString:@"\\\"" options:NSLiteralSearch range:NSMakeRange(0, [json length])];
- [json replaceOccurrencesOfString:@"\t" withString:@"\\t" options:NSLiteralSearch range:NSMakeRange(0, [json length])];
- [json replaceOccurrencesOfString:@"\r" withString:@"\\r" options:NSLiteralSearch range:NSMakeRange(0, [json length])];
- [json replaceOccurrencesOfString:@"\n" withString:@"\\n" options:NSLiteralSearch range:NSMakeRange(0, [json length])];
-
- [json appendString:@"\""];
- [json insertString:@"\"" atIndex:0];
-
- return json;
+ std::ostringstream str;
+ CYUTF8String string(CYCastUTF8String(self));
+ CYStringify(str, string.data, string.size);
+ std::string value(str.str());
+ return CYCastNSString(NULL, CYUTF8String(value.c_str(), value.size()));
}
- (NSString *) cy$toKey {
goto cyon;
if (DigitRange_[value[0]]) {
- size_t index(CYGetIndex(NULL, self));
+ size_t index(CYGetIndex(self));
if (index == _not(size_t))
goto cyon;
} else {
/* }}} */
#endif
-#define CYTry \
- try
-#define CYCatch \
- catch (id error) { \
- CYThrow(context, error, exception); \
- return NULL; \
- } catch (...) { \
- *exception = CYCastJSValue(context, "catch(...)"); \
- return NULL; \
- }
-
-
#ifdef __OBJC__
NSObject *CYCastNSObject_(apr_pool_t *pool, JSContextRef context, JSObjectRef object) {
JSValueRef exception(NULL);
NSNumber *CYCopyNSNumber(JSContextRef context, JSValueRef value) {
return [[NSNumber alloc] initWithDouble:CYCastDouble(context, value)];
}
-
-template <typename Type_>
-NSString *CYCastNSString(apr_pool_t *pool, Type_ value) {
- return CYPoolRelease(pool, CYCopyNSString(value));
-}
#endif
bool CYCastBool(JSContextRef context, JSValueRef value) {
@end
#endif
-NSString *CYCopyNSCYON(id value) {
- NSString *string;
-
- if (value == nil)
- string = @"nil";
- else {
- Class _class(object_getClass(value));
- SEL sel(@selector(cy$toCYON));
-
- if (objc_method *toCYON = class_getInstanceMethod(_class, sel))
- string = reinterpret_cast<NSString *(*)(id, SEL)>(method_getImplementation(toCYON))(value, sel);
- else if (objc_method *methodSignatureForSelector = class_getInstanceMethod(_class, @selector(methodSignatureForSelector:))) {
- if (reinterpret_cast<NSMethodSignature *(*)(id, SEL, SEL)>(method_getImplementation(methodSignatureForSelector))(value, @selector(methodSignatureForSelector:), sel) != nil)
- string = [value cy$toCYON];
- else goto fail;
- } else fail: {
- if (value == NSZombie_)
- string = @"_NSZombie_";
- else if (_class == NSZombie_)
- string = [NSString stringWithFormat:@"<_NSZombie_: %p>", value];
- // XXX: frowny /in/ the pants
- else if (value == NSMessageBuilder_ || value == Object_)
- string = nil;
- else
- string = [NSString stringWithFormat:@"%@", value];
- }
-
- // XXX: frowny pants
- if (string == nil)
- string = @"undefined";
- }
-
- return [string retain];
-}
-
NSString *CYCopyNSCYON(JSContextRef context, JSValueRef value, JSValueRef *exception) {
if (JSValueIsNull(context, value))
return [@"null" retain];
CYTry {
CYPoolTry {
- return CYCopyNSCYON(CYCastNSObject(NULL, context, value));
+ return [CYCastNSCYON(CYCastNSObject(NULL, context, value)) retain];
} CYPoolCatch(NULL)
} CYCatch
}
-NSString *CYPoolNSCYON(apr_pool_t *pool, id value) {
- return CYPoolRelease(pool, static_cast<id>(CYCopyNSCYON(value)));
-}
-
const char *CYPoolCCYON(apr_pool_t *pool, JSContextRef context, JSValueRef value, JSValueRef *exception) {
if (NSString *json = CYCopyNSCYON(context, value, exception)) {
const char *string(CYPoolCString(pool, json));
return JSObjectMake(context, Functor_, internal);
}
+static CYUTF16String CYCastUTF16String(JSStringRef value) {
+ return CYUTF16String(JSStringGetCharactersPtr(value), JSStringGetLength(value));
+}
+
+// XXX: sometimes pool is null
+static CYUTF8String CYPoolUTF8String(apr_pool_t *pool, JSStringRef value) {
+ CYUTF16String utf16(CYCastUTF16String(value));
+ const char *in(reinterpret_cast<const char *>(utf16.data));
+
+ iconv_t conversion(_syscall(iconv_open("UTF-8", "UCS-2-INTERNAL")));
+
+ size_t size(JSStringGetMaximumUTF8CStringSize(value));
+ char *out(new(pool) char[size]);
+ CYUTF8String utf8(out, size);
+
+ size = utf16.size * 2;
+ _syscall(iconv(conversion, const_cast<char **>(&in), &size, &out, &utf8.size));
+
+ *out = '\0';
+ utf8.size = out - utf8.data;
+
+ _syscall(iconv_close(conversion));
+
+ return utf8;
+}
+
static const char *CYPoolCString(apr_pool_t *pool, JSStringRef value) {
- if (pool == NULL) {
- // XXX: this could be much more efficient
- const char *string([CYCastNSString(NULL, value) UTF8String]);
- return string;
- } else {
- size_t size(JSStringGetMaximumUTF8CStringSize(value));
- char *string(new(pool) char[size]);
- JSStringGetUTF8CString(value, string, size);
- return string;
- }
+ CYUTF8String utf8(CYPoolUTF8String(pool, value));
+ _assert(memchr(utf8.data, '\0', utf8.size) == NULL);
+ return utf8.data;
}
static const char *CYPoolCString(apr_pool_t *pool, JSContextRef context, JSValueRef value) {
static const char *CYPoolTypeEncoding(apr_pool_t *pool, Class _class, SEL sel, objc_method *method) {
if (method != NULL)
return method_getTypeEncoding(method);
- else if (NSString *type = [[Bridge_ objectAtIndex:1] objectForKey:CYCastNSString(pool, sel_getName(sel))])
+ else if (NSString *type = [[Bridge_ objectAtIndex:1] objectForKey:CYCastNSString(pool, sel)])
return CYPoolCString(pool, type);
else
return NULL;
ssize_t index;
uint8_t *base;
- if (!Index_(pool, internal, property, index, base))
- return NULL;
+ CYTry {
+ if (!Index_(pool, internal, property, index, base))
+ return NULL;
- JSObjectRef owner(internal->GetOwner() ?: object);
+ JSObjectRef owner(internal->GetOwner() ?: object);
- CYTry {
return CYFromFFI(context, typical->type_->data.signature.elements[index].type, typical->GetFFI()->elements[index], base, false, owner);
} CYCatch
}
ssize_t index;
uint8_t *base;
- if (!Index_(pool, internal, property, index, base))
- return false;
-
CYTry {
+ if (!Index_(pool, internal, property, index, base))
+ return false;
+
CYPoolFFI(NULL, context, typical->type_->data.signature.elements[index].type, typical->GetFFI()->elements[index], base, value);
return true;
} CYCatch
CYTry {
CYPoolTry {
- return CYCastJSValue(context, CYJSString(CYPoolNSCYON(NULL, internal->GetValue())));
+ return CYCastJSValue(context, CYJSString(CYCastNSCYON(internal->GetValue())));
} CYPoolCatch(NULL)
} CYCatch
}