Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve Objective-C support #4777

Merged
merged 68 commits into from
Dec 3, 2024
Merged

Improve Objective-C support #4777

merged 68 commits into from
Dec 3, 2024

Conversation

LunaTheFoxgirl
Copy link
Contributor

@LunaTheFoxgirl LunaTheFoxgirl commented Nov 12, 2024

This pull-request aims to improve the Objective-C support of LDC. This PR additionally changes the Objective-C generation system to only support the Objective-C 2.0 ABI, as very little documentation of older ABIs have survived.

A good chunk of this code is more or less taken from the OpenD implementation, but I'm already working on cleaning parts up as I go. I am looking into how feasible it'll be to make extern(Objective-C) classes export neccesary ABI for Objective-C code to be able to call it as well, which mainly involves metadata generation.

TODO List:

  • Classes
    • ClassRO Tables
    • Class List Tables
    • Swift Stub Class Tables (@swift keyword)
    • Dynamic Class Casting
  • Methods
    • Method List Tables
    • Selector Reference Tables
  • Protocols
    • Protocol List Table
    • Dynamic Protocol Casting
  • Instance Variables
    • Generate instance variables and offsets

Sorry, something went wrong.

@LunaTheFoxgirl
Copy link
Contributor Author

Question: in this implementation I wish to add another UDA to ldc.attributes called @swift.

Idea there is to implement support for binding Swift interop class stubs as well, which have unique behaviour that would require this new attribute.

Essentially Swift may declare Objective-C classes in the __objc_stubs section with an isa field pointing to address 0x1. Followed by a function pointer that would need to be invoked to instantiate the class definition, via the objc_loadClassRef helper function.

This would allow interop with Swift code using bridging headers and wouldn't be too much extra work.

Is it okay if I add that to this PR?

@thewilsonator
Copy link
Contributor

Is it okay if I add that to this PR?

Yes.

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
@adamdruppe
Copy link
Contributor

I am looking into how feasible it'll be to make extern(Objective-C) classes export neccesary ABI for Objective-C code to be able to call it as well, which mainly involves metadata generation.

this already works in opend so if you copy the impl it should work here too; that's how the final iphone demo from my blog worked, as well as simpledisplay's cocoa implementation, and to triple check i just did one more test:

@interface Thing : NSObject
   - (void) test
@end 

int main() {
  [[[Thing alloc] init] test];
}

and then the D

extern(Objective-C)
class Thing : NSObject {
  void test() @selector("test") {
        printf("here\n");
   }
}

and all worked.

of course, possible i missed some details that won't emerge until you get into more features, but still a lot already works in the opend world.

@adamdruppe
Copy link
Contributor

BTW something i think would be kinda cool is a kind of import objective-c

dstep can do a good amount of that already, and the objective c parts look easier to parse and interface with than regular import c

so im p sure it doable with a week or two of work

@LunaTheFoxgirl
Copy link
Contributor Author

BTW something i think would be kinda cool is a kind of import objective-c

dstep can do a good amount of that already, and the objective c parts look easier to parse and interface with than regular import c

so im p sure it doable with a week or two of work

Yeah, I'm thinking of making an Objective-C code generator once I get the compiler support up and running.
Got protocols working now, might implement categories as well. Next up is codegen for swift stub classes.

@thewilsonator
Copy link
Contributor

(once this is good to go) Could you please open a PR to DMD with all of the frontend/druntime changes? (Just stub out anything needs anything extra to work)

@LunaTheFoxgirl
Copy link
Contributor Author

(once this is good to go) Could you please open a PR to DMD with all of the frontend/druntime changes? (Just stub out anything needs anything extra to work)

Sure, will do. However I think I'll reduce the scope of this PR slightly to just be the core objective-c and Swift stub class support. Given that some existing Apple platforms don't align neatly with the code I was referencing I'm doing a pretty big refactor of the objective-c data struct generation to be compatible with all of the Apple sub architectures supported.

@LunaTheFoxgirl
Copy link
Contributor Author

cc @adamdruppe once I finish this PR you may want to back integrate it into OpenD's LDC fork. Your current implementation is broken on quite a few platforms and in some cases may crash the Xcode linker

@adamdruppe
Copy link
Contributor

plz write up some notes as you go, as it might also apply back to dmd's separate implementation (and the abi spec on the website) as well. depends on if it was just me bugging it up or if the original was wrong too.

much of it came from reverse engineering the output on a x86 Mac so no surprise they'd be some differences (one we all found early is the arm objc_msgSend is slightly different on arm), then i just sucked at ldc code so prolly a lot of impl bugs too lol.

@LunaTheFoxgirl
Copy link
Contributor Author

plz write up some notes as you go, as it might also apply back to dmd's separate implementation (and the abi spec on the website) as well. depends on if it was just me bugging it up or if the original was wrong too.

much of it came from reverse engineering the output on a x86 Mac so no surprise they'd be some differences (one we all found early is the arm objc_msgSend is slightly different on arm), then i just sucked at ldc code so prolly a lot of impl bugs too lol.

