]> git.saurik.com Git - cycript.git/blobdiff - ObjectiveC/Library.mm
Embed Cycript git version in zip package filename.
[cycript.git] / ObjectiveC / Library.mm
index a685816ecbb22f3d3c30d86494575f019da97150..52df126d9a5e697048563d2e5ffdd8f0365531b0 100644 (file)
     } return value; \
 }
 
+#define _oassert(test) \
+    if (!(test)) \
+        @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"_assert(" #test ")" userInfo:nil];
+
 #ifndef __APPLE__
 #define class_getSuperclass GSObjCSuper
 #define class_getInstanceVariable GSCGetInstanceVariableDefinition
@@ -304,8 +308,6 @@ static Class NSCFBoolean_;
 static Class NSCFType_;
 static Class NSGenericDeallocHandler_;
 static Class NSZombie_;
-
-static std::set<Class> banned_;
 #else
 static Class NSBoolNumber_;
 #endif
@@ -370,7 +372,7 @@ JSValueRef CYGetClassPrototype(JSContextRef context, Class self, bool meta) {
         prototype = CYGetClassPrototype(context, class_getSuperclass(self), meta);
 
     JSObjectRef object(JSObjectMake(context, _class, NULL));
-    JSObjectSetPrototype(context, object, prototype);
+    CYSetPrototype(context, object, prototype);
     CYSetProperty(context, cy, name, object);
 
     return object;
@@ -383,7 +385,7 @@ _finline JSValueRef CYGetClassPrototype(JSContextRef context, Class self) {
 JSObjectRef Messages::Make(JSContextRef context, Class _class) {
     JSObjectRef value(JSObjectMake(context, Messages_, new Messages(_class)));
     if (Class super = class_getSuperclass(_class))
-        JSObjectSetPrototype(context, value, Messages::Make(context, super));
+        CYSetPrototype(context, value, Messages::Make(context, super));
     return value;
 }
 
@@ -406,7 +408,7 @@ bool CYIsKindOfClass(id object, Class _class) {
 
 JSObjectRef Instance::Make(JSContextRef context, id object, Flags flags) {
     JSObjectRef value(JSObjectMake(context, CYIsKindOfClass(object, NSBlock_) ? FunctionInstance_ : Instance_, new Instance(object, flags)));
-    JSObjectSetPrototype(context, value, CYGetClassPrototype(context, object_getClass(object)));
+    CYSetPrototype(context, value, CYGetClassPrototype(context, object_getClass(object)));
     return value;
 }
 
@@ -450,7 +452,7 @@ JSObjectRef CYMakeInstance(JSContextRef context, id object, bool transient) {
 - (JSType) cy$JSType;
 
 - (JSValueRef) cy$toJSON:(NSString *)key inContext:(JSContextRef)context;
-- (NSString *) cy$toCYON:(bool)objective;
+- (NSString *) cy$toCYON:(bool)objective inSet:(std::set<void *> &)objects;
 
 - (bool) cy$hasProperty:(NSString *)name;
 - (NSObject *) cy$getProperty:(NSString *)name;
@@ -468,31 +470,32 @@ JSObjectRef CYMakeInstance(JSContextRef context, id object, bool transient) {
 - (JSValueRef) cy$valueOfInContext:(JSContextRef)context;
 @end
 
-NSString *CYCastNSCYON(id value, bool objective) {
+NSString *CYCastNSCYON(id value, bool objective, std::set<void *> &objects) {
     NSString *string;
 
     if (value == nil)
         string = @"nil";
     else {
         Class _class(object_getClass(value));
-        SEL sel(@selector(cy$toCYON:));
+        SEL sel(@selector(cy$toCYON:inSet:));
 
-        if (objc_method *toCYON = class_getInstanceMethod(_class, sel))
-            string = reinterpret_cast<NSString *(*)(id, SEL, bool)>(method_getImplementation(toCYON))(value, sel, objective);
+        if (class_isMetaClass(_class)) {
+            const char *name(class_getName(value));
+            if (class_isMetaClass(value))
+                string = [NSString stringWithFormat:@"object_getClass(%s)", name];
+            else
+                string = [NSString stringWithUTF8String:name];
+        } else if (objc_method *toCYON = class_getInstanceMethod(_class, sel))
+            string = reinterpret_cast<NSString *(*)(id, SEL, bool, std::set<void *> &)>(method_getImplementation(toCYON))(value, sel, objective, objects);
         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:objective];
+                string = [value cy$toCYON:objective inSet:objects];
             else goto fail;
         } else fail: {
             if (false);
 #ifdef __APPLE__
-            else if (value == NSZombie_)
-                string = @"_NSZombie_";
             else if (_class == NSZombie_)
                 string = [NSString stringWithFormat:@"<_NSZombie_: %p>", value];
-            // XXX: frowny /in/ the pants
-            else if (banned_.find(value) != banned_.end())
-                string = nil;
 #endif
             else
                 string = [NSString stringWithFormat:@"%@", value];
@@ -506,6 +509,15 @@ NSString *CYCastNSCYON(id value, bool objective) {
     return string;
 }
 
+NSString *CYCastNSCYON(id value, bool objective, std::set<void *> *objects) {
+    if (objects != NULL)
+        return CYCastNSCYON(value, objective, *objects);
+    else {
+        std::set<void *> objects;
+        return CYCastNSCYON(value, objective, objects);
+    }
+}
+
 #ifdef __APPLE__
 struct PropertyAttributes {
     CYPool pool_;
@@ -792,7 +804,9 @@ NSObject *CYCopyNSObject(CYPool &pool, JSContextRef context, JSValueRef value) {
     return [[self mutableCopy] autorelease];
 }
 
-- (NSString *) cy$toCYON:(bool)objective {
+- (NSString *) cy$toCYON:(bool)objective inSet:(std::set<void *> &)objects {
+    _oassert(objects.insert(self).second);
+
     NSMutableString *json([[[NSMutableString alloc] init] autorelease]);
     [json appendString:@"@["];
 
@@ -808,7 +822,7 @@ NSObject *CYCopyNSObject(CYPool &pool, JSContextRef context, JSValueRef value) {
         else
             comma = true;
         if (object == nil || [object cy$JSType] != kJSTypeUndefined)
-            [json appendString:CYCastNSCYON(object, true)];
+            [json appendString:CYCastNSCYON(object, true, objects)];
         else {
             [json appendString:@","];
             comma = false;
@@ -881,7 +895,7 @@ NSObject *CYCopyNSObject(CYPool &pool, JSContextRef context, JSValueRef value) {
     return kJSTypeBoolean;
 }
 
-- (NSString *) cy$toCYON:(bool)objective {
+- (NSString *) cy$toCYON:(bool)objective inSet:(std::set<void *> &)objects {
     NSString *value([self boolValue] ? @"true" : @"false");
     return objective ? value : [NSString stringWithFormat:@"@%@", value];
 }
@@ -900,7 +914,9 @@ NSObject *CYCopyNSObject(CYPool &pool, JSContextRef context, JSValueRef value) {
     return [[self mutableCopy] autorelease];
 }
 
-- (NSString *) cy$toCYON:(bool)objective {
+- (NSString *) cy$toCYON:(bool)objective inSet:(std::set<void *> &)objects {
+    _oassert(objects.insert(self).second);
+
     NSMutableString *json([[[NSMutableString alloc] init] autorelease]);
     [json appendString:@"@{"];
 
@@ -915,10 +931,10 @@ NSObject *CYCopyNSObject(CYPool &pool, JSContextRef context, JSValueRef value) {
             [json appendString:@","];
         else
             comma = true;
-        [json appendString:CYCastNSCYON(key, true)];
+        [json appendString:CYCastNSCYON(key, true, objects)];
         [json appendString:@":"];
         NSObject *object([self objectForKey:key]);
-        [json appendString:CYCastNSCYON(object, true)];
+        [json appendString:CYCastNSCYON(object, true, objects)];
     }
 
     [json appendString:@"}"];
@@ -1038,7 +1054,7 @@ NSObject *CYCopyNSObject(CYPool &pool, JSContextRef context, JSValueRef value) {
     return kJSTypeNumber;
 }
 
-- (NSString *) cy$toCYON:(bool)objective {
+- (NSString *) cy$toCYON:(bool)objective inSet:(std::set<void *> &)objects {
     NSString *value([self cy$JSType] != kJSTypeBoolean ? [self stringValue] : [self boolValue] ? @"true" : @"false");
     return objective ? value : [NSString stringWithFormat:@"@%@", value];
 }
@@ -1056,7 +1072,7 @@ NSObject *CYCopyNSObject(CYPool &pool, JSContextRef context, JSValueRef value) {
     return kJSTypeNull;
 }
 
-- (NSString *) cy$toCYON:(bool)objective {
+- (NSString *) cy$toCYON:(bool)objective inSet:(std::set<void *> &)objects {
     NSString *value(@"null");
     return objective ? value : [NSString stringWithFormat:@"@%@", value];
 }
@@ -1086,8 +1102,8 @@ NSObject *CYCopyNSObject(CYPool &pool, JSContextRef context, JSValueRef value) {
     return kJSTypeObject;
 }
 
-- (NSString *) cy$toCYON:(bool)objective {
-    return [@"#" stringByAppendingString:[[self description] cy$toCYON:true]];
+- (NSString *) cy$toCYON:(bool)objective inSet:(std::set<void *> &)objects {
+    return [@"#" stringByAppendingString:[[self description] cy$toCYON:true inSet:objects]];
 }
 
 - (bool) cy$hasProperty:(NSString *)name {
@@ -1124,8 +1140,8 @@ NSObject *CYCopyNSObject(CYPool &pool, JSContextRef context, JSValueRef value) {
 /* Bridge: NSProxy {{{ */
 @implementation NSProxy (Cycript)
 
-- (NSString *) cy$toCYON:(bool)objective {
-    return [[self description] cy$toCYON:objective];
+- (NSString *) cy$toCYON:(bool)objective inSet:(std::set<void *> &)objects {
+    return [[self description] cy$toCYON:objective inSet:objects];
 }
 
 @end
@@ -1133,10 +1149,12 @@ NSObject *CYCopyNSObject(CYPool &pool, JSContextRef context, JSValueRef value) {
 /* Bridge: NSSet {{{ */
 @implementation NSSet (Cycript)
 
-- (NSString *) cy$toCYON:(bool)objective {
+- (NSString *) cy$toCYON:(bool)objective inSet:(std::set<void *> &)objects {
+    _oassert(objects.insert(self).second);
+
     NSMutableString *json([[[NSMutableString alloc] init] autorelease]);
     [json appendString:@"[NSSet setWithArray:"];
-    [json appendString:CYCastNSCYON([self allObjects], true)];
+    [json appendString:CYCastNSCYON([self allObjects], true, objects)];
     [json appendString:@"]]"];
     return json;
 }
@@ -1154,7 +1172,7 @@ NSObject *CYCopyNSObject(CYPool &pool, JSContextRef context, JSValueRef value) {
     return kJSTypeString;
 }
 
-- (NSString *) cy$toCYON:(bool)objective {
+- (NSString *) cy$toCYON:(bool)objective inSet:(std::set<void *> &)objects {
     std::ostringstream str;
     if (!objective)
         str << '@';
@@ -1203,7 +1221,7 @@ NSObject *CYCopyNSObject(CYPool &pool, JSContextRef context, JSValueRef value) {
     return kJSTypeUndefined;
 }
 
-- (NSString *) cy$toCYON:(bool)objective {
+- (NSString *) cy$toCYON:(bool)objective inSet:(std::set<void *> &)objects {
     NSString *value(@"undefined");
     return value; // XXX: maybe use the below code, adding @undefined?
     //return objective ? value : [NSString stringWithFormat:@"@%@", value];
@@ -1265,11 +1283,11 @@ JSValueRef CYCastJSValue(JSContextRef context, NSObject *value) { CYPoolTry {
     [super dealloc];
 } CYObjectiveCatch }
 
-- (NSString *) cy$toCYON:(bool)objective { CYObjectiveTry {
+- (NSString *) cy$toCYON:(bool)objective inSet:(std::set<void *> &)objects { CYObjectiveTry {
     CYPool pool;
-    const char *cyon(CYPoolCCYON(pool, context, object_));
+    const char *cyon(CYPoolCCYON(pool, context, object_, objects));
     if (cyon == NULL)
-        return [super cy$toCYON:objective];
+        return [super cy$toCYON:objective inSet:objects];
     else
         return [NSString stringWithUTF8String:cyon];
 } CYObjectiveCatch }
@@ -1307,9 +1325,9 @@ JSValueRef CYCastJSValue(JSContextRef context, NSObject *value) { CYPoolTry {
 
 @implementation CYJSArray
 
-- (NSString *) cy$toCYON:(bool)objective { CYObjectiveTry {
+- (NSString *) cy$toCYON:(bool)objective inSet:(std::set<void *> &)objects { CYObjectiveTry {
     CYPool pool;
-    return [NSString stringWithUTF8String:CYPoolCCYON(pool, context, object_)];
+    return [NSString stringWithUTF8String:CYPoolCCYON(pool, context, object_, objects)];
 } CYObjectiveCatch }
 
 - (id) initWithJSObject:(JSObjectRef)object inContext:(JSContextRef)context { CYObjectiveTry_ {
@@ -2048,6 +2066,7 @@ static void CYBitField(unsigned &length, unsigned &shift, id self, Ivar ivar, co
             break;
         else if (ivar_getOffset(ivars[i]) == offset) {
             const char *encoding(ivar_getTypeEncoding(ivars[i]));
+            _assert(encoding != NULL);
             _assert(encoding[0] == 'b');
             shift += CYCastDouble(encoding + 1);
         }
@@ -2071,6 +2090,7 @@ static JSValueRef Internal_getProperty(JSContextRef context, JSObjectRef object,
         void *data(reinterpret_cast<uint8_t *>(self) + offset);
 
         const char *encoding(ivar_getTypeEncoding(ivar));
+        _assert(encoding != NULL);
         if (encoding[0] == 'b') {
             unsigned length, shift;
             CYBitField(length, shift, self, ivar, encoding, offset);
@@ -2099,6 +2119,7 @@ static bool Internal_setProperty(JSContextRef context, JSObjectRef object, JSStr
         void *data(reinterpret_cast<uint8_t *>(self) + offset);
 
         const char *encoding(ivar_getTypeEncoding(ivar));
+        _assert(encoding != NULL);
         if (encoding[0] == 'b') {
             unsigned length, shift;
             CYBitField(length, shift, self, ivar, encoding, offset);
@@ -2631,11 +2652,13 @@ static JSValueRef Instance_getProperty_messages(JSContextRef context, JSObjectRe
 } CYCatch(NULL) }
 
 static JSValueRef Instance_callAsFunction_toCYON(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry {
+    std::set<void *> *objects(CYCastObjects(context, _this, count, arguments));
+
     if (!CYJSValueIsNSObject(context, _this))
         return NULL;
 
     Instance *internal(reinterpret_cast<Instance *>(JSObjectGetPrivate(_this)));
-    return CYCastJSValue(context, CYJSString(context, CYCastNSCYON(internal->GetValue(), false)));
+    return CYCastJSValue(context, CYJSString(context, CYCastNSCYON(internal->GetValue(), false, objects)));
 } CYCatch(NULL) }
 
 static JSValueRef Instance_callAsFunction_toJSON(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry {
@@ -2843,12 +2866,6 @@ void CYObjectiveC_Initialize() { /*XXX*/ JSContextRef context(NULL); CYPoolTry {
     NSCFType_ = objc_getClass("NSCFType");
 
     NSZombie_ = objc_getClass("_NSZombie_");
-
-    banned_.insert(Object_);
-    banned_.insert(objc_getClass("__NSAtom"));
-    banned_.insert(objc_getClass("__NSGenericDeallocHandler"));
-    banned_.insert(objc_getClass("NSMessageBuilder"));
-    banned_.insert(objc_getClass("__NSMessageBuilder"));
 #else
     NSBoolNumber_ = objc_getClass("NSBoolNumber");
 #endif
@@ -2972,12 +2989,14 @@ void CYObjectiveC_Initialize() { /*XXX*/ JSContextRef context(NULL); CYPoolTry {
     ObjectiveC_Protocols_ = JSClassCreate(&definition);
 
 #ifdef __APPLE__
-// XXX: this is horrible; there has to be a better way to do this
-#ifdef __LP64__
-    class_addMethod(NSCFType_, @selector(cy$toJSON:inContext:), reinterpret_cast<IMP>(&NSCFType$cy$toJSON$inContext$), "^{OpaqueJSValue=}32@0:8@16^{OpaqueJSContext=}24");
-#else
-    class_addMethod(NSCFType_, @selector(cy$toJSON:inContext:), reinterpret_cast<IMP>(&NSCFType$cy$toJSON$inContext$), "^{OpaqueJSValue=}16@0:4@8^{OpaqueJSContext=}12");
-#endif
+    class_addMethod(NSCFType_, @selector(cy$toJSON:inContext:), reinterpret_cast<IMP>(&NSCFType$cy$toJSON$inContext$),
+        // XXX: this is horrible; there has to be a better way to do this
+    #ifdef __LP64__
+        "^{OpaqueJSValue=}32@0:8@16^{OpaqueJSContext=}24"
+    #else
+        "^{OpaqueJSValue=}16@0:4@8^{OpaqueJSContext=}12"
+    #endif
+    );
 #endif
 } CYPoolCatch() }
 
@@ -3020,41 +3039,41 @@ void CYObjectiveC_SetupContext(JSContextRef context) { CYPoolTry {
     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);
+    CYSetPrototype(context, ArrayInstance_prototype, Array_prototype);
 
     JSObjectRef BooleanInstance(JSObjectMakeConstructor(context, BooleanInstance_, NULL));
     JSObjectRef BooleanInstance_prototype(CYCastJSObject(context, CYGetProperty(context, BooleanInstance, prototype_s)));
     CYSetProperty(context, cy, CYJSString("BooleanInstance_prototype"), BooleanInstance_prototype);
     JSObjectRef Boolean_prototype(CYGetCachedObject(context, CYJSString("Boolean_prototype")));
-    JSObjectSetPrototype(context, BooleanInstance_prototype, Boolean_prototype);
+    CYSetPrototype(context, BooleanInstance_prototype, Boolean_prototype);
 
     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);
+    CYSetPrototype(context, FunctionInstance_prototype, Function_prototype);
 
     JSObjectRef NumberInstance(JSObjectMakeConstructor(context, NumberInstance_, NULL));
     JSObjectRef NumberInstance_prototype(CYCastJSObject(context, CYGetProperty(context, NumberInstance, prototype_s)));
     CYSetProperty(context, cy, CYJSString("NumberInstance_prototype"), NumberInstance_prototype);
     JSObjectRef Number_prototype(CYGetCachedObject(context, CYJSString("Number_prototype")));
-    JSObjectSetPrototype(context, NumberInstance_prototype, Number_prototype);
+    CYSetPrototype(context, NumberInstance_prototype, Number_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);
+    CYSetPrototype(context, ObjectInstance_prototype, Object_prototype);
 
     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")));
-    JSObjectSetPrototype(context, StringInstance_prototype, String_prototype);
+    CYSetPrototype(context, StringInstance_prototype, String_prototype);
 
     JSObjectRef Class_prototype(CYCastJSObject(context, CYGetProperty(context, Class, prototype_s)));
     CYSetProperty(context, cy, CYJSString("Class_prototype"), Class_prototype);
-    JSObjectSetPrototype(context, Class_prototype, Instance_prototype);
+    CYSetPrototype(context, Class_prototype, Instance_prototype);
 
     CYSetProperty(context, cycript, CYJSString("Instance"), Instance);
     CYSetProperty(context, cycript, CYJSString("Selector"), Selector);
@@ -3069,8 +3088,8 @@ void CYObjectiveC_SetupContext(JSContextRef context) { CYPoolTry {
 
     CYSetProperty(context, all, CYJSString("objc_msgSend"), &$objc_msgSend, kJSPropertyAttributeDontEnum);
 
-    JSObjectSetPrototype(context, CYCastJSObject(context, CYGetProperty(context, Message, prototype_s)), Function_prototype);
-    JSObjectSetPrototype(context, CYCastJSObject(context, CYGetProperty(context, Selector, prototype_s)), Function_prototype);
+    CYSetPrototype(context, CYCastJSObject(context, CYGetProperty(context, Message, prototype_s)), Function_prototype);
+    CYSetPrototype(context, CYCastJSObject(context, CYGetProperty(context, Selector, prototype_s)), Function_prototype);
 } CYPoolCatch() }
 
 static CYHooks CYObjectiveCHooks = {