-/* 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 <http://www.gnu.org/licenses/>.
+**/
/* }}} */
-#if defined(__APPLE__) && defined(__arm__)
-#include <substrate.h>
-#else
-#include <objc/objc-api.h>
-#endif
-
#ifdef __APPLE__
#include "Struct.hpp"
#endif
#include "ObjectiveC/Internal.hpp"
-#include <objc/Protocol.h>
+#include <objc/objc-api.h>
#include "cycript.hpp"
#include <CoreFoundation/CoreFoundation.h>
#include <JavaScriptCore/JSStringRefCF.h>
#include <WebKit/WebScriptObject.h>
+#include <objc/runtime.h>
#endif
#include "Error.hpp"
#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; \
static JSClassRef Message_;
static JSClassRef Messages_;
static JSClassRef Selector_;
+static JSClassRef StringInstance_;
static JSClassRef Super_;
static JSClassRef ObjectiveC_Classes_;
#ifdef __APPLE__
static Class NSCFBoolean_;
static Class NSCFType_;
+static Class NSGenericDeallocHandler_;
static Class NSMessageBuilder_;
static Class NSZombie_;
#else
static Class NSArray_;
static Class NSDictionary_;
+static Class NSString_;
static Class Object_;
static Type_privateData *Object_type;
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"));
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;
}
- (bool) cy$deleteProperty:(NSString *)name;
- (void) cy$getPropertyNames:(JSPropertyNameAccumulatorRef)names inContext:(JSContextRef)context;
++ (bool) cy$hasImplicitProperties;
+
@end
@protocol Cycript
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
}
}
++ (bool) cy$hasImplicitProperties {
+ return false;
+}
+
@end
/* }}} */
/* Bridge: NSBoolNumber {{{ */
}
}
++ (bool) cy$hasImplicitProperties {
+ return false;
+}
+
@end
/* }}} */
/* Bridge: NSMutableArray {{{ */
- (void) cy$getPropertyNames:(JSPropertyNameAccumulatorRef)names inContext:(JSContextRef)context {
}
++ (bool) cy$hasImplicitProperties {
+ return true;
+}
+
@end
/* }}} */
/* Bridge: NSProxy {{{ */
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 {{{ */
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<id *>(data) = CYCastNSObject(pool, context, value);
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<NSObject **>(data)) {
JSValueRef value(CYCastJSValue(context, 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<Instance *>(JSObjectGetPrivate(object)));
id self(internal->GetValue());
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;
}
}
#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 }
} 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<Instance *>(JSObjectGetPrivate(object)));
id self(internal->GetValue());
}
#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))
} 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) {
if (count > 1)
throw CYJSError(context, "incorrect number of arguments to Instance constructor");
id self(count == 0 ? nil : CYCastPointer<id>(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) {
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<Instance *>(JSObjectGetPrivate(object)));
id self(internal->GetValue());
if (!CYIsClass(self))
} 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<Instance *>(JSObjectGetPrivate(_this)));
+ return CYCastJSValue(context, reinterpret_cast<uintptr_t>(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<Instance *>(JSObjectGetPrivate(_this)));
+ // XXX: but... but... THIS ISN'T A POINTER! :(
+ return CYCastJSValue(context, reinterpret_cast<uintptr_t>(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;
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}
};
{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());
#ifdef __APPLE__
NSCFBoolean_ = objc_getClass("NSCFBoolean");
NSCFType_ = objc_getClass("NSCFType");
+ NSGenericDeallocHandler_ = objc_getClass("__NSGenericDeallocHandler");
NSMessageBuilder_ = objc_getClass("NSMessageBuilder");
NSZombie_ = objc_getClass("_NSZombie_");
#else
NSArray_ = objc_getClass("NSArray");
NSDictionary_ = objc_getClass("NSDictionary");
+ NSString_ = objc_getClass("NSString");
Object_ = objc_getClass("Object");
JSClassDefinition definition;
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;
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
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
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;