+void CYSetProperty(JSContextRef context, JSObjectRef object, const char *name, JSValueRef value) {
+ JSValueRef exception(NULL);
+ JSObjectSetProperty(context, object, CYString(name), value, kJSPropertyAttributeNone, &exception);
+ CYThrow(context, exception);
+}
+
+struct ffiData {
+ apr_pool_t *pool_;
+ void (*function_)();
+ const char *type_;
+ sig::Signature signature_;
+ ffi_cif cif_;
+};
+
+char *CYPoolCString(apr_pool_t *pool, JSStringRef value) {
+ size_t size(JSStringGetMaximumUTF8CStringSize(value));
+ char *string(new(pool) char[size]);
+ JSStringGetUTF8CString(value, string, size);
+ JSStringRelease(value);
+ return string;
+}
+
+// XXX: this macro is dangerous
+#define CYCastCString(context, value) ({ \
+ JSValueRef exception(NULL); \
+ JSStringRef string(JSValueToStringCopy(context, value, &exception)); \
+ CYThrow(context, exception); \
+ size_t size(JSStringGetMaximumUTF8CStringSize(string)); \
+ char *utf8(reinterpret_cast<char *>(alloca(size))); \
+ JSStringGetUTF8CString(string, utf8, size); \
+ JSStringRelease(string); \
+ utf8; \
+})
+
+SEL CYCastSEL(JSContextRef context, JSValueRef value) {
+ if (JSValueIsNull(context, value))
+ return NULL;
+ else if (JSValueIsObjectOfClass(context, value, sel_))
+ return reinterpret_cast<SEL>(JSObjectGetPrivate((JSObjectRef) value));
+ else
+ return sel_registerName(CYCastCString(context, value));
+}
+
+void *CYCastPointer(JSContextRef context, JSValueRef value) {
+ switch (JSValueGetType(context, value)) {
+ case kJSTypeNull:
+ return NULL;
+ case kJSTypeString:
+ return dlsym(RTLD_DEFAULT, CYCastCString(context, value));
+ case kJSTypeObject:
+ if (JSValueIsObjectOfClass(context, value, ptr_)) {
+ ptrData *data(reinterpret_cast<ptrData *>(JSObjectGetPrivate((JSObjectRef) value)));
+ return data->value_;
+ }
+ default:
+ JSValueRef exception(NULL);
+ double number(JSValueToNumber(context, value, &exception));
+ CYThrow(context, exception);
+ return reinterpret_cast<void *>(static_cast<uintptr_t>(number));
+ }
+}
+
+void CYPoolFFI(apr_pool_t *pool, JSContextRef context, sig::Type *type, void *data, JSValueRef value) {
+ switch (type->primitive) {
+ case sig::boolean_P:
+ *reinterpret_cast<bool *>(data) = JSValueToBoolean(context, value);
+ break;
+
+#define CYPoolFFI_(primitive, native) \
+ case sig::primitive ## _P: { \
+ JSValueRef exception(NULL); \
+ double number(JSValueToNumber(context, value, &exception)); \
+ CYThrow(context, exception); \
+ *reinterpret_cast<native *>(data) = number; \
+ } break;
+
+ CYPoolFFI_(uchar, unsigned char)
+ CYPoolFFI_(char, char)
+ CYPoolFFI_(ushort, unsigned short)
+ CYPoolFFI_(short, short)
+ CYPoolFFI_(ulong, unsigned long)
+ CYPoolFFI_(long, long)
+ CYPoolFFI_(uint, unsigned int)
+ CYPoolFFI_(int, int)
+ CYPoolFFI_(ulonglong, unsigned long long)
+ CYPoolFFI_(longlong, long long)
+ CYPoolFFI_(float, float)
+ CYPoolFFI_(double, double)
+
+ case sig::object_P:
+ case sig::typename_P:
+ *reinterpret_cast<id *>(data) = CYCastNSObject(context, value);
+ break;
+
+ case sig::selector_P:
+ *reinterpret_cast<SEL *>(data) = CYCastSEL(context, value);
+ break;
+
+ case sig::pointer_P:
+ *reinterpret_cast<void **>(data) = CYCastPointer(context, value);
+ break;
+
+ case sig::string_P: {
+ JSValueRef exception(NULL);
+ JSStringRef string(JSValueToStringCopy(context, value, &exception));
+ CYThrow(context, exception);
+ size_t size(JSStringGetMaximumUTF8CStringSize(string));
+ char *utf8(new(pool) char[size]);
+ JSStringGetUTF8CString(string, utf8, size);
+ JSStringRelease(string);
+ *reinterpret_cast<char **>(data) = utf8;
+ } break;
+
+ case sig::struct_P:
+ goto fail;
+
+ case sig::void_P:
+ break;
+
+ default: fail:
+ NSLog(@"CYPoolFFI(%c)\n", type->primitive);
+ _assert(false);
+ }
+}
+
+JSValueRef CYFromFFI(JSContextRef context, sig::Type *type, void *data) {
+ JSValueRef value;
+
+ switch (type->primitive) {
+ case sig::boolean_P:
+ value = JSValueMakeBoolean(context, *reinterpret_cast<bool *>(data));
+ break;
+
+#define CYFromFFI_(primitive, native) \
+ case sig::primitive ## _P: \
+ value = JSValueMakeNumber(context, *reinterpret_cast<native *>(data)); \
+ break;
+
+ CYFromFFI_(uchar, unsigned char)
+ CYFromFFI_(char, char)
+ CYFromFFI_(ushort, unsigned short)
+ CYFromFFI_(short, short)
+ CYFromFFI_(ulong, unsigned long)
+ CYFromFFI_(long, long)
+ CYFromFFI_(uint, unsigned int)
+ CYFromFFI_(int, int)
+ CYFromFFI_(ulonglong, unsigned long long)
+ CYFromFFI_(longlong, long long)
+ CYFromFFI_(float, float)
+ CYFromFFI_(double, double)
+
+ case sig::object_P:
+ case sig::typename_P: {
+ value = CYCastJSValue(context, *reinterpret_cast<id *>(data));
+ } break;
+
+ case sig::selector_P: {
+ SEL sel(*reinterpret_cast<SEL *>(data));
+ value = sel == NULL ? JSValueMakeNull(context) : JSObjectMake(context, sel_, sel);
+ } break;
+
+ case sig::pointer_P: {
+ if (void *pointer = *reinterpret_cast<void **>(data)) {
+ apr_pool_t *pool;
+ apr_pool_create(&pool, NULL);
+ ptrData *data(new(pool) ptrData());
+ data->pool_ = pool;
+ data->value_ = pointer;
+ value = JSObjectMake(context, ptr_, data);
+ } else value = JSValueMakeNull(context);
+ } break;
+
+ case sig::string_P: {
+ char *utf8(*reinterpret_cast<char **>(data));
+ value = utf8 == NULL ? JSValueMakeNull(context) : JSValueMakeString(context, CYString(utf8));
+ } break;
+
+ case sig::struct_P:
+ goto fail;
+
+ case sig::void_P:
+ value = NULL;
+ break;
+
+ default: fail:
+ NSLog(@"CYFromFFI(%c)\n", type->primitive);
+ _assert(false);
+ }
+
+ return value;
+}
+
+class CYPool {
+ private:
+ apr_pool_t *pool_;
+
+ public:
+ CYPool() {
+ apr_pool_create(&pool_, NULL);
+ }
+
+ ~CYPool() {
+ apr_pool_destroy(pool_);
+ }
+
+ operator apr_pool_t *() const {
+ return pool_;
+ }
+};
+
+static JSValueRef CYCallFunction(JSContextRef context, size_t count, const JSValueRef *arguments, JSValueRef *exception, sig::Signature *signature, ffi_cif *cif, void (*function)()) { _pooled
+ @try {
+ if (count != signature->count - 1)
+ [NSException raise:NSInvalidArgumentException format:@"incorrect number of arguments to ffi function"];
+
+ CYPool pool;
+ void *values[count];
+
+ for (unsigned index(0); index != count; ++index) {
+ sig::Element *element(&signature->elements[index + 1]);
+ values[index] = new(pool) uint8_t[cif->arg_types[index]->size];
+ CYPoolFFI(pool, context, element->type, values[index], arguments[index]);
+ }
+
+ uint8_t value[cif->rtype->size];
+ ffi_call(cif, function, value, values);
+
+ return CYFromFFI(context, signature->elements[0].type, value);
+ } CYCatch
+}
+
+bool stret(ffi_type *ffi_type) {
+ return ffi_type->type == FFI_TYPE_STRUCT && (
+ ffi_type->size > OBJC_MAX_STRUCT_BY_VALUE ||
+ struct_forward_array[ffi_type->size] != 0
+ );
+}
+
+static JSValueRef $objc_msgSend(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { _pooled
+ const char *type;
+
+ @try {
+ if (count < 2)
+ [NSException raise:NSInvalidArgumentException format:@"too few arguments to objc_msgSend"];
+
+ id self(CYCastNSObject(context, arguments[0]));
+ if (self == nil)
+ return JSValueMakeNull(context);
+
+ SEL _cmd(CYCastSEL(context, arguments[1]));
+ NSMethodSignature *method([self methodSignatureForSelector:_cmd]);
+ if (method == nil)
+ [NSException raise:NSInvalidArgumentException format:@"unrecognized selector %s sent to object %p", sel_getName(_cmd), self];
+
+ type = [[method _typeString] UTF8String];
+ } CYCatch
+
+ CYPool pool;
+
+ sig::Signature signature;
+ sig::Parse(pool, &signature, type);
+
+ ffi_cif cif;
+ sig::sig_ffi_cif(pool, &sig::ObjectiveC, &signature, &cif);
+
+ void (*function)() = stret(cif.rtype) ? reinterpret_cast<void (*)()>(&objc_msgSend_stret) : reinterpret_cast<void (*)()>(&objc_msgSend);
+ return CYCallFunction(context, count, arguments, exception, &signature, &cif, function);
+}
+
+static JSValueRef ffi_callAsFunction(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) {
+ ffiData *data(reinterpret_cast<ffiData *>(JSObjectGetPrivate(object)));
+ return CYCallFunction(context, count, arguments, exception, &data->signature_, &data->cif_, data->function_);
+}
+
+static void ffi_finalize(JSObjectRef object) {
+ ffiData *data(reinterpret_cast<ffiData *>(JSObjectGetPrivate(object)));
+ apr_pool_destroy(data->pool_);
+}
+
+JSObjectRef CYMakeFunction(JSContextRef context, void (*function)(), const char *type) {
+ apr_pool_t *pool;
+ apr_pool_create(&pool, NULL);
+
+ ffiData *data(new(pool) ffiData());
+
+ data->pool_ = pool;
+ data->function_ = function;
+ data->type_ = apr_pstrdup(pool, type);
+
+ sig::Parse(pool, &data->signature_, type);
+ sig::sig_ffi_cif(pool, &sig::ObjectiveC, &data->signature_, &data->cif_);
+
+ return JSObjectMake(context, ffi_, data);
+}
+
+JSObjectRef ffi(JSContextRef context, JSObjectRef object, size_t count, const JSValueRef arguments[], JSValueRef *exception) {
+ @try {
+ if (count != 2)
+ [NSException raise:NSInvalidArgumentException format:@"incorrect number of arguments to ffi constructor"];
+ void (*function)() = reinterpret_cast<void (*)()>(CYCastPointer(context, arguments[0]));
+ const char *type(CYCastCString(context, arguments[1]));
+ return CYMakeFunction(context, function, type);
+ } CYCatch
+}
+
+JSValueRef ptr_getProperty_value(JSContextRef context, JSObjectRef object, JSStringRef name, JSValueRef *exception) {
+ ptrData *data(reinterpret_cast<ptrData *>(JSObjectGetPrivate(object)));
+ return JSValueMakeNumber(context, reinterpret_cast<uintptr_t>(data->value_));
+}
+
+static JSStaticValue ptr_staticValues[2] = {
+ {"value", &ptr_getProperty_value, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete},
+ {NULL, NULL, NULL, 0}
+};
+
+MSInitialize { _pooled
+ apr_initialize();