-
Notifications
You must be signed in to change notification settings - Fork 10.6k
Teach RemoteMirror how to project enum values #30161
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
Conversation
rdar://problem/31666592
…into tbkka-remoteMirror-projectEnum
This adds two new functions to the SwiftRemoteMirror
facility that support inspecting enum values.
Currently, these support non-payload enums and
single-payload enums, including nested enums and
payloads with struct, tuple, and reference payloads.
In particular, it handles nested `Optional` types.
TODO: Multi-payload enums use different strategies for
encoding the cases that aren't yet supported by this
code.
Resolves rdar://59961527
```
/// Projects the value of an enum.
///
/// Takes the address and typeref for an enum and determines the
/// index of the currently-selected case within the enum.
///
/// Returns true iff the enum case could be successfully determined.
/// In particular, note that this code may fail for valid in-memory data
/// if the compiler is using a strategy we do not yet understand.
SWIFT_REMOTE_MIRROR_LINKAGE
int swift_reflection_projectEnumValue(SwiftReflectionContextRef ContextRef,
swift_addr_t EnumAddress,
swift_typeref_t EnumTypeRef,
uint64_t *CaseIndex);
/// Finds information about a particular enum case.
///
/// Given an enum typeref and index of a case, returns:
/// * Typeref of the associated payload or zero if there is no payload
/// * Name of the case if known.
///
/// The Name points to a freshly-allocated C string on the heap. You
/// are responsible for freeing the string (via `free()`) when you are finished.
SWIFT_REMOTE_MIRROR_LINKAGE
int swift_reflection_getEnumCaseTypeRef(SwiftReflectionContextRef ContextRef,
swift_typeref_t EnumTypeRef,
unsigned CaseIndex,
char **CaseName,
swift_typeref_t *PayloadTypeRef);
```
|
This is not quite ready to merge: It currently hard-codes some pointer properties that need to be queried from the target. |
Data Layout Queries (DLQ) are questions handled by queryDataLayout() about the target environment. This adds two new parameters: DLQ_GetLeastValidPointerValue DLQ_GetObjCReservedLowBits These are the key parameters needed for the pointer XI calculations. TODO: The implementation of the new DLQ parameters is still incomplete.
| case DLQ_GetObjCReservedLowBits: { | ||
| auto result = static_cast<uint8_t *>(outBuffer); | ||
| #if __APPLE__ && __x86_64__ | ||
| // ObjC on 64-bit macOS reserves the bottom bit of all pointers. |
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.
What about iOS?
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.
As far as I understand, iOS Obj-C reserves high bits, not low bits. So this literally only applies to 64-bit macOS, no other platforms.
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.
Thanks for clarifying.
This is insufficient for cases where you are inspecting an image for another platform, but it's sufficient for the test suites (which are always run on the same environment as the target) and at least serves to document the necessary logic for clients that need to support cross-platform inspection.
| return true; | ||
| } | ||
| // XXX Has extra inhabitants, so it must be a pointer? | ||
| return reader.readHeapObjectExtraInhabitantIndex(address, extraInhabitantIndex); |
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'm not sure whether a BuiltinTypeInfo with extra inhabitants can ever be a function pointer or whether it's always just a regular pointer to heap data. (There are different rules for managing XIs for function pointers.)
| *extraInhabitantIndex = -1; | ||
| return true; | ||
| } | ||
| return reader.readHeapObjectExtraInhabitantIndex(address, extraInhabitantIndex); |
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 suspect ReferenceTypeInfo is used for both function pointers and heap pointers, but I'm not quite sure how to distinguish those cases.
|
I think I've worked out the pointer properties as well as I can for now. I'm still checking some details around XI handling for different kinds of pointers, but otherwise I think this is mergeable (pending CI, of course). |
|
@swift-ci Please test macOS |
|
@swift-ci Please test Linux |
|
Build failed |
|
Build failed |
The earlier version had a bad bug (a reused variable) that ended up always using the last field instead of the one with the most extra inhabitants. Using STL algorithms is safer.
For some reason, `TARGET_OS_IOS` is defined to be 1 on iOS and defined to 0 on all other Apple platforms. So you can't just check whether the symbol is defined or not. It probably would suffice to just use the value, but I had headaches a while back with some compiler that insisted on complaining about undefined preprocessor macros, so I've used the horribly conservative `defined(FOO) && FOO` construction here.
…-payload-enums are rejected (since they're not actually supported)
|
I think this is ready to merge. The tests could be a little more comprehensive, but the API seems to be working correctly for the current tests on both macOS x86_64 and the 32-bit iOS simulator. |
|
@swift-ci Please smoke test Linux |
|
@swift-ci Please test macOS |
|
@swift-ci Please smoke test Linux |
|
@swift-ci Please test macOS |
|
Build failed |
Add some missing 32-bit validation.
Also, when printing a payload-carrying enum value, just
include the type info ("struct foo") instead of the detailed
type ref.
|
@swift-ci Please test macOS |
|
@swift-ci Please smoke test Linux |
|
Build failed |
|
@swift-ci Please smoke test Linux |
|
This caused test failure in "Swift in the OS" bot |
|
PR #30281 should fix this. |
| if (Fields.size() == 0) { | ||
| return false; | ||
| } | ||
| // Tuples and Structs inherit XIs from their most capacious member |
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.
Can we re-use this computation from when we build the TypeInfo?
| unsigned long PayloadExtraInhabitants = PayloadCase.TI.getNumExtraInhabitants(); | ||
| unsigned discriminator = 0; | ||
| auto PayloadSize = PayloadCase.TI.getSize(); | ||
| if (NonPayloadCaseCount >= PayloadExtraInhabitants) { |
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.
This should also be re-used from when we lower the type
| // FIXME: Is there a better way to return a string here? | ||
| // Just returning Case.Name.c_str() doesn't work as the backing data gets | ||
| // released at the end of this function. | ||
| *CaseName = strdup(Name.c_str()); |
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.
Instead of leaking memory here what if all the case names were pre-allocated by the RecordTypeInfo for the enum, and owned by that same TypeInfo so that we can clean them up when the reflection context is deleted? This simplifies the function's contract.
| return false; | ||
| } | ||
|
|
||
| bool getEnumCaseTypeRef(const TypeRef *EnumTR, |
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 this API is redundant. In the C API, we already have swift_reflection_childOfTypeRef()
This adds two new functions to the SwiftRemoteMirror
facility that support inspecting enum values.
Currently, these support non-payload enums and
single-payload enums, including nested enums and
payloads with struct, tuple, and reference payloads.
In particular, it handles nested
Optionaltypes.TODO: Multi-payload enums use different strategies for
encoding the cases that aren't yet supported by this
code.
Resolves rdar://59961527