├── .gitignore ├── LICENSE.txt ├── Package.swift ├── README.md └── Sources ├── sample └── main.swift └── swiftype-dump ├── MachO.h ├── MachO.m ├── MetadataDebug.c ├── MetadataDebug.h ├── MetadataFuncs.c ├── MetadataFuncs.h ├── external ├── Metadata.h └── SwiftDemangle.h └── main.m /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | xcuserdata/ 5 | DerivedData/ 6 | .swiftpm/configuration/registries.json 7 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 8 | .netrc 9 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Leptos 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.10 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "swiftype-dump", 8 | platforms: [ 9 | .macOS(.v10_13), 10 | ], 11 | targets: [ 12 | // Targets are the basic building blocks of a package, defining a module or a test suite. 13 | // Targets can depend on other targets in this package and products from dependencies. 14 | .executableTarget( 15 | name: "swiftype-dump", 16 | cSettings: [ // ARC is enabled automatically 17 | .unsafeFlags([ 18 | "-Wall", 19 | "-Wextra", 20 | ]) 21 | ], 22 | linkerSettings: [ 23 | .unsafeFlags([ 24 | "-lswiftDemangle", 25 | ]) 26 | ] 27 | ), 28 | .executableTarget( // sample product to test `swiftype-dump` on 29 | name: "sample" 30 | ) 31 | ] 32 | ) 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## swiftype-dump 2 | 3 | This is a work-in-progress project. 4 | 5 | The goal of `swiftype-dump` is to produce a `.swiftinterface` file given a Mach-O as input. 6 | 7 | Thanks to help from https://derekselander.github.io/dsdump/#swift 8 | and https://github.com/blacktop/ipsw/blob/adf12a3/cmd/ipsw/cmd/swift_dump.go 9 | 10 | ### Design 11 | 12 | My current idea for the architecture is to gather all the metadata we have access to, 13 | represent that metadata in a tree, then produce an interface using the tree. 14 | 15 | #### Data sources 16 | 17 | The sources are: 18 | 19 | - Information from `__TEXT,__swift5_types` section 20 | - All the names for symbols defined in the binary which appear to be Swift functions 21 | 22 | Each of these sources will provide a full "path" to the type information. 23 | 24 | For example, 25 | [`CanonicalCombiningClass`](https://developer.apple.com/documentation/swift/unicode/canonicalcombiningclass) 26 | is a `struct` in the `enum` named `Unicode` which is in the module named `Swift`. 27 | The mangled name for this type is `s7UnicodeO23CanonicalCombiningClassV`. 28 | This encodes the full type information: 29 | 30 | ``` 31 | s // special case for Swift module 32 | 7UnicodeO 33 | 7 // length 34 | Unicode // name 35 | O // kind = enum 36 | 23CanonicalCombiningClassV 37 | 23 // length 38 | CanonicalCombiningClass // name 39 | V // kind = struct 40 | ``` 41 | 42 | See https://github.com/swiftlang/swift/blob/swift-6.0-RELEASE/docs/ABI/Mangling.rst 43 | for more information on decoding these mangled names. 44 | 45 | The data in `__TEXT,__swift5_types` has even more information and parsing the data 46 | is currently implemented in this repo. 47 | 48 | #### Consuming the data 49 | 50 | As we gather this information, we can place the type information in a tree. 51 | Swift calls each piece of "type information" a `Demangle::Node`. 52 | I'll use this name for clarity in this section. 53 | The name "node" also makes sense as we place these in a tree. 54 | 55 | We'll start with a root node and add objects using their full paths 56 | into the tree. Since each node in a path has a "kind" (`struct`, `enum`, etc.) 57 | we should check and `assert` that the kinds match between what we're inserting 58 | and what's currently in the tree. 59 | 60 | Let's look at an example: 61 | 62 | ```swift 63 | import SwiftUI 64 | 65 | public class ViewModel { 66 | public enum Section { 67 | case books 68 | case movies 69 | } 70 | 71 | public func color(for section: Section) -> Color { 72 | // ... 73 | } 74 | } 75 | ``` 76 | 77 | Let's say this code is in a module called `MediaApp`. 78 | 79 | The graph should look approximately like this: 80 | 81 | ```mermaid 82 | graph TD; 83 | :root --> MediaApp["MediaApp (kind = module)"] 84 | MediaApp --> ViewModel["ViewModel (kind = class)"] 85 | ViewModel --> Section["Section (kind = enum)"] 86 | ViewModel --> colorForSection["color(for:) (kind = func)"] 87 | :root --> SwiftUI["SwiftUI (kind = module)"] 88 | SwiftUI --> Color["Color (kind = struct)"] 89 | ``` 90 | 91 | When we parse the `color(for:)` function, we also add the return type and all the parameter types to the tree. 92 | 93 | #### Generation 94 | 95 | We can take our tree and the name of a module to produce a Swift interface. 96 | 97 | We start by looking at our tree and finding every module that is not the input module. 98 | Add `import` statements for each of these modules. 99 | 100 | Next we go through the input module and produce definitions for each entry 101 | (this is not trivial, however it's been mostly implemented in this repo and other projects have implemented it). 102 | -------------------------------------------------------------------------------- /Sources/sample/main.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | // to emit .swiftinterface 4 | // swiftc -enable-library-evolution -emit-module-interface-path sample.swiftinterface -module-name sample -parse -parse-as-library Sources/sample/main.swift 5 | // to emit binary (with optimization) 6 | // swiftc -o sample -O Sources/sample/main.swift 7 | 8 | public enum SampleEnum: CaseIterable { 9 | case alpha 10 | case bravo 11 | 12 | var isAlpha: Bool { 13 | guard case .alpha = self else { 14 | return false 15 | } 16 | return true 17 | } 18 | 19 | func isBravo() -> Bool { 20 | guard case .bravo = self else { 21 | return false 22 | } 23 | return true 24 | } 25 | } 26 | 27 | public struct SampleStruct: Identifiable { 28 | public let id: UUID 29 | public var count: Int = 0 30 | 31 | public var transform: AffineTransform 32 | var sampleEnum: SampleEnum 33 | } 34 | 35 | public class SampleClass { 36 | public let id: String 37 | public let location: Float 38 | 39 | struct SubType { 40 | } 41 | 42 | var sub: SubType 43 | 44 | public init(id: String, location: Float) { 45 | self.id = id 46 | self.location = location 47 | self.sub = SubType() 48 | } 49 | } 50 | 51 | public class SampleSubclass: SampleClass { 52 | var partTwo: SampleStruct 53 | 54 | init(partTwo: SampleStruct) { 55 | self.partTwo = partTwo 56 | super.init(id: partTwo.id.uuidString, location: 0) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Sources/swiftype-dump/MachO.h: -------------------------------------------------------------------------------- 1 | #ifndef MachO_h 2 | #define MachO_h 3 | 4 | #import 5 | 6 | #import 7 | #import 8 | #import 9 | 10 | /// key is an address in `mh` where data could be read from, 11 | /// value is the symbol name the data is bound to 12 | NSDictionary *reverseSymbolLookUp(const struct mach_header *const mh); 13 | 14 | const struct mach_header *machHeaderForImage(const void *const image); 15 | 16 | #endif /* MachO_h */ 17 | -------------------------------------------------------------------------------- /Sources/swiftype-dump/MachO.m: -------------------------------------------------------------------------------- 1 | #import "MachO.h" 2 | 3 | NSDictionary *reverseSymbolLookUp(const struct mach_header *const mh) { 4 | // TODO: see fishhook to implement 5 | // https://github.com/facebook/fishhook/blob/aadc161ac3b80db07a9908851839a17ba63a9eb1/fishhook.c#L167 6 | assert(0 && "Not yet implemented"); 7 | 8 | const struct load_command *lc = NULL; 9 | switch (mh->magic) { 10 | case MH_MAGIC_64: 11 | lc = (void *)mh + sizeof(struct mach_header_64); 12 | break; 13 | case MH_MAGIC: 14 | lc = (void *)mh + sizeof(struct mach_header); 15 | break; 16 | default: 17 | assert(0 && "unknown mach_header magic"); 18 | return nil; 19 | } 20 | 21 | for (uint32_t cmd = 0; cmd < mh->ncmds; cmd++) { 22 | if (lc->cmd == LC_SYMTAB) { 23 | const struct symtab_command *sym_cmd = (const void *)lc; 24 | 25 | // sizeof(struct nlist_64) == 16 26 | // sizeof(struct nlist) == 12 27 | switch (mh->magic) { 28 | case MH_MAGIC_64: { 29 | const struct nlist_64 *sym_table = (void *)mh + sym_cmd->symoff; 30 | const char *str_table = (void *)mh + sym_cmd->stroff; 31 | for (uint32_t sym_index = 0; sym_index < sym_cmd->nsyms; sym_index++) { 32 | const struct nlist_64 *entry = sym_table + sym_index; 33 | const char *name = str_table + entry->n_un.n_strx; 34 | printf("%s [0x%" __UINT8_FMTx__ "]\n", name, entry->n_type); 35 | } 36 | } break; 37 | case MH_MAGIC: { 38 | const struct nlist *sym_table = (void *)mh + sym_cmd->symoff; 39 | const char *str_table = (void *)mh + sym_cmd->stroff; 40 | for (uint32_t sym_index = 0; sym_index < sym_cmd->nsyms; sym_index++) { 41 | const struct nlist *entry = sym_table + sym_index; 42 | const char *name = str_table + entry->n_un.n_strx; 43 | printf("%s [0x%" __UINT8_FMTx__ "]\n", name, entry->n_type); 44 | } 45 | } break; 46 | default: 47 | assert(0 && "unknown mach_header magic"); 48 | return nil; 49 | } 50 | } 51 | lc = (void *)lc + lc->cmdsize; 52 | } 53 | 54 | return nil; 55 | } 56 | 57 | const struct mach_header *machHeaderForImage(const void *const image) { 58 | // TODO: how should we pick which slice to use? 59 | const uint32_t magic = *(const uint32_t *)image; 60 | switch (magic) { 61 | case MH_MAGIC: { 62 | return image; 63 | } break; 64 | case MH_MAGIC_64: { 65 | return image; 66 | } break; 67 | case FAT_MAGIC: { 68 | // TODO 69 | assert(0 && "FAT currently unsupported"); 70 | return nil; 71 | } break; 72 | case FAT_MAGIC_64: { 73 | // TODO 74 | assert(0 && "FAT_64 currently unsupported"); 75 | return nil; 76 | } break; 77 | default: 78 | assert(0 && "unknown file"); 79 | return nil; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Sources/swiftype-dump/MetadataDebug.c: -------------------------------------------------------------------------------- 1 | #include "MetadataDebug.h" 2 | #include "MetadataFuncs.h" 3 | 4 | #if DEBUG 5 | 6 | #include 7 | 8 | void debugPutTargetFieldDescriptor(const struct TargetFieldDescriptor *const desc) { 9 | printf("(%p) struct TargetFieldDescriptor {\n" 10 | "\tmangledTypeName: %s\n" 11 | "\tsuperclass: %p\n" 12 | "\tkind: %" __UINT16_FMTu__ "\n" 13 | "\tfieldRecordSize: %" __UINT16_FMTu__ "\n" 14 | "\tnumFields: %" __UINT32_FMTu__ "\n" 15 | "}\n", 16 | desc, 17 | (const char *)relativeDirectResolve(&desc->mangledTypeName), 18 | relativeDirectResolve(&desc->superclass), 19 | desc->kind, 20 | desc->fieldRecordSize, 21 | desc->numFields 22 | ); 23 | 24 | const void *const fields = (const void *)(desc + 1); 25 | for (uint32_t i = 0; i < desc->numFields; i++) { 26 | // byte arithmetic 27 | const struct TargetFieldRecord *const field = fields + (i * desc->fieldRecordSize); 28 | 29 | printf("(%p) struct TargetFieldRecord {\n" 30 | "\tflags: %" __UINT32_FMTu__ "\n" 31 | "\tmangledTypeName: %s\n" 32 | "\tfieldName: %s\n" 33 | "}\n", 34 | field, 35 | field->flags, 36 | (const char *)relativeDirectResolve(&field->mangledTypeName), 37 | (const char *)relativeDirectResolve(&field->fieldName) 38 | ); 39 | } 40 | } 41 | 42 | void debugPutTargetClassDescriptor(const struct TargetClassDescriptor *const desc) { 43 | printf("(%p) struct TargetClassDescriptor {\n" 44 | "\tname: %s\n" 45 | "\tnumFields: %" __UINT32_FMTu__ "\n" 46 | "\tfieldOffsetVectorOffset: %" __UINT32_FMTu__ "\n" 47 | "}\n", 48 | desc, 49 | (const char *)relativeDirectResolve(&desc->typeContext.name), 50 | desc->numFields, 51 | desc->fieldOffsetVectorOffset 52 | ); 53 | debugPutTargetFieldDescriptor(relativeDirectResolve(&desc->typeContext.fields)); 54 | } 55 | 56 | void debugPutTargetStructDescriptor(const struct TargetStructDescriptor *const desc) { 57 | printf("(%p) struct TargetStructDescriptor {\n" 58 | "\tname: %s\n" 59 | "\tnumFields: %" __UINT32_FMTu__ "\n" 60 | "\tfieldOffsetVectorOffset: %" __UINT32_FMTu__ "\n" 61 | "}\n", 62 | desc, 63 | (const char *)relativeDirectResolve(&desc->valueType.typeContext.name), 64 | desc->numFields, 65 | desc->fieldOffsetVectorOffset 66 | ); 67 | debugPutTargetFieldDescriptor(relativeDirectResolve(&desc->valueType.typeContext.fields)); 68 | } 69 | 70 | void debugPutTargetEnumDescriptor(const struct TargetEnumDescriptor *const desc) { 71 | printf("(%p) struct TargetEnumDescriptor {\n" 72 | "\tname: %s\n" 73 | "\tnumPayloadCases: %" __UINT32_FMTu__ "\n" 74 | "\tpayloadSizeOffset: %" __UINT32_FMTu__ "\n" 75 | "\tnumEmptyCases: %" __UINT32_FMTu__ "\n" 76 | "}\n", 77 | desc, 78 | (const char *)relativeDirectResolve(&desc->valueType.typeContext.name), 79 | desc->numPayloadCasesAndPayloadSizeOffset.numPayloadCases, 80 | desc->numPayloadCasesAndPayloadSizeOffset.payloadSizeOffset, 81 | desc->numEmptyCases 82 | ); 83 | debugPutTargetFieldDescriptor(relativeDirectResolve(&desc->valueType.typeContext.fields)); 84 | } 85 | 86 | #endif 87 | -------------------------------------------------------------------------------- /Sources/swiftype-dump/MetadataDebug.h: -------------------------------------------------------------------------------- 1 | #ifndef MetadataDebug_h 2 | #define MetadataDebug_h 3 | 4 | #import "external/Metadata.h" 5 | 6 | /* functions to print (for debugging) the structs in Metadata */ 7 | 8 | #if DEBUG 9 | 10 | void debugPutTargetFieldDescriptor(const struct TargetFieldDescriptor *const desc); 11 | 12 | void debugPutTargetClassDescriptor(const struct TargetClassDescriptor *const desc); 13 | void debugPutTargetStructDescriptor(const struct TargetStructDescriptor *const desc); 14 | void debugPutTargetEnumDescriptor(const struct TargetEnumDescriptor *const desc); 15 | 16 | #endif /* DEBUG */ 17 | 18 | #endif /* MetadataDebug_h */ 19 | -------------------------------------------------------------------------------- /Sources/swiftype-dump/MetadataFuncs.c: -------------------------------------------------------------------------------- 1 | #include "MetadataFuncs.h" 2 | -------------------------------------------------------------------------------- /Sources/swiftype-dump/MetadataFuncs.h: -------------------------------------------------------------------------------- 1 | #ifndef MetadataFuncs_h 2 | #define MetadataFuncs_h 3 | 4 | #import 5 | #import "external/Metadata.h" 6 | 7 | static inline const void *_Nullable relativeDirectResolve(const TargetRelativeDirectPointer *_Nonnull const value) { 8 | const void *const base = value; 9 | int32_t const offset = *value; 10 | if (offset == 0) { 11 | return NULL; 12 | } 13 | return base + offset; 14 | } 15 | 16 | // https://github.com/swiftlang/swift/blob/48015abbd89116163115e1d728e8478829c91271/include/swift/Basic/RelativePointer.h#L277 17 | static inline const void *_Nullable relativeContextResolve(const TargetRelativeContextPointer *_Nonnull const value) { 18 | const void *const base = value; 19 | int32_t const offset = *value; 20 | if (offset == 0) { 21 | return NULL; 22 | } 23 | // indirect 24 | if (offset & 1) { 25 | const void *const *const resolved = base + (offset & ~1); 26 | return *resolved; 27 | } 28 | return base + offset; 29 | } 30 | 31 | #endif /* MetadataFuncs_h */ 32 | -------------------------------------------------------------------------------- /Sources/swiftype-dump/external/Metadata.h: -------------------------------------------------------------------------------- 1 | // based on https://github.com/swiftlang/swift/blob/48015abbd89116163115e1d728e8478829c91271/include/swift/ABI/Metadata.h 2 | 3 | #import 4 | #import 5 | 6 | // https://github.com/swiftlang/swift/blob/48015abbd89116163115e1d728e8478829c91271/include/swift/ABI/MetadataValues.h#L1511 7 | typedef enum : uint8_t { 8 | /// This context descriptor represents a module. 9 | ContextDescriptorKindModule, 10 | /// This context descriptor represents an extension. 11 | ContextDescriptorKindExtension, 12 | /// This context descriptor represents an anonymous possibly-generic context 13 | /// such as a function body. 14 | ContextDescriptorKindAnonymous, 15 | /// This context descriptor represents a protocol context. 16 | ContextDescriptorKindProtocol, 17 | /// This context descriptor represents an opaque type alias. 18 | ContextDescriptorKindOpaqueType, 19 | 20 | /// This context descriptor represents a class. 21 | ContextDescriptorKindClass = 16, 22 | /// This context descriptor represents a struct. 23 | ContextDescriptorKindStruct, 24 | /// This context descriptor represents an enum. 25 | ContextDescriptorKindEnum, 26 | } ContextDescriptorKind; 27 | 28 | // https://github.com/swiftlang/swift/blob/48015abbd89116163115e1d728e8478829c91271/include/swift/Basic/RelativePointer.h#L232 29 | /// A relative reference to an object stored in memory. The reference may be 30 | /// direct or indirect, and uses the low bit of the (assumed at least 31 | /// 2-byte-aligned) pointer to differentiate. 32 | typedef int32_t TargetRelativeContextPointer; 33 | typedef int32_t TargetRelativeDirectPointer; 34 | 35 | 36 | // https://github.com/swiftlang/swift/blob/48015abbd89116163115e1d728e8478829c91271/include/swift/ABI/MetadataValues.h#L1545 37 | struct __attribute__((__packed__)) ContextDescriptorFlags { 38 | /// The kind of context this descriptor describes. 39 | ContextDescriptorKind kind : 5; 40 | uint8_t _padding : 1; // unused as far as I know 41 | /// Whether this is a unique record describing the referenced context. 42 | bool isUnique : 1; 43 | /// Whether the context being described is generic. 44 | bool isGeneric : 1; 45 | /// The format version of the descriptor. Higher version numbers may have 46 | /// additional fields that aren't present in older versions. 47 | uint8_t version; 48 | /// The most significant two bytes of the flags word, which can have 49 | /// kind-specific meaning. 50 | uint16_t kindSpecificFlags; 51 | }; 52 | _Static_assert(sizeof(struct ContextDescriptorFlags) == sizeof(uint32_t), "ContextDescriptorFlags is backed by a uint32_t"); 53 | 54 | // https://github.com/swiftlang/swift/blob/48015abbd89116163115e1d728e8478829c91271/include/swift/ABI/Metadata.h#L2832 55 | struct TargetContextDescriptor { 56 | /// Flags describing the context, including its kind and format version. 57 | struct ContextDescriptorFlags flags; 58 | /// The parent context, or null if this is a top-level context. 59 | TargetRelativeContextPointer parent; 60 | }; 61 | 62 | // https://github.com/swiftlang/swift/blob/48015abbd89116163115e1d728e8478829c91271/include/swift/ABI/Metadata.h#L2890 63 | struct TargetModuleContextDescriptor { 64 | struct TargetContextDescriptor context; 65 | 66 | /// The module name. 67 | TargetRelativeDirectPointer name; 68 | }; 69 | 70 | // https://github.com/swiftlang/swift/blob/48015abbd89116163115e1d728e8478829c91271/include/swift/ABI/Metadata.h#L2932 71 | struct TargetExtensionContextDescriptor { 72 | struct TargetContextDescriptor context; 73 | 74 | /// A mangling of the `Self` type context that the extension extends. 75 | /// The mangled name represents the type in the generic context encoded by 76 | /// this descriptor. For example, a nongeneric nominal type extension will 77 | /// encode the nominal type name. A generic nominal type extension will encode 78 | /// the instance of the type with any generic arguments bound. 79 | /// 80 | /// Note that the Parent of the extension will be the module context the 81 | /// extension is declared inside. 82 | TargetRelativeDirectPointer extendedContext; 83 | }; 84 | 85 | // https://github.com/swiftlang/swift/blob/48015abbd89116163115e1d728e8478829c91271/include/swift/ABI/Metadata.h#L2972 86 | struct TargetAnonymousContextDescriptor { 87 | struct TargetContextDescriptor context; 88 | }; 89 | 90 | // https://github.com/swiftlang/swift/blob/48015abbd89116163115e1d728e8478829c91271/include/swift/ABI/Metadata.h#L3054 91 | struct TargetProtocolDescriptor { 92 | struct TargetContextDescriptor context; 93 | 94 | /// The name of the protocol. 95 | TargetRelativeDirectPointer name; 96 | /// The number of generic requirements in the requirement signature of the 97 | /// protocol. 98 | uint32_t numRequirementsInSignature; 99 | /// The number of requirements in the protocol. 100 | /// If any requirements beyond MinimumWitnessTableSizeInWords are present 101 | /// in the witness table template, they will be not be overwritten with 102 | /// defaults. 103 | uint32_t numRequirements; 104 | /// Associated type names, as a space-separated list in the same order 105 | /// as the requirements. 106 | TargetRelativeDirectPointer associatedTypeNames; 107 | }; 108 | 109 | // https://github.com/swiftlang/swift/blob/48015abbd89116163115e1d728e8478829c91271/include/swift/ABI/Metadata.h#L3146 110 | struct TargetOpaqueTypeDescriptor { 111 | struct TargetContextDescriptor context; 112 | }; 113 | 114 | // https://github.com/swiftlang/swift/blob/48015abbd89116163115e1d728e8478829c91271/include/swift/ABI/Metadata.h#L3724 115 | struct TargetTypeContextDescriptor { 116 | struct TargetContextDescriptor context; 117 | 118 | /// The name of the type. 119 | TargetRelativeDirectPointer name; 120 | /// A pointer to the metadata access function for this type. 121 | int32_t /* ? */ accessFunctionPtr; 122 | /// A pointer to the field descriptor for the type, if any. 123 | TargetRelativeDirectPointer fields; 124 | }; 125 | 126 | // https://github.com/swiftlang/swift/blob/48015abbd89116163115e1d728e8478829c91271/include/swift/ABI/Metadata.h#L3947 127 | struct TargetClassDescriptor { 128 | struct TargetTypeContextDescriptor typeContext; 129 | 130 | /// The type of the superclass, expressed as a mangled type name that can 131 | /// refer to the generic arguments of the subclass type. 132 | TargetRelativeDirectPointer superclassType; 133 | 134 | uint32_t metadata; 135 | uint32_t extra; 136 | 137 | /// The number of additional members added by this class to the class 138 | /// metadata. This data is opaque by default to the runtime, other than 139 | /// as exposed in other members; it's really just 140 | /// NumImmediateMembers * sizeof(void*) bytes of data. 141 | /// 142 | /// Whether those bytes are added before or after the address point 143 | /// depends on areImmediateMembersNegative(). 144 | uint32_t numImmediateMembers; 145 | 146 | /// The number of stored properties in the class, not including its 147 | /// superclasses. If there is a field offset vector, this is its length. 148 | uint32_t numFields; 149 | 150 | /// The offset of the field offset vector for this class's stored 151 | /// properties in its metadata, in words. 0 means there is no field offset 152 | /// vector. 153 | /// 154 | /// If this class has a resilient superclass, this offset is relative to 155 | /// the size of the resilient superclass metadata. Otherwise, it is 156 | /// absolute. 157 | uint32_t fieldOffsetVectorOffset; 158 | }; 159 | 160 | // https://github.com/swiftlang/swift/blob/48015abbd89116163115e1d728e8478829c91271/include/swift/ABI/Metadata.h#L4338 161 | struct TargetValueTypeDescriptor { 162 | struct TargetTypeContextDescriptor typeContext; 163 | }; 164 | 165 | // https://github.com/swiftlang/swift/blob/48015abbd89116163115e1d728e8478829c91271/include/swift/ABI/Metadata.h#L4349 166 | struct TargetStructDescriptor { 167 | struct TargetValueTypeDescriptor valueType; 168 | 169 | /// The number of stored properties in the struct. 170 | /// If there is a field offset vector, this is its length. 171 | uint32_t numFields; 172 | /// The offset of the field offset vector for this struct's stored 173 | /// properties in its metadata, if any. 0 means there is no field offset 174 | /// vector. 175 | uint32_t fieldOffsetVectorOffset; 176 | }; 177 | 178 | // https://github.com/swiftlang/swift/blob/48015abbd89116163115e1d728e8478829c91271/include/swift/ABI/Metadata.h#L4477 179 | struct TargetEnumDescriptor { 180 | struct TargetValueTypeDescriptor valueType; 181 | 182 | struct __attribute__((__packed__)) { 183 | /// The number of non-empty cases in the enum 184 | uint32_t numPayloadCases : 24; 185 | /// The offset of the payload size in the metadata record in words 186 | uint32_t payloadSizeOffset : 8; 187 | } numPayloadCasesAndPayloadSizeOffset; 188 | 189 | /// The number of empty cases in the enum. 190 | uint32_t numEmptyCases; 191 | }; 192 | _Static_assert(sizeof(((struct TargetEnumDescriptor *)0)->numPayloadCasesAndPayloadSizeOffset) == sizeof(uint32_t), "numPayloadCasesAndPayloadSizeOffset is backed by a uint32_t"); 193 | 194 | // https://github.com/swiftlang/swift/blob/48015abbd89116163115e1d728e8478829c91271/include/swift/RemoteInspection/Records.h#L144 195 | typedef enum : uint16_t { 196 | // Swift nominal types. 197 | TargetFieldDescriptorKindStruct, 198 | TargetFieldDescriptorKindClass, 199 | TargetFieldDescriptorKindEnum, 200 | 201 | // Fixed-size multi-payload enums have a special descriptor format that 202 | // encodes spare bits. 203 | // 204 | // For now, a descriptor with this kind 205 | // just means we also have a builtin descriptor from which we get the 206 | // size and alignment. 207 | TargetFieldDescriptorKindMultiPayloadEnum, 208 | 209 | /// A Swift opaque protocol. There are no fields, just a record for the 210 | /// type itself. 211 | TargetFieldDescriptorKindProtocol, 212 | 213 | /// A Swift class-bound protocol. 214 | TargetFieldDescriptorKindClassProtocol, 215 | 216 | /// An Objective-C protocol, which may be imported or defined in Swift. 217 | TargetFieldDescriptorKindObjCProtocol, 218 | 219 | /// An Objective-C class, which may be imported or defined in Swift. 220 | /// In the former case, field type metadata is not emitted, and 221 | /// must be obtained from the Objective-C runtime. 222 | TargetFieldDescriptorKindObjCClass 223 | } TargetFieldDescriptorKind; 224 | 225 | // https://github.com/swiftlang/swift/blob/48015abbd89116163115e1d728e8478829c91271/include/swift/RemoteInspection/Records.h#L176 226 | struct TargetFieldDescriptor { 227 | TargetRelativeDirectPointer mangledTypeName; 228 | TargetRelativeDirectPointer superclass; 229 | 230 | TargetFieldDescriptorKind kind; 231 | 232 | uint16_t fieldRecordSize; 233 | uint32_t numFields; 234 | }; 235 | 236 | // https://github.com/swiftlang/swift/blob/48015abbd89116163115e1d728e8478829c91271/include/swift/RemoteInspection/Records.h#L34 237 | typedef enum : uint32_t { 238 | // Is this an indirect enum case? 239 | TargetFieldRecordFlagsIsIndirectCase = 0x1, 240 | // Is this a mutable `var` property? 241 | TargetFieldRecordFlagsIsVar = 0x2, 242 | // Is this an artificial field? 243 | TargetFieldRecordFlagsIsArtificial = 0x4, 244 | } TargetFieldRecordFlags; 245 | 246 | // https://github.com/swiftlang/swift/blob/48015abbd89116163115e1d728e8478829c91271/include/swift/RemoteInspection/Records.h#L85 247 | struct TargetFieldRecord { 248 | TargetFieldRecordFlags flags; 249 | 250 | TargetRelativeDirectPointer mangledTypeName; 251 | TargetRelativeDirectPointer fieldName; 252 | }; 253 | -------------------------------------------------------------------------------- /Sources/swiftype-dump/external/SwiftDemangle.h: -------------------------------------------------------------------------------- 1 | // copied from https://github.com/swiftlang/swift/blob/48015abbd89116163115e1d728e8478829c91271/include/swift/SwiftDemangle/SwiftDemangle.h 2 | 3 | //===--- SwiftDemangle.h - Public demangling interface ----------*- C++ -*-===// 4 | // 5 | // This source file is part of the Swift.org open source project 6 | // 7 | // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors 8 | // Licensed under Apache License v2.0 with Runtime Library Exception 9 | // 10 | // See https://swift.org/LICENSE.txt for license information 11 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 12 | // 13 | //===----------------------------------------------------------------------===// 14 | /// 15 | /// \file 16 | /// This header declares functions in the libswiftDemangle library, 17 | /// which provides external access to Swift's demangler. 18 | /// 19 | //===----------------------------------------------------------------------===// 20 | 21 | #ifndef SWIFT_DEMANGLE_SWIFT_DEMANGLE_H 22 | #define SWIFT_DEMANGLE_SWIFT_DEMANGLE_H 23 | 24 | #if defined(__cplusplus) 25 | extern "C" { 26 | #endif 27 | 28 | #if defined(swiftDemangle_EXPORTS) 29 | # if defined(__ELF__) 30 | # define SWIFT_DEMANGLE_LINKAGE __attribute__((__visibility__("protected"))) 31 | # elif defined(__MACH__) 32 | # define SWIFT_DEMANGLE_LINKAGE __attribute__((__visibility__("default"))) 33 | # else 34 | # define SWIFT_DEMANGLE_LINKAGE __declspec(dllexport) 35 | # endif 36 | #else 37 | # if defined(__ELF__) 38 | # define SWIFT_DEMANGLE_LINKAGE __attribute__((__visibility__("default"))) 39 | # elif defined(__MACH__) 40 | # define SWIFT_DEMANGLE_LINKAGE __attribute__((__visibility__("default"))) 41 | # else 42 | # define SWIFT_DEMANGLE_LINKAGE __declspec(dllimport) 43 | # endif 44 | #endif 45 | 46 | #if defined(__cplusplus) 47 | } 48 | #endif 49 | 50 | 51 | /// @{ 52 | /// Version constants for libswiftDemangle library. 53 | 54 | /// Major version changes when there are ABI or source incompatible changes. 55 | #define SWIFT_DEMANGLE_VERSION_MAJOR 1 56 | 57 | /// Minor version changes when new APIs are added in ABI- and source-compatible 58 | /// way. 59 | #define SWIFT_DEMANGLE_VERSION_MINOR 2 60 | 61 | /// @} 62 | 63 | #ifdef __cplusplus 64 | extern "C" { 65 | #endif 66 | 67 | /// Demangle Swift function names. 68 | /// 69 | /// \returns the length of the demangled function name (even if greater than the 70 | /// size of the output buffer) or 0 if the input is not a Swift-mangled function 71 | /// name (in which cases \p OutputBuffer is left untouched). 72 | SWIFT_DEMANGLE_LINKAGE 73 | size_t swift_demangle_getDemangledName(const char *MangledName, 74 | char *OutputBuffer, size_t Length); 75 | 76 | /// Demangle Swift function names with module names and implicit self 77 | /// and metatype type names in function signatures stripped. 78 | /// 79 | /// \returns the length of the demangled function name (even if greater than the 80 | /// size of the output buffer) or 0 if the input is not a Swift-mangled function 81 | /// name (in which cases \p OutputBuffer is left untouched). 82 | SWIFT_DEMANGLE_LINKAGE 83 | size_t swift_demangle_getSimplifiedDemangledName(const char *MangledName, 84 | char *OutputBuffer, 85 | size_t Length); 86 | 87 | /// Demangle a Swift symbol and return the module name of the mangled entity. 88 | /// 89 | /// \returns the length of the demangled module name (even if greater than the 90 | /// size of the output buffer) or 0 if the input is not a Swift-mangled name 91 | /// (in which cases \p OutputBuffer is left untouched). 92 | SWIFT_DEMANGLE_LINKAGE 93 | size_t swift_demangle_getModuleName(const char *MangledName, 94 | char *OutputBuffer, 95 | size_t Length); 96 | 97 | /// Demangles a Swift function name and returns true if the function 98 | /// conforms to the Swift calling convention. 99 | /// 100 | /// \returns true if the function conforms to the Swift calling convention. 101 | /// The return value is unspecified if the \p MangledName does not refer to a 102 | /// function symbol. 103 | SWIFT_DEMANGLE_LINKAGE 104 | int swift_demangle_hasSwiftCallingConvention(const char *MangledName); 105 | 106 | #ifdef __cplusplus 107 | } // extern "C" 108 | #endif 109 | 110 | #endif // SWIFT_DEMANGLE_SWIFT_DEMANGLE_H 111 | 112 | -------------------------------------------------------------------------------- /Sources/swiftype-dump/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import 4 | #import 5 | #import 6 | 7 | #import "external/Metadata.h" 8 | #import "external/SwiftDemangle.h" 9 | 10 | #import "MachO.h" 11 | #import "MetadataFuncs.h" 12 | #import "MetadataDebug.h" 13 | 14 | // does not support symbolic references 15 | static NSString *demangleSwiftNonSymbolic(const char *const mangled) { 16 | size_t const length = swift_demangle_getDemangledName(mangled, NULL, 0); 17 | if (length == 0) { 18 | return nil; 19 | } 20 | char *const output = malloc(length + 1); 21 | swift_demangle_getDemangledName(mangled, output, length + 1); 22 | return [[NSString alloc] initWithBytesNoCopy:output length:length encoding:NSUTF8StringEncoding freeWhenDone:YES]; 23 | } 24 | 25 | static inline BOOL stringHasPrefix(const char *const string, const char *prefix) { 26 | return strncmp(string, prefix, strlen(prefix)) == 0; 27 | } 28 | 29 | static NSString *fullyQualifiedName(const struct TargetContextDescriptor *const desc) { 30 | const struct TargetContextDescriptor *const parent = relativeContextResolve(&desc->parent); 31 | NSString *const parentName = parent ? fullyQualifiedName(parent) : nil; 32 | NSString *localName = nil; 33 | 34 | switch (desc->flags.kind) { 35 | case ContextDescriptorKindModule: { 36 | const char *const name = relativeDirectResolve(&((const struct TargetModuleContextDescriptor *)desc)->name); 37 | localName = @(name); 38 | } break; 39 | case ContextDescriptorKindExtension: { 40 | // TODO 41 | assert(0 && "Extension in name resolution"); 42 | return nil; 43 | } break; 44 | case ContextDescriptorKindAnonymous: { 45 | // TODO 46 | assert(0 && "Anonymous in name resolution"); 47 | return nil; 48 | } break; 49 | case ContextDescriptorKindProtocol: { 50 | // TODO 51 | assert(0 && "Protocol in name resolution"); 52 | return nil; 53 | } break; 54 | case ContextDescriptorKindOpaqueType: 55 | assert(0 && "OpaqueType in name resolution"); 56 | return nil; 57 | case ContextDescriptorKindClass: 58 | case ContextDescriptorKindStruct: 59 | case ContextDescriptorKindEnum: { 60 | const char *const name = relativeDirectResolve(&((const struct TargetTypeContextDescriptor *)desc)->name); 61 | localName = @(name); 62 | } break; 63 | default: 64 | assert(0 && "Unknown descriptor kind in name resolution"); 65 | return nil; 66 | } 67 | 68 | assert(localName != nil); 69 | if (parentName) { 70 | return [parentName stringByAppendingFormat:@".%@", localName]; 71 | } 72 | return localName; 73 | } 74 | 75 | // supports symbolic references 76 | static NSString *demangleSwiftName(const char *mangledName) { 77 | const char symbolFlag = mangledName[0]; 78 | const struct TargetContextDescriptor *reference = NULL; 79 | 80 | if (symbolFlag == 0) { 81 | assert(0 && "unexpected name (empty?)"); 82 | } else if (symbolFlag <= 0x17) { 83 | switch (symbolFlag) { 84 | case 1: { 85 | reference = relativeDirectResolve((void *)&mangledName[1]); 86 | } break; 87 | case 2: { 88 | const struct TargetContextDescriptor *const *const referenceRef = relativeDirectResolve((void *)&mangledName[1]); 89 | // this is a linked symbol - only de-reference if this image is loaded with dyld 90 | Dl_info addr_info; // we don't use `addr_info`, but if we pass NULL, `dladdr` returns 0 91 | if (dladdr(referenceRef, &addr_info)) { 92 | reference = *referenceRef; 93 | } else { 94 | // TODO: Use reverseSymbolLookUp (cache the result) 95 | // to get the symbol name. The symbol name should end with "Mn" ("nominal type descriptor"). 96 | // if it does, drop that suffix to get the underlying type. Demangle and return that. 97 | assert(0 && "image must be loaded with dyld"); 98 | } 99 | } break; 100 | default: 101 | assert(0 && "unsupported symbolic type"); 102 | return nil; 103 | } 104 | } else if (symbolFlag <= 0x1f) { 105 | assert(0 && "[unsupported] absolute symbol reference"); 106 | return nil; 107 | } 108 | 109 | if (reference) { 110 | return fullyQualifiedName(reference); 111 | } 112 | // https://github.com/swiftlang/swift/blob/586ac0953c15f775b93c8620b2b4433e111636ec/docs/ABI/Mangling.rst#mangling 113 | if (stringHasPrefix(mangledName, "$s") 114 | || stringHasPrefix(mangledName, "@__swiftmacro_") 115 | || stringHasPrefix(mangledName, "_T0") 116 | || stringHasPrefix(mangledName, "$S")) { 117 | return demangleSwiftNonSymbolic(mangledName); 118 | } 119 | // add the ABI stable mangling prefix 120 | size_t len = strlen(mangledName); 121 | size_t buffLen = 2 + len + 1; 122 | char *buff = malloc(buffLen); 123 | strncpy(buff, "$s", buffLen); 124 | strncpy(buff + 2, mangledName, buffLen - 2); 125 | NSString *result = demangleSwiftNonSymbolic(buff); 126 | free(buff); 127 | return result; 128 | } 129 | 130 | int main(int argc, const char *argv[]) { 131 | if (argc != 2) { 132 | return 1; 133 | } 134 | #if 1 /* currently we need to load with dyld (to get symbol fix-ups), hopefully this is temporary */ 135 | void *handle = dlopen(argv[1], RTLD_NOW); 136 | assert(handle != NULL); 137 | 138 | struct stat target_stat; 139 | if (stat(argv[1], &target_stat) != 0) { 140 | assert(0 && "stat on target failed"); 141 | return 1; 142 | } 143 | 144 | uint32_t count = _dyld_image_count(); 145 | const void *mh = NULL; 146 | // go backwards because we just loaded the image, so it should be more recent (last) 147 | while (count > 0) { 148 | count--; 149 | 150 | struct stat test_stat; 151 | if (stat(_dyld_get_image_name(count), &test_stat) == 0) { 152 | if (test_stat.st_dev == target_stat.st_dev && test_stat.st_ino == target_stat.st_ino) { 153 | mh = _dyld_get_image_header(count); 154 | break; 155 | } 156 | } 157 | } 158 | assert(mh && "mach header not found"); 159 | #else 160 | NSError *error = nil; 161 | 162 | NSURL *target = [NSURL fileURLWithPath:@(argv[1])]; 163 | NSData *data = [NSData dataWithContentsOfURL:target options:0 error:&error]; 164 | if (data == nil) { 165 | NSLog(@"%@", error); 166 | return 1; 167 | } 168 | const void *const mh = machHeaderForImage(data.bytes); 169 | #endif 170 | 171 | unsigned long sectSize = 0; 172 | const void *const sectHead = getsectiondata(mh, "__TEXT", "__swift5_types", §Size); 173 | if (sectHead == NULL) { 174 | assert(0 && "__TEXT,__swift5_types not found"); 175 | return 1; 176 | } 177 | 178 | unsigned long const itemCount = sectSize / sizeof(int32_t); 179 | const int32_t *const items = sectHead; 180 | for (unsigned long index = 0; index < itemCount; index++) { 181 | const struct TargetContextDescriptor *const casted = relativeDirectResolve(&items[index]); 182 | switch (casted->flags.kind) { 183 | case ContextDescriptorKindModule: { 184 | const struct TargetModuleContextDescriptor *const moduleDesc = (void *)casted; 185 | // TODO 186 | printf("/* Module not implemented */\n"); 187 | (void)moduleDesc; 188 | } break; 189 | case ContextDescriptorKindExtension: { 190 | const struct TargetExtensionContextDescriptor *const extensionDesc = (void *)casted; 191 | // TODO 192 | printf("/* Extension not implemented */\n"); 193 | (void)extensionDesc; 194 | } break; 195 | case ContextDescriptorKindAnonymous: { 196 | const struct TargetAnonymousContextDescriptor *const anonymousDesc = (void *)casted; 197 | // TODO 198 | printf("/* Anonymous not implemented */\n"); 199 | (void)anonymousDesc; 200 | } break; 201 | case ContextDescriptorKindProtocol: { 202 | const struct TargetProtocolDescriptor *const protocolDesc = (void *)casted; 203 | // TODO 204 | printf("/* Protocol not implemented */\n"); 205 | (void)protocolDesc; 206 | } break; 207 | case ContextDescriptorKindOpaqueType: { 208 | const struct TargetOpaqueTypeDescriptor *const opaqueDesc = (void *)casted; 209 | // TODO 210 | printf("/* OpaqueType not implemented */\n"); 211 | (void)opaqueDesc; 212 | } break; 213 | case ContextDescriptorKindClass: { 214 | const struct TargetClassDescriptor *const classDesc = (void *)casted; 215 | const struct TargetFieldDescriptor *const fieldsDesc = relativeDirectResolve(&classDesc->typeContext.fields); 216 | 217 | const char *const superclassMangled = relativeDirectResolve(&classDesc->superclassType); 218 | if (superclassMangled != NULL) { 219 | printf("class %s: %s {\n", 220 | [fullyQualifiedName(casted) UTF8String], 221 | [demangleSwiftName(superclassMangled) UTF8String]); 222 | } else { 223 | printf("class %s {\n", [fullyQualifiedName(casted) UTF8String]); 224 | } 225 | 226 | if (fieldsDesc != NULL) { 227 | const void *const fields = (const void *)(fieldsDesc + 1); 228 | for (uint32_t i = 0; i < fieldsDesc->numFields; i++) { 229 | // byte arithmetic 230 | const struct TargetFieldRecord *const field = fields + (i * fieldsDesc->fieldRecordSize); 231 | 232 | const char *const mangledName = relativeDirectResolve(&field->mangledTypeName); 233 | NSString *name = demangleSwiftName(mangledName); 234 | 235 | printf("\t%s %s: %s // flags: %" __UINT32_FMTu__ "\n", 236 | (field->flags & TargetFieldRecordFlagsIsVar) ? "var" : "let", 237 | (const char *)relativeDirectResolve(&field->fieldName), 238 | [name UTF8String], 239 | field->flags); 240 | } 241 | } 242 | printf("}\n"); 243 | } break; 244 | case ContextDescriptorKindStruct: { 245 | const struct TargetStructDescriptor *const structDesc = (void *)casted; 246 | const struct TargetFieldDescriptor *const fieldsDesc = relativeDirectResolve(&structDesc->valueType.typeContext.fields); 247 | 248 | printf("struct %s {\n", [fullyQualifiedName(casted) UTF8String]); 249 | if (fieldsDesc != NULL) { 250 | const void *const fields = (const void *)(fieldsDesc + 1); 251 | for (uint32_t i = 0; i < fieldsDesc->numFields; i++) { 252 | // byte arithmetic 253 | const struct TargetFieldRecord *const field = fields + (i * fieldsDesc->fieldRecordSize); 254 | 255 | const char *const mangledName = relativeDirectResolve(&field->mangledTypeName); 256 | NSString *name = demangleSwiftName(mangledName); 257 | 258 | printf("\t%s %s: %s // flags: %" __UINT32_FMTu__ "\n", 259 | (field->flags & TargetFieldRecordFlagsIsVar) ? "var" : "let", 260 | (const char *)relativeDirectResolve(&field->fieldName), 261 | [name UTF8String], 262 | field->flags); 263 | } 264 | } 265 | printf("}\n"); 266 | } break; 267 | case ContextDescriptorKindEnum: { 268 | const struct TargetEnumDescriptor *const enumDesc = (void *)casted; 269 | const struct TargetFieldDescriptor *const fieldsDesc = relativeDirectResolve(&enumDesc->valueType.typeContext.fields); 270 | 271 | printf("enum %s {\n", [fullyQualifiedName(casted) UTF8String]); 272 | if (fieldsDesc != NULL) { 273 | const void *const fields = (const void *)(fieldsDesc + 1); 274 | for (uint32_t i = 0; i < fieldsDesc->numFields; i++) { 275 | // byte arithmetic 276 | const struct TargetFieldRecord *const field = fields + (i * fieldsDesc->fieldRecordSize); 277 | 278 | const char *const associativeTypes = relativeDirectResolve(&field->mangledTypeName); 279 | if (associativeTypes != NULL) { 280 | NSString *demangledName = demangleSwiftName(associativeTypes); 281 | if ([demangledName hasPrefix:@"("]) { 282 | assert([demangledName hasSuffix:@")"] && "name starts with '(' but doesn't end with ')'"); 283 | printf("\tcase %s%s // flags: %" __UINT32_FMTu__ "\n", 284 | (const char *)relativeDirectResolve(&field->fieldName), 285 | [demangledName UTF8String], 286 | field->flags); 287 | } else { 288 | printf("\tcase %s(%s) // flags: %" __UINT32_FMTu__ "\n", 289 | (const char *)relativeDirectResolve(&field->fieldName), 290 | [demangledName UTF8String], 291 | field->flags); 292 | } 293 | } else { 294 | printf("\tcase %s // flags: %" __UINT32_FMTu__ "\n", 295 | (const char *)relativeDirectResolve(&field->fieldName), 296 | field->flags); 297 | } 298 | } 299 | } 300 | printf("}\n"); 301 | } break; 302 | } 303 | } 304 | } 305 | --------------------------------------------------------------------------------