-
Notifications
You must be signed in to change notification settings - Fork 10.4k
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
[runtime] make swift_getFieldAt
callable from C / Swift
#15565
Conversation
swift_getFieldAt
callable from C / Swiftswift_getFieldAt
callable from C / Swift
We really don't want people to use |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think Mirror
is a preferred interface for reflection from Swift, you should probably look into using that instead of calling swift_getFieldAt
directly, it's using that method internally?...
@xedin Mirror is implemented in C++ but I guess we can try to refactor it to expose an underscored method that can be called. However in any case this will be part of the ABI forever so we should at least pretend to design it properly. |
@xedin as far as I know there is no way to use Mirrors to get a type's fields. If there is a way, I would much rather use that instead.
Hmm when I try |
*waves hands frantically* None of this "forward-declare a C function using only Swift" stuff is supported. That said, Mirror.children is exactly this. |
There are things Mirror can't do that people need to do and have been doing unofficially by parsing metadata, particularly asking for the If you wanted to use this API from your own code outside of the standard library, though, you should declare |
@jckarter Why can't we just add this functionality to |
I think that would require fairly significant investment in Mirror, which is kind of an evolutionary dead end in its current form as far as reflection ability goes. |
Another thing that might make it easier for non-standard-library clients to interpret the metadata here would be to have nominal type context descriptors directly carry a reference to the associated field metadata structure, instead of registering them in parallel. @xedin IIRC you tried that; did it not work out for some reason? |
Yes, I think it tried that but there was a problem related to circularity I think. |
@jckarter @jrose-apple @slavapestov @xedin Would you say the best way forward then would be to create a swift evolution proposal to add proper reflection from the standard library? |
@paulofaria I think @jckarter is trying to explore feasibility of adding this information into type context descriptors, if that doesn't work out we'd either have to accept your patch or figure out some other way to expose this information. |
Any news about this pull request? |
@swift-ci Please test |
Build failed |
@swift-ci Please test linux |
Build failed |
@swift-ci Please test Linux |
When do we expect to see this in an official Swift version? |
This was brought into the 4.2 branch in #17174 so it should be in 4.2 snapshots already, and eventually be in the official 4.2 release. |
For what it's worth in anyone ending up here this method of getting metadata is now unavailable as of the Swift 4.2 branch via #18746. Still trying to sort out of there's a new workaround to resurface this information. |
#18746 was not committed to the 4.2 branch. You can still use this entry point in Swift 4.2. Anyone relying on Swift 4.2's metadata layout will have a lot more work in general moving to Swift 5's final layouts. |
For Swift 5, do you have any suggestion if we still want to get the type's fields information as the previous version do? |
The field type metadata is directly referenced from the nominal type descriptor now. You can get the mangled type name from that data structure and feed it to the swift_getTypeByMangledNameInContext function with the struct or class's generic arguments in order to get the metadata pointer for the field type. |
I solved it, thanks for the help :) |
@jckarter I was able to get non-generic metadata by passing nil to |
The generic context argument should be a pointer to the ContextDescriptor for the type whose fields you're decoding. The
If you already have metadata for a specific instance of the type, you should be able to extract the type descriptor pointer and generic arguments directly out of that metadata object. In the runtime C++ code, |
Thank you for your fast response. Unfortunately, I still wasn't able to get it right. Is this still the layout for struct StructMetadataLayout : MetadataLayoutType {
var valueWitnessTable: UnsafePointer<ValueWitnessTable>
var kind: Int
var typeDescriptor: UnsafeMutablePointer<StructTypeDescriptor>
var genericArgumentVector: UnsafeRawPointer
} struct ClassMetadataLayout : MetadataLayoutType {
var valueWitnessTable: UnsafePointer<ValueWitnessTable>
var isaPointer: Int
var superClass: Any.Type
var objCRuntimeReserve1: Int
var objCRuntimeReserve2: Int
var rodataPointer: Int
var classFlags: Int32
var instanceAddressPoint: Int32
var instanceSize: Int32
var instanceAlignmentMask: Int16
var runtimeReserveField: Int16
var classObjectSize: Int32
var classObjectAddressPoint: Int32
var typeDescriptor: UnsafeMutablePointer<ClassTypeDescriptor>
var genericArgumentVector: UnsafeRawPointer
} I'm trying to pass the raw pointer of |
Apparently, what I'm getting is a crash at |
The generic argument vector is stored inline as a trailing field of the metadata record, not as a pointer. It looks like you have its relative position within the struct correct, but the address of that field would be the address of the generic arguments you want to pass. The best way to get the offset would be to follow the logic of |
Field names will no longer be stored in the nominal type descriptor in Swift 4.2. This PR allows the
swift_getFieldAt
function to be callable from C / Swift so that it will still be possible to access the field name data in-process from a Swfit app.I've tested this from a Swift app and it works now. Prior to this change similar code would result in a bad access exception.
Joe noted that I should add a context pointer to the callback which I did. He also said:
I was a bit confused on how to implement it that way as I've never really used C++ before. I'm not quite sure how to pass the context-capturing std::function through a C closure. Any pointers there would be appreciated!
Related twitter thread: https://twitter.com/jckarter/status/978736605014892544