[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
>
>
>
>
- Re: Swift calling into GNUStep Progress, (continued)
- Re: Swift calling into GNUStep Progress, Gregory Casamento, 2023/08/06
- Re: Swift calling into GNUStep Progress, dr_clow, 2023/08/07
- Re: Swift calling into GNUStep Progress, Gregory Casamento, 2023/08/07
- Re: Swift calling into GNUStep Progress, M A, 2023/08/07
- Re: Swift calling into GNUStep Progress, dr_clow, 2023/08/07
- Re: Swift calling into GNUStep Progress, Gregory Casamento, 2023/08/08
- Re: Swift calling into GNUStep Progress, dr_clow, 2023/08/08
- Re: Swift calling into GNUStep Progress, dr_clow, 2023/08/08
- Re: Swift calling into GNUStep Progress, dr_clow, 2023/08/10
- Re: Swift calling into GNUStep Progress, Gregory Casamento, 2023/08/11
- Re: Swift calling into GNUStep Progress,
Ondrej Florian <=
- Re: Swift calling into GNUStep Progress, dr_clow, 2023/08/20