X-Git-Url: https://git.saurik.com/cycript.git/blobdiff_plain/64b8d29fad73d4dee83b8f0a9f35f30d9cc72846..2f1295ff240efc811e88b334caf08630a9966275:/ObjectiveC/Library.mm diff --git a/ObjectiveC/Library.mm b/ObjectiveC/Library.mm index 3608867..c43957e 100644 --- a/ObjectiveC/Library.mm +++ b/ObjectiveC/Library.mm @@ -1,48 +1,24 @@ -/* Cycript - Inlining/Optimizing JavaScript Compiler - * Copyright (C) 2009 Jay Freeman (saurik) +/* Cycript - Optimizing JavaScript Compiler/Runtime + * Copyright (C) 2009-2010 Jay Freeman (saurik) */ -/* Modified BSD License {{{ */ +/* GNU Lesser General Public License, Version 3 {{{ */ /* - * Redistribution and use in source and binary - * forms, with or without modification, are permitted - * provided that the following conditions are met: + * Cycript is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. * - * 1. Redistributions of source code must retain the - * above copyright notice, this list of conditions - * and the following disclaimer. - * 2. Redistributions in binary form must reproduce the - * above copyright notice, this list of conditions - * and the following disclaimer in the documentation - * and/or other materials provided with the - * distribution. - * 3. The name of the author may not be used to endorse - * or promote products derived from this software - * without specific prior written permission. + * Cycript 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 Lesser General Public + * License for more details. * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, - * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ + * You should have received a copy of the GNU Lesser General Public License + * along with Cycript. If not, see . +**/ /* }}} */ -#if defined(__APPLE__) && defined(__arm__) -#include -#else -#include -#endif - #ifdef __APPLE__ #include "Struct.hpp" #endif @@ -51,7 +27,7 @@ #include "ObjectiveC/Internal.hpp" -#include +#include #include "cycript.hpp" @@ -61,12 +37,13 @@ #include #include #include +#include #endif #include "Error.hpp" #include "JavaScript.hpp" #include "String.hpp" -#include "Bridge.hpp" +#include "Execute.hpp" #include #include @@ -131,6 +108,7 @@ #define object_getInstanceVariable(object, name, value) ({ \ objc_ivar *ivar(class_getInstanceVariable(object_getClass(object), name)); \ + _assert(value != NULL); \ if (ivar != NULL) \ GSObjCGetVariable(object, ivar_getOffset(ivar), sizeof(void *), value); \ ivar; \ @@ -260,6 +238,7 @@ static JSClassRef Internal_; static JSClassRef Message_; static JSClassRef Messages_; static JSClassRef Selector_; +static JSClassRef StringInstance_; static JSClassRef Super_; static JSClassRef ObjectiveC_Classes_; @@ -273,6 +252,7 @@ static JSClassRef ObjectiveC_Images_; #ifdef __APPLE__ static Class NSCFBoolean_; static Class NSCFType_; +static Class NSGenericDeallocHandler_; static Class NSMessageBuilder_; static Class NSZombie_; #else @@ -281,6 +261,7 @@ static Class NSBoolNumber_; static Class NSArray_; static Class NSDictionary_; +static Class NSString_; static Class Object_; static Type_privateData *Object_type; @@ -294,6 +275,8 @@ Type_privateData *Selector_privateData::GetType() const { return Selector_type; } +static JSValueRef Instance_callAsFunction_toString(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception); + JSValueRef CYGetClassPrototype(JSContextRef context, id self) { if (self == nil) return CYGetCachedObject(context, CYJSString("Instance_prototype")); @@ -316,12 +299,15 @@ JSValueRef CYGetClassPrototype(JSContextRef context, id self) { prototype = CYGetCachedObject(context, CYJSString("Array_prototype")); else if (self == NSDictionary_) prototype = CYGetCachedObject(context, CYJSString("Object_prototype")); + else if (self == NSString_) + prototype = CYGetCachedObject(context, CYJSString("StringInstance_prototype")); else prototype = CYGetClassPrototype(context, class_getSuperclass(self)); JSObjectRef object(JSObjectMake(context, _class, NULL)); JSObjectSetPrototype(context, object, prototype); CYSetProperty(context, cy, name, object); + return object; } @@ -403,6 +389,8 @@ JSObjectRef CYMakeInstance(JSContextRef context, id object, bool transient) { - (bool) cy$deleteProperty:(NSString *)name; - (void) cy$getPropertyNames:(JSPropertyNameAccumulatorRef)names inContext:(JSContextRef)context; ++ (bool) cy$hasImplicitProperties; + @end @protocol Cycript @@ -432,7 +420,7 @@ NSString *CYCastNSCYON(id value) { else if (_class == NSZombie_) string = [NSString stringWithFormat:@"<_NSZombie_: %p>", value]; // XXX: frowny /in/ the pants - else if (value == NSMessageBuilder_ || value == Object_) + else if (value == NSGenericDeallocHandler_ || value == NSMessageBuilder_ || value == Object_) string = nil; #endif else @@ -759,6 +747,10 @@ NSObject *CYCopyNSObject(apr_pool_t *pool, JSContextRef context, JSValueRef valu } } ++ (bool) cy$hasImplicitProperties { + return false; +} + @end /* }}} */ /* Bridge: NSBoolNumber {{{ */ @@ -833,6 +825,10 @@ NSObject *CYCopyNSObject(apr_pool_t *pool, JSContextRef context, JSValueRef valu } } ++ (bool) cy$hasImplicitProperties { + return false; +} + @end /* }}} */ /* Bridge: NSMutableArray {{{ */ @@ -994,6 +990,10 @@ NSObject *CYCopyNSObject(apr_pool_t *pool, JSContextRef context, JSValueRef valu - (void) cy$getPropertyNames:(JSPropertyNameAccumulatorRef)names inContext:(JSContextRef)context { } ++ (bool) cy$hasImplicitProperties { + return true; +} + @end /* }}} */ /* Bridge: NSProxy {{{ */ @@ -1034,6 +1034,49 @@ NSObject *CYCopyNSObject(apr_pool_t *pool, JSContextRef context, JSValueRef valu return [self cy$toCYON]; } +- (bool) cy$hasProperty:(NSString *)name { + if ([name isEqualToString:@"length"]) + return true; + + size_t index(CYGetIndex(name)); + if (index == _not(size_t) || index >= [self length]) + return [super cy$hasProperty:name]; + else + return true; +} + +- (NSObject *) cy$getProperty:(NSString *)name { + if ([name isEqualToString:@"length"]) { + NSUInteger count([self length]); +#ifdef __APPLE__ + return [NSNumber numberWithUnsignedInteger:count]; +#else + return [NSNumber numberWithUnsignedInt:count]; +#endif + } + + size_t index(CYGetIndex(name)); + if (index == _not(size_t) || index >= [self length]) + return [super cy$getProperty:name]; + else + return [self substringWithRange:NSMakeRange(index, 1)]; +} + +- (void) cy$getPropertyNames:(JSPropertyNameAccumulatorRef)names inContext:(JSContextRef)context { + [super cy$getPropertyNames:names inContext:context]; + + for (size_t index(0), length([self length]); index != length; ++index) { + char name[32]; + sprintf(name, "%zu", index); + JSPropertyNameAccumulatorAddName(names, CYJSString(name)); + } +} + +// XXX: this might be overly restrictive for NSString; I think I need a half-way between /injecting/ implicit properties and /accepting/ implicit properties ++ (bool) cy$hasImplicitProperties { + return false; +} + @end /* }}} */ /* Bridge: WebUndefined {{{ */ @@ -1343,6 +1386,8 @@ static void CYObjectiveC_CallFunction(JSContextRef context, ffi_cif *cif, void ( static bool CYObjectiveC_PoolFFI(apr_pool_t *pool, JSContextRef context, sig::Type *type, ffi_type *ffi, void *data, JSValueRef value) { CYSadTry { switch (type->primitive) { + // XXX: do something epic about blocks + case sig::block_P: case sig::object_P: case sig::typename_P: *reinterpret_cast(data) = CYCastNSObject(pool, context, value); @@ -1361,6 +1406,8 @@ static bool CYObjectiveC_PoolFFI(apr_pool_t *pool, JSContextRef context, sig::Ty static JSValueRef CYObjectiveC_FromFFI(JSContextRef context, sig::Type *type, ffi_type *ffi, void *data, bool initialize, JSObjectRef owner) { CYPoolTry { switch (type->primitive) { + // XXX: do something epic about blocks + case sig::block_P: case sig::object_P: if (NSObject *object = *reinterpret_cast(data)) { JSValueRef value(CYCastJSValue(context, object)); @@ -1552,6 +1599,13 @@ static void Messages_getPropertyNames(JSContextRef context, JSObjectRef object, #endif } +static bool CYHasImplicitProperties(Class _class) { + // XXX: this is an evil hack to deal with NSProxy; fix elsewhere + if (!CYImplements(_class, object_getClass(_class), @selector(cy$hasImplicitProperties), false)) + return true; + return [_class cy$hasImplicitProperties]; +} + static bool Instance_hasProperty(JSContextRef context, JSObjectRef object, JSStringRef property) { Instance *internal(reinterpret_cast(JSObjectGetPrivate(object))); id self(internal->GetValue()); @@ -1582,9 +1636,10 @@ static bool Instance_hasProperty(JSContextRef context, JSObjectRef object, JSStr return true; #endif - if (SEL sel = sel_getUid(string)) - if (CYImplements(self, _class, sel, true)) - return true; + if (CYHasImplicitProperties(_class)) + if (SEL sel = sel_getUid(string)) + if (CYImplements(self, _class, sel, true)) + return true; return false; } @@ -1619,9 +1674,10 @@ static JSValueRef Instance_getProperty(JSContextRef context, JSObjectRef object, } #endif - if (SEL sel = sel_getUid(string)) - if (CYImplements(self, _class, sel, true)) - return CYSendMessage(pool, context, self, NULL, sel, 0, NULL, false, exception); + if (CYHasImplicitProperties(_class)) + if (SEL sel = sel_getUid(string)) + if (CYImplements(self, _class, sel, true)) + return CYSendMessage(pool, context, self, NULL, sel, 0, NULL, false, exception); return NULL; } CYCatch } @@ -1695,6 +1751,18 @@ static bool Instance_deleteProperty(JSContextRef context, JSObjectRef object, JS } CYPoolCatch(NULL) } CYCatch return /*XXX*/ NULL; } +static void Instance_getPropertyNames_message(JSPropertyNameAccumulatorRef names, objc_method *method) { + const char *name(sel_getName(method_getName(method))); + if (strchr(name, ':') != NULL) + return; + + const char *type(method_getTypeEncoding(method)); + if (type == NULL || *type == '\0' || *type == 'v') + return; + + JSPropertyNameAccumulatorAddName(names, CYJSString(name)); +} + static void Instance_getPropertyNames(JSContextRef context, JSObjectRef object, JSPropertyNameAccumulatorRef names) { Instance *internal(reinterpret_cast(JSObjectGetPrivate(object))); id self(internal->GetValue()); @@ -1712,6 +1780,21 @@ static void Instance_getPropertyNames(JSContextRef context, JSObjectRef object, } #endif + if (CYHasImplicitProperties(_class)) + for (Class current(_class); current != nil; current = class_getSuperclass(current)) { +#if OBJC_API_VERSION >= 2 + unsigned int size; + objc_method **data(class_copyMethodList(current, &size)); + for (size_t i(0); i != size; ++i) + Instance_getPropertyNames_message(names, data[i]); + free(data); +#else + for (objc_method_list *methods(current->methods); methods != NULL; methods = methods->method_next) + for (int i(0); i != methods->method_count; ++i) + Instance_getPropertyNames_message(names, &methods->method_list[i]); +#endif + } + CYPoolTry { // XXX: this is an evil hack to deal with NSProxy; fix elsewhere if (CYImplements(self, _class, @selector(cy$getPropertyNames:inContext:), false)) @@ -2029,7 +2112,7 @@ static JSValueRef $objc_msgSend(JSContextRef context, JSObjectRef object, JSObje } CYCatch } /* Hook: objc_registerClassPair {{{ */ -#if defined(__APPLE__) && defined(__arm__) +#if defined(__APPLE__) && defined(__arm__) && 0 // XXX: replace this with associated objects MSHook(void, CYDealloc, id self, SEL sel) { @@ -2107,7 +2190,7 @@ static JSObjectRef Instance_new(JSContextRef context, JSObjectRef object, size_t if (count > 1) throw CYJSError(context, "incorrect number of arguments to Instance constructor"); id self(count == 0 ? nil : CYCastPointer(context, arguments[0])); - return Instance::Make(context, self); + return CYMakeInstance(context, self, false); } CYCatch } static JSValueRef CYValue_getProperty_value(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { @@ -2138,7 +2221,7 @@ static JSValueRef Instance_getProperty_constructor(JSContextRef context, JSObjec return Instance::Make(context, (id) object_getClass(internal->GetValue())); } -static JSValueRef Instance_getProperty_protocol(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { CYTry { +static JSValueRef Instance_getProperty_prototype(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { CYTry { Instance *internal(reinterpret_cast(JSObjectGetPrivate(object))); id self(internal->GetValue()); if (!CYIsClass(self)) @@ -2179,12 +2262,35 @@ static JSValueRef Instance_callAsFunction_toJSON(JSContextRef context, JSObjectR } CYPoolCatch(NULL) } CYCatch return /*XXX*/ NULL; } +#if 0 +static JSValueRef Instance_callAsFunction_valueOf(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry { + if (!JSValueIsObjectOfClass(context, _this, Instance_)) + return NULL; + + Instance *internal(reinterpret_cast(JSObjectGetPrivate(_this))); + return CYCastJSValue(context, reinterpret_cast(internal->GetValue())); +} CYCatch return /*XXX*/ NULL; } +#endif + +static JSValueRef Instance_callAsFunction_toPointer(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry { + if (!JSValueIsObjectOfClass(context, _this, Instance_)) + return NULL; + + Instance *internal(reinterpret_cast(JSObjectGetPrivate(_this))); + // XXX: but... but... THIS ISN'T A POINTER! :( + return CYCastJSValue(context, reinterpret_cast(internal->GetValue())); +} CYCatch return /*XXX*/ NULL; } + static JSValueRef Instance_callAsFunction_toString(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry { if (!JSValueIsObjectOfClass(context, _this, Instance_)) return NULL; Instance *internal(reinterpret_cast(JSObjectGetPrivate(_this))); + id value(internal->GetValue()); + if (value == nil) + return CYCastJSValue(context, "nil"); + CYPoolTry { // XXX: this seems like a stupid implementation; what if it crashes? why not use the CYONifier backend? return CYCastJSValue(context, CYJSString(context, [internal->GetValue() description])); @@ -2238,15 +2344,17 @@ static JSStaticValue Selector_staticValues[2] = { static JSStaticValue Instance_staticValues[5] = { {"constructor", &Instance_getProperty_constructor, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, {"messages", &Instance_getProperty_messages, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"prototype", &Instance_getProperty_protocol, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"prototype", &Instance_getProperty_prototype, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, {"value", &CYValue_getProperty_value, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, {NULL, NULL, NULL, 0} }; -static JSStaticFunction Instance_staticFunctions[5] = { +static JSStaticFunction Instance_staticFunctions[6] = { {"$cya", &CYValue_callAsFunction_$cya, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, {"toCYON", &Instance_callAsFunction_toCYON, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, {"toJSON", &Instance_callAsFunction_toJSON, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + //{"valueOf", &Instance_callAsFunction_valueOf, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"toPointer", &Instance_callAsFunction_toPointer, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, {"toString", &Instance_callAsFunction_toString, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, {NULL, NULL, 0} }; @@ -2264,6 +2372,12 @@ static JSStaticFunction Selector_staticFunctions[5] = { {NULL, NULL, 0} }; +static JSStaticFunction StringInstance_staticFunctions[2] = { + //{"valueOf", &Instance_callAsFunction_valueOf, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"toString", &Instance_callAsFunction_toString, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {NULL, NULL, 0} +}; + void CYObjectiveC_Initialize() { /*XXX*/ JSContextRef context(NULL); CYPoolTry { apr_pool_t *pool(CYGetGlobalPool()); @@ -2273,6 +2387,7 @@ void CYObjectiveC_Initialize() { /*XXX*/ JSContextRef context(NULL); CYPoolTry { #ifdef __APPLE__ NSCFBoolean_ = objc_getClass("NSCFBoolean"); NSCFType_ = objc_getClass("NSCFType"); + NSGenericDeallocHandler_ = objc_getClass("__NSGenericDeallocHandler"); NSMessageBuilder_ = objc_getClass("NSMessageBuilder"); NSZombie_ = objc_getClass("_NSZombie_"); #else @@ -2281,6 +2396,7 @@ void CYObjectiveC_Initialize() { /*XXX*/ JSContextRef context(NULL); CYPoolTry { NSArray_ = objc_getClass("NSArray"); NSDictionary_ = objc_getClass("NSDictionary"); + NSString_ = objc_getClass("NSString"); Object_ = objc_getClass("Object"); JSClassDefinition definition; @@ -2336,6 +2452,11 @@ void CYObjectiveC_Initialize() { /*XXX*/ JSContextRef context(NULL); CYPoolTry { definition.finalize = &CYFinalize; Selector_ = JSClassCreate(&definition); + definition = kJSClassDefinitionEmpty; + definition.className = "StringInstance"; + definition.staticFunctions = StringInstance_staticFunctions; + StringInstance_ = JSClassCreate(&definition); + definition = kJSClassDefinitionEmpty; definition.className = "Super"; definition.staticFunctions = Internal_staticFunctions; @@ -2368,7 +2489,7 @@ void CYObjectiveC_Initialize() { /*XXX*/ JSContextRef context(NULL); CYPoolTry { definition.getPropertyNames = &ObjectiveC_Protocols_getPropertyNames; ObjectiveC_Protocols_ = JSClassCreate(&definition); -#if defined(__APPLE__) && defined(__arm__) +#if defined(__APPLE__) && defined(__arm__) && 0 MSHookFunction(&objc_registerClassPair, MSHake(objc_registerClassPair)); #endif @@ -2396,16 +2517,23 @@ void CYObjectiveC_SetupContext(JSContextRef context) { CYPoolTry { JSObjectRef Instance(JSObjectMakeConstructor(context, Instance_, &Instance_new)); JSObjectRef Message(JSObjectMakeConstructor(context, Message_, NULL)); JSObjectRef Selector(JSObjectMakeConstructor(context, Selector_, &Selector_new)); + JSObjectRef StringInstance(JSObjectMakeConstructor(context, StringInstance_, NULL)); JSObjectRef Super(JSObjectMakeConstructor(context, Super_, &Super_new)); JSObjectRef Instance_prototype(CYCastJSObject(context, CYGetProperty(context, Instance, prototype_s))); CYSetProperty(context, cy, CYJSString("Instance_prototype"), Instance_prototype); + 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); + CYSetProperty(context, cycript, CYJSString("Instance"), Instance); CYSetProperty(context, cycript, CYJSString("Selector"), Selector); CYSetProperty(context, cycript, CYJSString("Super"), Super); -#if defined(__APPLE__) && defined(__arm__) +#if defined(__APPLE__) && defined(__arm__) && 0 CYSetProperty(context, all, CYJSString("objc_registerClassPair"), &objc_registerClassPair_, kJSPropertyAttributeDontEnum); #endif @@ -2430,5 +2558,7 @@ static CYHooks CYObjectiveCHooks = { 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;