Yeah my recommendation there is to look at the actual objective-c runtime source. The data structures relevant are listed there https://github.com/apple-oss-distributions/objc4 as well as the Swift interop documentation.

I'm adding the finishing touches to this PR tonight, and will mark it as ready to merge then.

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
@LunaTheFoxgirl LunaTheFoxgirl marked this pull request as ready for review November 22, 2024 05:32
@adamdruppe
Copy link
Contributor

I'll reread the whole thing when I get another chunk of time free, idk if it'll be today or tomorrow, but I'll let you know when I do.

My biggest concern is just if it works and that last test I did with it passing was a good sign!

@LunaTheFoxgirl
Copy link
Contributor Author

@kinke anything left to solve? All the tests succeed (minus the ones that are either broken for unrelated reasons and the ones where you've run out of compute credits)

@LunaTheFoxgirl
Copy link
Contributor Author

Also it seems my refactor somehow fixed all the random asserts that was happening.

@LunaTheFoxgirl
Copy link
Contributor Author

I think that's everything?
Sorry if I'm a bit pushy, I've been working unholy hours on this the past few days. I'm very exhausted ^^;

@kinke
Copy link
Member

kinke commented Nov 28, 2024

Yeah that's it from my side at least, thanks. :)

@p0nce
Copy link
Contributor

p0nce commented Nov 29, 2024

This is super valuable work! Thanks

@LunaTheFoxgirl
Copy link
Contributor Author

Is it possible that this could be merged for the next release or is there further things I need to sort out? I'm slightly burnt out after staring at aarch64 disassembly for 50+ hours the past few days @thewilsonator @kinke @jacob-carlborg

@kinke
Copy link
Member

kinke commented Nov 29, 2024

I think we can have this in v1.40 final, which I wanna release in December, regardless whether DMD v2.110 is finalized or not by then.

In that regard, feel free to add a changelog entry in CHANGELOG.md yourself, to let users know about this.

@LunaTheFoxgirl
Copy link
Contributor Author

I think we can have this in v1.40 final, which I wanna release in December, regardless whether DMD v2.110 is finalized or not by then.

In that regard, feel free to add a changelog entry in CHANGELOG.md yourself, to let users know about this.

Okay, done!

}

/// Returns: the number of times `cd` has been declared as optional.
private int declaredAsSwiftStubCount(ClassDeclaration cd, Scope* sc) const
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this exactly the same as declaredAsOptionalCount, it just takes a different kind of declaration? Can't that be reused?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possibly but I don't know my way around this codebase very well. Takes a long time to figure out how anything works. Also the Swift stub UDA only applies to classes, as it affects how the isa pointer/classref is dereferenced.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, isn't declaredAsOptionalCount what you copy-pasted? Seems very likely, especially since the documentation is now wrong, it says: as optional.

Copy link
Contributor Author

@LunaTheFoxgirl LunaTheFoxgirl Nov 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I copied it and modified it as needed since I didn't see an obvious way of checking for UDAs otherwise.

I'll look into making it a reusable function after I get some rest.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like you can replace ClassDeclaration with Dsymbol for the cd parameter, given it a more generic name, like "countUdas" and pass in the UDA identifier as an additional argument.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, something like that

if (linkNoObjc)
return;

args.push_back("-lobjc");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is different behavior compared to DMD.

// Dynamic casting in Objective-C works differently from D.
// We call objc_opt_isKindOfClass to get a bool defining
// whether the cast is valid, if it is then we go ahead.
if (DtoIsObjcLinkage(_to)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see a test for this. This is not implemented in upstream.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm avoiding writing my own tests, would prefer is someone else picks that up. I simply don't know this testing system very well.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's pretty straightforward. Just add a new file here https://github.com/ldc-developers/ldc/tree/master/tests/dmd/runnable and it will be picked up automatically. You can take a look at one of the existing Objective-C tests, like this one: https://github.com/ldc-developers/ldc/blob/master/tests/dmd/runnable/objc_call.d.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll let someone else decide if the tests are necessary or not.

@kinke
Copy link
Member

kinke commented Dec 3, 2024

Landing this eagerly for upcoming beta5. Thx all!

@kinke kinke merged commit 82878ef into ldc-developers:master Dec 3, 2024
16 of 20 checks passed
@s-ludwig
Copy link
Contributor

s-ludwig commented Dec 3, 2024

Very nice, thanks @LunaTheFoxgirl! That will lower the bar for macOS/iOS development considerably. It's a shame that the money from the bounty has been lost by now.

@LunaTheFoxgirl
Copy link
Contributor Author

LunaTheFoxgirl commented Dec 3, 2024

Very nice, thanks @LunaTheFoxgirl! That will lower the bar for macOS/iOS development considerably. It's a shame that the money from the bounty has been lost by now.

It is what it is. I've already begun working on dub packages to cover API bindings to Apple APIs, which should lower the barrier further.

That being said, I'm doing it by hand to make sure all of the API has appropriate ddoc documentation as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants