+static bool CYImplements(id object, Class _class, SEL selector, bool devoid) {
+ if (Method method = class_getInstanceMethod(_class, selector)) {
+ if (!devoid)
+ return true;
+ char type[16];
+ method_getReturnType(method, type, sizeof(type));
+ if (type[0] != 'v')
+ return true;
+ }
+
+ // XXX: possibly use a more "awesome" check?
+ return false;
+}
+
+static const char *CYPoolTypeEncoding(apr_pool_t *pool, Class _class, SEL sel, Method method) {
+ if (method != NULL)
+ return method_getTypeEncoding(method);
+ else if (NSString *type = [[Bridge_ objectAtIndex:1] objectForKey:CYCastNSString(pool, sel_getName(sel))])
+ return CYPoolCString(pool, type);
+ else
+ return NULL;
+}
+
+static void FunctionClosure_(ffi_cif *cif, void *result, void **arguments, void *arg) {
+ Closure_privateData *internal(reinterpret_cast<Closure_privateData *>(arg));
+
+ JSContextRef context(internal->context_);
+
+ size_t count(internal->cif_.nargs);
+ JSValueRef values[count];
+
+ for (size_t index(0); index != count; ++index)
+ values[index] = CYFromFFI(context, internal->signature_.elements[1 + index].type, internal->cif_.arg_types[index], arguments[index]);
+
+ JSValueRef value(CYCallAsFunction(context, internal->function_, NULL, count, values));
+ CYPoolFFI(NULL, context, internal->signature_.elements[0].type, internal->cif_.rtype, result, value);
+}
+
+static void MessageClosure_(ffi_cif *cif, void *result, void **arguments, void *arg) {
+ Closure_privateData *internal(reinterpret_cast<Closure_privateData *>(arg));
+
+ JSContextRef context(internal->context_);
+
+ size_t count(internal->cif_.nargs);
+ JSValueRef values[count];
+
+ for (size_t index(0); index != count; ++index)
+ values[index] = CYFromFFI(context, internal->signature_.elements[1 + index].type, internal->cif_.arg_types[index], arguments[index]);
+
+ JSObjectRef _this(CYCastJSObject(context, values[0]));
+
+ JSValueRef value(CYCallAsFunction(context, internal->function_, _this, count - 2, values + 2));
+ CYPoolFFI(NULL, context, internal->signature_.elements[0].type, internal->cif_.rtype, result, value);
+}
+
+static Closure_privateData *CYMakeFunctor_(JSContextRef context, JSObjectRef function, const char *type, void (*callback)(ffi_cif *, void *, void **, void *)) {
+ // XXX: in case of exceptions this will leak
+ // XXX: in point of fact, this may /need/ to leak :(
+ Closure_privateData *internal(new Closure_privateData(CYGetJSContext(), function, type));
+
+ ffi_closure *closure((ffi_closure *) _syscall(mmap(
+ NULL, sizeof(ffi_closure),
+ PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE,
+ -1, 0
+ )));
+
+ ffi_status status(ffi_prep_closure(closure, &internal->cif_, callback, internal));
+ _assert(status == FFI_OK);
+
+ _syscall(mprotect(closure, sizeof(*closure), PROT_READ | PROT_EXEC));
+
+ internal->value_ = closure;
+
+ return internal;
+}
+
+static JSObjectRef CYMakeFunctor(JSContextRef context, JSObjectRef function, const char *type) {
+ Closure_privateData *internal(CYMakeFunctor_(context, function, type, &FunctionClosure_));
+ return JSObjectMake(context, Functor_, internal);
+}
+
+static JSObjectRef CYMakeFunctor(JSContextRef context, JSValueRef value, const char *type) {
+ JSValueRef exception(NULL);
+ bool function(JSValueIsInstanceOfConstructor(context, value, Function_, &exception));
+ CYThrow(context, exception);
+
+ if (function) {
+ JSObjectRef function(CYCastJSObject(context, value));
+ return CYMakeFunctor(context, function, type);
+ } else {
+ void (*function)()(CYCastPointer<void (*)()>(context, value));
+ return CYMakeFunctor(context, function, type);
+ }
+}
+
+static JSObjectRef CYMakeMessage(JSContextRef context, SEL sel, IMP imp, const char *type) {
+ Message_privateData *internal(new Message_privateData(sel, type, imp));
+ return JSObjectMake(context, Message_, internal);
+}
+
+static IMP CYMakeMessage(JSContextRef context, JSValueRef value, const char *type) {
+ JSObjectRef function(CYCastJSObject(context, value));
+ Closure_privateData *internal(CYMakeFunctor_(context, function, type, &MessageClosure_));
+ return reinterpret_cast<IMP>(internal->GetValue());
+}
+
+static bool Messages_hasProperty(JSContextRef context, JSObjectRef object, JSStringRef property) {
+ Messages *internal(reinterpret_cast<Messages *>(JSObjectGetPrivate(object)));
+ Class _class(internal->GetValue());
+
+ CYPool pool;
+ const char *name(CYPoolCString(pool, property));
+
+ if (SEL sel = sel_getUid(name))
+ if (class_getInstanceMethod(_class, sel) != NULL)
+ return true;
+
+ return false;
+}
+
+static JSValueRef Messages_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) {
+ Messages *internal(reinterpret_cast<Messages *>(JSObjectGetPrivate(object)));
+ Class _class(internal->GetValue());
+
+ CYPool pool;
+ const char *name(CYPoolCString(pool, property));
+
+ if (SEL sel = sel_getUid(name))
+ if (Method method = class_getInstanceMethod(_class, sel))
+ return CYMakeMessage(context, sel, method_getImplementation(method), method_getTypeEncoding(method));
+
+ return NULL;
+}
+
+static bool Messages_setProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef *exception) {
+ Messages *internal(reinterpret_cast<Messages *>(JSObjectGetPrivate(object)));
+ Class _class(internal->GetValue());
+
+ CYPool pool;
+ const char *name(CYPoolCString(pool, property));
+
+ SEL sel(sel_registerName(name));
+
+ Method method(class_getInstanceMethod(_class, sel));
+
+ const char *type;
+ IMP imp;
+
+ if (JSValueIsObjectOfClass(context, value, Message_)) {
+ Message_privateData *message(reinterpret_cast<Message_privateData *>(JSObjectGetPrivate((JSObjectRef) value)));
+ type = sig::Unparse(pool, &message->signature_);
+ imp = reinterpret_cast<IMP>(message->GetValue());
+ } else {
+ type = CYPoolTypeEncoding(pool, _class, sel, method);
+ imp = CYMakeMessage(context, value, type);
+ }
+
+ if (method != NULL)
+ method_setImplementation(method, imp);
+ else
+ class_replaceMethod(_class, sel, imp, type);
+
+ return true;
+}
+
+#if !__OBJC2__
+static bool Messages_deleteProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) {
+ Messages *internal(reinterpret_cast<Messages *>(JSObjectGetPrivate(object)));
+ Class _class(internal->GetValue());
+
+ CYPool pool;
+ const char *name(CYPoolCString(pool, property));
+
+ if (SEL sel = sel_getUid(name))
+ if (Method method = class_getInstanceMethod(_class, sel)) {
+ objc_method_list list = {NULL, 1, {method}};
+ class_removeMethods(_class, &list);
+ return true;
+ }
+
+ return false;
+}
+#endif
+
+static void Messages_getPropertyNames(JSContextRef context, JSObjectRef object, JSPropertyNameAccumulatorRef names) {
+ Messages *internal(reinterpret_cast<Messages *>(JSObjectGetPrivate(object)));
+ Class _class(internal->GetValue());
+
+ unsigned int size;
+ Method *data(class_copyMethodList(_class, &size));
+ for (size_t i(0); i != size; ++i)
+ JSPropertyNameAccumulatorAddName(names, CYJSString(sel_getName(method_getName(data[i]))));
+ free(data);
+}
+
+static bool Instance_hasProperty(JSContextRef context, JSObjectRef object, JSStringRef property) {
+ Instance *internal(reinterpret_cast<Instance *>(JSObjectGetPrivate(object)));
+ id self(internal->GetValue());
+
+ if (JSStringIsEqualToUTF8CString(property, "$cyi"))
+ return true;
+
+ CYPool pool;
+ NSString *name(CYCastNSString(pool, property));
+
+ if (CYInternal *internal = CYInternal::Get(self))
+ if (internal->HasProperty(context, property))
+ return true;
+
+ Class _class(object_getClass(self));
+
+ CYPoolTry {
+ // XXX: this is an evil hack to deal with NSProxy; fix elsewhere
+ if (CYImplements(self, _class, @selector(cy$hasProperty:), false))
+ if ([self cy$hasProperty:name])
+ return true;
+ } CYPoolCatch(false)
+
+ const char *string(CYPoolCString(pool, name));
+
+ if (class_getProperty(_class, string) != NULL)
+ return true;
+
+ if (SEL sel = sel_getUid(string))
+ if (CYImplements(self, _class, sel, true))
+ return true;
+
+ return false;
+}
+
+static JSValueRef Instance_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) {
+ Instance *internal(reinterpret_cast<Instance *>(JSObjectGetPrivate(object)));
+ id self(internal->GetValue());
+
+ if (JSStringIsEqualToUTF8CString(property, "$cyi"))
+ return Internal::Make(context, self, object);
+
+ CYTry {
+ CYPool pool;
+ NSString *name(CYCastNSString(pool, property));
+
+ if (CYInternal *internal = CYInternal::Get(self))
+ if (JSValueRef value = internal->GetProperty(context, property))
+ return value;
+
+ CYPoolTry {
+ if (NSObject *data = [self cy$getProperty:name])
+ return CYCastJSValue(context, data);
+ } CYPoolCatch(NULL)
+
+ const char *string(CYPoolCString(pool, name));
+ Class _class(object_getClass(self));
+
+ if (objc_property_t property = class_getProperty(_class, string)) {
+ PropertyAttributes attributes(property);
+ SEL sel(sel_registerName(attributes.Getter()));
+ return CYSendMessage(pool, context, self, sel, 0, NULL, false, exception);
+ }
+
+ if (SEL sel = sel_getUid(string))
+ if (CYImplements(self, _class, sel, true))
+ return CYSendMessage(pool, context, self, sel, 0, NULL, false, exception);
+
+ return NULL;
+ } CYCatch
+}
+
+static bool Instance_setProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef *exception) {
+ Instance *internal(reinterpret_cast<Instance *>(JSObjectGetPrivate(object)));
+ id self(internal->GetValue());
+
+ CYPool pool;
+
+ CYTry {
+ NSString *name(CYCastNSString(pool, property));
+ NSString *data(CYCastNSObject(pool, context, value));
+
+ CYPoolTry {
+ if ([self cy$setProperty:name to:data])
+ return true;
+ } CYPoolCatch(NULL)
+
+ const char *string(CYPoolCString(pool, name));
+ Class _class(object_getClass(self));
+
+ if (objc_property_t property = class_getProperty(_class, string)) {
+ PropertyAttributes attributes(property);
+ if (const char *setter = attributes.Setter()) {
+ SEL sel(sel_registerName(setter));
+ JSValueRef arguments[1] = {value};
+ CYSendMessage(pool, context, self, sel, 1, arguments, false, exception);
+ return true;
+ }
+ }
+
+ size_t length(strlen(string));
+
+ char set[length + 5];
+
+ set[0] = 's';
+ set[1] = 'e';
+ set[2] = 't';
+
+ if (string[0] != '\0') {
+ set[3] = toupper(string[0]);
+ memcpy(set + 4, string + 1, length - 1);
+ }
+
+ set[length + 3] = ':';
+ set[length + 4] = '\0';
+
+ if (SEL sel = sel_getUid(set))
+ if (CYImplements(self, _class, sel, false)) {
+ JSValueRef arguments[1] = {value};
+ CYSendMessage(pool, context, self, sel, 1, arguments, false, exception);
+ }
+
+ if (CYInternal *internal = CYInternal::Set(self)) {
+ internal->SetProperty(context, property, value);
+ return true;
+ }
+
+ return false;
+ } CYCatch
+}
+
+static bool Instance_deleteProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) {
+ Instance *internal(reinterpret_cast<Instance *>(JSObjectGetPrivate(object)));
+ id self(internal->GetValue());
+
+ CYTry {
+ CYPoolTry {
+ NSString *name(CYCastNSString(NULL, property));
+ return [self cy$deleteProperty:name];
+ } CYPoolCatch(NULL)
+ } CYCatch
+}
+
+static void Instance_getPropertyNames(JSContextRef context, JSObjectRef object, JSPropertyNameAccumulatorRef names) {
+ Instance *internal(reinterpret_cast<Instance *>(JSObjectGetPrivate(object)));
+ id self(internal->GetValue());
+
+ CYPool pool;
+ Class _class(object_getClass(self));
+
+ {
+ unsigned int size;
+ objc_property_t *data(class_copyPropertyList(_class, &size));
+ for (size_t i(0); i != size; ++i)
+ JSPropertyNameAccumulatorAddName(names, CYJSString(property_getName(data[i])));
+ free(data);
+ }
+}
+
+static JSObjectRef Instance_callAsConstructor(JSContextRef context, JSObjectRef object, size_t count, const JSValueRef arguments[], JSValueRef *exception) {
+ CYTry {
+ Instance *internal(reinterpret_cast<Instance *>(JSObjectGetPrivate(object)));
+ JSObjectRef value(Instance::Make(context, [internal->GetValue() alloc], Instance::Uninitialized));
+ return value;
+ } CYCatch
+}
+
+static bool CYIsClass(id self) {
+ // XXX: this is a lame object_isClass
+ return class_getInstanceMethod(object_getClass(self), @selector(alloc)) != NULL;
+}
+
+static bool Instance_hasInstance(JSContextRef context, JSObjectRef constructor, JSValueRef instance, JSValueRef *exception) {
+ Instance *internal(reinterpret_cast<Instance *>(JSObjectGetPrivate((JSObjectRef) constructor)));
+ Class _class(internal->GetValue());
+ if (!CYIsClass(_class))
+ return false;
+
+ if (JSValueIsObjectOfClass(context, instance, Instance_)) {
+ Instance *linternal(reinterpret_cast<Instance *>(JSObjectGetPrivate((JSObjectRef) instance)));
+ // XXX: this isn't always safe
+ CYTry {
+ return [linternal->GetValue() isKindOfClass:_class];
+ } CYCatch
+ }
+
+ return false;
+}
+
+static bool Internal_hasProperty(JSContextRef context, JSObjectRef object, JSStringRef property) {
+ Internal *internal(reinterpret_cast<Internal *>(JSObjectGetPrivate(object)));
+ CYPool pool;
+
+ id self(internal->GetValue());
+ const char *name(CYPoolCString(pool, property));
+
+ if (object_getInstanceVariable(self, name, NULL) != NULL)
+ return true;
+
+ return false;
+}
+
+static JSValueRef Internal_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) {
+ Internal *internal(reinterpret_cast<Internal *>(JSObjectGetPrivate(object)));
+ CYPool pool;
+
+ CYTry {
+ id self(internal->GetValue());
+ const char *name(CYPoolCString(pool, property));
+
+ if (Ivar ivar = object_getInstanceVariable(self, name, NULL)) {
+ Type_privateData type(pool, ivar_getTypeEncoding(ivar));
+ return CYFromFFI(context, type.type_, type.GetFFI(), reinterpret_cast<uint8_t *>(self) + ivar_getOffset(ivar));
+ }
+
+ return NULL;
+ } CYCatch
+}
+
+static bool Internal_setProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef *exception) {
+ Internal *internal(reinterpret_cast<Internal *>(JSObjectGetPrivate(object)));
+ CYPool pool;
+
+ CYTry {
+ id self(internal->GetValue());
+ const char *name(CYPoolCString(pool, property));
+
+ if (Ivar ivar = object_getInstanceVariable(self, name, NULL)) {
+ Type_privateData type(pool, ivar_getTypeEncoding(ivar));
+ CYPoolFFI(pool, context, type.type_, type.GetFFI(), reinterpret_cast<uint8_t *>(self) + ivar_getOffset(ivar), value);
+ return true;
+ }
+
+ return false;
+ } CYCatch
+}
+
+static void Internal_getPropertyNames_(Class _class, JSPropertyNameAccumulatorRef names) {
+ if (Class super = class_getSuperclass(_class))
+ Internal_getPropertyNames_(super, names);
+
+ unsigned int size;
+ Ivar *data(class_copyIvarList(_class, &size));
+ for (size_t i(0); i != size; ++i)
+ JSPropertyNameAccumulatorAddName(names, CYJSString(ivar_getName(data[i])));
+ free(data);
+}
+
+static void Internal_getPropertyNames(JSContextRef context, JSObjectRef object, JSPropertyNameAccumulatorRef names) {
+ Internal *internal(reinterpret_cast<Internal *>(JSObjectGetPrivate(object)));
+ CYPool pool;
+
+ id self(internal->GetValue());
+ Class _class(object_getClass(self));
+
+ Internal_getPropertyNames_(_class, names);
+}
+
+static JSValueRef Internal_callAsFunction_$cya(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) {
+ Internal *internal(reinterpret_cast<Internal *>(JSObjectGetPrivate(object)));
+ return internal->GetOwner();
+}
+
+static bool Index_(apr_pool_t *pool, Struct_privateData *internal, JSStringRef property, ssize_t &index, uint8_t *&base) {