discuss-gnustep
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Swift calling into GNUStep Progress


From: Ondrej Florian
Subject: Re: Swift calling into GNUStep Progress
Date: Fri, 18 Aug 2023 01:03:39 +0200
User-agent: GNUMail (Version 1.4.0)

I am just wondering, would it make sense to make this part of 
https://github.com/onflapp/libs-steptalk ?
I was able to get quite far with it. 
It is even possible to create GUI app now (see 
https://github.com/onflapp/libs-steptalk/blob/master/Examples/AppKit/app.st)

On 2023-08-11 03:35:22 +0200 dr_clow@me.com wrote:

> Hello Friends,
> I just wanted to give you a little update regarding my progress. All of this 
> can also be viewed on my GitHub at 
> <https://github.com/austintatiousness/GNUStepSwiftBridge/>
> NSObjects and Subclassing
> I have two classes that help bridge Swift classes to Objective-C (1) 
> GNUStepNSObjectWrapper and (2) GNUStepNSObjectSubclassConstructor. The former 
> is used to simply wrap Objective-C NSObjects and the latter is used to 
> register subclasses with the GNUStep Objective-C runtime. All classes crated 
> with GNUStepNSObjectSubclassConstructor get a special Ivar '___swiftPtr ' 
> intended to hold a reference back the Swift. Once you use 
> GNUStepNSObjectSubclassConstructor to create a new subclass, you can then 
> wrap  it using GNUStepNSObjectWrapper.
> I've actually had to do this already, within the Swift AppKit API, I've 
> subclassed NSButton as "NSButtonForSwift" but expose it as NSButton to the 
> Swift API. This allows me to use closures instead of target/selectors.
> I don not currently like how I am using ___swiftPtr to hold a reference to 
> the Swift object created with any class registered with 
> GNUStepNSObjectSubclassConstructor, but it is the best solution I could come 
> up keep track of who owns the Objective-C object.
> I understand that on Apple platforms, Swift actually emits Objective-C 
> compatible Swift objects when @objc or inheriting from NSObject occurs, which 
> allows them casted on either side correctly.
> NSString, NSArray and NSDictionary are all going to be problematic types 
> because there is currently no way to share storage like in Apple's Swift 
> implementation. I think that some work needs to be done here to figure this 
> out. That is beyond my abilities. For now, I am just going to be copying data 
> between them.
> 
> Currently, variables for like NSButton 'var title: String' take a String type 
> and I just convert between NSString when getting and setting (I wish we could 
> offer both, but Swift doesn't allow that) ; but when we have functions like 
> "func setTitle(string: String)" I plan to also offer "func setTitle(string: 
> NSString)" so we do not have to do any copying.
> Additionally, NSArray and NSDictionary are only going to be able to store 
> Objective-C types. On Apple Platforms,
> 
> Regarding Sending Messages / Calling Method: I've to figure out how to use 
> objc_msgSend and objc_msgSend_stret from Swift, so instead I am now using 
> class_getMethodImplementation. Which has been incredibly successful. I 
> created some helper swift functions called objc_smart_getIMP to aid in 
> setting these up better.
> API Bridging Update:
> I've gotten some of the the GUI objects going: NSWindow, NSButton, NSImage, 
> NSImageView, NSFont, NSColor. You can get and set frames. It's pretty cool.
> 
> 
> 
> 
> 
>> On Aug 8, 2023, at 8:56 PM, dr_clow@me.com wrote:
> 
>> UPDATE: SUCCESS!
> 
>> Since I sent that last email. I had a little break through.
>> Then GNUStep ObjC function class_getMethodImplementation (Apple also has 
>> function class_getMethodImplementation_stret that is evidently not used in 
>> GNUStep and is just a stub) and allows one to look up the C function that 
>> is precisely used with the function.
>> So I created a nice Swift version of this lookup. public func 
>> objc_smart_getIMP<T>(object: GNUStepNSObjectWrapper, selector: String) -> 
>> T? {
>>      let c = object_getClass(&object._nsobjptr!.pointee)
>>      let v = class_getMethodImplementation(c, sel_getUid(selector))
>>      let rt: T? = unsafeBitCast(v, to: T.self)
>>      return rt
>> }
> 
>> And then in the the var frame property of my NSView wrapper the following 
>> public var frame: CGRect {
>>              get {
>>                      var imp:  (@convention(c) (id, SEL) -> (CGRect))? = 
>> objc_smart_getIMP(object: self, selector: "frame")
>>                      if let rtn = imp?(&self._nsobjptr!.pointee, 
>> sel_getUid("frame")) {
>>                              return rtn
>>                      }
>>                      return .init(x: 0, y: 0, width: 0, height: 0)
>>              }
>>              set {
>>                      guard let selfPtr = self._nsobjptr else {return}
>>                      let _: Any? = objc_smart_sendMessage(object: self, 
>> selector: 
>> "setFrame:", value1: newValue)
>>              }
>> }
> 
>> RESULT: It worked!
> 
>> When Swift Macros are working on Linux, this could be a Macro that 
>> generates everything we need. Maybe @GNUStepPropertyWrapper("frame", 
>> "getFrame") We could also have @GNUStepMethodWrapper that automatically 
>> wraps the method callers.
> 
> 
>>> On Aug 8, 2023, at 7:48 PM, dr_clow@me.com wrote:
> 
>>> One of the issues that I am identifying is trying to manage the functions 
>>> `objc_msgSend ` and `objc_msgSend_stret `. I did a write up on my GitHub 
>>> for this project, and (as of right now) am trying to come up with 
>>> solutions to getting the right memory out of calls to `objc_msgSend ` and 
>>> `objc_msgSend_stret `.
>>> Basically I need a way to make objc_msgSend_stret more generic.
>>> Also what datatype size does objc_msgSend_stret service vs objc_msgSend. I 
>>> am assuming, that objc_msgSend is good for pointers and datives that are 
>>> the size of pointers?
> 
>>> You can view my evolving discussion with myself at 
>>> https://github.com/austintatiousness/GNUStepSwiftBridge
> 
>>> - Austin
>>> objc_msgSend and objc_msgSend_stret
> 
>>> objc_msgSend() is a c function used by the Objective-C runtime to send 
>>> messages. Unfortunately it has a variable arguments which cannot be 
>>> imported into Swift. To overcome this, I have created some specialized 
>>> versions of of objc_msgSend that have different number of arguments. These 
>>> can be found in the ObjCSwiftInterop.c file.
> 
>>> Ultimately, I would like a swift version of this which more intelligently 
>>> decides how to map the values. I started work on this, in the AppKit.swift 
>>> file called func objc_smart_sendMessage<T>(object: 
>>> NSObjectGNUStepSwiftBridge, selector: String,  value1: Any?, value2: Any?, 
>>> value3: Any?, value4: Any?, value5: Any?, value6: Any?, value7: Any?, 
>>> value8: Any?, value9: Any?) -> T?
> 
>>> This solves the problem that we have to send messages of arbitrary side, 
>>> but it does not solve the issues around casting, and that two separate 
>>> functions objc_msgSend and objc_msgSend_stret depending on which type we 
>>> are going to get back from.
> 
>>>   
>>> <https://github.com/austintatiousness/GNUStepSwiftBridge/blob/main/README.md#casting>CASTING
> 
>>> Just some thoughts and notes: Please correct me where I am wrong or 
>>> misunderstanding.
> 
>>> objc_msgSend and objc_msgSend_stret are very difficult functions to 
>>> properly import into Swift. objc_msgSend is used for messages that return 
>>> Objective-C classes, and simple data values that have the same size as id 
>>> . From what I understand, both require the the function to be properly 
>>> cast to work and because they are implemented in assembly and they are 
>>> doing some magic with the registers to properly get the result. 
>>> objc_msgSend returns void* but objc_msgSend_stret on GNUStep's runtime 
>>> returns void. Before you can use objc_msgSend_stret, you have to cast it 
>>> to a function that returns the value you are expecting.
> 
>>> UIView *view = [[NSView alloc] initWithFrame:NSRectZero];
> 
>>> NSRect (*sendRectFn)(id receiver, SEL operation);
>>> sendRectFn = (NSRect(*)(id, SEL))objc_msgSend_stret;
>>> NSRect frame = sendRectFn(view, @selector(frame));
>>> Idea 1: THIS DOES NOT WORK. I really have no idea what I am doing.
> 
>>> void* forSwift_objcMsgSend_stret(id ID, SEL cmd, int64_t returnSize) {
>>>     void* itemArr = malloc(returnSize);
>>>     void* (*sendRectFn)(id receiver, SEL operation);
>>>     sendRectFn = (void(*)(id, SEL))objc_msgSend_stret;
>>>     itemArr = sendRectFn(ID, cmd);
>>>     return itemArr;
>>> }
>>> What we need to be able to do is express this in Swift using
> 
>>> I do not know how the objc_msgSend_stret knows what the returned data type 
>>> should be.
> 
>>>> On Aug 8, 2023, at 4:56 AM, Gregory Casamento 
>>>> <greg.casamento@gmail.com> wrote:
> 
>>>> M A,
> 
>>>> I guess you could consider the issues and such on github as a todo list. 
>>>> You are welcome to take on any tasks on there if you like.  Each repo has 
>>>> it's own "Issues" tab where you can see what issues are outstanding.   Or 
>>>> if you can think of features that you think might be useful, please 
>>>> discuss it here and we can all work together to make it happen if it 
>>>> sounds reasonable.
> 
>>>> Wrapping an ObjC class around swift sounds interesting, though most 
>>>> people go the other way around these days... Swift->ObjC.
> 
>>>> Yours, GC
> 
>>>> On Mon, Aug 7, 2023 at 1:58 PM M A <teammember0x01@gmail.com 
>>>> <mailto:teammember0x01@gmail.com>> wrote:
>>>>> A to do list would be a great edition to this project's website.
> 
>>>>> One thing I would add it making a program that can wrap an Objective-c 
>>>>> class around Swift code. There are just too many classes and methods to 
>>>>> do it all by hand.
> 
>>>>>> On Aug 7, 2023, at 1:51 PM, Gregory Casamento <greg.casamento@gmail.com 
>>>>>> <mailto:greg.casamento@gmail.com>> wrote:
>>>>>> > I am extremely impressed!!!  This is great!  Please let me know if I 
>>>>>> can help in any way.
>>>>>> > GC
>>>>>> > On Mon, Aug 7, 2023 at 10:35 AM <dr_clow@me.com 
>>>>>> <mailto:dr_clow@me.com>> wrote:
>>>>>> Gregory, > > Thank you. I over last night, I was able to solve almost 
>>>>>> all the issues with calling into GNUStep's AppKit and Foundation. I've 
>>>>>> been able to set up buttons that respond to selectors, create objects 
>>>>>> at runtime and register them with the runtime. Right now, I am working 
>>>>>> on generalizing a sort of "Smart" version of obj_msgSend that allows me 
>>>>>> to not have to write a separate version of that handles each type 
>>>>>> parameters. As it is now, the only way I am getting it to work is to 
>>>>>> make a version of objc_msgSend that explicitly takes, for example, an 
>>>>>> NSRect, or id. > > This is probably because I just don't fully 
>>>>>> understand how pointers work in Swift. If anyone has any idea of how we 
>>>>>> can generalize the function, I would greatly appreciate it. Thanks!
>>>>>> > Below is a screen shot of a working app written in Swift. The button 
>>>>>> does work and does open the other window. It's pretty cool. You can see 
>>>>>> the code on my GitHub. It's messy still.  > > > <Image 8-7-23 at 
>>>>>> 9.33 AM.jpeg>
>>>>>> >> On Aug 6, 2023, at 7:59 PM, Gregory Casamento 
>>>>>> <greg.casamento@gmail.com <mailto:greg.casamento@gmail.com>> wrote:
>>>>>>> >> Hey, I just want you to know that this is VERY VERY cool!!!  Yours, 
>>>>>>> GC
>>>>>>> >> On Sun, Aug 6, 2023 at 12:05 PM <dr_clow@me.com 
>>>>>>> <mailto:dr_clow@me.com>> wrote:
>>>>>>> I have solved the NSWindow initializer issue. I didn't realize I was 
>>>>>>> passing Swift's Foundation.NSRect and not the C version. Sill haven't 
>>>>>>> solved the issues regarding adding new ObjC classes to the runtime at 
>>>>>>> runtime through Swift. Any ideas here would be appreciated.  >> >> The 
>>>>>>> image below is an GNUStep app written in Swift. The Menu is from the 
>>>>>>> GORM file from the Terminal (I had to start somewhere!)
>>>>>>> >> <Screenshot 2023-08-06 at 10.54.06 AM.png>
>>>>>>> >> >>> On Aug 5, 2023, at 9:03 PM, dr_clow@me.com 
>>>>>>> <mailto:dr_clow@me.com> wrote:
>>>>>>>> >>> I just wanted to update everyone on my progress and solicit some 
>>>>>>>> help if possible.
>>>>>>>> >>> State of my progress: >>> I've had a lot of success patching into 
>>>>>>>> GNUStep's libobjc2 C runtime from within Swift. I've been able to 
>>>>>>>> create NSWindows through Swift, call methods, et cetera. You can see 
>>>>>>>> my progress here 
>>>>>>>> https://github.com/austintatiousness/GNUStepSwiftBridge . This 
>>>>>>>> assumes that you're running this from within OnFlapp's GNUStep 
>>>>>>>> Desktop. >>> >>> Solution to objcSendMessage: >>> Because Swift 
>>>>>>>> doesn't allow variable argument parameters, I had to create various 
>>>>>>>> versions of objcSendMessage (e.g forSwift_objcSendMessage1, 
>>>>>>>> forSwift_objcSendMessage2, forSwift_objcSendMessage3) to accommodate 
>>>>>>>> various number of arguments. >>> >>> Problem 1: NSWindow 
>>>>>>>> initWithContentRect:styleMask:backing:defer
>>>>>>>> >>> 1) I am having trouble with the NSWindow.initWith… functions. I 
>>>>>>>> am sure that it is because of the way that I am casting all the 
>>>>>>>> values from Swift into to the C implementation. Either I just don't 
>>>>>>>> understand how the casting between Swift and C works OR I am just 
>>>>>>>> using the wrong variables.  I include a C version of the NSRect 
>>>>>>>> struct in my project. >>> >>> let  nsWindowClass = 
>>>>>>>> objc_getClass("NSWindow")
>>>>>>>> var allocatedObject = 
>>>>>>>> forSwift_objcSendMessage(&nsWindowClass!.pointee, 
>>>>>>>> sel_registerName("alloc"))
>>>>>>>> >>> var styleMask: UInt64 = 1 + 2 + 4
>>>>>>>> var backingStoreType: UInt64 = 0
>>>>>>>> var deferr: UInt8 = 0
>>>>>>>> var rect = NSRect(x: 200, y: 200, width: 300, height: 300)
>>>>>>>> >>> allocatedObject = 
>>>>>>>> forSwift_objcSendMessage4(&allocatedObject!.pointee, 
>>>>>>>> sel_registerName("initWithContentRect:styleMask:backing:defer:"), 
>>>>>>>> &rect, &styleMask, &backingStoreType, &deferr)
>>>>>>>> >>> I've tried several times to change the various integer types from 
>>>>>>>> UInt64 to UInt8 to no avail. >>> >>> Problem 2: Registering new 
>>>>>>>> classes  with the runtime. >>> This is the current state of the 
>>>>>>>> HelloWorld target: >>> >>> For reasons I cannot explain, I am able to 
>>>>>>>> allocate a new obj-c object with objc_allocateClassPair and then 
>>>>>>>> register it using objc_registerClassPair but when objc_getClass using 
>>>>>>>> the same class name that I registered, it returns nil.
>>>>>>>> >>> Any help would be appreciated. I am currently unable to make 
>>>>>>>> progress on adding delegates with out being able to register new ObjC 
>>>>>>>> classes with the runtime. >>> >>> Thanks!
>>>>>>> >> >> >> -- >> Gregory Casamento
>>>>>>> GNUstep Lead Developer / OLC, Principal Consultant
>>>>>>> http://www.gnustep.org <http://www.gnustep.org/> - 
>>>>>>> http://heronsperch.blogspot.com <http://heronsperch.blogspot.com/>
>>>>>>> https://www.patreon.com/bePatron?u=352392 - Become a Patron
>>>>>>> https://www.openhub.net/languages/objective_c - OpenHub standings
>>>>>> > > > -- > Gregory Casamento
>>>>>> GNUstep Lead Developer / OLC, Principal Consultant
>>>>>> http://www.gnustep.org <http://www.gnustep.org/> - 
>>>>>> http://heronsperch.blogspot.com <http://heronsperch.blogspot.com/>
>>>>>> https://www.patreon.com/bePatron?u=352392 - Become a Patron
>>>>>> https://www.openhub.net/languages/objective_c - OpenHub standings
> 
> 
> 
> 
>>>> --
>>>> Gregory Casamento
>>>> GNUstep Lead Developer / OLC, Principal Consultant
>>>> http://www.gnustep.org <http://www.gnustep.org/> - 
>>>> http://heronsperch.blogspot.com <http://heronsperch.blogspot.com/>
>>>> https://www.patreon.com/bePatron?u=352392 - Become a Patron
>>>> https://www.openhub.net/languages/objective_c - OpenHub standings
> 
> 
> 
>



reply via email to

[Prev in Thread] Current Thread [Next in Thread]