]> git.saurik.com Git - cycript.git/blobdiff - ObjectiveC/Library.mm
Rename cy$JSValueInContext to cy$valueOfInContext.
[cycript.git] / ObjectiveC / Library.mm
index 10ce9cde9f535cc8dd4598469d64002dcdf51733..9b242d8471a9f21c6206f6617abae4d5ab98f9e2 100644 (file)
@@ -234,6 +234,12 @@ bool CYGetOffset(apr_pool_t *pool, JSContextRef context, NSString *value, ssize_
 }
 
 static JSClassRef Instance_;
+
+static JSClassRef ArrayInstance_;
+static JSClassRef FunctionInstance_;
+static JSClassRef ObjectInstance_;
+static JSClassRef StringInstance_;
+
 static JSClassRef Internal_;
 static JSClassRef Message_;
 static JSClassRef Messages_;
@@ -260,6 +266,7 @@ static Class NSBoolNumber_;
 #endif
 
 static Class NSArray_;
+static Class NSBlock_;
 static Class NSDictionary_;
 static Class NSString_;
 static Class Object_;
@@ -297,6 +304,8 @@ JSValueRef CYGetClassPrototype(JSContextRef context, id self) {
 
     if (self == NSArray_)
         prototype = CYGetCachedObject(context, CYJSString("ArrayInstance_prototype"));
+    else if (self == NSBlock_)
+        prototype = CYGetCachedObject(context, CYJSString("FunctionInstance_prototype"));
     else if (self == NSDictionary_)
         prototype = CYGetCachedObject(context, CYJSString("ObjectInstance_prototype"));
     else if (self == NSString_)
@@ -376,12 +385,11 @@ JSObjectRef CYMakeInstance(JSContextRef context, id object, bool transient) {
 
 @interface NSObject (Cycript)
 
-- (JSValueRef) cy$JSValueInContext:(JSContextRef)context;
+- (JSValueRef) cy$valueOfInContext:(JSContextRef)context;
 - (JSType) cy$JSType;
 
 - (NSObject *) cy$toJSON:(NSString *)key;
 - (NSString *) cy$toCYON;
-- (NSString *) cy$toKey;
 
 - (bool) cy$hasProperty:(NSString *)name;
 - (NSObject *) cy$getProperty:(NSString *)name;
@@ -395,7 +403,7 @@ JSObjectRef CYMakeInstance(JSContextRef context, id object, bool transient) {
 
 @protocol Cycript
 - (id) cy$box;
-- (JSValueRef) cy$JSValueInContext:(JSContextRef)context;
+- (JSValueRef) cy$valueOfInContext:(JSContextRef)context;
 @end
 
 NSString *CYCastNSCYON(id value) {
@@ -758,6 +766,13 @@ NSObject *CYCopyNSObject(apr_pool_t *pool, JSContextRef context, JSValueRef valu
 
 @end
 /* }}} */
+/* Bridge: NSBlock {{{ */
+#ifdef __APPLE__
+@interface NSBlock
+- (void) invoke;
+@end
+#endif
+/* }}} */
 /* Bridge: NSBoolNumber {{{ */
 #ifndef __APPLE__
 @implementation NSBoolNumber (Cycript)
@@ -774,7 +789,7 @@ NSObject *CYCopyNSObject(apr_pool_t *pool, JSContextRef context, JSValueRef valu
     return [self boolValue] ? @"@true" : @"@false";
 }
 
-- (JSValueRef) cy$JSValueInContext:(JSContextRef)context { CYObjectiveTry_(context) {
+- (JSValueRef) cy$valueOfInContext:(JSContextRef)context { CYObjectiveTry_(context) {
     return CYCastJSValue(context, (bool) [self boolValue]);
 } CYObjectiveCatch }
 
@@ -803,7 +818,7 @@ NSObject *CYCopyNSObject(apr_pool_t *pool, JSContextRef context, JSValueRef valu
             [json appendString:@","];
         else
             comma = true;
-        [json appendString:[key cy$toKey]];
+        [json appendString:CYCastNSCYON(key)];
         [json appendString:@":"];
         NSObject *object([self objectForKey:key]);
         [json appendString:CYCastNSCYON(object)];
@@ -934,7 +949,7 @@ NSObject *CYCopyNSObject(apr_pool_t *pool, JSContextRef context, JSValueRef valu
     return [self cy$JSType] != kJSTypeBoolean ? [NSString stringWithFormat:@"@%@", self] : [self boolValue] ? @"@true" : @"@false";
 }
 
-- (JSValueRef) cy$JSValueInContext:(JSContextRef)context { CYObjectiveTry_(context) {
+- (JSValueRef) cy$valueOfInContext:(JSContextRef)context { CYObjectiveTry_(context) {
     return [self cy$JSType] != kJSTypeBoolean ? CYCastJSValue(context, [self doubleValue]) : CYCastJSValue(context, [self boolValue]);
 } CYObjectiveCatch }
 
@@ -964,7 +979,7 @@ NSObject *CYCopyNSObject(apr_pool_t *pool, JSContextRef context, JSValueRef valu
     return self;
 }
 
-- (JSValueRef) cy$JSValueInContext:(JSContextRef)context { CYObjectiveTry_(context) {
+- (JSValueRef) cy$valueOfInContext:(JSContextRef)context { CYObjectiveTry_(context) {
     return NULL;
 } CYObjectiveCatch }
 
@@ -980,10 +995,6 @@ NSObject *CYCopyNSObject(apr_pool_t *pool, JSContextRef context, JSValueRef valu
     return [[self cy$toJSON:@""] cy$toCYON];
 }
 
-- (NSString *) cy$toKey {
-    return [self cy$toCYON];
-}
-
 - (bool) cy$hasProperty:(NSString *)name {
     return false;
 }
@@ -1046,12 +1057,6 @@ NSObject *CYCopyNSObject(apr_pool_t *pool, JSContextRef context, JSValueRef valu
     return CYCastNSString(NULL, CYUTF8String(value.c_str(), value.size()));
 }
 
-- (NSString *) cy$toKey {
-    if (CYIsKey(CYCastUTF8String(self)))
-        return self;
-    return [self cy$toCYON];
-}
-
 - (bool) cy$hasProperty:(NSString *)name {
     if ([name isEqualToString:@"length"])
         return true;
@@ -1095,7 +1100,7 @@ NSObject *CYCopyNSObject(apr_pool_t *pool, JSContextRef context, JSValueRef valu
     return false;
 }
 
-- (JSValueRef) cy$JSValueInContext:(JSContextRef)context { CYObjectiveTry_(context) {
+- (JSValueRef) cy$valueOfInContext:(JSContextRef)context { CYObjectiveTry_(context) {
     return CYCastJSValue(context, CYJSString(context, self));
 } CYObjectiveCatch }
 
@@ -1116,7 +1121,7 @@ NSObject *CYCopyNSObject(apr_pool_t *pool, JSContextRef context, JSValueRef valu
     return @"undefined";
 }
 
-- (JSValueRef) cy$JSValueInContext:(JSContextRef)context { CYObjectiveTry_(context) {
+- (JSValueRef) cy$valueOfInContext:(JSContextRef)context { CYObjectiveTry_(context) {
     return CYJSUndefined(context);
 } CYObjectiveCatch }
 
@@ -1231,6 +1236,11 @@ JSValueRef CYCastJSValue(JSContextRef context, NSObject *value) { CYPoolTry {
 
 @implementation CYJSArray
 
+- (NSString *) cy$toCYON {
+    CYPool pool;
+    return [NSString stringWithUTF8String:CYPoolCCYON(pool, context_, object_)];
+}
+
 - (id) initWithJSObject:(JSObjectRef)object inContext:(JSContextRef)context { CYObjectiveTry {
     if ((self = [super init]) != nil) {
         object_ = object;
@@ -1823,6 +1833,82 @@ static JSObjectRef Instance_callAsConstructor(JSContextRef context, JSObjectRef
     return value;
 } CYCatch }
 
+static JSValueRef Instance_callAsFunction(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry {
+    Instance *internal(reinterpret_cast<Instance *>(JSObjectGetPrivate(object)));
+    id self(internal->GetValue());
+
+    if (![self isKindOfClass:NSBlock_])
+        CYThrow("non-NSBlock object is not a function");
+
+    struct BlockDescriptor1 {
+        unsigned long int reserved;
+        unsigned long int size;
+    };
+
+    struct BlockDescriptor2 {
+        void (*copy_helper)(void *dst, void *src);
+        void (*dispose_helper)(void *src);
+    };
+
+    struct BlockDescriptor3 {
+        const char *signature;
+        const char *layout;
+    };
+
+    struct BlockLiteral {
+        Class isa;
+        int flags;
+        int reserved;
+        void (*invoke)(void *, ...);
+        void *descriptor;
+    } *literal = reinterpret_cast<BlockLiteral *>(self);
+
+    enum {
+        BLOCK_DEALLOCATING = 0x0001,
+        BLOCK_REFCOUNT_MASK = 0xfffe,
+        BLOCK_NEEDS_FREE = 1 << 24,
+        BLOCK_HAS_COPY_DISPOSE = 1 << 25,
+        BLOCK_HAS_CTOR = 1 << 26,
+        BLOCK_IS_GC = 1 << 27,
+        BLOCK_IS_GLOBAL = 1 << 28,
+        BLOCK_HAS_STRET = 1 << 29,
+        BLOCK_HAS_SIGNATURE = 1 << 30,
+    };
+
+    if ((literal->flags & BLOCK_HAS_SIGNATURE) != 0) {
+        uint8_t *descriptor(reinterpret_cast<uint8_t *>(literal->descriptor));
+        descriptor += sizeof(BlockDescriptor1);
+        if ((literal->flags & BLOCK_HAS_COPY_DISPOSE) != 0)
+            descriptor += sizeof(BlockDescriptor2);
+        BlockDescriptor3 *descriptor3(reinterpret_cast<BlockDescriptor3 *>(descriptor));
+
+        if (const char *type = descriptor3->signature) {
+            CYPool pool;
+
+            void *setup[1];
+            setup[0] = &self;
+
+            sig::Signature signature;
+            sig::Parse(pool, &signature, type, &Structor_);
+
+            ffi_cif cif;
+            sig::sig_ffi_cif(pool, &sig::ObjectiveC, &signature, &cif);
+
+            void (*function)() = reinterpret_cast<void (*)()>(literal->invoke);
+            return CYCallFunction(pool, context, 1, setup, count, arguments, false, exception, &signature, &cif, function);
+        }
+    }
+
+    if (count != 0)
+        CYThrow("NSBlock without signature field passed arguments");
+
+    CYPoolTry {
+        [self invoke];
+    } CYPoolCatch(NULL);
+
+    return NULL;
+} CYCatch }
+
 static bool Instance_hasInstance(JSContextRef context, JSObjectRef constructor, JSValueRef instance, JSValueRef *exception) { CYTry {
     Instance *internal(reinterpret_cast<Instance *>(JSObjectGetPrivate((JSObjectRef) constructor)));
     Class _class(internal->GetValue());
@@ -2326,10 +2412,10 @@ static JSValueRef Instance_callAsFunction_valueOf(JSContextRef context, JSObject
     Instance *internal(reinterpret_cast<Instance *>(JSObjectGetPrivate(_this)));
     id value(internal->GetValue());
 
-    if (![value respondsToSelector:@selector(cy$JSValueInContext:)])
+    if (![value respondsToSelector:@selector(cy$valueOfInContext:)])
         return _this;
 
-    if (JSValueRef result = [value cy$JSValueInContext:context])
+    if (JSValueRef result = [value cy$valueOfInContext:context])
         return result;
 
     return _this;
@@ -2452,6 +2538,7 @@ void CYObjectiveC_Initialize() { /*XXX*/ JSContextRef context(NULL); CYPoolTry {
 #endif
 
     NSArray_ = objc_getClass("NSArray");
+    NSBlock_ = objc_getClass("NSBlock");
     NSDictionary_ = objc_getClass("NSDictionary");
     NSString_ = objc_getClass("NSString");
     Object_ = objc_getClass("Object");
@@ -2468,10 +2555,23 @@ void CYObjectiveC_Initialize() { /*XXX*/ JSContextRef context(NULL); CYPoolTry {
     definition.deleteProperty = &Instance_deleteProperty;
     definition.getPropertyNames = &Instance_getPropertyNames;
     definition.callAsConstructor = &Instance_callAsConstructor;
+    definition.callAsFunction = &Instance_callAsFunction;
     definition.hasInstance = &Instance_hasInstance;
     definition.finalize = &CYFinalize;
     Instance_ = JSClassCreate(&definition);
 
+    definition.className = "ArrayInstance";
+    ArrayInstance_ = JSClassCreate(&definition);
+
+    definition.className = "FunctionInstance";
+    FunctionInstance_ = JSClassCreate(&definition);
+
+    definition.className = "ObjectInstance";
+    ObjectInstance_ = JSClassCreate(&definition);
+
+    definition.className = "StringInstance";
+    StringInstance_ = JSClassCreate(&definition);
+
     definition = kJSClassDefinitionEmpty;
     definition.className = "Internal";
     definition.staticFunctions = Internal_staticFunctions;
@@ -2590,19 +2690,25 @@ void CYObjectiveC_SetupContext(JSContextRef context) { CYPoolTry {
     JSObjectRef Instance_prototype(CYCastJSObject(context, CYGetProperty(context, Instance, prototype_s)));
     CYSetProperty(context, cy, CYJSString("Instance_prototype"), Instance_prototype);
 
-    JSObjectRef ArrayInstance(JSObjectMakeConstructor(context, Instance_, NULL));
+    JSObjectRef ArrayInstance(JSObjectMakeConstructor(context, ArrayInstance_, NULL));
     JSObjectRef ArrayInstance_prototype(CYCastJSObject(context, CYGetProperty(context, ArrayInstance, prototype_s)));
     CYSetProperty(context, cy, CYJSString("ArrayInstance_prototype"), ArrayInstance_prototype);
     JSObjectRef Array_prototype(CYGetCachedObject(context, CYJSString("Array_prototype")));
     JSObjectSetPrototype(context, ArrayInstance_prototype, Array_prototype);
 
-    JSObjectRef ObjectInstance(JSObjectMakeConstructor(context, Instance_, NULL));
+    JSObjectRef FunctionInstance(JSObjectMakeConstructor(context, FunctionInstance_, NULL));
+    JSObjectRef FunctionInstance_prototype(CYCastJSObject(context, CYGetProperty(context, FunctionInstance, prototype_s)));
+    CYSetProperty(context, cy, CYJSString("FunctionInstance_prototype"), FunctionInstance_prototype);
+    JSObjectRef Function_prototype(CYGetCachedObject(context, CYJSString("Function_prototype")));
+    JSObjectSetPrototype(context, FunctionInstance_prototype, Function_prototype);
+
+    JSObjectRef ObjectInstance(JSObjectMakeConstructor(context, ObjectInstance_, NULL));
     JSObjectRef ObjectInstance_prototype(CYCastJSObject(context, CYGetProperty(context, ObjectInstance, prototype_s)));
     CYSetProperty(context, cy, CYJSString("ObjectInstance_prototype"), ObjectInstance_prototype);
     JSObjectRef Object_prototype(CYGetCachedObject(context, CYJSString("Object_prototype")));
     JSObjectSetPrototype(context, ObjectInstance_prototype, Object_prototype);
 
-    JSObjectRef StringInstance(JSObjectMakeConstructor(context, Instance_, NULL));
+    JSObjectRef StringInstance(JSObjectMakeConstructor(context, StringInstance_, NULL));
     JSObjectRef StringInstance_prototype(CYCastJSObject(context, CYGetProperty(context, StringInstance, prototype_s)));
     CYSetProperty(context, cy, CYJSString("StringInstance_prototype"), StringInstance_prototype);
     JSObjectRef String_prototype(CYGetCachedObject(context, CYJSString("String_prototype")));
@@ -2621,7 +2727,6 @@ void CYObjectiveC_SetupContext(JSContextRef context) { CYPoolTry {
 
     CYSetProperty(context, all, CYJSString("objc_msgSend"), &$objc_msgSend, kJSPropertyAttributeDontEnum);
 
-    JSObjectRef Function_prototype(CYGetCachedObject(context, CYJSString("Function_prototype")));
     JSObjectSetPrototype(context, CYCastJSObject(context, CYGetProperty(context, Message, prototype_s)), Function_prototype);
     JSObjectSetPrototype(context, CYCastJSObject(context, CYGetProperty(context, Selector, prototype_s)), Function_prototype);
 } CYPoolCatch() }