Mercurial > projects > dstep
changeset 1:033d260cfc9b
First upload of the bridge
author | Jacob Carlborg <doob@me.com> |
---|---|
date | Thu, 18 Jun 2009 22:00:13 +0200 |
parents | c7db221de6e8 |
children | 9fd439a28ce3 |
files | .hgignore dsss.conf dstep/Cocoa/NSAutoreleasePool.d dstep/Cocoa/NSObject.d dstep/Cocoa/NSString.d dstep/internal/String.d dstep/internal/Traits.d dstep/internal/Tuple.d dstep/internal/Types.d dstep/internal/Util.d dstep/internal/Version.d dstep/internal/collection/Array.d dstep/objc/bindings.d dstep/objc/bridge/Bridge.d dstep/objc/bridge/Capsule.d dstep/objc/bridge/ClassInitializer.d dstep/objc/bridge/Type.d dstep/objc/bridge/TypeEncoding.d dstep/objc/bridge/Wrapper.d dstep/objc/message.d dstep/objc/objc.d dstep/objc/runtime.d sciprts/dgen.rb sciprts/dstepgen.rb scripts/dgen.rb scripts/dstepgen.rb |
diffstat | 26 files changed, 5605 insertions(+), 2738 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgignore Sat Jan 31 17:22:44 2009 +0100 +++ b/.hgignore Thu Jun 18 22:00:13 2009 +0200 @@ -8,4 +8,7 @@ *.sh *.a *.rf -*.dylib \ No newline at end of file +*.dylib +*.dstep +main.d +main \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dsss.conf Thu Jun 18 22:00:13 2009 +0200 @@ -0,0 +1,2 @@ +#[dstep] +[main.d] \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dstep/Cocoa/NSAutoreleasePool.d Thu Jun 18 22:00:13 2009 +0200 @@ -0,0 +1,31 @@ +/** + * Copyright: Copyright (c) 2009 Jacob Carlborg. + * Authors: Jacob Carlborg + * Version: Initial created: Apr 19, 2009 + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) + */ +module dstep.Cocoa.NSAutoreleasePool; + +import dstep.Cocoa.NSObject; +import dstep.objc.objc; +import dstep.objc.runtime; +/* +class NSAutoreleasePool : NSObject +{ + this (id object) + { + super(object); + } + + static NSAutoreleasePool alloc () + { + id r = objc.getClass!("NSAutoreleasePool").msgSend(sel.registerName!("alloc")); + return r ? new NSAutoreleasePool(r) : null; + } + + NSAutoreleasePool init () + { + return invokeObjcCall!(NSAutoreleasePool, "init"); + } + +}*/ \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dstep/Cocoa/NSObject.d Thu Jun 18 22:00:13 2009 +0200 @@ -0,0 +1,42 @@ +/** + * Copyright: Copyright (c) 2009 Jacob Carlborg. + * Authors: Jacob Carlborg + * Version: Initial created: Mar 28, 2009 + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) + */ +module dstep.Cocoa.NSObject; + +import mambo.io; + +import dstep.objc.bridge.Bridge; +import dstep.objc.bridge.Wrapper; + + + +class NSObject : ObjcWrapper +{ + mixin ObjcWrap; + + int i = 34; + + NSObject foo (NSObject o) + { + println("foo in NSObject"); + o.add(1, 2); + println(this is o); + + this.add(3, 4); + + return this; + } + + mixin ObjcBindMethod!(foo); + + int add (int x, int y) + { + println(x + y); + return x + y; + } + + mixin ObjcBindMethod!(add); +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dstep/Cocoa/NSString.d Thu Jun 18 22:00:13 2009 +0200 @@ -0,0 +1,36 @@ +/** + * Copyright: Copyright (c) 2009 Jacob Carlborg. + * Authors: Jacob Carlborg + * Version: Initial created: Apr 19, 2009 + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) + */ +module dstep.Cocoa.NSString; + +import tango.stdc.stringz; + +import dstep.Cocoa.NSObject; +import dstep.objc.bridge.Bridge; +import dstep.objc.bridge.Wrapper; +import dstep.internal.String; +import dstep.objc.objc; + +import mambo.io; + +class NSString : NSObject +{ + mixin ObjcWrap; + + string UTF8String () + { + char* s = invokeObjcSelf!(char*, "UTF8String"); + return fromStringz(s); + } + + int add (int x, int y) + { + println("NSString"); + return 0; + } + + mixin ObjcBindMethod!(add); +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dstep/internal/String.d Thu Jun 18 22:00:13 2009 +0200 @@ -0,0 +1,10 @@ +/** + * Copyright: Copyright (c) 2008-2009 Jacob Carlborg. + * Authors: Jacob Carlborg + * Version: Initial created: 2008 + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) + */ +module dstep.internal.String; + +public import dstep.internal.collection.Array; +public import mambo.string; \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dstep/internal/Traits.d Thu Jun 18 22:00:13 2009 +0200 @@ -0,0 +1,109 @@ +/** + * Copyright: Copyright (c) 2009 Jacob Carlborg. + * Authors: Jacob Carlborg + * Version: Initial created: Apr 28, 2009 + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) + */ +module dstep.internal.Traits; + +/** + * Returns the name of the given function + * + * Params: + * func = the function alias to get the name of + * + * Returns: the name of the function + */ +template functionNameOf (alias func) +{ + version(LDC) + const functionNameOf = (&func).stringof[1 .. $]; + + else + const functionNameOf = (&func).stringof[2 .. $]; +} + +/** + * Returns the parameter names of the given function + * + * Params: + * func = the function alias to get the parameter names of + * + * Returns: an array of strings containing the parameter names + */ +template parameterNamesOf (alias func) +{ + const parameterNamesOf = parameterNamesOfImpl!(func); +} + +private char[][] parameterNamesOfImpl (alias func) () +{ + char[] funcStr = typeof(&func).stringof; + + auto start = funcStr.indexOf('('); + auto end = funcStr.indexOf(')'); + + const firstPattern = ' '; + const secondPattern = ','; + + funcStr = funcStr[start + 1 .. end]; + + if (funcStr == "") + return null; + + funcStr ~= secondPattern; + + char[] token; + char[][] arr; + + foreach (c ; funcStr) + { + if (c != firstPattern && c != secondPattern) + token ~= c; + + else + { + if (token) + arr ~= token; + + token = null; + } + } + + char[][] result; + bool skip = false; + + foreach (str ; arr) + { + skip = !skip; + + if (skip) + continue; + + result ~= str; + } + + return result; +} + +/** + * Compile-time function to get the index of the give element. + * + * Performs a linear scan, returning the index of the first occurrence + * of the specified element in the array, or U.max if the array does + * not contain the element. + * + * Params: + * arr = the array to get the index of the element from + * element = the element to find + * + * Returns: the index of the element or size_t.max if the element was not found. + */ +private size_t indexOf (T) (T[] arr, T element) +{ + foreach (i, e ; arr) + if (e == element) + return i; + + return size_t.max; +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dstep/internal/Tuple.d Thu Jun 18 22:00:13 2009 +0200 @@ -0,0 +1,9 @@ +/** + * Copyright: Copyright (c) 2009 Jacob Carlborg. + * Authors: Jacob Carlborg + * Version: Initial created: May 10, 2009 + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) + */ +module dstep.internal.Tuple; + +public import mambo.util.Tuple; \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dstep/internal/Types.d Thu Jun 18 22:00:13 2009 +0200 @@ -0,0 +1,30 @@ +/** + * Copyright: Copyright (c) 2009 Jacob Carlborg. + * Authors: Jacob Carlborg + * Version: Initial created: Feb 1, 2009 + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) + */ +module dstep.internal.Types; + +import dstep.internal.Version; + +version (Windows) +{ + alias int c_long; + alias uint c_ulong; +} + +else +{ + static if (D_LP64) + { + alias long c_long; + alias ulong c_ulong; + } + + else + { + alias int c_long; + alias uint c_ulong; + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dstep/internal/Util.d Thu Jun 18 22:00:13 2009 +0200 @@ -0,0 +1,7 @@ +/** + * Copyright: Copyright (c) 2009 Jacob Carlborg. + * Authors: Jacob Carlborg + * Version: Initial created: Feb 1, 2009 + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) + */ +module dstep.internal.Util;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dstep/internal/Version.d Thu Jun 18 22:00:13 2009 +0200 @@ -0,0 +1,141 @@ +/** + * Copyright: Copyright (c) 2009 Jacob Carlborg. + * Authors: Jacob Carlborg + * Version: Initial created: Mar 28, 2009 + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) + */ +module dstep.internal.Version; + +public import mambo.util.Version; + +/*static if ((void*).sizeof > int.sizeof) + version = D_LP64; + +version (GNU) + version (darwin) + version = OSX; + +version (DigitalMars) + version (OSX) + version = darwin; + +//Compiler Vendors +version (DigitalMars) const bool DigitalMars = true; +else const bool DigitalMars = false; + +version (GNU) const bool GNU = true; +else const bool GNU = false; + +version (LDC) const bool LDC = true; +else const bool LDC = false; + +version (LLVM) const bool LLVM = true; +else const bool LLVM = false; + + + +//Processors +version (PPC) const bool PPC = true; +else const bool PPC = false; + +version (PPC64) const bool PPC64 = true; +else const bool PPC64 = false; + +version (SPARC) const bool SPARC = true; +else const bool SPARC = false; + +version (SPARC64) const bool SPARC64 = true; +else const bool SPARC64 = false; + +version (X86) const bool X86 = true; +else const bool X86 = false; + +version (X86_64) const bool X86_64 = true; +else const bool X86_64 = false; + + + +//Operating Systems +version (aix) const bool aix = true; +else const bool aix = false; + +version (cygwin) const bool cygwin = true; +else const bool cygwin = false; + +version (darwin) const bool darwin = true; +else const bool darwin = false; + +version (OSX) const bool OSX = true; +else const bool OSX = false; + +version (freebsd) const bool freebsd = true; +else const bool freebsd = false; + +version (linux) const bool linux = true; +else const bool linux = false; + +version (solaris) const bool solaris = true; +else const bool solaris = false; + +version (Unix) const bool Unix = true; +else const bool Unix = false; + +version (Win32) const bool Win32 = true; +else const bool Win32 = false; + +version (Win64) const bool Win64 = true; +else const bool Win64 = false; + +version (Windows) const bool Windows = true; +else const bool Windows = false; + + + +//Rest +version (BigEndian) const bool BigEndian = true; +else const bool BigEndian = false; + +version (LittleEndian) const bool LittleEndian = true; +else const bool LittleEndian = false; + +version (D_Coverage) const bool D_Coverage = true; +else const bool D_Coverage = false; + +version (D_Ddoc) const bool D_Ddoc = true; +else const bool D_Ddoc = false; + +version (D_InlineAsm_X86) const bool D_InlineAsm_X86 = true; +else const bool D_InlineAsm_X86 = false; + +version (D_InlineAsm_X86_64) const bool D_InlineAsm_X86_64 = true; +else const bool D_InlineAsm_X86_64 = false; + +version (D_LP64) const bool D_LP64 = true; +else const bool D_LP64 = false; + +version (D_PIC) const bool D_PIC = true; +else const bool D_PIC = false; + +version (GNU_BitsPerPointer32) const bool GNU_BitsPerPointer32 = true; +else const bool GNU_BitsPerPointer32 = false; + +version (GNU_BitsPerPointer64) const bool GNU_BitsPerPointer64 = true; +else const bool GNU_BitsPerPointer64 = false; + +version (all) const bool all = true; +else const bool D_InlineAsm_X86_64 = false; + +version (none) const bool D_InlineAsm_X86_64 = true; +else const bool none = false; + +version (Tango) +{ + const bool Tango = true; + const bool Phobos = false; +} + +else +{ + const bool Tango = false; + const bool Phobos = true; +}*/ \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dstep/internal/collection/Array.d Thu Jun 18 22:00:13 2009 +0200 @@ -0,0 +1,9 @@ +/** + * Copyright: Copyright (c) 2008-2009 Jacob Carlborg. + * Authors: Jacob Carlborg + * Version: Initial created: 2008 + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) + */ +module dstep.internal.collection.Array; + +public import mambo.collection.Array; \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dstep/objc/bindings.d Thu Jun 18 22:00:13 2009 +0200 @@ -0,0 +1,152 @@ +/** + * Copyright: Copyright (c) 2009 Jacob Carlborg. + * Authors: Jacob Carlborg + * Version: Initial created: Feb 1, 2009 + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) + */ +module dstep.objc.bindings; + +import dstep.objc.message; +import dstep.objc.objc; +import dstep.objc.runtime; +import dstep.internal.Util; + +extern (C) +{ + // runtime.h + id object_copy (id obj, size_t size); + id object_dispose (id obj); + Class object_getClass (id obj); + Class object_setClass (id obj, Class cls); + char* object_getClassName (id obj); + void* object_getIndexedIvars (id obj); + id object_getIvar (id obj, Ivar ivar); + void object_setIvar (id obj, Ivar ivar, id value); + Ivar object_setInstanceVariable (id obj, char* name, void* value); + Ivar object_getInstanceVariable (id obj, char* name, void** outValue); + id objc_getClass (char* name); + id objc_getMetaClass (char* name); + id objc_lookUpClass (char* name); + id objc_getRequiredClass (char* name); + Class objc_getFutureClass (char* name); + void objc_setFutureClass (Class cls, char* name); + int objc_getClassList (Class* buffer, int bufferCount); + id objc_getProtocol (char* name); + Protocol** objc_copyProtocolList (uint* outCount); + char* class_getName (Class cls); + bool class_isMetaClass (Class cls); + Class class_getSuperclass (Class cls); + Class class_setSuperclass (Class cls, Class newSuper); + int class_getVersion (Class cls); + void class_setVersion (Class cls, int version_); + size_t class_getInstanceSize (Class cls); + Ivar class_getInstanceVariable (Class cls, char* name); + Ivar class_getClassVariable (Class cls, char* name); + Ivar* class_copyIvarList (Class cls, uint* outCount); + Method class_getInstanceMethod (Class cls, SEL name); + Method class_getClassMethod (Class cls, SEL name); + IMP class_getMethodImplementation (Class cls, SEL name); + IMP class_getMethodImplementation_stret (Class cls, SEL name); + bool class_respondsToSelector (Class cls, SEL sel); + Method* class_copyMethodList (Class cls, uint* outCount); + bool class_conformsToProtocol (Class cls, Protocol* protocol); + Protocol** class_copyProtocolList (Class cls, uint* outCount); + objc_property_t* class_getProperty (Class cls, char* name); + objc_property_t* class_copyPropertyList (Class cls, uint* outCount); + char* class_getIvarLayout (Class cls); + char* class_getWeakIvarLayout (Class cls); + id class_createInstance (Class cls, size_t extraBytes); + Class objc_allocateClassPair (Class superclass, char* name, size_t extraBytes); + void objc_registerClassPair (Class cls); + Class objc_duplicateClass (Class original, char* name, size_t extraBytes); + void objc_disposeClassPair (Class cls); + bool class_addMethod (Class cls, SEL name, IMP imp, char* types); + IMP class_replaceMethod (Class cls, SEL name, IMP imp, char* types); + bool class_addIvar (Class cls, char* name, size_t size, ubyte alignment, char* types); + bool class_addProtocol (Class cls, Protocol* protocol); + void class_setIvarLayout (Class cls, char* layout); + void class_setWeakIvarLayout (Class cls, char* layout); + SEL method_getName (Method m); + IMP method_getImplementation (Method m); + char* method_getTypeEncoding (Method m); + uint method_getNumberOfArguments (Method m); + char* method_copyReturnType (Method m); + char* method_copyArgumentType (Method m, uint index); + void method_getReturnType (Method m, char* dst, size_t dst_len); + void method_getArgumentType (Method m, uint index, char* dst, size_t dst_len); + objc_method_description* method_getDescription (Method m); + IMP method_setImplementation (Method m, IMP imp); + void method_exchangeImplementations (Method m1, Method m2); + char* ivar_getName (Ivar v); + char* ivar_getTypeEncoding (Ivar v); + ptrdiff_t ivar_getOffset (Ivar v); + char* property_getName (objc_property_t* property); + char* property_getAttributes (objc_property_t* property); + bool protocol_conformsToProtocol (id proto, id other); + bool protocol_isEqual (id proto, id other); + char* protocol_getName (id p); + objc_method_description protocol_getMethodDescription (id p, SEL aSel, bool isRequiredMethod, bool isInstanceMethod); + objc_method_description* protocol_copyMethodDescriptionList(id p, bool isRequiredMethod, bool isInstanceMethod, uint* outCount); + objc_property_t* protocol_getProperty (id proto, char* name, bool isRequiredProperty, bool isInstanceProperty); + objc_property_t* protocol_copyPropertyList (id proto, uint* outCount); + Protocol** protocol_copyProtocolList (id proto, uint* outCount); + char** objc_copyImageNames (uint* outCount); + char* class_getImageName (Class cls); + char** objc_copyClassNamesForImage (char* image, uint* outCount); + char* sel_getName (SEL sel); + SEL sel_getUid (char* str); + SEL sel_registerName (char* str); + bool sel_isEqual (SEL lhs, SEL rhs); + void objc_enumerationMutation (id arg0); + void objc_setForwardHandler (void* fwd, void* fwd_stret); + bool sel_isMapped (SEL sel); + id object_copyFromZone (id anObject, size_t nBytes, void* z); + id object_realloc (id anObject, size_t nBytes); + id object_reallocFromZone (id anObject, size_t nBytes, void* z); + void* objc_getClasses (); + void objc_addClass (Class myClass); + void objc_setMultithreaded (bool flag); + id class_createInstanceFromZone (Class arg0, size_t idxIvars, void* z); + void class_addMethods (Class arg0, objc_method_list* arg1); + void class_removeMethods (Class arg0, objc_method_list* arg1); + Class class_poseAs (Class imposter, Class original); + uint method_getSizeOfArguments (Method m); + uint method_getArgumentInfo (objc_method* m, int arg, char** type, int* offset); + bool class_respondsToMethod (Class arg0, SEL arg1); + IMP class_lookupMethod (Class arg0, SEL arg1); + Class objc_getOrigClass (char* name); + objc_method_list* class_nextMethodList (Class arg0, void* arg1); + + // message.h + id objc_msgSend (id self, SEL op, ...); + id objc_msgSendSuper (objc_super* super_, SEL op, ...); + void objc_msgSend_stret (void* stretAddr, id self, SEL op, ...); + void objc_msgSendSuper_stret (void* stretAddr, objc_super* super_, SEL op, ...); + id objc_msgSendv (id self, SEL op, size_t arg_size, void* arg_frame); + void objc_msgSendv_stret (void* stretAddr, id self, SEL op, size_t arg_size, void* arg_frame); + + id method_invoke (id receiver, Method m, ...); + void method_invoke_stret (id receiver, Method m, ...); + + version (X86_64) + real objc_msgSend_fpret (id self, SEL op, ...); + + else version (X86) + { + double objc_msgSendv_fpret (id self, SEL op, uint arg_size, void* arg_frame); + double objc_msgSend_fpret (id self, SEL op, ...); + } +} + +version (build) +{ + version (GNU) + { + pragma(link, "objc -framework Cocoa"); + } + + else + { + pragma(link, "objc -L-framework -LCocoa"); + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dstep/objc/bridge/Bridge.d Thu Jun 18 22:00:13 2009 +0200 @@ -0,0 +1,431 @@ +/** + * Copyright: Copyright (c) 2009 Jacob Carlborg. + * Authors: Jacob Carlborg + * Version: Initial created: Feb 4, 2009 + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) + */ +module dstep.objc.bridge.Bridge; + +import mambo.io; + +version (Tango) +{ + import tango.core.Memory; + import tango.core.Traits : ParameterTupleOf, ReturnTypeOf; + + alias GC.addRoot addRoot; +} + +else +{ + import std.gc : addRoot; + import std.traits : ParameterTypeTuple, ReturnType; + + alias ReturnType ReturnTypeOf; + alias ParameterTypeTuple ParameterTupleOf; +} + +import dstep.internal.String; +import dstep.internal.Tuple; +import dstep.internal.Version; +import dstep.objc.bridge.Capsule; +import dstep.objc.bridge.ClassInitializer; +import dstep.objc.bridge.TypeEncoding; +import dstep.objc.bridge.Wrapper; +import dstep.objc.message; +import dstep.objc.objc; +import dstep.objc.runtime; + +/** + * Builds a string representing a selector out of the given function + * + * It will build the string like this: + * + * --- + * foo (int x, int y); // foo:y: + * bar (); // bar + * fooBar (int x); // fooBar: + * --- + * + * Params: + * func = the function alias to build the selector of + * + * Returns: a string representing the selector + */ +template selector (alias func) +{ + const selector = buildSelector!(func); +} + +template ObjcWrap () +{ + mixin ObjcWrap!(this.stringof); +} + +template ObjcWrap (string name) +{ + import dstep.objc.bridge.ClassInitializer : ObjcSubclassInitializer, subclassInit; + import dstep.objc.objc : Class, id, IMP, SEL; + + static private Class objcClass_; + static private Class objcSuperClass_; + + this (id object) + { + super(object); + } + + static typeof(this) alloc () + { + return invokeObjcSelfClass!(typeof(this), "alloc"); + } + + typeof(this) init () + { + id ret = invokeObjcSelf!(id, "init"); + + if (!ret) + return null; + + if (ret is objcObject) + { + dObject = this; + return this; + } + + auto object = new typeof(this) (ret); + object.dObject = this; + + return object; + } + + mixin ObjcSubclassInitializer!("D_" ~ name, name); +} + +struct ObjcMethodDeclaration (alias imp, R, string name, ARGS...) +{ + alias imp methodImp; + alias R returnType; + const string methodName = name; + alias ARGS argsType; +} + +template ObjcBindMethod (alias method) +{ + import dstep.objc.bridge.TypeEncoding : buildSelector, encode; + + mixin ObjcBindMethod!(method, buildSelector!(method)); +} + +template ObjcBindMethod (alias method, string selector) +{ + mixin ObjcBindMethod!(method, ReturnTypeOf!(method), selector, ParameterTupleOf!(method)); +} + +template ObjcBindMethod (alias method, R, string selector, ARGS...) +{ + import dstep.objc.bridge.Capsule : decapsule, encapsule, isCapsule, needsEncapsulation; + import dstep.internal.Tuple; + + private ObjcMethodDeclaration!(method, R, selector, ARGS) objcMethodDeclaration; + + private R delegate (ARGS) resolveVirtualCall () + { + return null; + } + + static if (needsEncapsulation!(R)) + { + private alias ReplaceAllClasses!(id, ARGS) ObjcArgs; + + private static id forwardVirtualCall (id self, SEL cmd, ObjcArgs objcArgs) + in + { + assert(isCapsule(self)); + } + body + { + R delegate (ARGS) dg; + dg.funcptr = &method; + dg.ptr = Bridge.getDObject(self); + + ARGS args; + + foreach (i, a ; objcArgs) + { + alias typeof(args[i]) ArgType; + + args[i] = decapsule!(ArgType)(a); + } + + auto result = dg(args); + + return encapsule!(R)(result); + } + } + + else + { + private alias ReplaceAllClasses!(id, ARGS) ObjcArgs; + + private static R forwardVirtualCall (id self, SEL cmd, ObjcArgs objcArgs) + in + { + assert(isCapsule(self)); + } + body + { + R delegate (ARGS) dg; + dg.funcptr = &method; + dg.ptr = Bridge.getDObject(self); + + ARGS args; + + foreach (i, a ; objcArgs) + { + alias typeof(args[i]) ArgType; + + args[i] = decapsule!(ArgType)(a); + } + + return dg(args); + } + } +} + +template ObjcBindClassMethod (alias method, string selector) +{ + mixin ObjcBindClassMethod!(method, ReturnTypeOf!(method), selector, ParameterTupleOf!(method)); +} + +template ObjcBindClassMethod (alias method, R, string selector, ARGS...) +{ + private ObjcMethodDeclaration!(method, R, selector, ARGS) objcClassMethodDeclaration; + + static if (needsEncapsulation!(R)) + { + private alias ReplaceAllClasses!(id, ARGS) ObjcArgs; + + private static id forwardStaticCall (id self, SEL cmd, ObjcArgs objcArgs) + in + { + assert(isCapsule(self)); + } + body + { + R function (ARGS) dg = &method; + + ARGS args; + + foreach (i, a ; objcArgs) + { + alias typeof(args[i]) ArgType; + + args[i] = decapsule!(ArgType)(a); + } + + auto result = dg(args); + + return encapsule!(R)(result); + } + } + + else + { + private alias ReplaceAllClasses!(id, ARGS) ObjcArgs; + + private static R forwardStaticCall (id self, SEL cmd, ObjcArgs objcArgs) + in + { + assert(isCapsule(self)); + } + body + { + R function (ARGS) dg = &method; + + ARGS args; + + foreach (i, a ; objcArgs) + { + alias typeof(args[i]) ArgType; + + args[i] = decapsule!(ArgType)(a); + } + + return dg(args); + } + } +} + +struct Bridge +{ + static: + + const objcMethodDeclarationVar = "objcMethodDeclaration"; + const objcClassMethodDeclarationVar = "objcClassMethodDeclaration"; + const dObjectVar = "dObject"; + alias void* DObjectType; + private ClassInfo[string] registeredClasses; + + void registerClass (string className, ClassInfo classInfo) + { + if (className.length > 2 && className[0 .. 2] == "D_") + registeredClasses[className[0 .. 2]] = classInfo; + + else + registeredClasses[className] = classInfo; + } + + Object createRegisteredObject (T) (string className) + { + return null; + } + + DObjectType getDObject (id self) + { + DObjectType dObject; + + self.getInstanceVariable!(DObjectType, dObjectVar)(dObject); + + return dObject; + } + + id setDObject (Object dObject, id objcObject) + { + auto o = cast(DObjectType) dObject; + addRoot(o); + + objcObject.setInstanceVariable!(DObjectType, Bridge.dObjectVar)(o); + + return objcObject; + } + + R invokeObjcMethod (R, string selector, ARGS...) (id self, ARGS args) + { + static if (!is(R : void)) + R result; + + SEL sel = sel.registerName!(selector); + + alias ReplaceAllClasses!(id, ARGS) ObjcArgs; + ObjcArgs objcArgs; + + foreach (i, a ; args) + { + alias typeof(a) ArgType; + + objcArgs[i] = encapsule!(ArgType)(a); + } + + static if (is(R == struct)) + { + self.msgSend_stret(result, sel, objcArgs); + return result; + } + + else static if (is(R == float) || is(R == double) || is(R == real)) + { + version (X86) + { + static if (is(R == float)) + return self.msgSend_fpret!(R)(sel, objcArgs); + + else + return self.msgSend_fpret(sel, objcArgs); + } + + else version (X86_64) + { + static if (is(R == real)) + return self.msgSend_fpret(sel, objcArgs); + + else + return self.msgSend!(R)(sel, objcArgs); + } + + else + return self.msgSend!(R)(sel, objcArgs); + } + + else static if (is(R : ObjcWrapper)) + { + id r = self.msgSend(sel, objcArgs); + + if (!r) + return null; + + if (isCapsule(r)) + { + result = decapsule!(R)(r); + + if (result) + return result; + } + + return new R(r); + } + + else static if (is(R : Object)) + { + id r = self.msgSend(sel, objcArgs); + + if (!r) + return null; + + return decapsule!(R)(r); + } + + else + return self.msgSend!(R, ObjcArgs)(sel, objcArgs); + } + + R invokeObjcClassMethod (R = id, string selector, ARGS...) (Class cls, ARGS args) + { + return invokeObjcMethod!(R, selector, ARGS)(cast(id) cls, args); + } + + R invokeObjcSuperMethod (R = id, string selector, ARGS...) (objc_super* self, ARGS args) + { + R result; + SEL sel = sel.registerName!(selector); + + alias ReplaceAllClasses!(id, ARGS) ObjcArgs; + ObjcArgs objcArgs; + + foreach (i, a ; args) + { + alias typeof(a) ArgType; + + objcArgs[i] = encapsule!(ArgType)(a); + } + + static if (is(R == struct)) + { + self.msgSendSuper_stret(result, sel, objcArgs); + } + + else static if (is(R : ObjcWrapper)) + { + id r = self.msgSendSuper(sel, objcArgs); + + if (isCapsule(r)) + { + result = decapsule!(R)(r); + + if (result) + return result; + } + + return r ? new R(r) : null; + } + + else static if (is(R : Object)) + { + id r = self.msgSendSuper(sel, objcArgs); + return decapsule!(R)(r); + } + + else + return self.msgSend!(R)(sel, objcArgs); + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dstep/objc/bridge/Capsule.d Thu Jun 18 22:00:13 2009 +0200 @@ -0,0 +1,177 @@ +/** + * Copyright: Copyright (c) 2009 Jacob Carlborg. + * Authors: Jacob Carlborg + * Version: Initial created: Feb 3, 2009 + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) + */ +module dstep.objc.bridge.Capsule; + +version (Tango) +{ + import tango.core.Memory; + import tango.math.Math : log2; + + alias GC.addRoot addRoot; + + import tango.core.Traits; +} + +else +{ + import std.gc : addRoot; + import std.math : log2; +} + +import dstep.internal.String; +import dstep.objc.bridge.Bridge; +import dstep.objc.bridge.TypeEncoding; +import dstep.objc.bridge.Wrapper; +import dstep.objc.message; +import dstep.objc.objc; +import dstep.objc.runtime; +import bindings = dstep.objc.bindings; + +import mambo.io; + +template ObjcType (T) +{ + static if (needsEncapsulation!(T)) + alias id ObjcType; + + else + alias T ObjcType; +} + +Class capsuleClass () +{ + if (!Capsule.capsuleClass) + initCapsuleClass; + + return Capsule.capsuleClass; +} + +private struct Capsule +{ + static Class capsuleClass; + + static id create (Object object, Class capsuleClass) + { + id capsule = objc.createInstance(capsuleClass, 0); + + Bridge.setDObject(object, capsule); + + return capsule; + } + + static id description (id self, SEL op) + in + { + assert(isCapsule(self)); + } + body + { + return encapsuleString(decapsule!(Object)(self).toString); + } + + static hash_t hash (id self, SEL op) + in + { + assert(isCapsule(self)); + } + body + { + return decapsule!(Object)(self).toHash; + } + + static byte isEqual (id self, SEL op, Object object) + in + { + assert(isCapsule(self)); + } + body + { + return decapsule!(Object)(self) == object; + } +} + +private void initCapsuleClass () +{ + Class superClass = cast(Class) objc.getClass!("NSObject"); + Capsule.capsuleClass = objc.allocateClassPair!("D_Object")(superClass, 0); + + ubyte alignment = cast(ubyte) log2(Bridge.DObjectType.sizeof); + + Capsule.capsuleClass.addIvar!(Bridge.dObjectVar, encode!(Bridge.DObjectType))(Bridge.DObjectType.sizeof, alignment); + + Capsule.capsuleClass.addMethod!(encodeCallable!(Capsule.description))(sel.registerName!("description"), cast(IMP) &Capsule.description); + Capsule.capsuleClass.addMethod!(encodeCallable!(Capsule.hash))(sel.registerName!("hash"), cast(IMP) &Capsule.hash); + Capsule.capsuleClass.addMethod!(encodeCallable!(Capsule.isEqual))(sel.registerName!("isEqual"), cast (IMP) &Capsule.isEqual); + + objc.registerClassPair(capsuleClass); +} + +bool isCapsule (id capsule) +{ + if (!capsule) + return false; + + Class cls = capsule.isa; + return cls.getInstanceVariable!(Bridge.dObjectVar) !is null; +} + +template needsEncapsulation (T) +{ + static if (is(T == class)) + const needsEncapsulation = true; + + else + const needsEncapsulation = false; +} + +ObjcType!(T) encapsule (T) (T value) +{ + static if (needsEncapsulation!(T)) + { + if (!value) + return null; + + if (auto wrapper = cast(ObjcWrapper) value) + return wrapper.objcObject; + + return Capsule.create(value, capsuleClass); + } + + else + return value; +} + +template decapsule (T) +{ + T decapsule (ObjcType!(T) value) + { + static if (needsEncapsulation!(T)) + { + if (isCapsule(value)) + return cast(T) Bridge.getDObject(value); + + else + { + static if (is(T : ObjcWrapper)) + new T(value); + + else + return null; + } + } + + else + return value; + } +} + +private id encapsuleString (string str) +{ + enum { NSUTF8StringEncoding = 4 } + + return objc.getClass!("NSString").msgSend(sel.registerName!("alloc")).msgSend(sel.registerName!("initWithBytes:length:encoding:"), str.ptr, str.length, NSUTF8StringEncoding); +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dstep/objc/bridge/ClassInitializer.d Thu Jun 18 22:00:13 2009 +0200 @@ -0,0 +1,135 @@ +/** + * Copyright: Copyright (c) 2009 Jacob Carlborg. + * Authors: Jacob Carlborg + * Version: Initial created: Apr 7, 2009 + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) + */ +module dstep.objc.bridge.ClassInitializer; + +version (Tango) + import tango.math.Math; + +else + import std.math; + +import dstep.internal.String; +import bindings = dstep.objc.bindings; +import dstep.objc.bridge.Bridge; +import dstep.objc.bridge.TypeEncoding; +import dstep.objc.objc; +import dstep.objc.runtime; + +package template ObjcSubclassInitializer (string subclassName, string superclassName) +{ + import dstep.internal.String : string; + import dstep.objc.bridge.Bridge : ObjcBindMethod; + import dstep.objc.runtime : Method, objc_method, sel, objc; + import dstep.objc.bridge.TypeEncoding : encode; + + static Class objcClass () + { + if (objcClass_) + return objcClass_; + + Class superClass = cast(Class) objc.getClass!(superclassName); + + while (!superClass) + superClass = super.objcClass; + + return objcClass_ = subclassInit!(subclassName)(superClass, collectObjcInstanceMethods, collectObjcClassMethods); + } + + private static Method[] collectObjcInstanceMethods () + { + Method[] methods; + + mixin("alias " ~ superclassName ~ " Type;"); + + foreach (i, f; typeof(Type.tupleof)) + { + const len = Type.stringof.length; + const fieldName = Type.tupleof[i].stringof[1 + len + 2 .. $]; + + static if (fieldName == Bridge.objcMethodDeclarationVar) + { + typeof(Type.tupleof[i]) field; + + Method m = new objc_method; + m.method_name = sel.registerName!(field.methodName); + m.method_types = encode!(field.returnType, id, SEL, field.argsType).ptr; + m.method_imp = cast(IMP) &Type.ObjcBindMethod!(field.methodImp, field.returnType, field.methodName, field.argsType).forwardVirtualCall; + + methods ~= m; + } + } + + return methods; + } + + private static Method[] collectObjcClassMethods () + { + Method[] methods; + + mixin("alias " ~ superclassName ~ " Type;"); + + foreach (i, f; typeof(Type.tupleof)) + { + const len = Type.stringof.length; + const fieldName = Type.tupleof[i].stringof[1 + len + 2 .. $]; + + static if (fieldName == Bridge.objcClassMethodDeclarationVar) + { + typeof(Type.tupleof[i]) field; + + Method m = new objc_method; + m.method_name = sel.registerName!(field.methodName); + m.method_types = encode!(field.returnType, field.argsType).ptr; + m.method_imp = cast(IMP) &Type.ObjcBindClassMethod!(field.methodImp, field.returnType, field.methodName, field.argsType).forwardStaticCall; + + methods ~= m; + } + } + + return methods; + } + + private R invokeObjcSelf (R, string name, ARGS...) (ARGS args) + { + return Bridge.invokeObjcMethod!(R, name, ARGS)(objcObject, args); + } + + private R invokeObjcSuper (R, string name, ARGS...) (ARGS args) + { + return Bridge.invokeObjcMethod!(R, name, ARGS)(objcSuper, args); + } + + private static R invokeObjcSelfClass (R, string name, ARGS...) (ARGS args) + { + return Bridge.invokeObjcClassMethod!(R, name, ARGS)(objcClass, args); + } + + private static R invokeObjcSuperClass (R, string name, ARGS...) (ARGS args) + { + return Bridge.invokeObjcClassMethod!(R, name, ARGS)(objcClass.getSuperclass, args); + } +} + +Class subclassInit (string className) (Class superClass, Method[] instanceMethods = null, Method[] classMethods = null) +{ + Class cls = objc.allocateClassPair!(className)(superClass, 0); + Class metaClass = (cast(id) cls).getClass; + + ubyte alignment = cast(ubyte) log2(Bridge.DObjectType.sizeof); + + cls.addIvar!(Bridge.dObjectVar, encode!(Bridge.DObjectType))(Bridge.DObjectType.sizeof, alignment); + + foreach (method ; instanceMethods) + bindings.class_addMethod(cls, method.method_name, method.method_imp, method.method_types); + + foreach (method ; classMethods) + bindings.class_addMethod(metaClass, method.method_name, method.method_imp, method.method_types); + + objc.registerClassPair(cls); + + return cls; +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dstep/objc/bridge/Type.d Thu Jun 18 22:00:13 2009 +0200 @@ -0,0 +1,36 @@ +/** + * Copyright: Copyright (c) 2009 Jacob Carlborg. + * Authors: Jacob Carlborg + * Version: Initial created: May 16, 2009 + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) + */ +module dstep.objc.bridge.Type; + +version (Tango) + import tango.core.Tuple; + +else + import std.typetuple; + +import dstep.objc.objc; + +template ObjcTypeTuple (TList...) +{ + alias replaceClasses!(TList).types ObjcTypeTuple; +} + +private struct Wrap (TList...) +{ + TList types; +} + +private Wrap!(TList) replaceClasses (TList...) () +{ + alias Tuple!(TList) Types; + + foreach (ref type ; Types) + static if (is(type == class)) + type = id; + + return Wrap!(Types); +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dstep/objc/bridge/TypeEncoding.d Thu Jun 18 22:00:13 2009 +0200 @@ -0,0 +1,156 @@ +/** + * Copyright: Copyright (c) 2009 Jacob Carlborg. + * Authors: Jacob Carlborg + * Version: Initial created: Feb 8, 2009 + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) + */ +module dstep.objc.bridge.TypeEncoding; + +version (Tango) + import tango.core.Traits; + +else + import std.traits; + +import dstep.internal.String; +import dstep.internal.Traits; +import dstep.objc.objc; +import dstep.objc.runtime; + +/** + * Creates the encoded (mangled) form of a type for the + * Objective-C runtime. + */ +template encode (TL...) +{ + const string encode = encodeImpl!(TL)(); +} + +private string encodeImpl (TL...) () +{ + char[] s; + + foreach (T ; TL) + { + static if (is (T == id) || is (T == class)) + s ~= _C_ID; + + else static if (is (T == Class)) + s ~= _C_CLASS; + + else static if (is (T == SEL)) + s ~= _C_SEL; + + else static if (is (T == byte)) + s ~= _C_CHR; + + else static if (is (T == ubyte)) + s ~= _C_UCHR; + + else static if (is (T == short)) + s ~= _C_SHT; + + else static if (is (T == ushort)) + s ~= _C_USHT; + + else static if (is (T == int)) + s ~= _C_INT; + + else static if (is (T == uint)) + s ~= _C_UINT; + + else static if (is (T == long)) + s ~= _C_LNG_LNG; + + else static if (is (T == ulong)) + s ~= _C_ULNG_LNG; + + else static if (is (T == float)) + s ~= _C_FLT; + + else static if (is (T == double)) + s ~= _C_DBL; + + else static if (is (T == bool)) + s ~= _C_BOOL; + + else static if (is (T == void)) + s ~= _C_VOID; + + else static if (is (T U : T*)) + s ~= _C_VOID ~ encode!(U); + + else static if (is (T : char*)) + s ~= _C_CHARPTR; + + else static if (is (T U : T)) + s ~= _C_ARY_B ~ encode!(U) ~ _C_ARY_E; + + else static if (is (T == union)) + s ~= _C_UNION_B ~ encode!(typeof(T.tupleof)) ~ _C_UNION_E; + + else static if (is (T == struct)) + s ~= _C_STRUCT_B ~ encode!(typeof(T.tupleof)) ~ _C_STRUCT_E; + + else + s ~= _C_UNDEF; + } + + return s; +} + +/** + * Create the encoded (mangled) form of a type for the + * Objective-C runtime out of the given callable type. + * This is a shortcut for encode + */ +template encodeCallable (alias Callable) +{ + static assert(isCallableType!(typeof(Callable)), "dstep.objc.bridge.TypeCoding.encodeCallable: The given type is not a callable type"); + + const encodeCallable = encode!(ReturnTypeOf!(typeof(Callable)), id, SEL, ParameterTupleOf!(typeof(Callable))); +} + +/** + * Builds a string representing a selector out of the given function + * + * It will build the string like this: + * + * --- + * foo (int x, int y); // foo:y: + * bar (); // bar + * fooBar (int x); // fooBar: + * --- + * + * Params: + * func = the function alias to build the selector of + * + * Returns: a string representing the selector + */ +template buildSelector (alias method) +{ + const buildSelector = buildSelectorImpl!(method); +} + +private string buildSelectorImpl (alias method) () +{ + string result = functionNameOf!(method); + string[] parameterNames = parameterNamesOf!(method); + + if (parameterNames.length == 0) + return result; + + else if (parameterNames.length == 1) + return result ~ ':'; + + else if (parameterNames.length >= 2) + parameterNames = parameterNames[1 .. $]; + + result ~= ':'; + + foreach (str ; parameterNames) + result ~= str ~ ':'; + + return result; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dstep/objc/bridge/Wrapper.d Thu Jun 18 22:00:13 2009 +0200 @@ -0,0 +1,80 @@ +/** + * Copyright: Copyright (c) 2009 Jacob Carlborg. + * Authors: Jacob Carlborg + * Version: Initial created: Feb 1, 2009 + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) + */ +module dstep.objc.bridge.Wrapper; + +import dstep.internal.String; +import dstep.objc.bridge.Bridge; +import dstep.objc.bridge.Capsule; +import dstep.objc.message; +import dstep.objc.objc; +import dstep.objc.runtime; + +class ObjcWrapper +{ + package objc_super* objcSuper; + + static private Class objcClass_; + static private Class objcSuperClass_; + + private id objcObject_; + + /// Initialize object from an Objective-C object instance to wrap. + this (id object) + { + objcObject_ = object; + dObject = this; + } + + static Class objcClass () + { + return capsuleClass; + } + + static Class objcSuperClass () + { + if (!objcSuperClass_) + return objcSuperClass_ = objcClass.getSuperclass; + + return objcSuperClass_; + } + + id objcObject () + { + return objcObject_; + } + + protected id objcObject (id object) + { + return objcObject_ = object; + } + + protected void dObject (Object dObject) + { + Bridge.setDObject(dObject, objcObject); + } + + /*this (bool init = true, bool alloc = true) + { + if (init && alloc) + { + id result = objc_msgSend!(id)(objc_getClass(this.classinfo.name), sel.registerName!("alloc")); + id result2; + + if (result) + result2 = objc_msgSend(result, sel.registerName!("init")); + + if (result2) + objc.object = result2; + + else + objc.object = result; + } + + else if (alloc) + objc.object = objc_msgSend(objc_getClass(this.classinfo.name), sel.registerName!("alloc")); + }*/ +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dstep/objc/message.d Thu Jun 18 22:00:13 2009 +0200 @@ -0,0 +1,117 @@ +/** + * Copyright: Copyright (c) 2009 Jacob Carlborg. + * Authors: Jacob Carlborg + * Version: Initial created: Feb 1, 2009 + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) + */ +module dstep.objc.message; + +import dstep.internal.Version; +import bindings = dstep.objc.bindings; +import dstep.objc.objc; +import dstep.objc.runtime; + +alias void* marg_list; + +version (X86) + const int STRUCT_SIZE_LIMIT = 8; + +else version (PPC) + const int STRUCT_SIZE_LIMIT = 4; + +else version (X86_64) + const int STRUCT_SIZE_LIMIT = 16; + +else version (PPC64) // Not sure about this + const int STRUCT_SIZE_LIMIT = 16; + +struct objc_super +{ + id receiver; + Class super_class; + + // for dwt compatibility + alias super_class cls; + + R msgSendSuper (R = id, ARGS...) (SEL op, ARGS args) + { + return (cast(R function (id, SEL, ARGS...))&bindings.objc_msgSendSuper)(this, op, args); + } + + void msgSendSuper_stret (T, ARGS...) (out T stretAddr, id self, SEL op, ARGS args) + { + if (T.sizeof > STRUCT_SIZE_LIMIT) + (cast(void function (T*, id, SEL, ARGS...))&bindings.objc_msgSendSuper_stret)(&stretAddr, self, op, args); + + else + stretAddr = (*cast(T function (id, SEL, ARGS...))&bindings.objc_msgSendSuper)(self, op, args); + } +} + +R objc_msgSend (R = id, ARGS...) (id self, SEL op, ARGS args) +{ + return (cast(R function (id, SEL, ARGS...))&bindings.objc_msgSend)(self, op, args); +} + +R objc_msgSendSuper (R = id, ARGS...) (objc_super* super_, SEL op, ARGS args) +{ + return (cast(R function (id, SEL, ARGS...))&bindings.objc_msgSendSuper)(super_, op, args); +} + +void objc_msgSend_stret (T, ARGS...) (out T stretAddr, id self, SEL op, ARGS args) +{ + if (T.sizeof > STRUCT_SIZE_LIMIT) + (cast(void function (T*, id, SEL, ARGS...))&bindings.objc_msgSend_stret)(&stretAddr, self, op, args); + + else + stretAddr = (*cast(T function (id, SEL, ARGS...))&bindings.objc_msgSend)(self, op, args); +} + +static if (X86 || X86_64) +{ + R objc_msgSend_fpret (R = id, ARGS...) (id self, SEL op, ARGS args) + { + version (X86_64) + static assert(!is(R : real), "Only real are legal return value for objc_msgSend_fpret"); + + else + static assert(!is(R : double) && !is(R : float), "Only double and float are legal return values for objc_msgSend_fpret"); + + return (cast(R function (id, SEL, ARGS...))&bindings.objc_msgSend_fpret)(self, op, args); + } +} + +R method_invoke (R = id, ARGS...) (id receiver, Method m, ARGS args) +{ + static assert(receiver !is null); + return (cast(R function (id, SEL, ARGS...))&bindings.method_invoke)(receiver, m, args); +} + +void method_invoke_stret (ARGS...) (id receiver, Method m, ARGS args) +{ + static assert(receiver !is null); + return (cast(R function (id, SEL, ARGS...))&bindings.method_invoke_stret)(receiver, m, args); +} + +R objc_msgSendv (R = id, T) (id self, SEL op, size_t arg_size, T arg_frame) +{ + (cast(R function (id, SEL, size_t, T))&bindings.objc_msgSendv)(self, op, arg_size, arg_frame); +} + +void objc_msgSendv_stret (R = id, T) (out T stretAddr, id self, SEL op, size_t arg_size, T arg_frame) +{ + if (R.sizeof > STRUCT_SIZE_LIMIT) + (cast(void function (R*, id, SEL, size_t, T))&bindings.objc_msgSendv_stret)(&stretAddr, self, op, arg_size, arg_frame); + + else + stretAddr = (*cast(R function (id, SEL, size_t, T))&bindings.objc_msgSendv)(self, op, arg_size, arg_frame); +} + +version (X86) +{ + R objc_msgSendv_fpret (R = id, T) (id self, SEL op, uint arg_size, T arg_frame) + { + static assert(!is(R : double) && !is(R : float), "Only double and float are legal return values for objc_msgSendv_fpret"); + return (cast(R function (id, SEL, uint, T))&bindings.objc_msgSendv_fpret)(self, op, arg_size, arg_frame); + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dstep/objc/objc.d Thu Jun 18 22:00:13 2009 +0200 @@ -0,0 +1,220 @@ +/** + * Copyright: Copyright (c) 2009 Jacob Carlborg. + * Authors: Jacob Carlborg + * Version: Initial created: Feb 1, 2009 + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) + */ +module dstep.objc.objc; + +import dstep.internal.String; +import dstep.internal.Types; +import bindings = dstep.objc.bindings; +import dstep.objc.message; +import dstep.objc.runtime; + +import mambo.io; + +alias objc_object* id; +alias objc_class* Class; +alias objc_selector* SEL; +alias extern (C) id function(id self, SEL op, ...) IMP; +alias bool BOOL; +alias char* STR; + +alias typeof(null) Nil; +alias typeof(null) nil; + +alias c_long arith_t; +alias c_ulong uarith_t; + +const bool YES = true; +const bool NO = false; + +struct objc_object +{ + Class isa; + + + + // D Extensions: + + string getClassName () + { + return fromStringz(bindings.object_getClassName(this)); + } + + Ivar setInstanceVariable (T, string name) (T value) + { + return bindings.object_setInstanceVariable(this, name.ptr, value); + } + + Ivar getInstanceVariable (T, string name) (out T outValue) + { + return bindings.object_getInstanceVariable(this, name.ptr, &outValue); + } + + objc_property_t* getProperty (string name, bool isRequiredProperty, bool isInstanceProperty) + { + return bindings.protocol_getProperty(this, name.toStringz(), isRequiredProperty, isInstanceProperty); + } + + objc_property_t* copyPropertyList (out uint outCount) + { + return bindings.protocol_copyPropertyList(this, &outCount); + } + + Protocol** copyProtocolList (out uint outCount) + { + return bindings.protocol_copyProtocolList(this, &outCount); + } + + string getName () + { + return fromStringz(bindings.protocol_getName(this)); + } + + objc_method_description* copyMethodDescriptionList (bool isRequiredMethod, bool isInstanceMethod, out uint outCount) + { + return bindings.protocol_copyMethodDescriptionList(this, isRequiredMethod, isInstanceMethod, &outCount); + } + + id copy (size_t size) + { + return bindings.object_copy(this, size); + } + + id dispose () + { + return bindings.object_dispose(this); + } + + Class getClass () + { + return bindings.object_getClass(this); + } + + Class setClass (Class cls) + { + return bindings.object_setClass(this, cls); + } + + void* getIndexedIvars () + { + return bindings.object_getIndexedIvars(this); + } + + id getIvar (Ivar ivar) + { + return bindings.object_getIvar(this, ivar); + } + + void setIvar (Ivar ivar, Protocol* value) + { + return bindings.object_setIvar(this, ivar, value); + } + + bool conformsToProtocol (Protocol* other) + { + return bindings.protocol_conformsToProtocol(this, other); + } + + bool isEqual (id other) + { + return bindings.protocol_isEqual(this, other); + } + + objc_method_description getMethodDescription (SEL aSel, bool isRequiredMethod, bool isInstanceMethod) + { + return bindings.protocol_getMethodDescription(this, aSel, isRequiredMethod, isInstanceMethod); + } + + + + // message.h + + R msgSend (R = id, ARGS...) (SEL op, ARGS args) + { + return (cast(R function (id, SEL, ARGS))&bindings.objc_msgSend)(this, op, args); + } + + void msgSend_stret (T, ARGS...) (out T stretAddr, SEL op, ARGS args) + { + if (T.sizeof > STRUCT_SIZE_LIMIT) + (cast(void function (T*, id, SEL, ARGS...))&bindings.objc_msgSend_stret)(&stretAddr, this, op, args); + + else + stretAddr = (*cast(T function (id, SEL, ARGS...))&bindings.objc_msgSend)(this, op, args); + } + + static if (dstep.internal.Version.X86 || X86_64) + { + R msgSend_fpret (R = id, ARGS...) (SEL op, ARGS args) + { + version (X86_64) + static assert(!is(R == real), "dstep.objc.objc.objc_object.msgSend_fpret: Only real are legal return value for objc_msgSend_fpret"); + + else + static assert(!is(R == double) && !is(R == float), "dstep.objc.objc.objc_object.msgSend_fpret: Only double and float are legal return values for objc_msgSend_fpret"); + + return (cast(R function (id, SEL, ARGS...))&bindings.objc_msgSend_fpret)(this, op, args); + } + } + + R invoke (R = id, ARGS...) (Method m, ARGS args) + { + return (cast(R function (id, SEL, ARGS...))&bindings.method_invoke)(this, m, args); + } + + void invoke_stret (ARGS...) (Method m, ARGS args) + { + return (cast(R function (id, SEL, ARGS...))&bindings.method_invoke_stret)(this, m, args); + } + + R msgSendv (R = id, T) (SEL op, size_t arg_size, T arg_frame) + { + (cast(R function (id, SEL, size_t, T))&bindings.objc_msgSendv)(this, op, arg_size, arg_frame); + } + + void msgSendv_stret (R = id, T) (out T stretAddr, SEL op, size_t arg_size, T arg_frame) + { + if (R.sizeof > STRUCT_SIZE_LIMIT) + (cast(void function (R*, id, SEL, size_t, T))&bindings.objc_msgSendv_stret)(&stretAddr, this, op, arg_size, arg_frame); + + else + stretAddr = (*cast(R function (id, SEL, size_t, T))&bindings.objc_msgSendv)(this, op, arg_size, arg_frame); + } + + version (X86) + { + R msgSendv_fpret (R = id, T) (SEL op, uint arg_size, T arg_frame) + { + static assert(!is(R == double) && !is(R == float), "dstep.objc.objc.objc_object.msgSend_fpret: Only double and float are legal return values for objc_msgSendv_fpret"); + return (cast(R function (id, SEL, uint, T))&bindings.objc_msgSendv_fpret)(this, op, arg_size, arg_frame); + } + } +} + +struct objc_selector +{ + void* sel_id; + char* sel_types; + + + + // D Extensions: + + string getName () + { + return fromStringz(bindings.sel_getName(this)); + } + + bool isEqual (SEL rhs) + { + return bindings.sel_isEqual(this, rhs); + } + + bool isMapped () + { + return bindings.sel_isMapped(this); + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dstep/objc/runtime.d Thu Jun 18 22:00:13 2009 +0200 @@ -0,0 +1,896 @@ +/** + * Copyright: Copyright (c) 2009 Jacob Carlborg. + * Authors: Jacob Carlborg + * Version: Initial created: Feb 1, 2009 + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) + */ +module dstep.objc.runtime; + +import mambo.io; + +import dstep.internal.String; +import dstep.internal.Types; +import dstep.internal.Version; +import bindings = dstep.objc.bindings; +import dstep.objc.message; +import dstep.objc.objc; + +alias objc_method* Method; +alias objc_ivar* Ivar; +alias objc_category* Category; +alias void* objc_property; +alias objc_property* objc_property_t; +alias objc_object Protocol; +alias objc_symtab* Symtab; +alias objc_cache* Cache; + +const char _C_ID = '@'; +const char _C_CLASS = '#'; +const char _C_SEL = ':'; +const char _C_CHR = 'c'; +const char _C_UCHR = 'C'; +const char _C_SHT = 's'; +const char _C_USHT = 'S'; +const char _C_INT = 'i'; +const char _C_UINT = 'I'; +const char _C_LNG = 'l'; +const char _C_ULNG = 'L'; +const char _C_LNG_LNG = 'q'; +const char _C_ULNG_LNG = 'Q'; +const char _C_FLT = 'f'; +const char _C_DBL = 'd'; +const char _C_BFLD = 'b'; +const char _C_BOOL = 'B'; +const char _C_VOID = 'v'; +const char _C_UNDEF = '?'; +const char _C_PTR = '^'; +const char _C_CHARPTR = '*'; +const char _C_ATOM = '%'; +const char _C_ARY_B = '['; +const char _C_ARY_E = ']'; +const char _C_UNION_B = '('; +const char _C_UNION_E = ')'; +const char _C_STRUCT_B = '{'; +const char _C_STRUCT_E = '}'; +const char _C_VECTOR = '!'; +const char _C_CONST = 'r'; + +struct objc_class +{ + Class isa; + Class super_class; + /*const*/ char* name; + c_long version_; + c_long info; + c_long instance_size; + objc_ivar_list* ivars; + objc_method_list** methodLists; + objc_cache* cache; + objc_protocol_list* protocols; + + + + // D Extensions: + + void setFutureClass (string name) () + { + bindings.objc_setFutureClass(this, name.ptr); + } + + string getName () + { + return fromStringz(bindings.class_getName(this)); + } + + Ivar getInstanceVariable (string name) () + { + return bindings.class_getInstanceVariable(this, name.ptr); + } + + Ivar getClassVariable (string name) () + { + return bindings.class_getClassVariable(this, name.ptr); + } + + Ivar[] copyIvarList () + { + uint outCount; + + Ivar* list = bindings.class_copyIvarList(this, &outCount); + + if (list) + return list[0 .. outCount]; + + return null; + } + + Method[] copyMethodList () + { + uint outCount; + + Method* list = bindings.class_copyMethodList(this, &outCount); + + if (list) + return list[0 .. outCount]; + + return null; + } + + Protocol*[] copyProtocolList () + { + uint outCount; + + Protocol** list = bindings.class_copyProtocolList(this, &outCount); + + if (list) + return list[0 .. outCount]; + + return null; + } + + string getIvarLayout () + { + return fromStringz(bindings.class_getIvarLayout(this)); + } + + string getWeakIvarLayout () + { + return fromStringz(bindings.class_getWeakIvarLayout(this)); + } + + Class duplicateClass (string name) (size_t extraBytes) + { + return bindings.objc_duplicateClass(this, name.ptr, extraBytes); + } + + bool addMethod (string types) (SEL name, IMP imp) + { + return bindings.class_addMethod(this, name, imp, types.ptr); + } + + IMP replaceMethod (string types) (SEL name, IMP imp) + { + return bindings.class_replaceMethod(this, name, imp, types.ptr); + } + + bool addIvar (string name, string types) (size_t size, ubyte alignment) + { + return bindings.class_addIvar(this, name.ptr, size, alignment, types.ptr); + } + + void setIvarLayout (string layout) () + { + bindings.class_setIvarLayout(this, layout.ptr); + } + + void setWeakIvarLayout (string layout) () + { + bindings.class_setWeakIvarLayout(this, layout.ptr); + } + + objc_property_t* getProperty (string name) () + { + return bindings.class_getProperty(this, name.ptr); + } + + string getImageName () + { + return fromStringz(bindings.class_getImageName(this)); + } + + bool isMetaClass () + { + return bindings.class_isMetaClass(this); + } + + Class getSuperclass () + { + return bindings.class_getSuperclass(this); + } + + Class setSuperClass (Class newSuper) + { + return bindings.class_setSuperclass(this, newSuper); + } + + int getVersion () + { + return bindings.class_getVersion(this); + } + + void serVersion (int version_) + { + return bindings.class_setVersion(this, version_); + } + + size_t getInstanceSize () + { + return bindings.class_getInstanceSize(this); + } + + Method getInstanceMethod (SEL name) + { + return bindings.class_getInstanceMethod(this, name); + } + + Method getClassMethod (SEL name) + { + return bindings.class_getClassMethod(this, name); + } + + IMP getMethodImplementation (SEL name) + { + return bindings.class_getMethodImplementation(this, name); + } + + IMP getMethodImplementation_stret (SEL name) + { + return bindings.class_getMethodImplementation_stret(this, name); + } + + bool respondsToSelector (SEL sel) + { + return bindings.class_respondsToSelector(this, sel); + } + + bool conformsToProtocol (Protocol* protocol) + { + return bindings.class_conformsToProtocol(this, protocol); + } + + void addProtocol (Protocol* protocol) + { + return bindings.class_addProtocol(this, protocol); + } + + id createInstanceFromZone (size_t idxIvars, void* z) + { + return bindings.class_createInstanceFromZone(this, idxIvars, z); + } + + void addMethods (objc_method_list* methodList) + { + return.class_addMethods(this, methodList); + } + + void removeMethods (objc_method_list* methodList) + { + return bindings.class_removeMethods(this, methodList); + } + + Class poseAs (Class original) + { + return bindings.class_poseAs(this, original); + } + + bool respondsToMethod (SEL sel) + { + return bindings.class_respondsToMethod(this, sel); + } + + objc_method_list* class_nextMethodList (void* arg) + { + return bindings.class_nextMethodList(this, arg); + } +} + +struct objc_method_description +{ + SEL name; + char* types; +} + +struct objc_method_description_list +{ + int count; + objc_method_description[1] list; +} + +struct objc_protocol_list +{ + objc_protocol_list* next; + c_long count; + Protocol*[1] list; +} + +struct objc_category +{ + char* category_name; + char* class_name; + objc_method_list* instance_methods; + objc_method_list* class_methods; + objc_protocol_list* protocols; +} + +struct objc_ivar +{ + char* ivar_name; + char* ivar_type; + int ivar_offset; + + static if (D_LP64) + int space; + + + + // D Extensions: + + string getName () + { + return fromStringz(bindings.ivar_getName(this)); + } + + string getTypeEncoding () + { + return fromStringz(bindings.ivar_getTypeEncoding(this)); + } + + ptrdiff_t getOffset () + { + return bindings.ivar_getOffset(this); + } +} + +struct objc_ivar_list +{ + int ivar_count; + + static if (D_LP64) + int space; + + /* variable length structure */ + objc_ivar[1] ivar_list; +} + +struct objc_method +{ + SEL method_name; + char* method_types; + IMP method_imp; + + + + // D Extensions: + + /*uint getArgumentInfo (string[] type) (int arg, int* offset) + { + char*[] arr; + arr.length = type.length; + + foreach (i, str ; type) + arr[i] = str.ptr; + + return bindings.method_getArgumentInfo(this, arg, arr.ptr, offset); + }*/ + + string getTypeEncoding () + { + return fromStringz(bindings.method_getTypeEncoding(this)); + } + + string copyReturnType () + { + return fromStringz(bindings.method_copyReturnType(this)); + } + + string copyArgumentType (uint index) + { + return fromStringz(bindings.method_copyArgumentType(this, index)); + } + + void getReturnType (string dst, size_t dst_len) + { + bindings.method_getReturnType(this, dst.toStringz(), dst_len); + } + + void getArgumentType (uint index, string dst, size_t dst_len) + { + bindings.method_getArgumentType(this, index, dst.toStringz(), dst_len); + } + + SEL getName () + { + return bindings.method_getName(this); + } + + IMP getImplementation () + { + return bindings.method_getImplementation(this); + } + + uint getNumberOfArguments () + { + return bindings.method_getNumberOfArguments(this); + } + + objc_method_description* getDescription () + { + return bindings.method_getDescription(this); + } + + IMP setImplementation (IMP imp) + { + return bindings.method_setImplementation(this, imp); + } + + void exchangeImplementations (Method m2) + { + bindings.method_exchangeImplementations(this, m2); + } + + uint getSizeOfArguments () + { + return bindings.method_getSizeOfArguments(this); + } +} + +struct objc_method_list +{ + objc_method_list* obsolete; + + int method_count; + + static if (D_LP64) + int space; + + /* variable length structure */ + objc_method method_list[1]; +} + +struct objc_symtab +{ + c_ulong sel_ref_cnt; + SEL* refs; + ushort cls_def_cnt; + ushort cat_def_cnt; + void*[1] defs; +} + +struct objc_cache +{ + uint mask /* total = mask + 1 */; + uint occupied; + Method[1] buckets; +} + +struct objc_module +{ + c_ulong version_; + c_ulong size; + char* name; + Symtab symtab; +} + +string object_getClassName (id obj) +{ + return fromStringz(bindings.object_getClassName(obj)); +} + +Ivar object_setInstanceVariable (T) (id obj, string name, T value) +{ + return bindings.object_setInstanceVariable(obj, name.toStringz(), value); +} + +Ivar object_getInstanceVariable (T) (id obj, string name, out T outValue) +{ + return bindings.object_getInstanceVariable(obj, name.toStringz(), &outValue); +} + +id objc_getClass (string name) +{ + return bindings.objc_getClass(name.toStringz()); +} + +id objc_getMetaClass (string name) +{ + return bindings.objc_getMetaClass(name.toStringz()); +} + +id objc_lookUpClass (string name) +{ + return bindings.objc_lookUpClass(name.toStringz()); +} + +id objc_getRequiredClass (string name) +{ + return bindings.objc_getRequiredClass(name.toStringz()); +} + +Class objc_getFutureClass (string name) +{ + return bindings.objc_getFutureClass(name.toStringz()); +} + +void objc_setFutureClass (Class cls, string name) +{ + bindings.objc_setFutureClass(cls, name.toStringz()); +} + +id objc_getProtocol (string name) +{ + return bindings.objc_getProtocol(name.toStringz()); +} + +Protocol** objc_copyProtocolList (out uint outCount) +{ + return bindings.objc_copyProtocolList(&outCount); +} + +string class_getName (Class cls) +{ + return fromStringz(bindings.class_getName(cls)); +} + +Ivar class_getInstanceVariable (Class cls, string name) +{ + return bindings.class_getInstanceVariable(cls, name.toStringz()); +} + +Ivar class_getClassVariable (Class cls, string name) +{ + return bindings.class_getClassVariable(cls, name.toStringz()); +} + +Ivar* class_copyIvarList (Class cls, out uint outCount) +{ + return bindings.class_copyIvarList(cls, &outCount); +} + +Method* class_copyMethodList (Class cls, out uint outCount) +{ + return bindings.class_copyMethodList(cls, &outCount); +} + +Protocol** class_copyProtocolList (Class cls, out uint outCount) +{ + return bindings.class_copyProtocolList(cls, &outCount); +} + +objc_property_t* class_getProperty (Class cls, string name) +{ + return bindings.class_getProperty(cls, name.toStringz()); +} + +objc_property_t* class_copyPropertyList (Class cls, out uint outCount) +{ + return bindings.class_copyPropertyList(cls, &outCount); +} + +string class_getIvarLayout (Class cls) +{ + return fromStringz(bindings.class_getIvarLayout(cls)); +} + +string class_getWeakIvarLayout (Class cls) +{ + return fromStringz(bindings.class_getWeakIvarLayout(cls)); +} + +Class objc_allocateClassPair (Class superclass, string name, size_t extraBytes) +{ + return bindings.objc_allocateClassPair(superclass, name.toStringz(), extraBytes); +} + +Class objc_duplicateClass (Class original, string name, size_t extraBytes) +{ + return bindings.objc_duplicateClass(original, name.toStringz(), extraBytes); +} + +bool class_addMethod (Class cls, SEL name, IMP imp, string types) +{ + return bindings.class_addMethod(cls, name, imp, types.toStringz()); +} + +IMP class_replaceMethod (Class cls, SEL name, IMP imp, string types) +{ + return bindings.class_replaceMethod(cls, name, imp, types.toStringz()); +} + +bool class_addIvar (Class cls, string name, size_t size, ubyte alignment, string types) +{ + return bindings.class_addIvar(cls, name.toStringz(), size, alignment, types.toStringz()); +} + +void class_setIvarLayout (Class cls, string layout) +{ + bindings.class_setIvarLayout(cls, layout.toStringz()); +} + +void class_setWeakIvarLayout (Class cls, string layout) +{ + bindings.class_setWeakIvarLayout(cls, layout.toStringz()); +} + +string method_getTypeEncoding (Method m) +{ + return fromStringz(bindings.method_getTypeEncoding(m)); +} + +string method_copyReturnType (Method m) +{ + return fromStringz(bindings.method_copyReturnType(m)); +} + +string method_copyArgumentType (Method m, uint index) +{ + return fromStringz(bindings.method_copyArgumentType(m, index)); +} + +void method_getReturnType (Method m, string dst, size_t dst_len) +{ + bindings.method_getReturnType(m, dst.toStringz(), dst_len); +} + +void method_getArgumentType (Method m, uint index, string dst, size_t dst_len) +{ + bindings.method_getArgumentType(m, index, dst.toStringz(), dst_len); +} + +string ivar_getName (Ivar v) +{ + return fromStringz(bindings.ivar_getName(v)); +} + +string ivar_getTypeEncoding (Ivar v) +{ + return fromStringz(bindings.ivar_getTypeEncoding(v)); +} + +string property_getName (objc_property_t* property) +{ + return fromStringz(bindings.property_getName(property)); +} + +string property_getAttributes (objc_property_t* property) +{ + return fromStringz(bindings.property_getAttributes(property)); +} + +string protocol_getName (id p) +{ + return fromStringz(bindings.protocol_getName(p)); +} + +objc_method_description* protocol_copyMethodDescriptionList(id p, bool isRequiredMethod, bool isInstanceMethod, out uint outCount) +{ + return bindings.protocol_copyMethodDescriptionList(p, isRequiredMethod, isInstanceMethod, &outCount); +} + +objc_property_t* protocol_getProperty (id proto, string name, bool isRequiredProperty, bool isInstanceProperty) +{ + return bindings.protocol_getProperty(proto, name.toStringz(), isRequiredProperty, isInstanceProperty); +} + +objc_property_t* protocol_copyPropertyList (id proto, out uint outCount) +{ + return bindings.protocol_copyPropertyList(proto, &outCount); +} + +Protocol** protocol_copyProtocolList (id proto, out uint outCount) +{ + return bindings.protocol_copyProtocolList(proto, &outCount); +} + +static string[] objc_copyImageNames (out uint outCount) +{ + string[] ret; + + char** result = bindings.objc_copyImageNames(&outCount); + ret.length = outCount; + + for (size_t i = 0; i < outCount; i++) + ret[i] = fromStringz(result[i]); + + return ret; +} + +string class_getImageName (Class cls) +{ + return fromStringz(bindings.class_getImageName(cls)); +} + +string[] objc_copyClassNamesForImage (string image, out uint outCount) +{ + string[] ret; + + char** result = bindings.objc_copyClassNamesForImage(image.toStringz(), &outCount); + ret.length = outCount; + + for (size_t i = 0; i < outCount; i++) + ret[i] = fromStringz(result[i]); + + return ret; +} + +string sel_getName (SEL sel) +{ + return fromStringz(bindings.sel_getName(sel)); +} + +SEL sel_getUid (string str) +{ + return bindings.sel_getUid(str.toStringz()); +} + +SEL sel_registerName (string str) +{ + return bindings.sel_registerName(str.toStringz()); +} + +uint method_getArgumentInfo (objc_method* m, int arg, string[] type, int* offset) +{ + char*[] arr; + arr.length = type.length; + + foreach (i, str ; type) + arr[i] = str.toStringz(); + + return bindings.method_getArgumentInfo(m, arg, arr.ptr, offset); +} + +Class objc_getOrigClass (string name) +{ + return bindings.objc_getOrigClass(name.toStringz()); +} + +alias bindings.object_copy object_copy; +alias bindings.object_dispose object_dispose; +alias bindings.object_getClass object_getClass; +alias bindings.object_setClass object_setClass; +alias bindings.object_getIndexedIvars object_getIndexedIvars; +alias bindings.object_getIvar object_getIvar; +alias bindings.object_setIvar object_setIvar; +alias bindings.objc_getClassList objc_getClassList; +alias bindings.class_isMetaClass class_isMetaClass; +alias bindings.class_getSuperclass class_getSuperclass; +alias bindings.class_setSuperclass class_setSuperclass; +alias bindings.class_getVersion class_getVersion; +alias bindings.class_setVersion class_setVersion; +alias bindings.class_getInstanceSize class_getInstanceSize; +alias bindings.class_getInstanceMethod class_getInstanceMethod; +alias bindings.class_getClassMethod class_getClassMethod; +alias bindings.class_getMethodImplementation class_getMethodImplementation; +alias bindings.class_getMethodImplementation_stret class_getMethodImplementation_stret; +alias bindings.class_respondsToSelector class_respondsToSelector; +alias bindings.class_conformsToProtocol class_conformsToProtocol; +alias bindings.class_createInstance class_createInstance; +alias bindings.objc_registerClassPair objc_registerClassPair; +alias bindings.objc_disposeClassPair objc_disposeClassPair; +alias bindings.class_addProtocol class_addProtocol; +alias bindings.method_getName method_getName; +alias bindings.method_getImplementation method_getImplementation; +alias bindings.method_getNumberOfArguments method_getNumberOfArguments; +alias bindings.method_getDescription method_getDescription; +alias bindings.method_setImplementation method_setImplementation; +alias bindings.method_exchangeImplementations method_exchangeImplementations; +alias bindings.ivar_getOffset ivar_getOffset; +alias bindings.protocol_conformsToProtocol protocol_conformsToProtocol; +alias bindings.protocol_isEqual protocol_isEqual; +alias bindings.protocol_getMethodDescription protocol_getMethodDescription; +alias bindings.sel_isEqual sel_isEqual; +alias bindings.objc_enumerationMutation objc_enumerationMutation; +alias bindings.objc_setForwardHandler objc_setForwardHandler; +alias bindings.sel_isMapped sel_isMapped; +alias bindings.object_copyFromZone object_copyFromZone; +alias bindings.object_realloc object_realloc; +alias bindings.object_reallocFromZone object_reallocFromZone; +alias bindings.objc_getClasses objc_getClasses; +alias bindings.objc_addClass objc_addClass; +alias bindings.objc_setMultithreaded objc_setMultithreaded; +alias bindings.class_createInstanceFromZone class_createInstanceFromZone; +alias bindings.class_addMethods class_addMethods; +alias bindings.class_removeMethods class_removeMethods; +alias bindings.class_poseAs class_poseAs; +alias bindings.method_getSizeOfArguments method_getSizeOfArguments; +alias bindings.class_respondsToMethod class_respondsToMethod; +alias bindings.class_lookupMethod class_lookupMethod; +alias bindings.class_nextMethodList class_nextMethodList; + + + + + + + + +// D Extensions: + +struct objc +{ + static string[] copyImageNames (out uint outCount) + { + string[] ret; + + char** result = bindings.objc_copyImageNames(&outCount); + ret.length = outCount; + + for (size_t i = 0; i < outCount; i++) + ret[i] = fromStringz(result[i]); + + return ret; + } + + alias bindings.objc_enumerationMutation objc_enumerationMutation; + alias bindings.objc_setForwardHandler objc_setForwardHandler; + alias bindings.objc_setMultithreaded objc_setMultithreaded; + + static id getClass (string name) () + { + return bindings.objc_getClass(name.ptr); + } + + static id getMetaClass (string name) () + { + return bindings.objc_getMetaClass(name.ptr); + } + + static id lookUpClass (string name) () + { + return bindings.objc_lookUpClass(name.ptr); + } + + static id getRequiredClass (string name) () + { + return bindings.objc_getRequiredClass(name.ptr); + } + + static Class getFutureClass (string name) () + { + return bindings.objc_getFutureClass(name.ptr); + } + + static string[] objc_copyClassNamesForImage (string image) (out uint outCount) + { + string[] ret; + + char** result = bindings.objc_copyClassNamesForImage(image.ptr, &outCount); + ret.length = outCount; + + for (size_t i = 0; i < outCount; i++) + ret[i] = fromStringz(result[i]); + + return ret; + } + + static Class objc_getOrigClass (string name) () + { + return bindings.objc_getOrigClass(name.ptr); + } + + static Class allocateClassPair (string name) (Class superclass, size_t extraBytes) + { + return bindings.objc_allocateClassPair(superclass, name.ptr, extraBytes); + } + + alias bindings.objc_registerClassPair registerClassPair; + alias bindings.objc_disposeClassPair disposeClassPair; + alias bindings.objc_getClasses getClasses; + alias bindings.objc_addClass addClass; + alias bindings.class_createInstance createInstance; +} + +struct property +{ + static string property_getName (objc_property_t* property) + { + return fromStringz(bindings.property_getName(property)); + } + + static string property_getAttributes (objc_property_t* property) + { + return fromStringz(bindings.property_getAttributes(property)); + } +} + +struct sel +{ + static SEL getUid (string str) () + { + return bindings.sel_getUid(str.ptr); + } + + static SEL registerName (string str) () + { + return bindings.sel_registerName(str.ptr); + } +} \ No newline at end of file
--- a/sciprts/dgen.rb Sat Jan 31 17:22:44 2009 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1349 +0,0 @@ -#!/usr/bin/env ruby - -## -# Copyright:: Copyright (c) 2009 Jacob Carlborg. All rights reserved. -# Author:: Jacob Carlborg -# Version:: Initial created: 2009 -# License:: [revised BSD license]http://opensource.org/licenses/bsd-license.php or -# [Academic Free License v3.0]http://opensource.org/licenses/afl-3.0.php -# - -require "rubygems" -gem "xml-simple" -require "xmlsimple" -require "optparse" - -# Extensions to String -class String - - # Passes each character to the supplied block - # - # === Example: - # "str".each_char { |c| puts c } - # # outputs below - # s - # t - # r - # - def each_char - if block_given? - scan(/./m) do |x| - yield x - end - else - scan(/./m) - end - end - - # Indents the string - # levels:: how many levels/tabs to indent - # - # === Example: - # "str".indent #=> str - # "str".indent(3) #=> str - # - def indent (levels = 1) - "\t" * levels << self - end - - # Appends a semicolon and a newline character on the string - # semicolon:: should a semicolon be appended - # - # === Example: - # "str".nl - # "str".nl(false) - # # outputs below - # str; - # - # str - # - # - def nl (semicolon = true) - if semicolon - if self[-1, 1] == "{" || self[-1, 1] == "}" - self << "\n" - else - self << ";\n" - end - else - self << "\n" - end - end - - # Returns the index of the given character or length of the string if not found - # char:: the character to look for - # start:: the index where to start the search - # - # === Example: - # "012345789".index_of("1") #=> 1 - # "0123 0123".index_of("3", 6) #=> 8 - # - def index_of (char, start = 0) - return self.length if start >= self.length - - i = 0 - - if start == 0 - self.each_char do |c| - return i if char == c - i += 1 - end - else - self[start + 1 .. -1].each_char do |c| - return i + start + 1 if char == c - i += 1 - end - end - - return self.length - end - - # Returns true if the string is an Objective-C struct - # - # === Example: - # "{?=ii}".struct? #=> true - # - def struct? - self =~ /\{/ - end -end - -# Extensions that adds support for member access syntax -class Hash - def type - result = self["type"] - return result unless result.nil? - self[:type] - end - - def type= (type) - self[:type] = type - end - - def id - result = self["id"] - return result unless result.nil? - self[:id] - end - - def id= (id) - self[:id] = id - end - - def methods - result = self["methods"] - return result unless result.nil? - self[:methods] - end - - def methods= (methods) - self[:methods] = methods - end - - def method - result = self["method"] - return result unless result.nil? - self[:method] - end - - def method= (method) - self[:method] = method - end - - def method_missing (method, *args) - self.class.instance_eval do - define_method(method) do |*args| - if args.length > 0 - self[method[0 ... -1]] = args[0] - self[eval(":#{method}"[0 ... -1])] = args[0] - else - result = self[method] - return result unless result.nil? - self[eval(":#{method}")] - end - end - end - - if (method = method.id2name) =~ /=/ - eval("self.#{method} (args.length < 2 ? args[0] : args)") - else - eval("self.#{method}") - end - end -end - -# This Struct represents an Objective-C Framework -Framework = Struct.new(:name, :files) do - def initialize - self.files = [] - self.name = "" - end -end - -# This Struct represents a C/Objective-C header -HeaderFile = Struct.new(:name, :framework, :cftypes, :constants, :d_constants, :d_constants_static_this, :defines, - :enums, :functions, :function_wrappers, :imports, :path, :structs, :typedefs) do - def initialize - self.name = "" - self.cftypes = [] - self.constants = [] - self.defines = [] - self.enums = [] - self.framework = "" - self.functions = [] - self.function_wrappers = [] - self.imports = [] - self.path = "" - self.structs = [] - self.typedefs = [] - end -end - -# Performs the conversion of an xml metadata file to the D programming language -class ObjcToD - FIRST_YEAR = "2009" - VERSION = 1.0 - - attr_accessor :out_dir, :files - - # Creates a new instance of the ObjcToD class - def initialize - @classes = {} - @copyright = nil - @d_constants = [] - @d_constants_static_this = [] - @files = [] - @frameworks = [] - @function_wrappers = [] - @headers = [] - @package = "dstep" - end - - # Generates the D code from the xml metadata - def generate_code - @files.each do |dstep_file| - xml = XmlSimple.xml_in(dstep_file) - - unless xml.framework.nil? - frameworks = xml.framework - - frameworks.each do |frame| - framework = Framework.new - framework.name = frame.name - - frame.file.each do |file| - header = HeaderFile.new - header.name = file.name - header.constants = constants(file.constant) unless file.constant.nil? - header.d_constants = d_constants unless file.constant.nil? - header.d_constants_static_this = d_constants_static_this unless file.constant.nil? - header.enums = enums(file.enum) unless file.enum.nil? - header.functions = functions(file.function) unless file.function.nil? - header.function_wrappers = function_wrappers unless file.function.nil? - header.imports = imports(file.import, file.name, framework.name) unless file.import.nil? - header.structs = structs(file.struct) unless file.struct.nil? - - header.typedefs = typedefs(file.typedef) unless file.typedef.nil? - - framework.files << header - end - - @frameworks << framework - end - end - - unless xml.file.nil? - files = xml.file - - file.each do |file| - header = HeaderFile.new - header.name = file.name - header.constants = constants(file.constant) unless file.constant.nil? - header.d_constants = d_constants unless file.constant.nil? - header.d_constants_static_this = d_constants_static_this unless file.constant.nil? - header.enums = enums(file.enum) unless file.enum.nil? - header.functions = functions(file.function) unless file.function.nil? - header.function_wrappers = function_wrappers unless file.function.nil? - header.imports = imports(file.import, file.name) unless file.import.nil? - header.structs = structs(file.struct) unless file.struct.nil? - header.typedefs = typedefs(file.typedef) unless file.typedef.nil? - - @headers << header - end - end - - unless xml["class"].nil? - classes(xml["class"]) - end - end - end - - # Outputs the generate D code - def output_code - @frameworks.each do |framework| - framework_path = framework_path = "#{@out_dir}/#{@package}/#{framework.name}" - - FileUtils.mkdir_p(framework_path) unless File.exist?(framework_path) - - framework.files.each do |header| - file_path = "#{framework_path}/#{header.name}.d" - - File.open(file_path, "w") do |file| - file << copyright - file << "module #{@package}.#{framework.name}.#{header.name};" - file << header.imports.nl(false) - file << header.defines - file << header.typedefs - file << header.cftypes - file << header.constants - file << header.d_constants - file << header.enums - file << header.structs - - unless header.d_constants_static_this.nil? - file << "static this ()".nl(false) - file << "{".nl(false) - file << header.d_constants_static_this - file << "}".nl(false).nl(false) - end - - classes = get_classes(header.name) - - classes.each do |clazz, value| - file << value.code.nl(false) - @classes.delete(clazz) - end - - file << header.functions - file << header.function_wrappers - end - end - end - - package_path = "#{@out_dir}/#{@package}" - FileUtils.mkdir_p(package_path) unless File.exist?(package_path) - - @headers.each do |header| - header_path = "#{package_path}/#{header.name}.d" - - File.open(header_path, "w") do |file| - file << copyright - file << "module #{@package}.#{framework.name}.#{header.name};" - file << header.imports.nl(false) - file << header.defines - file << header.typedefs - file << header.cftypes - file << header.constants - file << header.d_constants - file << header.enums - file << header.structs - - unless header.d_constants_static_this.nil? - file << "static this ()".nl(false) - file << "{".nl(false) - file << header.d_constants_static_this - file << "}".nl(false).nl(false) - end - - classes = get_classes(header.name) - - classes.each do |clazz, value| - file << value.code.nl(false) - @classes.delete(clazz) - end - - file << header.functions - file << header.functions_wrappers - end - end - - @classes.each do |clazz, value| - class_path = "#{package_path}/#{clazz}.d" - - File.open(class_path, "w") do |file| - file << value.code unless value.nil? - end - end - end - - # Creates and returns the copyright header that is included in the top of every file - def copyright - return @copyright unless @copyright.nil? - - # Add an extra empty string in the begining because array indices begin with zero and months don't - months = ["", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] - initialt_year = "2009" - - str = StringIO.new - date = Date.today - current_year = date.year.to_s - year = initialt_year - initial_created = "#{months[date.month]} #{date.day}, #{initialt_year}" - - year << "-" << current_year unless initialt_year == current_year - - str << "/**\n" - str << " * Copyright: Copyright (c) #{year} Jacob Carlborg. All rights reserved.\n" - str << " * Authors: Jacob Carlborg\n" - str << " * Version: Initial created: #{initial_created} \n" - str << " * License: $(LINK2 http://opensource.org/licenses/bsd-license.php, revised BSD license) or <br />\n" - str << " * $(LINK2 http://opensource.org/licenses/afl-3.0.php, Academic Free License v3.0)\n" - str << " */\n" - - @copyright = str.string - end - - # Gets name of the framework that the given class belongs to - def get_framework (class_name) - @frameworks.each do |framework| - framework.files.each do |file| - return framework.name if file.name == class_name - end - end - - return [] - end - - # Gets the classes that belongs to the given file - def get_classes (name) - classes = @classes.find_all do |clazz, value| - value.file == name - end - - return classes - end - - # Generates the D code for the classes - def classes (classes) - classes.each do |clazz| - str = StringIO.new - - str << "class " - - if clazz == "" - str << clazz.name.nl(false) - else - str << clazz.name - str << " : " - str << clazz.parent.nl(false) - end - - str << "{".nl(false) - str << methods(clazz.method, clazz.name) - str << "}".nl(false) - - @classes[clazz.name] ||= {} - @classes[clazz.name].code = str.string - framework = get_framework(clazz.file) - @classes[clazz.name].framework = framework unless framework.nil? - @classes[clazz.name].file = clazz.file - end - end - - # Generates the D code for the constants/globals - def constants (constants) - str = StringIO.new - - str << "extern (C)".nl(false) - str << "{".nl(false) - str << "extern".indent.nl(false) - str << "{".indent.nl(false) - - constants.each do |constant| - type = get_type(constant.type, constant["type64"], constant["declaredType"]) - const = constant["const"] == "true" - - if constant.type == "@" - @d_constants << { :name => constant.name.dup, :type => constant.declaredType.gsub("*", ""), :const => const } - - if const - str << "const id".indent(2) - else - str << "id".indent(2) - end - - str << " " - str << constant["name"] - str << "_".nl - else - if const - str << "const ".indent(2) - str << type - else - str << "id".indent(2) - end - - str << " " - str << constant["name"].nl - end - end - - str << "}".indent.nl(false) - str << "}".nl(false).nl(false) - - str.string - end - - # Generates the D code for the constants that D can't handle directly, like classes - def d_constants - str = StringIO.new - - @d_constants.each do |constant| - # Deep copy constant - c = constant.dup - c.name = c.name.dup - c.type = c.type.dup - - str << "const " if constant.const - str << constant.type - str << " " - str << constant.name.nl - @d_constants_static_this << c - end - - str << "\n" - @d_constants.clear - str.string - end - - # Generates the D code for the constants the has to be in a "static this" - def d_constants_static_this - str = StringIO.new - - @d_constants_static_this.each do |constant| - str << constant.name.indent - str << " = new " - str << constant.type - str << "(" - str << constant.name - str << "_)".nl - end - - @d_constants_static_this.clear - str.string - end - - # Generates the D code for the enums - def enums (enums) - str = StringIO.new - - enums.each do |enum| - str << "enum" - - if enum["name"] == "" - str << "\n{".nl - - enum.member.each_with_index do |member, i| - str << member["name"].indent - str << " = " - str << member.value - str << ",".nl(false) unless i == enum.member.length - 1 - end - else - str << enum["name"].nl - str << "{" - - enum["member"].each_with_index do |member, i| - str << member["name"].indent - str << " = " - str << member.value - str << ",".nl(false) unless i == enum.member.length - 1 - end - end - - str << "\n}".nl.nl(false) - end - - str.string - end - - # Generates the D code for the function/method args - def args (args, variadic, method = false) - return "" if args.nil? - - str = StringIO.new - if variadic - #p args - end - args.each do |arg| - - if method || arg.type != "@" - type = get_type(arg.type, arg.type64, arg.declaredType) - else - type = "id" - end - - str << type - str << " " - str << arg["name"] - str << ", " - end - - if variadic - else - end - - str << "..." if variadic - - return str.string if variadic - return str.string[0 ... -2] unless variadic - end - - # A helper function that generates the D code for the functions/methods - def build_function (function, method = false, static = false) - str = StringIO.new - variadic = function["variadic"] == "true" - return_type = "" - args = "" - original_type = function.returnValue[0].type - - if !method && original_type == "@" - return_type = "id" - else - return_type = get_type(original_type, function.returnValue[0].type64, function.returnValue[0].declaredType) - args(function.arg, variadic) unless function.arg.nil? - end - - str << "static " if static - str << return_type - str << " " - str << function["name"] - str << " (" - str << args(function["arg"], variadic, method) - str << ")".nl - - str.string - end - - # Generates the D code for the functions - def functions (functions) - str = StringIO.new - - str << "extern (C)".nl(false) - str << "{".nl(false) - - functions.each do |function| - wrapper_needed = false - original_type = function["returnValue"][0].type - - if original_type == "@" - @function_wrappers << function - wrapper_needed = true - else - function["arg"].each do |arg| - if (arg.type || arg["type64"]) == "@" - @function_wrappers << function - wrapper_needed = true - end - end unless function["arg"].nil? - end - - return_type = get_type(function["returnValue"][0].type, function["returnValue"][0]["type64"], function["returnValue"][0]["declaredType"]) - variadic = function["variadic"] == "true" - - str << functions_helper(function, wrapper_needed) - end - - str << "}".nl(false).nl(false) - str.string - end - - # A helper function that generates the D code for the functions - def functions_helper (function, wrapper_needed) - str = StringIO.new - - if wrapper_needed - declaration = build_function(function) - index = declaration.index_of(" ") - index = declaration.index_of(" ", index) - str << (declaration[0 ... index] + "_" + declaration[index .. -1]).indent - else - str << build_function(function).indent - end - - str.string - end - - # Generates the D code for the functions that D can't handle without wrappers, like functions that takes - # objects as arguments or returns an object - def function_wrappers - str = StringIO.new - - @function_wrappers.each do |function| - original_type = function["returnValue"][0].type - variadic = function["variadic"] == "true" - args = StringIO.new - out_args = [] - return_type = get_type(original_type, function.returnValue[0].type64, function.returnValue[0].declaredType) - - function["arg"].each do |arg| - args << arg["name"] - - if arg.type == "@" - args << " !is null ? " - args << arg["name"] - args << ".id : null" - elsif arg.type == "^@" - type = get_type(arg.type, arg["type64"], arg["declaredType"]) - out_args << { :type => type[4 .. -1], :name => arg["name"] } - - args << " &" - args << arg["name"] - args << "_.id" - end - - args << ", " - end unless function["arg"].nil? - - str << build_function(function, true)[0 ... -2].nl(false) - str << "{".nl(false) - - out_args.each do |arg| - str << arg.type.indent - str << " " - str << arg.name - str << "_ = new " - str << arg.type - str << "(false, false)".nl - end - - str << "\n" if out_args.length > 0 - - if original_type == "@" - str << "id result = ".indent - str << function["name"] - elsif original_type != "v" - if out_args.length > 0 - str << return_type.indent - str << " result = " - else - str << "return ".indent - end - end - - if original_type == "v" - str << function["name"].indent - elsif original_type != "@" - str << "cast(" - str << return_type - str << ") " - str << function["name"] - end - - str << "_" - str << "(" - str << args.string[0 ... -2] unless function["arg"].nil? - str << ")".nl - str << "\n" if out_args.length > 0 - - out_args.each do |arg| - str << arg.name.indent(2) - str << " = " - str << arg.name - str << "_".nl - end - - if out_args.length > 0 - str << "\n" - str << "return result".indent unless original_type == "v" - end - - if original_type == "@" - str << "return result !is null ? new ".indent - str << return_type - str << "(result)".nl - elsif original_type != "v" && out_args.length > 0 - str << "".nl - end - - str << "}".nl(false).nl(false) - end - - @function_wrappers.clear - str.string[0 ... -2] - end - - # Generates the D code for the imports - def imports (imports, filename, framework_name = nil) - str = StringIO.new - - imports.each do |import| - str << "import #{@package}." - str << import.gsub("/", ".").gsub("Frameworks", "").gsub(".framework", "").gsub("Headers", "").gsub(/\.{2,}/, ".").nl - end - - str << "\n\n" - str = str.string.sort.to_s - - return "\n\npublic:" << str if filename == framework_name - return str - end - - # Generates the D code for the methods - def methods (methods, class_name) - str = StringIO.new - - str << "this (bool init = true, bool alloc = true)".indent.nl(false) - str << "{".indent.nl(false) - str << "if (alloc && init)".indent(2).nl(false) - str << "{".indent(2).nl(false) - str << "id result = objc_msgSend(class_#{class_name}, sel_alloc)".indent(3).nl - str << "id result2".indent(3).nl.nl(false) - str << "if (result)".indent(3).nl(false) - str << "result2 = objc_msgSend(result, sel_init)".indent(4).nl.nl(false) - str << "if (result2)".indent(3).nl(false) - str << "this.id = result2".indent(4).nl.nl(false) - str << "else".indent(3).nl(false) - str << "this.id = result".indent(4).nl - str << "}".indent(2).nl(false).nl(false) - str << "else if (alloc)".indent(2).nl(false) - str << "this.id = objc_msgSend(this.id, sel_alloc)".indent(3).nl - str << "}".indent.nl(false).nl(false) - - str << "this (id object)".indent.nl(false) - str << "{".indent.nl(false) - str << "this.id = object".indent(2).nl - str << "}".indent.nl(false).nl(false) - - str << "this (id object)".indent.nl(false) - str << "{".indent.nl(false) - str << "this.id = object.id".indent(2).nl - str << "}".indent.nl(false).nl(false) - - str << "static #{class_name} alloc ()".indent.nl(false) - str << "{".indent.nl(false) - str << "id result = objc_msgSend(class_#{class_name}, sel_alloc)".indent(2).nl - str << "return result !is null ? new #{class_name}(result) : null".indent(2).nl - str << "}".indent.nl(false).nl(false) - - str << "#{class_name} init ()".indent.nl(false) - str << "{".indent.nl(false) - str << "id result = objc_msgSend(this.id, sel_init)".indent(2).nl - str << "return result is this.id ? this : (result !is null ? new #{class_name}(result) : null)".indent(2).nl - str << "}".indent.nl(false).nl(false) - - methods.each do |method| - next if method.selector == ("alloc" || "init") && method.static == "true" - - return_type = get_type(method["returnValue"][0].type, method["returnValue"][0]["type64"], method["returnValue"][0]["declaredType"]) - variadic = method["variadic"] == "true" - static = method["static"] == "true" - this = "this.id" unless static - this = "class_#{class_name}" if static - args = "" - out_args = [] - original_type = method.returnValue[0].type - - if method.selector[0 ... 4] == "init" && return_type == "id" - return_type = class_name - method.returnValue[0].declaredType = class_name - end - - method["arg"].each do |arg| - args << " &" if arg.type == "^@" - args << arg["name"] - - if arg.type == "@" - args << " !is null ? " - args << arg["name"] - args << ".id : null" - elsif arg.type == "^@" - type = get_type(arg.type, arg.type, arg["declaredType"]) - out_args << { :type => type[4 .. -1], :name => arg["name"] } - args << "_.id" - end - - args << ", " - end unless method["arg"].nil? - - declaration = build_function(method, true, static)[0 ... -2].indent.nl(false) - index = declaration.index_of(" ") - - str << (declaration[0 .. index] + get_method_name(method["selector"]) + declaration[index .. -1]).gsub(/ +/, " ") - str << "{".indent.nl(false) - - out_args.each do |arg| - str << arg.type.indent(2) - str << " " - str << arg.name - str << "_ = new " - str << arg.type - str << "(false, false)".nl - end - - str << "\n" if out_args.length > 0 - - - if method["returnValue"][0].type == "@" - str << "id result = objc_msgSend(#{this}, sel_".indent(2) - str << transform_selector(method["selector"]) - str << ", " unless method.arg.nil? - str << args[0 ... -2] unless method.arg.nil? - str << ")".nl - str << "\n" if out_args.length > 0 - - out_args.each do |arg| - str << arg.name.indent(2) - str << " = " - str << arg.name - str << "_".nl - end - - str << "\n" if out_args.length > 0 - - if method["returnValue"][0]["declaredType"] == class_name - str << "return result is this.id ? this : (return result ".indent(2) - else - str << "return result ".indent(2) - end - - str << "!is null ? new " - str << return_type - str << "(result) : null" - - if method["returnValue"][0]["declaredType"] == class_name - str << ")".nl - else - str << "".nl - end - - str << "}".indent.nl(false).nl(false) - else - if original_type == "d" || original_type == "f" - - str << "version (X86)".indent(2).nl(false) - - if out_args.length > 0 - str << return_type.indent(3) - str << " result " - else - if original_type == "d" - str << "return ".indent(3) - else - str << "return cast(".indent(3) - str << return_type - str << ") " - end - end - - str << "objc_msgSend_fpret(#{this}, sel_" - str << transform_selector(method["selector"]) - str << ", " unless method.arg.nil? - - args = "" - - method["arg"].each do |arg| - args << arg["name"] - args << ", " - end unless method["arg"].nil? - - str << args[0 ... -2] if args.length > 0 - str << ")".nl.nl(false) - - str << "else".indent(2).nl(false) - - if out_args.length > 0 - str << return_type.indent(3) - str << " result " - else - if original_type == "d" - str << "return ".indent(3) - else - str << "return cast(".indent(3) - str << return_type - str << ") " - end - end - - str << "objc_msgSend(#{this}, sel_" - str << transform_selector(method["selector"]) - str << ", " unless method.arg.nil? - - args = "" - - method["arg"].each do |arg| - args << arg["name"] - args << ", " - end unless method["arg"].nil? - - str << args[0 ... -2] if args.length > 0 - str << ")".nl - str << "\n" if out_args.length > 0 - - out_args.each do |arg| - str << arg.name.indent(3) - str << " = " - str << arg.name - str << "_".nl - end - - if out_args.length > 0 - str << "\n" - str << "retrun result".indent(3).nl - end - - str << "\n" if out_args.length > 0 - str << "}".indent.nl(false).nl(false) - else - unless return_type == "void" - if out_args.length > 0 - str << return_type.indent(2) - str << " result " - else - str << "return cast(".indent(2) - end - - str << return_type - str << ") " - end - - str << "objc_msgSend(#{this}, sel_".indent(2) if return_type == "void" - str << "objc_msgSend(#{this}, sel_" unless return_type == "void" - str << transform_selector(method["selector"]) - str << ", " unless method.arg.nil? - - args = "" - - method["arg"].each do |arg| - args << arg["name"] - args << ", " - end unless method["arg"].nil? - - str << args[0 ... -2] if args.length > 0 - str << ")".nl - - str << "\n" if out_args.length > 0 - - out_args.each do |arg| - str << arg.name.indent(2) - str << " = " - str << arg.name - str << "_".nl - end - - if out_args.length > 0 - str << "\n" - str << "retrun result".indent(2).nl - end - - str << "}".indent.nl(false).nl(false) - end - end - end - - str.string[0 .. -2] - end - - # Generates the D code for the structs - def structs (structs) - str = StringIO.new - - structs.each do |struct| - str << "struct " - str << struct.name.nl(false) - str << "{".nl - - struct.member.each do |member| - type = get_type(member.type, member.type64, member.declaredType) - - str << type.indent - str << " " - str << member.name.nl - end unless struct.member.nil? - - str << "}".nl - str << "\n\n" - end - - str.string[0 .. -2] - end - - # Generates the D code for the typedefs - def typedefs (typedefs) - str = StringIO.new - - typedefs.each do |typedef| - - type = "" - struct = false - - if typedef.type.struct? - type = get_struct_type(typedef.type, typedef["type64"], typedef["declaredType"]) - struct = true - else - type = get_type(typedef.type, typedef.type64, typedef.declaredType) - end - - # Special case - type = "wchar" if typedef.name == "unichar" - - if struct - str << typedef["declaredType"].gsub("*", "").nl - str << "alias " - str << type[:name] - str << " " - str << typedef["name"].nl - else - str << "alias " - str << type - str << " " - str << typedef.name.nl - end - end - - str << "\n" - str.string - end - - # Adds specific D extensions to classes, like opIndex and opApply - def d_class_extensions (args) - - end - - # Checks if the declared type should be used instead of the type - # type:: the type to check - # - def check_declared_type (type) - case type - when "unichar"; return "wchar" - when "BOOL"; return "bool" - when "CGFloat"; return type - when "NSInteger"; return type - when "NSUInteger"; return type - - when "unichar*"; return "wchar*" - when "BOOL*"; return "bool*" - when "CGFloat*"; return type - when "NSInteger*"; return type - when "NSUInteger*"; return type - - when "unichar**"; return "wchar**" - when "BOOL**"; return "bool**" - when "CGFloat**"; return type - when "NSInteger**"; return type - when "NSUInteger**"; return type - else return nil; - end - end - - # Gets the method name from the supplied selector - # selector:: the selector to get the method name from - # - # === Example: - # get_method_name("initWithContentsOfURL:options:error:") #=> initWithContentsOfURL - # - def get_method_name (selector) - i = selector.index_of(":") - selector[0 ... i] - end - - # Gets the D type from the encoded C/Objective-C type - # type:: the type - # type64:: the type for 64bit targets - # declared_type:: the declared type - # - # === Example: - # get_type("I", "Q", "NSUInteger") #=> NSUInteger - # get_type("I", "I", "unsigned int") #=> uint - # - def get_type (type, type64, declared_type) - - t = check_declared_type(declared_type) - return t unless t.nil? - - unless type64 == "" || type64.nil? - return declared_type if type != type64 - end - - case type - when "c"; return "byte" - when "i"; return "int" - when "s"; return "short" - when "l"; return "int" - when "q"; return "long" - when "C"; return "ubyte" - when "I"; return "uint" - when "S"; return "ushort" - when "L"; return "uint" - when "Q"; return "ulong" - when "f"; return "float" - when "d"; return "double" - when "B"; return "bool" - when "v"; return "void" - when "*"; return "char*" - when '#'; return "Class" - when ":"; return "SEL" - when "@" - return declared_type.gsub(/\*+/, "") unless declared_type.nil? - raise "No declared type given" - else - case type[0, 1] - when "[" - str = "" - t = type[1 ... -1] - count = $1 if t =~ /(\d+)/ - str = get_type($', declared_type) - str << "[#{count}]" - - return str - when "(" - resolved_types = [] - types = $2 if type =~ /\((.+)=(.+)\)/ - - types.each_char do |t| - resolved_types << get_type(t, declared_type) - end - - return resolved_types - when "^" - if type == "^@" - return "out " << get_type(type[1 .. -1], type64[1 .. -1], declared_type).dup - elsif type.length > 2 && type[0 ... 2] == "^^" - return "out " << get_type(type[2 .. -1], type64[2 .. -1], declared_type).dup - elsif type == "^?" - tmp = cfp_to_dfp(type) - return tmp unless tmp.nil? - end - - if !type.nil? && !type64.nil? - return get_type(type[1 .. -1], type64[1 .. -1], declared_type).dup << "*" - elsif !type.nil? - return get_type(type[1 .. -1], type64, declared_type).dup << "*" - end - when "{" - return declared_type - end - end - - return declared_type - end - - # Gets the D type from the encoded C/Objective-C type when it's a struct - # type:: the type - # type64:: the type for 64bit targets - # declared_type:: the declared type - # - # === Example - # get_struct_type("{some=III}", "{some=III}", "struct some") - # # outputs below - # { :name => "some", :types => ["uint", "uint", "uint"] } - # - def get_struct_type (type, type64, declared_type) - - return { :name => declared_type } if declared_type[0 ... 2] == "NS" - - case type[0, 1] - when "{" - resolved_types = [] - - name = $1 if type =~ /\^{0,}\{(.{0,})=(.{0,})\}/ - types = $2 - - unless types.nil? - types.each_char do |t| - resolved_types << get_type(t, type64, declared_type) - end - end - - return { :name => name, :types => resolved_types } - when "^" - get_struct_type(type[1 .. -1], type64, declared_type) - hash = get_struct_type(type[1 .. -1], type64, declared_type) - hash[:name].dup << "*" - return hash - end - end - - # C Function Pointer to D Function Pointer - def cfp_to_dfp (fp) - reg = /(\w+)\s*\(\*(\w*)\)\s*\((.*)\)/ - - return nil if fp !~ reg - - return_type = $1 - name = $2 - arg_types = $3 - - return "#{return_type} function (#{arg_types})" - end - - # Is the supplied argument an "out" argument - # arg:: the arg - # - # === Example: - # out_arg?("out NSError") #=> true - # - def out_arg? (arg) - return arg[0 .. 4] == "out " - end - - # Transform the supplied selector to a valid D representation - # selector:: the selector to transform - # - # === Example: - # transform_selector("initWithContentsOfURL:options:error:") - # # outputs below - # initWithContentsOfURL_options_error_ - # - def transform_selector (selector) - selector.gsub(/:/, "_") - end -end - -# Prints the message to stderr, exits -def die (*msg) - $stderr.puts msg - exit 1 -end - -if __FILE__ == $0 - objc_to_d = ObjcToD.new - - OptionParser.new do |opts| - opts.banner = "Usage: #{File.basename(__FILE__)} [options] <dstep files...>" - opts.separator "" - opts.separator "Options:" - - opts.on("-o", "--output DIRECTORY", "Place the output files in this directory'") do |opt| - die "The specified directory \"#{opt}\" does not exists" if File.exist?(opt) == false - die "Output directory cannot be specified more than once" if objc_to_d.out_dir - objc_to_d.out_dir = opt - end - - help_msg = "Use the `-h' flag or for help." - - opts.on("-h", "--help", "Show this message.") do - puts opts, help_msg - exit - end - - opts.on('-v', '--version', 'Show version.') do - puts ObjcToD::VERSION - exit - end - - opts.separator "" - - if ARGV.empty? - die opts.banner - else - begin - opts.parse!(ARGV) - - die "No output directory given" if objc_to_d.out_dir.nil? - - ARGV.each do |file| - objc_to_d.files << file - end - - objc_to_d.generate_code - objc_to_d.output_code - rescue => e - msg = e.message - msg = "Internal error" if msg.empty? - - die msg, opts.banner, help_msg - end - end - end -end \ No newline at end of file
--- a/sciprts/dstepgen.rb Sat Jan 31 17:22:44 2009 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1388 +0,0 @@ -#!/usr/bin/env ruby - -# Copyright (c) 2008-2009, Jacob Carlborg. All rights reserved. -# Copyright (c) 2006-2007, Apple Inc. All rights reserved. -# Copyright (c) 2005-2006 FUJIMOTO Hisakuni -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 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. Neither the name of Apple Inc. ("Apple") nor the names of -# its contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS CONTRIBUTORS 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. - -require "rubygems" -gem "builder", "~> 2.0" -require "builder" -require "tmpdir" -require "optparse" -include Builder - -$KCODE = "UTF8" - -# Extensions that adds support for member access syntax -class Hash - def type - result = self["type"] - return result unless result.nil? - self[:type] - end - - def type= (type) - self[:type] = type - end - - def id - result = self["id"] - return result unless result.nil? - self[:id] - end - - def id= (id) - self[:id] = id - end - - def methods - result = self["methods"] - return result unless result.nil? - self[:methods] - end - - def methods= (methods) - self[:methods] = methods - end - - def method_missing (method, *args) - self.class.instance_eval do - define_method(method) do |*args| - if args.length > 0 - self[method[0 ... -1]] = args[0] - self[eval(":#{method}"[0 ... -1])] = args[0] - else - result = self[method] - return result unless result.nil? - self[eval(":#{method}")] - end - end - end - - if (method = method.id2name) =~ /=/ - eval("self.#{method} (args.length < 2 ? args[0] : args)") - else - eval("self.#{method}") - end - end -end - -# This Struct represents an Objective-C Framework -Framework = Struct.new(:name, :headers) do - def initialize - self.headers = [] - self.name = "" - end -end - -# This Struct represents a C/Objective-C header -HeaderFile = Struct.new(:name, :framework, :cftypes, :constants, :defines, :enums, :externs, :functions, - :imports, :inline_functions, :opaques, :path, :structs, :typedefs) do - def initialize - self.name = "" - self.cftypes = [] - self.constants = [] - self.defines = [] - self.enums = [] - self.externs = [] - self.framework = "" - self.functions = [] - self.imports = [] - self.inline_functions = [] - self.opaques = [] - self.path = "" - self.structs = [] - self.typedefs = [] - end -end - -# This class scans the headers -class HeaderScaner - CPP = ['/usr/bin/cpp-4.0', '/usr/bin/cpp-3.3', '/usr/bin/cpp3'].find { |x| File.exist?(x) } - raise "cpp not found" if CPP.nil? - CPPFLAGS = "-D__APPLE_CPP__ -include /usr/include/AvailabilityMacros.h" - CPPFLAGS << "-D__GNUC__" unless /\Acpp-4/.match(File.basename(CPP)) - - attr_accessor :frameworks, :headers, :do_64bit - - def initialize - @extern_name = 'extern' - @frameworks = [] - @file_content = nil - @headers = [] - @classes = {} - @informal_protocols = {} - @function_pointer_types = {} - @do_64bit = false - end - - def classes - @classes - end - - def protocols - @informal_protocols - end - - def cftypes (header) - re = /typedef\s+(const\s+)?(struct\s*\w+\s*\*\s*)([^\s]+Ref)\s*;/ - @cpp_result.scan(re).each do |m| - header.cftypes << { :name => m[2], :type => m[1], :const => m[0] =~ /const/ ? true : false} - end - end - - def constants (header) - tmp = header.externs.map do |i| - constant?(i, true) - end - - header.constants = tmp.flatten.compact - end - - def defines (header) - re = /#define\s+([^\s]+)\s+(\([^)]+\)|[^\s]+)\s*$/ - @file_content.scan(re).each do |m| - next unless !m[0].include?('(') && m[1] != '\\' - header.defines << { :name => m[0], :value => m[1] } - end - end - - def enums (header) - re = /\benum\b\s*(\w+\s+)?\{([^}]*)\}/ - @cpp_result.scan(re).each do |m| - enum = { :name => m[0], :members => [] } - - m[1].split(",").map do |i| - name, value = i.split("=", 2).map do |x| - x.strip - end - - enum[:members] << { :name => name, :value => value } unless name.empty? || name[0] == ?# - end - - header.enums << enum - end - end - - def function_pointer_types (header) - re = /typedef\s+([\w\s]+)\s*\(\s*\*\s*(\w+)\s*\)\s*\(([^)]*)\)\s*;/ - data = @cpp_result.scan(re) - re = /typedef\s+([\w\s]+)\s*\(([^)]+)\)\s*;/ - data |= @cpp_result.scan(re).map do |m| - ary = m[0].split(/(\w+)$/) - ary[1] << " *" - ary << m[1] - ary - end - - data.each do |m| - name = m[1] - args = m[2].split(",").map do |x| - if x.include?(" ") - ptr = x.sub!(/\[\]\s*$/, "") - x = x.sub(/\w+\s*$/, "").strip - ptr ? x + "*" : x - else - x.strip - end - end - - type = "#{m[0]}(*)(#{args.join(', ')})" - @function_pointer_types[name] = type - end - end - - def typedefs (header) - re = /^\s*typedef\s+(.+)\s+([\w\*]+)\s*;$/ - data = @cpp_result - data.scan(re).each do |m| - var = get_var_info(m[0] + " " + m[1]) - header.typedefs << get_var_info(m[0] + " " + m[1]) if var - end - end - - def externs (header) - re = /^\s*#{@extern_name}\s+\b(.*)\s*;.*$/ - header.externs = @cpp_result.scan(re).map do |m| - m[0].strip - end - end - - def imports (header) - tmp = [] - - @file_content.each do |line| - if line =~ /#(include|import) <(.+)\.h>/ - next if $2 == header.name - tmp << $2 - elsif line =~ /@class(\s+)(\w+)(,|;)/ - next if $2 == header.name - - if $3 == ";" - tmp << header.framework + "/" + $2 unless header.framework == "" - tmp << $2 if header.framework == "" - elsif - str = line[6 + $1.length ... -2] - str.gsub!(" ", "") - arr = str.split(",") - - arr.each do |s| - tmp << header.framework + "/" + s unless header.framework == "" - tmp << s if header.framework == "" - end - end - end - end - - header.imports = tmp.compact.uniq - end - - def informal_protocols (header) - self.methods(header) - end - - def methods (header) - interface_re = /^@(interface|protocol)\s+(\w+)\s*:?\s*(\w*)\s*(\([^)]+\))?/ - - end_re = /^@end/ - body_re = /^[-+]\s*(\([^)]+\))?\s*([^:\s;]+)/ - args_re = /\w+\s*:/ - prop_re = /^@property\s*(\([^)]+\))?\s*([^;]+);$/ - current_interface = current_category = nil - i = 0 - parent = nil - - @cpp_result.each_line do |line| - size = line.size - line.strip! - - if md = interface_re.match(line) - parent = nil - current_interface = md[1] == "protocol" ? "NSObject" : md[2] - current_category = md[4].delete("()").strip if md[4] - parent = md[3] unless md[3] == "" - - elsif end_re.match(line) - current_interface = current_category = nil - - elsif current_interface && md = prop_re.match(line) - # Parsing Objective-C 2.0 properties - if (a = md[2].split(/\s/)).length >= 2 && /^\w+$/.match(name = a[-1]) && (type = a[0 .. -2].join(" ")).index(",").nil? - getter, setter = name, "set#{name[0].chr.upcase + name[1 .. -1]}" - readonly = false - - if attributes = md[1] - if md = /getter\s*=\s*(\w+)/.match(attributes) - getter = md[1] - end - - if md = /setter\s*=\s*(\w+)/.match(attributes) - setter = md[1] - end - - readonly = true if attributes.index("readonly") - end - - typeinfo = VarInfo.new(type, "", "") - - @classes[current_interface] ||= {} - methods = (@classes[current_interface].methods ||= []) - methods << MethodInfo.new(typeinfo, getter, false, [], line) - - unless readonly - methods << MethodInfo.new(VarInfo.new("void", "", ""), setter + ":", false, [typeinfo], line) - end - end - - elsif current_interface && (line[0] == ?+ || line[0] == ?-) - mtype = line[0] - data = @cpp_result[i .. -1] - body_md = body_re.match(data) - - next if body_md.nil? - - rettype = body_md[1] ? body_md[1].delete("()") : "id" - retval = VarInfo.new(rettype, "", "") - args = [] - selector = "" - data = data[0 .. data.index(";")] - args_data = [] - - data.scan(args_re) do |x| - args_data << [$`, x, $'] - end - - variadic = false - args_data.each_with_index do |ary, n| - before, argname, argtype = ary - arg_nameless = (n > 0 && /\)\s*$/.match(before)) - argname = ":" if arg_nameless - realargname = nil - - if n < args_data.length - 1 - argtype.sub!(args_data[n + 1][2], "") - - if arg_nameless - argtype.sub!(/(\w+\s*)?\w+\s*:\s*$/, "") - else - unless argtype.sub!(/(\w+)\s+\w+:\s*$/) { |s| realargname = $1; "" } - # maybe the next argument is nameless - argtype.sub!(/\w+\s*:\s*$/, "") - end - end - else - argtype.sub!(/\s+__attribute__\(\(.+\)\)/, "") - - if arg_nameless - argtype.sub!(/\w+\s*;$/, "") - else - unless argtype.sub!(/(\w+)\s*;$/) { |s| realargname = $1; "" } - variadic = argtype.sub!(/,\s*\.\.\.\s*;/, "") != nil - argtype.sub!(/\w+\s*$/, "") if variadic - end - end - end - - selector << argname - realargname ||= argname.sub(/:/, "") - args << VarInfo.new(argtype, realargname, "") unless argtype.empty? - end - - selector = body_md[2] if selector.empty? - args << VarInfo.new("...", "vararg", "") if variadic - method = MethodInfo.new(retval, selector, line[0] == ?+, args, data) - - if current_category && current_interface == "NSObject" - (@informal_protocols[current_category] ||= []) << method - end - - @classes[current_interface] ||= {} - - if header.name == current_interface - @classes[current_interface].file = header.name - else - @classes[current_interface].file ||= header.name - end - - unless parent == current_interface || parent =~ /\s+/ || parent.nil? - @classes[current_interface].parent = parent - end - - (@classes[current_interface].methods ||= []) << method - end - i += size - end - end - - def structs (header) - re = /typedef\s+struct\s*\w*\s*((\w+)|\{([^{}]*(\{[^}]+\})?)*\}\s*([^\s]+))\s*(__attribute__\(.+\))?\s*;/ - i = 0 - body = nil - @cpp_result.scan(re).each do |m| - struct = { :name => m[4], :members => [] } - - unless struct[:name].nil? - if struct[:name][0, 1] == "*" - struct[:name].sub!("*", "") - end - end - - return_type = nil - stripped_return_type = nil - body = m[2] - - if m[2] - m[2].split(/,|;/).map do |i| - str, bytes = i.split(":", 2).map do |x| - x.strip - end - - var = get_var_info(str, true) - - if var - if var.return_type == "***dummy***" - var.return_type = return_type - var.stripped_return_type = stripped_return_type - else - return_type = var.return_type - stripped_return_type = var.stripped_return_type - end - - struct[:members] << { :name => var.name, :bytes => bytes, :declaredType => var.return_type, :type => "", :type64 => "" } unless str.empty? || str[0] == ?# - - names = [] - - tmp = struct[:members].collect do |member| - unless names.include?(member[:name]) - names << member[:name] - member - end - end - - struct[:members] = tmp.compact - end - end - end - - header.structs << struct if body - end - end - - def functions (header, inline = false) - if inline - inline_func_re = /(inline|__inline__)\s+((__attribute__\(\([^)]*\)\)\s+)?([\w\s\*<>]+)\s*\(([^)]*)\)\s*)\{/ - res = @cpp_result.scan(inline_func_re) - res.each do |x| - x.delete_at(0) - x.delete_at(1) - end - else - skip_inline_re = /(static)?\s(inline|__inline__)[^{;]+(;|\{([^{}]*(\{[^}]+\})?)*\})\s*/ - func_re = /(^([\w\s\*<>]+)\s*\(([^)]*)\)\s*)(__attribute__[^;]+)?;/ - res = @cpp_result.gsub(skip_inline_re, '').scan(func_re) - end - - funcs = res.map do |m| - orig, base, args = m - base.sub!(/^.*extern\s/, "") - func = constant?(base) - - if func - args = args.strip.split(",").map do |i| - constant?(i) - end - - next if args.any? do |x| - x.nil? - end - - args = [] if args.size == 1 && args[0].return_type == "void" - FunctionInfo.new(func, args, orig, inline) - end - end.compact - - if inline - header.inline_functions = funcs - else - header.functions = funcs - end - end - - def prepare (path) - @file_content = File.read(path) - @file_content.gsub!(%r{(/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/)|(//.*)}, ""); - @complete_cpp_result, @cpp_result = do_cpp(path, false, true, "") - end - - def scan (frameworks, headers) - @frameworks = frameworks - @headers = headers - - @frameworks.each do |framework| - framework.headers.each do |header| - prepare(header.path) - - imports(header) - cftypes(header) - externs(header) - constants(header) - enums(header) - structs(header) - typedefs(header) - functions(header) - functions(header, true) - defines(header) - methods(header) - function_pointer_types(header) - end - end - - @headers.each do |header| - prepare(header.path) - - imports(header) - cftypes(header) - externs(header) - constants(header) - enums(header) - structs(header) - typedefs(header) - functions(header) - functions(header, true) - defines(header) - methods(header) - function_pointer_types(header) - end - end - - def get_var_info (str, multi = false) - str.strip! - - return nil if str.empty? - - if str == "..." - VarInfo.new("...", "...", str) - else - str = "***dummy*** " + str if str[-1].chr == '*' || str.index(/\s/).nil? - tokens = multi ? str.split(',') : [str] - part = tokens.first - re = /^([^()]*)\b(\w+)\b\s*(\[[^\]]*\])*$/ - m = re.match(part) - - if m - return nil if m[1].split(/\s+/).any? do |x| - ['end', 'typedef'].include?(x) - end - - m = m.to_a[1..-1].compact.map do |i| - i.strip - end - - m[0] += m[2] if m.size == 3 - m[0] = 'void' if m[1] == 'void' - - var = begin - VarInfo.new(m[0], m[1], part) - rescue - return nil - end - - if tokens.size > 1 - [var, *tokens[1..-1].map { |x| constant?(m[0] + x.strip.sub(/^\*+/, '')) }] - else - var - end - end - end - end - - def constant? (str, multi = false) - str.strip! - return nil if str.empty? - - if str == '...' - VarInfo.new('...', '...', str) - else - str << " dummy" if str[-1].chr == '*' or str.index(/\s/).nil? - tokens = multi ? str.split(',') : [str] - part = tokens.first - re = /^([^()]*)\b(\w+)\b\s*(\[[^\]]*\])*$/ - m = re.match(part) - - if m - return nil if m[1].split(/\s+/).any? do |x| - ['end', 'typedef'].include?(x) - end - - m = m.to_a[1..-1].compact.map do |i| - i.strip - end - - m[0] += m[2] if m.size == 3 - m[0] = 'void' if m[1] == 'void' - - var = begin - VarInfo.new(m[0], m[1], part) - rescue - return nil - end - - if tokens.size > 1 - [var, *tokens[1..-1].map { |x| constant?(m[0] + x.strip.sub(/^\*+/, '')) }] - else - var - end - end - end - end - - class VarInfo - - attr_reader :name, :orig, :const, :type_modifier - attr_accessor :octype, :resolved_type, :resolved_type64, :return_type, :stripped_return_type - - def initialize (type, name, orig) - @return_type = type.clone - @name = name - @orig = orig - @const = false - @type_modifier = "" - - @return_type.gsub!(/\[[^\]]*\]/, "*") - - if @return_type =~ /\bconst\b/ - @const = true - @return_type.gsub!("const", "") - end - - if @return_type =~ /\b(in|out|inout|oneway|bycopy|byref)\b/ - - case $1 - when "in" - @type_modifier << "n" - when "out" - @type_modifier << "o" - when "inout" - @type_modifier << "N" - when "oneway" - @type_modifier << "w" - when "bycopy" - @type_modifier << "c" - when "byref" - @type_modifier << "r" - end - - @return_type.gsub!("#{$1}", "") - end - - @return_type.gsub!(/\s?\*\s?/, "*") - @return_type.gsub!(/^\(|\)$/, "") - @return_type.strip! - - t = type.gsub(/\b(__)?const\b/, "") - t.gsub!(/<[^>]*>/, '') - t.gsub!(/\b(in|out|inout|oneway|const)\b/, "") - t.gsub!(/\b__private_extern__\b/, "") - t.gsub!(/^\s*\(?\s*/, "") - t.gsub!(/\s*\)?\s*$/, "") - - raise "Empty type (was '#{type}')" if t.empty? - - @stripped_return_type = t - end - - def function_pointer? (function_pointer_types) - type = @function_pointer_types[@stripped_return_type] || @stripped_return_type - @function_pointer_type ||= FunctionPointerInfo.new_from_type(type) - end - - def <=>(x) - self.name <=> x.name - end - - def hash - @name.hash - end - - def eql? (o) - @name == o.name - end - end - - class FunctionInfo < VarInfo - - attr_reader :args, :argc - - def initialize (function, args, orig, inline = false) - super(function.return_type, function.name, orig) - - @args = args - @argc = @args.size - @variadic = false - - if @args[-1] && @args[-1].return_type == "..." - @argc -= 1 - @variadic = true - @args.pop - end - - @inline = inline - self - end - - def variadic? - @variadic - end - - def inline? - @inline - end - end - - class FunctionPointerInfo < FunctionInfo - def initialize (return_type, arg_types, orig) - args = arg_types.map do |x| - VarInfo.new(x, "", "") - end - - super(VarInfo.new(return_type, "", ""), args, orig) - end - - def self.new_from_type (type) - @cache ||= {} - info = @cache[type] - - return info if info - - tokens = type.split(/\(\*\)/) - return nil if tokens.size != 2 - - return_type = tokens.first.strip - rest = tokens.last.sub(/^\s*\(\s*/, "").sub(/\s*\)\s*$/, "") - - arg_types = rest.split(/,/).map do |x| - x.strip - end - - @cache[type] = self.new(return_type, arg_types, type) - end - end - - class MethodInfo < FunctionInfo - - attr_reader :selector - - def initialize (method, selector, is_class, args, orig) - super(method, args, orig) - - @selector = selector - @is_class = is_class - self - end - - def class_method? - @is_class - end - - def <=>(o) - @selector <=> o.selector - end - - def hash - @selector.hash - end - - def eql? (o) - @selector == o.selector - end - end - - def do_cpp (path, fails_on_error = true, do_64 = true, flags = "") - f_on = false - err_file = '/tmp/.cpp.err' - cpp_line = "#{CPP} #{CPPFLAGS} #{flags} #{do_64 ? '-D__LP64__' : ''} \"#{path}\" 2>#{err_file}" - complete_result = `#{cpp_line}` - - if $?.to_i != 0 && fails_on_error - $stderr.puts File.read(err_file) - File.unlink(err_file) - raise "#{CPP} returned #{$?.to_int/256} exit status\nline was: #{cpp_line}" - end - - result = complete_result.select do |s| - # First pass to only grab non-empty lines and the pre-processed lines - # only from the target header (and not the entire pre-processing result). - next if s.strip.empty? - m = %r{^#\s*\d+\s+"([^"]+)"}.match(s) - f_on = (File.basename(m[1]) == File.basename(path)) if m - f_on - end.select do |s| - # Second pass to ignore all pro-processor comments that were left. - /^#/.match(s) == nil - end.join - - File.unlink(err_file) - return [complete_result, result] - end -end - -class DStepGenerator - - VERSION = 1.0 - - attr_accessor :out_file, :scaner - - def initialize - @do_64bit = false - @frameworks = [] - @framework_paths = [] - @headers = [] - @import_directives = "" - @informal_protocols = [] - @classes = [] - @scaner = HeaderScaner.new - @scaner.do_64bit = @do_64bit - end - - def do_64bit - return @do_64bit - end - - def do_64bit= (do_64bit) - @do_64bit = do_64bit - @scaner.do_64bit = do_64bit - end - - def collect - scaner.scan(@frameworks, @headers) - @classes = scaner.classes - @informal_protocols = scaner.protocols - end - - def handle_framework (framework) - val = framework.name - path = framework_path(val) - - raise "Can't locate framework '#{val}'" if path.nil? - @framework_paths << File.dirname(path) - raise "Can't find framework '#{val}'" if path.nil? - - parent_path, name = path.scan(/^(.+)\/(\w+)\.framework\/?$/)[0] - - if @private - headers_path = File.join(path, "PrivateHeaders") - raise "Can't locate private framework headers at '#{headers_path}'" unless File.exist?(headers_path) - - headers = Dir.glob(File.join(headers_path, "**", "*.h")) - public_headers_path = File.join(path, "Headers") - public_headers = if File.exist?(public_headers_path) - HeaderScaner::CPPFLAGS << " -I#{public_headers_path} " - Dir.glob(File.join8(headers_path, "**", "*.h")) - else - [] - end - else - headers_path = File.join(path, "Headers") - raise "Can't locate public framework headers at '#{headers_path}'" unless File.exist?(headers_path) - public_headers = headers = Dir.glob(File.join(headers_path, "**", "*.h")) - end - - # We can't just "#import <x/x.h>" as the main Framework header might not include _all_ headers. - # So we are tricking this by importing the main header first, then all headers. - header_basenames = (headers | public_headers).map do |x| - x.sub(/#{headers_path}\/*/, "") - end - - if idx = header_basenames.index("#{name}.h") - header_basenames.delete_at(idx) - header_basenames.unshift("#{name}.h") - end - - @import_directives = header_basenames.map do |x| - "#import <#{name}/#{x}>" - end.join("\n") - - @import_directives << "\n" - - @compiler_flags ||= "-F\"#{parent_path}\" -framework #{name}" - @cpp_flags ||= "" - @cpp_flags << "-F\"#{parent_path}\" " - - headers.each do |header| - header_file = HeaderFile.new - header_file.path = header - header_file.name = File.basename(header, File.extname(header)) - header_file.framework = framework.name - - framework.headers << header_file - end - - # Memorize the dependencies. - @dependencies = DStepGenerator.dependencies_of_framework(path) - end - - def framework_path (val) - return path if File.exist?(val) - - val += ".framework" unless /\.framework$/.match(val) - paths = ["/System/Library/Frameworks", "/Library/Frameworks", "#{ENV['HOME']}/Library/Frameworks"] - paths << "/System/Library/PrivateFrameworks" if @private - - paths.each do |dir| - path = File.join(dir, val) - return path if File.exist?(path) - end - - return nil - end - - def self.dependencies_of_framework (path) - @dependencies ||= {} - name = File.basename(path, ".framework") - path = File.join(path, name) - deps = @dependencies[path] - - if deps.nil? - deps = `otool -L "#{path}"`.scan(/\t([^\s]+)/).map do |m| - dpath = m[0] - next if File.basename(dpath) == name - next if dpath.include?("PrivateFrameworks") - next unless dpath.sub!(/\.framework\/Versions\/\w+\/\w+$/, "") - dpath + ".framework" - end.compact - - @dependencies[path] = deps - end - - return deps - end - - def compile_and_execute (code, enable_64bit = false) - compiler_line = "gcc " - src = File.new(unique_tmp_path("src", ".m"), "w") - src << code - src.close - - arch_flag = if enable_64bit - "-arch x86_64 -arch ppc64" - else - "-arch i386 -arch ppc" - end - - compiler_line << arch_flag - - bin = unique_tmp_path "bin" - log = unique_tmp_path "log" - - line = "#{compiler_line} -o #{bin} #{src.path} #{@compiler_flags}> #{log}" - - unless system(line) - msg = "Cannot compile Objective-C code ...aborting\nCommand was: #{line}\n\nLog:\n#{File.read(log)}\n\n" - $stderr << msg - File.delete src.path - raise msg - end - - result = `#{bin}` - - unless $?.success? - raise "Cannot execute compiled Objective-C code ... aborting\nCommand was: #{line}\nBinary is: #{bin}" - end - - File.delete bin - File.delete log - File.delete src.path - - return result - end - - def unique_tmp_path (base, extension = "", dir = Dir.tmpdir) - i = 0 - loop do - path = File.join(dir, "#{base}-#{Process.pid}-#{i}#{extension}") - return path unless File.exists?(path) - i += 1 - end - end - - def add_header (path) - header = HeaderFile.new - header.path = path - header.name = File.basename(path, File.extname(path)) - @import_directives << "#include <#{path}>\n" - @headers << header - end - - def add_framework (name) - framework = Framework.new - framework.name = name - handle_framework(framework) - #@import_directives << "#import <#{framework.name}/#{framework.name}.h>\n" - @frameworks << framework - end - - def collect_header_types (header, enable_64bit) - types = [] - - header.cftypes.each do |cftype| - types << cftype[:type] - end - - header.constants.each do |constant| - types << constant.stripped_return_type - end - - header.structs.each do |struct| - types << struct[:name] - - struct[:members].each do |member| - types << member[:declaredType] - end - end - - header.typedefs.each do |typedef| - types << typedef.stripped_return_type - end - - header.functions.each do |function| - types << function.stripped_return_type - - function.args.each do |arg| - types << arg.stripped_return_type - end - end - - types - end - - def collect_classes_types (enable_64bit) - types = [] - - @classes.each do |clazz, value| - value.methods.each do |method| - types << method.stripped_return_type - - method.args.each do |arg| - types << arg.stripped_return_type - end - end - end - - types - end - - def collect_informal_protocols_types (enable_64bit) - types = [] - - @informal_protocols.each do |name, methods| - methods.each do |method| - types << method.stripped_return_type - - method.args.each do |arg| - types << arg.stripped_return_type - end - end - end - - types - end - - def resolve_header_types (header, enable_64bit, resolved_types, x) - i = x - - header.cftypes.each do |cftype| - cftype[enable_64bit ? :type64 : :type] = resolved_types[i] - i += 1 - end - - header.constants.each do |constant| - constant.resolved_type = resolved_types[i] unless enable_64bit - constant.resolved_type64 = resolved_types[i] if enable_64bit - i += 1 - end - - header.structs.each do |struct| - struct[enable_64bit ? :type64 : :type] = resolved_types[i] - i += 1 - - struct[:members].each do |member| - member[enable_64bit ? :type64 : :type] = resolved_types[i] - i += 1 - end - end - - header.typedefs.each do |typedef| - typedef.resolved_type = resolved_types[i] unless enable_64bit - typedef.resolved_type64 = resolved_types[i] if enable_64bit - i += 1 - end - - header.functions.each do |function| - function.resolved_type = resolved_types[i] unless enable_64bit - function.resolved_type64 = resolved_types[i] if enable_64bit - i += 1 - - function.args.each do |arg| - arg.resolved_type = resolved_types[i] unless enable_64bit - arg.resolved_type64 = resolved_types[i] if enable_64bit - i += 1 - end - end - - i - end - - def resolve_methods_types (enable_64bit, resolved_types, x) - i = x - - @classes.each do |clazz, value| - value.methods.each do |method| - method.resolved_type = resolved_types[i] unless enable_64bit - method.resolved_type64 = resolved_types[i] if enable_64bit - i += 1 - - method.args.each do |arg| - arg.resolved_type = resolved_types[i] unless enable_64bit - arg.resolved_type64 = resolved_types[i] if enable_64bit - i += 1 - end - end - end - - i - end - - def resolve_informal_protocols_types (enable_64bit, resolved_types, x) - i = x - - @informal_protocols.each do |name, methods| - methods.each do |method| - method.resolved_type = resolved_types[i] unless enable_64bit - method.resolved_type64 = resolved_types[i] if enable_64bit - i += 1 - - method.args.each do |arg| - arg.resolved_type = resolved_types[i] unless enable_64bit - arg.resolved_type64 = resolved_types[i] if enable_64bit - i += 1 - end - end - end - - i - end - - def resolve_types (enable_64bit = false) - code = "#include <stdio.h>\n" - code << @import_directives - types = [] - - @frameworks.each do |framework| - framework.headers.each do |header| - types << collect_header_types(header, enable_64bit) - end - end - - @headers.each do |header| - types << collect_header_types(header, enable_64bit) - end - - types << collect_classes_types(enable_64bit) - types << collect_informal_protocols_types(enable_64bit) - - code << "int main () \n{\n" - types.flatten! - - types.each do |type| - code << ' printf("%s\n", ' + "@encode(#{type}));\n" - end - - code << " return 0;\n}" - - resolved_types = [] - - compile_and_execute(code, enable_64bit).split("\n").each do |line| - resolved_types << line - end - - i = 0 - - @frameworks.each do |framework| - framework.headers.each do |header| - i = resolve_header_types(header, enable_64bit, resolved_types, i) - end - end - - @headers.each do |header| - i = resolve_header_types(header, enable_64bit, resolved_types, i) - end - - i = resolve_methods_types(enable_64bit, resolved_types, i) - i = resolve_informal_protocols_types(enable_64bit, resolved_types, i) - end - - def generate_header (xml, header) - xml.file :name => header.name do - header.imports.each do |import| - xml.import import - end - - header.defines.each do |define| - xml.define define - end - - header.cftypes.each do |cftype| - xml.cftype cftype - end - - header.constants.each do |constant| - xml.constant :name => constant.name, :declaredType => constant.return_type, :type => constant.resolved_type, :type64 => constant.resolved_type64, :const => constant.const - end - - header.enums.each do |enum| - xml.enum :name => enum[:name] do - enum[:members].each do |member| - xml.member member - end - end - end - - header.structs.each do |struct| - xml.struct :name => struct[:name], :type => struct[:type], :type64 => struct[:type64] do - struct[:members].each do |member| - xml.member member - end - end - end - - header.typedefs.each do |typedef| - xml.typedef :name => typedef.name, :declaredType => typedef.return_type, :type => typedef.resolved_type, :type64 => typedef.resolved_type64, :const => typedef.const - end - - header.functions.each do |function| - xml.function :name => function.name, :inline => function.inline?, :variadic => function.variadic? do - function.args.each do |arg| - xml.arg :name => arg.name, :declaredType => arg.return_type, :type => arg.resolved_type, :type64 => arg.resolved_type64, :const => arg.const, :typeModifier => arg.type_modifier - end - - xml.returnValue :declaredType => function.return_type, :type => function.resolved_type, :type64 => function.resolved_type64, :const => function.const, :typeModifier => function.type_modifier - end - end - end - end - - def generate_classes (xml) - @classes.each do |clazz, value| - xml.class :name => clazz, :parent => value.parent, :file => value.file do - value.methods.each do |method| - xml.method :selector => method.selector, :classMethod => method.class_method?, :variadic => method.variadic? do - method.args.each do |arg| - xml.arg :name => arg.name, :declaredType => arg.return_type, :type => arg.resolved_type, :type64 => arg.resolved_type64, :const => arg.const, :typeModifier => arg.type_modifier - end - - xml.returnValue :declaredType => method.return_type, :type => method.resolved_type, :type64 => method.resolved_type64, :const => method.const, :typeModifier => method.type_modifier - end - end - end - end - end - - def generate_informal_protocols (xml) - @informal_protocols.each do |name, methods| - xml.informalProtocol :name => name do - methods.each do |method| - xml.method :selector => method.selector, :classMethod => method.class_method?, :variadic => method.variadic? do - method.args.each do |arg| - xml.arg :name => arg.name, :declaredType => arg.return_type, :type => arg.resolved_type, :type64 => arg.resolved_type64, :const => arg.const, :typeModifier => arg.type_modifier - end - - xml.returnValue :declaredType => method.return_type, :type => method.resolved_type, :type64 => method.resolved_type64, :const => method.const, :typeModifier => method.type_modifier - end - end - end - end - end - - def generate - resolve_types - resolve_types(true) if @do_64bit - - file = STDOUT if @out_file == nil - file = File.open @out_file, "w" unless @out_file == nil - - xml = XmlMarkup.new(:target => file, :indent => 4) - xml.instruct! - - xml.dstep :xmlns => "http://www.dsource.org/projects/dstep", "xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance", "xsi:schemaLocation" => "http://www.dsource.org/projects/dstep/trunk/scripts/dstep.xsd" do - @frameworks.each do |framework| - xml.framework :name => framework.name do - framework.headers.each do |header| - generate_header(xml, header) - end - end - end - - @headers.each do |header| - generate_header(xml, header) - end - - generate_classes(xml) - generate_informal_protocols(xml) - end - - file.close unless file == STDOUT - end -end - -def die (*msg) - $stderr.puts msg - exit 1 -end - -if __FILE__ == $0 - dstep_gen = DStepGenerator.new - - OptionParser.new do |opts| - opts.banner = "Usage: #{File.basename(__FILE__)} [options] <headers...>" - opts.separator "" - opts.separator "Options:" - - opts.on("-f", "--framework FRAMEWORK", "Generate metadata for the given framework.") do |opt| - dstep_gen.add_framework(opt) - end - - opts.on(nil, "--64-bit", "Write 64-bit annotations.") do - dstep_gen.do_64bit = true - end - - opts.on("-o", "--output FILE", "Write output to the given file.") do |opt| - die "Output file can't be specified more than once" if dstep_gen.out_file - dstep_gen.out_file = opt - end - - # opts.on("-d", "--output-dir PATH", "Write ouptut to the given paht, use this with the --framework option") do |opt| - # die "Output directory can't be specified more than once" if dstep_gen.out_dir - # dstep_gen.out_dir = opt - # end - - - - help_msg = "Use the `-h' flag or for help." - - opts.on("-h", "--help", "Show this message.") do - puts opts, help_msg - exit - end - - opts.on('-v', '--version', 'Show version.') do - puts DStepGenerator::VERSION - exit - end - - opts.separator "" - - if ARGV.empty? - die opts.banner - else - begin - opts.parse!(ARGV) - - ARGV.each do |header| - dstep_gen.add_header(header) - end - - dstep_gen.collect - dstep_gen.generate - rescue => e - msg = e.message - msg = "Internal error" if msg.empty? - - die msg, opts.banner, help_msg - end - end - end -end \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/dgen.rb Thu Jun 18 22:00:13 2009 +0200 @@ -0,0 +1,1324 @@ +#!/usr/bin/env ruby + +## +# Copyright:: Copyright (c) 2009 Jacob Carlborg. All rights reserved. +# Author:: Jacob Carlborg +# Version:: Initial created: 2009 +# License:: [Boost Software License 1.0]http://www.boost.org/LICENSE_1_0.txt +# + +require "rubygems" +gem "xml-simple" +require "xmlsimple" +require "optparse" + +# Extensions to String +class String + + # Passes each character to the supplied block + # + # === Example: + # "str".each_char { |c| puts c } + # # outputs below + # s + # t + # r + # + def each_char + if block_given? + scan(/./m) do |x| + yield x + end + else + scan(/./m) + end + end + + # Indents the string + # levels:: how many levels/tabs to indent + # + # === Example: + # "str".indent #=> str + # "str".indent(3) #=> str + # + def indent (levels = 1) + "\t" * levels << self + end + + # Appends a semicolon and a newline character on the string + # semicolon:: should a semicolon be appended + # + # === Example: + # "str".nl + # "str".nl(false) + # # outputs below + # str; + # + # str + # + # + def nl (semicolon = true) + if semicolon + if self[-1, 1] == "{" || self[-1, 1] == "}" + self << "\n" + else + self << ";\n" + end + else + self << "\n" + end + end + + # Returns the index of the given character or length of the string if not found + # char:: the character to look for + # start:: the index where to start the search + # + # === Example: + # "012345789".index_of("1") #=> 1 + # "0123 0123".index_of("3", 6) #=> 8 + # + def index_of (char, start = 0) + return self.length if start >= self.length + + i = 0 + + if start == 0 + self.each_char do |c| + return i if char == c + i += 1 + end + else + self[start + 1 .. -1].each_char do |c| + return i + start + 1 if char == c + i += 1 + end + end + + return self.length + end + + # Returns true if the string is an Objective-C struct + # + # === Example: + # "{?=ii}".struct? #=> true + # + def struct? + self =~ /\{/ + end +end + +# Extensions that adds support for member access syntax +class Hash + def type + result = self["type"] + return result unless result.nil? + self[:type] + end + + def type= (type) + self[:type] = type + end + + def id + result = self["id"] + return result unless result.nil? + self[:id] + end + + def id= (id) + self[:id] = id + end + + def methods + result = self["methods"] + return result unless result.nil? + self[:methods] + end + + def methods= (methods) + self[:methods] = methods + end + + def method + result = self["method"] + return result unless result.nil? + self[:method] + end + + def method= (method) + self[:method] = method + end + + def method_missing (method, *args) + self.class.instance_eval do + define_method(method) do |*args| + if args.length > 0 + self[method[0 ... -1]] = args[0] + self[eval(":#{method}"[0 ... -1])] = args[0] + else + result = self[method] + return result unless result.nil? + self[eval(":#{method}")] + end + end + end + + if (method = method.id2name) =~ /=/ + eval("self.#{method} (args.length < 2 ? args[0] : args)") + else + eval("self.#{method}") + end + end +end + +# This Struct represents an Objective-C Framework +Framework = Struct.new(:name, :files) do + def initialize + self.files = [] + self.name = "" + end +end + +# This Struct represents a C/Objective-C header +HeaderFile = Struct.new(:name, :framework, :cftypes, :constants, :d_constants, :d_constants_static_this, :defines, + :enums, :functions, :function_wrappers, :imports, :path, :structs, :typedefs) do + def initialize + self.name = "" + self.cftypes = [] + self.constants = [] + self.defines = [] + self.enums = [] + self.framework = "" + self.functions = [] + self.function_wrappers = [] + self.imports = [] + self.path = "" + self.structs = [] + self.typedefs = [] + end +end + +# Performs the conversion of an xml metadata file to the D programming language +class ObjcToD + FIRST_YEAR = "2009" + VERSION = 1.0 + + attr_accessor :out_dir, :files + + # Creates a new instance of the ObjcToD class + def initialize + @classes = {} + @copyright = nil + @d_constants = [] + @d_constants_static_this = [] + @files = [] + @frameworks = [] + @function_wrappers = [] + @headers = [] + @package = "dstep" + end + + # Generates the D code from the xml metadata + def generate_code + @files.each do |dstep_file| + xml = XmlSimple.xml_in(dstep_file) + + unless xml.framework.nil? + frameworks = xml.framework + + frameworks.each do |frame| + framework = Framework.new + framework.name = frame.name + + frame.file.each do |file| + header = HeaderFile.new + header.name = file.name + header.constants = constants(file.constant) unless file.constant.nil? + header.d_constants = d_constants unless file.constant.nil? + header.d_constants_static_this = d_constants_static_this unless file.constant.nil? + header.enums = enums(file.enum) unless file.enum.nil? + header.functions = functions(file.function) unless file.function.nil? + header.function_wrappers = function_wrappers unless file.function.nil? + header.imports = imports(file.import, file.name, framework.name) unless file.import.nil? + header.structs = structs(file.struct) unless file.struct.nil? + + header.typedefs = typedefs(file.typedef) unless file.typedef.nil? + + framework.files << header + end + + @frameworks << framework + end + end + + unless xml.file.nil? + files = xml.file + + files.each do |file| + header = HeaderFile.new + header.name = file.name + header.constants = constants(file.constant) unless file.constant.nil? + header.d_constants = d_constants unless file.constant.nil? + header.d_constants_static_this = d_constants_static_this unless file.constant.nil? + header.enums = enums(file.enum) unless file.enum.nil? + header.functions = functions(file.function) unless file.function.nil? + header.function_wrappers = function_wrappers unless file.function.nil? + header.imports = imports(file.import, file.name) unless file.import.nil? + header.structs = structs(file.struct) unless file.struct.nil? + header.typedefs = typedefs(file.typedef) unless file.typedef.nil? + + @headers << header + end + end + + unless xml["class"].nil? + classes(xml["class"]) + end + end + end + + # Outputs the generate D code + def output_code + @frameworks.each do |framework| + framework_path = framework_path = "#{@out_dir}/#{@package}/#{framework.name}" + + FileUtils.mkdir_p(framework_path) unless File.exist?(framework_path) + + framework.files.each do |header| + file_path = "#{framework_path}/#{header.name}.d" + + File.open(file_path, "w") do |file| + file << copyright + file << "module #{@package}.#{framework.name}.#{header.name};" + file << header.imports.nl(false) + file << header.defines + file << header.typedefs + file << header.cftypes + file << header.constants + file << header.d_constants + file << header.enums + file << header.structs + + unless header.d_constants_static_this.nil? + file << "static this ()".nl(false) + file << "{".nl(false) + file << header.d_constants_static_this + file << "}".nl(false).nl(false) + end + + classes = get_classes(header.name) + + classes.each do |clazz, value| + file << value.code.nl(false) + @classes.delete(clazz) + end + + file << header.functions + file << header.function_wrappers + end + end + end + + package_path = "#{@out_dir}/#{@package}" + FileUtils.mkdir_p(package_path) unless File.exist?(package_path) + + @headers.each do |header| + header_path = "#{package_path}/#{header.name}.d" + + File.open(header_path, "w") do |file| + file << copyright + file << "module #{@package}.#{header.name};" + file << header.imports.nl(false) + file << header.defines + file << header.typedefs + file << header.cftypes + file << header.constants + file << header.d_constants + file << header.enums + file << header.structs + + unless header.d_constants_static_this.nil? + file << "static this ()".nl(false) + file << "{".nl(false) + file << header.d_constants_static_this + file << "}".nl(false).nl(false) + end + + classes = get_classes(header.name) + + classes.each do |clazz, value| + file << value.code.nl(false) + @classes.delete(clazz) + end + + file << header.functions + file << header.function_wrappers + end + end + + @classes.each do |clazz, value| + class_path = "#{package_path}/#{clazz}.d" + + File.open(class_path, "w") do |file| + file << value.code unless value.nil? + end + end + end + + # Creates and returns the copyright header that is included in the top of every file + def copyright + return @copyright unless @copyright.nil? + + # Add an extra empty string in the begining because array indices begin with zero and months don't + months = ["", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] + initialt_year = "2009" + + str = StringIO.new + date = Date.today + current_year = date.year.to_s + year = initialt_year + initial_created = "#{months[date.month]} #{date.day}, #{initialt_year}" + + year << "-" << current_year unless initialt_year == current_year + + str << "/**\n" + str << " * Copyright: Copyright (c) #{year} Jacob Carlborg.\n" + str << " * Authors: Jacob Carlborg\n" + str << " * Version: Initial created: #{initial_created} \n" + str << " * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0)\n" + str << " */\n" + + @copyright = str.string + end + + # Gets name of the framework that the given class belongs to + def get_framework (class_name) + @frameworks.each do |framework| + framework.files.each do |file| + return framework.name if file.name == class_name + end + end + + return [] + end + + # Gets the classes that belongs to the given file + def get_classes (name) + classes = @classes.find_all do |clazz, value| + value.file == name + end + + return classes + end + + # Generates the D code for the classes + def classes (classes) + classes.each do |clazz| + str = StringIO.new + + str << "class " + + if clazz == "" + str << clazz.name.nl(false) + else + str << clazz.name + str << " : " + str << clazz.parent.nl(false) + end + + str << "{".nl(false) + str << methods(clazz.method, clazz.name) + str << "}".nl(false) + + @classes[clazz.name] ||= {} + @classes[clazz.name].code = str.string + framework = get_framework(clazz.file) + @classes[clazz.name].framework = framework unless framework.nil? + @classes[clazz.name].file = clazz.file + end + end + + # Generates the D code for the constants/globals + def constants (constants) + str = StringIO.new + + str << "private extern (C)".nl(false) + str << "{".nl(false) + str << "extern".indent.nl(false) + str << "{".indent.nl(false) + + constants.each do |constant| + type = get_type(constant.type, constant["type64"], constant["declaredType"]) + const = constant["const"] == "true" + + if constant.type == "@" + @d_constants << { :name => constant.name.dup, :type => constant.declaredType.gsub("*", ""), :const => const } + + if const + str << "const id".indent(2) + else + str << "id".indent(2) + end + + str << " " + str << constant["name"] + str << "_".nl + else + if const + str << "const ".indent(2) + str << type + else + str << "id".indent(2) + end + + str << " " + str << constant["name"].nl + end + end + + str << "}".indent.nl(false) + str << "}".nl(false).nl(false) + + str.string + end + + # Generates the D code for the constants that D can't handle directly, like classes + def d_constants + str = StringIO.new + + @d_constants.each do |constant| + # Deep copy constant + c = constant.dup + c.name = c.name.dup + c.type = c.type.dup + + str << "const " if constant.const + str << constant.type + str << " " + str << constant.name.nl + @d_constants_static_this << c + end + + str << "\n" + @d_constants.clear + str.string + end + + # Generates the D code for the constants the has to be in a "static this" + def d_constants_static_this + str = StringIO.new + + @d_constants_static_this.each do |constant| + str << constant.name.indent + str << " = new " + str << constant.type + str << "(" + str << constant.name + str << "_)".nl + end + + @d_constants_static_this.clear + str.string + end + + # Generates the D code for the enums + def enums (enums) + str = StringIO.new + + enums.each do |enum| + str << "enum" + + if enum["name"] == "" + str << "\n{".nl + + enum.member.each_with_index do |member, i| + str << member["name"].indent + str << " = " + str << member.value + str << ",".nl(false) unless i == enum.member.length - 1 + end + else + str << enum["name"].nl + str << "{" + + enum["member"].each_with_index do |member, i| + str << member["name"].indent + str << " = " + str << member.value + str << ",".nl(false) unless i == enum.member.length - 1 + end + end + + str << "\n}".nl.nl(false) + end + + str.string + end + + # Generates the D code for the function/method args + def args (args, variadic, method = false) + return "" if args.nil? + + str = StringIO.new + if variadic + #p args + end + args.each do |arg| + + if method || arg.type != "@" + type = get_type(arg.type, arg.type64, arg.declaredType) + else + type = "id" + end + + str << type + str << " " + str << arg["name"] + str << ", " + end + + if variadic + else + end + + str << "..." if variadic + + return str.string if variadic + return str.string[0 ... -2] unless variadic + end + + # A helper function that generates the D code for the functions/methods + def build_function (function, method = false, static = false) + str = StringIO.new + variadic = function["variadic"] == "true" + return_type = "" + args = "" + original_type = function.returnValue[0].type + + if !method && original_type == "@" + return_type = "id" + else + return_type = get_type(original_type, function.returnValue[0].type64, function.returnValue[0].declaredType) + args(function.arg, variadic) unless function.arg.nil? + end + + str << "static " if static + str << return_type + str << " " + str << function["name"] + str << " (" + str << args(function["arg"], variadic, method) + str << ")".nl + + str.string + end + + # Generates the D code for the functions + def functions (functions) + str = StringIO.new + + str << "private extern (C)".nl(false) + str << "{".nl(false) + + functions.each do |function| + wrapper_needed = false + original_type = function["returnValue"][0].type + + if original_type == "@" + @function_wrappers << function + wrapper_needed = true + else + function["arg"].each do |arg| + if (arg.type || arg["type64"]) == "@" + @function_wrappers << function + wrapper_needed = true + end + end unless function["arg"].nil? + end + + return_type = get_type(function["returnValue"][0].type, function["returnValue"][0]["type64"], function["returnValue"][0]["declaredType"]) + variadic = function["variadic"] == "true" + + str << functions_helper(function, wrapper_needed) + end + + str << "}".nl(false).nl(false) + str.string + end + + # A helper function that generates the D code for the functions + def functions_helper (function, wrapper_needed) + str = StringIO.new + + if wrapper_needed + declaration = build_function(function) + index = declaration.index_of(" ") + index = declaration.index_of(" ", index) + str << (declaration[0 ... index] + "_" + declaration[index .. -1]).indent + else + str << build_function(function).indent + end + + str.string + end + + # Generates the D code for the functions that D can't handle without wrappers, like functions that takes + # objects as arguments or returns an object + def function_wrappers + str = StringIO.new + + @function_wrappers.each do |function| + original_type = function["returnValue"][0].type + variadic = function["variadic"] == "true" + args = StringIO.new + out_args = [] + return_type = get_type(original_type, function.returnValue[0].type64, function.returnValue[0].declaredType) + + function["arg"].each do |arg| + args << arg["name"] + + if arg.type == "@" + args << " !is null ? " + args << arg["name"] + args << ".id : null" + elsif arg.type == "^@" + type = get_type(arg.type, arg["type64"], arg["declaredType"]) + out_args << { :type => type[4 .. -1], :name => arg["name"] } + + args << " &" + args << arg["name"] + args << "_.id" + end + + args << ", " + end unless function["arg"].nil? + + str << build_function(function, true)[0 ... -2].nl(false) + str << "{".nl(false) + + out_args.each do |arg| + str << arg.type.indent + str << " " + str << arg.name + str << "_ = new " + str << arg.type + str << "(false, false)".nl + end + + str << "\n" if out_args.length > 0 + + if original_type == "@" + str << "id result = ".indent + str << function["name"] + elsif original_type != "v" + if out_args.length > 0 + str << return_type.indent + str << " result = " + else + str << "return ".indent + end + end + + if original_type == "v" + str << function["name"].indent + elsif original_type != "@" + str << "cast(" + str << return_type + str << ") " + str << function["name"] + end + + str << "_" + str << "(" + str << args.string[0 ... -2] unless function["arg"].nil? + str << ")".nl + str << "\n" if out_args.length > 0 + + out_args.each do |arg| + str << arg.name.indent(2) + str << " = " + str << arg.name + str << "_".nl + end + + if out_args.length > 0 + str << "\n" + str << "return result".indent unless original_type == "v" + end + + if original_type == "@" + str << "return result !is null ? new ".indent + str << return_type + str << "(result)".nl + elsif original_type != "v" && out_args.length > 0 + str << "".nl + end + + str << "}".nl(false).nl(false) + end + + @function_wrappers.clear + str.string[0 ... -2] + end + + # Generates the D code for the imports + def imports (imports, filename, framework_name = nil) + str = StringIO.new + + imports.each do |import| + str << "import #{@package}." + i = import.index('/') + import = import[0 ... i].downcase + import[i .. -1] if i + str << import.gsub("/", ".").gsub("Frameworks", "").gsub(".framework", "").gsub("Headers", "").gsub(/\.{2,}/, ".").nl + end + + str << "\n\n" + str = str.string.sort.to_s + + return "\n\npublic:" << str if filename == framework_name + return str + end + + # Generates the D code for the methods + def methods (methods, class_name) + str = StringIO.new + + methods.each do |method| + next if method.selector == ("alloc" || "init") + + return_type = get_type(method["returnValue"][0].type, method["returnValue"][0]["type64"], method["returnValue"][0]["declaredType"]) + variadic = method["variadic"] == "true" + static = method["static"] == "true" + args = "" + out_args = [] + original_type = method.returnValue[0].type + + if method.selector[0 ... 4] == "init" && return_type == "id" + return_type = class_name + method.returnValue[0].declaredType = class_name + end + + method["arg"].each do |arg| + args << " &" if arg.type == "^@" + args << arg["name"] + + if arg.type == "@" + args << " !is null ? " + args << arg["name"] + args << ".id : null" + elsif arg.type == "^@" + type = get_type(arg.type, arg.type, arg["declaredType"]) + out_args << { :type => type[4 .. -1], :name => arg["name"] } + args << "_.id" + end + + args << ", " + end unless method["arg"].nil? + + declaration = build_function(method, true, static)[0 ... -2].indent.nl(false) + index = declaration.index_of(" ") + + str << (declaration[0 .. index] + get_method_name(method["selector"]) + declaration[index .. -1]).gsub(/ +/, " ") + str << "{".indent.nl(false) + + out_args.each do |arg| + str << arg.type.indent(2) + str << " " + str << arg.name + str << "_ = new " + str << arg.type + str << "(false, false)".nl + end + + str << "\n" if out_args.length > 0 + + + if method["returnValue"][0].type == "@" + str << "id result = objc_msgSend(#{this}, sel_".indent(2) + str << transform_selector(method["selector"]) + str << ", " unless method.arg.nil? + str << args[0 ... -2] unless method.arg.nil? + str << ")".nl + str << "\n" if out_args.length > 0 + + out_args.each do |arg| + str << arg.name.indent(2) + str << " = " + str << arg.name + str << "_".nl + end + + str << "\n" if out_args.length > 0 + + if method["returnValue"][0]["declaredType"] == class_name + str << "return result is this.id ? this : (return result ".indent(2) + else + str << "return result ".indent(2) + end + + str << "!is null ? new " + str << return_type + str << "(result) : null" + + if method["returnValue"][0]["declaredType"] == class_name + str << ")".nl + else + str << "".nl + end + + str << "}".indent.nl(false).nl(false) + else + if original_type == "d" || original_type == "f" + + str << "version (X86)".indent(2).nl(false) + + if out_args.length > 0 + str << return_type.indent(3) + str << " result " + else + if original_type == "d" + str << "return ".indent(3) + else + str << "return cast(".indent(3) + str << return_type + str << ") " + end + end + + str << "objc_msgSend_fpret(#{this}, sel_" + str << transform_selector(method["selector"]) + str << ", " unless method.arg.nil? + + args = "" + + method["arg"].each do |arg| + args << arg["name"] + args << ", " + end unless method["arg"].nil? + + str << args[0 ... -2] if args.length > 0 + str << ")".nl.nl(false) + + str << "else".indent(2).nl(false) + + if out_args.length > 0 + str << return_type.indent(3) + str << " result " + else + if original_type == "d" + str << "return ".indent(3) + else + str << "return cast(".indent(3) + str << return_type + str << ") " + end + end + + str << "objc_msgSend(#{this}, sel_" + str << transform_selector(method["selector"]) + str << ", " unless method.arg.nil? + + args = "" + + method["arg"].each do |arg| + args << arg["name"] + args << ", " + end unless method["arg"].nil? + + str << args[0 ... -2] if args.length > 0 + str << ")".nl + str << "\n" if out_args.length > 0 + + out_args.each do |arg| + str << arg.name.indent(3) + str << " = " + str << arg.name + str << "_".nl + end + + if out_args.length > 0 + str << "\n" + str << "retrun result".indent(3).nl + end + + str << "\n" if out_args.length > 0 + str << "}".indent.nl(false).nl(false) + else + unless return_type == "void" + if out_args.length > 0 + str << return_type.indent(2) + str << " result " + else + str << "return cast(".indent(2) + end + + str << return_type + str << ") " + end + + str << "objc_msgSend(#{this}, sel_".indent(2) if return_type == "void" + str << "objc_msgSend(#{this}, sel_" unless return_type == "void" + str << transform_selector(method["selector"]) + str << ", " unless method.arg.nil? + + args = "" + + method["arg"].each do |arg| + args << arg["name"] + args << ", " + end unless method["arg"].nil? + + str << args[0 ... -2] if args.length > 0 + str << ")".nl + + str << "\n" if out_args.length > 0 + + out_args.each do |arg| + str << arg.name.indent(2) + str << " = " + str << arg.name + str << "_".nl + end + + if out_args.length > 0 + str << "\n" + str << "retrun result".indent(2).nl + end + + str << "}".indent.nl(false).nl(false) + end + end + end + + str.string[0 .. -2] + end + + # Generates the D code for the structs + def structs (structs) + str = StringIO.new + + structs.each do |struct| + str << "struct " + str << struct.name.nl(false) + str << "{".nl + + struct.member.each do |member| + type = get_type(member.type, member.type64, member.declaredType) + + str << type.indent + str << " " + str << member.name.nl + end unless struct.member.nil? + + str << "}".nl + str << "\n\n" + end + + str.string[0 .. -2] + end + + # Generates the D code for the typedefs + def typedefs (typedefs) + str = StringIO.new + + typedefs.each do |typedef| + + type = "" + struct = false + + if typedef.type.struct? + type = get_struct_type(typedef.type, typedef["type64"], typedef["declaredType"]) + struct = true + else + type = get_type(typedef.type, typedef.type64, typedef.declaredType) + end + + # Special case + type = "wchar" if typedef.name == "unichar" + + if struct + str << typedef["declaredType"].gsub("*", "").nl + str << "alias " + str << type[:name] + str << " " + str << typedef["name"].nl + else + str << "alias " + str << type + str << " " + str << typedef.name.nl + end + end + + str << "\n" + str.string + end + + # Adds specific D extensions to classes, like opIndex and opApply + def d_class_extensions (args) + + end + + # Checks if the declared type should be used instead of the type + # type:: the type to check + # + def check_declared_type (type) + case type + when "unichar"; return "wchar" + when "BOOL"; return "bool" + when "CGFloat"; return type + when "NSInteger"; return type + when "NSUInteger"; return type + + when "unichar*"; return "wchar*" + when "BOOL*"; return "bool*" + when "CGFloat*"; return type + when "NSInteger*"; return type + when "NSUInteger*"; return type + + when "unichar**"; return "wchar**" + when "BOOL**"; return "bool**" + when "CGFloat**"; return type + when "NSInteger**"; return type + when "NSUInteger**"; return type + else return nil; + end + end + + # Gets the method name from the supplied selector + # selector:: the selector to get the method name from + # + # === Example: + # get_method_name("initWithContentsOfURL:options:error:") #=> initWithContentsOfURL + # + def get_method_name (selector) + i = selector.index_of(":") + selector[0 ... i] + end + + # Gets the D type from the encoded C/Objective-C type + # type:: the type + # type64:: the type for 64bit targets + # declared_type:: the declared type + # + # === Example: + # get_type("I", "Q", "NSUInteger") #=> NSUInteger + # get_type("I", "I", "unsigned int") #=> uint + # + def get_type (type, type64, declared_type) + + return declared_type if type.nil? || type64.nil? + + t = check_declared_type(declared_type) + return t unless t.nil? + + unless type64 == "" || type64.nil? + return declared_type if type != type64 + end + + case type + when "c"; return "byte" + when "i"; return "int" + when "s"; return "short" + when "l"; return "int" + when "q"; return "long" + when "C"; return "ubyte" + when "I"; return "uint" + when "S"; return "ushort" + when "L"; return "uint" + when "Q"; return "ulong" + when "f"; return "float" + when "d"; return "double" + when "B"; return "bool" + when "v"; return "void" + when "*"; return "char*" + when '#'; return "Class" + when ":"; return "SEL" + when "@" + return declared_type.gsub(/\*+/, "") unless declared_type.nil? + raise "No declared type given" + else + case type[0, 1] + when "[" + str = "" + t = type[1 ... -1] + count = $1 if t =~ /(\d+)/ + t = $' + + t64 = type64[1 ... -1] + count = $1 if t64 =~ /(\d+)/ + t64 = $' + + str = get_type(t, t64, declared_type) + str << "[#{count}]" + + return str + when "(" + resolved_types = [] + types = $2 if type =~ /\((.+)=(.+)\)/ + types64 = $2 if type64 =~ /\((.+)=(.+)\)/ + + for i in types + resolved_types << get_type(types[i], types64[i], declared_type) + end + + return resolved_types + when "^" + if type == "^@" + return "out " << get_type(type[1 .. -1], type64[1 .. -1], declared_type).dup + elsif type.length > 2 && type[0 ... 2] == "^^" + return "out " << get_type(type[2 .. -1], type64[2 .. -1], declared_type).dup + elsif type == "^?" + tmp = cfp_to_dfp(type) + return tmp unless tmp.nil? + end + + if !type.nil? && !type64.nil? + return get_type(type[1 .. -1], type64[1 .. -1], declared_type).dup << "*" + elsif !type.nil? + return get_type(type[1 .. -1], type64, declared_type).dup << "*" + end + when "{" + return declared_type + end + end + + return declared_type + end + + # Gets the D type from the encoded C/Objective-C type when it's a struct + # type:: the type + # type64:: the type for 64bit targets + # declared_type:: the declared type + # + # === Example + # get_struct_type("{some=III}", "{some=III}", "struct some") + # # outputs below + # { :name => "some", :types => ["uint", "uint", "uint"] } + # + def get_struct_type (type, type64, declared_type) + + return { :name => declared_type } if declared_type[0 ... 2] == "NS" + + case type[0, 1] + when "{" + resolved_types = [] + + if type =~ /\^{0,}\{(.{0,})=(.{0,})\}/ + name = $1 + types = $2 + elsif type =~ /\^{0,}\((.{0,})=(.{0,})\)/ + name = $1 + types = $2 + end + + unless types.nil? + types.each_char do |t| + resolved_types << get_type(t, type64, declared_type) + end + end + + name = declared_type if name.nil? + + return { :name => name, :types => resolved_types } + when "^" + get_struct_type(type[1 .. -1], type64, declared_type) + hash = get_struct_type(type[1 .. -1], type64, declared_type) + hash[:name].dup << "*" + return hash + end + end + + # C Function Pointer to D Function Pointer + def cfp_to_dfp (fp) + reg = /(\w+)\s*\(\*(\w*)\)\s*\((.*)\)/ + + return nil if fp !~ reg + + return_type = $1 + name = $2 + arg_types = $3 + + return "#{return_type} function (#{arg_types})" + end + + # Is the supplied argument an "out" argument + # arg:: the arg + # + # === Example: + # out_arg?("out NSError") #=> true + # + def out_arg? (arg) + return arg[0 .. 4] == "out " + end + + # Transform the supplied selector to a valid D representation + # selector:: the selector to transform + # + # === Example: + # transform_selector("initWithContentsOfURL:options:error:") + # # outputs below + # initWithContentsOfURL_options_error_ + # + def transform_selector (selector) + selector.gsub(/:/, "_") + end +end + +# Prints the message to stderr, exits +def die (*msg) + $stderr.puts msg + exit 1 +end + +if __FILE__ == $0 + objc_to_d = ObjcToD.new + + OptionParser.new do |opts| + opts.banner = "Usage: #{File.basename(__FILE__)} [options] <dstep files...>" + opts.separator "" + opts.separator "Options:" + + opts.on("-o", "--output DIRECTORY", "Place the output files in this directory'") do |opt| + die "The specified directory \"#{opt}\" does not exists" if File.exist?(opt) == false + die "Output directory cannot be specified more than once" if objc_to_d.out_dir + objc_to_d.out_dir = opt + end + + help_msg = "Use the `-h' flag or for help." + + opts.on("-h", "--help", "Show this message.") do + puts opts, help_msg + exit + end + + opts.on('-v', '--version', 'Show version.') do + puts ObjcToD::VERSION + exit + end + + opts.separator "" + + if ARGV.empty? + die opts.banner + else + #begin + opts.parse!(ARGV) + + die "No output directory given" if objc_to_d.out_dir.nil? + + ARGV.each do |file| + objc_to_d.files << file + end + + objc_to_d.generate_code + objc_to_d.output_code + # rescue => e + # msg = e.message + # msg = "Internal error" if msg.empty? + # + # die msg, opts.banner, help_msg + # end + end + end +end \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/dstepgen.rb Thu Jun 18 22:00:13 2009 +0200 @@ -0,0 +1,1451 @@ +#!/usr/bin/env ruby + +# Copyright (c) 2008-2009, Jacob Carlborg. All rights reserved. +# Copyright (c) 2006-2007, Apple Inc. All rights reserved. +# Copyright (c) 2005-2006 FUJIMOTO Hisakuni +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 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. Neither the name of Apple Inc. ("Apple") nor the names of +# its contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS CONTRIBUTORS 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. + +require "rubygems" +gem "builder", "~> 2.0" +require "builder" +require "tmpdir" +require "optparse" +include Builder + +$KCODE = "UTF8" + +# Extensions that adds support for member access syntax +class Hash + def type + result = self["type"] + return result unless result.nil? + self[:type] + end + + def type= (type) + self[:type] = type + end + + def id + result = self["id"] + return result unless result.nil? + self[:id] + end + + def id= (id) + self[:id] = id + end + + def methods + result = self["methods"] + return result unless result.nil? + self[:methods] + end + + def methods= (methods) + self[:methods] = methods + end + + def method_missing (method, *args) + self.class.instance_eval do + define_method(method) do |*args| + if args.length > 0 + self[method[0 ... -1]] = args[0] + self[eval(":#{method}"[0 ... -1])] = args[0] + else + result = self[method] + return result unless result.nil? + self[eval(":#{method}")] + end + end + end + + if (method = method.id2name) =~ /=/ + eval("self.#{method} (args.length < 2 ? args[0] : args)") + else + eval("self.#{method}") + end + end +end + +# This Struct represents an Objective-C Framework +Framework = Struct.new(:name, :headers, :path) do + def initialize + self.headers = [] + self.name = "" + self.path = "" + end +end + +# This Struct represents a C/Objective-C header +HeaderFile = Struct.new(:name, :framework, :cftypes, :constants, :defines, :enums, :externs, :functions, + :imports, :inline_functions, :opaques, :path, :structs, :typedefs) do + def initialize + self.name = "" + self.cftypes = [] + self.constants = [] + self.defines = [] + self.enums = [] + self.externs = [] + self.framework = "" + self.functions = [] + self.imports = [] + self.inline_functions = [] + self.opaques = [] + self.path = "" + self.structs = [] + self.typedefs = [] + end +end + +# This class scans the headers +class HeaderScaner + CPP = ['/usr/bin/cpp-4.0', '/usr/bin/cpp-3.3', '/usr/bin/cpp3'].find { |x| File.exist?(x) } + raise "cpp not found" if CPP.nil? + CPPFLAGS = "-D__APPLE_CPP__ -include /usr/include/AvailabilityMacros.h" + CPPFLAGS << "-D__GNUC__" unless /\Acpp-4/.match(File.basename(CPP)) + + attr_accessor :frameworks, :headers, :do_64bit + + def initialize + @extern_name = 'extern' + @frameworks = [] + @file_content = nil + @headers = [] + @classes = {} + @informal_protocols = {} + @function_pointer_types = {} + @do_64bit = false + end + + def classes + @classes + end + + def protocols + @informal_protocols + end + + def cftypes (header) + re = /typedef\s+(const\s+)?(struct\s*\w+\s*\*\s*)([^\s]+Ref)\s*;/ + @cpp_result.scan(re).each do |m| + header.cftypes << { :name => m[2], :type => m[1], :const => m[0] =~ /const/ ? true : false} + end + end + + def constants (header) + tmp = header.externs.map do |i| + constant?(i, true) + end + + header.constants = tmp.flatten.compact + end + + def defines (header) + re = /#define\s+([^\s]+)\s+(\([^)]+\)|[^\s]+)\s*$/ + @file_content.scan(re).each do |m| + next unless !m[0].include?('(') && m[1] != '\\' + header.defines << { :name => m[0], :value => m[1] } + end + end + + def enums (header) + re = /\benum\b\s*(\w+\s+)?\{([^}]*)\}/ + @cpp_result.scan(re).each do |m| + enum = { :name => m[0], :members => [] } + + m[1].split(",").map do |i| + name, value = i.split("=", 2).map do |x| + x.strip + end + + enum[:members] << { :name => name, :value => value } unless name.empty? || name[0] == ?# + end + + header.enums << enum + end + end + + def function_pointer_types (header) + re = /typedef\s+([\w\s]+)\s*\(\s*\*\s*(\w+)\s*\)\s*\(([^)]*)\)\s*;/ + data = @cpp_result.scan(re) + re = /typedef\s+([\w\s]+)\s*\(([^)]+)\)\s*;/ + data |= @cpp_result.scan(re).map do |m| + ary = m[0].split(/(\w+)$/) + ary[1] << " *" + ary << m[1] + ary + end + + data.each do |m| + name = m[1] + args = m[2].split(",").map do |x| + if x.include?(" ") + ptr = x.sub!(/\[\]\s*$/, "") + x = x.sub(/\w+\s*$/, "").strip + ptr ? x + "*" : x + else + x.strip + end + end + + type = "#{m[0]}(*)(#{args.join(', ')})" + @function_pointer_types[name] = type + end + end + + def typedefs (header) + re = /^\s*typedef\s+(.+)\s+([\w\*]+)\s*;$/ + data = @cpp_result + data.scan(re).each do |m| + var = get_var_info(m[0] + " " + m[1]) + header.typedefs << get_var_info(m[0] + " " + m[1]) if var + end + end + + def externs (header) + re = /^\s*#{@extern_name}\s+\b(.*)\s*;.*$/ + header.externs = @cpp_result.scan(re).map do |m| + m[0].strip + end + end + + def imports (header) + tmp = [] + + @file_content.each do |line| + if line =~ /#(include|import) <(.+)\.h>/ + next if $2 == header.name + tmp << $2 + elsif line =~ /@class(\s+)(\w+)(,|;)/ + next if $2 == header.name + + if $3 == ";" + tmp << header.framework + "/" + $2 unless header.framework == "" + tmp << $2 if header.framework == "" + elsif + str = line[6 + $1.length ... -2] + str.gsub!(" ", "") + arr = str.split(",") + + arr.each do |s| + tmp << header.framework + "/" + s unless header.framework == "" + tmp << s if header.framework == "" + end + end + end + end + + header.imports = tmp.compact.uniq + end + + def informal_protocols (header) + self.methods(header) + end + + def methods (header) + interface_re = /^@(interface|protocol)\s+(\w+)\s*:?\s*(\w*)\s*(\([^)]+\))?/ + + end_re = /^@end/ + body_re = /^[-+]\s*(\([^)]+\))?\s*([^:\s;]+)/ + args_re = /\w+\s*:/ + prop_re = /^@property\s*(\([^)]+\))?\s*([^;]+);$/ + current_interface = current_category = nil + i = 0 + parent = nil + + @cpp_result.each_line do |line| + size = line.size + line.strip! + + if md = interface_re.match(line) + parent = nil + current_interface = md[1] == "protocol" ? "NSObject" : md[2] + current_category = md[4].delete("()").strip if md[4] + parent = md[3] unless md[3] == "" + + elsif end_re.match(line) + current_interface = current_category = nil + + elsif current_interface && md = prop_re.match(line) + # Parsing Objective-C 2.0 properties + if (a = md[2].split(/\s/)).length >= 2 && /^\w+$/.match(name = a[-1]) && (type = a[0 .. -2].join(" ")).index(",").nil? + getter, setter = name, "set#{name[0].chr.upcase + name[1 .. -1]}" + readonly = false + + if attributes = md[1] + if md = /getter\s*=\s*(\w+)/.match(attributes) + getter = md[1] + end + + if md = /setter\s*=\s*(\w+)/.match(attributes) + setter = md[1] + end + + readonly = true if attributes.index("readonly") + end + + typeinfo = VarInfo.new(type, "", "") + + @classes[current_interface] ||= {} + methods = (@classes[current_interface].methods ||= []) + methods << MethodInfo.new(typeinfo, getter, false, [], line) + + unless readonly + methods << MethodInfo.new(VarInfo.new("void", "", ""), setter + ":", false, [typeinfo], line) + end + end + + elsif current_interface && (line[0] == ?+ || line[0] == ?-) + mtype = line[0] + data = @cpp_result[i .. -1] + body_md = body_re.match(data) + + next if body_md.nil? + + rettype = body_md[1] ? body_md[1].delete("()") : "id" + retval = VarInfo.new(rettype, "", "") + args = [] + selector = "" + data = data[0 .. data.index(";")] + args_data = [] + + data.scan(args_re) do |x| + args_data << [$`, x, $'] + end + + variadic = false + args_data.each_with_index do |ary, n| + before, argname, argtype = ary + arg_nameless = (n > 0 && /\)\s*$/.match(before)) + argname = ":" if arg_nameless + realargname = nil + + if n < args_data.length - 1 + argtype.sub!(args_data[n + 1][2], "") + + if arg_nameless + argtype.sub!(/(\w+\s*)?\w+\s*:\s*$/, "") + else + unless argtype.sub!(/(\w+)\s+\w+:\s*$/) { |s| realargname = $1; "" } + # maybe the next argument is nameless + argtype.sub!(/\w+\s*:\s*$/, "") + end + end + else + argtype.sub!(/\s+__attribute__\(\(.+\)\)/, "") + + if arg_nameless + argtype.sub!(/\w+\s*;$/, "") + else + unless argtype.sub!(/(\w+)\s*;$/) { |s| realargname = $1; "" } + variadic = argtype.sub!(/,\s*\.\.\.\s*;/, "") != nil + argtype.sub!(/\w+\s*$/, "") if variadic + end + end + end + + selector << argname + realargname ||= argname.sub(/:/, "") + args << VarInfo.new(argtype, realargname, "") unless argtype.empty? + end + + selector = body_md[2] if selector.empty? + args << VarInfo.new("...", "vararg", "") if variadic + method = MethodInfo.new(retval, selector, line[0] == ?+, args, data) + + if current_category && current_interface == "NSObject" + (@informal_protocols[current_category] ||= []) << method + end + + @classes[current_interface] ||= {} + + if header.name == current_interface + @classes[current_interface].file = header.name + else + @classes[current_interface].file ||= header.name + end + + unless parent == current_interface || parent =~ /\s+/ || parent.nil? + @classes[current_interface].parent = parent + end + + (@classes[current_interface].methods ||= []) << method + end + i += size + end + end + + def structs (header) + re = /typedef\s+struct\s*\w*\s*((\w+)|\{([^{}]*(\{[^}]+\})?)*\}\s*([^\s]+))\s*(__attribute__\(.+\))?\s*;/ + i = 0 + body = nil + @cpp_result.scan(re).each do |m| + struct = { :name => m[4], :members => [] } + + unless struct[:name].nil? + if struct[:name][0, 1] == "*" + struct[:name].sub!("*", "") + end + end + + return_type = nil + stripped_return_type = nil + body = m[2] + + if m[2] + m[2].split(/,|;/).map do |i| + str, bytes = i.split(":", 2).map do |x| + x.strip + end + + var = get_var_info(str, true) + + if var + if var.return_type == "***dummy***" + var.return_type = return_type + var.stripped_return_type = stripped_return_type + else + return_type = var.return_type + stripped_return_type = var.stripped_return_type + end + + struct[:members] << { :name => var.name, :bytes => bytes, :declaredType => var.return_type, :type => "", :type64 => "" } unless str.empty? || str[0] == ?# + + names = [] + + tmp = struct[:members].collect do |member| + unless names.include?(member[:name]) + names << member[:name] + member + end + end + + struct[:members] = tmp.compact + end + end + end + + header.structs << struct if body + end + end + + def functions (header, inline = false) + if inline + inline_func_re = /(inline|__inline__)\s+((__attribute__\(\([^)]*\)\)\s+)?([\w\s\*<>]+)\s*\(([^)]*)\)\s*)\{/ + res = @cpp_result.scan(inline_func_re) + res.each do |x| + x.delete_at(0) + x.delete_at(1) + end + else + skip_inline_re = /(static)?\s(inline|__inline__)[^{;]+(;|\{([^{}]*(\{[^}]+\})?)*\})\s*/ + func_re = /(^([\w\s\*<>]+)\s*\(([^)]*)\)\s*)(__attribute__[^;]+)?;/ + res = @cpp_result.gsub(skip_inline_re, '').scan(func_re) + end + + funcs = res.map do |m| + orig, base, args = m + base.sub!(/^.*extern\s/, "") + func = constant?(base) + + if func + args = args.strip.split(",").map do |i| + constant?(i) + end + + next if args.any? do |x| + x.nil? + end + + args = [] if args.size == 1 && args[0].return_type == "void" + FunctionInfo.new(func, args, orig, inline) + end + end.compact + + if inline + header.inline_functions = funcs + else + header.functions = funcs + end + end + + def prepare (path) + @file_content = File.read(path) + @file_content.gsub!(%r{(/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/)|(//.*)}, ""); + @complete_cpp_result, @cpp_result = do_cpp(path, false, true, "") + end + + def scan (frameworks, headers) + @frameworks = frameworks + @headers = headers + + @frameworks.each do |framework| + framework.headers.each do |header| + prepare(header.path) + + imports(header) + cftypes(header) + externs(header) + constants(header) + enums(header) + structs(header) + typedefs(header) + functions(header) + functions(header, true) + defines(header) + methods(header) + function_pointer_types(header) + end + end + + @headers.each do |header| + prepare(header.path) + + imports(header) + cftypes(header) + externs(header) + constants(header) + enums(header) + structs(header) + typedefs(header) + functions(header) + functions(header, true) + defines(header) + methods(header) + function_pointer_types(header) + end + end + + def get_var_info (str, multi = false) + str.strip! + + return nil if str.empty? + + if str == "..." + VarInfo.new("...", "...", str) + else + str = "***dummy*** " + str if str[-1].chr == '*' || str.index(/\s/).nil? + tokens = multi ? str.split(',') : [str] + part = tokens.first + re = /^([^()]*)\b(\w+)\b\s*(\[[^\]]*\])*$/ + m = re.match(part) + + if m + return nil if m[1].split(/\s+/).any? do |x| + ['end', 'typedef'].include?(x) + end + + m = m.to_a[1..-1].compact.map do |i| + i.strip + end + + m[0] += m[2] if m.size == 3 + m[0] = 'void' if m[1] == 'void' + + var = begin + VarInfo.new(m[0], m[1], part) + rescue + return nil + end + + if tokens.size > 1 + [var, *tokens[1..-1].map { |x| constant?(m[0] + x.strip.sub(/^\*+/, '')) }] + else + var + end + end + end + end + + def constant? (str, multi = false) + str.strip! + return nil if str.empty? + + if str == '...' + VarInfo.new('...', '...', str) + else + str << " dummy" if str[-1].chr == '*' or str.index(/\s/).nil? + tokens = multi ? str.split(',') : [str] + part = tokens.first + re = /^([^()]*)\b(\w+)\b\s*(\[[^\]]*\])*$/ + m = re.match(part) + + if m + return nil if m[1].split(/\s+/).any? do |x| + ['end', 'typedef'].include?(x) + end + + m = m.to_a[1..-1].compact.map do |i| + i.strip + end + + m[0] += m[2] if m.size == 3 + m[0] = 'void' if m[1] == 'void' + + var = begin + VarInfo.new(m[0], m[1], part) + rescue + return nil + end + + if tokens.size > 1 + [var, *tokens[1..-1].map { |x| constant?(m[0] + x.strip.sub(/^\*+/, '')) }] + else + var + end + end + end + end + + class VarInfo + + attr_reader :name, :orig, :const, :type_modifier + attr_accessor :octype, :resolved_type, :resolved_type64, :return_type, :stripped_return_type + + def initialize (type, name, orig) + @return_type = type.clone + @name = name + @orig = orig + @const = false + @type_modifier = "" + + @return_type.gsub!(/\[[^\]]*\]/, "*") + + if @return_type =~ /\bconst\b/ + @const = true + @return_type.gsub!("const", "") + end + + if @return_type =~ /\b(in|out|inout|oneway|bycopy|byref)\b/ + + case $1 + when "in" + @type_modifier << "n" + when "out" + @type_modifier << "o" + when "inout" + @type_modifier << "N" + when "oneway" + @type_modifier << "w" + when "bycopy" + @type_modifier << "c" + when "byref" + @type_modifier << "r" + end + + @return_type.gsub!("#{$1}", "") + end + + @return_type.gsub!(/\s?\*\s?/, "*") + @return_type.gsub!(/^\(|\)$/, "") + @return_type.strip! + + t = type.gsub(/\b(__)?const\b/, "") + t.gsub!(/<[^>]*>/, '') + t.gsub!(/\b(in|out|inout|oneway|const)\b/, "") + t.gsub!(/\b__private_extern__\b/, "") + t.gsub!(/^\s*\(?\s*/, "") + t.gsub!(/\s*\)?\s*$/, "") + + raise "Empty type (was '#{type}')" if t.empty? + + @stripped_return_type = t + end + + def function_pointer? (function_pointer_types) + type = @function_pointer_types[@stripped_return_type] || @stripped_return_type + @function_pointer_type ||= FunctionPointerInfo.new_from_type(type) + end + + def <=>(x) + self.name <=> x.name + end + + def hash + @name.hash + end + + def eql? (o) + @name == o.name + end + end + + class FunctionInfo < VarInfo + + attr_reader :args, :argc + + def initialize (function, args, orig, inline = false) + super(function.return_type, function.name, orig) + + @args = args + @argc = @args.size + @variadic = false + + if @args[-1] && @args[-1].return_type == "..." + @argc -= 1 + @variadic = true + @args.pop + end + + @inline = inline + self + end + + def variadic? + @variadic + end + + def inline? + @inline + end + end + + class FunctionPointerInfo < FunctionInfo + def initialize (return_type, arg_types, orig) + args = arg_types.map do |x| + VarInfo.new(x, "", "") + end + + super(VarInfo.new(return_type, "", ""), args, orig) + end + + def self.new_from_type (type) + @cache ||= {} + info = @cache[type] + + return info if info + + tokens = type.split(/\(\*\)/) + return nil if tokens.size != 2 + + return_type = tokens.first.strip + rest = tokens.last.sub(/^\s*\(\s*/, "").sub(/\s*\)\s*$/, "") + + arg_types = rest.split(/,/).map do |x| + x.strip + end + + @cache[type] = self.new(return_type, arg_types, type) + end + end + + class MethodInfo < FunctionInfo + + attr_reader :selector + + def initialize (method, selector, is_class, args, orig) + super(method, args, orig) + + @selector = selector + @is_class = is_class + self + end + + def class_method? + @is_class + end + + def <=>(o) + @selector <=> o.selector + end + + def hash + @selector.hash + end + + def eql? (o) + @selector == o.selector + end + end + + def do_cpp (path, fails_on_error = true, do_64 = true, flags = "") + f_on = false + err_file = '/tmp/.cpp.err' + cpp_line = "#{CPP} #{CPPFLAGS} #{flags} #{do_64 ? '-D__LP64__' : ''} \"#{path}\" 2>#{err_file}" + complete_result = `#{cpp_line}` + + if $?.to_i != 0 && fails_on_error + $stderr.puts File.read(err_file) + File.unlink(err_file) + raise "#{CPP} returned #{$?.to_int/256} exit status\nline was: #{cpp_line}" + end + + result = complete_result.select do |s| + # First pass to only grab non-empty lines and the pre-processed lines + # only from the target header (and not the entire pre-processing result). + next if s.strip.empty? + m = %r{^#\s*\d+\s+"([^"]+)"}.match(s) + f_on = (File.basename(m[1]) == File.basename(path)) if m + f_on + end.select do |s| + # Second pass to ignore all pro-processor comments that were left. + /^#/.match(s) == nil + end.join + + File.unlink(err_file) + return [complete_result, result] + end +end + +class DStepGenerator + + VERSION = 1.0 + + attr_accessor :out_file, :scaner, :code_to_inject + + def initialize + @do_64bit = false + @frameworks = [] + @framework_paths = [] + @headers = [] + @import_directives = "#import <Foundation/Foundation.h>\n" + @informal_protocols = [] + @classes = [] + @scaner = HeaderScaner.new + @scaner.do_64bit = @do_64bit + @umbrella_framework = nil + @handled_dependencies = [] + + # link to foundation framework by default + @compiler_flags = "-framework Foundation" + end + + def do_64bit + return @do_64bit + end + + def do_64bit= (do_64bit) + @do_64bit = do_64bit + @scaner.do_64bit = do_64bit + end + + def collect + scaner.scan(@frameworks, @headers) + @classes = scaner.classes + @informal_protocols = scaner.protocols + end + + def handle_framework (framework, sub_framework = false, parent_framework = nil) + val = framework.name + path = framework_path(val) + + raise "Can't locate framework '#{val}'" if path.nil? + @framework_paths << File.dirname(path) + raise "Can't find framework '#{val}'" if path.nil? + + parent_path, name = path.scan(/^(.+)\/(\w+)\.framework\/?$/)[0] + + if @private + headers_path = File.join(path, "PrivateHeaders") + raise "Can't locate private framework headers at '#{headers_path}'" unless File.exist?(headers_path) + + headers = Dir.glob(File.join(headers_path, "**", "*.h")) + public_headers_path = File.join(path, "Headers") + public_headers = if File.exist?(public_headers_path) + HeaderScaner::CPPFLAGS << " -I#{public_headers_path} " + Dir.glob(File.join8(headers_path, "**", "*.h")) + else + [] + end + else + headers_path = File.join(path, "Headers") + raise "Can't locate public framework headers at '#{headers_path}'" unless File.exist?(headers_path) + public_headers = headers = Dir.glob(File.join(headers_path, "**", "*.h")) + end + + # We can't just "#import <x/x.h>" as the main Framework header might not include _all_ headers. + # So we are tricking this by importing the main header first, then all headers. + header_basenames = (headers | public_headers).map do |x| + x.sub(/#{headers_path}\/*/, "") + end + + if idx = header_basenames.index("#{name}.h") + header_basenames.delete_at(idx) + header_basenames.unshift("#{name}.h") + end + + if sub_framework + pp = framework_path(parent_framework.name) + tmp, pname = pp.scan(/^(.+)\/(\w+)\.framework\/?$/)[0] + @import_directives << "#import <#{pname}/#{pname}.h>" + else + @import_directives << header_basenames.map do |x| + "#import <#{name}/#{x}>" + end.join("\n") + end + + @import_directives << "\n" + + @compiler_flags ||= "-F\"#{parent_path}\" -framework #{name}" + @cpp_flags ||= "" + @cpp_flags << "-F\"#{parent_path}\" " + + headers.each do |header| + header_file = HeaderFile.new + header_file.path = header + header_file.name = File.basename(header, File.extname(header)) + header_file.framework = framework.name + + framework.headers << header_file + end + + # Memorize the dependencies. + @dependencies = DStepGenerator.dependencies_of_framework(path) + + handle_sub_frameworks(framework) + end + + def handle_sub_frameworks (framework) + path = framework_path(framework.name) + frameworks_path = File.join(path, "Frameworks") + frameworks = Dir.glob(File.join(frameworks_path, "*.framework")) + + frameworks.each do |f| + add_framework(f, true, framework) + end + end + + def framework_path (val) + return val if File.exist?(val) + + val += ".framework" unless /\.framework$/.match(val) + paths = ["/System/Library/Frameworks", "/Library/Frameworks", "#{ENV['HOME']}/Library/Frameworks"] + paths << "/System/Library/PrivateFrameworks" if @private + + paths.each do |dir| + path = File.join(dir, val) + return path if File.exist?(path) + end + + return nil + end + + def umbrella_framework (val) + + unless val == "" || val.nil? + @umbrella_framework = val + + unless @compiler_flags.nil? || @compiler_flags == "" + i = @compiler_flags.index("-framework") + @compiler_flags = @compiler_flags[0 .. i - 1] + "-framework #{val}" + else + @compiler_flags = "-framework #{val}" + end + end + end + + def self.dependencies_of_framework (path) + @dependencies ||= {} + name = File.basename(path, ".framework") + path = File.join(path, name) + deps = @dependencies[path] + + if deps.nil? + deps = `otool -L "#{path}"`.scan(/\t([^\s]+)/).map do |m| + dpath = m[0] + next if File.basename(dpath) == name + next if dpath.include?("PrivateFrameworks") + next unless dpath.sub!(/\.framework\/Versions\/\w+\/\w+$/, "") + dpath + ".framework" + end.compact + + @dependencies[path] = deps + end + + return deps + end + + def compile_and_execute (code, enable_64bit = false, clean_when_fail = false) + compiler_line = "gcc " + src = File.new(unique_tmp_path("src", ".m", "/Users/doob/development/eclipse_workspace/dstep/bindings"), "w") + src << code + src.close + + arch_flag = if enable_64bit + "-arch x86_64 -arch ppc64" + else + "-arch i386 -arch ppc" + end + + compiler_line << arch_flag + + bin = unique_tmp_path "bin", "", "/Users/doob/development/eclipse_workspace/dstep/bindings" + log = unique_tmp_path "log", ".txt", "/Users/doob/development/eclipse_workspace/dstep/bindings" + + line = "#{compiler_line} -o #{bin} #{src.path} #{@compiler_flags} 2>#{log}" + + unless system(line) + msg = "Cannot compile Objective-C code ...aborting\nCommand was: #{line}\n\nLog:\n#{File.read(log)}\n\n" + $stderr << msg + File.delete src.path if clean_when_fail + raise msg + end + + result = `#{bin}` + + unless $?.success? + raise "Cannot execute compiled Objective-C code ... aborting\nCommand was: #{line}\nBinary is: #{bin}" + end + + File.delete bin + File.delete log + File.delete src.path + + return result + end + + def unique_tmp_path (base, extension = "", dir = Dir.tmpdir) + i = 0 + loop do + path = File.join(dir, "#{base}-#{Process.pid}-#{i}#{extension}") + return path unless File.exists?(path) + i += 1 + end + end + + def add_header (path) + header = HeaderFile.new + header.path = path + header.name = File.basename(path, File.extname(path)) + @import_directives << "#include <#{path}>\n" + @headers << header + end + + def add_framework (name, sub_framework = false, parent_framework = nil) + framework = Framework.new + framework.name = name + handle_framework(framework, sub_framework, parent_framework) + #@import_directives << "#import <#{framework.name}/#{framework.name}.h>\n" + @frameworks << framework + end + + def collect_header_types (header, enable_64bit) + types = [] + + header.cftypes.each do |cftype| + types << cftype[:type] + end + + header.constants.each do |constant| + types << constant.stripped_return_type + end + + header.structs.each do |struct| + types << struct[:name] + + struct[:members].each do |member| + types << member[:declaredType] + end + end + + header.typedefs.each do |typedef| + types << typedef.stripped_return_type + end + + header.functions.each do |function| + types << function.stripped_return_type + + function.args.each do |arg| + types << arg.stripped_return_type + end + end + + types + end + + def collect_classes_types (enable_64bit) + types = [] + + @classes.each do |clazz, value| + value.methods.each do |method| + types << method.stripped_return_type + + method.args.each do |arg| + types << arg.stripped_return_type + end + end + end + + types + end + + def collect_informal_protocols_types (enable_64bit) + types = [] + + @informal_protocols.each do |name, methods| + methods.each do |method| + types << method.stripped_return_type + + method.args.each do |arg| + types << arg.stripped_return_type + end + end + end + + types + end + + def resolve_header_types (header, enable_64bit, resolved_types, x) + i = x + + header.cftypes.each do |cftype| + cftype[enable_64bit ? :type64 : :type] = resolved_types[i] + i += 1 + end + + header.constants.each do |constant| + constant.resolved_type = resolved_types[i] unless enable_64bit + constant.resolved_type64 = resolved_types[i] if enable_64bit + i += 1 + end + + header.structs.each do |struct| + struct[enable_64bit ? :type64 : :type] = resolved_types[i] + i += 1 + + struct[:members].each do |member| + member[enable_64bit ? :type64 : :type] = resolved_types[i] + i += 1 + end + end + + header.typedefs.each do |typedef| + typedef.resolved_type = resolved_types[i] unless enable_64bit + typedef.resolved_type64 = resolved_types[i] if enable_64bit + i += 1 + end + + header.functions.each do |function| + function.resolved_type = resolved_types[i] unless enable_64bit + function.resolved_type64 = resolved_types[i] if enable_64bit + i += 1 + + function.args.each do |arg| + arg.resolved_type = resolved_types[i] unless enable_64bit + arg.resolved_type64 = resolved_types[i] if enable_64bit + i += 1 + end + end + + i + end + + def resolve_methods_types (enable_64bit, resolved_types, x) + i = x + + @classes.each do |clazz, value| + value.methods.each do |method| + method.resolved_type = resolved_types[i] unless enable_64bit + method.resolved_type64 = resolved_types[i] if enable_64bit + i += 1 + + method.args.each do |arg| + arg.resolved_type = resolved_types[i] unless enable_64bit + arg.resolved_type64 = resolved_types[i] if enable_64bit + i += 1 + end + end + end + + i + end + + def resolve_informal_protocols_types (enable_64bit, resolved_types, x) + i = x + + @informal_protocols.each do |name, methods| + methods.each do |method| + method.resolved_type = resolved_types[i] unless enable_64bit + method.resolved_type64 = resolved_types[i] if enable_64bit + i += 1 + + method.args.each do |arg| + arg.resolved_type = resolved_types[i] unless enable_64bit + arg.resolved_type64 = resolved_types[i] if enable_64bit + i += 1 + end + end + end + + i + end + + def resolve_types (enable_64bit = false) + code = "#include <stdio.h>\n" + code << "#import <objc/objc-class.h>\n" + + # @dependencies.each do |f| + # # get the framework name + # i = f.index('.') + # str = f[0 ... i] + # i = str.rindex('/') + # str = str[i + 1 .. -1] + # + # code << "#import <#{str}/#{str}.h>\n" + # end + + code << @import_directives + types = [] + + code << @code_to_inject + ";\n" unless @code_to_inject.nil? + + @frameworks.each do |framework| + framework.headers.each do |header| + + header.imports do |import| + code << "#import <#{import}.h>\n" + end + + types << collect_header_types(header, enable_64bit) + end + end + + @headers.each do |header| + types << collect_header_types(header, enable_64bit) + end + + types << collect_classes_types(enable_64bit) + types << collect_informal_protocols_types(enable_64bit) + + code << "int main ()\n{\n" + types.flatten! + + types.each do |type| + code << ' printf("%s\n", ' + "@encode(#{type}));\n" + end + + code << " return 0;\n}" + + resolved_types = [] + + compile_and_execute(code, enable_64bit).split("\n").each do |line| + resolved_types << line + end + + i = 0 + + @frameworks.each do |framework| + framework.headers.each do |header| + i = resolve_header_types(header, enable_64bit, resolved_types, i) + end + end + + @headers.each do |header| + i = resolve_header_types(header, enable_64bit, resolved_types, i) + end + + i = resolve_methods_types(enable_64bit, resolved_types, i) + i = resolve_informal_protocols_types(enable_64bit, resolved_types, i) + end + + def generate_header (xml, header) + xml.file :name => header.name do + header.imports.each do |import| + xml.import import + end + + header.defines.each do |define| + xml.define define + end + + header.cftypes.each do |cftype| + xml.cftype cftype + end + + header.constants.each do |constant| + xml.constant :name => constant.name, :declaredType => constant.return_type, :type => constant.resolved_type, :type64 => constant.resolved_type64, :const => constant.const + end + + header.enums.each do |enum| + xml.enum :name => enum[:name] do + enum[:members].each do |member| + xml.member member + end + end + end + + header.structs.each do |struct| + xml.struct :name => struct[:name], :type => struct[:type], :type64 => struct[:type64] do + struct[:members].each do |member| + xml.member member + end + end + end + + header.typedefs.each do |typedef| + xml.typedef :name => typedef.name, :declaredType => typedef.return_type, :type => typedef.resolved_type, :type64 => typedef.resolved_type64, :const => typedef.const + end + + header.functions.each do |function| + xml.function :name => function.name, :inline => function.inline?, :variadic => function.variadic? do + function.args.each do |arg| + xml.arg :name => arg.name, :declaredType => arg.return_type, :type => arg.resolved_type, :type64 => arg.resolved_type64, :const => arg.const, :typeModifier => arg.type_modifier + end + + xml.returnValue :declaredType => function.return_type, :type => function.resolved_type, :type64 => function.resolved_type64, :const => function.const, :typeModifier => function.type_modifier + end + end + end + end + + def generate_classes (xml) + @classes.each do |clazz, value| + xml.class :name => clazz, :parent => value.parent, :file => value.file do + value.methods.each do |method| + xml.method :selector => method.selector, :classMethod => method.class_method?, :variadic => method.variadic? do + method.args.each do |arg| + xml.arg :name => arg.name, :declaredType => arg.return_type, :type => arg.resolved_type, :type64 => arg.resolved_type64, :const => arg.const, :typeModifier => arg.type_modifier + end + + xml.returnValue :declaredType => method.return_type, :type => method.resolved_type, :type64 => method.resolved_type64, :const => method.const, :typeModifier => method.type_modifier + end + end + end + end + end + + def generate_informal_protocols (xml) + @informal_protocols.each do |name, methods| + xml.informalProtocol :name => name do + methods.each do |method| + xml.method :selector => method.selector, :classMethod => method.class_method?, :variadic => method.variadic? do + method.args.each do |arg| + xml.arg :name => arg.name, :declaredType => arg.return_type, :type => arg.resolved_type, :type64 => arg.resolved_type64, :const => arg.const, :typeModifier => arg.type_modifier + end + + xml.returnValue :declaredType => method.return_type, :type => method.resolved_type, :type64 => method.resolved_type64, :const => method.const, :typeModifier => method.type_modifier + end + end + end + end + end + + def generate + resolve_types + resolve_types(true) if @do_64bit + + file = STDOUT if @out_file == nil + file = File.open @out_file, "w" unless @out_file == nil + + xml = XmlMarkup.new(:target => file, :indent => 4) + xml.instruct! + + xml.dstep :xmlns => "http://www.dsource.org/projects/dstep", "xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance", "xsi:schemaLocation" => "http://www.dsource.org/projects/dstep/trunk/scripts/dstep.xsd" do + @frameworks.each do |framework| + xml.framework :name => framework.name do + framework.headers.each do |header| + generate_header(xml, header) + end + end + end + + @headers.each do |header| + generate_header(xml, header) + end + + generate_classes(xml) + generate_informal_protocols(xml) + end + + file.close unless file == STDOUT + end +end + +def die (*msg) + $stderr.puts msg + exit 1 +end + +if __FILE__ == $0 + dstep_gen = DStepGenerator.new + + OptionParser.new do |opts| + opts.banner = "Usage: #{File.basename(__FILE__)} [options] <headers...>" + opts.separator "" + opts.separator "Options:" + + opts.on("-f", "--framework FRAMEWORK", "Generate metadata for the given framework.") do |opt| + dstep_gen.add_framework(opt) + end + + opts.on("-u", "--umbrella FRAMEWORK", "Link againts the given umbrella framework.") do |opt| + dstep_gen.umbrella_framework(opt) + end + + opts.on(nil, "--64-bit", "Write 64-bit annotations.") do + dstep_gen.do_64bit = true + end + + opts.on("-o", "--output FILE", "Write output to the given file.") do |opt| + die "Output file can't be specified more than once" if dstep_gen.out_file + dstep_gen.out_file = opt + end + + # opts.on("-d", "--output-dir PATH", "Write ouptut to the given paht, use this with the --framework option") do |opt| + # die "Output directory can't be specified more than once" if dstep_gen.out_dir + # dstep_gen.out_dir = opt + # end + + opts.on("-C", "--code CODE", "Add code") do |opt| + dstep_gen.code_to_inject = opt + end + + help_msg = "Use the `-h' flag or for help." + + opts.on("-h", "--help", "Show this message.") do + puts opts, help_msg + exit + end + + opts.on('-v', '--version', 'Show version.') do + puts DStepGenerator::VERSION + exit + end + + opts.separator "" + + if ARGV.empty? + die opts.banner + else + #begin + opts.parse!(ARGV) + + ARGV.each do |header| + dstep_gen.add_header(header) + end + + dstep_gen.collect + dstep_gen.generate + # rescue => e + # msg = e.message + # msg = "Internal error" if msg.empty? + # + # die msg, opts.banner, help_msg + # end + end + end +end