Files
swift-mirror/stdlib/tools/swift-reflection-test/swift-reflection-test.c
tbkka 0d361bd3ea Teach RemoteMirror how to project enum values (#30161)
Teach RemoteMirror how to project enum values

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.

Note: This relies on information from dataLayoutQuery
to correctly decode invalid pointer values that are used
to encode enums.  Existing clients will need to augment
their DLQ functions before using these new APIs.

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);
```


Co-authored-by: Mike Ash <mikeash@apple.com>
2020-03-06 13:17:40 -08:00

809 lines
25 KiB
C

//===--- swift-reflection-test.c - Reflection testing application ---------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
// This file supports performing target-specific remote reflection tests
// on live swift executables.
//===----------------------------------------------------------------------===//
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
#include "swift/SwiftRemoteMirror/SwiftRemoteMirror.h"
#include "swift/Demangling/ManglingMacros.h"
#include "messages.h"
#include "overrides.h"
#include <assert.h>
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
#include <unistd.h>
#elif defined(_WIN32)
#include <io.h>
#include <fcntl.h>
#endif
#if defined(__APPLE__) && defined(__MACH__)
#include <TargetConditionals.h>
#endif
#if defined(__clang__) || defined(__GNUC__)
#define NORETURN __attribute__((noreturn))
#elif defined(_MSC_VER)
#define NORETURN __declspec(noreturn)
#else
#define NORETURN
#endif
typedef struct PipeMemoryReader {
int to_child[2];
int from_child[2];
} PipeMemoryReader;
typedef struct RemoteSection {
uintptr_t StartAddress;
uintptr_t Size;
uintptr_t EndAddress;
} RemoteSection;
typedef struct RemoteReflectionInfo {
RemoteSection fieldmd;
RemoteSection assocty;
RemoteSection builtin;
RemoteSection capture;
RemoteSection typeref;
RemoteSection reflstr;
uintptr_t StartAddress;
size_t TotalSize;
} RemoteReflectionInfo;
NORETURN
static void errorAndExit(const char *message) {
fprintf(stderr, "%s\n", message);
abort();
}
NORETURN
static void errnoAndExit(const char *message) {
fprintf(stderr, "%s: %s\n", message, strerror(errno));
abort();
}
#if 0
#include <inttypes.h>
#define DEBUG_LOG(fmt, ...) fprintf(stderr, "%s: " fmt "\n",\
__func__, __VA_ARGS__)
#else
#define DEBUG_LOG(fmt, ...) (void)0
#endif
static const size_t ReadEnd = 0;
static const size_t WriteEnd = 1;
static
int PipeMemoryReader_getParentReadFD(const PipeMemoryReader *Reader) {
return Reader->from_child[ReadEnd];
}
static
int PipeMemoryReader_getChildWriteFD(const PipeMemoryReader *Reader) {
return Reader->from_child[WriteEnd];
}
static
int PipeMemoryReader_getParentWriteFD(const PipeMemoryReader *Reader) {
return Reader->to_child[WriteEnd];
}
static
int PipeMemoryReader_getChildReadFD(const PipeMemoryReader *Reader) {
return Reader->to_child[ReadEnd];
}
static
uint8_t PipeMemoryReader_getPointerSize(void *Context) {
return sizeof(uintptr_t);
}
static
void PipeMemoryReader_collectBytesFromPipe(const PipeMemoryReader *Reader,
void *Dest, size_t Size) {
int ReadFD = PipeMemoryReader_getParentReadFD(Reader);
while (Size) {
int bytesRead = read(ReadFD, Dest, Size);
if (bytesRead < 0)
if (errno == EINTR)
continue;
else
errnoAndExit("collectBytesFromPipe");
else if (bytesRead == 0)
errorAndExit("collectBytesFromPipe: Unexpected end of file");
Size -= bytesRead;
// Arithmetic on a void pointer is a GNU extension.
Dest = (char*)(Dest) + bytesRead;
}
}
static int PipeMemoryReader_queryDataLayout(void *Context,
DataLayoutQueryType type,
void *inBuffer, void *outBuffer) {
#if defined(__APPLE__) && __APPLE__
int applePlatform = 1;
#else
int applePlatform = 0;
#endif
#if defined(__APPLE__) && __APPLE__ && ((defined(TARGET_OS_IOS) && TARGET_OS_IOS) || (defined(TARGET_OS_IOS) && TARGET_OS_WATCH) || (defined(TARGET_OS_TV) && TARGET_OS_TV))
int iosDerivedPlatform = 1;
#else
int iosDerivedPlatform = 0;
#endif
switch (type) {
case DLQ_GetPointerSize: {
uint8_t *result = (uint8_t *)outBuffer;
*result = sizeof(void *);
return 1;
}
case DLQ_GetSizeSize: {
uint8_t *result = (uint8_t *)outBuffer;
*result = sizeof(size_t);
return 1;
}
case DLQ_GetObjCReservedLowBits: {
uint8_t *result = (uint8_t *)outBuffer;
if (applePlatform && !iosDerivedPlatform && (sizeof(void *) == 8)) {
// Only for 64-bit macOS (not iOS, not even when simulated on x86_64)
*result = 1;
} else {
*result = 0;
}
return 1;
}
case DLQ_GetLeastValidPointerValue: {
uint64_t *result = (uint64_t *)outBuffer;
if (applePlatform && (sizeof(void *) == 8)) {
// Swift reserves the first 4GiB on Apple 64-bit platforms
*result = 0x100000000;
return 1;
} else {
// Swift reserves the first 4KiB everywhere else
*result = 0x1000;
}
return 1;
}
}
return 0;
}
static void PipeMemoryReader_freeBytes(void *reader_context, const void *bytes,
void *context) {
free((void *)bytes);
}
static
const void *PipeMemoryReader_readBytes(void *Context, swift_addr_t Address,
uint64_t Size, void **outFreeContext) {
const PipeMemoryReader *Reader = (const PipeMemoryReader *)Context;
uintptr_t TargetAddress = Address;
size_t TargetSize = (size_t)Size;
DEBUG_LOG("Requesting read of %zu bytes from 0x%" PRIxPTR,
TargetSize, TargetAddress);
int WriteFD = PipeMemoryReader_getParentWriteFD(Reader);
write(WriteFD, REQUEST_READ_BYTES, 2);
write(WriteFD, &TargetAddress, sizeof(TargetAddress));
write(WriteFD, &TargetSize, sizeof(size_t));
void *Buf = malloc(Size);
PipeMemoryReader_collectBytesFromPipe(Reader, Buf, Size);
*outFreeContext = NULL;
return Buf;
}
static
swift_addr_t PipeMemoryReader_getSymbolAddress(void *Context,
const char *SymbolName,
uint64_t Length) {
const PipeMemoryReader *Reader = (const PipeMemoryReader *)Context;
uintptr_t Address = 0;
DEBUG_LOG("Requesting address of symbol %s", SymbolName);
int WriteFD = PipeMemoryReader_getParentWriteFD(Reader);
write(WriteFD, REQUEST_SYMBOL_ADDRESS, 2);
write(WriteFD, SymbolName, Length);
write(WriteFD, "\n", 1);
PipeMemoryReader_collectBytesFromPipe(Reader, (uint8_t*)&Address,
sizeof(Address));
DEBUG_LOG("Address of %s is 0x%" PRIxPTR, SymbolName, Address);
return (uintptr_t)Address;
}
static InstanceKind
PipeMemoryReader_receiveInstanceKind(const PipeMemoryReader *Reader) {
int WriteFD = PipeMemoryReader_getParentWriteFD(Reader);
write(WriteFD, REQUEST_INSTANCE_KIND, 2);
uint8_t KindValue = 0;
PipeMemoryReader_collectBytesFromPipe(Reader, &KindValue, sizeof(KindValue));
DEBUG_LOG("Requested instance kind is %u", KindValue);
return KindValue;
}
static uintptr_t
PipeMemoryReader_receiveInstanceAddress(const PipeMemoryReader *Reader) {
int WriteFD = PipeMemoryReader_getParentWriteFD(Reader);
write(WriteFD, REQUEST_INSTANCE_ADDRESS, 2);
uintptr_t InstanceAddress = 0;
PipeMemoryReader_collectBytesFromPipe(Reader, (uint8_t *)&InstanceAddress,
sizeof(InstanceAddress));
DEBUG_LOG("Requested instance address is 0x%" PRIxPTR, InstanceAddress);
return InstanceAddress;
}
static
void PipeMemoryReader_sendDoneMessage(const PipeMemoryReader *Reader) {
int WriteFD = PipeMemoryReader_getParentWriteFD(Reader);
write(WriteFD, REQUEST_DONE, 2);
}
static
PipeMemoryReader createPipeMemoryReader() {
PipeMemoryReader Reader;
#if defined(_WIN32)
if (_pipe(Reader.to_child, 256, _O_BINARY))
errnoAndExit("Couldn't create pipes to child process");
if (_pipe(Reader.from_child, 256, _O_BINARY))
errnoAndExit("Couldn't create pipes from child process");
#else
if (pipe(Reader.to_child))
errnoAndExit("Couldn't create pipes to child process");
if (pipe(Reader.from_child))
errnoAndExit("Couldn't create pipes from child process");
#endif
return Reader;
}
#if defined(__APPLE__) && defined(__MACH__)
static void
PipeMemoryReader_receiveImages(SwiftReflectionContextRef RC,
const PipeMemoryReader *Reader) {
int WriteFD = PipeMemoryReader_getParentWriteFD(Reader);
write(WriteFD, REQUEST_IMAGES, 2);
size_t NumReflectionInfos;
PipeMemoryReader_collectBytesFromPipe(Reader, &NumReflectionInfos,
sizeof(NumReflectionInfos));
DEBUG_LOG("Receiving %z images from child", NumReflectionInfos);
if (NumReflectionInfos == 0)
return;
struct { uintptr_t Start, Size; } *Images;
Images = calloc(NumReflectionInfos, sizeof(*Images));
PipeMemoryReader_collectBytesFromPipe(Reader, Images,
NumReflectionInfos * sizeof(*Images));
for (size_t i = 0; i < NumReflectionInfos; ++i) {
DEBUG_LOG("Adding image at 0x%" PRIxPTR, Images[i].Start);
swift_reflection_addImage(RC, Images[i].Start);
}
free(Images);
}
#else
static swift_reflection_section_t
makeLocalSection(const void *Buffer, RemoteSection Section,
RemoteReflectionInfo Info) {
if (Section.Size == 0) {
swift_reflection_section_t LS = {NULL, NULL};
return LS;
}
uintptr_t Base
= (uintptr_t)Buffer + Section.StartAddress - Info.StartAddress;
swift_reflection_section_t LS = {
(void *)Base,
(void *)(Base + Section.Size)
};
return LS;
}
static
uintptr_t getStartAddress(const RemoteSection Sections[], size_t Count) {
uintptr_t Start = 0;
for (size_t i = 0; i < Count; ++i) {
if (Sections[i].StartAddress != 0) {
if (Start != 0)
Start = MIN(Start, Sections[i].StartAddress);
else
Start = Sections[i].StartAddress;
}
}
return Start;
}
static
uintptr_t getEndAddress(const RemoteSection Sections[], size_t Count) {
uintptr_t End = 0;
for (size_t i = 0; i < Count; ++i) {
if (Sections[i].StartAddress != 0)
End = MAX(End, Sections[i].EndAddress);
}
return End;
}
static
RemoteReflectionInfo makeRemoteReflectionInfo(RemoteSection fieldmd,
RemoteSection assocty,
RemoteSection builtin,
RemoteSection capture,
RemoteSection typeref,
RemoteSection reflstr) {
RemoteReflectionInfo Info = {
fieldmd,
assocty,
builtin,
capture,
typeref,
reflstr,
0,
0
};
const RemoteSection Sections[6] = {
fieldmd, assocty, builtin, capture, typeref, reflstr
};
Info.StartAddress = getStartAddress(Sections, 6);
uintptr_t EndAddress = getEndAddress(Sections, 6);
Info.TotalSize = EndAddress - Info.StartAddress;
return Info;
}
static
RemoteSection makeRemoteSection(const PipeMemoryReader *Reader) {
uintptr_t Start;
size_t Size;
PipeMemoryReader_collectBytesFromPipe(Reader, &Start, sizeof(Start));
PipeMemoryReader_collectBytesFromPipe(Reader, &Size, sizeof(Size));
RemoteSection RS = {Start, Size, Start + Size};
return RS;
}
static void
PipeMemoryReader_receiveReflectionInfo(SwiftReflectionContextRef RC,
const PipeMemoryReader *Reader) {
int WriteFD = PipeMemoryReader_getParentWriteFD(Reader);
write(WriteFD, REQUEST_REFLECTION_INFO, 2);
size_t NumReflectionInfos;
PipeMemoryReader_collectBytesFromPipe(Reader, &NumReflectionInfos,
sizeof(NumReflectionInfos));
if (NumReflectionInfos == 0)
return;
RemoteReflectionInfo *RemoteInfos = calloc(NumReflectionInfos,
sizeof(RemoteReflectionInfo));
if (RemoteInfos == NULL)
errnoAndExit("malloc failed");
for (size_t i = 0; i < NumReflectionInfos; ++i) {
RemoteInfos[i] = makeRemoteReflectionInfo(
makeRemoteSection(Reader),
makeRemoteSection(Reader),
makeRemoteSection(Reader),
makeRemoteSection(Reader),
makeRemoteSection(Reader),
makeRemoteSection(Reader));
}
// Now pull in the remote sections into our address space.
for (size_t i = 0; i < NumReflectionInfos; ++i) {
RemoteReflectionInfo RemoteInfo = RemoteInfos[i];
void *outFreeContext = NULL;
const void *Buffer = PipeMemoryReader_readBytes((void *)Reader,
RemoteInfo.StartAddress,
RemoteInfo.TotalSize,
&outFreeContext);
if (!Buffer)
errorAndExit("Couldn't read reflection information");
swift_reflection_info_t Info = {
{makeLocalSection(Buffer, RemoteInfo.fieldmd, RemoteInfo), 0},
{makeLocalSection(Buffer, RemoteInfo.assocty, RemoteInfo), 0},
{makeLocalSection(Buffer, RemoteInfo.builtin, RemoteInfo), 0},
{makeLocalSection(Buffer, RemoteInfo.capture, RemoteInfo), 0},
{makeLocalSection(Buffer, RemoteInfo.typeref, RemoteInfo), 0},
{makeLocalSection(Buffer, RemoteInfo.reflstr, RemoteInfo), 0},
/*LocalStartAddress*/ (uintptr_t) Buffer,
/*RemoteStartAddress*/ RemoteInfo.StartAddress,
};
swift_reflection_addReflectionInfo(RC, Info);
}
free(RemoteInfos);
}
#endif
uint64_t PipeMemoryReader_getStringLength(void *Context, swift_addr_t Address) {
const PipeMemoryReader *Reader = (const PipeMemoryReader *)Context;
int WriteFD = PipeMemoryReader_getParentWriteFD(Reader);
uintptr_t TargetAddress = (uintptr_t)Address;
write(WriteFD, REQUEST_STRING_LENGTH, 2);
write(WriteFD, &TargetAddress, sizeof(TargetAddress));
uintptr_t Length = 0;
PipeMemoryReader_collectBytesFromPipe(Reader, &Length, sizeof(Length));
return Length;
}
int reflectHeapObject(SwiftReflectionContextRef RC,
const PipeMemoryReader Pipe) {
uintptr_t instance = PipeMemoryReader_receiveInstanceAddress(&Pipe);
if (instance == 0) {
// Child has no more instances to examine
PipeMemoryReader_sendDoneMessage(&Pipe);
return 0;
}
printf("Instance pointer in child address space: 0x%lx\n",
instance);
swift_typeref_t TR = swift_reflection_typeRefForInstance(RC, instance);
printf("Type reference:\n");
swift_reflection_dumpTypeRef(TR);
printf("\n");
printf("Type info:\n");
swift_reflection_dumpInfoForInstance(RC, instance);
printf("\n");
PipeMemoryReader_sendDoneMessage(&Pipe);
return 1;
}
int reflectExistential(SwiftReflectionContextRef RC,
const PipeMemoryReader Pipe,
swift_typeref_t MockExistentialTR) {
uintptr_t instance = PipeMemoryReader_receiveInstanceAddress(&Pipe);
if (instance == 0) {
// Child has no more instances to examine
PipeMemoryReader_sendDoneMessage(&Pipe);
return 0;
}
printf("Instance pointer in child address space: 0x%lx\n",
instance);
swift_typeref_t InstanceTypeRef;
swift_addr_t StartOfInstanceData = 0;
if (!swift_reflection_projectExistential(RC, instance, MockExistentialTR,
&InstanceTypeRef,
&StartOfInstanceData)) {
printf("swift_reflection_projectExistential failed.\n");
PipeMemoryReader_sendDoneMessage(&Pipe);
return 0;
}
printf("Type reference:\n");
swift_reflection_dumpTypeRef(InstanceTypeRef);
printf("\n");
printf("Type info:\n");
swift_reflection_dumpInfoForTypeRef(RC, InstanceTypeRef);
printf("\n");
PipeMemoryReader_sendDoneMessage(&Pipe);
return 1;
}
int reflectEnum(SwiftReflectionContextRef RC,
const PipeMemoryReader Pipe) {
static const char Name[] = MANGLING_PREFIX_STR "ypD";
swift_typeref_t AnyTR
= swift_reflection_typeRefForMangledTypeName(
RC, Name, sizeof(Name)-1);
uintptr_t AnyInstance = PipeMemoryReader_receiveInstanceAddress(&Pipe);
if (AnyInstance == 0) {
// Child has no more instances to examine
PipeMemoryReader_sendDoneMessage(&Pipe);
return 0;
}
swift_typeref_t EnumTypeRef;
swift_addr_t EnumInstance = 0;
if (!swift_reflection_projectExistential(RC, AnyInstance, AnyTR,
&EnumTypeRef,
&EnumInstance)) {
printf("swift_reflection_projectExistential failed.\n");
PipeMemoryReader_sendDoneMessage(&Pipe);
return 0;
}
printf("Instance pointer in child address space: 0x%lx\n",
(uintptr_t)EnumInstance);
printf("Type reference:\n");
swift_reflection_dumpTypeRef(EnumTypeRef);
printf("\n");
printf("Type info:\n");
swift_reflection_dumpInfoForTypeRef(RC, EnumTypeRef);
printf("\n");
printf("Enum value:\n");
swift_typeinfo_t InstanceTypeInfo = swift_reflection_infoForTypeRef(RC, EnumTypeRef);
if (InstanceTypeInfo.Kind != SWIFT_NO_PAYLOAD_ENUM
&& InstanceTypeInfo.Kind != SWIFT_SINGLE_PAYLOAD_ENUM
&& InstanceTypeInfo.Kind != SWIFT_MULTI_PAYLOAD_ENUM) {
// Enums with a single payload case and no non-payload cases
// can get rewritten by the compiler to just the payload
// type.
swift_reflection_dumpInfoForTypeRef(RC, EnumTypeRef);
PipeMemoryReader_sendDoneMessage(&Pipe);
return 1;
}
int CaseIndex;
if (!swift_reflection_projectEnumValue(RC, EnumInstance, EnumTypeRef, &CaseIndex)) {
printf("swift_reflection_projectEnumValue failed.\n\n");
PipeMemoryReader_sendDoneMessage(&Pipe);
return 1; // <<< Test cases also verify failures, so this must "succeed"
}
char *CaseName = NULL;
swift_typeref_t PayloadTypeRef = 0;
if (!swift_reflection_getEnumCaseTypeRef(RC, EnumTypeRef, CaseIndex, &CaseName, &PayloadTypeRef)) {
printf("swift_reflection_getEnumCaseTypeRef failed.\n");
PipeMemoryReader_sendDoneMessage(&Pipe);
return 0;
}
if (PayloadTypeRef == 0) {
// Enum case has no payload
printf("(enum_value name=%s index=%llu)\n",
CaseName, (unsigned long long)CaseIndex);
} else {
printf("(enum_value name=%s index=%llu\n",
CaseName, (unsigned long long)CaseIndex);
swift_reflection_dumpTypeRef(PayloadTypeRef);
printf(")\n");
}
printf("\n");
// FIXME: Is there a better idiom for handling the returned case name?
free(CaseName);
PipeMemoryReader_sendDoneMessage(&Pipe);
return 1;
}
int reflectEnumValue(SwiftReflectionContextRef RC,
const PipeMemoryReader Pipe) {
static const char Name[] = MANGLING_PREFIX_STR "ypD";
swift_typeref_t AnyTR
= swift_reflection_typeRefForMangledTypeName(
RC, Name, sizeof(Name)-1);
uintptr_t AnyInstance = PipeMemoryReader_receiveInstanceAddress(&Pipe);
if (AnyInstance == 0) {
// Child has no more instances to examine
PipeMemoryReader_sendDoneMessage(&Pipe);
return 0;
}
swift_typeref_t EnumTypeRef;
swift_addr_t EnumInstance = 0;
if (!swift_reflection_projectExistential(RC, AnyInstance, AnyTR,
&EnumTypeRef,
&EnumInstance)) {
printf("swift_reflection_projectExistential failed.\n");
PipeMemoryReader_sendDoneMessage(&Pipe);
return 0;
}
printf("Type reference:\n");
swift_reflection_dumpTypeRef(EnumTypeRef);
printf("Value: ");
int parens = 0;
while (EnumTypeRef != 0) {
swift_typeinfo_t EnumTypeInfo = swift_reflection_infoForTypeRef(RC, EnumTypeRef);
if (EnumTypeInfo.Kind != SWIFT_NO_PAYLOAD_ENUM
&& EnumTypeInfo.Kind != SWIFT_SINGLE_PAYLOAD_ENUM
&& EnumTypeInfo.Kind != SWIFT_MULTI_PAYLOAD_ENUM) {
if (parens == 0) {
printf(".??"); // Enum was optimized away, print "something"
} else {
printf("_");
}
break;
}
int CaseIndex;
if (!swift_reflection_projectEnumValue(RC, EnumInstance, EnumTypeRef, &CaseIndex)) {
printf("swift_reflection_projectEnumValue failed.\n\n");
PipeMemoryReader_sendDoneMessage(&Pipe);
return 1; // <<< Test cases rely on detecting this, so must "succeed"
}
char *CaseName = NULL;
swift_typeref_t PayloadTypeRef = 0;
if (!swift_reflection_getEnumCaseTypeRef(RC, EnumTypeRef, CaseIndex,
&CaseName, &PayloadTypeRef)) {
printf("swift_reflection_getEnumCaseTypeRef failed.\n");
PipeMemoryReader_sendDoneMessage(&Pipe);
return 0;
}
printf(".%s", CaseName);
// FIXME: Is there a better idiom for handling the returned case name?
free(CaseName);
EnumTypeRef = PayloadTypeRef;
if (EnumTypeRef != 0) {
printf("(");
parens += 1;
}
}
for (int i = 0; i < parens; ++i) {
printf(")");
}
printf("\n\n");
PipeMemoryReader_sendDoneMessage(&Pipe);
return 1;
}
int doDumpHeapInstance(const char *BinaryFilename) {
PipeMemoryReader Pipe = createPipeMemoryReader();
#if defined(_WIN32)
#else
pid_t pid = _fork();
switch (pid) {
case -1:
errnoAndExit("Couldn't fork child process");
case 0: { // Child:
close(PipeMemoryReader_getParentWriteFD(&Pipe));
close(PipeMemoryReader_getParentReadFD(&Pipe));
dup2(PipeMemoryReader_getChildReadFD(&Pipe), STDIN_FILENO);
dup2(PipeMemoryReader_getChildWriteFD(&Pipe), STDOUT_FILENO);
_execv(BinaryFilename, NULL);
exit(EXIT_SUCCESS);
}
default: { // Parent
close(PipeMemoryReader_getChildReadFD(&Pipe));
close(PipeMemoryReader_getChildWriteFD(&Pipe));
SwiftReflectionContextRef RC =
swift_reflection_createReflectionContextWithDataLayout(
(void *)&Pipe, PipeMemoryReader_queryDataLayout,
PipeMemoryReader_freeBytes, PipeMemoryReader_readBytes,
PipeMemoryReader_getStringLength,
PipeMemoryReader_getSymbolAddress);
uint8_t PointerSize = PipeMemoryReader_getPointerSize((void*)&Pipe);
if (PointerSize != sizeof(uintptr_t))
errorAndExit("Child process had unexpected architecture");
#if defined(__APPLE__) && defined(__MACH__)
PipeMemoryReader_receiveImages(RC, &Pipe);
#else
PipeMemoryReader_receiveReflectionInfo(RC, &Pipe);
#endif
while (1) {
InstanceKind Kind = PipeMemoryReader_receiveInstanceKind(&Pipe);
switch (Kind) {
case Object:
printf("Reflecting an object.\n");
if (!reflectHeapObject(RC, Pipe))
return EXIT_SUCCESS;
break;
case Existential: {
static const char Name[] = MANGLING_PREFIX_STR "ypD";
swift_typeref_t AnyTR
= swift_reflection_typeRefForMangledTypeName(RC,
Name, sizeof(Name)-1);
printf("Reflecting an existential.\n");
if (!reflectExistential(RC, Pipe, AnyTR))
return EXIT_SUCCESS;
break;
}
case ErrorExistential: {
static const char ErrorName[] = MANGLING_PREFIX_STR "s5Error_pD";
swift_typeref_t ErrorTR
= swift_reflection_typeRefForMangledTypeName(RC,
ErrorName, sizeof(ErrorName)-1);
printf("Reflecting an error existential.\n");
if (!reflectExistential(RC, Pipe, ErrorTR))
return EXIT_SUCCESS;
break;
}
case Closure:
printf("Reflecting a closure.\n");
if (!reflectHeapObject(RC, Pipe))
return EXIT_SUCCESS;
break;
case Enum: {
printf("Reflecting an enum.\n");
if (!reflectEnum(RC, Pipe))
return EXIT_SUCCESS;
break;
}
case EnumValue: {
printf("Reflecting an enum value.\n");
if (!reflectEnumValue(RC, Pipe))
return EXIT_SUCCESS;
break;
}
case None:
swift_reflection_destroyReflectionContext(RC);
printf("Done.\n");
return EXIT_SUCCESS;
}
}
}
}
#endif
return EXIT_SUCCESS;
}
#if defined(__APPLE__) && defined(__MACH__)
#include <dlfcn.h>
static unsigned long long computeClassIsSwiftMask(void) {
uintptr_t *objc_debug_swift_stable_abi_bit_ptr =
(uintptr_t *)dlsym(RTLD_DEFAULT, "objc_debug_swift_stable_abi_bit");
return objc_debug_swift_stable_abi_bit_ptr ?
*objc_debug_swift_stable_abi_bit_ptr : 1;
}
#else
static unsigned long long computeClassIsSwiftMask(void) {
return 1;
}
#endif
void printUsageAndExit() {
fprintf(stderr, "swift-reflection-test <binary filename>\n");
exit(EXIT_FAILURE);
}
int main(int argc, char *argv[]) {
if (argc != 2)
printUsageAndExit();
const char *BinaryFilename = argv[1];
#if defined(_WIN32)
// FIXME(compnerd) weak linking is not permitted on PE/COFF, we should fall
// back to GetProcAddress to see if the symbol is present.
#else
// swift_reflection_classIsSwiftMask is weak linked so we can work
// with older Remote Mirror dylibs.
if (&swift_reflection_classIsSwiftMask != NULL)
swift_reflection_classIsSwiftMask = computeClassIsSwiftMask();
#endif
uint16_t Version = swift_reflection_getSupportedMetadataVersion();
printf("Metadata version: %u\n", Version);
return doDumpHeapInstance(BinaryFilename);
}