From: Jay Freeman (saurik) Date: Mon, 31 Aug 2009 10:14:24 +0000 (+0000) Subject: Initial FFI call success. X-Git-Tag: v0.9.432~428 X-Git-Url: https://git.saurik.com/cycript.git/commitdiff_plain/ea2d184cbcdf25cfa51727f175356fe2ee211136?ds=inline Initial FFI call success. --- diff --git a/Tweak.mm b/Tweak.mm index 2dc974a..a6d9e0c 100644 --- a/Tweak.mm +++ b/Tweak.mm @@ -39,6 +39,12 @@ #include +#include "sig/parse.hpp" +#include "sig/ffi_type.hpp" + +#include +#include + #include #include @@ -60,6 +66,9 @@ #include #include +#undef _assert +#undef _trace + /* XXX: bad _assert */ #define _assert(test) do { \ if ((test)) break; \ @@ -71,12 +80,17 @@ CFLog(kCFLogLevelNotice, CFSTR("_trace():%u"), __LINE__); \ } while (false) -static JSContextRef context_; +static JSContextRef Context_; + +static JSClassRef ffi_; static JSClassRef joc_; + static JSObjectRef Array_; + static JSStringRef name_; static JSStringRef message_; static JSStringRef length_; + static Class NSCFBoolean_; struct Client { @@ -86,7 +100,7 @@ struct Client { @interface NSObject (Cyrver) - (NSString *) cy$toJSON; -// XXX: - (JSValueRef) cy$JSValueInContext:(JSContextRef)context; +- (JSValueRef) cy$JSValueInContext:(JSContextRef)context; @end @implementation NSObject (Cyrver) @@ -95,6 +109,10 @@ struct Client { return [self description]; } +- (JSValueRef) cy$JSValueInContext:(JSContextRef)context { + return JSObjectMake(context, joc_, [self retain]); +} + @end @implementation WebUndefined (Cyrver) @@ -213,29 +231,30 @@ struct Client { @end JSContextRef JSGetContext() { - return context_; + return Context_; } -id JSObjectToNSObject(JSContextRef ctx, JSObjectRef object) { - if (JSValueIsObjectOfClass(ctx, object, joc_)) +void CYThrow(JSContextRef context, JSValueRef value); + +id JSObjectToNSObject(JSContextRef context, JSObjectRef object) { + if (JSValueIsObjectOfClass(context, object, joc_)) return reinterpret_cast(JSObjectGetPrivate(object)); - // XXX: exception - else if (JSValueIsInstanceOfConstructor(ctx, object, Array_, NULL)) - return [[[CY$JSArray alloc] initWithJSObject:object inContext:ctx] autorelease]; - else - return [[[CY$JSObject alloc] initWithJSObject:object inContext:ctx] autorelease]; + JSValueRef exception(NULL); + bool array(JSValueIsInstanceOfConstructor(context, object, Array_, &exception)); + CYThrow(context, exception); + if (array) + return [[[CY$JSArray alloc] initWithJSObject:object inContext:context] autorelease]; + return [[[CY$JSObject alloc] initWithJSObject:object inContext:context] autorelease]; } CFStringRef CYCopyCFString(JSStringRef value) { return JSStringCopyCFString(kCFAllocatorDefault, value); } -void CYThrow(JSContextRef ctx, JSValueRef value); - -CFStringRef CYCopyCFString(JSContextRef ctx, JSValueRef value) { +CFStringRef CYCopyCFString(JSContextRef context, JSValueRef value) { JSValueRef exception(NULL); - JSStringRef string(JSValueToStringCopy(ctx, value, &exception)); - CYThrow(context_, exception); + JSStringRef string(JSValueToStringCopy(context, value, &exception)); + CYThrow(context, exception); CFStringRef object(CYCopyCFString(string)); JSStringRelease(string); return object; @@ -245,8 +264,8 @@ NSString *CYCastNSString(JSStringRef value) { return [reinterpret_cast(CYCopyCFString(value)) autorelease]; } -CFTypeRef CYCopyCFType(JSContextRef ctx, JSValueRef value) { - JSType type(JSValueGetType(ctx, value)); +CFTypeRef CYCopyCFType(JSContextRef context, JSValueRef value) { + JSType type(JSValueGetType(context, value)); switch (type) { case kJSTypeUndefined: @@ -258,22 +277,22 @@ CFTypeRef CYCopyCFType(JSContextRef ctx, JSValueRef value) { break; case kJSTypeBoolean: - return CFRetain(JSValueToBoolean(ctx, value) ? kCFBooleanTrue : kCFBooleanFalse); + return CFRetain(JSValueToBoolean(context, value) ? kCFBooleanTrue : kCFBooleanFalse); break; case kJSTypeNumber: { JSValueRef exception(NULL); - double number(JSValueToNumber(ctx, value, &exception)); - CYThrow(context_, exception); + double number(JSValueToNumber(context, value, &exception)); + CYThrow(context, exception); return CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &number); } break; case kJSTypeString: - return CYCopyCFString(context_, value); + return CYCopyCFString(context, value); break; case kJSTypeObject: - return CFRetain((CFTypeRef) JSObjectToNSObject(ctx, (JSObjectRef) value)); + return CFRetain((CFTypeRef) JSObjectToNSObject(context, (JSObjectRef) value)); break; default: @@ -290,25 +309,29 @@ NSArray *CYCastNSArray(JSPropertyNameArrayRef names) { return array; } -id CYCastNSObject(JSContextRef ctx, JSValueRef value) { - const NSObject *object(reinterpret_cast(CYCopyCFType(ctx, value))); +id CYCastNSObject(JSContextRef context, JSValueRef value) { + const NSObject *object(reinterpret_cast(CYCopyCFType(context, value))); return object == nil ? nil : [object autorelease]; } -void CYThrow(JSContextRef ctx, JSValueRef value) { +void CYThrow(JSContextRef context, JSValueRef value) { if (value == NULL) return; - @throw CYCastNSObject(ctx, value); + @throw CYCastNSObject(context, value); } -JSValueRef CYCastJSValue(JSContextRef ctx, id value) { - return [value cy$JSValueInContext:ctx]; +JSValueRef CYCastJSValue(JSContextRef context, id value) { + return value == nil ? JSValueMakeNull(context) : [value cy$JSValueInContext:context]; } -JSStringRef CYCopyJSString(JSContextRef ctx, id value) { +JSStringRef CYCopyJSString(id value) { return JSStringCreateWithCFString(reinterpret_cast([value description])); } +JSStringRef CYCopyJSString(const char *value) { + return JSStringCreateWithUTF8CString(value); +} + @implementation CY$JSObject - (id) initWithJSObject:(JSObjectRef)object inContext:(JSContextRef)context { @@ -327,7 +350,7 @@ JSStringRef CYCopyJSString(JSContextRef ctx, id value) { - (id) objectForKey:(id)key { JSValueRef exception(NULL); - JSStringRef string(CYCopyJSString(context_, key)); + JSStringRef string(CYCopyJSString(key)); JSValueRef value(JSObjectGetProperty(context_, object_, string, &exception)); JSStringRelease(string); CYThrow(context_, exception); @@ -343,7 +366,7 @@ JSStringRef CYCopyJSString(JSContextRef ctx, id value) { - (void) setObject:(id)object forKey:(id)key { JSValueRef exception(NULL); - JSStringRef string(CYCopyJSString(context_, key)); + JSStringRef string(CYCopyJSString(key)); JSObjectSetProperty(context_, object_, string, CYCastJSValue(context_, object), kJSPropertyAttributeNone, &exception); JSStringRelease(string); CYThrow(context_, exception); @@ -351,7 +374,7 @@ JSStringRef CYCopyJSString(JSContextRef ctx, id value) { - (void) removeObjectForKey:(id)key { JSValueRef exception(NULL); - JSStringRef string(CYCopyJSString(context_, key)); + JSStringRef string(CYCopyJSString(key)); // XXX: this returns a bool JSObjectDeleteProperty(context_, object_, string, &exception); JSStringRelease(string); @@ -388,8 +411,8 @@ JSStringRef CYCopyJSString(JSContextRef ctx, id value) { @end -CFStringRef JSValueToJSONCopy(JSContextRef ctx, JSValueRef value) { - id object(CYCastNSObject(ctx, value)); +CFStringRef JSValueToJSONCopy(JSContextRef context, JSValueRef value) { + id object(CYCastNSObject(context, value)); return reinterpret_cast([(object == nil ? @"null" : [object cy$toJSON]) retain]); } @@ -466,18 +489,213 @@ static void OnAccept(CFSocketRef socket, CFSocketCallBackType type, CFDataRef ad } } -static JSValueRef joc_getProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef *exception) { +static JSValueRef joc_getProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef *exception) { return NULL; } -static JSValueRef obc_getProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef *exception) { +typedef id jocData; + +static void joc_finalize(JSObjectRef object) { + id data(reinterpret_cast(JSObjectGetPrivate(object))); + [data release]; +} + +static JSValueRef obc_getProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef *exception) { NSString *name([(NSString *) JSStringCopyCFString(kCFAllocatorDefault, propertyName) autorelease]); if (Class _class = NSClassFromString(name)) - return JSObjectMake(ctx, joc_, _class); + return JSObjectMake(context, joc_, [_class retain]); return NULL; } +void CYSetProperty(JSContextRef context, JSObjectRef object, const char *name, JSValueRef value) { + JSValueRef exception(NULL); + JSStringRef string(CYCopyJSString(name)); + JSObjectSetProperty(context, object, string, value, kJSPropertyAttributeNone, &exception); + JSStringRelease(string); + CYThrow(context, exception); +} + +struct ffiData { + apr_pool_t *pool_; + void (*function_)(); + const char *type_; + sig::Signature signature_; + ffi_cif cif_; +}; + +void CYToFFI(apr_pool_t *pool, JSContextRef context, JSValueRef *exception, sig::Type *type, void *data, JSValueRef value) { + switch (type->primitive) { + case sig::boolean_P: + *reinterpret_cast(data) = JSValueToBoolean(context, value); + break; + +#define CYToFFI_(primitive, native) \ + case sig::primitive ## _P: { \ + double number(JSValueToNumber(context, value, exception)); \ + if (exception == NULL) \ + *reinterpret_cast(data) = number; \ + } break; + + CYToFFI_(uchar, unsigned char) + CYToFFI_(char, char) + CYToFFI_(ushort, unsigned short) + CYToFFI_(short, short) + CYToFFI_(ulong, unsigned long) + CYToFFI_(long, long) + CYToFFI_(uint, unsigned int) + CYToFFI_(int, int) + CYToFFI_(ulonglong, unsigned long long) + CYToFFI_(longlong, long long) + CYToFFI_(float, float) + CYToFFI_(double, double) + + case sig::object_P: + case sig::typename_P: + case sig::selector_P: + case sig::pointer_P: + goto fail; + + case sig::string_P: { + JSStringRef string(JSValueToStringCopy(context, value, exception)); + if (exception != NULL) { + size_t size(JSStringGetMaximumUTF8CStringSize(string)); + char *utf8(reinterpret_cast(apr_palloc(pool, size))); + JSStringGetUTF8CString(string, utf8, size); + JSStringRelease(string); + *reinterpret_cast(data) = utf8; + } + } break; + + case sig::struct_P: + goto fail; + + case sig::void_P: + break; + + default: fail: + NSLog(@"CYToFFI(%c)\n", type->primitive); + _assert(false); + } +} + +JSValueRef CYFromFFI(apr_pool_t *pool, JSContextRef context, JSValueRef *exception, sig::Type *type, void *data) { + JSValueRef value; + + switch (type->primitive) { + case sig::boolean_P: + value = JSValueMakeBoolean(context, *reinterpret_cast(data)); + break; + +#define CYFromFFI_(primitive, native) \ + case sig::primitive ## _P: \ + value = JSValueMakeNumber(context, *reinterpret_cast(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(data)); + } break; + + case sig::selector_P: + case sig::pointer_P: + goto fail; + + case sig::string_P: { + char *utf8(*reinterpret_cast(data)); + JSStringRef string(JSStringCreateWithUTF8CString(utf8)); + value = JSValueMakeString(context, string); + JSStringRelease(string); + } 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; +} + +static JSValueRef ffi_callAsFunction(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { + ffiData *data(reinterpret_cast(JSObjectGetPrivate(object))); + + if (count != data->signature_.count - 1) + _assert(false); + + JSValueRef result(NULL); + + apr_pool_t *pool; + apr_pool_create(&pool, NULL); + + void *values[count]; + + for (unsigned index(0); index != count; ++index) { + sig::Element *element(&data->signature_.elements[index + 1]); + values[index] = apr_palloc(pool, data->cif_.arg_types[index]->size); + CYToFFI(pool, context, exception, element->type, values[index], arguments[index]); + if (*exception != NULL) + goto error; + } + + uint8_t value[data->cif_.rtype->size]; + + @try { + ffi_call(&data->cif_, data->function_, value, values); + } @catch (id error) { + _assert(false); + } + + result = CYFromFFI(pool, context, exception, data->signature_.elements[0].type, value); + + error: + apr_pool_destroy(pool); + return result; +} + +static void ffi_finalize(JSObjectRef object) { + ffiData *data(reinterpret_cast(JSObjectGetPrivate(object))); + apr_pool_destroy(data->pool_); +} + +void CYSetFunction(JSContextRef context, JSObjectRef object, const char *name, void (*function)(), const char *type) { + apr_pool_t *pool; + apr_pool_create(&pool, NULL); + + ffiData *data(reinterpret_cast(apr_palloc(pool, sizeof(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::sig_objc_ffi_type, &data->signature_, &data->cif_); + + JSObjectRef value(JSObjectMake(context, ffi_, data)); + CYSetProperty(context, object, name, value); +} + MSInitialize { + apr_initialize(); + NSAutoreleasePool *pool([[NSAutoreleasePool alloc] init]); NSCFBoolean_ = objc_getClass("NSCFBoolean"); @@ -507,13 +725,22 @@ MSInitialize { definition.getProperty = &obc_getProperty; JSClassRef obc(JSClassCreate(&definition)); + definition = kJSClassDefinitionEmpty; + definition.callAsFunction = &ffi_callAsFunction; + definition.finalize = &ffi_finalize; + ffi_ = JSClassCreate(&definition); + definition = kJSClassDefinitionEmpty; definition.getProperty = &joc_getProperty; + definition.finalize = &joc_finalize; joc_ = JSClassCreate(&definition); - context_ = JSGlobalContextCreate(obc); + JSContextRef context(JSGlobalContextCreate(obc)); + Context_ = context; + + JSObjectRef global(JSContextGetGlobalObject(context)); - JSObjectRef global(JSContextGetGlobalObject(JSGetContext())); + CYSetFunction(context, global, "objc_getClass", reinterpret_cast(&objc_getClass), "#*"); name_ = JSStringCreateWithUTF8CString("name"); message_ = JSStringCreateWithUTF8CString("message"); @@ -522,10 +749,10 @@ MSInitialize { JSStringRef name(JSStringCreateWithUTF8CString("Array")); JSValueRef exception(NULL); JSValueRef value(JSObjectGetProperty(JSGetContext(), global, name, &exception)); - CYThrow(context_, exception); + CYThrow(context, exception); JSStringRelease(name); Array_ = JSValueToObject(JSGetContext(), value, &exception); - CYThrow(context_, exception); + CYThrow(context, exception); [pool release]; } diff --git a/make.sh b/make.sh index 55963c6..7d0b27a 100755 --- a/make.sh +++ b/make.sh @@ -1,2 +1,2 @@ #!/bin/bash -PKG_ARCH=${PKG_ARCH-iphoneos-arm} PATH=/apl/n42/pre/bin:$PATH exec /apl/tel/exec.sh :readline make "$@" +PKG_ARCH=${PKG_ARCH-iphoneos-arm} PATH=/apl/n42/pre/bin:$PATH exec /apl/tel/exec.sh :apr:apr-lib:libffi:readline make "$@" diff --git a/makefile b/makefile index 166f79d..0e566fe 100644 --- a/makefile +++ b/makefile @@ -1,9 +1,47 @@ +ifndef PKG_TARG +target := +else +target := $(PKG_TARG)- +endif + package: name := Cyrver -flags := -framework CFNetwork -framework JavaScriptCore -framework WebCore -install_name /usr/lib/libcyrver.dylib -base := $(shell cd ~; pwd)/menes/tweaks -include $(base)/tweak.mk +flags := -framework CFNetwork -framework JavaScriptCore -framework WebCore -install_name /usr/lib/libcyrver.dylib -I. +menes := $(shell cd ~; pwd)/menes + +link := -framework CoreFoundation -framework Foundation -F${PKG_ROOT}/System/Library/PrivateFrameworks -L$(menes)/mobilesubstrate -lsubstrate -lapr-1 -lffi + +all: $(name).dylib $(control) + +clean: + rm -f $(name).dylib + +$(name).dylib: Tweak.mm makefile $(menes)/mobilesubstrate/substrate.h sig/*.[ch]pp + $(target)g++ -save-temps -dynamiclib -mthumb -g0 -O2 -Wall -Werror -o $@ $(filter %.cpp,$^) $(filter %.mm,$^) -lobjc -I$(menes)/mobilesubstrate $(link) $(flags) + ldid -S $@ + +package: all + rm -rf package + mkdir -p package/DEBIAN + mkdir -p package/Library/MobileSubstrate/DynamicLibraries + cp -a control $(control) package/DEBIAN + if [[ -e Settings.plist ]]; then \ + mkdir -p package/Library/PreferenceLoader/Preferences; \ + cp -a Settings.png package/Library/PreferenceLoader/Preferences/$(name)Icon.png; \ + cp -a Settings.plist package/Library/PreferenceLoader/Preferences/$(name).plist; \ + fi + if [[ -e Tweak.plist ]]; then cp -a Tweak.plist package/Library/MobileSubstrate/DynamicLibraries/$(name).plist; fi + cp -a $(name).dylib package/Library/MobileSubstrate/DynamicLibraries + $(MAKE) extra + dpkg-deb -b package $(shell grep ^Package: control | cut -d ' ' -f 2-)_$(shell grep ^Version: control | cut -d ' ' -f 2)_iphoneos-arm.deb + +extra: + +%: %.mm + $(target)g++ -o $@ -Wall -Werror $< -lobjc -framework CoreFoundation -framework Foundation + +.PHONY: all clean extra package all: cyrver diff --git a/sig/ffi_type.cpp b/sig/ffi_type.cpp new file mode 100644 index 0000000..49e4084 --- /dev/null +++ b/sig/ffi_type.cpp @@ -0,0 +1,133 @@ +#include "minimal/stdlib.h" + +#include "sig/ffi_type.hpp" +#include "sig/types.hpp" + +namespace sig { + +ffi_type *sig_objc_ffi_type(apr_pool_t *pool, struct Type *type) { + switch (type->primitive) { + case typename_P: return &ffi_type_pointer; + + case union_P: + /* XXX: we can totally make this work */ + _assert(false); + break; + + case string_P: return &ffi_type_pointer; + case selector_P: return &ffi_type_pointer; + case object_P: return &ffi_type_pointer; + case boolean_P: return &ffi_type_uchar; + case uchar_P: return &ffi_type_uchar; + case uint_P: return &ffi_type_uint; + case ulong_P: return &ffi_type_ulong; + case ulonglong_P: return &ffi_type_ulong; + case ushort_P: return &ffi_type_ushort; + + case array_P: + /* XXX: implement */ + _assert(false); + break; + + case pointer_P: return &ffi_type_pointer; + + case bit_P: + /* XXX: we can totally make this work */ + _assert(false); + break; + + case char_P: return &ffi_type_schar; + case double_P: return &ffi_type_double; + case float_P: return &ffi_type_float; + case int_P: return &ffi_type_sint; + case long_P: return &ffi_type_sint; + case longlong_P: return &ffi_type_slong; + case short_P: return &ffi_type_sshort; + + case void_P: return &ffi_type_void; + + case struct_P: { + ffi_type *aggregate = reinterpret_cast(apr_palloc(pool, sizeof(ffi_type))); + aggregate->size = 0; + aggregate->alignment = 0; + aggregate->type = FFI_TYPE_STRUCT; + + aggregate->elements = reinterpret_cast(apr_palloc(pool, (type->data.signature.count + 1) * sizeof(ffi_type *))); + sig_ffi_types(pool, &sig_objc_ffi_type, &type->data.signature, aggregate->elements, 0, 0); + aggregate->elements[type->data.signature.count] = NULL; + + return aggregate; + } break; + + default: + _assert(false); + break; + } +} + +ffi_type *sig_java_ffi_type(apr_pool_t *pool, struct Type *type) { + switch (type->primitive) { + case typename_P: return &ffi_type_pointer; + case union_P: return &ffi_type_pointer; + case string_P: return &ffi_type_pointer; + case selector_P: return &ffi_type_pointer; + case object_P: return &ffi_type_pointer; + case boolean_P: return &ffi_type_uchar; + case uchar_P: return &ffi_type_uchar; + case uint_P: return &ffi_type_uint; + case ulong_P: return &ffi_type_ulong; + case ulonglong_P: return &ffi_type_ulong; + case ushort_P: return &ffi_type_ushort; + case array_P: return &ffi_type_pointer; + case pointer_P: return &ffi_type_pointer; + + /* XXX: bit type */ + case bit_P: return &ffi_type_uint; + + case char_P: return &ffi_type_schar; + case double_P: return &ffi_type_double; + case float_P: return &ffi_type_double; + case int_P: return &ffi_type_sint; + case long_P: return &ffi_type_sint; + case longlong_P: return &ffi_type_slong; + case short_P: return &ffi_type_sshort; + case void_P: return &ffi_type_void; + case struct_P: return &ffi_type_pointer; + + default: + _assert(false); + break; + } +} + +void sig_ffi_types( + apr_pool_t *pool, + ffi_type *(*sig_ffi_type)(apr_pool_t *, struct Type *), + struct Signature *signature, + ffi_type **ffi_types, + size_t skip, + size_t offset +) { + _assert(signature->count >= skip); + for (size_t index = skip; index != signature->count; ++index) + ffi_types[index - skip + offset] = (*sig_ffi_type)(pool, signature->elements[index].type); +} + +void sig_ffi_cif( + apr_pool_t *pool, + ffi_type *(*sig_ffi_type)(apr_pool_t *, struct Type *), + struct Signature *signature, + ffi_cif *ffi_cif, + size_t skip, + ffi_type **types, + size_t offset +) { + if (types == NULL) + types = reinterpret_cast(apr_palloc(pool, (signature->count - 1) * sizeof(ffi_type *))); + ffi_type *type = (*sig_ffi_type)(pool, signature->elements[0].type); + sig_ffi_types(pool, sig_ffi_type, signature, types, 1 + skip, offset); + ffi_status status = ffi_prep_cif(ffi_cif, FFI_DEFAULT_ABI, signature->count - 1 - skip + offset, type, types); + _assert(status == FFI_OK); +} + +} diff --git a/sig/ffi_type.hpp b/sig/ffi_type.hpp new file mode 100644 index 0000000..c0a0bde --- /dev/null +++ b/sig/ffi_type.hpp @@ -0,0 +1,35 @@ +#ifndef SIG_FFI_TYPE_H +#define SIG_FFI_TYPE_H + +#include +#include + +#include "sig/types.hpp" + +namespace sig { + +ffi_type *sig_objc_ffi_type(apr_pool_t *pool, struct Type *type); +ffi_type *sig_java_ffi_type(apr_pool_t *pool, struct Type *type); + +void sig_ffi_types( + apr_pool_t *pool, + ffi_type *(*sig_ffi_type)(apr_pool_t *, struct Type *), + struct Signature *signature, + ffi_type **ffi_types, + size_t skip, + size_t offset +); + +void sig_ffi_cif( + apr_pool_t *pool, + ffi_type *(*sig_ffi_type)(apr_pool_t *, struct Type *), + struct Signature *signature, + ffi_cif *ffi_cif, + size_t skip = 0, + ffi_type **types = NULL, + size_t offset = 0 +); + +} + +#endif/*SIG_FFI_TYPE_H*/ diff --git a/sig/parse.cpp b/sig/parse.cpp new file mode 100644 index 0000000..cf628a8 --- /dev/null +++ b/sig/parse.cpp @@ -0,0 +1,258 @@ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include "minimal/stdlib.h" + +#include + +#include + +#include "sig/parse.hpp" + +namespace sig { + +void (*sig_aggregate)(apr_pool_t *pool, enum Primitive primitive, const char *name, struct Signature *signature, const char *types) = NULL; + +/* XXX: I really screwed up this time */ +void *prealloc_(apr_pool_t *pool, void *odata, size_t osize, size_t nsize) { + void *ndata = apr_palloc(pool, nsize); + memcpy(ndata, odata, osize); + return ndata; +} + +void sig_parse_signature(apr_pool_t *pool, struct Signature *signature, const char **name, char eos) { + _assert(*name != NULL); + + bool named = **name == '"'; + + signature->elements = NULL; + signature->count = 0; + + for (;;) { + if (**name == eos) { + ++*name; + return; + } + + signature->elements = (struct Element *) prealloc_(pool, signature->elements, signature->count * sizeof(struct Element), (signature->count + 1) * sizeof(struct Element)); + _assert(signature->elements != NULL); + + struct Element *element = &signature->elements[signature->count++]; + + if (**name != '"') + element->name = NULL; + else { + char *quote = strchr(++*name, '"'); + element->name = apr_pstrmemdup(pool, *name, quote - *name); + *name = quote + 1; + } + + element->type = sig_parse_type(pool, name, eos, named); + + if (**name < '0' || **name > '9') + element->offset = _not(size_t); + else { + element->offset = 0; + + do + element->offset = element->offset * 10 + (*(*name)++ - '0'); + while (**name >= '0' && **name <= '9'); + } + } +} + +struct Type *sig_parse_type(apr_pool_t *pool, const char **name, char eos, bool named) { + char next = *(*name)++; + if (next == '?') + return NULL; + + struct Type *type = (struct Type *) apr_palloc(pool, sizeof(struct Type)); + _assert(type != NULL); + memset(type, 0, sizeof(struct Type)); + + parse: + switch (next) { + case '#': type->primitive = typename_P; break; + + case '(': + type->primitive = union_P; + next = ')'; + goto aggregate; + + case '*': type->primitive = string_P; break; + case ':': type->primitive = selector_P; break; + + case '@': + if (**name == '"') { + char *quote = strchr(*name + 1, '"'); + if (!named || quote[1] == eos || quote[1] == '"') { + type->name = apr_pstrmemdup(pool, *name + 1, quote - *name - 1); + *name = quote + 1; + } + } + + type->primitive = object_P; + break; + + case 'B': type->primitive = boolean_P; break; + case 'C': type->primitive = uchar_P; break; + case 'I': type->primitive = uint_P; break; + case 'L': type->primitive = ulong_P; break; + case 'Q': type->primitive = ulonglong_P; break; + case 'S': type->primitive = ushort_P; break; + + case '[': + type->primitive = array_P; + type->data.data.size = strtoul(*name, (char **) name, 10); + type->data.data.type = sig_parse_type(pool, name, eos, false); + if (**name != ']') { + printf("']' != \"%s\"\n", *name); + _assert(false); + } + ++*name; + break; + + case '^': + type->primitive = pointer_P; + if (**name == 'v') { + type->data.data.type = NULL; + ++*name; + } else if (**name == '"') { + type->data.data.type = NULL; + } else { + type->data.data.type = sig_parse_type(pool, name, eos, named); + } + break; + + case 'b': + type->primitive = bit_P; + type->data.data.size = strtoul(*name, (char **) name, 10); + break; + + case 'c': type->primitive = char_P; break; + case 'd': type->primitive = double_P; break; + case 'f': type->primitive = float_P; break; + case 'i': type->primitive = int_P; break; + case 'l': type->primitive = long_P; break; + case 'q': type->primitive = longlong_P; break; + case 's': type->primitive = short_P; break; + case 'v': type->primitive = void_P; break; + + case '{': + type->primitive = struct_P; + next = '}'; + goto aggregate; + + aggregate: { + char end = next; + const char *begin = *name; + do next = *(*name)++; + while ( + next != '=' && + next != '}' + ); + size_t length = *name - begin - 1; + if (strncmp(begin, "?", length) != 0) + type->name = (char *) apr_pstrmemdup(pool, begin, length); + else + type->name = NULL; + + char *types; + if (next != '=') + types = NULL; + else { + const char *temp = *name; + sig_parse_signature(pool, &type->data.signature, name, end); + types = (char *) apr_pstrmemdup(pool, temp, *name - temp - 1); + } + + if (type->name != NULL && sig_aggregate != NULL) { + char *angle = strchr(type->name, '<'); + if (angle == NULL) + (*sig_aggregate)(pool, type->primitive, type->name, &type->data.signature, types); + else { + angle = (char *) apr_pstrmemdup(pool, type->name, angle - type->name); + (*sig_aggregate)(pool, type->primitive, angle, &type->data.signature, types); + } + } + } break; + + case 'N': type->flags |= JOC_TYPE_INOUT; goto next; + case 'n': type->flags |= JOC_TYPE_IN; goto next; + case 'O': type->flags |= JOC_TYPE_BYCOPY; goto next; + case 'o': type->flags |= JOC_TYPE_OUT; goto next; + case 'R': type->flags |= JOC_TYPE_BYREF; goto next; + case 'r': type->flags |= JOC_TYPE_CONST; goto next; + case 'V': type->flags |= JOC_TYPE_ONEWAY; goto next; + + next: + next = *(*name)++; + goto parse; + break; + + default: + printf("invalid type character: '%c' {%s}\n", next, *name - 10); + _assert(false); + } + + return type; +} + +void Parse(apr_pool_t *pool, struct Signature *signature, const char *name) { + const char *temp = name; + sig_parse_signature(pool, signature, &temp, '\0'); + _assert(temp[-1] == '\0'); +} + +const char *sig_unparse_signature(apr_pool_t *pool, struct Signature *signature) { + const char *value = ""; + size_t offset; + + for (offset = 0; offset != signature->count; ++offset) { + const char *type = sig_unparse_type(pool, signature->elements[offset].type); + value = apr_pstrcat(pool, value, type, NULL); + } + + return value; +} + +const char *sig_unparse_type(apr_pool_t *pool, struct Type *type) { + if (type == NULL) + return "?"; + else switch (type->primitive) { + case typename_P: return "#"; + case union_P: return apr_psprintf(pool, "(%s)", sig_unparse_signature(pool, &type->data.signature)); + case string_P: return "*"; + case selector_P: return ":"; + case object_P: return type->name == NULL ? "@" : apr_psprintf(pool, "@\"%s\"", type->name); + case boolean_P: return "B"; + case uchar_P: return "C"; + case uint_P: return "I"; + case ulong_P: return "L"; + case ulonglong_P: return "Q"; + case ushort_P: return "S"; + + case array_P: { + const char *value = sig_unparse_type(pool, type->data.data.type); + return apr_psprintf(pool, "[%lu%s]", type->data.data.size, value); + } break; + + case pointer_P: return apr_psprintf(pool, "^%s", type->data.data.type == NULL ? "" : sig_unparse_type(pool, type->data.data.type)); + case bit_P: return apr_psprintf(pool, "b%zu", type->data.data.size); + case char_P: return "c"; + case double_P: return "d"; + case float_P: return "f"; + case int_P: return "i"; + case long_P: return "l"; + case longlong_P: return "q"; + case short_P: return "s"; + case void_P: return "v"; + case struct_P: return apr_psprintf(pool, "{%s=%s}", type->name == NULL ? "?" : type->name, sig_unparse_signature(pool, &type->data.signature)); + } + + _assert(false); + return NULL; +} + +} diff --git a/sig/parse.hpp b/sig/parse.hpp new file mode 100644 index 0000000..b315807 --- /dev/null +++ b/sig/parse.hpp @@ -0,0 +1,22 @@ +#ifndef SIG_PARSE_H +#define SIG_PARSE_H + +#include "sig/types.hpp" + +#include + +namespace sig { + +extern void (*sig_aggregate)(apr_pool_t *pool, enum Primitive primitive, const char *name, struct Signature *signature, const char *types); + +void sig_parse_signature(apr_pool_t *pool, struct Signature *signature, const char **name, char eos); +struct Type *sig_parse_type(apr_pool_t *pool, const char **name, char eos, bool named); + +void Parse(apr_pool_t *pool, struct Signature *signature, const char *name); + +const char *sig_unparse_signature(apr_pool_t *pool, struct Signature *signature); +const char *sig_unparse_type(apr_pool_t *pool, struct Type *type); + +} + +#endif/*SIG_PARSE_H*/ diff --git a/sig/types.hpp b/sig/types.hpp new file mode 100644 index 0000000..3e8d1f2 --- /dev/null +++ b/sig/types.hpp @@ -0,0 +1,74 @@ +#ifndef SIG_TYPES_H +#define SIG_TYPES_H + +#include "minimal/stdlib.h" + +namespace sig { + +enum Primitive { + typename_P = '#', + union_P = '(', + string_P = '*', + selector_P = ':', + object_P = '@', + boolean_P = 'B', + uchar_P = 'C', + uint_P = 'I', + ulong_P = 'L', + ulonglong_P = 'Q', + ushort_P = 'S', + array_P = '[', + pointer_P = '^', + bit_P = 'b', + char_P = 'c', + double_P = 'd', + float_P = 'f', + int_P = 'i', + long_P = 'l', + longlong_P = 'q', + short_P = 's', + void_P = 'v', + struct_P = '{' +}; + +struct Element { + char *name; + struct Type *type; + size_t offset; +}; + +struct Signature { + struct Element *elements; + size_t count; +}; + +#define JOC_TYPE_INOUT (1 << 0) +#define JOC_TYPE_IN (1 << 1) +#define JOC_TYPE_BYCOPY (1 << 2) +#define JOC_TYPE_OUT (1 << 3) +#define JOC_TYPE_BYREF (1 << 4) +#define JOC_TYPE_CONST (1 << 5) +#define JOC_TYPE_ONEWAY (1 << 6) + +struct Type { + enum Primitive primitive; + char *name; + uint8_t flags; + + union { + struct { + struct Type *type; + size_t size; + } data; + + struct Signature signature; + char *extra; + } data; +}; + +struct Type *joc_parse_type(char **name, char eos, bool variable, bool signature); +void joc_parse_signature(struct Signature *signature, char **name, char eos, bool variable); + +} + +#endif/*SIG_TYPES_H*/