├── .github └── workflows │ └── documentation.yml ├── .gitignore ├── .travis.yml ├── LICENSE ├── Package.swift ├── README.md ├── Sources ├── CEcho │ ├── CallAccessor.c │ ├── Functions.c │ ├── ImageInspectionELF.c │ ├── ImageInspectionMachO.c │ ├── KnownMetadata.c │ ├── ValueWitnessTable.c │ └── include │ │ ├── Builtins.def │ │ ├── CallAccessor.h │ │ ├── Functions.h │ │ ├── ImageInspection.h │ │ ├── KnownMetadata.h │ │ └── ValueWitnessTable.h └── Echo │ ├── ContextDescriptor │ ├── AnonymousDescriptor.swift │ ├── ClassDescriptor.swift │ ├── ContextDescriptor.swift │ ├── ContextDescriptorValues.swift │ ├── EnumDescriptor.swift │ ├── ExtensionDescriptor.swift │ ├── FieldDescriptor.swift │ ├── GenericContext.swift │ ├── ModuleDescriptor.swift │ ├── OpaqueDescriptor.swift │ ├── ProtocolDescriptor.swift │ ├── StructDescriptor.swift │ └── TypeContextDescriptor.swift │ ├── Echo.swift │ ├── Metadata │ ├── ClassMetadata.swift │ ├── EnumMetadata.swift │ ├── EnumValueWitnessTable.swift │ ├── ExistentialMetadata.swift │ ├── ExistentialMetatypeMetadata.swift │ ├── ForeignClassMetadata.swift │ ├── FunctionMetadata.swift │ ├── HeapGenericLocalVariableMetadata.swift │ ├── HeapLocalVariableMetadata.swift │ ├── Metadata.swift │ ├── MetadataAccessFunction.swift │ ├── MetadataRequest.swift │ ├── MetadataValues.swift │ ├── MetatypeMetadata.swift │ ├── ObjCClassWrapperMetadata.swift │ ├── OpaqueMetadata.swift │ ├── StructMetadata.swift │ ├── TupleMetadata.swift │ ├── TypeMetadata.swift │ └── ValueWitnessTable.swift │ ├── Runtime │ ├── ConformanceDescriptor.swift │ ├── ExistentialContainer.swift │ ├── Functions.swift │ ├── HeapObject.swift │ ├── ImageInspection.swift │ ├── KeyPaths.swift │ ├── KnownMetadata.swift │ ├── RuntimeValues.swift │ └── WitnessTable.swift │ └── Utils │ ├── FieldType.swift │ ├── Misc.swift │ ├── RelativeDirectPointer.swift │ ├── RelativeIndirectPointer.swift │ ├── RelativeIndirectablePointer.swift │ ├── RelativeIndirectablePointerIntPair.swift │ ├── RelativePointer.swift │ └── SignedPointer.swift └── Tests ├── EchoTests ├── Context Descriptor │ ├── AnonymousDescriptor.swift │ ├── ClassDescriptor.swift │ ├── EnumDescriptor.swift │ ├── ExtensionDescriptor.swift │ ├── FieldDescriptor.swift │ ├── GenericContext.swift │ ├── ModuleDescriptor.swift │ ├── ProtocolDescriptor.swift │ └── StructDescriptor.swift ├── EchoTests.swift ├── Metadata │ ├── ClassMetadata.swift │ ├── EnumMetadata.swift │ ├── ExistentialMetadata.swift │ ├── ExistentialMetatypeMetadata.swift │ ├── FunctionMetadata.swift │ ├── MetadataAccessFunction.swift │ ├── MetatypeMetadata.swift │ ├── ObjCClassWrapperMetadata.swift │ ├── OpaqueMetadata.swift │ ├── StructMetadata.swift │ └── TupleMetadata.swift ├── Runtime │ ├── ConformanceDescriptor.swift │ ├── ExistentialContainer.swift │ └── ImageInspection.swift ├── Utils │ └── Misc.swift └── XCTestManifests.swift └── LinuxMain.swift /.github/workflows/documentation.yml: -------------------------------------------------------------------------------- 1 | name: Documentation 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - .github/workflows/documentation.yml 9 | - Sources/Echo/**.swift 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v1 18 | - name: Gen Documentation 19 | uses: SwiftDocOrg/swift-doc@master 20 | with: 21 | inputs: "Sources/Echo" 22 | module-name: Echo 23 | output: "Documentation" 24 | - name: Upload 25 | uses: SwiftDocOrg/github-wiki-publish-action@v1 26 | with: 27 | path: "Documentation" 28 | env: 29 | GH_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_PERSONAL_ACCESS_TOKEN }} 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Swift Package Manager 2 | # 3 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 4 | # Packages/ 5 | # Package.pins 6 | # Package.resolved 7 | .build/ 8 | .swiftpm/ 9 | Package.resolved 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: generic 2 | 3 | matrix: 4 | include: 5 | - os: linux 6 | dist: xenial 7 | sudo: required 8 | before_install: 9 | - sudo apt-get install clang libicu-dev 10 | before_script: 11 | - wget "https://swift.org/builds/swift-5.3.3-release/ubuntu1604/swift-5.3.3-RELEASE/swift-5.3.3-RELEASE-ubuntu16.04.tar.gz" -O "/tmp/swift-5.3.3-RELEASE-ubuntu16.04.tar.gz" 12 | - tar xzf "/tmp/swift-5.3.3-RELEASE-ubuntu16.04.tar.gz" 13 | - export PATH="$PATH:$PWD/swift-5.3.3-RELEASE-ubuntu16.04/usr/bin" 14 | - os: osx 15 | osx_image: xcode12.2 16 | 17 | script: 18 | - swift test 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Alejandro Alonso 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.1 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "Echo", 7 | products: [ 8 | .library( 9 | name: "Echo", 10 | targets: ["Echo"] 11 | ) 12 | ], 13 | dependencies: [ 14 | .package(url: "https://github.com/apple/swift-atomics.git", from: "0.0.1") 15 | ], 16 | targets: [ 17 | .target( 18 | name: "CEcho", 19 | dependencies: [] 20 | ), 21 | .target( 22 | name: "Echo", 23 | dependencies: ["CEcho", "Atomics"] 24 | ), 25 | .testTarget( 26 | name: "EchoTests", 27 | dependencies: ["Echo"] 28 | ) 29 | ] 30 | ) 31 | 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Echo 2 | 3 | [![Build Status](https://travis-ci.com/Azoy/Echo.svg?branch=main)](https://travis-ci.com/github/Azoy/Echo/pull_requests) 4 | 5 | A complete reflection package for Swift. 6 | 7 | Documentation: [Echo Wiki](https://github.com/Azoy/Echo/wiki) 8 | 9 | ## Installation 10 | 11 | Depending on what version of Swift you are targeting, the installation and what version you install can differ. 12 | 13 | Example 1: You are a developer who has an iOS app who supports Swift 5.0 - 5.3. In this case, you may want the `swift-5.0` branch to not have to worry about "is this thing I'm using only available on newer runtimes?" 14 | 15 | Example 2: You have Swift on the server using Linux and want to include this dependency. This is easy because whatever version of Swift you're running, is the branch you need to include. Your SotS project uses Swift 5.3, so simply use the `swift-5.3` branch. 16 | 17 | Example 3: You have some macOS app that is only supported on Swift 5.3 and will only ever support the latest version of Swift. Similar to the Swift of the Server example, you just need the latest `swift-5.3` branch and 18 | 19 | After understanding what branch you need to include, adding Echo is fairly simple using the Swift Pacakge Manager to add a dependency: 20 | 21 | ```swift 22 | .package(url: "https://github.com/Azoy/Echo.git", .branch("swift-5.3")) 23 | ``` 24 | 25 | 26 | ## A note about ABI stability 27 | 28 | This library exposes some metadata data structures that are not considered ABI stable on any platform, namely Tuple/Existential/Function/Metatype metadata. Although these metadata structures work if you're using the branch that matches the language runtime you're using, in the future these types may break. This is mostly a concern for those deploying applications on ABI stable platforms and need code to both work for the older runtimes and newer runtimes. If this is you, I highly suggest to read the documentation for all of the data structures you are using to ensure you're not using anything that could break in the future. If you're a developer deploying an application on a platform who does not have ABI stability, namely Linux and Windows, this should be of no concern to you because there is no guarantee anything will work across Swift versions, so it's up to you to update this dependency. 29 | 30 | ## Usage 31 | 32 | Some example scenarios: 33 | 34 | ```swift 35 | // Perhaps I need the generic argument to whatever type 36 | // is passed in here. 37 | func printGenericArgs(with x: T) { 38 | guard let metadata = reflect(x) as? TypeMetadata else { 39 | return 40 | } 41 | 42 | print(metadata.genericTypes) 43 | } 44 | 45 | // [Swift.Int] 46 | printGenericArgs(with: [1, 2, 3]) 47 | 48 | // [Swift.String, Swift.Int] 49 | printGenericArgs(with: ["Romeo": 128, "Juliet": 129]) 50 | ``` 51 | 52 | Or maybe you want to access all of the conformances for a type: 53 | 54 | ```swift 55 | func printAllConformances(for _: T.Type) { 56 | guard let metadata = reflect(T.self) as? TypeMetadata else { 57 | print("Not a TypeMetadata type!") 58 | return 59 | } 60 | 61 | for conformance in metadata.conformances { 62 | print("\(metadata.descriptor.name): \(conformance.protocol.name)") 63 | } 64 | } 65 | 66 | // ... 67 | // Int: ExpressibleByIntegerLiteral 68 | // Int: Comparable 69 | // Int: Hashable 70 | // Int: Equatable 71 | // ... 72 | printAllConformances(for: Int.self) 73 | ``` 74 | -------------------------------------------------------------------------------- /Sources/CEcho/CallAccessor.c: -------------------------------------------------------------------------------- 1 | // 2 | // CallAccessor.c 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | #include "CallAccessor.h" 10 | 11 | #if defined(__arm64e__) 12 | #include 13 | #endif 14 | 15 | #define SWIFTCC __attribute__((swiftcall)) 16 | 17 | const MetadataResponse echo_callAccessor0(const void *ptr, size_t request) { 18 | #if defined(__arm64e__) 19 | ptr = ptrauth_sign_unauthenticated(ptr, ptrauth_key_function_pointer, 0); 20 | #endif 21 | 22 | typedef SWIFTCC MetadataResponse(MetadataAccess)(size_t); 23 | 24 | return ((MetadataAccess *)ptr)(request); 25 | } 26 | 27 | const MetadataResponse echo_callAccessor1(const void *ptr, size_t request, 28 | const void *arg0) { 29 | #if defined(__arm64e__) 30 | ptr = ptrauth_sign_unauthenticated(ptr, ptrauth_key_function_pointer, 0); 31 | #endif 32 | 33 | typedef SWIFTCC MetadataResponse(MetadataAccess)(size_t, const void*); 34 | 35 | return ((MetadataAccess *)ptr)(request, arg0); 36 | } 37 | 38 | const MetadataResponse echo_callAccessor2(const void *ptr, size_t request, 39 | const void *arg0, const void *arg1) { 40 | #if defined(__arm64e__) 41 | ptr = ptrauth_sign_unauthenticated(ptr, ptrauth_key_function_pointer, 0); 42 | #endif 43 | 44 | typedef SWIFTCC MetadataResponse(MetadataAccess)(size_t, const void*, 45 | const void*); 46 | 47 | return ((MetadataAccess *)ptr)(request, arg0, arg1); 48 | } 49 | 50 | const MetadataResponse echo_callAccessor3(const void *ptr, size_t request, 51 | const void *arg0, const void *arg1, 52 | const void *arg2) { 53 | #if defined(__arm64e__) 54 | ptr = ptrauth_sign_unauthenticated(ptr, ptrauth_key_function_pointer, 0); 55 | #endif 56 | 57 | typedef SWIFTCC MetadataResponse(MetadataAccess)(size_t, const void*, 58 | const void*, const void*); 59 | 60 | return ((MetadataAccess *)ptr)(request, arg0, arg1, arg2); 61 | } 62 | 63 | const MetadataResponse echo_callAccessor(const void *ptr, size_t request, 64 | const void *args) { 65 | #if defined(__arm64e__) 66 | ptr = ptrauth_sign_unauthenticated(ptr, ptrauth_key_function_pointer, 0); 67 | #endif 68 | 69 | typedef SWIFTCC MetadataResponse(MetadataAccess)(size_t, const void*); 70 | 71 | return ((MetadataAccess *)ptr)(request, args); 72 | } 73 | -------------------------------------------------------------------------------- /Sources/CEcho/Functions.c: -------------------------------------------------------------------------------- 1 | // 2 | // Functions.c 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2019 - 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | #include "include/Functions.h" 10 | 11 | #if defined(__arm64e__) 12 | #include 13 | 14 | const void *__ptrauth_strip_asda(const void *ptr) { 15 | return ptrauth_strip(ptr, ptrauth_key_asda); 16 | } 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /Sources/CEcho/ImageInspectionELF.c: -------------------------------------------------------------------------------- 1 | // 2 | // ImageInspectionELF.c 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | #if defined(__ELF__) 10 | 11 | #ifndef _GNU_SOURCE 12 | #define _GNU_SOURCE 13 | #endif 14 | 15 | #include "ImageInspection.h" 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | // Callback used when iterating the shared objects loaded in this program's 24 | // current memory. At the moment we only care about Swift's protocol 25 | // conformances. 26 | static int imageCallback(struct dl_phdr_info *info, size_t size, void *data) { 27 | // Before doing anything here, check our cache to see if we've already added 28 | // this image's information. If not, this will cache our image name for future 29 | // iterations. 30 | if (!cacheSharedObject(info->dlpi_name)) { 31 | return 0; 32 | } 33 | 34 | // Our personal shared object in memory that isn't mapped into the process. 35 | void *so = NULL; 36 | const ElfW(Ehdr) *soHeader = NULL; 37 | 38 | // Open the full shared object file into memory so that we can inspect the 39 | // section header table. This is somewhat unfortunate, but is required 40 | // because ELF doesn't need the section header table at runtime, so it's 41 | // most likely stripped from the object currently in memory. 42 | FILE *file = fopen(info->dlpi_name, "rb"); 43 | 44 | if (file == NULL) { 45 | return 0; 46 | } 47 | 48 | // Figure out the size of the file. 49 | fseek(file, 0, SEEK_END); 50 | size_t fileSize = ftell(file); 51 | rewind(file); 52 | 53 | so = malloc(fileSize); 54 | soHeader = (ElfW(Ehdr) *)so; 55 | 56 | if (so == NULL) { 57 | return 0; 58 | } 59 | 60 | // Read the whole shared object and close our file handle now that we're done 61 | // with the file. 62 | fread(so, fileSize, 1, file); 63 | fclose(file); 64 | 65 | // The section header table is located at the section header table offset 66 | // from the base address. 67 | const ElfW(Shdr) *sections = so + soHeader->e_shoff; 68 | // Our string table section, i.e. the section that provides all the names for 69 | // the object's sections, is the shstrdx'th index into the section header 70 | // table. 71 | const ElfW(Shdr) *stringTableSection = §ions[soHeader->e_shstrndx]; 72 | // The actual string table is located at the section offset from the base 73 | // address 74 | const char *const stringTable = so + stringTableSection->sh_offset; 75 | 76 | // Loop through every section header looking for the one that describes any 77 | // Swift section that we may be interested in. 78 | for (int i = 0; i != soHeader->e_shnum; i += 1) { 79 | int sectionNameIdx = sections[i].sh_name; 80 | // Our actual section name is in the string table at our index. 81 | const char *sectionName = stringTable + sectionNameIdx; 82 | 83 | // We found Swift's protocol section! 84 | if (!strcmp(sectionName, "swift5_protocols")) { 85 | size_t sectionAddr = sections[i].sh_addr; 86 | // The actual conformances section within the program's memory is located 87 | // at the base address of this shared object's memory plus the vaddr 88 | // provided by the section header. 89 | const void *protos = (const void *)(info->dlpi_addr + sectionAddr); 90 | 91 | registerProtocols(protos, sections[i].sh_size); 92 | continue; 93 | } 94 | 95 | // We found Swift's protocol conformances section! 96 | if (!strcmp(sectionName, "swift5_protocol_conformances")) { 97 | size_t sectionAddr = sections[i].sh_addr; 98 | // The actual conformances section within the program's memory is located 99 | // at the base address of this shared object's memory plus the vaddr 100 | // provided by the section header. 101 | const void *conformances = (const void *)(info->dlpi_addr + sectionAddr); 102 | 103 | registerProtocolConformances(conformances, sections[i].sh_size); 104 | continue; 105 | } 106 | 107 | // We found Swift's type metadata section! 108 | if (!strcmp(sectionName, "swift5_type_metadata")) { 109 | size_t sectionAddr = sections[i].sh_addr; 110 | // The actual conformances section within the program's memory is located 111 | // at the base address of this shared object's memory plus the vaddr 112 | // provided by the section header. 113 | const void *types = (const void *)(info->dlpi_addr + sectionAddr); 114 | 115 | registerTypeMetadata(types, sections[i].sh_size); 116 | continue; 117 | } 118 | } 119 | 120 | // And finally, free the memory we allocated for our shared object :) 121 | free(so); 122 | 123 | return 0; 124 | } 125 | 126 | // Iterate through all of the current shared objects loaded into this 127 | // program's memory. 128 | void iterateSharedObjects() { 129 | dl_iterate_phdr(imageCallback, NULL); 130 | } 131 | 132 | #define SWIFT_REGISTER_SECTION(name, handle) \ 133 | handle(&__start_##name, &__stop_##name - &__start_##name); 134 | 135 | __attribute__((__constructor__)) 136 | static void loadImages() { 137 | // This will register the executable's protocol list. 138 | SWIFT_REGISTER_SECTION(swift5_protocols, registerProtocols) 139 | 140 | // This will register the executable's protocol conformance list. 141 | SWIFT_REGISTER_SECTION(swift5_protocol_conformances, registerProtocolConformances) 142 | 143 | // This will register the executable's type metadata list. 144 | SWIFT_REGISTER_SECTION(swift5_type_metadata, registerTypeMetadata) 145 | } 146 | 147 | #undef SWIFT_REGISTER_SECTION 148 | 149 | #endif // defined(__ELF__) 150 | -------------------------------------------------------------------------------- /Sources/CEcho/ImageInspectionMachO.c: -------------------------------------------------------------------------------- 1 | // 2 | // ImageInspectionMachO.c 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | #if defined(__MACH__) 10 | 11 | #include "ImageInspection.h" 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /Sources/CEcho/KnownMetadata.c: -------------------------------------------------------------------------------- 1 | // 2 | // KnownMetadata.c 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2019 - 2020 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | #include "include/KnownMetadata.h" 10 | 11 | // Grab the metadata pointer that's appropriate for Swift. By default, 12 | // getting the address of the reference type metadata points to the full 13 | // metadata which points to the value witness table rather than the kind. 14 | // Move the pointer down one word to be correct. 15 | #define BUILTIN(NAME, SYMBOL) \ 16 | void *getBuiltin##NAME##Metadata() { \ 17 | return &$s##SYMBOL##N + sizeof(void*); \ 18 | } 19 | #include "include/Builtins.def" 20 | 21 | #undef BUILTIN 22 | -------------------------------------------------------------------------------- /Sources/CEcho/ValueWitnessTable.c: -------------------------------------------------------------------------------- 1 | // 2 | // ValueWitnessTable.c 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | #if defined(__arm64e__) 10 | 11 | #include "ValueWitnessTable.h" 12 | 13 | // VWT functions. 14 | 15 | void *echo_vwt_initializeBufferWithCopyOfBuffer(const void *ptr, void *dest, 16 | void *src, 17 | const void *metadata) { 18 | const ValueWitnessTable *vwt = (const ValueWitnessTable *)ptr; 19 | return vwt->initializeBufferWithCopyOfBuffer(dest, src, metadata); 20 | } 21 | 22 | void echo_vwt_destroy(const void *ptr, void *value, const void *metadata) { 23 | const ValueWitnessTable *vwt = (const ValueWitnessTable *)ptr; 24 | return vwt->destroy(value, metadata); 25 | } 26 | 27 | void *echo_vwt_initializeWithCopy(const void *ptr, void *dest, void *src, 28 | const void *metadata) { 29 | const ValueWitnessTable *vwt = (const ValueWitnessTable *)ptr; 30 | return vwt->initializeWithCopy(dest, src, metadata); 31 | } 32 | 33 | void *echo_vwt_assignWithCopy(const void *ptr, void *dest, void *src, 34 | const void *metadata) { 35 | const ValueWitnessTable *vwt = (const ValueWitnessTable *)ptr; 36 | return vwt->assignWithCopy(dest, src, metadata); 37 | } 38 | 39 | void *echo_vwt_initializeWithTake(const void *ptr, void *dest, void *src, 40 | const void *metadata) { 41 | const ValueWitnessTable *vwt = (const ValueWitnessTable *)ptr; 42 | return vwt->initializeWithTake(dest, src, metadata); 43 | } 44 | 45 | void *echo_vwt_assignWithTake(const void *ptr, void *dest, void *src, 46 | const void *metadata) { 47 | const ValueWitnessTable *vwt = (const ValueWitnessTable *)ptr; 48 | return vwt->assignWithTake(dest, src, metadata); 49 | } 50 | 51 | unsigned echo_vwt_getEnumTagSinglePayload(const void *ptr, const void *instance, 52 | unsigned numEmptyCases, 53 | const void *metadata) { 54 | const ValueWitnessTable *vwt = (const ValueWitnessTable *)ptr; 55 | return vwt->getEnumTagSinglePayload(instance, numEmptyCases, metadata); 56 | } 57 | 58 | void echo_vwt_storeEnumTagSinglePayload(const void *ptr, void *instance, 59 | unsigned tag, unsigned numEmptyCases, 60 | const void *metadata) { 61 | const ValueWitnessTable *vwt = (const ValueWitnessTable *)ptr; 62 | return vwt->storeEnumTagSinglePayload(instance, tag, numEmptyCases, metadata); 63 | } 64 | 65 | // Enum VWT functions 66 | 67 | unsigned echo_vwt_getEnumTag(const void *ptr, const void *instance, 68 | const void *metadata) { 69 | const EnumValueWitnessTable *vwt = (const EnumValueWitnessTable *)ptr; 70 | return vwt->getEnumTag(instance, metadata); 71 | } 72 | 73 | void echo_vwt_destructiveProjectEnumData(const void *ptr, void *instance, 74 | const void *metadata) { 75 | const EnumValueWitnessTable *vwt = (const EnumValueWitnessTable *)ptr; 76 | return vwt->destructiveProjectEnumData(instance, metadata); 77 | } 78 | 79 | void echo_vwt_destructiveInjectEnumTag(const void *ptr, void *instance, 80 | unsigned tag, 81 | const void *metadata) { 82 | const EnumValueWitnessTable *vwt = (const EnumValueWitnessTable *)ptr; 83 | return vwt->destructiveInjectEnumTag(instance, tag, metadata); 84 | } 85 | 86 | #endif // defined(__arm64e__) 87 | -------------------------------------------------------------------------------- /Sources/CEcho/include/Builtins.def: -------------------------------------------------------------------------------- 1 | // 2 | // Builtins.def 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2019 - 2020 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | #ifndef BUILTIN 10 | #define BUILTIN(NAME, SYMBOL) 11 | #endif 12 | 13 | BUILTIN(Int1, Bi1_) 14 | BUILTIN(Int7, Bi7_) 15 | BUILTIN(Int8, Bi8_) 16 | BUILTIN(Int16, Bi16_) 17 | BUILTIN(Int32, Bi32_) 18 | BUILTIN(Int64, Bi64_) 19 | BUILTIN(Int128, Bi128_) 20 | BUILTIN(Int256, Bi256_) 21 | BUILTIN(Int512, Bi512_) 22 | 23 | BUILTIN(Word, Bw) 24 | 25 | BUILTIN(FPIEE16, Bf16_) 26 | BUILTIN(FPIEE32, Bf32_) 27 | BUILTIN(FPIEE64, Bf64_) 28 | BUILTIN(FPIEE80, Bf80_) 29 | BUILTIN(FPIEE128, Bf128_) 30 | 31 | BUILTIN(NativeObject, Bo) 32 | BUILTIN(BridgeObject, Bb) 33 | BUILTIN(RawPointer, Bp) 34 | BUILTIN(UnsafeValueBuffer, BB) 35 | 36 | BUILTIN(UnknownObject, BO) 37 | 38 | #undef BUILTIN 39 | -------------------------------------------------------------------------------- /Sources/CEcho/include/CallAccessor.h: -------------------------------------------------------------------------------- 1 | // 2 | // CallAccessor.h 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | #ifndef CALL_ACCESSOR_H 10 | #define CALL_ACCESSOR_H 11 | 12 | #include 13 | 14 | typedef struct MetadataResponse { 15 | const void *Metadata; 16 | size_t State; 17 | } MetadataResponse; 18 | 19 | const MetadataResponse echo_callAccessor0(const void *ptr, size_t request); 20 | 21 | const MetadataResponse echo_callAccessor1(const void *ptr, size_t request, 22 | const void *arg0); 23 | 24 | const MetadataResponse echo_callAccessor2(const void *ptr, size_t request, 25 | const void *arg0, const void *arg1); 26 | 27 | const MetadataResponse echo_callAccessor3(const void *ptr, size_t request, 28 | const void *arg0, const void *arg1, 29 | const void *arg2); 30 | 31 | // Where args is a list of pointers. 32 | const MetadataResponse echo_callAccessor(const void *ptr, size_t request, 33 | const void *args); 34 | 35 | #endif /* CALL_ACCESSOR_H */ 36 | -------------------------------------------------------------------------------- /Sources/CEcho/include/Functions.h: -------------------------------------------------------------------------------- 1 | // 2 | // Functions.h 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2019 - 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | #ifndef SWIFT_RUNTIME_FUNCTIONS_H 10 | #define SWIFT_RUNTIME_FUNCTIONS_H 11 | 12 | #include 13 | #include 14 | 15 | //===----------------------------------------------------------------------===// 16 | // Pointer Authentication 17 | //===----------------------------------------------------------------------===// 18 | 19 | #if defined(__arm64e__) 20 | const void *__ptrauth_strip_asda(const void *ptr); 21 | #endif 22 | 23 | //===----------------------------------------------------------------------===// 24 | // Box Functions 25 | //===----------------------------------------------------------------------===// 26 | 27 | // void swift_deallocBox(HeapObject *obj); 28 | extern void swift_deallocBox(void *heapObj); 29 | 30 | // OpaqueValue *swift_projectBox(HeapObject *obj); 31 | extern void *swift_projectBox(void *heapObj); 32 | 33 | // HeapObject *swift_allocEmptyBox(); 34 | extern void *swift_allocEmptyBox(); 35 | 36 | //===----------------------------------------------------------------------===// 37 | // Object Functions 38 | //===----------------------------------------------------------------------===// 39 | 40 | // HeapObject *swift_allocObject(Metadata *type, size_t size, size_t alignMask); 41 | extern void *swift_allocObject(void *type, size_t size, size_t alignMask); 42 | 43 | // HeapObject *swift_initStackObject(HeapMetadata *metadata, 44 | // HeapObject *obj); 45 | extern void *swift_initStackObject(void *metadata, void *obj); 46 | 47 | // void swift_verifyEndOfLifetime(HeapObject *obj); 48 | extern void swift_verifyEndOfLifetime(void *obj); 49 | 50 | // void swift_deallocObject(HeapObject *obj, size_t size, size_t alignMask); 51 | extern void swift_deallocObject(void *obj, size_t size, size_t alignMask); 52 | 53 | // void swift_deallocUninitializedObject(HeapObject *obj, size_t size, 54 | // size_t alignMask); 55 | extern void swift_deallocUninitializedObject(void *obj, size_t size, 56 | size_t alignMask); 57 | 58 | // void swift_release(HeapObject *obj); 59 | extern void swift_release(void *heapObj); 60 | 61 | // HeapObject *swift_weakLoadStrong(WeakReference *weakRef); 62 | extern void *swift_weakLoadStrong(void *weakRef); 63 | 64 | //===----------------------------------------------------------------------===// 65 | // Protocol Conformances 66 | //===----------------------------------------------------------------------===// 67 | 68 | // WitnessTable *swift_conformsToProtocol(Metadata *type, 69 | // ProtocolDescriptor *protocol); 70 | extern void *swift_conformsToProtocol(const void *type, const void *protocol); 71 | 72 | //===----------------------------------------------------------------------===// 73 | // Casting 74 | //===----------------------------------------------------------------------===// 75 | 76 | // bool swift_dynamicCast(OpaqueValue *dest, OpaqueValue *src, 77 | // const Metadata *srcType, const Metadata *targetType, 78 | // DynamicCastFlags flags); 79 | extern bool swift_dynamicCast(void *dest, void *src, const void *srcType, 80 | const void *targetType, size_t flags); 81 | 82 | //===----------------------------------------------------------------------===// 83 | // Obj-C Support 84 | //===----------------------------------------------------------------------===// 85 | 86 | #if defined(__OBJC__) 87 | #include 88 | 89 | extern Class swift_getInitializedObjCClass(Class c); 90 | 91 | #endif // defined(__OBJC__) 92 | 93 | #endif /* SWIFT_RUNTIME_FUNCTIONS_H */ 94 | -------------------------------------------------------------------------------- /Sources/CEcho/include/ImageInspection.h: -------------------------------------------------------------------------------- 1 | // 2 | // ImageInspection.h 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | #ifndef IMAGE_INSPECTION_H 10 | #define IMAGE_INSPECTION_H 11 | 12 | #include 13 | 14 | extern void registerProtocols(const char *section, size_t size); 15 | extern void registerProtocolConformances(const char *section, size_t size); 16 | extern void registerTypeMetadata(const char *section, size_t size); 17 | 18 | //===----------------------------------------------------------------------===// 19 | // Mach-O Image Inspection 20 | //===----------------------------------------------------------------------===// 21 | 22 | #if defined(__MACH__) 23 | 24 | #include 25 | #include 26 | 27 | extern void lookupSection(const struct mach_header *header, const char *segment, 28 | const char *section, 29 | void (*registerFunc)(const char *, size_t)); 30 | 31 | void _loadImageFunc(const struct mach_header *header, intptr_t size) { 32 | lookupSection(header, "__TEXT", "__swift5_proto", 33 | registerProtocolConformances); 34 | 35 | lookupSection(header, "__TEXT", "__swift5_protos", 36 | registerProtocols); 37 | 38 | lookupSection(header, "__TEXT", "__swift5_types", 39 | registerTypeMetadata); 40 | } 41 | 42 | __attribute((__constructor__)) 43 | void loadImages() { 44 | _dyld_register_func_for_add_image(_loadImageFunc); 45 | } 46 | 47 | #endif // defined(__MACH__) 48 | 49 | //===----------------------------------------------------------------------===// 50 | // ELF Image Inspection 51 | //===----------------------------------------------------------------------===// 52 | 53 | #if defined(__ELF__) 54 | 55 | #include 56 | 57 | // The Swift runtime solely uses the trick below to get all of the protocol 58 | // conformance sections whenever an image is loaded into. We can't do that for 59 | // Echo because there are plenty of conformances defined in libraries like the 60 | // standard library that we need to search through. Thus the need for the work 61 | // found in ImageInspectionELF.c 62 | 63 | // Create an empty section with the same name as one passed in to generate the 64 | // start and stop variables for that section. We do this in addition to 65 | // iterating through program headers to get the executable's sections. 66 | #define SWIFT_SECTION(name) \ 67 | __asm__("\t.section " #name ", \"a\"\n"); \ 68 | __attribute((__visibility__("hidden"), aligned(1))) extern const char __start_##name; \ 69 | __attribute((__visibility__("hidden"), aligned(1))) extern const char __stop_##name; 70 | 71 | #if defined(__cplusplus) 72 | extern "C" { 73 | #endif 74 | 75 | SWIFT_SECTION(swift5_protocols) 76 | SWIFT_SECTION(swift5_protocol_conformances) 77 | SWIFT_SECTION(swift5_type_metadata) 78 | 79 | #if defined(__cplusplus) 80 | } // extern "C" 81 | #endif 82 | 83 | #undef SWIFT_SECTION 84 | 85 | void iterateSharedObjects(); 86 | 87 | extern bool cacheSharedObject(const char *name); 88 | 89 | #endif // defined(__ELF) 90 | 91 | //===----------------------------------------------------------------------===// 92 | // COFF Image Inspection 93 | //===----------------------------------------------------------------------===// 94 | 95 | #if !defined(__ELF__) && !defined(__MACH__) 96 | #endif 97 | 98 | #endif /* IMAGE_INSPECTION_H */ 99 | -------------------------------------------------------------------------------- /Sources/CEcho/include/KnownMetadata.h: -------------------------------------------------------------------------------- 1 | // 2 | // KnownMetadata.h 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2019 - 2020 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | #ifndef KNOWN_METADATA_H 10 | #define KNOWN_METADATA_H 11 | 12 | // The mangling scheme for builtin metadata is: 13 | // $s SYMBOL N 14 | // $s = Swift mangling prefix 15 | // SYMBOL = The builtin type mangling 16 | // N = Metadata 17 | // Example: Builtin.NativeObject Metadata is $sBoN 18 | // 19 | // Reference the runtime defined metadata variable for builtin types. 20 | #define BUILTIN(NAME, SYMBOL) \ 21 | extern void $s##SYMBOL##N; 22 | #include "Builtins.def" 23 | 24 | #undef BUILTIN 25 | 26 | // Define this utility function because you can't see variables that start with 27 | // $ in Swift. 28 | #define BUILTIN(NAME, SYMBOL) \ 29 | void *getBuiltin##NAME##Metadata(); 30 | #include "Builtins.def" 31 | 32 | #undef BUILTIN 33 | 34 | #endif /* KNOWN_METADATA_H */ 35 | -------------------------------------------------------------------------------- /Sources/CEcho/include/ValueWitnessTable.h: -------------------------------------------------------------------------------- 1 | // 2 | // ValueWitnessTable.h 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | #ifndef VALUE_WITNESS_TABLE_H 10 | #define VALUE_WITNESS_TABLE_H 11 | 12 | #if defined(__arm64e__) 13 | 14 | #include 15 | #include 16 | 17 | #define VWT_FP(key) __ptrauth_swift_value_witness_function_pointer(key) 18 | 19 | // VWT 20 | 21 | typedef struct ValueWitnessTable { 22 | void *(* VWT_FP(0xda4a) initializeBufferWithCopyOfBuffer)(void *, void *, 23 | const void *); 24 | void (* VWT_FP(0x04f8) destroy)(void *, const void *); 25 | void *(* VWT_FP(0xe3ba) initializeWithCopy)(void *, void *, const void *); 26 | void *(* VWT_FP(0x8751) assignWithCopy)(void *, void *, const void *); 27 | void *(* VWT_FP(0x48d8) initializeWithTake)(void *, void *, const void *); 28 | void *(* VWT_FP(0xefda) assignWithTake)(void *, void *, const void *); 29 | unsigned (* VWT_FP(0x60f0) getEnumTagSinglePayload)(const void *, unsigned, 30 | const void *); 31 | void (* VWT_FP(0xa0d1) storeEnumTagSinglePayload)(void *, unsigned, unsigned, 32 | const void *); 33 | 34 | size_t size; 35 | size_t stride; 36 | unsigned flags; 37 | unsigned extraInhabitantCount; 38 | } ValueWitnessTable; 39 | 40 | // Enum VWT 41 | 42 | typedef struct EnumValueWitnessTable { 43 | ValueWitnessTable base; 44 | 45 | int (* VWT_FP(0xa3b5) getEnumTag)(const void *, const void *); 46 | void (* VWT_FP(0x041d) destructiveProjectEnumData)(void *, const void *); 47 | void (* VWT_FP(0xb2e4) destructiveInjectEnumTag)(void *, unsigned, 48 | const void *); 49 | } EnumValueWitnessTable; 50 | 51 | // A note for the following: All of these functions are required for platforms 52 | // that utilize pointer authentication because we need to sign the vwt function 53 | // pointers. We can't import these structs in Swift due to the clang importer 54 | // not importing members who have ptrauth qualifiers. Define these stubs that 55 | // call these functions. The first parameter for all of these stubs is a pointer 56 | // to the VWT. 57 | 58 | // VWT functions. 59 | 60 | void *echo_vwt_initializeBufferWithCopyOfBuffer(const void *, void *, void *, 61 | const void *); 62 | void echo_vwt_destroy(const void *, void *, const void *); 63 | void *echo_vwt_initializeWithCopy(const void *, void *, void *, const void *); 64 | void *echo_vwt_assignWithCopy(const void *, void *, void *, const void *); 65 | void *echo_vwt_initializeWithTake(const void *, void *, void *, const void *); 66 | void *echo_vwt_assignWithTake(const void *, void *, void *, const void *); 67 | unsigned echo_vwt_getEnumTagSinglePayload(const void *, const void *, unsigned, 68 | const void *); 69 | void echo_vwt_storeEnumTagSinglePayload(const void *, void *, unsigned, unsigned, 70 | const void *); 71 | 72 | // Enum VWT functions 73 | 74 | unsigned echo_vwt_getEnumTag(const void *, const void *, const void *); 75 | void echo_vwt_destructiveProjectEnumData(const void *, void *, const void *); 76 | void echo_vwt_destructiveInjectEnumTag(const void *, void *, unsigned, 77 | const void *); 78 | 79 | #endif // defined(__arm64e__) 80 | 81 | #endif /* VALUE_WITNESS_TABLE_H */ 82 | -------------------------------------------------------------------------------- /Sources/Echo/ContextDescriptor/AnonymousDescriptor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AnonymousDescriptor.swift 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2019 - 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | /// An anonymous descriptor describes a context which is anonymous, like a 10 | /// function or a private declaration will have an anonymous parent context. 11 | /// 12 | /// ABI Stability: 13 | /// 14 | /// | macOS | iOS/tvOS | watchOS | Linux | Windows | 15 | /// |-------|----------|---------|-------|---------| 16 | /// | 10.14 | 12.2 | 5.2 | NA | NA | 17 | /// 18 | public struct AnonymousDescriptor: ContextDescriptor, LayoutWrapper { 19 | typealias Layout = _ContextDescriptor 20 | 21 | // Backing context descriptor pointer. 22 | public let ptr: UnsafeRawPointer 23 | 24 | /// The specific flags that describe an anonymous descriptor. 25 | public var anonymousFlags: Flags { 26 | Flags(bits: layout._flags.kindSpecificFlags) 27 | } 28 | 29 | /// The trailing mangled name for the anonymous context. 30 | public var mangledName: String? { 31 | guard anonymousFlags.hasMangledName else { 32 | return nil 33 | } 34 | 35 | var offset = trailing 36 | 37 | // If this context isn't generic, then the only trailing member is the 38 | // mangled name. Otherwise, we need to calculate how large the generic 39 | // context is and find the mangled name after it. 40 | if flags.isGeneric { 41 | offset += genericContext!.size 42 | } 43 | 44 | let address = offset.relativeDirectAddress(as: CChar.self) 45 | return address.string 46 | } 47 | } 48 | 49 | extension AnonymousDescriptor: Equatable {} 50 | 51 | -------------------------------------------------------------------------------- /Sources/Echo/ContextDescriptor/ContextDescriptor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContextDescriptor.swift 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2019 - 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | /// A context descriptor describes any entity in Swift that contains other 10 | /// types or contexts. 11 | /// 12 | /// ABI Stability: Stable since the following 13 | /// 14 | /// | macOS | iOS/tvOS | watchOS | Linux | Windows | 15 | /// |-------|----------|---------|-------|---------| 16 | /// | 10.14 | 12.2 | 5.2 | NA | NA | 17 | /// 18 | public protocol ContextDescriptor { 19 | /// Backing context descriptor pointer. 20 | var ptr: UnsafeRawPointer { get } 21 | } 22 | 23 | extension ContextDescriptor { 24 | var _descriptor: _ContextDescriptor { 25 | ptr.load(as: _ContextDescriptor.self) 26 | } 27 | 28 | /// The flags that describe this context including what kind it is, 29 | /// whether it's a generic context, and whether the context is unique along 30 | /// with other things. 31 | public var flags: ContextDescriptorFlags { 32 | _descriptor._flags 33 | } 34 | 35 | /// The parent context which this context descriptor resides in. 36 | public var parent: ContextDescriptor? { 37 | guard !_descriptor._parent.isNull else { 38 | return nil 39 | } 40 | 41 | let offset = ptr.offset(of: 1, as: Int32.self) 42 | 43 | return getContextDescriptor( 44 | at: _descriptor._parent.address(from: offset) 45 | ) 46 | } 47 | 48 | var genericContextOffset: Int { 49 | switch self { 50 | case is AnonymousDescriptor: 51 | return MemoryLayout<_ContextDescriptor>.size 52 | case is ClassDescriptor: 53 | return MemoryLayout<_ClassDescriptor>.size 54 | case is EnumDescriptor: 55 | return MemoryLayout<_EnumDescriptor>.size 56 | case is ExtensionDescriptor: 57 | return MemoryLayout<_ExtensionDescriptor>.size 58 | case is OpaqueDescriptor: 59 | return MemoryLayout<_OpaqueDescriptor>.size 60 | case is StructDescriptor: 61 | return MemoryLayout<_StructDescriptor>.size 62 | default: 63 | fatalError() 64 | } 65 | } 66 | 67 | /// The generic information about a context including the number of generic 68 | /// parameters, number of requirements, the parameters themselves, the 69 | /// requirements themselves, and much more. 70 | public var genericContext: GenericContext? { 71 | guard flags.isGeneric else { return nil } 72 | 73 | if let type = self as? TypeContextDescriptor { 74 | return type.typeGenericContext.baseContext 75 | } 76 | 77 | return GenericContext(ptr: ptr + genericContextOffset) 78 | } 79 | } 80 | 81 | // This is used as a base type that can be bit casted to. 82 | struct _ContextDescriptor { 83 | let _flags: ContextDescriptorFlags 84 | let _parent: RelativeIndirectablePointer<_ContextDescriptor> 85 | } 86 | 87 | func getContextDescriptor( 88 | at ptr: UnsafeRawPointer 89 | ) -> ContextDescriptor { 90 | #if _ptrauth(_arm64e) 91 | let ptr = __ptrauth_strip_asda(ptr)! 92 | #endif 93 | 94 | let flags = ptr.load(as: ContextDescriptorFlags.self) 95 | 96 | switch flags.kind { 97 | case .anonymous: 98 | return AnonymousDescriptor(ptr: ptr) 99 | case .class: 100 | return ClassDescriptor(ptr: ptr) 101 | case .enum: 102 | return EnumDescriptor(ptr: ptr) 103 | case .extension: 104 | return ExtensionDescriptor(ptr: ptr) 105 | case .module: 106 | return ModuleDescriptor(ptr: ptr) 107 | case .opaqueType: 108 | return OpaqueDescriptor(ptr: ptr) 109 | case .protocol: 110 | return ProtocolDescriptor(ptr: ptr) 111 | case .struct: 112 | return StructDescriptor(ptr: ptr) 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /Sources/Echo/ContextDescriptor/EnumDescriptor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EnumDescriptor.swift 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2019 - 2020 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | /// An enum descriptor that describes some enum context. 10 | /// 11 | /// ABI Stability: Stable since the following 12 | /// 13 | /// | macOS | iOS/tvOS | watchOS | Linux | Windows | 14 | /// |-------|----------|---------|-------|---------| 15 | /// | 10.14 | 12.2 | 5.2 | NA | NA | 16 | /// 17 | public struct EnumDescriptor: TypeContextDescriptor, LayoutWrapper { 18 | typealias Layout = _EnumDescriptor 19 | 20 | /// Backing context descriptor pointer. 21 | public let ptr: UnsafeRawPointer 22 | 23 | /// The number of enum cases which have payloads (associated types). 24 | /// Ex. case number(Int) 25 | public var numPayloadCases: Int { 26 | Int(layout._numPayloadCasesAndPayloadSizeOffset) & 0xFFFFFF 27 | } 28 | 29 | /// The payload size offset is the number of words from the metadata pointer 30 | /// to the payload size, if any. 31 | public var payloadSizeOffset: Int { 32 | Int((layout._numPayloadCasesAndPayloadSizeOffset & 0xFF000000) >> 24) 33 | } 34 | 35 | /// Whether or not this enum has a payload size offset. 36 | public var hasPayloadSizeOffset: Bool { 37 | payloadSizeOffset != 0 38 | } 39 | 40 | /// The number of enum cases that have no payload. 41 | /// Ex. case blue 42 | public var numEmptyCases: Int { 43 | Int(layout._numEmptyCases) 44 | } 45 | 46 | /// The total number of cases this enum has. 47 | public var numCases: Int { 48 | numEmptyCases + numPayloadCases 49 | } 50 | 51 | /// The foreign metadata initialization info for this enum metadata, if it 52 | /// has any. 53 | public var foreignMetadataInitialization: ForeignMetadataInitialization? { 54 | guard typeFlags.metadataInitKind == .foreign else { 55 | return nil 56 | } 57 | 58 | var offset = 0 59 | 60 | if flags.isGeneric { 61 | offset += typeGenericContext.size 62 | } 63 | 64 | return ForeignMetadataInitialization(ptr: trailing + offset) 65 | } 66 | 67 | /// The singleton metadata initialization info for this enum metadata, if it 68 | /// has any. 69 | public var singletonMetadataInitialization: SingletonMetadataInitialization? { 70 | guard typeFlags.metadataInitKind == .singleton else { 71 | return nil 72 | } 73 | 74 | var offset = 0 75 | 76 | if flags.isGeneric { 77 | offset += typeGenericContext.size 78 | } 79 | 80 | return SingletonMetadataInitialization(ptr: trailing + offset) 81 | } 82 | } 83 | 84 | extension EnumDescriptor: Equatable {} 85 | 86 | struct _EnumDescriptor { 87 | let _base: _TypeDescriptor 88 | let _numPayloadCasesAndPayloadSizeOffset: UInt32 89 | let _numEmptyCases: UInt32 90 | } 91 | -------------------------------------------------------------------------------- /Sources/Echo/ContextDescriptor/ExtensionDescriptor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExtensionDescriptor.swift 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2019 - 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | /// An extension descriptor that describes some extension context. 10 | /// 11 | /// ABI Stability: Stable since the following 12 | /// 13 | /// | macOS | iOS/tvOS | watchOS | Linux | Windows | 14 | /// |-------|----------|---------|-------|---------| 15 | /// | 10.14 | 12.2 | 5.2 | NA | NA | 16 | /// 17 | public struct ExtensionDescriptor: ContextDescriptor, LayoutWrapper { 18 | typealias Layout = _ExtensionDescriptor 19 | 20 | /// Backing context descriptor pointer. 21 | public let ptr: UnsafeRawPointer 22 | 23 | /// The mangled name which this extension extends. 24 | /// Ex. If this extension extends Int, this mangled name might be 25 | /// Si or some symbolic reference to Int's context descriptor. 26 | public var extendedContext: UnsafeRawPointer { 27 | address(for: \._extendedContext) 28 | } 29 | } 30 | 31 | extension ExtensionDescriptor: Equatable {} 32 | 33 | struct _ExtensionDescriptor { 34 | let _base: _ContextDescriptor 35 | let _extendedContext: RelativeDirectPointer 36 | } 37 | -------------------------------------------------------------------------------- /Sources/Echo/ContextDescriptor/FieldDescriptor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FieldDescriptor.swift 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2019 - 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | /// A special descriptor that describes a type's fields. 10 | /// 11 | /// ABI Stability: Stable since the following 12 | /// 13 | /// | macOS | iOS/tvOS | watchOS | Linux | Windows | 14 | /// |-------|----------|---------|-------|---------| 15 | /// | 10.14 | 12.2 | 5.2 | NA | NA | 16 | /// 17 | public struct FieldDescriptor: LayoutWrapper { 18 | typealias Layout = _FieldDescriptor 19 | 20 | /// Backing field descriptor pointer. 21 | let ptr: UnsafeRawPointer 22 | 23 | /// Whether or not this field descriptor has a mangled name. 24 | public var hasMangledTypeName: Bool { 25 | layout._mangledTypeName.offset != 0 26 | } 27 | 28 | /// The mangled name for this field descriptor. 29 | public var mangledTypeName: UnsafeRawPointer { 30 | address(for: \._mangledTypeName) 31 | } 32 | 33 | /// The superclass mangled name for a class. 34 | public var superclass: UnsafeRawPointer { 35 | address(for: \._superclass) 36 | } 37 | 38 | /// The kind of field descriptor this is. 39 | public var kind: Kind { 40 | Kind(rawValue: layout._kind)! 41 | } 42 | 43 | /// The size in bytes of each field record. 44 | public var recordSize: Int { 45 | Int(layout._recordSize) 46 | } 47 | 48 | /// The number of fields (properties) that this type declares. 49 | public var numFields: Int { 50 | Int(layout._numFields) 51 | } 52 | 53 | /// An array of this type's field records. 54 | public var records: [FieldRecord] { 55 | Array(unsafeUninitializedCapacity: numFields) { 56 | for i in 0 ..< numFields { 57 | let address = trailing.offset(of: i, as: _FieldRecord.self) 58 | $0[i] = FieldRecord(ptr: address) 59 | } 60 | 61 | $1 = numFields 62 | } 63 | } 64 | } 65 | 66 | /// A record that describes a single stored property or an enum case. 67 | /// 68 | /// ABI Stability: Stable since the following 69 | /// 70 | /// | macOS | iOS/tvOS | watchOS | Linux | Windows | 71 | /// |-------|----------|---------|-------|---------| 72 | /// | 10.14 | 12.2 | 5.2 | NA | NA | 73 | /// 74 | public struct FieldRecord: LayoutWrapper { 75 | typealias Layout = _FieldRecord 76 | 77 | /// Backing field record pointer. 78 | let ptr: UnsafeRawPointer 79 | 80 | /// The flags that describe this field record. 81 | public var flags: Flags { 82 | layout._flags 83 | } 84 | 85 | /// Whether or not this record has a mangled type name that describes the 86 | /// fields type. 87 | public var hasMangledTypeName: Bool { 88 | layout._mangledTypeName.offset != 0 89 | } 90 | 91 | /// The mangled type name that demangles to the field's type. 92 | /// 93 | /// Note: Use this in combination with metadata's type(of:) method to get a 94 | /// field's type. 95 | /// 96 | /// Example: 97 | /// let metadata = reflect(SomeType.self) as! TypeMetadata 98 | /// for field in metadata.contextDescriptor.fields.records { 99 | /// let fieldType = metadata.type(of: field.mangledTypeName) 100 | /// } 101 | public var mangledTypeName: UnsafeRawPointer { 102 | address(for: \._mangledTypeName) 103 | } 104 | 105 | /// The name of the field. 106 | public var name: String { 107 | address(for: \._fieldName).string 108 | } 109 | 110 | /// The reference storage modifer on this field, if any. 111 | public var referenceStorage: ReferenceStorageKind { 112 | var fieldTypePtr = mangledTypeName 113 | 114 | let firstChar = fieldTypePtr.load(as: CChar.self) 115 | 116 | // If the field name begins with a relative address, skip over that. 117 | if firstChar >= 0x1 && firstChar <= 0x1F { 118 | // Skip the first character. 119 | fieldTypePtr += 1 120 | 121 | // Skip over the relative address depending on what the first character is. 122 | // You can find something similar in Utils/Misc.swift. 123 | if firstChar >= 0x1 && firstChar <= 0x17 { 124 | fieldTypePtr += 4 125 | } else { 126 | fieldTypePtr += MemoryLayout.size 127 | } 128 | } 129 | 130 | let fieldType = fieldTypePtr.string 131 | let refStorageMangle = String(fieldType.suffix(2)) 132 | 133 | guard let kind = ReferenceStorageKind(rawValue: refStorageMangle) else { 134 | return .none 135 | } 136 | 137 | return kind 138 | } 139 | } 140 | 141 | struct _FieldDescriptor { 142 | let _mangledTypeName: RelativeDirectPointer 143 | let _superclass: RelativeDirectPointer 144 | let _kind: UInt16 145 | let _recordSize: UInt16 146 | let _numFields: UInt32 147 | } 148 | 149 | struct _FieldRecord { 150 | let _flags: FieldRecord.Flags 151 | let _mangledTypeName: RelativeDirectPointer 152 | let _fieldName: RelativeDirectPointer 153 | } 154 | -------------------------------------------------------------------------------- /Sources/Echo/ContextDescriptor/ModuleDescriptor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ModuleDescriptor.swift 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2019 - 2020 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | /// A module descriptor that describes some Swift module. 10 | /// 11 | /// ABI Stability: Stable since the following 12 | /// 13 | /// | macOS | iOS/tvOS | watchOS | Linux | Windows | 14 | /// |-------|----------|---------|-------|---------| 15 | /// | 10.14 | 12.2 | 5.2 | NA | NA | 16 | /// 17 | public struct ModuleDescriptor: ContextDescriptor, LayoutWrapper { 18 | typealias Layout = _ModuleDescriptor 19 | 20 | /// Backing context descriptor pointer. 21 | public let ptr: UnsafeRawPointer 22 | 23 | /// The name of the compiled Swift module. 24 | public var name: String { 25 | address(for: \._name).string 26 | } 27 | } 28 | 29 | extension ModuleDescriptor: Equatable {} 30 | 31 | struct _ModuleDescriptor { 32 | let _base: _ContextDescriptor 33 | let _name: RelativeDirectPointer 34 | } 35 | -------------------------------------------------------------------------------- /Sources/Echo/ContextDescriptor/OpaqueDescriptor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OpaqueDescriptor.swift 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2020 - 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | /// Represents a descriptor for an opaque type. 10 | /// 11 | /// ABI Stability: Stable since the following 12 | /// 13 | /// | macOS | iOS/tvOS | watchOS | Linux | Windows | 14 | /// |-------------|------------|-----------|-------|---------| 15 | /// | 10.15 <= .3 | 13.0 <= .3 | 6.0 <= .1 | NA | NA | 16 | /// 17 | public struct OpaqueDescriptor: ContextDescriptor, LayoutWrapper { 18 | typealias Layout = _OpaqueDescriptor 19 | 20 | /// Backing OpaqueDescriptor pointer. 21 | public let ptr: UnsafeRawPointer 22 | 23 | /// The number of underlying types for this opaque type. 24 | public var numUnderlyingTypes: Int { 25 | Int(layout._base._flags.kindSpecificFlags) 26 | } 27 | 28 | /// An array of mangled type names of the underlying types composing this 29 | /// opaque type. 30 | public var underlyingTypeMangledNames: [UnsafeRawPointer] { 31 | Array(unsafeUninitializedCapacity: numUnderlyingTypes) { 32 | var start = trailing 33 | 34 | if flags.isGeneric { 35 | start += genericContext!.size 36 | } 37 | 38 | for i in 0 ..< numUnderlyingTypes { 39 | let address = start.offset(of: i, as: RelativeDirectPointer.self) 40 | $0[i] = address.relativeDirectAddress(as: CChar.self) 41 | } 42 | 43 | $1 = numUnderlyingTypes 44 | } 45 | } 46 | } 47 | 48 | extension OpaqueDescriptor: Equatable {} 49 | 50 | struct _OpaqueDescriptor { 51 | let _base: _ContextDescriptor 52 | } 53 | -------------------------------------------------------------------------------- /Sources/Echo/ContextDescriptor/ProtocolDescriptor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProtocolDescriptor.swift 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2019 - 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | /// A protocol descriptor that describes some protocol context. 10 | /// 11 | /// ABI Stability: Stable since the following 12 | /// 13 | /// | macOS | iOS/tvOS | watchOS | Linux | Windows | 14 | /// |-------|----------|---------|-------|---------| 15 | /// | 10.14 | 12.2 | 5.2 | NA | NA | 16 | /// 17 | public struct ProtocolDescriptor: ContextDescriptor, LayoutWrapper { 18 | typealias Layout = _ProtocolDescriptor 19 | 20 | /// Backing context descriptor pointer. 21 | public let ptr: UnsafeRawPointer 22 | 23 | /// The specific flags that describe a protocol descriptor. 24 | public var protocolFlags: Flags { 25 | Flags(bits: flags.kindSpecificFlags) 26 | } 27 | 28 | /// The name of this protocol. 29 | public var name: String { 30 | address(for: \._name).string 31 | } 32 | 33 | /// The number of generic requirements in this protocol's signature. 34 | public var numRequirementsInSignature: Int { 35 | Int(layout._numRequirementsInSignature) 36 | } 37 | 38 | /// The number of protocol requirements this protocol defines. 39 | public var numRequirements: Int { 40 | Int(layout._numRequirements) 41 | } 42 | 43 | /// A string of all associatedtype names. 44 | public var associatedTypeNames: String { 45 | address(for: \._associatedTypeNames).string 46 | } 47 | 48 | /// An array of all the generic requirements in this protocol's signature. 49 | public var requirementSignature: [GenericRequirementDescriptor] { 50 | Array(unsafeUninitializedCapacity: numRequirementsInSignature) { 51 | for i in 0 ..< numRequirementsInSignature { 52 | let address = trailing.offset( 53 | of: i, 54 | as: _GenericRequirementDescriptor.self 55 | ) 56 | 57 | $0[i] = GenericRequirementDescriptor(ptr: address) 58 | } 59 | 60 | $1 = numRequirementsInSignature 61 | } 62 | } 63 | 64 | /// An array of all the protocol requirements this protocol defines. 65 | public var requirements: [ProtocolRequirement] { 66 | Array(unsafeUninitializedCapacity: numRequirements) { 67 | let genericReqSize = MemoryLayout<_GenericRequirementDescriptor>.size 68 | let start = trailing + numRequirementsInSignature * genericReqSize 69 | 70 | for i in 0 ..< numRequirements { 71 | let address = start.offset(of: i, as: _ProtocolRequirement.self) 72 | $0[i] = ProtocolRequirement(ptr: address) 73 | } 74 | 75 | $1 = numRequirements 76 | } 77 | } 78 | } 79 | 80 | extension ProtocolDescriptor: Equatable {} 81 | 82 | /// A protocol requirement that is defined in some protocol. 83 | /// 84 | /// ABI Stability: Stable since the following 85 | /// 86 | /// | macOS | iOS/tvOS | watchOS | Linux | Windows | 87 | /// |-------|----------|---------|-------|---------| 88 | /// | 10.14 | 12.2 | 5.2 | NA | NA | 89 | /// 90 | public struct ProtocolRequirement: LayoutWrapper { 91 | typealias Layout = _ProtocolRequirement 92 | 93 | /// Backing protocol requirement pointer. 94 | let ptr: UnsafeRawPointer 95 | 96 | /// The flags that describe this protocol requirement. 97 | public var flags: Flags { 98 | layout._flags 99 | } 100 | } 101 | 102 | struct _ProtocolDescriptor { 103 | let _base: _ContextDescriptor 104 | let _name: RelativeDirectPointer 105 | let _numRequirementsInSignature: UInt32 106 | let _numRequirements: UInt32 107 | let _associatedTypeNames: RelativeDirectPointer 108 | } 109 | 110 | struct _ProtocolRequirement { 111 | let _flags: ProtocolRequirement.Flags 112 | let _defaultImpl: RelativeDirectPointer<()> 113 | } 114 | -------------------------------------------------------------------------------- /Sources/Echo/ContextDescriptor/StructDescriptor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StructDescriptor.swift 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2019 - 2020 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | /// A struct descriptor that describes some structure context. 10 | /// 11 | /// ABI Stability: Stable since the following 12 | /// 13 | /// | macOS | iOS/tvOS | watchOS | Linux | Windows | 14 | /// |-------|----------|---------|-------|---------| 15 | /// | 10.14 | 12.2 | 5.2 | NA | NA | 16 | /// 17 | public struct StructDescriptor: TypeContextDescriptor, LayoutWrapper { 18 | typealias Layout = _StructDescriptor 19 | 20 | /// Backing context descriptor pointer. 21 | public let ptr: UnsafeRawPointer 22 | 23 | /// The number of stored properties this struct defines. 24 | public var numFields: Int { 25 | Int(layout._numFields) 26 | } 27 | 28 | /// The number of words from the metadata pointer to the vector of field 29 | /// offsets for this struct. 30 | /// E.g. If this is 2: 31 | /// let fieldOffsetVector = metadataPtr + MemoryLayout.size * 2 32 | /// // fieldOffsetVector is a buffer pointer to Int32's that tell the 33 | /// // stored offset for a specific field at index i. 34 | public var fieldOffsetVectorOffset: Int { 35 | Int(layout._fieldOffsetVectorOffset) 36 | } 37 | 38 | /// The foreign metadata initialization info for this struct metadata, if it 39 | /// has any. 40 | public var foreignMetadataInitialization: ForeignMetadataInitialization? { 41 | guard typeFlags.metadataInitKind == .foreign else { 42 | return nil 43 | } 44 | 45 | var offset = 0 46 | 47 | if flags.isGeneric { 48 | offset += typeGenericContext.size 49 | } 50 | 51 | return ForeignMetadataInitialization(ptr: trailing + offset) 52 | } 53 | 54 | /// The singleton metadata initialization info for this struct metadata, if it 55 | /// has any. 56 | public var singletonMetadataInitialization: SingletonMetadataInitialization? { 57 | guard typeFlags.metadataInitKind == .singleton else { 58 | return nil 59 | } 60 | 61 | var offset = 0 62 | 63 | if flags.isGeneric { 64 | offset += typeGenericContext.size 65 | } 66 | 67 | return SingletonMetadataInitialization(ptr: trailing + offset) 68 | } 69 | } 70 | 71 | extension StructDescriptor: Equatable {} 72 | 73 | struct _StructDescriptor { 74 | let _base: _TypeDescriptor 75 | let _numFields: UInt32 76 | let _fieldOffsetVectorOffset: UInt32 77 | } 78 | -------------------------------------------------------------------------------- /Sources/Echo/ContextDescriptor/TypeContextDescriptor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TypeContextDescriptor.swift 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2019 - 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | /// A type context descriptor is a context descriptor who defines some new type. 10 | /// This includes structs, classes, and enums. Protocols also define a new type, 11 | /// but aren't considered type context descriptors. 12 | /// 13 | /// ABI Stability: Stable since the following 14 | /// 15 | /// | macOS | iOS/tvOS | watchOS | Linux | Windows | 16 | /// |-------|----------|---------|-------|---------| 17 | /// | 10.14 | 12.2 | 5.2 | NA | NA | 18 | /// 19 | public protocol TypeContextDescriptor: ContextDescriptor { 20 | /// The flags that describe this type context descriptor. 21 | var typeFlags: TypeContextDescriptorFlags { get } 22 | 23 | /// The name of this type. 24 | var name: String { get } 25 | 26 | /// The metadata access function. 27 | var accessor: MetadataAccessFunction { get } 28 | 29 | /// The field descriptor that describes the stored representation of this 30 | /// type. 31 | var fields: FieldDescriptor { get } 32 | 33 | /// If this type has foreign metadata initialization, return it. 34 | var foreignMetadataInitialization: ForeignMetadataInitialization? { get } 35 | 36 | /// If this type has singleton metadata initialization, return it. 37 | var singletonMetadataInitialization: SingletonMetadataInitialization? { get } 38 | } 39 | 40 | extension TypeContextDescriptor { 41 | var _typeDescriptor: _TypeDescriptor { 42 | ptr.load(as: _TypeDescriptor.self) 43 | } 44 | 45 | /// The flags that describe this type context descriptor. 46 | public var typeFlags: TypeContextDescriptorFlags { 47 | TypeContextDescriptorFlags(bits: UInt64(flags.kindSpecificFlags)) 48 | } 49 | 50 | /// The name of this type. 51 | public var name: String { 52 | let offset = ptr.offset(of: 2, as: Int32.self) 53 | let address = _typeDescriptor._name.address(from: offset) 54 | return address.string 55 | } 56 | 57 | /// The metadata access function. 58 | public var accessor: MetadataAccessFunction { 59 | let offset = ptr.offset(of: 3, as: Int32.self) 60 | let accessor = _typeDescriptor._accessor.address(from: offset) 61 | return MetadataAccessFunction(ptr: accessor) 62 | } 63 | 64 | /// Whether or not this type has a field descriptor. 65 | public var isReflectable: Bool { 66 | _typeDescriptor._fields.offset != 0 67 | } 68 | 69 | /// The field descriptor that describes the stored representation of this 70 | /// type. 71 | public var fields: FieldDescriptor { 72 | let offset = ptr.offset(of: 4, as: Int32.self) 73 | let address = _typeDescriptor._fields.address(from: offset) 74 | return FieldDescriptor(ptr: address) 75 | } 76 | 77 | /// The generic context that is unique to type context descriptors. 78 | public var typeGenericContext: TypeGenericContext { 79 | TypeGenericContext(ptr: ptr + genericContextOffset) 80 | } 81 | } 82 | 83 | /// Structure that contains the completion function for initializing singleton 84 | /// foreign metadata. 85 | public struct ForeignMetadataInitialization: LayoutWrapper { 86 | typealias Layout = _ForeignMetadataInitialization 87 | 88 | /// Backing ForeignMetadataInitialzation pointer. 89 | let ptr: UnsafeRawPointer 90 | } 91 | 92 | /// Structure that contains information needed to perform initialization of 93 | /// singleton value metadata. 94 | public struct SingletonMetadataInitialization: LayoutWrapper { 95 | typealias Layout = _SingletonMetadataInitialization 96 | 97 | /// Backing SingletonMetadataInitialization pointer. 98 | let ptr: UnsafeRawPointer 99 | } 100 | 101 | struct _TypeDescriptor { 102 | let _base: _ContextDescriptor 103 | let _name: RelativeDirectPointer 104 | let _accessor: RelativeDirectPointer 105 | let _fields: RelativeDirectPointer<_FieldDescriptor> 106 | } 107 | 108 | struct _ForeignMetadataInitialization { 109 | let _completionFunc: RelativeDirectPointer 110 | } 111 | 112 | struct _SingletonMetadataInitialization { 113 | let _initializationCache: RelativeDirectPointer 114 | 115 | // This is either a relative direct pointer to some incomplete metadata, or 116 | // a relative direct pointer to some resilent class metadata pattern. 117 | let _incompleteMetadataOrResilientPattern: Int32 118 | 119 | let _completionFunc: RelativeDirectPointer 120 | } 121 | -------------------------------------------------------------------------------- /Sources/Echo/Echo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Echo.swift 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2019 - 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | //===----------------------------------------------------------------------===// 10 | // Generic Reflect 11 | //===----------------------------------------------------------------------===// 12 | 13 | /// The main entry point to grab type metadata from some metatype. 14 | /// - Parameter type: Metatype to get metadata from. 15 | /// - Returns: `Metadata` for the given type. 16 | public func reflect(_ type: Any.Type) -> Metadata { 17 | let ptr = unsafeBitCast(type, to: UnsafeRawPointer.self) 18 | 19 | return getMetadata(at: ptr) 20 | } 21 | 22 | /// The main entry point to grab type metadata from some instance. 23 | /// - Parameter instance: Any instance value to get metadata from. 24 | /// - Returns: `Metadata` for the given instance. 25 | public func reflect(_ instance: Any) -> Metadata { 26 | container(for: instance).metadata 27 | } 28 | 29 | //===----------------------------------------------------------------------===// 30 | // Class Reflect 31 | //===----------------------------------------------------------------------===// 32 | 33 | /// The main entry point to grab a `class`'s metadata from some metatype that 34 | /// represents a `class`. 35 | /// - Parameter type: Metatype to get struct metadata from. 36 | /// - Returns: `ClassMetadata` for the given metatype. 37 | public func reflectClass(_ type: Any.Type) -> ClassMetadata? { 38 | let ptr = unsafeBitCast(type, to: UnsafeRawPointer.self) 39 | let kind = getMetadataKind(at: ptr) 40 | 41 | guard kind == .class || kind == .objcClassWrapper else { 42 | return nil 43 | } 44 | 45 | if kind == .class { 46 | return ClassMetadata(ptr: ptr) 47 | } else { 48 | return ObjCClassWrapperMetadata(ptr: ptr).classMetadata 49 | } 50 | } 51 | 52 | /// The main entry point to grab a `class`'s metadata from some instance. 53 | /// - Parameter instance: Any instance value of a `class` to get metadata from. 54 | /// - Returns: `ClassMetadata` for the given instance. 55 | public func reflectClass(_ instance: Any) -> ClassMetadata? { 56 | let box = container(for: instance) 57 | 58 | guard box.metadata.kind == .class || 59 | box.metadata.kind == .objcClassWrapper else { 60 | return nil 61 | } 62 | 63 | // I explicitly construct a new class metadata value here because I don't 64 | // want to have to cast the existential Metadata to ClassMetadata for 65 | // performance reasons. 66 | if box.metadata.kind == .class { 67 | return ClassMetadata(ptr: box.metadata.ptr) 68 | } else { 69 | return ObjCClassWrapperMetadata(ptr: box.metadata.ptr).classMetadata 70 | } 71 | } 72 | 73 | //===----------------------------------------------------------------------===// 74 | // Enum Reflect 75 | //===----------------------------------------------------------------------===// 76 | 77 | /// The main entry point to grab an `enum`'s metadata from some metatype that 78 | /// represents a `enum`. 79 | /// - Parameter type: Metatype to get `enum` metadata from. 80 | /// - Returns: `EnumMetadata` for the given metatype. 81 | public func reflectEnum(_ type: Any.Type) -> EnumMetadata? { 82 | let ptr = unsafeBitCast(type, to: UnsafeRawPointer.self) 83 | let kind = getMetadataKind(at: ptr) 84 | 85 | guard kind == .enum || kind == .optional else { 86 | return nil 87 | } 88 | 89 | return EnumMetadata(ptr: ptr) 90 | } 91 | 92 | /// The main entry point to grab a `enum`'s metadata from some instance. 93 | /// - Parameter instance: Any instance value of a `enum` to get metadata from. 94 | /// - Returns: `EnumMetadata` for the given instance. 95 | public func reflectEnum(_ instance: Any) -> EnumMetadata? { 96 | let box = container(for: instance) 97 | 98 | guard box.metadata.kind == .enum || box.metadata.kind == .optional else { 99 | return nil 100 | } 101 | 102 | // I explicitly construct a new enum metadata value here because I don't 103 | // want to have to cast the existential Metadata to EnumMetadata for 104 | // performance reasons. 105 | return EnumMetadata(ptr: box.metadata.ptr) 106 | } 107 | 108 | //===----------------------------------------------------------------------===// 109 | // Struct Reflect 110 | //===----------------------------------------------------------------------===// 111 | 112 | /// The main entry point to grab a `struct`'s metadata from some metatype that 113 | /// represents a `struct`. 114 | /// - Parameter type: Metatype to get struct metadata from. 115 | /// - Returns: `StructMetadata` for the given metatype. 116 | public func reflectStruct(_ type: Any.Type) -> StructMetadata? { 117 | let ptr = unsafeBitCast(type, to: UnsafeRawPointer.self) 118 | 119 | guard getMetadataKind(at: ptr) == .struct else { 120 | return nil 121 | } 122 | 123 | return StructMetadata(ptr: ptr) 124 | } 125 | 126 | /// The main entry point to grab a `struct`'s metadata from some instance. 127 | /// - Parameter instance: Any instance value of a `struct` to get metadata from. 128 | /// - Returns: `StructMetadata` for the given instance. 129 | public func reflectStruct(_ instance: Any) -> StructMetadata? { 130 | let box = container(for: instance) 131 | 132 | guard box.metadata.kind == .struct else { 133 | return nil 134 | } 135 | 136 | // I explicitly construct a new struct metadata value here because I don't 137 | // want to have to cast the existential Metadata to StructMetadata for 138 | // performance reasons. 139 | return StructMetadata(ptr: box.metadata.ptr) 140 | } 141 | 142 | //===----------------------------------------------------------------------===// 143 | // Container 144 | //===----------------------------------------------------------------------===// 145 | 146 | /// Given an `Any` value, return the container that represents the value. 147 | /// - Parameter instance: Some instance of `Any` 148 | /// - Returns: The existential container that said instance represents. 149 | public func container(for instance: Any) -> AnyExistentialContainer { 150 | var box = unsafeBitCast(instance, to: AnyExistentialContainer.self) 151 | 152 | while box.type == Any.self { 153 | box = box.projectValue().load(as: AnyExistentialContainer.self) 154 | } 155 | 156 | return box 157 | } 158 | -------------------------------------------------------------------------------- /Sources/Echo/Metadata/ClassMetadata.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ClassMetadata.swift 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2019 - 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | /// The metadata structure that represents a `class` type in Swift. 10 | /// 11 | /// ABI Stability: Stable since the following 12 | /// 13 | /// | macOS | iOS/tvOS | watchOS | Linux | Windows | 14 | /// |-------|----------|---------|-------|---------| 15 | /// | 10.14 | 12.2 | 5.2 | NA | NA | 16 | /// 17 | public struct ClassMetadata: TypeMetadata, LayoutWrapper { 18 | typealias Layout = _ClassMetadata 19 | 20 | /// Backing class metadata pointer. 21 | public let ptr: UnsafeRawPointer 22 | 23 | /// The class context descriptor that describes this class. 24 | public var descriptor: ClassDescriptor { 25 | precondition(isSwiftClass) 26 | return ClassDescriptor(ptr: layout._descriptor.signed) 27 | } 28 | 29 | /// The Objective-C ISA pointer, if it has one. 30 | public var isaPointer: UnsafeRawPointer? { 31 | let int = ptr.load(as: Int.self) 32 | guard int != 0 else { 33 | return nil 34 | } 35 | 36 | return UnsafeRawPointer(bitPattern: int)! 37 | } 38 | 39 | /// The superclass type that this class inherits from, if it inherits one at 40 | /// all. 41 | public var superclassType: Any.Type? { 42 | layout._superclass 43 | } 44 | 45 | /// The superclass type metadata that this class inherits from, it it inherits 46 | /// one at all. 47 | public var superclassMetadata: ClassMetadata? { 48 | superclassType.map { reflectClass($0)! } 49 | } 50 | 51 | /// The offset of the address point within the class. 52 | public var classAddressPoint: Int { 53 | Int(layout._classAddressPoint) 54 | } 55 | 56 | /// The specific flags that describe this class metadata. 57 | public var flags: Flags { 58 | layout._flags 59 | } 60 | 61 | /// The size of the class including headers and suffixes. 62 | public var classSize: Int { 63 | Int(layout._classSize) 64 | } 65 | 66 | // FIXME: Below isn't quite right yet... It doesn't take into account what's 67 | // on disk and what's currently being run. 68 | // See: https://twitter.com/slava_pestov/status/1185589328526876672 69 | 70 | /// Whether or not this class was defined in Swift. 71 | public var isSwiftClass: Bool { 72 | #if canImport(Darwin) 73 | // Xcode uses this and older runtimes do too 74 | var mask = 0x1 75 | 76 | // Swift in the OS uses 0x2 77 | if #available(macOS 10.14.4, iOS 12.2, tvOS 12.2, watchOS 5.2, *) { 78 | mask = 0x2 79 | } 80 | #else 81 | let mask = 0x1 82 | #endif 83 | 84 | return Int(bitPattern: layout._rodata) & mask != 0 85 | } 86 | 87 | /// The address point for instances of this type. 88 | public var instanceAddressPoint: Int { 89 | Int(layout._instanceAddressPoint) 90 | } 91 | 92 | /// The required size of instances of this type. 93 | public var instanceSize: Int { 94 | Int(layout._instanceSize) 95 | } 96 | 97 | /// The alignment mask of the address point for instances of this type. 98 | public var instanceAlignmentMask: Int { 99 | Int(layout._instanceAlignMask) 100 | } 101 | 102 | /// An array of field offsets for this class's stored representation. 103 | public var fieldOffsets: [Int] { 104 | Array(unsafeUninitializedCapacity: descriptor.numFields) { 105 | let start = ptr.offset(of: descriptor.fieldOffsetVectorOffset) 106 | 107 | for i in 0 ..< descriptor.numFields { 108 | let offset = start.load( 109 | fromByteOffset: i * MemoryLayout.size, 110 | as: Int.self 111 | ) 112 | 113 | $0[i] = offset 114 | } 115 | 116 | $1 = descriptor.numFields 117 | } 118 | } 119 | } 120 | 121 | extension ClassMetadata: Equatable {} 122 | 123 | struct _ClassMetadata { 124 | let _kind: Int 125 | let _superclass: Any.Type? 126 | let _reserved: (Int, Int) 127 | let _rodata: UnsafeRawPointer 128 | let _flags: ClassMetadata.Flags 129 | let _instanceAddressPoint: UInt32 130 | let _instanceSize: UInt32 131 | let _instanceAlignMask: UInt16 132 | let _runtimeReserved: UInt16 133 | let _classSize: UInt32 134 | let _classAddressPoint: UInt32 135 | let _descriptor: SignedPointer 136 | let _ivarDestroyer: UnsafeRawPointer 137 | } 138 | -------------------------------------------------------------------------------- /Sources/Echo/Metadata/EnumMetadata.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EnumMetadata.swift 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2019 - 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | /// The metadata structure that represents an `enum` type in Swift. 10 | /// 11 | /// ABI Stability: Stable since the following 12 | /// 13 | /// | macOS | iOS/tvOS | watchOS | Linux | Windows | 14 | /// |-------|----------|---------|-------|---------| 15 | /// | 10.14 | 12.2 | 5.2 | NA | NA | 16 | /// 17 | public struct EnumMetadata: TypeMetadata, LayoutWrapper { 18 | typealias Layout = _EnumMetadata 19 | 20 | /// Backing enum metadata pointer. 21 | public let ptr: UnsafeRawPointer 22 | 23 | /// The enum context descriptor that describes this enum. 24 | public var descriptor: EnumDescriptor { 25 | EnumDescriptor(ptr: layout._descriptor.signed) 26 | } 27 | } 28 | 29 | extension EnumMetadata: Equatable {} 30 | 31 | struct _EnumMetadata { 32 | let _kind: Int 33 | let _descriptor: SignedPointer 34 | } 35 | -------------------------------------------------------------------------------- /Sources/Echo/Metadata/EnumValueWitnessTable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EnumValueWitnessTable.swift 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2019 - 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | /// The value witness table for enums that have enum specific value witness 10 | /// functions. 11 | /// 12 | /// ABI Stability: Stable since the following 13 | /// 14 | /// | macOS | iOS/tvOS | watchOS | Linux | Windows | 15 | /// |-------|----------|---------|-------|---------| 16 | /// | 10.14 | 12.2 | 5.2 | NA | NA | 17 | /// 18 | public struct EnumValueWitnessTable: LayoutWrapper { 19 | typealias Layout = SignedPointer 20 | 21 | /// Backing enum value witness table pointer. 22 | let ptr: UnsafeRawPointer 23 | 24 | var _enumVwt: _EnumValueWitnessTable { 25 | layout.signed.load(as: _EnumValueWitnessTable.self) 26 | } 27 | 28 | /// The base value witness table. 29 | public var vwt: ValueWitnessTable { 30 | ValueWitnessTable(ptr: ptr) 31 | } 32 | 33 | /// Given an instance of an enum, retrieve the "tag", the number that 34 | /// determines which case is currently being inhabited. 35 | /// - Parameter instance: An enum instance of the type this value witness 36 | /// resides in. 37 | /// - Returns: The tag number for which case is being inhabited. 38 | public func getEnumTag(for instance: UnsafeRawPointer) -> UInt32 { 39 | #if _ptrauth(_arm64e) 40 | return echo_vwt_getEnumTag(layout.signed, instance, trailing) 41 | #else 42 | return _enumVwt._getEnumTag(instance, trailing) 43 | #endif 44 | } 45 | 46 | /// Given an instance of an enum, destructively remove the payload. 47 | /// - Parameter instance: An enum instance of the type this value witness 48 | /// resides in. 49 | public func destructiveProjectEnumData( 50 | for instance: UnsafeMutableRawPointer 51 | ) { 52 | #if _ptrauth(_arm64e) 53 | echo_vwt_destructiveProjectEnumData(layout.signed, instance, trailing) 54 | #else 55 | _enumVwt._destructiveProjectEnumData(instance, trailing) 56 | #endif 57 | } 58 | 59 | /// Given an instance of an enum and a case tag, destructively inject the tag 60 | /// into the enum instance. 61 | /// - Parameter instance: An enum instance of the type this value witness 62 | /// resides in. 63 | /// - Parameter tag: A case tag value within [0..numCases) 64 | public func destructiveInjectEnumTag( 65 | for instance: UnsafeMutableRawPointer, 66 | tag: UInt32 67 | ) { 68 | #if _ptrauth(_arm64e) 69 | echo_vwt_destructiveInjectEnumTag(layout.signed, instance, tag, trailing) 70 | #else 71 | _enumVwt._destructiveInjectEnumTag(instance, tag, trailing) 72 | #endif 73 | } 74 | } 75 | 76 | struct _EnumValueWitnessTable { 77 | let _vwt: _ValueWitnessTable 78 | let _getEnumTag: @convention(c) ( 79 | // Enum value 80 | UnsafeRawPointer, 81 | // Metadata 82 | UnsafeRawPointer 83 | ) -> UInt32 // returns the tag value (which case value in [0..numElements-1]) 84 | let _destructiveProjectEnumData: @convention(c) ( 85 | // Enum value 86 | UnsafeMutableRawPointer, 87 | // Metadata 88 | UnsafeRawPointer 89 | ) -> () 90 | let _destructiveInjectEnumTag: @convention(c) ( 91 | // Enum value 92 | UnsafeMutableRawPointer, 93 | // Tag 94 | UInt32, 95 | // Metadata 96 | UnsafeRawPointer 97 | ) -> () 98 | } 99 | -------------------------------------------------------------------------------- /Sources/Echo/Metadata/ExistentialMetadata.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExistentialMetadata.swift 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2019 - 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | /// The metadata structure that represents some existential type, mainly 10 | /// `protocol`s, in Swift. 11 | /// 12 | /// ABI Stability: Unstable across all platforms 13 | /// 14 | /// | macOS | iOS/tvOS | watchOS | Linux | Windows | 15 | /// |-------|----------|---------|-------|---------| 16 | /// | NA | NA | NA | NA | NA | 17 | /// 18 | public struct ExistentialMetadata: Metadata, LayoutWrapper { 19 | typealias Layout = _ExistentialMetadata 20 | 21 | /// Backing existential metadata pointer. 22 | public let ptr: UnsafeRawPointer 23 | 24 | /// The flags specific to existential metadata. 25 | public var flags: Flags { 26 | layout._flags 27 | } 28 | 29 | /// The number of protocols that compose this existential type. 30 | public var numProtocols: Int { 31 | Int(layout._numProtos) 32 | } 33 | 34 | /// The superclass type that this existential is constrained to, if any. 35 | public var superclass: Any.Type? { 36 | guard flags.hasSuperclassConstraint else { return nil } 37 | 38 | return trailing.load(as: Any.Type.self) 39 | } 40 | 41 | /// The superclass metadata that this existential is constrained to, if any. 42 | public var superclassMetadata: Metadata? { 43 | superclass.map { reflect($0) } 44 | } 45 | 46 | /// An array of protocols that make up this existential. 47 | public var protocols: [ProtocolDescriptor] { 48 | Array(unsafeUninitializedCapacity: numProtocols) { 49 | var start = trailing 50 | 51 | if flags.hasSuperclassConstraint { 52 | start = start.offset(of: 1) 53 | } 54 | 55 | for i in 0 ..< numProtocols { 56 | let proto = start.load( 57 | fromByteOffset: i * MemoryLayout.size, 58 | as: ProtocolDescriptor.self 59 | ) 60 | 61 | $0[i] = proto 62 | } 63 | 64 | $1 = numProtocols 65 | } 66 | } 67 | } 68 | 69 | extension ExistentialMetadata: Equatable {} 70 | 71 | struct _ExistentialMetadata { 72 | let _kind: Int 73 | let _flags: ExistentialMetadata.Flags 74 | let _numProtos: UInt32 75 | } 76 | -------------------------------------------------------------------------------- /Sources/Echo/Metadata/ExistentialMetatypeMetadata.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExistentialMetatypeMetadata.swift 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2020 - 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | /// The metadata structure that represents a metatype wrapping an existential 10 | /// type. 11 | /// 12 | /// ABI Stability: Unstable across all platforms 13 | /// 14 | /// | macOS | iOS/tvOS | watchOS | Linux | Windows | 15 | /// |-------|----------|---------|-------|---------| 16 | /// | NA | NA | NA | NA | NA | 17 | /// 18 | public struct ExistentialMetatypeMetadata: Metadata, LayoutWrapper { 19 | typealias Layout = _ExistentialMetatypeMetadata 20 | 21 | /// Backing existential metatype metadata pointer. 22 | public let ptr: UnsafeRawPointer 23 | 24 | /// The existential instance type that this metatype wraps. 25 | public var instanceType: Any.Type { 26 | layout._instanceType 27 | } 28 | 29 | /// The metadata for the existential instance type that this metatype wraps. 30 | public var instanceMetadata: ExistentialMetadata { 31 | reflect(instanceType) as! ExistentialMetadata 32 | } 33 | 34 | /// The flags specific to existential metadata. 35 | public var flags: ExistentialMetadata.Flags { 36 | layout._flags 37 | } 38 | } 39 | 40 | extension ExistentialMetatypeMetadata: Equatable {} 41 | 42 | struct _ExistentialMetatypeMetadata { 43 | let _kind: Int 44 | let _instanceType: Any.Type 45 | let _flags: ExistentialMetadata.Flags 46 | } 47 | -------------------------------------------------------------------------------- /Sources/Echo/Metadata/ForeignClassMetadata.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ForeignClassMetadata.swift 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2020 - 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | /// The metadata structure that represents a foreign reference counted object 10 | /// from another language that Swift observes. 11 | public struct ForeignClassMetadata: Metadata, LayoutWrapper { 12 | typealias Layout = _ForeignClassMetadata 13 | 14 | /// Backing foreign class metadata pointer. 15 | public let ptr: UnsafeRawPointer 16 | } 17 | 18 | extension ForeignClassMetadata: Equatable {} 19 | 20 | struct _ForeignClassMetadata { 21 | let _kind: Int 22 | let _descriptor: ClassDescriptor 23 | let _superclass: Any.Type? 24 | } 25 | -------------------------------------------------------------------------------- /Sources/Echo/Metadata/FunctionMetadata.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FunctionMetadata.swift 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2019 - 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | /// The metadata structure that represents a function type in Swift. 10 | /// 11 | /// ABI Stability: Unstable across all platforms 12 | /// 13 | /// | macOS | iOS/tvOS | watchOS | Linux | Windows | 14 | /// |-------|----------|---------|-------|---------| 15 | /// | NA | NA | NA | NA | NA | 16 | /// 17 | public struct FunctionMetadata: Metadata, LayoutWrapper { 18 | typealias Layout = _FunctionMetadata 19 | 20 | /// Backing function metadata pointer. 21 | public let ptr: UnsafeRawPointer 22 | 23 | /// The flags specific to function metadata. 24 | public var flags: Flags { 25 | layout._flags 26 | } 27 | 28 | /// The result type for this function. 29 | public var resultType: Any.Type { 30 | layout._result 31 | } 32 | 33 | /// The result type metadata for this function. 34 | public var resultMetadata: Metadata { 35 | reflect(resultType) 36 | } 37 | 38 | /// An array of parameter types for this function. 39 | public var paramTypes: [Any.Type] { 40 | Array(unsafeUninitializedCapacity: flags.numParams) { 41 | for i in 0 ..< flags.numParams { 42 | let type = trailing.load( 43 | fromByteOffset: i * MemoryLayout.size, 44 | as: Any.Type.self 45 | ) 46 | 47 | $0[i] = type 48 | } 49 | 50 | $1 = flags.numParams 51 | } 52 | } 53 | 54 | /// An array of parameter type metadata for this function. 55 | public var paramMetadata: [Metadata] { 56 | paramTypes.map { reflect($0) } 57 | } 58 | 59 | /// An array of parameter flags that describe each parameter for this 60 | /// function, if any. 61 | public var paramFlags: [ParamFlags] { 62 | guard flags.hasParamFlags else { return [] } 63 | 64 | return Array(unsafeUninitializedCapacity: flags.numParams) { 65 | let start = trailing.offset(of: flags.numParams) 66 | 67 | for i in 0 ..< flags.numParams { 68 | let paramFlag = start.load( 69 | fromByteOffset: i * MemoryLayout.stride, 70 | as: ParamFlags.self 71 | ) 72 | 73 | $0[i] = paramFlag 74 | } 75 | 76 | $1 = flags.numParams 77 | } 78 | } 79 | } 80 | 81 | extension FunctionMetadata: Equatable {} 82 | 83 | struct _FunctionMetadata { 84 | let _kind: Int 85 | let _flags: FunctionMetadata.Flags 86 | let _result: Any.Type 87 | } 88 | -------------------------------------------------------------------------------- /Sources/Echo/Metadata/HeapGenericLocalVariableMetadata.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HeapGenericLocalVariableMetadata.swift 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2020 - 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | /// The metadata structure that represents generic boxes that are instantiated 10 | /// at runtime. 11 | /// 12 | /// ABI Stability: Unstable across all platforms 13 | /// 14 | /// | macOS | iOS/tvOS | watchOS | Linux | Windows | 15 | /// |-------|----------|---------|-------|---------| 16 | /// | NA | NA | NA | NA | NA | 17 | /// 18 | public struct HeapGenericLocalVariableMetadata: Metadata, LayoutWrapper { 19 | typealias Layout = _HeapGenericLocalVariableMetadata 20 | 21 | /// Backing heap generic local variable metadata pointer. 22 | public let ptr: UnsafeRawPointer 23 | 24 | /// The offset from the box pointer to the value. 25 | public var offset: Int { 26 | Int(layout._offset) 27 | } 28 | 29 | /// The boxed type. 30 | public var boxedType: Any.Type { 31 | layout._boxedType 32 | } 33 | 34 | /// The metadata for the boxed type. 35 | public var boxedMetadata: Metadata { 36 | reflect(boxedType) 37 | } 38 | } 39 | 40 | extension HeapGenericLocalVariableMetadata: Equatable {} 41 | 42 | struct _HeapGenericLocalVariableMetadata { 43 | let _kind: Int 44 | let _offset: UInt32 45 | let _boxedType: Any.Type 46 | } 47 | -------------------------------------------------------------------------------- /Sources/Echo/Metadata/HeapLocalVariableMetadata.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HeapLocalVariableMetadata.swift 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2020 - 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | /// The metadata structure that represents local variables that are heap 10 | /// allocated. 11 | /// 12 | /// ABI Stability: Unstable across all platforms 13 | /// 14 | /// | macOS | iOS/tvOS | watchOS | Linux | Windows | 15 | /// |-------|----------|---------|-------|---------| 16 | /// | NA | NA | NA | NA | NA | 17 | /// 18 | public struct HeapLocalVariableMetadata: Metadata, LayoutWrapper { 19 | typealias Layout = _HeapLocalVariableMetadata 20 | 21 | /// Backing heap local variable metadata pointer. 22 | public let ptr: UnsafeRawPointer 23 | 24 | public var offsetToFirstCapture: Int { 25 | Int(layout._offsetToFirstCapture) 26 | } 27 | 28 | public var captureDescription: UnsafePointer { 29 | layout._captureDescription 30 | } 31 | } 32 | 33 | extension HeapLocalVariableMetadata: Equatable {} 34 | 35 | struct _HeapLocalVariableMetadata { 36 | let _kind: Int 37 | let _offsetToFirstCapture: UInt32 38 | let _captureDescription: UnsafePointer 39 | } 40 | -------------------------------------------------------------------------------- /Sources/Echo/Metadata/Metadata.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Metadata.swift 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2019 - 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | /// Metadata refers to the Swift metadata records in a given binary. All 10 | /// metadata records include a value witness table, which describe how to 11 | /// properly copy, destroy, etc. the memory of a type, along with a given 12 | /// "kind". 13 | public protocol Metadata { 14 | /// Backing metadata pointer. 15 | var ptr: UnsafeRawPointer { get } 16 | } 17 | 18 | extension Metadata { 19 | /// The type that this metadata represents. 20 | public var type: Any.Type { 21 | unsafeBitCast(ptr, to: Any.Type.self) 22 | } 23 | 24 | /// The value witness table for this type metadata. 25 | public var vwt: ValueWitnessTable { 26 | let address = ptr.offset(of: -1) 27 | return ValueWitnessTable(ptr: address) 28 | } 29 | 30 | /// The enum value witness table for this enum metadata. 31 | public var enumVwt: EnumValueWitnessTable { 32 | precondition(kind == .enum || kind == .optional) 33 | let address = ptr.offset(of: -1) 34 | return EnumValueWitnessTable(ptr: address) 35 | } 36 | 37 | /// The kind of metadata this is. 38 | public var kind: MetadataKind { 39 | // ISA pointer. Obj-C compatibile 40 | guard let kind = MetadataKind(rawValue: ptr.load(as: Int.self)) else { 41 | return .class 42 | } 43 | 44 | return kind 45 | } 46 | } 47 | 48 | extension Metadata { 49 | // Allocation methods 50 | 51 | /// Given a pointer to some existential container, allocate a box on the heap 52 | /// for the container to put the type's value in. 53 | /// - Parameter container: Pointer to some existential container. 54 | /// - Returns: A pointer to the newly allocated buffer. If the value is 55 | /// stored inline, then this is a pointer to the container's 56 | /// data field. 57 | public func allocateBoxForExistential( 58 | in container: UnsafeMutablePointer 59 | ) -> UnsafeRawPointer { 60 | guard !vwt.flags.isValueInline else { 61 | return container.raw 62 | } 63 | 64 | let box = swift_allocBox(for: self) 65 | container.pointee.data.0 = Int(bitPattern: box.heapObj) 66 | return box.buffer 67 | } 68 | } 69 | 70 | func getMetadataKind(at ptr: UnsafeRawPointer) -> MetadataKind { 71 | // If we can't form a metadata kind here, it is most likely an obj-c 72 | // compatible class whose kind is the ISA pointer address. 73 | guard let kind = MetadataKind(rawValue: ptr.load(as: Int.self)) else { 74 | return .class 75 | } 76 | 77 | return kind 78 | } 79 | 80 | // Determine what metadata to return given a blank pointer to some metadata. 81 | func getMetadata(at ptr: UnsafeRawPointer) -> Metadata { 82 | let int = ptr.load(as: Int.self) 83 | let kind = MetadataKind(rawValue: int) 84 | 85 | switch kind { 86 | case .class: 87 | return ClassMetadata(ptr: ptr) 88 | case .struct: 89 | return StructMetadata(ptr: ptr) 90 | case .enum, .optional: 91 | return EnumMetadata(ptr: ptr) 92 | case .foreignClass: 93 | return ForeignClassMetadata(ptr: ptr) 94 | case .opaque: 95 | return OpaqueMetadata(ptr: ptr) 96 | case .tuple: 97 | return TupleMetadata(ptr: ptr) 98 | case .function: 99 | return FunctionMetadata(ptr: ptr) 100 | case .existential: 101 | return ExistentialMetadata(ptr: ptr) 102 | case .metatype: 103 | return MetatypeMetadata(ptr: ptr) 104 | case .objcClassWrapper: 105 | return ObjCClassWrapperMetadata(ptr: ptr) 106 | case .existentialMetatype: 107 | return ExistentialMetatypeMetadata(ptr: ptr) 108 | case .heapLocalVariable: 109 | return HeapLocalVariableMetadata(ptr: ptr) 110 | case .heapGenericLocalVariable: 111 | return HeapGenericLocalVariableMetadata(ptr: ptr) 112 | default: 113 | // ISA pointer. Obj-C compatibile 114 | if int > 2047 { 115 | return ClassMetadata(ptr: ptr) 116 | } 117 | 118 | fatalError("Getting metadata for an unknwon kind: \(int)") 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /Sources/Echo/Metadata/MetadataRequest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MetadataRequest.swift 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2019 - 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | /// A metadata request is a "request" to a runtime function that returns some 10 | /// metadata in some state, either blocking until the runtime can produce said 11 | /// metadata, or non-blocking returning an abstract metadata record. 12 | public struct MetadataRequest { 13 | /// A request as represented in bits. 14 | public var bits: Int 15 | 16 | /// The go to metadata request kind which asks for complete metadata blocking 17 | /// until it returns. 18 | public static var complete: MetadataRequest { 19 | .init(state: .complete) 20 | } 21 | 22 | /// Initializes a metadata request with a given state and blocking info. 23 | /// - Parameter state: The metadata state that is being requested. 24 | /// - Parameter isNonBlocking: Whether this request doesn't block or not. 25 | public init(state: MetadataState, isNonBlocking: Bool = false) { 26 | bits = Int(state.rawValue) 27 | bits |= (isNonBlocking ? 1 : 0) << 8 28 | } 29 | 30 | /// The metadata state that is being requested with this request. 31 | public var state: MetadataState { 32 | MetadataState(rawValue: UInt8(bits & 0xFF))! 33 | } 34 | 35 | /// Whether this request doesn't block or not. 36 | public var isNonBlocking: Bool { 37 | bits & 0x100 != 0 38 | } 39 | } 40 | 41 | /// A metadata response is the return type for some runtime functions that 42 | /// either produce metadata or check it. 43 | public struct MetadataResponse { 44 | /// The type for the metadata requested. 45 | public let type: Any.Type 46 | 47 | /// The internal state value of this metadata. 48 | let _state: Int 49 | 50 | /// The metadata requested. 51 | public var metadata: Metadata { 52 | reflect(type) 53 | } 54 | 55 | /// The metadata state of the returned metadata record. 56 | public var state: MetadataState { 57 | MetadataState(rawValue: UInt8(_state))! 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Sources/Echo/Metadata/MetadataValues.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MetadataValues.swift 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2020 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | /// A discriminator that determines what type of metadata this is. 10 | public enum MetadataKind: Int { 11 | case `class` = 0 12 | 13 | // (0 | Flags.isNonHeap) 14 | case `struct` = 512 15 | 16 | // (1 | Flags.isNonHeap) 17 | case `enum` = 513 18 | 19 | // (2 | Flags.isNonHeap) 20 | case optional = 514 21 | 22 | // (3 | Flags.isNonHeap) 23 | case foreignClass = 515 24 | 25 | // (0 | Flags.isRuntimePrivate | Flags.isNonHeap) 26 | case opaque = 768 27 | 28 | // (1 | Flags.isRuntimePrivate | Flags.isNonHeap) 29 | case tuple = 769 30 | 31 | // (2 | Flags.isRuntimePrivate | Flags.isNonHeap) 32 | case function = 770 33 | 34 | // (3 | Flags.isRuntimePrivate | Flags.isNonHeap) 35 | case existential = 771 36 | 37 | // (4 | Flags.isRuntimePrivate | Flags.isNonHeap) 38 | case metatype = 772 39 | 40 | // (5 | Flags.isRuntimePrivate | Flags.isNonHeap) 41 | case objcClassWrapper = 773 42 | 43 | // (6 | Flags.isRuntimePrivate | Flags.isNonHeap) 44 | case existentialMetatype = 774 45 | 46 | // (0 | Flags.isNonType) 47 | case heapLocalVariable = 1024 48 | 49 | // (0 | Flags.isRuntimePrivate | Flags.isNonType) 50 | case heapGenericLocalVariable = 1280 51 | 52 | // (1 | Flags.isRuntimePrivate | Flags.isNonType) 53 | case errorObject = 1281 54 | } 55 | 56 | extension MetadataKind { 57 | enum Flags: Int { 58 | case isRuntimePrivate = 0x100 59 | case isNonHeap = 0x200 60 | case isNonType = 0x400 61 | } 62 | } 63 | 64 | /// The public and current state of a metadata record. 65 | public enum MetadataState: UInt8 { 66 | /// Complete metadata is the final state for all metadata in which everything 67 | /// has been initialized and computed for all type operations. 68 | case complete = 0x0 69 | 70 | /// Metadata in this state includes everything in a layout complete state 71 | /// along with class related instance layout requirements, 72 | /// initialization, and Objective-C dynamic registration. 73 | case nonTransitiveComplete = 0x1 74 | 75 | /// This metadata has its value witness table computed along with everything 76 | /// included in the abstract state. 77 | case layoutComplete = 0x3F 78 | 79 | /// Abstract metadata include it's basic type information, but may not include 80 | /// things like a value witness table. Any references to other types within 81 | /// the metadata are not created yet (superclass). 82 | case abstract = 0xFF 83 | } 84 | 85 | extension ClassMetadata { 86 | /// The flags that describe some class metadata. 87 | public struct Flags { 88 | /// Flags as represented in bits. 89 | public let bits: UInt32 90 | 91 | /// Whether this class uses pre Swift 5.0 ABI. 92 | public var isSwiftPreStableABI: Bool { 93 | bits & 0x1 != 0 94 | } 95 | 96 | /// Whether this class uses Swift's native reference counting mechanism. 97 | public var usesSwiftRefCounting: Bool { 98 | bits & 0x2 != 0 99 | } 100 | 101 | /// Whether this class has a custom Objective-C name. 102 | public var hasCustomObjCName: Bool { 103 | bits & 0x4 != 0 104 | } 105 | } 106 | } 107 | 108 | extension ExistentialMetadata { 109 | /// The flags that describe some existential metadata. 110 | public struct Flags { 111 | /// Flags as represented in bits. 112 | public let bits: UInt32 113 | 114 | /// The number of witness tables that are needed for this existential. 115 | public var numWitnessTables: Int { 116 | Int(bits & 0xFFFFFF) 117 | } 118 | 119 | /// The kind of special protocol this is. 120 | public var specialProtocol: SpecialProtocol { 121 | SpecialProtocol(rawValue: UInt8((bits & 0x3F000000) >> 24))! 122 | } 123 | 124 | /// Whether this existential has a superclass constraint. 125 | public var hasSuperclassConstraint: Bool { 126 | bits & 0x40000000 != 0 127 | } 128 | 129 | /// Whether this existential is class constrained. E.g. AnyObject constraint. 130 | public var isClassConstraint: Bool { 131 | // Note this is inverted on purpose 132 | bits & 0x80000000 == 0 133 | } 134 | } 135 | } 136 | 137 | /// A discriminator to determine the special protocolness of an existential. 138 | public enum SpecialProtocol: UInt8 { 139 | /// Every other protocol (not special at all, sorry.) 140 | case none = 0 141 | 142 | /// Swift.Error 143 | case error = 1 144 | } 145 | 146 | extension FunctionMetadata { 147 | /// The flags that describe some function metadata. 148 | public struct Flags { 149 | /// Flags as represented in bits. 150 | public let bits: Int 151 | 152 | /// The number of parameters in this function. 153 | public var numParams: Int { 154 | bits & 0xFFFF 155 | } 156 | 157 | /// The calling convention for this function. 158 | public var convention: FunctionConvention { 159 | FunctionConvention(rawValue: UInt8((bits & 0xFF0000) >> 16))! 160 | } 161 | 162 | /// Whether or not this function throws. 163 | public var `throws`: Bool { 164 | bits & 0x1000000 != 0 165 | } 166 | 167 | /// Whether or not this function has parameter flags describing the 168 | /// parameters. 169 | public var hasParamFlags: Bool { 170 | bits & 0x2000000 != 0 171 | } 172 | 173 | /// Whether or not this function is @escaping. 174 | public var isEscaping: Bool { 175 | bits & 0x4000000 != 0 176 | } 177 | } 178 | } 179 | 180 | /// A discriminator to determine what calling convention a function has. 181 | public enum FunctionConvention: UInt8 { 182 | case swift = 0 183 | case block = 1 184 | case thin = 2 185 | case c = 3 186 | } 187 | 188 | extension FunctionMetadata { 189 | /// The flags that represent some function parameter. 190 | public struct ParamFlags { 191 | /// Flags as represented in bits. 192 | public let bits: UInt32 193 | 194 | /// The value ownership kind for this parameter. 195 | public var valueOwnership: ValueOwnership { 196 | ValueOwnership(rawValue: UInt8(bits & 0x7F))! 197 | } 198 | 199 | /// Whether or not this parameter is variadic. E.g. Int... 200 | public var isVariadic: Bool { 201 | bits & 0x80 != 0 202 | } 203 | 204 | /// Whether or not this parameter is marked @autoclosure 205 | public var isAutoclosure: Bool { 206 | bits & 0x100 != 0 207 | } 208 | } 209 | } 210 | 211 | /// A discriminator to determine what the ownership rules are (currently) for 212 | /// a function parameter. 213 | public enum ValueOwnership: UInt8 { 214 | /// The default ownership rule for all Swift parameters, which might mean 215 | /// shared or owned. 216 | case `default` = 0 217 | 218 | /// Inout refers to passing a value by reference for mutation. 219 | case `inout` = 1 220 | 221 | /// Shared refers to passing a value by reference, but without mutation. 222 | /// Similar to Rust's immutable borrow. 223 | case shared = 2 224 | 225 | /// Owned refers to passing a value via copy. 226 | case owned = 3 227 | } 228 | 229 | extension ValueWitnessTable { 230 | /// The flags that describe some value witness table. 231 | public struct Flags { 232 | enum Flags: UInt32 { 233 | case isNonPOD = 0x010000 234 | case isNonInline = 0x020000 235 | // unused = 0x040000 236 | case hasSpareBits = 0x080000 237 | case isNonBitwiseTakable = 0x100000 238 | case hasEnumWitnesses = 0x200000 239 | case incomplete = 0x400000 240 | } 241 | 242 | /// Flags as represented in bits. 243 | public let bits: UInt32 244 | 245 | /// The alignment for this type. 246 | public var alignment: Int { 247 | alignmentMask + 1 248 | } 249 | 250 | var alignmentMask: Int { 251 | Int(bits & 0xFF) 252 | } 253 | 254 | /// Whether or not this value can be stored inline in an existential 255 | /// container. 256 | public var isValueInline: Bool { 257 | bits & Flags.isNonInline.rawValue == 0 258 | } 259 | 260 | /// Whether or not this type is a "plain old datatype" 261 | public var isPOD: Bool { 262 | bits & Flags.isNonPOD.rawValue == 0 263 | } 264 | 265 | /// Whether or not this type is bitwise takable. 266 | public var isBitwiseTakable: Bool { 267 | bits & Flags.isNonBitwiseTakable.rawValue == 0 268 | } 269 | 270 | /// Whether or not this value witness table has enum witnesses. This is only 271 | /// true for enum and optional metadata. 272 | public var hasEnumWitnesses: Bool { 273 | bits & Flags.hasEnumWitnesses.rawValue != 0 274 | } 275 | 276 | /// Whether or not this value witness table is incomplete. 277 | public var isIncomplete: Bool { 278 | bits & Flags.incomplete.rawValue != 0 279 | } 280 | } 281 | } 282 | -------------------------------------------------------------------------------- /Sources/Echo/Metadata/MetatypeMetadata.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MetatypeMetadata.swift 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2019 - 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | /// The metadata structure that represents a metatype wrapping some instance 10 | /// type. 11 | /// 12 | /// ABI Stability: Unstable across all platforms 13 | /// 14 | /// | macOS | iOS/tvOS | watchOS | Linux | Windows | 15 | /// |-------|----------|---------|-------|---------| 16 | /// | NA | NA | NA | NA | NA | 17 | /// 18 | public struct MetatypeMetadata: Metadata, LayoutWrapper { 19 | typealias Layout = _MetatypeMetadata 20 | 21 | /// Backing metatype metadata pointer. 22 | public let ptr: UnsafeRawPointer 23 | 24 | /// The instance type that this metatype wraps. 25 | public var instanceType: Any.Type { 26 | layout._instanceType 27 | } 28 | 29 | /// The metadata for the instance type this metatype wraps. 30 | public var instanceMetadata: Metadata { 31 | reflect(instanceType) 32 | } 33 | } 34 | 35 | extension MetatypeMetadata: Equatable {} 36 | 37 | struct _MetatypeMetadata { 38 | let _kind: Int 39 | let _instanceType: Any.Type 40 | } 41 | -------------------------------------------------------------------------------- /Sources/Echo/Metadata/ObjCClassWrapperMetadata.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ObjCClassWrapperMetadata.swift 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2019 - 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | /// The metadata structure that represents an Objective-C class that wasn't 10 | /// Swift compiled. 11 | /// 12 | /// ABI Stability: Stable since the following 13 | /// 14 | /// | macOS | iOS/tvOS | watchOS | Linux | Windows | 15 | /// |-------|----------|---------|-------|---------| 16 | /// | 10.14 | 12.2 | 5.2 | NA | NA | 17 | /// 18 | public struct ObjCClassWrapperMetadata: Metadata, LayoutWrapper { 19 | typealias Layout = _ObjCClassWrapperMetadata 20 | 21 | /// Backing Objective-C class wrapper metadata pointer. 22 | public let ptr: UnsafeRawPointer 23 | 24 | /// The class type that this wrapper houses. 25 | public var classType: Any.Type { 26 | layout._classMetadata 27 | } 28 | 29 | /// The class metadata. 30 | public var classMetadata: ClassMetadata { 31 | reflectClass(classType)! 32 | } 33 | 34 | /// The list of conformances defined for this type metadata. 35 | /// 36 | /// NOTE: This list is populated once before the program starts with all of 37 | /// the conformances that are statically know at compile time. If you 38 | /// are attempting to load libraries dynamically at runtime, this list 39 | /// will update automatically, so make sure if you need up to date 40 | /// information on a type's conformances, fetch this often. Example: 41 | /// 42 | /// let metadata = ... 43 | /// var conformances = metadata.conformances 44 | /// loadPlugin(...) 45 | /// // conformances is now outdated! Refresh it by calling this again. 46 | /// conformances = metadata.conformances 47 | public var conformances: [ConformanceDescriptor] { 48 | conformanceLock.withLock { 49 | Echo.conformances[ptr, default: []] 50 | } 51 | } 52 | } 53 | 54 | extension ObjCClassWrapperMetadata: Equatable {} 55 | 56 | struct _ObjCClassWrapperMetadata { 57 | let _kind: Int 58 | let _classMetadata: Any.Type 59 | } 60 | -------------------------------------------------------------------------------- /Sources/Echo/Metadata/OpaqueMetadata.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OpaqueMetadata.swift 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2019 - 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | /// The metadata structure that represents some opaque type in Swift. An opaque 10 | /// type might include a private structure, or perhaps some metadata for the 11 | /// various Builtin types in Swift. 12 | /// 13 | /// ABI Stability: Stable since the following 14 | /// 15 | /// | macOS | iOS/tvOS | watchOS | Linux | Windows | 16 | /// |-------|----------|---------|-------|---------| 17 | /// | 10.14 | 12.2 | 5.2 | NA | NA | 18 | /// 19 | public struct OpaqueMetadata: Metadata { 20 | /// Backing opaque metadata pointer. 21 | public let ptr: UnsafeRawPointer 22 | } 23 | 24 | extension OpaqueMetadata: Equatable {} 25 | -------------------------------------------------------------------------------- /Sources/Echo/Metadata/StructMetadata.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StructMetadata.swift 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2019 - 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | /// The metadata structure that represents a `struct` type in Swift. 10 | /// 11 | /// ABI Stability: Stable since the following 12 | /// 13 | /// | macOS | iOS/tvOS | watchOS | Linux | Windows | 14 | /// |-------|----------|---------|-------|---------| 15 | /// | 10.14 | 12.2 | 5.2 | NA | NA | 16 | /// 17 | public struct StructMetadata: TypeMetadata, LayoutWrapper { 18 | typealias Layout = _StructMetadata 19 | 20 | /// Backing struct metadata pointer. 21 | public let ptr: UnsafeRawPointer 22 | 23 | /// The struct context descriptor that describes this struct. 24 | public var descriptor: StructDescriptor { 25 | StructDescriptor(ptr: layout._descriptor.signed) 26 | } 27 | 28 | /// An array of field offsets for this struct's stored representation. 29 | public var fieldOffsets: [Int] { 30 | let start = ptr.offset(of: descriptor.fieldOffsetVectorOffset) 31 | 32 | return Array(unsafeUninitializedCapacity: descriptor.numFields) { 33 | for i in 0 ..< descriptor.numFields { 34 | let offset = start.load( 35 | fromByteOffset: i * MemoryLayout.size, 36 | as: UInt32.self 37 | ) 38 | 39 | $0[i] = Int(offset) 40 | } 41 | 42 | $1 = descriptor.numFields 43 | } 44 | } 45 | } 46 | 47 | extension StructMetadata: Equatable {} 48 | 49 | struct _StructMetadata { 50 | let _kind: Int 51 | let _descriptor: SignedPointer 52 | } 53 | -------------------------------------------------------------------------------- /Sources/Echo/Metadata/TupleMetadata.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TupleMetadata.swift 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2019 - 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | /// The metadata structure that represents a tuple type in Swift. 10 | /// 11 | /// ABI Stability: Unstable across all platforms 12 | /// 13 | /// | macOS | iOS/tvOS | watchOS | Linux | Windows | 14 | /// |-------|----------|---------|-------|---------| 15 | /// | NA | NA | NA | NA | NA | 16 | /// 17 | public struct TupleMetadata: Metadata, LayoutWrapper { 18 | typealias Layout = _TupleMetadata 19 | 20 | /// Backing tuple metadata pointer. 21 | public let ptr: UnsafeRawPointer 22 | 23 | /// The number of elements in this tuple. 24 | public var numElements: Int { 25 | layout._numElements 26 | } 27 | 28 | /// An array of labels for each element in this tuple. 29 | public var labels: [String] { 30 | let split = layout._labels?.string.split( 31 | separator: " ", 32 | maxSplits: numElements, 33 | omittingEmptySubsequences: false 34 | ) 35 | 36 | var result = [String]() 37 | 38 | for i in 0 ..< numElements { 39 | if let split = split, split[i] != "" { 40 | result.append(String(split[i])) 41 | } else { 42 | result.append("\(i)") 43 | } 44 | } 45 | 46 | return result 47 | } 48 | 49 | /// An array of elements that describe each tuple more in depth, including 50 | /// offset and type. 51 | public var elements: [Element] { 52 | return Array(unsafeUninitializedCapacity: numElements) { 53 | for i in 0 ..< numElements { 54 | let address = trailing.offset(of: i, as: _TupleElement.self) 55 | $0[i] = Element(ptr: address) 56 | } 57 | 58 | $1 = numElements 59 | } 60 | } 61 | } 62 | 63 | extension TupleMetadata { 64 | /// The structure that represents a tuple element in some tuple metadata. 65 | public struct Element: LayoutWrapper { 66 | typealias Layout = _TupleElement 67 | 68 | /// Backing tuple element pointer. 69 | let ptr: UnsafeRawPointer 70 | 71 | /// The type for this tuple element. 72 | public var type: Any.Type { 73 | layout._metadata 74 | } 75 | 76 | /// The metadata for the type for this tuple element. 77 | public var metadata: Metadata { 78 | reflect(type) 79 | } 80 | 81 | /// The offset in bytes to this element from a tuple pointer. 82 | public var offset: Int { 83 | Int(layout._offset) 84 | } 85 | } 86 | } 87 | 88 | extension TupleMetadata: Equatable {} 89 | 90 | struct _TupleMetadata { 91 | let _kind: Int 92 | let _numElements: Int 93 | let _labels: UnsafePointer? 94 | } 95 | 96 | struct _TupleElement { 97 | let _metadata: Any.Type 98 | #if canImport(Darwin) 99 | let _offset: Int 100 | #else 101 | let _offset: UInt32 102 | let _padding: UInt32 103 | #endif 104 | } 105 | -------------------------------------------------------------------------------- /Sources/Echo/Metadata/TypeMetadata.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TypeMetadata.swift 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2019 - 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | #if os(Linux) 10 | import CEcho 11 | #endif 12 | import Foundation 13 | 14 | /// Type metadata refers to those metadata records who declare a new type in 15 | /// Swift. Said metadata records only refer to structs, classes, and enums. 16 | /// 17 | /// ABI Stability: Stable since the following 18 | /// 19 | /// | macOS | iOS/tvOS | watchOS | Linux | Windows | 20 | /// |-------|----------|---------|-------|---------| 21 | /// | 10.14 | 12.2 | 5.2 | NA | NA | 22 | /// 23 | public protocol TypeMetadata: Metadata {} 24 | 25 | extension TypeMetadata { 26 | /// The list of conformances defined for this type metadata. 27 | /// 28 | /// NOTE: This list is populated once before the program starts with all of 29 | /// the conformances that are statically know at compile time. If you 30 | /// are attempting to load libraries dynamically at runtime, this list 31 | /// will update automatically, so make sure if you need up to date 32 | /// information on a type's conformances, fetch this often. Example: 33 | /// 34 | /// let metadata = ... 35 | /// var conformances = metadata.conformances 36 | /// loadPlugin(...) 37 | /// // conformances is now outdated! Refresh it by calling this again. 38 | /// conformances = metadata.conformances 39 | public var conformances: [ConformanceDescriptor] { 40 | #if os(Linux) 41 | iterateSharedObjects() 42 | #endif 43 | 44 | return conformanceLock.withLock { 45 | Echo.conformances[contextDescriptor.ptr, default: []] 46 | } 47 | } 48 | 49 | /// The base type context descriptor for this type metadata record. 50 | public var contextDescriptor: TypeContextDescriptor { 51 | switch self { 52 | case let structMetadata as StructMetadata: 53 | return structMetadata.descriptor 54 | case let enumMetadata as EnumMetadata: 55 | return enumMetadata.descriptor 56 | case let classMetadata as ClassMetadata: 57 | return classMetadata.descriptor 58 | default: 59 | fatalError("Unknown TypeMetadata conformance") 60 | } 61 | } 62 | 63 | /// An array of field offsets for this type's stored representation. 64 | public var fieldOffsets: [Int] { 65 | switch self { 66 | case let structMetadata as StructMetadata: 67 | return structMetadata.fieldOffsets 68 | case let classMetadata as ClassMetadata: 69 | return classMetadata.fieldOffsets 70 | case is EnumMetadata: 71 | return [] 72 | default: 73 | fatalError("Unknown TypeMetadata conformance") 74 | } 75 | } 76 | 77 | var genericArgumentPtr: UnsafeRawPointer { 78 | switch self { 79 | case is StructMetadata: 80 | return ptr + MemoryLayout<_StructMetadata>.size 81 | 82 | case is EnumMetadata: 83 | return ptr + MemoryLayout<_EnumMetadata>.size 84 | 85 | case let classMetadata as ClassMetadata: 86 | return ptr.offset(of: classMetadata.descriptor.genericArgumentOffset) 87 | 88 | default: 89 | fatalError("Unknown TypeMetadata conformance") 90 | } 91 | } 92 | 93 | /// An array of types that represent the generic arguments that make up this 94 | /// type. 95 | public var genericTypes: [Any.Type] { 96 | guard contextDescriptor.flags.isGeneric else { 97 | return [] 98 | } 99 | 100 | let numParams = contextDescriptor.genericContext!.numParams 101 | 102 | return Array(unsafeUninitializedCapacity: numParams) { 103 | // Explicitly only call this once because class metadata could require 104 | // computation, so only do it once if needed. 105 | let gap = genericArgumentPtr 106 | 107 | for i in 0 ..< numParams { 108 | let type = gap.load( 109 | fromByteOffset: i * MemoryLayout.stride, 110 | as: Any.Type.self 111 | ) 112 | 113 | $0[i] = type 114 | } 115 | 116 | $1 = numParams 117 | } 118 | } 119 | 120 | /// An array of metadata records for the types that represent the generic 121 | /// arguments that make up this type. 122 | public var genericMetadata: [Metadata] { 123 | genericTypes.map { reflect($0) } 124 | } 125 | 126 | /// Given a mangled type name to some field, superclass, etc., return the 127 | /// type. Using this is the preferred way to interact with mangled type names 128 | /// because this uses the metadata's generic context and arguments and such to 129 | /// fill in generic types along with caching the mangled name for future use. 130 | /// - Parameter mangledName: The mangled type name pointer to some type in 131 | /// this metadata's reach. 132 | /// - Returns: The type that the mangled type name refers to, if we're able 133 | /// to demangle it. 134 | public func type( 135 | of mangledName: UnsafeRawPointer 136 | ) -> Any.Type? { 137 | let entry = mangledNameLock.withLock { 138 | mangledNames[mangledName] 139 | } 140 | 141 | if entry != nil { 142 | return entry! 143 | } 144 | 145 | let length = getSymbolicMangledNameLength(mangledName) 146 | let name = mangledName.assumingMemoryBound(to: UInt8.self) 147 | let type = _getTypeByMangledNameInContext( 148 | name, 149 | UInt(length), 150 | genericContext: contextDescriptor.ptr, 151 | genericArguments: genericArgumentPtr 152 | ) 153 | 154 | mangledNameLock.withLock { 155 | mangledNames[mangledName] = type 156 | } 157 | 158 | return type 159 | } 160 | } 161 | 162 | let mangledNameLock = NSLock() 163 | var mangledNames = [UnsafeRawPointer: Any.Type?]() 164 | -------------------------------------------------------------------------------- /Sources/Echo/Metadata/ValueWitnessTable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ValueWitnessTable.swift 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2019 - 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | #if _ptrauth(_arm64e) 10 | import CEcho 11 | #endif 12 | 13 | /// The value witness table is a table of function pointers that describe how 14 | /// to properly copy, destroy, etc. memory for a given type. It also contains 15 | /// type layout information about the type including size, stride, and 16 | /// alignment. 17 | /// 18 | /// ABI Stability: Stable since the following 19 | /// 20 | /// | macOS | iOS/tvOS | watchOS | Linux | Windows | 21 | /// |-------|----------|---------|-------|---------| 22 | /// | 10.14 | 12.2 | 5.2 | NA | NA | 23 | /// 24 | public struct ValueWitnessTable: LayoutWrapper { 25 | typealias Layout = SignedPointer 26 | 27 | /// Backing value witness table pointer. 28 | let ptr: UnsafeRawPointer 29 | 30 | var _vwt: _ValueWitnessTable { 31 | layout.signed.load(as: _ValueWitnessTable.self) 32 | } 33 | 34 | /// Given a buffer an instance of the type in the source buffer, initialize 35 | /// the destination buffer with a copy of the source. 36 | /// - Parameter dest: The desintation buffer. 37 | /// - Parameter source: The source buffer with the instance. 38 | public func initializeBufferWithCopyOfBuffer( 39 | _ dest: UnsafeMutableRawPointer, 40 | _ source: UnsafeMutableRawPointer 41 | ) { 42 | #if _ptrauth(_arm64e) 43 | echo_vwt_initializeBufferWithCopyOfBuffer(layout.signed, dest, source, 44 | trailing) 45 | #else 46 | _ = _vwt._initializeBufferWithCopyOfBuffer(dest, source, trailing) 47 | #endif 48 | } 49 | 50 | /// Given an instance of this type, destroy it. The resulting pointer is now 51 | /// an invalid instance. 52 | /// - Parameter value: An instance of this type. 53 | public func destroy(_ value: UnsafeMutableRawPointer) { 54 | #if _ptrauth(_arm64e) 55 | echo_vwt_destroy(layout.signed, value, trailing) 56 | #else 57 | _vwt._destroy(value, trailing) 58 | #endif 59 | } 60 | 61 | /// Given an invalid instance of this type and a valid instance of this type, 62 | /// copy the source instance into the destination. 63 | /// - Parameter dest: An invalid instance of type. 64 | /// - Parameter source: An instance of type. 65 | public func initializeWithCopy( 66 | _ dest: UnsafeMutableRawPointer, 67 | _ source: UnsafeMutableRawPointer 68 | ) { 69 | #if _ptrauth(_arm64e) 70 | echo_vwt_initializeWithCopy(layout.signed, dest, source, trailing) 71 | #else 72 | _ = _vwt._initializeWithCopy(dest, source, trailing) 73 | #endif 74 | } 75 | 76 | /// Given a valid instance of this type and another valid instance of this 77 | /// type, copy the contents from the source into the destination. 78 | /// - Parameter dest: An instance of type whose contents are going to be 79 | /// overwritten. 80 | /// - Parameter source: An instance of type. 81 | public func assignWithCopy( 82 | _ dest: UnsafeMutableRawPointer, 83 | _ source: UnsafeMutableRawPointer 84 | ) { 85 | #if _ptrauth(_arm64e) 86 | echo_vwt_assignWithCopy(layout.signed, dest, source, trailing) 87 | #else 88 | _ = _vwt._assignWithCopy(dest, source, trailing) 89 | #endif 90 | } 91 | 92 | /// Given an invalid instance of this type and a valid instance of this type, 93 | /// initialize the destination by destroying the source instance. 94 | /// - Parameter dest: An invalid instance of type. 95 | /// - Parameter source: An instance of type. 96 | public func initializeWithTake( 97 | _ dest: UnsafeMutableRawPointer, 98 | _ source: UnsafeMutableRawPointer 99 | ) { 100 | #if _ptrauth(_arm64e) 101 | echo_vwt_initializeWithTake(layout.signed, dest, source, trailing) 102 | #else 103 | _ = _vwt._initializeWithTake(dest, source, trailing) 104 | #endif 105 | } 106 | 107 | /// Given a valid instance of this type and another valid instance of this 108 | /// type, copy the contents from the source instance into the destination 109 | /// while also destroying the source instance. 110 | /// - Parameter dest: An instance of type. 111 | /// - Parameter source: An instance of type. 112 | public func assignWithTake( 113 | _ dest: UnsafeMutableRawPointer, 114 | _ source: UnsafeMutableRawPointer 115 | ) { 116 | #if _ptrauth(_arm64e) 117 | echo_vwt_assignWithTake(layout.signed, dest, source, trailing) 118 | #else 119 | _ = _vwt._assignWithTake(dest, source, trailing) 120 | #endif 121 | } 122 | 123 | /// Given an instance of an enum case who has a single payload of this type, 124 | /// get the case tag of the enum. 125 | /// - Parameter instance: The enum case instance. 126 | /// - Parameter numEmptyCases: The number of cases without payloads in this 127 | /// enum. 128 | /// - Returns: The enum case tag for the payload containing this type. 129 | public func getEnumTagSinglePayload( 130 | _ instance: UnsafeRawPointer, 131 | _ numEmptyCases: UInt32 132 | ) -> UInt32 { 133 | #if _ptrauth(_arm64e) 134 | return echo_vwt_getEnumTagSinglePayload(layout.signed, instance, 135 | numEmptyCases, trailing) 136 | #else 137 | return _vwt._getEnumTagSinglePayload(instance, numEmptyCases, trailing) 138 | #endif 139 | } 140 | 141 | /// Given some uninitialzed data of an enum case who has a single payload of 142 | /// this type, store the tag in the data. 143 | /// - Parameter instance: The uninitialized instance of the enum case. 144 | /// - Parameter tag: The enum case tag number for the payload with this type. 145 | /// - Parameter numEmptyCases: The number of cases without payloads in this 146 | /// enum. 147 | public func storeEnumTagSinglePayload( 148 | _ instance: UnsafeMutableRawPointer, 149 | _ tag: UInt32, 150 | _ numEmptyCases: UInt32 151 | ) { 152 | #if _ptrauth(_arm64e) 153 | echo_vwt_storeEnumTagSinglePayload(layout.signed, instance, tag, 154 | numEmptyCases, trailing) 155 | #else 156 | _vwt._storeEnumTagSinglePayload(instance, tag, numEmptyCases, trailing) 157 | #endif 158 | } 159 | 160 | /// The size in bytes that this type represents in memory. 161 | public var size: Int { 162 | _vwt._size 163 | } 164 | 165 | /// The required size in bytes needed to represent an element if this type 166 | /// were in an array. 167 | public var stride: Int { 168 | _vwt._stride 169 | } 170 | 171 | /// The flags describing this value witness table. 172 | public var flags: Flags { 173 | _vwt._flags 174 | } 175 | 176 | /// The number of extra inhabitants in this type. 177 | public var extraInhabitantCount: Int { 178 | Int(_vwt._extraInhabitantCount) 179 | } 180 | } 181 | 182 | struct _ValueWitnessTable { 183 | let _initializeBufferWithCopyOfBuffer: @convention(c) ( 184 | // Destination buffer 185 | UnsafeMutableRawPointer, 186 | // Source buffer 187 | UnsafeMutableRawPointer, 188 | // Type metadata 189 | UnsafeRawPointer 190 | ) -> UnsafeMutableRawPointer // returns destination value 191 | 192 | let _destroy: @convention(c) ( 193 | // Value 194 | UnsafeMutableRawPointer, 195 | // Type metadata 196 | UnsafeRawPointer 197 | ) -> () 198 | 199 | let _initializeWithCopy: @convention(c) ( 200 | // Destination value 201 | UnsafeMutableRawPointer, 202 | // Source value 203 | UnsafeMutableRawPointer, 204 | // Type metadata 205 | UnsafeRawPointer 206 | ) -> UnsafeMutableRawPointer // returns destination 207 | 208 | let _assignWithCopy: @convention(c) ( 209 | // Destination value 210 | UnsafeMutableRawPointer, 211 | // Source value 212 | UnsafeMutableRawPointer, 213 | // Type metadata 214 | UnsafeRawPointer 215 | ) -> UnsafeMutableRawPointer // returns destination 216 | 217 | let _initializeWithTake: @convention(c) ( 218 | // Destination value 219 | UnsafeMutableRawPointer, 220 | // Source value 221 | UnsafeMutableRawPointer, 222 | // Type metadata 223 | UnsafeRawPointer 224 | ) -> UnsafeMutableRawPointer // returns destination 225 | 226 | let _assignWithTake: @convention(c) ( 227 | // Destination value 228 | UnsafeMutableRawPointer, 229 | // Source value 230 | UnsafeMutableRawPointer, 231 | // Type metadata 232 | UnsafeRawPointer 233 | ) -> UnsafeMutableRawPointer // returns destination 234 | 235 | let _getEnumTagSinglePayload: @convention(c) ( 236 | // Instance of single payload enum 237 | UnsafeRawPointer, 238 | // Number of empty cases 239 | UInt32, 240 | // Type metadata 241 | UnsafeRawPointer 242 | ) -> UInt32 // returns tag of enum 243 | 244 | let _storeEnumTagSinglePayload: @convention(c) ( 245 | // Instance of single payload enum 246 | UnsafeMutableRawPointer, 247 | // Which enum case 248 | UInt32, 249 | // Number of empty cases 250 | UInt32, 251 | // Type metadata 252 | UnsafeRawPointer 253 | ) -> () 254 | 255 | let _size: Int 256 | let _stride: Int 257 | let _flags: ValueWitnessTable.Flags 258 | let _extraInhabitantCount: UInt32 259 | } 260 | -------------------------------------------------------------------------------- /Sources/Echo/Runtime/ConformanceDescriptor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ConformanceDescriptor.swift 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2020 - 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | #if canImport(ObjectiveC) 10 | import ObjectiveC 11 | #endif 12 | 13 | /// A structure that helps describe a particular conformance in Swift. 14 | /// Information includes what type is being conformed to what protocol, some 15 | /// flags like if the conformance is retroactive, has conditional requirements, 16 | /// etc. 17 | /// 18 | /// ABI Stability: Stable since the following 19 | /// 20 | /// | macOS | iOS/tvOS | watchOS | Linux | Windows | 21 | /// |-------|----------|---------|-------|---------| 22 | /// | 10.14 | 12.2 | 5.2 | NA | NA | 23 | /// 24 | public struct ConformanceDescriptor: LayoutWrapper { 25 | typealias Layout = _ConformanceDescriptor 26 | 27 | let ptr: UnsafeRawPointer 28 | 29 | /// The specific flags that describe this conformance descriptor. 30 | public var flags: Flags { 31 | layout._flags 32 | } 33 | 34 | /// The protocol that this type conforms to. 35 | public var `protocol`: ProtocolDescriptor { 36 | ProtocolDescriptor(ptr: address(for: \._protocol)) 37 | } 38 | 39 | /// The context descriptor of the type being conformed. 40 | public var contextDescriptor: TypeContextDescriptor? { 41 | let start = address(for: \._typeRef) 42 | 43 | switch flags.typeReferenceKind { 44 | case .directTypeDescriptor: 45 | let ptr = start.relativeDirectAddress(as: _ContextDescriptor.self) 46 | return getContextDescriptor(at: ptr) as? TypeContextDescriptor 47 | case .indirectTypeDescriptor: 48 | var ptr = start.relativeDirectAddress(as: UnsafeRawPointer.self) 49 | ptr = ptr.load(as: UnsafeRawPointer.self) 50 | return getContextDescriptor(at: ptr) as? TypeContextDescriptor 51 | default: 52 | return nil 53 | } 54 | } 55 | 56 | /// The ObjectiveC class metadata of the type being conformed. 57 | #if canImport(ObjectiveC) 58 | public var objcClass: ObjCClassWrapperMetadata? { 59 | let start = address(for: \._typeRef) 60 | 61 | switch flags.typeReferenceKind { 62 | case .directObjCClass: 63 | let ptr = start.relativeDirectAddress(as: CChar.self) 64 | .assumingMemoryBound(to: CChar.self) 65 | 66 | guard let anyClass = objc_lookUpClass(ptr) else { 67 | fatalError("No Objective-C class named \(ptr.string)") 68 | } 69 | 70 | return reflect(anyClass) as? ObjCClassWrapperMetadata 71 | case .indirectObjCClass: 72 | let ptr = start.relativeDirectAddress(as: _ClassMetadata.self) 73 | return ObjCClassWrapperMetadata(ptr: ptr) 74 | default: 75 | return nil 76 | } 77 | } 78 | #endif 79 | 80 | /// The witness table pattern is a base witness table that this conformance 81 | /// can base actual witness tables off of. In the case that this conformance 82 | /// does not have a generic witness table (flags.hasGenericWitnessTable), this 83 | /// witness table pattern is actually the real witness table. 84 | public var witnessTablePattern: WitnessTable { 85 | WitnessTable(ptr: address(for: \._witnessTablePattern)) 86 | } 87 | } 88 | 89 | struct _ConformanceDescriptor { 90 | let _protocol: RelativeIndirectablePointer<_ProtocolDescriptor> 91 | let _typeRef: Int32 92 | let _witnessTablePattern: RelativeDirectPointer<_WitnessTable> 93 | let _flags: ConformanceDescriptor.Flags 94 | } 95 | -------------------------------------------------------------------------------- /Sources/Echo/Runtime/ExistentialContainer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExistentialContainer.swift 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2019 - 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | // typealias Any = AnyExistentialContainer :) 10 | 11 | /// An any existential container holds the necessary information for any value 12 | /// with type Any. I.e. the type erased type. 13 | /// 14 | /// ABI Stability: Stable since the following 15 | /// 16 | /// | macOS | iOS/tvOS | watchOS | Linux | Windows | 17 | /// |-------|----------|---------|-------|---------| 18 | /// | 10.14 | 12.2 | 5.2 | NA | NA | 19 | /// 20 | public struct AnyExistentialContainer { 21 | /// The storage needed to house the value of said type. This can be stored 22 | /// inline with all the data necessary to represent said type in 3 words, or 23 | /// could be stored indirectly through some pointer hoops. It's best to 24 | /// access the value through `projectValue`. 25 | public var data: (Int, Int, Int) = (0, 0, 0) 26 | 27 | /// The type being stored. 28 | public var type: Any.Type 29 | 30 | /// The metadata for the type being stored. 31 | public var metadata: Metadata { 32 | reflect(type) 33 | } 34 | 35 | /// Initializes an existential container. 36 | /// - Parameter type: The type to create an existential container for. 37 | public init(type: Any.Type) { 38 | self.type = type 39 | } 40 | 41 | /// Initializes an existential container. 42 | /// - Parameter metadata: The metadata for the type to create an existential 43 | /// container for. 44 | public init(metadata: Metadata) { 45 | self.type = metadata.type 46 | } 47 | 48 | /// Accesses the value of `type` being stored in this container. 49 | /// - Returns: The opaque pointer to value of `type`. 50 | public mutating func projectValue() -> UnsafeRawPointer { 51 | // If the value is stored inline, just return a pointer to data. 52 | guard !metadata.vwt.flags.isValueInline else { 53 | return withUnsafePointer(to: &self) { 54 | $0.raw 55 | } 56 | } 57 | 58 | // Otherwise, we need to go through some hoops to access the pointer. 59 | let alignMask = UInt(metadata.vwt.flags.alignmentMask) 60 | let heapObjSize = UInt(MemoryLayout.size) 61 | let byteOffset = (heapObjSize + alignMask) & ~alignMask 62 | let bytePtr = withUnsafePointer(to: &self) { 63 | $0.withMemoryRebound(to: UnsafePointer.self, capacity: 1) { 64 | $0.pointee.raw 65 | } 66 | } 67 | 68 | return bytePtr + Int(byteOffset) 69 | } 70 | } 71 | 72 | /// An existential container is a type in Swift that contains some struct or 73 | /// class with information of what the type it's containing is, and the witness 74 | /// tables needed that the existential (protocol) is. 75 | /// 76 | /// ABI Stability: Stable since the following 77 | /// 78 | /// | macOS | iOS/tvOS | watchOS | Linux | Windows | 79 | /// |-------|----------|---------|-------|---------| 80 | /// | 10.14 | 12.2 | 5.2 | NA | NA | 81 | /// 82 | public struct ExistentialContainer { 83 | /// The base any existential, also known as Any. 84 | public var base: AnyExistentialContainer 85 | 86 | /// A pointer to the witness table. 87 | public var witnessTable: WitnessTable 88 | } 89 | 90 | /// An existential container is a type in Swift that contains some struct or 91 | /// class with information of what the type it's containing is, and the witness 92 | /// tables needed that the existential (protocol) is. Dual supports an 93 | /// existential that is composed of two protocols. E.g. X & Y 94 | /// 95 | /// ABI Stability: Stable since the following 96 | /// 97 | /// | macOS | iOS/tvOS | watchOS | Linux | Windows | 98 | /// |-------|----------|---------|-------|---------| 99 | /// | 10.14 | 12.2 | 5.2 | NA | NA | 100 | /// 101 | public struct DualExistentialContainer { 102 | /// The base any existential, also known as Any. 103 | public var base: AnyExistentialContainer 104 | 105 | /// A pointer to witness tables. 106 | public var witnessTables: (WitnessTable, WitnessTable) 107 | } 108 | -------------------------------------------------------------------------------- /Sources/Echo/Runtime/Functions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Functions.swift 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2019 - 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | import CEcho 10 | 11 | //===----------------------------------------------------------------------===// 12 | // Box Functions 13 | //===----------------------------------------------------------------------===// 14 | 15 | /// A box pair is the pair of heap object + value within said heap object. 16 | /// When you allocate a new box, you're given a pointer to the heap object 17 | /// along with a pointer to the value inside the heap object. 18 | public struct BoxPair { 19 | /// A pointer to the allocated heap object. 20 | public let heapObj: UnsafePointer 21 | 22 | /// A pointer to the value inside the heap object. 23 | public let buffer: UnsafeRawPointer 24 | } 25 | 26 | @_silgen_name("swift_allocBox") 27 | public func swift_allocBox(for type: Any.Type) -> BoxPair 28 | 29 | public func swift_allocBox(for metadata: Metadata) -> BoxPair { 30 | swift_allocBox(for: metadata.type) 31 | } 32 | 33 | @_silgen_name("swift_makeBoxUnique") 34 | func _swift_makeBoxUnique( 35 | for buffer: UnsafeRawPointer, 36 | type: Any.Type, 37 | alignMask: UInt 38 | ) -> BoxPair 39 | 40 | /* 41 | public func swift_projectBox( 42 | for heapObj: UnsafePointer 43 | ) -> UnsafeRawPointer { 44 | swift_projectBox(heapObj.raw.mutable)!.raw 45 | } 46 | */ 47 | 48 | //===----------------------------------------------------------------------===// 49 | // HeapObject Functions 50 | //===----------------------------------------------------------------------===// 51 | 52 | /// Allocates a new reference counted class instance. 53 | /// - Parameter type: The class metadata to allocate an instance for. 54 | /// - Returns: A pointer to the heap object. This is intentionally left raw 55 | /// because there is almost always more initialization that needs to 56 | /// happen after allocation. 57 | public func swift_allocObject( 58 | for type: ClassMetadata, 59 | size: Int, 60 | alignment: Int 61 | ) -> UnsafeRawPointer { 62 | let object = swift_allocObject( 63 | type.ptr.mutable, 64 | size, 65 | alignment 66 | ) 67 | 68 | // We unsafely unwrap this here because if the above operation fails to 69 | // allocate, the Swift runtime will have crashed before we unwrap. 70 | return object.unsafelyUnwrapped.raw 71 | } 72 | 73 | /* 74 | public func swift_release(_ heapObj: UnsafePointer) { 75 | swift_release(heapObj.raw.mutable) 76 | } 77 | */ 78 | 79 | //===----------------------------------------------------------------------===// 80 | // Mangling Functions 81 | //===----------------------------------------------------------------------===// 82 | 83 | struct TypeNamePair { 84 | public let data: UnsafePointer 85 | public let length: UInt 86 | } 87 | 88 | @_silgen_name("swift_getTypeName") 89 | func _swift_getTypeName( 90 | for metadata: UnsafeRawPointer, 91 | qualified: Bool 92 | ) -> TypeNamePair 93 | 94 | /// Gets the types name, either qualified (full name representation), or non 95 | /// qualified (just the type name). 96 | /// - Parameter type: Type to get the name of. 97 | /// - Parameter qualified: Whether or not this name should be qualified. 98 | /// - Returns: A string who contains the name of the type. 99 | public func swift_getTypeName( 100 | for type: Any.Type, 101 | qualified: Bool 102 | ) -> String { 103 | String( 104 | cString: _swift_getTypeName( 105 | for: unsafeBitCast(type, to: UnsafeRawPointer.self), 106 | qualified: qualified 107 | ).data 108 | ) 109 | } 110 | 111 | /// Gets the types name, either qualified (full name representation), or non 112 | /// qualified (just the type name). 113 | /// - Parameter metadata: Type metadata to get the name of. 114 | /// - Parameter qualified: Whether or not this name should be qualified. 115 | /// - Returns: A string who contains the name of the type metadata. 116 | public func swift_getTypeName( 117 | for metadata: Metadata, 118 | qualified: Bool 119 | ) -> String { 120 | swift_getTypeName(for: metadata.type, qualified: qualified) 121 | } 122 | 123 | //===----------------------------------------------------------------------===// 124 | // Protocol Functions 125 | //===----------------------------------------------------------------------===// 126 | 127 | /// Checks whether the type conforms to the given protocol. 128 | /// - Parameter type: Type to check conformance of. 129 | /// - Parameter protocol: The protocol to check to see if the type conforms to. 130 | /// - Returns: A witness table of the conformance if it does conform, or nil if 131 | /// it doesn't conform. 132 | public func swift_conformsToProtocol( 133 | type: Any.Type, 134 | protocol: ProtocolDescriptor 135 | ) -> WitnessTable? { 136 | let wtPtr = swift_conformsToProtocol( 137 | unsafeBitCast(type, to: UnsafeRawPointer.self), 138 | `protocol`.ptr 139 | ) 140 | 141 | guard wtPtr != nil else { 142 | return nil 143 | } 144 | 145 | return WitnessTable(ptr: wtPtr!) 146 | } 147 | 148 | /// Checks whether the type conforms to the given protocol. 149 | /// - Parameter metadata: Type metadata to check conformance of. 150 | /// - Parameter protocol: The protocol to check to see if the type conforms to. 151 | /// - Returns: A witness table of the conformance if it does conform, or nil if 152 | /// it doesn't conform. 153 | public func swift_conformsToProtocol( 154 | metadata: Metadata, 155 | protocol: ProtocolDescriptor 156 | ) -> WitnessTable? { 157 | swift_conformsToProtocol(type: metadata.type, protocol: `protocol`) 158 | } 159 | -------------------------------------------------------------------------------- /Sources/Echo/Runtime/HeapObject.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HeapObject.swift 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2019 - 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | /// Some object whose value is being stored on the heap and is being reference 10 | /// counted by the Swift runtime. 11 | /// 12 | /// ABI Stability: Stable since the following 13 | /// 14 | /// | macOS | iOS/tvOS | watchOS | Linux | Windows | 15 | /// |-------|----------|---------|-------|---------| 16 | /// | 10.14 | 12.2 | 5.2 | NA | NA | 17 | /// 18 | public struct HeapObject { 19 | /// The type being stored in this heap object. 20 | public let type: Any.Type 21 | 22 | /// This isn't exposed because I haven't created the structure that 23 | /// correctly represents a heap objects ref count yet. 24 | let _refCount: UInt64 25 | 26 | /// The metadata for the type being stored in this heap object. 27 | public var metadata: Metadata { 28 | reflect(type) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Sources/Echo/Runtime/ImageInspection.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageInspection.swift 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | #if canImport(MachO) 12 | import MachO 13 | #elseif canImport(Glibc) 14 | import Glibc 15 | #endif 16 | 17 | #if os(Linux) 18 | import CEcho 19 | #endif 20 | 21 | //===----------------------------------------------------------------------===// 22 | // __swift5_protos/swift5_protocols 23 | //===----------------------------------------------------------------------===// 24 | 25 | /// The list of all protocols this program has loaded. 26 | /// 27 | /// NOTE: This list is populated once before the program starts with all of 28 | /// the protocols that are statically know at compile time. If you 29 | /// are attempting to load libraries dynamically at runtime, this list 30 | /// will update automatically, so make sure if you need up to date 31 | /// information on these protocols, fetch this often. Example: 32 | /// 33 | /// var protocols = Echo.protocols 34 | /// loadPlugin(...) 35 | /// // protocols is now outdated! Refresh it by calling this again. 36 | /// protocols = Echo.protocols 37 | public var protocols: [ProtocolDescriptor] { 38 | #if os(Linux) 39 | iterateSharedObjects() 40 | #endif 41 | 42 | let protos = protocolLock.withLock { 43 | _protocols 44 | } 45 | 46 | return Array(unsafeUninitializedCapacity: protos.count) { 47 | for (i, proto) in protos.enumerated() { 48 | $0[i] = ProtocolDescriptor(ptr: proto) 49 | } 50 | 51 | $1 = protos.count 52 | } 53 | } 54 | 55 | let protocolLock = NSLock() 56 | var _protocols = Set() 57 | 58 | @_cdecl("registerProtocols") 59 | func registerProtocols(section: UnsafeRawPointer, size: Int) { 60 | for i in 0 ..< size / 4 { 61 | let start = section.offset(of: i, as: Int32.self) 62 | let ptr = start.relativeDirectAddress(as: _ProtocolDescriptor.self) 63 | 64 | _ = protocolLock.withLock { 65 | _protocols.insert(ptr) 66 | } 67 | } 68 | } 69 | 70 | //===----------------------------------------------------------------------===// 71 | // __swift5_proto/swift5_protocol_conformances 72 | //===----------------------------------------------------------------------===// 73 | 74 | let conformanceLock = NSLock() 75 | var conformances = [UnsafeRawPointer: [ConformanceDescriptor]]() 76 | 77 | @_cdecl("registerProtocolConformances") 78 | func registerProtocolConformances(section: UnsafeRawPointer, size: Int) { 79 | for i in 0 ..< size / 4 { 80 | let start = section.offset(of: i, as: Int32.self) 81 | let ptr = start.relativeDirectAddress(as: _ConformanceDescriptor.self) 82 | let conformance = ConformanceDescriptor(ptr: ptr) 83 | 84 | #if canImport(ObjectiveC) 85 | if let objcClass = conformance.objcClass { 86 | conformanceLock.withLock { 87 | conformances[objcClass.ptr, default: []].append(conformance) 88 | } 89 | continue 90 | } 91 | #endif 92 | 93 | if let descriptor = conformance.contextDescriptor { 94 | conformanceLock.withLock { 95 | conformances[descriptor.ptr, default: []].append(conformance) 96 | } 97 | } 98 | } 99 | } 100 | 101 | //===----------------------------------------------------------------------===// 102 | // __swift5_types/swift5_type_metadata 103 | //===----------------------------------------------------------------------===// 104 | 105 | /// The list of all protocols this program has loaded. 106 | /// 107 | /// NOTE: This list is populated once before the program starts with all of 108 | /// the protocols that are statically know at compile time. If you 109 | /// are attempting to load libraries dynamically at runtime, this list 110 | /// will update automatically, so make sure if you need up to date 111 | /// information on these protocols, fetch this often. Example: 112 | /// 113 | /// var protocols = Echo.protocols 114 | /// loadPlugin(...) 115 | /// // protocols is now outdated! Refresh it by calling this again. 116 | /// protocols = Echo.protocols 117 | public var types: [ContextDescriptor] { 118 | #if os(Linux) 119 | iterateSharedObjects() 120 | #endif 121 | 122 | let types = typeLock.withLock { 123 | _types 124 | } 125 | 126 | var result = [ContextDescriptor]() 127 | result.reserveCapacity(types.count) 128 | 129 | for type in types { 130 | result.append(getContextDescriptor(at: type)) 131 | } 132 | 133 | return result 134 | } 135 | 136 | let typeLock = NSLock() 137 | var _types = Set() 138 | 139 | @_cdecl("registerTypeMetadata") 140 | func registerTypeMetadata(section: UnsafeRawPointer, size: Int) { 141 | for i in 0 ..< size / 4 { 142 | let start = section.offset(of: i, as: Int32.self) 143 | let ptr = start.relativeDirectAddress(as: _ContextDescriptor.self) 144 | 145 | _ = typeLock.withLock { 146 | _types.insert(ptr) 147 | } 148 | } 149 | } 150 | 151 | //===----------------------------------------------------------------------===// 152 | // Mach-O Image Inspection 153 | //===----------------------------------------------------------------------===// 154 | 155 | #if canImport(MachO) 156 | 157 | #if arch(x86_64) || arch(arm64) 158 | typealias mach_header_platform = mach_header_64 159 | #else 160 | typealias mach_header_platform = mach_header 161 | #endif 162 | 163 | @_cdecl("lookupSection") 164 | func lookupSection( 165 | _ header: UnsafePointer?, 166 | segment: UnsafePointer?, 167 | section: UnsafePointer?, 168 | do handler: @convention(c) (UnsafeRawPointer, Int) -> () 169 | ) { 170 | guard let header = header else { 171 | return 172 | } 173 | 174 | var size: UInt = 0 175 | 176 | let section = header.withMemoryRebound( 177 | to: mach_header_platform.self, 178 | capacity: 1 179 | ) { 180 | getsectiondata($0, segment, section, &size) 181 | } 182 | 183 | guard section != nil else { 184 | return 185 | } 186 | 187 | handler(section!, Int(size)) 188 | } 189 | 190 | #endif 191 | 192 | //===----------------------------------------------------------------------===// 193 | // ELF Image Inspection 194 | //===----------------------------------------------------------------------===// 195 | 196 | #if os(Linux) 197 | 198 | let sharedObjectLock = NSLock() 199 | var sharedObjects = Set() 200 | 201 | @_cdecl("cacheSharedObject") 202 | func cacheSharedObject(cString: UnsafePointer) -> Bool { 203 | let str = String(cString: cString) 204 | 205 | let entry = sharedObjectLock.withLock { 206 | sharedObjects.insert(str) 207 | } 208 | 209 | return entry.inserted 210 | } 211 | 212 | #endif 213 | -------------------------------------------------------------------------------- /Sources/Echo/Runtime/KeyPaths.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KeyPaths.swift 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2020 - 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | /* 10 | extension AnyKeyPath { 11 | public var keyPathObject: KeyPathObject { 12 | // Because keypaths are classes, they are just pointers to a heap object. 13 | KeyPathObject(ptr: unsafeBitCast(self, to: UnsafeRawPointer.self)) 14 | } 15 | } 16 | 17 | public struct KeyPathObject: LayoutWrapper { 18 | typealias Layout = _KeyPathObject 19 | 20 | public let ptr: UnsafeRawPointer 21 | 22 | public var heapObject: HeapObject { 23 | layout._heapObject 24 | } 25 | 26 | public var kvcString: String? { 27 | layout._kvc?.string 28 | } 29 | 30 | public var bufferHeader: KeyPathBufferHeader { 31 | layout._bufferHeader 32 | } 33 | 34 | public var components: [KeyPathComponent] { 35 | var result = [KeyPathComponent]() 36 | 37 | var componentPtr = ptr.offset(of: 4) 38 | var bufferSize = Int(bufferHeader.size) 39 | 40 | while bufferSize > 0 { 41 | let component = KeyPathComponent(ptr: componentPtr) 42 | result.append(component) 43 | 44 | switch component.kind { 45 | case .external, 46 | .struct, 47 | .class, 48 | .optionalChain, 49 | .optionalWrap, 50 | .optionalForce: 51 | // This includes the component itself (along with the padding on 64 bit 52 | // machines) and the metadata pointer afterwards. 53 | componentPtr = componentPtr.offset(of: 2) 54 | bufferSize -= MemoryLayout.size * 2 55 | 56 | // However, if the offset is too large for the payload space then there 57 | // will be another 4 byte payload immediately after the component. 64 58 | // bit machines don't have to worry about this because the padding is 59 | // used, but on 32 bit theres an extra payload we have to take into 60 | // account. 61 | if MemoryLayout.size == 4 && component.hasTrailingPayload { 62 | componentPtr = componentPtr.offset(of: 1) 63 | bufferSize -= MemoryLayout.size 64 | } 65 | 66 | case .computed: 67 | // 1 offset is the component, the other 2 are the identifier pointer 68 | // and the getter pointer. On 64 bit machines this includes the padding 69 | // after the component. 70 | componentPtr = componentPtr.offset(of: 3) 71 | bufferSize -= MemoryLayout.size * 3 72 | 73 | // An optional setter pointer if the computed proptery is settable. 74 | // (Not settable for a get only or a nonmutating set). 75 | if component.isComputedSettable { 76 | componentPtr = componentPtr.offset(of: 1) 77 | bufferSize -= MemoryLayout.size 78 | } 79 | 80 | // Computed components have a 2 word header that include 1. the amount 81 | // of bytes the captures are and 2. a pointer to the argument witness 82 | // table. 83 | if component.hasCapturedArguments { 84 | let argumentSize = componentPtr.load(as: Int.self) 85 | 86 | componentPtr = componentPtr.offset(of: 2) 87 | bufferSize -= MemoryLayout.size * 2 88 | 89 | // Advance argumentSize bytes. 90 | componentPtr += argumentSize 91 | bufferSize -= argumentSize 92 | } 93 | 94 | // And finally, the metadata pointer. 95 | componentPtr = componentPtr.offset(of: 1) 96 | bufferSize -= MemoryLayout.size 97 | } 98 | } 99 | 100 | return result 101 | } 102 | } 103 | 104 | extension KeyPathObject: CustomStringConvertible { 105 | public var description: String { 106 | let components = self.components 107 | 108 | assert(heapObject.metadata.kind == .class, 109 | "KeyPath class object that's not a class?") 110 | let classMetadata = heapObject.metadata as! ClassMetadata 111 | assert(classMetadata.genericTypes.count == 2, 112 | "KeyPath type without 2 generic types? Root and Leaf?") 113 | let root = classMetadata.genericTypes[0] 114 | 115 | guard components.allSatisfy({ $0.kind == .struct || 116 | $0.kind == .class }) else { 117 | return "\(classMetadata.type)" 118 | } 119 | 120 | var currentRoot = reflect(root) as! TypeMetadata 121 | var fields = currentRoot.contextDescriptor.fields 122 | var fieldOffsets = currentRoot.fieldOffsets 123 | var result = "\\\(root)." 124 | 125 | for component in components { 126 | for (i, fieldOffset) in fieldOffsets.enumerated() { 127 | if component.storedOffset == fieldOffset { 128 | let field = fields.records[i] 129 | let fieldName = field.name 130 | result += "\(fieldName)." 131 | 132 | let fieldType = currentRoot.type(of: field.mangledTypeName)! 133 | 134 | // If the field type is not a nominal type, then this is our last 135 | // component. 136 | guard let newRoot = reflect(fieldType) as? TypeMetadata else { 137 | break 138 | } 139 | 140 | currentRoot = newRoot 141 | fields = currentRoot.contextDescriptor.fields 142 | fieldOffsets = currentRoot.fieldOffsets 143 | } 144 | } 145 | } 146 | 147 | result.removeLast() 148 | 149 | return result 150 | } 151 | } 152 | 153 | extension AnyKeyPath: CustomStringConvertible { 154 | public var description: String { 155 | keyPathObject.description 156 | } 157 | } 158 | 159 | public struct KeyPathBufferHeader { 160 | public let bits: UInt32 161 | 162 | public init(hasReferencePrefix: Bool, isTrivial: Bool, size: UInt32) { 163 | var bits = size 164 | 165 | if hasReferencePrefix { 166 | bits |= 0x40000000 167 | } 168 | 169 | if isTrivial { 170 | bits |= 0x80000000 171 | } 172 | 173 | self.bits = bits 174 | } 175 | 176 | public var hasReferencePrefix: Bool { 177 | bits & 0x40000000 != 0 178 | } 179 | 180 | public var isTrivial: Bool { 181 | bits & 0x80000000 != 0 182 | } 183 | 184 | public var size: UInt32 { 185 | bits & 0xFFFFFF 186 | } 187 | } 188 | 189 | public struct KeyPathComponent: LayoutWrapper { 190 | typealias Layout = UInt32 191 | 192 | let ptr: UnsafeRawPointer 193 | 194 | public var hasCapturedArguments: Bool { 195 | precondition(kind == .computed) 196 | return layout & 0x80000 != 0 197 | } 198 | 199 | var hasTrailingPayload: Bool { 200 | return payload == 0xFFFFFF 201 | } 202 | 203 | public var identifierKind: (Bool, Bool) { 204 | (layout & 0x200000 != 0, layout & 0x100000 != 0) 205 | } 206 | 207 | public var isComputedMutating: Bool { 208 | precondition(kind == .computed) 209 | return layout & 0x800000 != 0 210 | } 211 | 212 | public var isComputedSettable: Bool { 213 | precondition(kind == .computed) 214 | return layout & 0x400000 != 0 215 | } 216 | 217 | public var isEndOfReferencePrefix: Bool { 218 | layout & 0x80000000 != 0 219 | } 220 | 221 | public var kind: Kind { 222 | switch (layout & 0x7F000000) >> 24 { 223 | case 0: 224 | return .external 225 | case 1: 226 | return .struct 227 | case 2: 228 | return .computed 229 | case 3: 230 | return .class 231 | case 4: // .optional 232 | switch payload { 233 | case 0: 234 | return .optionalChain 235 | case 1: 236 | return .optionalWrap 237 | case 2: 238 | return .optionalForce 239 | default: 240 | fatalError("Invalid optional key path component payload") 241 | } 242 | default: 243 | fatalError("Invalid key path component kind") 244 | } 245 | } 246 | 247 | public var payload: UInt32 { 248 | layout & 0xFFFFFF 249 | } 250 | 251 | public var storedOffset: Int { 252 | precondition(kind == .struct || kind == .class) 253 | guard !hasTrailingPayload else { 254 | return Int(trailing.load(as: UInt32.self)) 255 | } 256 | 257 | return Int(payload) 258 | } 259 | } 260 | 261 | extension KeyPathComponent { 262 | public enum Kind { 263 | case external 264 | case `struct` 265 | case computed 266 | case `class` 267 | case optionalChain 268 | case optionalWrap 269 | case optionalForce 270 | } 271 | } 272 | 273 | struct _KeyPathObject { 274 | let _heapObject: HeapObject 275 | let _kvc: UnsafePointer? 276 | let _bufferHeader: KeyPathBufferHeader 277 | } 278 | */ 279 | -------------------------------------------------------------------------------- /Sources/Echo/Runtime/KnownMetadata.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KnownMetadata.swift 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2019 - 2020 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | import CEcho 10 | 11 | public enum KnownMetadata {} 12 | 13 | extension KnownMetadata { 14 | public enum Builtin {} 15 | } 16 | 17 | extension KnownMetadata.Builtin { 18 | public static var int1: OpaqueMetadata { 19 | OpaqueMetadata(ptr: getBuiltinInt1Metadata()) 20 | } 21 | 22 | public static var int7: OpaqueMetadata { 23 | OpaqueMetadata(ptr: getBuiltinInt7Metadata()) 24 | } 25 | 26 | public static var int8: OpaqueMetadata { 27 | OpaqueMetadata(ptr: getBuiltinInt8Metadata()) 28 | } 29 | 30 | public static var int16: OpaqueMetadata { 31 | OpaqueMetadata(ptr: getBuiltinInt16Metadata()) 32 | } 33 | 34 | public static var int32: OpaqueMetadata { 35 | OpaqueMetadata(ptr: getBuiltinInt32Metadata()) 36 | } 37 | 38 | public static var int64: OpaqueMetadata { 39 | OpaqueMetadata(ptr: getBuiltinInt64Metadata()) 40 | } 41 | 42 | public static var int128: OpaqueMetadata { 43 | OpaqueMetadata(ptr: getBuiltinInt128Metadata()) 44 | } 45 | 46 | public static var int256: OpaqueMetadata { 47 | OpaqueMetadata(ptr: getBuiltinInt256Metadata()) 48 | } 49 | 50 | public static var int512: OpaqueMetadata { 51 | OpaqueMetadata(ptr: getBuiltinInt512Metadata()) 52 | } 53 | 54 | public static var word: OpaqueMetadata { 55 | OpaqueMetadata(ptr: getBuiltinWordMetadata()) 56 | } 57 | 58 | public static var fpiee16: OpaqueMetadata { 59 | OpaqueMetadata(ptr: getBuiltinFPIEE16Metadata()) 60 | } 61 | 62 | public static var fpiee32: OpaqueMetadata { 63 | OpaqueMetadata(ptr: getBuiltinFPIEE32Metadata()) 64 | } 65 | 66 | public static var fpiee64: OpaqueMetadata { 67 | OpaqueMetadata(ptr: getBuiltinFPIEE64Metadata()) 68 | } 69 | 70 | public static var fpiee80: OpaqueMetadata { 71 | OpaqueMetadata(ptr: getBuiltinFPIEE80Metadata()) 72 | } 73 | 74 | public static var fpiee128: OpaqueMetadata { 75 | OpaqueMetadata(ptr: getBuiltinFPIEE128Metadata()) 76 | } 77 | 78 | public static var nativeObject: OpaqueMetadata { 79 | OpaqueMetadata(ptr: getBuiltinNativeObjectMetadata()) 80 | } 81 | 82 | public static var bridgeObject: OpaqueMetadata { 83 | OpaqueMetadata(ptr: getBuiltinBridgeObjectMetadata()) 84 | } 85 | 86 | public static var rawPointer: OpaqueMetadata { 87 | OpaqueMetadata(ptr: getBuiltinRawPointerMetadata()) 88 | } 89 | 90 | public static var unsafeValueBuffer: OpaqueMetadata { 91 | OpaqueMetadata(ptr: getBuiltinUnsafeValueBufferMetadata()) 92 | } 93 | 94 | public static var unknownObject: OpaqueMetadata { 95 | OpaqueMetadata(ptr: getBuiltinUnknownObjectMetadata()) 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /Sources/Echo/Runtime/RuntimeValues.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RuntimeValues.swift 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2020 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | extension ConformanceDescriptor { 10 | /// The flags that describe a conformance to a protocol for a type. 11 | public struct Flags { 12 | /// Flags as represented in bits. 13 | public let bits: UInt32 14 | 15 | /// Whether or not the conformance descriptor's witness table pattern is 16 | /// used as a pattern or if it's served as the real witness table. This is 17 | /// most likely true when the conformance is about a generic type and false 18 | /// when the conformance is about a non generic type. Please make sure to 19 | /// consult this flag beforehand though to make sure. 20 | public var hasGenericWitnessTable: Bool { 21 | bits & (0x1 << 17) != 0 22 | } 23 | 24 | /// Whether or not this conformance has resilient witnesses. 25 | public var hasResilientWitnesses: Bool { 26 | bits & (0x1 << 16) != 0 27 | } 28 | 29 | /// Whether or not this conformance is retroactive. A conformance is 30 | /// considered retroactive when it happens in a module that is not the 31 | /// module the protocol was defined in and not the module the type conforming 32 | /// was defined in. 33 | public var isRetroactive: Bool { 34 | bits & (0x1 << 6) != 0 35 | } 36 | 37 | /// Whether or not this conformance was synthesized non-uniquely. This 38 | /// happens when an imported C structure or such defines a Swift conformance. 39 | public var isSynthesizedNonUnique: Bool { 40 | bits & (0x1 << 7) != 0 41 | } 42 | 43 | /// The number of conditional requirements this conformance requires. This 44 | /// occurs with conditional conformance situations where a type only conforms 45 | /// if one/a few/all of its generic parameters conform to some protocol. 46 | /// Another example is a type conforming to some protocol if it has some 47 | /// same type requirement. 48 | public var numConditionalRequirements: Int { 49 | Int(bits & (0xFF << 8)) >> 8 50 | } 51 | 52 | /// The type reference kind to the type that is conforming to some protocol 53 | /// in this conformance. 54 | public var typeReferenceKind: TypeReferenceKind { 55 | TypeReferenceKind(rawValue: UInt16(bits & (0x7 << 3)) >> 3)! 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Sources/Echo/Runtime/WitnessTable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WitnessTable.swift 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2020 - 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | /// In its simpliest form, a witness table is simply a table of function pointers 10 | /// that fulfill the requirements a protocol imposes. Witness tables instruct 11 | /// exactly how a type conforms to a protocol and the functions needed to 12 | /// satisy a protocol requirement. 13 | /// 14 | /// - Note: While witness tables in the form provided are ABI stable (on some 15 | /// platforms), its runtime layout is not ABI stable. The only ABI 16 | /// stable portion of a witness table is the conformance descriptor 17 | /// pointer at the beginning, the rest of the layout is completely 18 | /// dependent on the protocol and runtime being used. 19 | /// 20 | /// ABI Stability: Stable since the following 21 | /// 22 | /// | macOS | iOS/tvOS | watchOS | Linux | Windows | 23 | /// |-------|----------|---------|-------|---------| 24 | /// | 10.14 | 12.2 | 5.2 | NA | NA | 25 | /// 26 | public struct WitnessTable: LayoutWrapper { 27 | typealias Layout = _WitnessTable 28 | 29 | let ptr: UnsafeRawPointer 30 | 31 | /// The conformance descriptor that describes the protocol conformance 32 | /// relationship for whatever type this witness table is representing, and 33 | /// the protocol that type conforms to. 34 | public var conformanceDescriptor: ConformanceDescriptor { 35 | layout._conformance 36 | } 37 | } 38 | 39 | struct _WitnessTable { 40 | let _conformance: ConformanceDescriptor 41 | } 42 | -------------------------------------------------------------------------------- /Sources/Echo/Utils/FieldType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FieldType.swift 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2019 - 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | // This isn't used anywhere yet... ? 10 | struct FieldType { 11 | let bits: UInt 12 | 13 | var isIndirect: Bool { 14 | bits & 0x1 != 0 15 | } 16 | 17 | var isWeak: Bool { 18 | bits & 0x2 != 0 19 | } 20 | 21 | var metadata: Metadata { 22 | reflect(type) 23 | } 24 | 25 | var type: Any.Type { 26 | let typeMask = UInt.max & ~UInt(MemoryLayout.alignment - 1) 27 | let address = UnsafeRawPointer(bitPattern: bits & typeMask)! 28 | return unsafeBitCast(address, to: Any.Type.self) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Sources/Echo/Utils/Misc.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Misc.swift 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2019 - 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension UnsafePointer { 12 | var raw: UnsafeRawPointer { 13 | UnsafeRawPointer(self) 14 | } 15 | } 16 | 17 | extension UnsafeMutablePointer { 18 | var raw: UnsafeRawPointer { 19 | UnsafeRawPointer(self) 20 | } 21 | } 22 | 23 | extension UnsafeMutableRawPointer { 24 | var raw: UnsafeRawPointer { 25 | UnsafeRawPointer(self) 26 | } 27 | } 28 | 29 | extension UnsafeRawPointer { 30 | func offset( 31 | of offset: Int 32 | ) -> UnsafeRawPointer { 33 | advanced(by: MemoryLayout.size * offset) 34 | } 35 | 36 | func offset( 37 | of offset: Int, 38 | as type: T.Type 39 | ) -> UnsafeRawPointer { 40 | advanced(by: MemoryLayout.size * offset) 41 | } 42 | 43 | var mutable: UnsafeMutableRawPointer { 44 | UnsafeMutableRawPointer(mutating: self) 45 | } 46 | } 47 | 48 | extension UnsafePointer where Pointee == CChar { 49 | var string: String { 50 | String(cString: self) 51 | } 52 | } 53 | 54 | extension UnsafeRawPointer { 55 | var string: String { 56 | String(cString: assumingMemoryBound(to: CChar.self)) 57 | } 58 | } 59 | 60 | extension NSLock { 61 | func withLock(_ closure: () throws -> T) rethrows -> T { 62 | lock() 63 | defer { unlock() } 64 | return try closure() 65 | } 66 | } 67 | 68 | protocol LayoutWrapper { 69 | associatedtype Layout 70 | 71 | var ptr: UnsafeRawPointer { get } 72 | } 73 | 74 | extension LayoutWrapper { 75 | var layout: Layout { 76 | ptr.load(as: Layout.self) 77 | } 78 | 79 | var trailing: UnsafeRawPointer { 80 | ptr + MemoryLayout.size 81 | } 82 | 83 | func address( 84 | for field: KeyPath 85 | ) -> UnsafeRawPointer { 86 | let offset = MemoryLayout.offset(of: field)! 87 | return ptr + offset 88 | } 89 | 90 | func address( 91 | for field: KeyPath 92 | ) -> UnsafeRawPointer { 93 | let offset = MemoryLayout.offset(of: field)! 94 | return layout[keyPath: field].address(from: ptr + offset) 95 | } 96 | } 97 | 98 | // https://github.com/apple/swift/blob/master/stdlib/public/core/KeyPath.swift 99 | func getSymbolicMangledNameLength(_ base: UnsafeRawPointer) -> Int { 100 | var end = base 101 | while let current = Optional(end.load(as: UInt8.self)), current != 0 { 102 | // Skip the current character 103 | end = end + 1 104 | 105 | // Skip over a symbolic reference 106 | if current >= 0x1 && current <= 0x17 { 107 | end += 4 108 | } else if current >= 0x18 && current <= 0x1F { 109 | end += MemoryLayout.size 110 | } 111 | } 112 | 113 | return end - base 114 | } 115 | -------------------------------------------------------------------------------- /Sources/Echo/Utils/RelativeDirectPointer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RelativeDirectPointer.swift 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2019 - 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | struct RelativeDirectPointer: RelativePointer { 10 | let offset: Int32 11 | 12 | func pointee(from ptr: UnsafeRawPointer) -> Pointee? { 13 | if isNull { 14 | return nil 15 | } 16 | 17 | return address(from: ptr).load(as: Pointee.self) 18 | } 19 | } 20 | 21 | extension UnsafeRawPointer { 22 | func relativeDirectAddress(as type: T.Type) -> UnsafeRawPointer { 23 | let relativePointer = RelativeDirectPointer( 24 | offset: load(as: Int32.self) 25 | ) 26 | return relativePointer.address(from: self) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Sources/Echo/Utils/RelativeIndirectPointer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RelativeIndirectPointer.swift 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2019 - 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | struct RelativeIndirectPointer: RelativePointer { 10 | typealias Pointee = UnsafePointer 11 | 12 | let offset: Int32 13 | 14 | func pointee(from ptr: UnsafeRawPointer) -> Pointee? { 15 | if isNull { 16 | return nil 17 | } 18 | 19 | return address(from: ptr).load(as: Pointee.self) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Sources/Echo/Utils/RelativeIndirectablePointer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RelativeIndirectablePointer.swift 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2019 - 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | struct RelativeIndirectablePointer: RelativePointer { 10 | let offset: Int32 11 | 12 | func address(from ptr: UnsafeRawPointer) -> UnsafeRawPointer { 13 | let start = ptr + Int(offset & ~1) 14 | 15 | if Int(offset) & 1 == 1 { 16 | return start.load(as: UnsafeRawPointer.self) 17 | } else { 18 | return start 19 | } 20 | } 21 | 22 | func pointee(from ptr: UnsafeRawPointer) -> Pointee? { 23 | if isNull { 24 | return nil 25 | } 26 | 27 | return address(from: ptr).load(as: Pointee.self) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Sources/Echo/Utils/RelativeIndirectablePointerIntPair.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RelativeIndirectablePointerIntPair.swift 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2019 - 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | struct RelativeIndirectablePointerIntPair< 10 | Pointee, 11 | IntTy: FixedWidthInteger 12 | >: RelativePointer { 13 | let offset: Int32 14 | 15 | var intMask: Int32 { 16 | Int32(MemoryLayout.alignment - 1) & ~0x1 17 | } 18 | 19 | var int: IntTy { 20 | IntTy(offset & intMask) >> 1 21 | } 22 | 23 | var isSet: Bool { 24 | int & 1 != 0 25 | } 26 | 27 | func address(from ptr: UnsafeRawPointer) -> UnsafeRawPointer { 28 | let offsetIndirect = offset & ~intMask 29 | let addr = ptr + Int(offsetIndirect & ~1) 30 | 31 | if Int(offsetIndirect) & 1 == 1 { 32 | return addr.load(as: UnsafeRawPointer.self) 33 | } else { 34 | return addr 35 | } 36 | } 37 | 38 | func pointee(from ptr: UnsafeRawPointer) -> Pointee? { 39 | if isNull { 40 | return nil 41 | } 42 | 43 | return address(from: ptr).load(as: Pointee.self) 44 | } 45 | } 46 | 47 | extension UnsafeRawPointer { 48 | func relativeIndirectableIntPairAddress( 49 | as type: T.Type, 50 | and typ2: U.Type 51 | ) -> UnsafeRawPointer { 52 | let relativePointer = RelativeIndirectablePointerIntPair( 53 | offset: load(as: Int32.self) 54 | ) 55 | return relativePointer.address(from: self) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Sources/Echo/Utils/RelativePointer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RelativePointer.swift 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2019 - 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | // This relative pointer design is a little different because typical Swift 10 | // implementations would do withUnsafePointer(to: &self), etc. That's fine and 11 | // all, but it requires marking the function mutating which requires the data 12 | // structures that use them to mark mutating everywhere, and it gets ugly. I 13 | // could do something like my current approach and make a copy in every accessor 14 | // that uses a data structure with relative pointers, but it becomes repetitive 15 | // writing `var _structure = _structure`. All of that for writing the naive way 16 | // of porting relative references in Swift. My approach is a little more verbose 17 | // accessing a relative pointer's value, but it doesn't require the var dance. 18 | // I might reconsider this decision down the road, but for now this is what I'm 19 | // going with. At the end of the day, I don't want to require the user to be 20 | // forced to write `var metadata = reflect(type)` as a var when naturally the 21 | // first instinct is to go for a let first. 22 | protocol RelativePointer { 23 | associatedtype Pointee 24 | 25 | var offset: Int32 { get } 26 | 27 | func address(from ptr: UnsafeRawPointer) -> UnsafeRawPointer 28 | func pointee(from ptr: UnsafeRawPointer) -> Pointee? 29 | } 30 | 31 | extension RelativePointer { 32 | var isNull: Bool { 33 | offset == 0 34 | } 35 | 36 | func address(from ptr: UnsafeRawPointer) -> UnsafeRawPointer { 37 | ptr + Int(offset) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Sources/Echo/Utils/SignedPointer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SignedPointer.swift 3 | // Echo 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2021 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | import CEcho 10 | 11 | // A wrapper around a pointer who will return the signed version of the wrapped 12 | // pointer through the `signed` property. 13 | struct SignedPointer { 14 | var ptr: UnsafeRawPointer 15 | 16 | var signed: UnsafeRawPointer { 17 | ptr 18 | } 19 | } 20 | 21 | // Swift type descriptors are signed with the process independent data key. 22 | #if _ptrauth(_arm64e) 23 | extension SignedPointer where Pointee == ProtocolDescriptor { 24 | var signed: UnsafeRawPointer { 25 | __ptrauth_strip_asda(ptr)! 26 | } 27 | } 28 | 29 | extension SignedPointer where Pointee: TypeContextDescriptor { 30 | var signed: UnsafeRawPointer { 31 | __ptrauth_strip_asda(ptr)! 32 | } 33 | } 34 | 35 | extension SignedPointer where Pointee == ValueWitnessTable { 36 | var signed: UnsafeRawPointer { 37 | __ptrauth_strip_asda(ptr)! 38 | } 39 | } 40 | #endif 41 | -------------------------------------------------------------------------------- /Tests/EchoTests/Context Descriptor/AnonymousDescriptor.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import Echo 3 | 4 | struct AnonymousFoo { 5 | private struct AnonymousBar {} 6 | private struct AnonymousGenericBar {} 7 | 8 | static func test() throws { 9 | let metadata = reflectStruct(AnonymousBar.self)! 10 | let parent = metadata.descriptor.parent! as! AnonymousDescriptor 11 | XCTAssertTrue(parent.anonymousFlags.hasMangledName) 12 | let mangledName = try XCTUnwrap(parent.mangledName) 13 | XCTAssertEqual( 14 | mangledName, 15 | "$s9EchoTests12AnonymousFooV0C3Bar33_16BDE84827F25937B00C6B35A30DC536LLV" 16 | ) 17 | } 18 | 19 | static func testGeneric() throws { 20 | let metadata = reflectStruct(AnonymousGenericBar.self)! 21 | let parent = metadata.descriptor.parent! as! AnonymousDescriptor 22 | XCTAssertTrue(parent.anonymousFlags.hasMangledName) 23 | let mangledName = try XCTUnwrap(parent.mangledName) 24 | XCTAssertEqual( 25 | mangledName, 26 | "$s9EchoTests12AnonymousFooV0C10GenericBar33_16BDE84827F25937B00C6B35A30DC536LLV" 27 | ) 28 | } 29 | } 30 | 31 | extension EchoTests { 32 | func testAnonymousDescriptor() throws { 33 | try AnonymousFoo.test() 34 | try AnonymousFoo.testGeneric() 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Tests/EchoTests/Context Descriptor/ClassDescriptor.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import Echo 3 | 4 | class Super { 5 | let name: String 6 | 7 | init(name: String) { 8 | self.name = name 9 | } 10 | 11 | func sayHello() {} 12 | } 13 | 14 | class Child: Super {} 15 | 16 | extension EchoTests { 17 | func testClassDescriptor() { 18 | let metadata = reflectClass(Super.self)! 19 | let descriptor = metadata.descriptor 20 | XCTAssertEqual(descriptor.superclass.load(as: CChar.self), 0) // nullptr 21 | XCTAssertEqual(descriptor.numFields, 1) 22 | XCTAssertEqual(descriptor.numMembers, 3) // name, init, sayHello 23 | XCTAssertEqual(descriptor.fieldOffsetVectorOffset, 10) 24 | 25 | let child = reflectClass(Child.self)! 26 | let size = getSymbolicMangledNameLength(child.descriptor.superclass) 27 | // 5 because symbolic prefix (1), symbol (4) 28 | XCTAssertEqual(size, 5) 29 | XCTAssertEqual(child.descriptor.numFields, 0) 30 | XCTAssertEqual(child.descriptor.numMembers, 0) 31 | XCTAssertEqual(child.descriptor.fieldOffsetVectorOffset, 13) 32 | } 33 | } 34 | 35 | -------------------------------------------------------------------------------- /Tests/EchoTests/Context Descriptor/EnumDescriptor.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import Echo 3 | 4 | enum Colors { 5 | case blue 6 | case red 7 | case yellow 8 | case green(Bool) 9 | } 10 | 11 | extension EchoTests { 12 | func testEnumDescriptor() { 13 | let metadata = reflectEnum(Colors.self)! 14 | XCTAssertEqual(metadata.descriptor.numEmptyCases, 3) 15 | XCTAssertEqual(metadata.descriptor.numPayloadCases, 1) 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /Tests/EchoTests/Context Descriptor/ExtensionDescriptor.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import Echo 3 | 4 | struct ExtensionFoo {} 5 | 6 | // Must be generic extension to trigger unique descriptor 7 | extension ExtensionFoo where T == Int { 8 | struct ExtensionBar {} 9 | } 10 | 11 | extension EchoTests { 12 | func testExtensionDescriptor() { 13 | let metadata = reflectStruct(ExtensionFoo.ExtensionBar.self)! 14 | let extensionDescriptor = metadata.descriptor.parent as! ExtensionDescriptor 15 | let extendedContext = extensionDescriptor.extendedContext 16 | 17 | let size = getSymbolicMangledNameLength(extendedContext) 18 | // 9 because symbolic prefix (1), symbol (4), ySiG (4) 19 | // where ySiG is binding the type to 20 | XCTAssertEqual(size, 9) 21 | } 22 | } 23 | 24 | -------------------------------------------------------------------------------- /Tests/EchoTests/Context Descriptor/FieldDescriptor.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import Echo 3 | 4 | enum FieldDescriptorTests { 5 | 6 | class FieldTesting { 7 | weak var superman: Super? 8 | unowned let child1: Child 9 | unowned(unsafe) let child2: Child 10 | 11 | init(child1: Child, child2: Child) { 12 | self.child1 = child1 13 | self.child2 = child2 14 | } 15 | } 16 | 17 | static func testClass() throws { 18 | let metadata = reflectClass(FieldTesting.self)! 19 | let fields = metadata.descriptor.fields 20 | 21 | XCTAssert(fields.hasMangledTypeName) 22 | XCTAssertEqual(fields.kind, .class) 23 | 24 | let typeName = metadata.type(of: fields.mangledTypeName)! 25 | XCTAssert(typeName == FieldTesting.self) 26 | 27 | XCTAssertEqual(fields.numFields, 3) 28 | 29 | XCTAssertEqual(fields.recordSize, 12) 30 | for (i, record) in fields.records.enumerated() { 31 | XCTAssert(record.hasMangledTypeName) 32 | 33 | let varType = metadata.type(of: record.mangledTypeName)! 34 | 35 | switch i { 36 | case 0: 37 | XCTAssert(varType == Super?.self) 38 | case 1...2: 39 | XCTAssert(varType == Child.self) 40 | default: 41 | break 42 | } 43 | 44 | switch i { 45 | case 0: 46 | XCTAssertEqual(record.referenceStorage, .weak) 47 | XCTAssert(record.flags.isVar) 48 | case 1: 49 | XCTAssertEqual(record.referenceStorage, .unowned) 50 | XCTAssertFalse(record.flags.isVar) 51 | case 2: 52 | XCTAssertEqual(record.referenceStorage, .unmanaged) 53 | XCTAssertFalse(record.flags.isVar) 54 | default: 55 | break 56 | } 57 | 58 | XCTAssertFalse(record.flags.isIndirectCase) 59 | } 60 | } 61 | 62 | enum ABC { 63 | case a, b, c 64 | } 65 | 66 | enum Color { 67 | indirect case color(Color) 68 | case hue(String) 69 | } 70 | 71 | static func testEnum() throws { 72 | let metadata = reflectEnum(ABC.self)! 73 | let fields = metadata.descriptor.fields 74 | 75 | XCTAssert(fields.hasMangledTypeName) 76 | XCTAssertEqual(fields.kind, .enum) 77 | 78 | let typeName = metadata.type(of: fields.mangledTypeName)! 79 | XCTAssert(typeName == ABC.self) 80 | 81 | XCTAssertEqual(fields.numFields, 3) 82 | 83 | XCTAssertEqual(fields.recordSize, 12) 84 | for record in fields.records { 85 | XCTAssertFalse(record.hasMangledTypeName) 86 | XCTAssertEqual(record.referenceStorage, .none) 87 | } 88 | 89 | let colorMetadata = reflectEnum(Color.self)! 90 | let colorFields = colorMetadata.descriptor.fields 91 | 92 | XCTAssertEqual(colorFields.kind, .multiPayloadEnum) 93 | 94 | for (i, record) in colorFields.records.enumerated() { 95 | switch i { 96 | case 0: 97 | XCTAssert(record.flags.isIndirectCase) 98 | XCTAssertFalse(record.flags.isVar) 99 | case 1: 100 | XCTAssertFalse(record.flags.isIndirectCase) 101 | XCTAssertFalse(record.flags.isVar) 102 | default: 103 | break 104 | } 105 | } 106 | 107 | } 108 | 109 | static func testStruct() throws { 110 | let metadata = reflectStruct(Dog.self)! 111 | let fields = metadata.descriptor.fields 112 | 113 | XCTAssert(fields.hasMangledTypeName) 114 | XCTAssertEqual(fields.kind, .struct) 115 | 116 | let typeName = metadata.type(of: fields.mangledTypeName)! 117 | XCTAssert(typeName == Dog.self) 118 | 119 | XCTAssertEqual(fields.numFields, 2) 120 | 121 | XCTAssertEqual(fields.recordSize, 12) 122 | for (i, record) in fields.records.enumerated() { 123 | XCTAssert(record.hasMangledTypeName) 124 | 125 | let varType = metadata.type(of: record.mangledTypeName)! 126 | 127 | switch i { 128 | case 0: 129 | XCTAssert(varType == String.self) 130 | case 1: 131 | XCTAssert(varType == Int.self) 132 | default: 133 | break 134 | } 135 | 136 | XCTAssertEqual(record.referenceStorage, .none) 137 | XCTAssertFalse(record.flags.isIndirectCase) 138 | XCTAssertFalse(record.flags.isVar) 139 | } 140 | } 141 | } 142 | 143 | extension EchoTests { 144 | func testFieldDescriptor() throws { 145 | try FieldDescriptorTests.testClass() 146 | try FieldDescriptorTests.testEnum() 147 | try FieldDescriptorTests.testStruct() 148 | } 149 | } 150 | 151 | -------------------------------------------------------------------------------- /Tests/EchoTests/Context Descriptor/GenericContext.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import Echo 3 | 4 | struct Cat3 {} 5 | 6 | enum GenericContextTests { 7 | static func testNoRequirements() throws { 8 | let metadata = reflectStruct(Cat2.self)! 9 | XCTAssert(metadata.descriptor.flags.isGeneric) 10 | let gc = metadata.descriptor.genericContext! 11 | 12 | XCTAssertEqual(gc.numExtraArguments, 0) 13 | XCTAssertEqual(gc.numKeyArguments, 2) 14 | XCTAssertEqual(gc.numParams, 2) 15 | XCTAssertEqual(gc.numRequirements, 0) 16 | XCTAssertEqual(gc.size, 12) 17 | XCTAssertEqual(gc.parameters.map { $0.bits }, [128, 128]) 18 | XCTAssertEqual(gc.requirements.count, 0) 19 | 20 | // TYPE 21 | 22 | let typeGc = metadata.descriptor.typeGenericContext 23 | XCTAssertEqual(typeGc.size, 20) 24 | XCTAssertEqual(typeGc.genericMetadataPattern.flags.bits, 1073741824) 25 | } 26 | 27 | static func testRequirements() throws { 28 | let metadata = reflectStruct(Cat3.self)! 29 | XCTAssert(metadata.descriptor.flags.isGeneric) 30 | let gc = metadata.descriptor.genericContext! 31 | 32 | XCTAssertEqual(gc.numExtraArguments, 0) 33 | XCTAssertEqual(gc.numKeyArguments, 3) 34 | XCTAssertEqual(gc.numParams, 2) 35 | XCTAssertEqual(gc.numRequirements, 1) 36 | XCTAssertEqual(gc.size, 24) 37 | XCTAssertEqual(gc.parameters.map { $0.bits }, [128, 128]) 38 | XCTAssertEqual(gc.requirements.count, 1) 39 | 40 | for (i, requirement) in gc.requirements.enumerated() { 41 | switch i { 42 | case 0: 43 | XCTAssert(requirement.flags.kind == .protocol) 44 | XCTAssertEqual(requirement.protocol.name, "Equatable") 45 | default: 46 | break 47 | } 48 | } 49 | 50 | // TYPE 51 | 52 | let typeGc = metadata.descriptor.typeGenericContext 53 | XCTAssertEqual(typeGc.size, 32) 54 | XCTAssertEqual(typeGc.genericMetadataPattern.flags.bits, 1073741824) 55 | } 56 | } 57 | 58 | extension EchoTests { 59 | func testGenericContext() throws { 60 | try GenericContextTests.testNoRequirements() 61 | try GenericContextTests.testRequirements() 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Tests/EchoTests/Context Descriptor/ModuleDescriptor.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import Echo 3 | 4 | extension EchoTests { 5 | func testModuleDescriptor() throws { 6 | let metadata = reflectStruct(Int.self)! 7 | let module = try XCTUnwrap(metadata.descriptor.parent) as? ModuleDescriptor 8 | XCTAssertNotNil(module) 9 | XCTAssertEqual(module!.name, "Swift") 10 | XCTAssertNil(module?.parent) 11 | } 12 | } 13 | 14 | -------------------------------------------------------------------------------- /Tests/EchoTests/Context Descriptor/ProtocolDescriptor.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import Echo 3 | 4 | protocol Testing { 5 | associatedtype Hello 6 | associatedtype World 7 | 8 | var name: String { get set } 9 | 10 | func sayHello() -> String 11 | } 12 | 13 | extension EchoTests { 14 | func testProtocolDescriptor() throws { 15 | // Can't reference protocols with associatedtypes yet (!) 16 | let metadata = reflect(_typeByName("9EchoTests7TestingP")!) as! ExistentialMetadata 17 | let proto = metadata.protocols[0] 18 | 19 | XCTAssertEqual(proto.associatedTypeNames, "Hello World") 20 | XCTAssertEqual(proto.name, "Testing") 21 | XCTAssertEqual(proto.numRequirements, 6) 22 | XCTAssertEqual(proto.numRequirementsInSignature, 0) 23 | XCTAssertEqual(proto.protocolFlags.bits, 1) 24 | XCTAssertEqual(proto.requirementSignature.count, 0) 25 | 26 | for (i, requirement) in proto.requirements.enumerated() { 27 | switch i { 28 | case 0: 29 | XCTAssertEqual(requirement.flags.bits, 7) 30 | case 1: 31 | XCTAssertEqual(requirement.flags.bits, 7) 32 | case 2: 33 | XCTAssertEqual(requirement.flags.bits, 19) 34 | case 3: 35 | XCTAssertEqual(requirement.flags.bits, 20) 36 | case 4: 37 | XCTAssertEqual(requirement.flags.bits, 22) 38 | case 5: 39 | XCTAssertEqual(requirement.flags.bits, 17) 40 | default: 41 | break 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Tests/EchoTests/Context Descriptor/StructDescriptor.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import Echo 3 | 4 | struct Dog { 5 | let name: String 6 | let age: Int 7 | } 8 | 9 | extension EchoTests { 10 | func testStructDescriptor() throws { 11 | let metadata = reflectStruct(Dog.self)! 12 | let descriptor = metadata.descriptor 13 | XCTAssertEqual(descriptor.numFields, 2) 14 | XCTAssertEqual(descriptor.fieldOffsetVectorOffset, 2) 15 | XCTAssertNil(descriptor.foreignMetadataInitialization) 16 | XCTAssertNil(descriptor.singletonMetadataInitialization) 17 | } 18 | } 19 | 20 | -------------------------------------------------------------------------------- /Tests/EchoTests/EchoTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import Echo 3 | 4 | final class EchoTests: XCTestCase { 5 | static var allTests = [ 6 | // Context Descriptors 7 | 8 | ("testAnonymousDescriptor", testAnonymousDescriptor), 9 | ("testClassDescriptor", testClassDescriptor), 10 | ("testEnumDescriptor", testEnumDescriptor), 11 | ("testExtensionDescriptor", testExtensionDescriptor), 12 | ("testFieldDescriptor", testFieldDescriptor), 13 | ("testGenericContext", testGenericContext), 14 | ("testModuleDescriptor", testModuleDescriptor), 15 | ("testProtocolDescriptor", testProtocolDescriptor), 16 | ("testStructDescriptor", testStructDescriptor), 17 | 18 | // Metadata 19 | 20 | ("testClassMetadata", testClassMetadata), 21 | ("testEnumMetadata", testEnumMetadata), 22 | ("testExistentialMetadata", testExistentialMetadata), 23 | ("testExistentialMetatypeMetadata", testExistentialMetatypeMetadata), 24 | ("testFunctionMetadata", testFunctionMetadata), 25 | ("testMetadataAccessFunction", testMetadataAccessFunction), 26 | ("testMetatypeMetadata", testMetatypeMetadata), 27 | ("testObjCClassWrapperMetadata", testObjCClassWrapperMetadata), 28 | ("testOpaqueMetadata", testOpaqueMetadata), 29 | ("testStructMetadata", testStructMetadata), 30 | ("testTupleMetadata", testTupleMetadata), 31 | 32 | // Runtime 33 | 34 | ("testConformanceDescriptor", testConformanceDescriptor), 35 | ("testExistentialContainer", testExistentialContainer), 36 | ("testImageInspection", testImageInspection), 37 | ] 38 | } 39 | 40 | -------------------------------------------------------------------------------- /Tests/EchoTests/Metadata/ClassMetadata.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import Echo 3 | 4 | class Boat { 5 | let name: String 6 | let designDate: Int 7 | 8 | init(name: String, designDate: Int) { 9 | self.name = name 10 | self.designDate = designDate 11 | } 12 | } 13 | 14 | class Boat2 { 15 | let name: T 16 | let designDate: U 17 | 18 | init(name: T, designDate: U) { 19 | self.name = name 20 | self.designDate = designDate 21 | } 22 | } 23 | 24 | class Boat3: JSONEncoder { 25 | let name: T 26 | 27 | init(name: T) { 28 | self.name = name 29 | } 30 | } 31 | 32 | enum ClassMetadataTests { 33 | static func testClass() throws { 34 | let maybeMetadata = reflectClass(Boat.self) 35 | XCTAssertNotNil(maybeMetadata) 36 | 37 | let metadata = maybeMetadata! 38 | 39 | XCTAssertEqual(metadata.classAddressPoint, 16) 40 | XCTAssertEqual(metadata.classSize, 120) 41 | XCTAssertEqual(metadata.instanceAddressPoint, 0) 42 | XCTAssertEqual(metadata.instanceAlignmentMask, 7) 43 | XCTAssertEqual(metadata.instanceSize, 40) 44 | XCTAssertEqual(metadata.flags.bits, 2) 45 | XCTAssertEqual(metadata.fieldOffsets, [16, 32]) 46 | XCTAssertEqual(metadata.isSwiftClass, true) 47 | #if canImport(ObjectiveC) 48 | XCTAssertNotNil(metadata.isaPointer) 49 | XCTAssertNotNil(metadata.superclassType) 50 | #else 51 | XCTAssertNil(metadata.isaPointer) 52 | XCTAssertNil(metadata.superclassType) 53 | #endif 54 | XCTAssert(typeArraysEquals(metadata.genericTypes, [])) 55 | XCTAssertEqual(metadata.kind, .class) 56 | XCTAssert(metadata.type == Boat.self) 57 | 58 | // VWT 59 | 60 | var extraInhabitantCount = 2147483647 61 | #if os(Linux) 62 | extraInhabitantCount = 4096 63 | #endif 64 | 65 | XCTAssertEqual(metadata.vwt.extraInhabitantCount, extraInhabitantCount) 66 | XCTAssertEqual(metadata.vwt.size, 8) 67 | XCTAssertEqual(metadata.vwt.stride, 8) 68 | XCTAssertEqual(metadata.vwt.flags.bits, 65543) 69 | } 70 | 71 | static func testGenericClass() throws { 72 | let maybeMetadata = reflectClass(Boat2.self) 73 | XCTAssertNotNil(maybeMetadata) 74 | 75 | let metadata = maybeMetadata! 76 | 77 | XCTAssertEqual(metadata.classAddressPoint, 16) 78 | XCTAssertEqual(metadata.classSize, 136) 79 | XCTAssertEqual(metadata.instanceAddressPoint, 0) 80 | XCTAssertEqual(metadata.instanceAlignmentMask, 7) 81 | XCTAssertEqual(metadata.instanceSize, 40) 82 | XCTAssertEqual(metadata.flags.bits, 2) 83 | XCTAssertEqual(metadata.fieldOffsets, [16, 32]) 84 | XCTAssertEqual(metadata.isSwiftClass, true) 85 | #if canImport(ObjectiveC) 86 | XCTAssertNotNil(metadata.isaPointer) 87 | XCTAssertNotNil(metadata.superclassType) 88 | #else 89 | XCTAssertNil(metadata.isaPointer) 90 | XCTAssertNil(metadata.superclassType) 91 | #endif 92 | XCTAssert(typeArraysEquals(metadata.genericTypes, [String.self, Int.self])) 93 | XCTAssertEqual(metadata.kind, .class) 94 | XCTAssert(metadata.type == Boat2.self) 95 | 96 | // VWT 97 | 98 | var extraInhabitantCount = 2147483647 99 | #if os(Linux) 100 | extraInhabitantCount = 4096 101 | #endif 102 | 103 | XCTAssertEqual(metadata.vwt.extraInhabitantCount, extraInhabitantCount) 104 | XCTAssertEqual(metadata.vwt.size, 8) 105 | XCTAssertEqual(metadata.vwt.stride, 8) 106 | XCTAssertEqual(metadata.vwt.flags.bits, 65543) 107 | 108 | // Resilient Superclass Generic Types 109 | 110 | let resilientMetadata = reflectClass(Boat3.self)! 111 | XCTAssert(typeArraysEquals(resilientMetadata.genericTypes, [String.self])) 112 | XCTAssertNotNil(resilientMetadata.superclassType) 113 | XCTAssert(resilientMetadata.superclassType! == JSONEncoder.self) 114 | } 115 | 116 | #if canImport(ObjectiveC) 117 | static func testObjCClass() throws { 118 | let maybeMetadata = reflectClass(NSObject.self) 119 | XCTAssertNotNil(maybeMetadata) 120 | 121 | let metadata = maybeMetadata! 122 | 123 | XCTAssertEqual(metadata.classAddressPoint, 32767) 124 | XCTAssertEqual(metadata.instanceAddressPoint, 32767) 125 | XCTAssertEqual(metadata.instanceAlignmentMask, 32767) 126 | XCTAssertEqual(metadata.isSwiftClass, false) 127 | } 128 | #endif 129 | } 130 | 131 | extension EchoTests { 132 | func testClassMetadata() throws { 133 | try ClassMetadataTests.testClass() 134 | try ClassMetadataTests.testGenericClass() 135 | #if canImport(ObjectiveC) 136 | try ClassMetadataTests.testObjCClass() 137 | #endif 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /Tests/EchoTests/Metadata/EnumMetadata.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import Echo 3 | 4 | enum Colors2 { 5 | case red 6 | case blue(T) 7 | indirect case green(Colors2) 8 | } 9 | 10 | enum EnumMetadataTests { 11 | static func testEnum() throws { 12 | let maybeMetadata = reflectEnum(Colors.self) 13 | XCTAssertNotNil(maybeMetadata) 14 | 15 | let metadata = maybeMetadata! 16 | 17 | XCTAssert(metadata.kind == .enum || metadata.kind == .optional) 18 | XCTAssertEqual(metadata.fieldOffsets, []) 19 | XCTAssert(typeArraysEquals(metadata.genericTypes, [])) 20 | 21 | // VWT 22 | 23 | XCTAssertEqual(metadata.vwt.extraInhabitantCount, 251) 24 | XCTAssertEqual(metadata.vwt.size, 1) 25 | XCTAssertEqual(metadata.vwt.stride, 1) 26 | XCTAssertEqual(metadata.vwt.flags.bits, 2097152) 27 | 28 | // Enum VWT 29 | 30 | withUnsafePointer(to: Colors.blue) { 31 | XCTAssertEqual(metadata.enumVwt.getEnumTag(for: $0), 1) 32 | } 33 | } 34 | 35 | static func testGenericEnum() throws { 36 | let maybeMetadata = reflectEnum(Colors2.self) 37 | XCTAssertNotNil(maybeMetadata) 38 | 39 | let metadata = maybeMetadata! 40 | 41 | XCTAssert(metadata.kind == .enum || metadata.kind == .optional) 42 | XCTAssertEqual(metadata.fieldOffsets, []) 43 | XCTAssert(typeArraysEquals(metadata.genericTypes, [Int.self])) 44 | 45 | // VWT 46 | 47 | XCTAssertEqual(metadata.vwt.extraInhabitantCount, 253) 48 | XCTAssertEqual(metadata.vwt.size, 9) 49 | XCTAssertEqual(metadata.vwt.stride, 16) 50 | XCTAssertEqual(metadata.vwt.flags.bits, 2162695) 51 | 52 | // Enum VWT 53 | 54 | withUnsafePointer(to: Colors2.blue(128)) { 55 | XCTAssertEqual(metadata.enumVwt.getEnumTag(for: $0), 0) 56 | } 57 | } 58 | } 59 | 60 | extension EchoTests { 61 | func testEnumMetadata() throws { 62 | try EnumMetadataTests.testEnum() 63 | try EnumMetadataTests.testGenericEnum() 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Tests/EchoTests/Metadata/ExistentialMetadata.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import Echo 3 | 4 | protocol Testable {} 5 | protocol Testable2 {} 6 | 7 | extension EchoTests { 8 | func testExistentialMetadata() throws { 9 | let maybeMetadata = reflect(Testable.self) as? ExistentialMetadata 10 | XCTAssertNotNil(maybeMetadata) 11 | 12 | let metadata = maybeMetadata! 13 | 14 | XCTAssertEqual(metadata.flags.bits, 2147483649) 15 | XCTAssertEqual(metadata.numProtocols, 1) 16 | XCTAssertNil(metadata.superclass) 17 | XCTAssertEqual(metadata.kind, .existential) 18 | 19 | // VWT 20 | 21 | var extraInhabitantCount = 2147483647 22 | #if os(Linux) 23 | extraInhabitantCount = 4096 24 | #endif 25 | 26 | XCTAssertEqual(metadata.vwt.extraInhabitantCount, extraInhabitantCount) 27 | XCTAssertEqual(metadata.vwt.size, 40) 28 | XCTAssertEqual(metadata.vwt.stride, 40) 29 | XCTAssertEqual(metadata.vwt.flags.bits, 196615) 30 | 31 | // Dual Existential 32 | 33 | let maybeMetadata2 = reflect((Testable & Testable2).self) as? ExistentialMetadata 34 | XCTAssertNotNil(maybeMetadata2) 35 | 36 | let metadata2 = maybeMetadata2! 37 | 38 | XCTAssertEqual(metadata2.flags.bits, 2147483650) 39 | XCTAssertEqual(metadata2.numProtocols, 2) 40 | XCTAssertNil(metadata2.superclass) 41 | XCTAssertEqual(metadata2.kind, .existential) 42 | 43 | // VWT 44 | 45 | XCTAssertEqual(metadata2.vwt.extraInhabitantCount, extraInhabitantCount) 46 | XCTAssertEqual(metadata2.vwt.size, 48) 47 | XCTAssertEqual(metadata2.vwt.stride, 48) 48 | XCTAssertEqual(metadata2.vwt.flags.bits, 196615) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Tests/EchoTests/Metadata/ExistentialMetatypeMetadata.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import Echo 3 | 4 | extension EchoTests { 5 | func testExistentialMetatypeMetadata() throws { 6 | let maybeMetadata = reflect(Testable.Type.self) as? ExistentialMetatypeMetadata 7 | XCTAssertNotNil(maybeMetadata) 8 | 9 | let metadata = maybeMetadata! 10 | 11 | XCTAssert(metadata.instanceType == Testable.self) 12 | XCTAssertEqual(metadata.flags.bits, 2147483649) 13 | 14 | // VWT 15 | 16 | var extraInhabitantCount = 2147483647 17 | #if os(Linux) 18 | extraInhabitantCount = 4096 19 | #endif 20 | 21 | XCTAssertEqual(metadata.vwt.extraInhabitantCount, extraInhabitantCount) 22 | XCTAssertEqual(metadata.vwt.size, 16) 23 | XCTAssertEqual(metadata.vwt.stride, 16) 24 | XCTAssertEqual(metadata.vwt.flags.bits, 7) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Tests/EchoTests/Metadata/FunctionMetadata.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import Echo 3 | 4 | func add(_ x: Int, _ y: Int...) -> Int { 5 | fatalError() 6 | } 7 | 8 | extension EchoTests { 9 | func testFunctionMetadata() throws { 10 | let maybeMetadata = reflect(add(_:_:)) as? FunctionMetadata 11 | XCTAssertNotNil(maybeMetadata) 12 | 13 | let metadata = maybeMetadata! 14 | 15 | XCTAssertEqual(metadata.flags.bits, 100663298) 16 | XCTAssert(typeArraysEquals(metadata.paramTypes, [Int.self, Int.self])) 17 | XCTAssert(metadata.resultType == Int.self) 18 | XCTAssertEqual(metadata.kind, .function) 19 | 20 | for (i, paramFlag) in metadata.paramFlags.enumerated() { 21 | switch i { 22 | case 0: 23 | XCTAssertEqual(paramFlag.bits, 0) 24 | case 1: 25 | XCTAssertEqual(paramFlag.bits, 128) 26 | default: 27 | fatalError() 28 | } 29 | } 30 | 31 | // VWT 32 | 33 | var extraInhabitantCount = 2147483647 34 | #if os(Linux) 35 | extraInhabitantCount = 4096 36 | #endif 37 | 38 | XCTAssertEqual(metadata.vwt.extraInhabitantCount, extraInhabitantCount) 39 | XCTAssertEqual(metadata.vwt.size, 16) 40 | XCTAssertEqual(metadata.vwt.stride, 16) 41 | XCTAssertEqual(metadata.vwt.flags.bits, 65543) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Tests/EchoTests/Metadata/MetadataAccessFunction.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import Echo 3 | 4 | struct FooBar0 {} 5 | struct FooBar1 {} 6 | struct FooBar2 {} 7 | struct FooBar3 {} 8 | struct FooBar4 {} 9 | 10 | struct FooBaz1 {} 11 | struct FooBaz2 {} 12 | struct FooBaz3 {} 13 | struct FooBaz4 {} 14 | 15 | enum MetadataAccessFunctionTests { 16 | static func testPlain() throws { 17 | // 0 ARG 18 | 19 | let metadata0 = reflectStruct(FooBar0.self)! 20 | let accessor0 = metadata0.descriptor.accessor 21 | let response0 = accessor0(.complete) 22 | XCTAssertEqual(response0.state, .complete) 23 | XCTAssert(response0.type == FooBar0.self) 24 | 25 | // 1 ARG 26 | 27 | let metadata1 = reflectStruct(FooBar1.self)! 28 | let accessor1 = metadata1.descriptor.accessor 29 | let response1 = accessor1(.complete, Double.self) 30 | XCTAssertEqual(response1.state, .complete) 31 | XCTAssert(response1.type == FooBar1.self) 32 | 33 | // 2 ARG 34 | 35 | let metadata2 = reflectStruct(FooBar2.self)! 36 | let accessor2 = metadata2.descriptor.accessor 37 | let response2 = accessor2(.complete, Double.self, Double.self) 38 | XCTAssertEqual(response2.state, .complete) 39 | XCTAssert(response2.type == FooBar2.self) 40 | 41 | // 3 ARG 42 | 43 | let metadata3 = reflectStruct(FooBar3.self)! 44 | let accessor3 = metadata3.descriptor.accessor 45 | let response3 = accessor3(.complete, Double.self, Double.self, Double.self) 46 | XCTAssertEqual(response3.state, .complete) 47 | XCTAssert(response3.type == FooBar3.self) 48 | 49 | // 4 ARG 50 | 51 | let metadata4 = reflectStruct(FooBar4.self)! 52 | let accessor4 = metadata4.descriptor.accessor 53 | let response4 = accessor4(.complete, Double.self, Double.self, Double.self, Double.self) 54 | XCTAssertEqual(response4.state, .complete) 55 | XCTAssert(response4.type == FooBar4.self) 56 | } 57 | 58 | static func testWitnessTable() throws { 59 | let equatableMetadata = reflect(_typeByName("SQ")!) as! ExistentialMetadata 60 | let equatable = equatableMetadata.protocols[0] 61 | var doubleEquatable: WitnessTable? = nil 62 | 63 | let hashableMetadata = reflect(_typeByName("SH")!) as! ExistentialMetadata 64 | let hashable = hashableMetadata.protocols[0] 65 | var doubleHashable: WitnessTable? = nil 66 | 67 | for conformance in reflectStruct(Double.self)!.conformances { 68 | if conformance.protocol == equatable { 69 | XCTAssert(!conformance.flags.hasGenericWitnessTable) 70 | doubleEquatable = conformance.witnessTablePattern 71 | } 72 | 73 | if conformance.protocol == hashable { 74 | XCTAssert(!conformance.flags.hasGenericWitnessTable) 75 | doubleHashable = conformance.witnessTablePattern 76 | } 77 | } 78 | 79 | let typeWitness = (Double.self, doubleEquatable) 80 | let typeWitness1 = (Double.self, doubleHashable) 81 | 82 | // 1 ARG 83 | 84 | let metadata1 = reflectStruct(FooBaz1.self)! 85 | let accessor1 = metadata1.descriptor.accessor 86 | let response1 = accessor1(.complete, typeWitness) 87 | XCTAssertEqual(response1.state, .complete) 88 | XCTAssert(response1.type == FooBaz1.self) 89 | 90 | // 2 ARG 91 | 92 | let metadata2 = reflectStruct(FooBaz2.self)! 93 | let accessor2 = metadata2.descriptor.accessor 94 | let response2 = accessor2(.complete, typeWitness, typeWitness) 95 | XCTAssertEqual(response2.state, .complete) 96 | XCTAssert(response2.type == FooBaz2.self) 97 | 98 | // 3 ARG 99 | 100 | let metadata3 = reflectStruct(FooBaz3.self)! 101 | let accessor3 = metadata3.descriptor.accessor 102 | let response3 = accessor3(.complete, typeWitness, typeWitness, typeWitness) 103 | XCTAssertEqual(response3.state, .complete) 104 | XCTAssert(response3.type == FooBaz3.self) 105 | 106 | // 4 ARG 107 | 108 | let metadata4 = reflectStruct(FooBaz4.self)! 109 | let accessor4 = metadata4.descriptor.accessor 110 | let response4 = accessor4(.complete, typeWitness, typeWitness, typeWitness, typeWitness) 111 | XCTAssertEqual(response4.state, .complete) 112 | XCTAssert(response4.type == FooBaz4.self) 113 | 114 | // STDLIB TYPES 115 | 116 | let dictMetadata = reflectStruct([Int: Int].self)! 117 | let dictAccessor = dictMetadata.descriptor.accessor 118 | // The last argument has a nil witness table because the value type in 119 | // dictionary has no conformance requirements. 120 | let dictResponse = dictAccessor(.complete, typeWitness1, (Double.self, nil)) 121 | XCTAssertEqual(dictResponse.state, .complete) 122 | XCTAssert(dictResponse.type == [Double: Double].self) 123 | } 124 | } 125 | 126 | extension EchoTests { 127 | func testMetadataAccessFunction() throws { 128 | try MetadataAccessFunctionTests.testPlain() 129 | try MetadataAccessFunctionTests.testWitnessTable() 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /Tests/EchoTests/Metadata/MetatypeMetadata.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import Echo 3 | 4 | extension EchoTests { 5 | func testMetatypeMetadata() throws { 6 | let maybeMetadata = reflect(Int.Type.self) as? MetatypeMetadata 7 | XCTAssertNotNil(maybeMetadata) 8 | 9 | let metadata = maybeMetadata! 10 | 11 | XCTAssert(metadata.instanceType == Int.self) 12 | XCTAssertEqual(metadata.kind, .metatype) 13 | 14 | // VWT 15 | 16 | var extraInhabitantCount = 2147483647 17 | #if os(Linux) 18 | extraInhabitantCount = 4096 19 | #endif 20 | 21 | XCTAssertEqual(metadata.vwt.extraInhabitantCount, extraInhabitantCount) 22 | XCTAssertEqual(metadata.vwt.size, 8) 23 | XCTAssertEqual(metadata.vwt.stride, 8) 24 | XCTAssertEqual(metadata.vwt.flags.bits, 7) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Tests/EchoTests/Metadata/ObjCClassWrapperMetadata.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import Echo 3 | 4 | extension EchoTests { 5 | func testObjCClassWrapperMetadata() throws { 6 | #if canImport(ObjectiveC) 7 | let maybeMetadata = reflect(NSObject.self) as? ObjCClassWrapperMetadata 8 | XCTAssertNotNil(maybeMetadata) 9 | 10 | let metadata = maybeMetadata! 11 | 12 | // We compare strings here because comparing `NSObject.self` would compare 13 | // the objc class wrapper against class metadata. 14 | XCTAssert("\(metadata.classType)" == "NSObject") 15 | XCTAssertEqual(metadata.kind, .objcClassWrapper) 16 | 17 | // VWT 18 | 19 | XCTAssertEqual(metadata.vwt.extraInhabitantCount, 2147483647) 20 | XCTAssertEqual(metadata.vwt.size, 8) 21 | XCTAssertEqual(metadata.vwt.stride, 8) 22 | XCTAssertEqual(metadata.vwt.flags.bits, 65543) 23 | #endif 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Tests/EchoTests/Metadata/OpaqueMetadata.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import Echo 3 | 4 | extension EchoTests { 5 | func testOpaqueMetadata() throws { 6 | let int128 = Echo.KnownMetadata.Builtin.int128 7 | let int512 = Echo.KnownMetadata.Builtin.int512 8 | XCTAssertFalse(int128 == int512) 9 | XCTAssertEqual(int128.kind, .opaque) 10 | 11 | // VWT 12 | 13 | XCTAssertEqual(int512.vwt.extraInhabitantCount, 0) 14 | XCTAssertEqual(int512.vwt.size, 64) 15 | XCTAssertEqual(int512.vwt.stride, 64) 16 | XCTAssertEqual(int512.vwt.flags.bits, 131087) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Tests/EchoTests/Metadata/StructMetadata.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import Echo 3 | 4 | struct Cat { 5 | let name: String 6 | let age: Int 7 | } 8 | 9 | struct Cat2 { 10 | let name: T 11 | let age: U 12 | } 13 | 14 | enum StructMetadataTests { 15 | static func testStruct() throws { 16 | let maybeMetadata = reflectStruct(Cat.self) 17 | XCTAssertNotNil(maybeMetadata) 18 | 19 | let metadata = maybeMetadata! 20 | 21 | XCTAssertEqual(metadata.fieldOffsets, [0, 16]) 22 | XCTAssert(typeArraysEquals(metadata.genericTypes, [])) 23 | XCTAssertEqual(metadata.kind, .struct) 24 | XCTAssert(metadata.type == Cat.self) 25 | 26 | // VWT 27 | 28 | var extraInhabitantCount = 2147483647 29 | #if os(Linux) 30 | extraInhabitantCount = 4096 31 | #endif 32 | 33 | XCTAssertEqual(metadata.vwt.extraInhabitantCount, extraInhabitantCount) 34 | XCTAssertEqual(metadata.vwt.size, 24) 35 | XCTAssertEqual(metadata.vwt.stride, 24) 36 | XCTAssertEqual(metadata.vwt.flags.bits, 65543) 37 | 38 | // type(of:) 39 | 40 | for (i, record) in metadata.descriptor.fields.records.enumerated() { 41 | XCTAssert(record.hasMangledTypeName) 42 | 43 | switch i { 44 | case 0: 45 | XCTAssert(metadata.type(of: record.mangledTypeName) == String.self) 46 | case 1: 47 | XCTAssert(metadata.type(of: record.mangledTypeName) == Int.self) 48 | default: 49 | fatalError() 50 | } 51 | } 52 | } 53 | 54 | static func testGenericStruct() throws { 55 | let maybeMetadata = reflectStruct(Cat2.self) 56 | XCTAssertNotNil(maybeMetadata) 57 | 58 | let metadata = maybeMetadata! 59 | 60 | XCTAssertEqual(metadata.fieldOffsets, [0, 16]) 61 | XCTAssert(typeArraysEquals(metadata.genericTypes, [String.self, Int.self])) 62 | XCTAssertEqual(metadata.kind, .struct) 63 | XCTAssert(metadata.type == Cat2.self) 64 | 65 | // VWT 66 | 67 | var extraInhabitantCount = 2147483647 68 | #if os(Linux) 69 | extraInhabitantCount = 4096 70 | #endif 71 | 72 | XCTAssertEqual(metadata.vwt.extraInhabitantCount, extraInhabitantCount) 73 | XCTAssertEqual(metadata.vwt.size, 24) 74 | XCTAssertEqual(metadata.vwt.stride, 24) 75 | XCTAssertEqual(metadata.vwt.flags.bits, 65543) 76 | } 77 | } 78 | 79 | extension EchoTests { 80 | func testStructMetadata() throws { 81 | try StructMetadataTests.testStruct() 82 | try StructMetadataTests.testGenericStruct() 83 | } 84 | } 85 | 86 | -------------------------------------------------------------------------------- /Tests/EchoTests/Metadata/TupleMetadata.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import Echo 3 | 4 | extension EchoTests { 5 | func testTupleMetadata() throws { 6 | let maybeMetadata = reflect((Int, String).self) as? TupleMetadata 7 | XCTAssertNotNil(maybeMetadata) 8 | 9 | let metadata = maybeMetadata! 10 | 11 | XCTAssertEqual(metadata.numElements, 2) 12 | 13 | for i in 0 ..< metadata.numElements { 14 | switch i { 15 | case 0: 16 | XCTAssertEqual(metadata.labels[i], "0") 17 | XCTAssert(metadata.elements[i].type == Int.self) 18 | XCTAssertEqual(metadata.elements[i].offset, 0) 19 | case 1: 20 | XCTAssertEqual(metadata.labels[i], "1") 21 | XCTAssert(metadata.elements[i].type == String.self) 22 | XCTAssertEqual(metadata.elements[i].offset, MemoryLayout.size) 23 | default: 24 | fatalError() 25 | } 26 | } 27 | 28 | // LABELS 29 | 30 | let _metadata = reflect((age: Int, name: String).self) as! TupleMetadata 31 | XCTAssertEqual(_metadata.labels, ["age", "name"]) 32 | 33 | // VWT 34 | 35 | var extraInhabitantCount = 2147483647 36 | #if os(Linux) 37 | extraInhabitantCount = 4096 38 | #endif 39 | 40 | XCTAssertEqual(metadata.vwt.extraInhabitantCount, extraInhabitantCount) 41 | XCTAssertEqual(metadata.vwt.size, 24) 42 | XCTAssertEqual(metadata.vwt.stride, 24) 43 | XCTAssertEqual(metadata.vwt.flags.bits, 65543) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Tests/EchoTests/Runtime/ConformanceDescriptor.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import Echo 3 | 4 | protocol Wheel {} 5 | protocol DumbWheel {} 6 | 7 | struct CheeseWheel: Wheel {} 8 | extension CheeseWheel: Equatable {} 9 | extension CheeseWheel: DumbWheel {} 10 | 11 | extension EchoTests { 12 | func testConformanceDescriptor() throws { 13 | let metadata = reflectStruct(CheeseWheel.self)! 14 | let wheelConf = metadata.conformances[0] 15 | 16 | XCTAssertNotNil(wheelConf.contextDescriptor) 17 | #if canImport(ObjectiveC) 18 | XCTAssertNil(wheelConf.objcClass) 19 | #endif 20 | XCTAssertEqual(wheelConf.flags.bits, 0) 21 | XCTAssertEqual(wheelConf.protocol.name, "Wheel") 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Tests/EchoTests/Runtime/ExistentialContainer.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import Echo 3 | 4 | extension EchoTests { 5 | func testExistentialContainer() throws { 6 | // NO WITNESS TABLE 7 | 8 | let x: Any = 128 9 | var xBox = container(for: x) 10 | let dataPtr = xBox.projectValue() 11 | 12 | XCTAssert(xBox.type == Int.self) 13 | XCTAssertEqual(dataPtr.load(as: Int.self), 128) 14 | 15 | // SINGLE WITNESS TABLE 16 | 17 | let y: Wheel = CheeseWheel() 18 | var yBox = unsafeBitCast(y, to: ExistentialContainer.self) 19 | let yPtr = yBox.base.projectValue() 20 | 21 | XCTAssert(yBox.base.type == CheeseWheel.self) 22 | XCTAssertEqual(yPtr.load(as: CheeseWheel.self), CheeseWheel()) 23 | XCTAssertEqual(yBox.witnessTable.conformanceDescriptor.protocol.name, "Wheel") 24 | 25 | // DUAL WITNESS TABLE 26 | 27 | let z: Wheel & DumbWheel = CheeseWheel() 28 | var zBox = unsafeBitCast(z, to: DualExistentialContainer.self) 29 | let zPtr = zBox.base.projectValue() 30 | 31 | XCTAssert(zBox.base.type == CheeseWheel.self) 32 | XCTAssertEqual(zPtr.load(as: CheeseWheel.self), CheeseWheel()) 33 | XCTAssertEqual(zBox.witnessTables.0.conformanceDescriptor.protocol.name, "DumbWheel") 34 | XCTAssertEqual(zBox.witnessTables.1.conformanceDescriptor.protocol.name, "Wheel") 35 | 36 | // Wrapped existentials 37 | 38 | func wrap(_ x: T) -> Any { 39 | x 40 | } 41 | 42 | let wrapped = wrap(wrap(wrap(wrap(128)))) 43 | var wrappedBox = container(for: wrapped) 44 | let wrappedPtr = wrappedBox.projectValue() 45 | XCTAssert(wrappedBox.type == Int.self) 46 | XCTAssertEqual(wrappedPtr.load(as: Int.self), 128) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Tests/EchoTests/Runtime/ImageInspection.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import Echo 3 | 4 | enum ImageInspectionTests { 5 | static func testConformances() throws { 6 | // Echo Types 7 | 8 | let metadata = reflectStruct(StructMetadata.self)! 9 | XCTAssertEqual(metadata.conformances.count, 4) 10 | 11 | // Stdlib types 12 | 13 | let int = reflectStruct(Int.self)! 14 | 15 | var intConfCount = 27 16 | #if os(Linux) 17 | intConfCount = 25 18 | #endif 19 | 20 | XCTAssert(int.conformances.count >= intConfCount) 21 | } 22 | 23 | static func testProtos() throws { 24 | let protos = Echo.protocols 25 | 26 | // Echo Protos 27 | 28 | var echoFound = false 29 | for proto in protos { 30 | guard let module = proto.parent as? ModuleDescriptor else { 31 | continue 32 | } 33 | 34 | guard module.name == "Echo", proto.name == "Metadata" else { 35 | continue 36 | } 37 | 38 | echoFound = true 39 | } 40 | 41 | XCTAssertTrue(echoFound) 42 | 43 | // Stdlib protos 44 | 45 | var stdlibFound = false 46 | for proto in protos { 47 | guard let module = proto.parent as? ModuleDescriptor else { 48 | continue 49 | } 50 | 51 | guard module.name == "Swift", proto.name == "RandomNumberGenerator" else { 52 | continue 53 | } 54 | 55 | stdlibFound = true 56 | } 57 | 58 | XCTAssertTrue(stdlibFound) 59 | } 60 | 61 | static func testTypes() throws { 62 | let types = Echo.types 63 | 64 | // Echo Protos 65 | 66 | var echoFound = false 67 | for type in types { 68 | guard let module = type.parent as? ModuleDescriptor else { 69 | continue 70 | } 71 | 72 | guard let structDescriptor = type as? StructDescriptor else { 73 | continue 74 | } 75 | 76 | guard module.name == "Echo", structDescriptor.name == "StructMetadata" else { 77 | continue 78 | } 79 | 80 | echoFound = true 81 | } 82 | 83 | XCTAssertTrue(echoFound) 84 | 85 | // Stdlib protos 86 | 87 | var stdlibFound = false 88 | for type in types { 89 | guard let module = type.parent as? ModuleDescriptor else { 90 | continue 91 | } 92 | 93 | guard let structDescriptor = type as? StructDescriptor else { 94 | continue 95 | } 96 | 97 | guard module.name == "Swift", structDescriptor.name == "Int" else { 98 | continue 99 | } 100 | 101 | stdlibFound = true 102 | } 103 | 104 | XCTAssertTrue(stdlibFound) 105 | } 106 | } 107 | 108 | extension EchoTests { 109 | func testImageInspection() throws { 110 | try ImageInspectionTests.testConformances() 111 | try ImageInspectionTests.testProtos() 112 | try ImageInspectionTests.testTypes() 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /Tests/EchoTests/Utils/Misc.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Misc.swift 3 | // EchoTests 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2020 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | func typeArraysEquals(_ lhs: [Any.Type], _ rhs: [Any.Type]) -> Bool { 10 | assert(lhs.count == rhs.count) 11 | 12 | for i in 0 ..< lhs.count { 13 | if lhs[i] != rhs[i] { 14 | return false 15 | } 16 | } 17 | 18 | return true 19 | } 20 | 21 | // https://github.com/apple/swift/blob/master/stdlib/public/core/KeyPath.swift 22 | func getSymbolicMangledNameLength(_ base: UnsafeRawPointer) -> Int { 23 | var end = base 24 | while let current = Optional(end.load(as: UInt8.self)), current != 0 { 25 | // Skip the current character 26 | end = end + 1 27 | 28 | // Skip over a symbolic reference 29 | if current >= 0x1 && current <= 0x17 { 30 | end += 4 31 | } else if current >= 0x18 && current <= 0x1F { 32 | end += MemoryLayout.size 33 | } 34 | } 35 | 36 | return end - base 37 | } 38 | -------------------------------------------------------------------------------- /Tests/EchoTests/XCTestManifests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | #if !canImport(ObjectiveC) 4 | public func allTests() -> [XCTestCaseEntry] { 5 | return [ 6 | testCase(EchoTests.allTests), 7 | ] 8 | } 9 | #endif 10 | 11 | -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | import EchoTests 4 | 5 | var tests = [XCTestCaseEntry]() 6 | tests += EchoTests.allTests() 7 | XCTMain(tests) 8 | --------------------------------------------------------------------------------