]> git.saurik.com Git - cycript.git/blobdiff - ObjectiveC/Library.mm
On iOS 9, JSObjectGetPrototype changes JSValueRef.
[cycript.git] / ObjectiveC / Library.mm
index 53d0611ac566f534c77f35988cb95b564ffb72c8..cb955092bf4872ebf5014e7b76f677aac5d5f376 100644 (file)
@@ -1,21 +1,21 @@
 /* Cycript - Optimizing JavaScript Compiler/Runtime
- * Copyright (C) 2009-2013  Jay Freeman (saurik)
+ * Copyright (C) 2009-2014  Jay Freeman (saurik)
 */
 
-/* GNU General Public License, Version 3 {{{ */
+/* GNU Affero General Public License, Version 3 {{{ */
 /*
- * Cycript is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation, either version 3 of the License,
- * or (at your option) any later version.
- *
- * Cycript is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Cycript.  If not, see <http://www.gnu.org/licenses/>.
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 **/
 /* }}} */
 
     } 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_ {
@@ -2081,8 +2099,8 @@ static JSValueRef Internal_getProperty(JSContextRef context, JSObjectRef object,
             uintptr_t mask((1 << length) - 1);
             return CYCastJSValue(context, (field >> shift) & mask);
         } else {
-            Type_privateData type(pool, ivar_getTypeEncoding(ivar));
-            return CYFromFFI(context, type.type_, type.GetFFI(), data);
+            auto type(new(pool) Type_privateData(ivar_getTypeEncoding(ivar)));
+            return CYFromFFI(context, type->type_, type->GetFFI(), data);
         }
     }
 
@@ -2110,8 +2128,8 @@ static bool Internal_setProperty(JSContextRef context, JSObjectRef object, JSStr
             uintptr_t mask((1 << length) - 1);
             field = field & ~(mask << shift) | (uintptr_t(CYCastDouble(context, value)) & mask) << shift;
         } else {
-            Type_privateData type(pool, ivar_getTypeEncoding(ivar));
-            CYPoolFFI(&pool, context, type.type_, type.GetFFI(), reinterpret_cast<uint8_t *>(self) + ivar_getOffset(ivar), value);
+            auto type(new(pool) Type_privateData(ivar_getTypeEncoding(ivar)));
+            CYPoolFFI(&pool, context, type->type_, type->GetFFI(), reinterpret_cast<uint8_t *>(self) + ivar_getOffset(ivar), value);
             return true;
         }
     }
@@ -2634,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 {
@@ -2712,16 +2732,11 @@ static JSValueRef Class_callAsFunction_pointerTo(JSContextRef context, JSObjectR
     if (!CYIsClass(value))
         CYThrow("non-Class object cannot be used as Type");
 
-    // XXX: this is a very silly implementation
-
-    std::ostringstream type;
-    type << "@\"";
-    type << class_getName(value);
-    type << "\"";
-
-    CYPoolTry {
-        return CYMakeType(context, type.str().c_str());
-    } CYPoolCatch(NULL)
+    sig::Type type;
+    memset(&type, 0, sizeof(type));
+    type.primitive = sig::object_P;
+    type.name = class_getName(value);
+    return CYMakeType(context, &type);
 } CYCatch(NULL) return /*XXX*/ NULL; }
 
 static JSValueRef Selector_callAsFunction_toString(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry {
@@ -2825,8 +2840,8 @@ void CYObjectiveC_Initialize() { /*XXX*/ JSContextRef context(NULL); CYPoolTry {
 
     CYPool &pool(CYGetGlobalPool());
 
-    Object_type = new(pool) Type_privateData("@");
-    Selector_type = new(pool) Type_privateData(":");
+    Object_type = new(pool) Type_privateData(sig::object_P);
+    Selector_type = new(pool) Type_privateData(sig::selector_P);
 
     NSArray_ = objc_getClass("NSArray");
     NSBlock_ = objc_getClass("NSBlock");
@@ -2846,12 +2861,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
@@ -2975,12 +2984,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() }
 
@@ -3023,41 +3034,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);
@@ -3072,11 +3083,11 @@ 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 = {
+static CYHook CYObjectiveCHook = {
     &CYObjectiveC_ExecuteStart,
     &CYObjectiveC_ExecuteEnd,
     &CYObjectiveC_CallFunction,
@@ -3086,13 +3097,7 @@ static CYHooks CYObjectiveCHooks = {
     &CYObjectiveC_FromFFI,
 };
 
-struct CYObjectiveC {
-    CYObjectiveC() {
-        hooks_ = &CYObjectiveCHooks;
-        // XXX: evil magic juju to make this actually take effect on a Mac when compiled with autoconf/libtool doom!
-        _assert(hooks_ != NULL);
-    }
-} CYObjectiveC;
+CYRegisterHook CYObjectiveC(&CYObjectiveCHook);
 
 extern "C" void CydgetSetupContext(JSGlobalContextRef context) { CYObjectiveTry_ {
     CYSetupContext(context);