├── .github ├── actions │ └── setup-swift │ │ └── action.yml └── workflows │ └── build-and-test.yml ├── .gitignore ├── .vscode ├── settings.json └── tasks.json ├── CMakeLists.txt ├── CMakePresets.json ├── License.txt ├── Package.swift ├── Readme.md ├── SPMPreBuild.ps1 ├── Sources ├── CInterop │ └── include │ │ └── module.modulemap ├── CMakeLists.txt ├── DotNetMetadata │ ├── Assembly+resolve.swift │ ├── Assembly.swift │ ├── AssemblyLoadContext.swift │ ├── AssemblyReference.swift │ ├── Attributable.swift │ ├── Attribute.swift │ ├── AttributeTargets.swift │ ├── AttributeType.swift │ ├── Attributes │ │ ├── AttributeUsageAttribute.swift │ │ ├── ComImportAttribute.swift │ │ ├── ComVisibleAttribute.swift │ │ ├── FlagsAttribute.swift │ │ ├── GuidAttribute.swift │ │ └── ObsoleteAttribute.swift │ ├── BaseInterface.swift │ ├── BoundMethod.swift │ ├── BoundType.swift │ ├── CMakeLists.txt │ ├── Collection+single.swift │ ├── CoreLibrary.swift │ ├── Event.swift │ ├── ExportedType.swift │ ├── Field.swift │ ├── GenericParam.swift │ ├── InvalidMetadataError.swift │ ├── Member.swift │ ├── Method.swift │ ├── ModuleFile+resolve.swift │ ├── Optional+lazyInit.swift │ ├── Param.swift │ ├── Property.swift │ ├── Reexported.swift │ ├── SystemAssemblies.swift │ ├── TableHelpers.swift │ ├── TypeDefinition+findMembers.swift │ ├── TypeDefinition.swift │ ├── TypeDefinitionKind.swift │ ├── TypeLayout.swift │ ├── TypeName.swift │ ├── TypeNode.swift │ └── UnexpectedTypeError.swift ├── DotNetMetadataCInterop │ ├── CMakeLists.txt │ ├── include │ │ ├── Metadata.h │ │ ├── PE.h │ │ └── module.modulemap │ └── shim.c ├── DotNetMetadataFormat │ ├── ArrayShape.swift │ ├── AssemblyEnums.swift │ ├── AssemblyIdentity.swift │ ├── AssemblyPublicKey.swift │ ├── AttributesEnums+properties.swift │ ├── AttributesEnums.swift │ ├── CMakeLists.txt │ ├── CodedIndex.swift │ ├── CodedIndices.swift │ ├── CompressedInts.swift │ ├── Constant.swift │ ├── FourPartVersion.swift │ ├── Heaps.swift │ ├── IntegerSize.swift │ ├── InvalidFormatError.swift │ ├── LayoutKind.swift │ ├── MetadataToken.swift │ ├── ModuleFile.swift │ ├── NameKind.swift │ ├── PEView+Section.swift │ ├── PEView.swift │ ├── RandomAccessCollection+binarySearch.swift │ ├── SignatureToken.swift │ ├── Signatures+reading.swift │ ├── Signatures.swift │ ├── Table+Collection.swift │ ├── Table+find.swift │ ├── Table.swift │ ├── TableID.swift │ ├── TableRow.swift │ ├── TableRowIndex.swift │ ├── TableRowReader.swift │ ├── TableRowRef.swift │ ├── TableRowSizeBuilder.swift │ ├── TableRows+Conformance.swift │ ├── TableRows.swift │ ├── TableSizes.swift │ ├── Tables.swift │ ├── TypeDefTable+findMethodOwner.swift │ ├── UnsafePointers+Extensions.swift │ └── Visibility.swift ├── DotNetXMLDocs │ ├── AssemblyDocumentation+init.swift │ ├── AssemblyDocumentation+parse.swift │ ├── AssemblyDocumentation.swift │ ├── AssemblyDocumentationSink.swift │ ├── CMakeLists.txt │ ├── DocumentationFormatError.swift │ ├── DocumentationText+parsing.swift │ ├── DocumentationText.swift │ ├── DocumentationTypeNode+parsing.swift │ ├── DocumentationTypeNode.swift │ ├── DocumentationTypeReference+parsing.swift │ ├── DocumentationTypeReference.swift │ ├── MemberDocumentation+parsing.swift │ ├── MemberDocumentation.swift │ ├── MemberDocumentationKey+parsing.swift │ ├── MemberDocumentationKey.swift │ └── Substring+parsing.swift ├── DotNetXMLDocsFromMetadata │ ├── AssemblyDocumentation+lookup.swift │ ├── CMakeLists.txt │ ├── DocumentationTypeNode+init.swift │ ├── DocumentationTypeReference+init.swift │ └── MemberDocumentationKey+init.swift └── WindowsMetadata │ ├── Attributes │ ├── ActivatableAttribute.swift │ ├── AllowMultipleAttribute.swift │ ├── ApiContractAttribute.swift │ ├── AttributeUsageAttribute.swift │ ├── ComposableAttribute.swift │ ├── ContractVersionAttribute.swift │ ├── DefaultAttribute.swift │ ├── DefaultOverloadAttribute.swift │ ├── DeprecatedAttribute.swift │ ├── DualApiPartitionAttribute.swift │ ├── ExclusiveToAttribute.swift │ ├── ExperimentalAttribute.swift │ ├── GuidAttribute.swift │ ├── InternalAttribute.swift │ ├── LengthIsAttribute.swift │ ├── MarshalingBehaviorAttribute.swift │ ├── NoExceptionAttribute.swift │ ├── OverloadAttribute.swift │ ├── OverridableAttribute.swift │ ├── ProtectedAttribute.swift │ ├── StaticAttribute.swift │ ├── ThreadingAttribute.swift │ └── VariantAttribute.swift │ ├── CMakeLists.txt │ ├── InterfaceID.swift │ ├── MidlNameMangling.swift │ ├── Platform.swift │ ├── SHA1.swift │ ├── TwoPartVersion.swift │ ├── VersionApplicability.swift │ ├── WinMDError.swift │ ├── WinMDLoadContext.swift │ ├── WinRTIntegerType.swift │ ├── WinRTParameterizedType.swift │ ├── WinRTPrimitiveType.swift │ ├── WinRTTypeName+fromType.swift │ ├── WinRTTypeName.swift │ ├── WinRTTypeSignature+fromBoundType.swift │ ├── WinRTTypeSignature+parameterizedID.swift │ ├── WinRTTypeSignature+string.swift │ ├── WinRTTypeSignature.swift │ ├── WindowsKit+ApplicationPlatform.swift │ ├── WindowsKit+Extension.swift │ ├── WindowsKit+ExtensionManifest.swift │ ├── WindowsKit.swift │ └── XMLElement+singleElement.swift ├── Tests ├── DotNetMetadata │ ├── AttributeApplicationTests.swift │ ├── AttributeConstructionTests.swift │ ├── EnumTests.swift │ ├── EventTests.swift │ ├── FieldTests.swift │ ├── NetFX45MscorlibTests+methods.swift │ ├── NetFX45MscorlibTests+typeDefinitions.swift │ ├── NetFX45MscorlibTests.swift │ ├── PropertyTests.swift │ ├── ReferenceCycleTests.swift │ ├── SystemAssemblyPathsTests.swift │ ├── TypeDefinitionTests.swift │ ├── TypeNameTests.swift │ ├── TypeTests.swift │ └── Utilities │ │ ├── CSharpCompilation.swift │ │ ├── CSharpCompilerArgs.swift │ │ ├── CompiledAssemblyTestCase.swift │ │ ├── DotNetTool.swift │ │ └── Win32Process.swift ├── DotNetMetadataFormat │ ├── AssemblyIdentityTests.swift │ ├── BinarySearchTests.swift │ ├── CompressedIntsTests.swift │ ├── SignatureTests.swift │ └── TableColumnSizeTests.swift ├── DotNetXMLDocs │ ├── AssemblyDocumentationTests.swift │ ├── DocumentationTypeNodeTests.swift │ ├── DocumentationTypeReferenceTests.swift │ ├── InstalledWindowsSDKTests.swift │ ├── MemberDocumentationKeyTests.swift │ └── MemberDocumentationTests.swift └── WindowsMetadata │ ├── MidlNameManglingTests.swift │ ├── SHA1Tests.swift │ ├── WinMetadataTests.swift │ ├── WinRTTypeSignatureTests.swift │ └── WindowsKitTests.swift └── WindowsMetadataCoreLibrary ├── Assemble.ps1 ├── CMakeLists.txt └── mscorlib.il /.github/actions/setup-swift/action.yml: -------------------------------------------------------------------------------- 1 | name: Setup Swift 2 | description: Installs dependencies and setups the environment for building Swift code on Windows. 3 | runs: 4 | using: "composite" 5 | 6 | steps: 7 | - name: Setup Visual Studio Development Environment 8 | uses: compnerd/gha-setup-vsdevenv@main 9 | with: 10 | winsdk: "10.0.22621.0" # GitHub runners have 10.0.26100.0 which regresses Swift's ucrt module 11 | 12 | - name: Install Swift 13 | uses: compnerd/gha-setup-swift@81f383b35a86e6e966de139be25b451d4f7dd953 # The next main commit breaks our %Path% 14 | with: 15 | branch: swift-5.8-release 16 | tag: 5.8-RELEASE 17 | -------------------------------------------------------------------------------- /.github/workflows/build-and-test.yml: -------------------------------------------------------------------------------- 1 | name: Build & Test 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | workflow_dispatch: 11 | 12 | jobs: 13 | spm: 14 | name: "With SPM" 15 | runs-on: windows-2022 16 | timeout-minutes: 10 17 | 18 | steps: 19 | - uses: actions/checkout@v3 20 | 21 | - uses: ./.github/actions/setup-swift 22 | 23 | - name: Build 24 | shell: pwsh 25 | run: swift build --verbose --build-tests 26 | 27 | - name: Build mscorlib.winmd 28 | shell: pwsh 29 | run: | 30 | & .\SPMPreBuild.ps1 -BuildDir .build\x86_64-unknown-windows-msvc\debug 31 | 32 | - name: Test 33 | shell: pwsh 34 | run: swift test --verbose --skip-build 35 | 36 | cmake: 37 | name: With CMake (No Tests) 38 | runs-on: windows-2022 39 | timeout-minutes: 15 40 | 41 | steps: 42 | - uses: actions/checkout@v3 43 | 44 | - uses: ./.github/actions/setup-swift 45 | 46 | - name: CMake Configure 47 | shell: pwsh 48 | run: cmake --preset debug 49 | 50 | - name: CMake Build 51 | shell: pwsh 52 | run: cmake --build --preset debug -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /.swiftpm 4 | /.vscode/launch.json 5 | /build -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "swift.autoGenerateLaunchConfigurations": false, 3 | "C_Cpp.autoAddFileAssociations": false, 4 | "cmake.automaticReconfigure": true, 5 | "cmake.useCMakePresets": "always", 6 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "Build Debug with Tests (SPM)", 6 | "type": "swift", 7 | "args": [ 8 | "build", 9 | "--build-tests", 10 | "-Xlinker", 11 | "-debug:dwarf" 12 | ], 13 | "cwd": ".", 14 | "disableTaskQueue": true, 15 | "problemMatcher": [ 16 | "$swiftc" 17 | ], 18 | "group": { 19 | "kind": "build", 20 | "isDefault": true 21 | }, 22 | "presentation": { 23 | "clear": true, 24 | "revealProblems": "onProblem" 25 | } 26 | }, 27 | { 28 | "label": "Build Debug (CMake)", 29 | "type": "cmake", 30 | "command": "build", 31 | "preset": "${command:cmake.activeBuildPresetName}", 32 | "problemMatcher": [ "$swiftc" ], 33 | "group": { 34 | "kind": "build" 35 | }, 36 | "presentation": { 37 | "clear": true, 38 | "revealProblems": "onProblem" 39 | } 40 | } 41 | ] 42 | } -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.21.0) 2 | 3 | project(SwiftWinRT LANGUAGES C Swift) 4 | 5 | if("${CMAKE_EXPORT_COMPILE_COMMANDS}" STREQUAL "") 6 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 7 | endif() 8 | 9 | add_subdirectory(Sources) 10 | add_subdirectory(WindowsMetadataCoreLibrary) -------------------------------------------------------------------------------- /CMakePresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "cmakeMinimumRequired": { 4 | "major": 3, 5 | "minor": 21, 6 | "patch": 0 7 | }, 8 | "configurePresets": [ 9 | { 10 | "name": "debug", 11 | "displayName": "Debug", 12 | "generator": "Ninja", 13 | "binaryDir": "${sourceDir}/build/debug", 14 | "cacheVariables": { 15 | "CMAKE_BUILD_TYPE": "Debug", 16 | "CMAKE_C_COMPILER": "clang", 17 | "CMAKE_C_FLAGS_DEBUG": "-O0 -gdwarf -DNDEBUG", 18 | "CMAKE_Swift_FLAGS_DEBUG": "-DDEBUG -Onone -Xfrontend -g -Xfrontend -debug-info-format=dwarf -use-ld=lld -Xlinker -debug:dwarf" 19 | } 20 | } 21 | ], 22 | "buildPresets": [ 23 | { 24 | "name": "debug", 25 | "displayName": "Debug", 26 | "configurePreset": "debug" 27 | } 28 | ] 29 | } -------------------------------------------------------------------------------- /License.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Tristan Labelle 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.8 2 | 3 | import PackageDescription 4 | 5 | // Workaround for SPM library support limitations causing "LNK4217: locally defined symbol imported" spew 6 | let executableLinkerSettings = [ LinkerSetting.unsafeFlags(["-Xlinker", "-ignore:4217"]) ] 7 | 8 | let package = Package( 9 | name: "DotNetMetadata", 10 | products: [ 11 | .library( 12 | name: "DotNetMetadata", 13 | targets: [ 14 | "DotNetMetadataFormat", 15 | "DotNetMetadata", 16 | "DotNetXMLDocs", 17 | "DotNetXMLDocsFromMetadata", 18 | "WindowsMetadata" 19 | ]) 20 | ], 21 | targets: [ 22 | .target( 23 | name: "DotNetMetadataCInterop", 24 | exclude: ["CMakeLists.txt"]), 25 | 26 | .target( 27 | name: "DotNetMetadataFormat", 28 | dependencies: [ "DotNetMetadataCInterop" ], 29 | exclude: ["CMakeLists.txt"]), 30 | .testTarget( 31 | name: "DotNetMetadataFormatTests", 32 | dependencies: [ "DotNetMetadataFormat" ], 33 | path: "Tests/DotNetMetadataFormat", 34 | linkerSettings: executableLinkerSettings), 35 | 36 | .target( 37 | name: "DotNetMetadata", 38 | dependencies: [ "DotNetMetadataFormat" ], 39 | exclude: ["CMakeLists.txt"]), 40 | .testTarget( 41 | name: "DotNetMetadataTests", 42 | dependencies: [ "DotNetMetadata" ], 43 | path: "Tests/DotNetMetadata", 44 | linkerSettings: executableLinkerSettings), 45 | 46 | .target( 47 | name: "WindowsMetadata", 48 | dependencies: [ "DotNetMetadata" ], 49 | exclude: ["CMakeLists.txt"]), 50 | .testTarget( 51 | name: "WindowsMetadataTests", 52 | dependencies: [ "WindowsMetadata" ], 53 | path: "Tests/WindowsMetadata", 54 | linkerSettings: executableLinkerSettings), 55 | 56 | .target( 57 | name: "DotNetXMLDocs", 58 | exclude: ["CMakeLists.txt"]), 59 | .testTarget( 60 | name: "DotNetXMLDocsTests", 61 | dependencies: [ "DotNetXMLDocs" ], 62 | path: "Tests/DotNetXMLDocs", 63 | linkerSettings: executableLinkerSettings), 64 | 65 | .target( 66 | name: "DotNetXMLDocsFromMetadata", 67 | dependencies: [ "DotNetMetadata", "DotNetXMLDocs" ], 68 | exclude: ["CMakeLists.txt"]), 69 | ] 70 | ) 71 | -------------------------------------------------------------------------------- /SPMPreBuild.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding(PositionalBinding=$false)] 2 | param( 3 | [Parameter(Mandatory=$false)] 4 | [string] $BuildDir 5 | ) 6 | 7 | if (!$BuildDir) { 8 | switch ($env:PROCESSOR_ARCHITECTURE) { 9 | "AMD64" { $TargetTripleArch = "x86_64" } 10 | "x86" { $TargetTripleArch = "i686" } 11 | default { throw "Unknown architecture: $env:PROCESSOR_ARCHITECTURE" } 12 | } 13 | 14 | $TargetTriple = "$TargetTripleArch-unknown-windows-msvc" 15 | $BuildDir = "$PSScriptRoot\.build\$TargetTriple\debug" 16 | } 17 | 18 | New-Item -ItemType Directory -Path $BuildDir -Force | Out-Null 19 | & $PSScriptRoot\WindowsMetadataCoreLibrary\Assemble.ps1 -OutputPath $BuildDir\mscorlib.winmd -------------------------------------------------------------------------------- /Sources/CInterop/include/module.modulemap: -------------------------------------------------------------------------------- 1 | module CInterop { 2 | header "PE.h" 3 | header "Metadata.h" 4 | export * 5 | } -------------------------------------------------------------------------------- /Sources/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(DotNetMetadataCInterop) 2 | add_subdirectory(DotNetMetadataFormat) 3 | add_subdirectory(DotNetMetadata) 4 | add_subdirectory(DotNetXMLDocs) 5 | add_subdirectory(DotNetXMLDocsFromMetadata) 6 | add_subdirectory(WindowsMetadata) -------------------------------------------------------------------------------- /Sources/DotNetMetadata/AssemblyReference.swift: -------------------------------------------------------------------------------- 1 | import DotNetMetadataFormat 2 | 3 | public final class AssemblyReference { 4 | public private(set) weak var owner: Assembly! 5 | internal let tableRowIndex: TableRowIndex // In AssemblyRef table 6 | 7 | init(owner: Assembly, tableRowIndex: TableRowIndex) { 8 | self.owner = owner 9 | self.tableRowIndex = tableRowIndex 10 | } 11 | 12 | internal var moduleFile: ModuleFile { owner.moduleFile } 13 | internal var tableRow: AssemblyRefTable.Row { moduleFile.assemblyRefTable[tableRowIndex] } 14 | 15 | public var name: String { moduleFile.resolve(tableRow.name) } 16 | public var version: FourPartVersion { tableRow.version } 17 | 18 | public var culture: String? { 19 | let culture = moduleFile.resolve(tableRow.culture) 20 | return culture.isEmpty ? nil : culture 21 | } 22 | 23 | public var publicKey: AssemblyPublicKey? { 24 | let tableRow = tableRow 25 | let bytes = Array(moduleFile.resolve(tableRow.publicKeyOrToken)) 26 | return bytes.isEmpty ? nil : .from(bytes: bytes, isToken: tableRow.flags.contains(.publicKey)) 27 | } 28 | 29 | public var identity: AssemblyIdentity { 30 | AssemblyIdentity(name: name, version: version, culture: culture, publicKey: publicKey) 31 | } 32 | 33 | public var flags: AssemblyFlags { tableRow.flags } 34 | public var hashValue: [UInt8] { Array(moduleFile.resolve(tableRow.hashValue)) } 35 | 36 | private var cachedAttributes: [Attribute]? 37 | public var attributes: [Attribute] { 38 | cachedAttributes.lazyInit { 39 | owner.getAttributes(owner: .init(tag: .interfaceImpl, rowIndex: tableRowIndex)) 40 | } 41 | } 42 | 43 | private var cachedTarget: Assembly? = nil 44 | public func resolve() throws -> Assembly { 45 | if let cachedTarget { return cachedTarget } 46 | let target = try owner.context.load(identity: identity) 47 | self.cachedTarget = target 48 | return target 49 | } 50 | 51 | internal func breakReferenceCycles() { 52 | cachedAttributes = nil 53 | cachedTarget = nil 54 | } 55 | } -------------------------------------------------------------------------------- /Sources/DotNetMetadata/Attributable.swift: -------------------------------------------------------------------------------- 1 | public protocol Attributable: AnyObject { 2 | var attributeTarget: AttributeTargets { get } 3 | var attributes: [Attribute] { get } 4 | } 5 | 6 | extension Attributable { 7 | public func findAttribute(namespace: String?, name: String) throws -> Attribute? { 8 | try attributes.first { try $0.type.namespace == namespace && $0.type.name == name } 9 | } 10 | 11 | public func hasAttribute(namespace: String?, name: String) throws -> Bool { 12 | try findAttribute(namespace: namespace, name: name) != nil 13 | } 14 | 15 | public func hasAttribute(_ type: T.Type) throws -> Bool { 16 | try attributes.contains { try $0.hasType(T.self) } 17 | } 18 | 19 | public func findAttribute(_ type: T.Type) throws -> T? { 20 | guard let attribute = try attributes.first(where: { try $0.hasType(T.self) }) else { return nil } 21 | return try attribute.decode(as: T.self) 22 | } 23 | 24 | public func getAttributes(_ type: T.Type) throws -> [T] { 25 | var result = [T]() 26 | for attribute in attributes { 27 | guard try attribute.hasType(T.self) else { continue } 28 | result.append(try attribute.decode(as: T.self)) 29 | } 30 | return result 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Sources/DotNetMetadata/AttributeTargets.swift: -------------------------------------------------------------------------------- 1 | public struct AttributeTargets: Hashable, OptionSet { 2 | public var rawValue: Int32 // Despite [Flags], the underlying type is Int32 in metadata 3 | 4 | public init(rawValue: Int32) { 5 | self.rawValue = rawValue 6 | } 7 | 8 | public static func | (left: Self, right: Self) -> Self { 9 | Self(rawValue: left.rawValue | right.rawValue) 10 | } 11 | 12 | public static func & (left: Self, right: Self) -> Self { 13 | Self(rawValue: left.rawValue & right.rawValue) 14 | } 15 | 16 | public static let none = Self() 17 | 18 | /// Attribute can be applied to an assembly. 19 | public static let assembly = Self(rawValue: 1) 20 | /// Attribute can be applied to a module. 21 | public static let module = Self(rawValue: 2) 22 | /// Attribute can be applied to a class. 23 | public static let `class` = Self(rawValue: 4) 24 | /// Attribute can be applied to a structure; that is, a value type. 25 | public static let `struct` = Self(rawValue: 8) 26 | /// Attribute can be applied to an enumeration. 27 | public static let `enum` = Self(rawValue: 0x10) 28 | /// Attribute can be applied to a constructor. 29 | public static let constructor = Self(rawValue: 0x20) 30 | /// Attribute can be applied to a method. 31 | public static let method = Self(rawValue: 0x40) 32 | /// Attribute can be applied to a property. 33 | public static let property = Self(rawValue: 0x80) 34 | /// Attribute can be applied to a field. 35 | public static let field = Self(rawValue: 0x100) 36 | /// Attribute can be applied to an event. 37 | public static let event = Self(rawValue: 0x200) 38 | /// Attribute can be applied to an interface. 39 | public static let interface = Self(rawValue: 0x400) 40 | /// Attribute can be applied to a parameter. 41 | public static let parameter = Self(rawValue: 0x800) 42 | /// Attribute can be applied to a delegate. 43 | public static let delegate = Self(rawValue: 0x1000) 44 | /// Attribute can be applied to a return value. 45 | public static let returnValue = Self(rawValue: 0x2000) 46 | /// Attribute can be applied to a generic parameter. 47 | public static let genericParameter = Self(rawValue: 0x4000) 48 | /// Attribute can be applied to any application element. 49 | public static let all = Self(rawValue: 0x7FFF) 50 | 51 | public static let interfaceImpl = Self() // Not defined in the base class library 52 | 53 | public static let allTypes: Self = .class | .struct | .interface | .enum | .delegate 54 | public static let allMembers: Self = .field | .method | .property | .event | .constructor 55 | } -------------------------------------------------------------------------------- /Sources/DotNetMetadata/AttributeType.swift: -------------------------------------------------------------------------------- 1 | public protocol AttributeType { 2 | static var namespace: String? { get } 3 | static var name: String { get } 4 | static var validOn: AttributeTargets { get } 5 | static var allowMultiple: Bool { get } 6 | static var inherited: Bool { get } 7 | 8 | static func decode(_ attribute: Attribute) throws -> Self 9 | } 10 | 11 | extension Attribute { 12 | public func hasType(_ type: T.Type) throws -> Bool { 13 | let actualType = try self.type 14 | return actualType.namespace == type.namespace && actualType.name == type.name 15 | } 16 | 17 | public func decode(as type: T.Type) throws -> T { 18 | try type.decode(self) 19 | } 20 | } -------------------------------------------------------------------------------- /Sources/DotNetMetadata/Attributes/AttributeUsageAttribute.swift: -------------------------------------------------------------------------------- 1 | public struct AttributeUsageAttribute: AttributeType { 2 | public var validOn: AttributeTargets 3 | public var allowMultiple: Bool 4 | public var inherited: Bool 5 | 6 | public init(validOn: AttributeTargets, allowMultiple: Bool, inherited: Bool) { 7 | self.validOn = validOn 8 | self.allowMultiple = allowMultiple 9 | self.inherited = inherited 10 | } 11 | 12 | public static var namespace: String? { "System" } 13 | public static var name: String { "AttributeUsageAttribute" } 14 | public static var validOn: AttributeTargets { .class } 15 | public static var allowMultiple: Bool { false } 16 | public static var inherited: Bool { true } 17 | 18 | public static func decode(_ attribute: Attribute) throws -> AttributeUsageAttribute { 19 | let arguments = try attribute.arguments 20 | guard arguments.count == 1, 21 | case .constant(let validOnConstant) = arguments[1], 22 | case .int32(let validOnValue) = validOnConstant else { throw InvalidMetadataError.attributeArguments } 23 | 24 | var inherited: Bool? = nil 25 | var allowMultiple: Bool? = nil 26 | for namedArg in try attribute.namedArguments { 27 | guard case .property(let property) = namedArg.target, 28 | case .constant(let valueConstant) = namedArg.value, 29 | case .boolean(let value) = valueConstant else { 30 | throw InvalidMetadataError.attributeArguments 31 | } 32 | 33 | switch property.name { 34 | case "Inherited": inherited = value 35 | case "AllowMultiple": allowMultiple = value 36 | default: throw InvalidMetadataError.attributeArguments 37 | } 38 | } 39 | 40 | return AttributeUsageAttribute( 41 | validOn: AttributeTargets(rawValue: validOnValue), 42 | allowMultiple: allowMultiple ?? false, 43 | inherited: inherited ?? true) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Sources/DotNetMetadata/Attributes/ComImportAttribute.swift: -------------------------------------------------------------------------------- 1 | public struct ComImportAttribute: AttributeType { 2 | public static var namespace: String? { "System.Runtime.InteropServices" } 3 | public static var name: String { "ComImportAttribute" } 4 | public static var validOn: AttributeTargets { .class | .interface } 5 | public static var allowMultiple: Bool { false } 6 | public static var inherited: Bool { false } 7 | 8 | public static func decode(_ attribute: Attribute) throws -> Self { .init() } 9 | } 10 | -------------------------------------------------------------------------------- /Sources/DotNetMetadata/Attributes/ComVisibleAttribute.swift: -------------------------------------------------------------------------------- 1 | public struct ComVisibleAttribute: AttributeType { 2 | public var value: Bool 3 | 4 | public init(_ value: Bool) { 5 | self.value = value 6 | } 7 | 8 | public static var namespace: String? { "System.Runtime.InteropServices" } 9 | public static var name: String { "ComVisibleAttribute" } 10 | public static var validOn: AttributeTargets { .assembly | .allTypes | .method | .property | .field } 11 | public static var allowMultiple: Bool { false } 12 | public static var inherited: Bool { false } 13 | 14 | public static func decode(_ attribute: Attribute) throws -> Self { 15 | let arguments = try attribute.arguments 16 | guard arguments.count == 1, 17 | case .constant(let constant) = arguments[0], 18 | case .boolean(let value) = constant else { throw InvalidMetadataError.attributeArguments } 19 | 20 | return .init(value) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Sources/DotNetMetadata/Attributes/FlagsAttribute.swift: -------------------------------------------------------------------------------- 1 | /// Indicates that an enumeration can be treated as a bit field; that is, a set of flags. 2 | public struct FlagsAttribute: AttributeType { 3 | public static var namespace: String? { "System" } 4 | public static var name: String { "FlagsAttribute" } 5 | public static var validOn: AttributeTargets { .enum } 6 | public static var allowMultiple: Bool { false } 7 | public static var inherited: Bool { false } 8 | 9 | public static func decode(_ attribute: Attribute) throws -> Self { .init() } 10 | } 11 | -------------------------------------------------------------------------------- /Sources/DotNetMetadata/Attributes/GuidAttribute.swift: -------------------------------------------------------------------------------- 1 | public struct GuidAttribute: AttributeType { 2 | public var value: String 3 | 4 | public init(_ value: String) { 5 | self.value = value 6 | } 7 | 8 | public static var namespace: String? { "System.Runtime.InteropServices" } 9 | public static var name: String { "GuidAttribute" } 10 | public static var validOn: AttributeTargets { .assembly | .allTypes } 11 | public static var allowMultiple: Bool { false } 12 | public static var inherited: Bool { true } 13 | 14 | public static func decode(_ attribute: Attribute) throws -> GuidAttribute { 15 | let arguments = try attribute.arguments 16 | guard arguments.count == 1, 17 | case .constant(let constant) = arguments[0], 18 | case .string(let value) = constant else { throw InvalidMetadataError.attributeArguments } 19 | return .init(value) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Sources/DotNetMetadata/Attributes/ObsoleteAttribute.swift: -------------------------------------------------------------------------------- 1 | public struct ObsoleteAttribute: AttributeType { 2 | public var message: String? = nil 3 | public var isError: Bool = false 4 | public var diagnosticId: String? = nil 5 | public var urlFormat: String? = nil 6 | 7 | public init() {} 8 | 9 | public init(message: String?, isError: Bool, diagnosticId: String? = nil, urlFormat: String? = nil) { 10 | self.message = message 11 | self.isError = isError 12 | self.diagnosticId = diagnosticId 13 | self.urlFormat = urlFormat 14 | } 15 | 16 | public static var namespace: String? { "System" } 17 | public static var name: String { "ObsoleteAttribute" } 18 | public static var validOn: AttributeTargets { .allTypes | .allMembers } 19 | public static var allowMultiple: Bool { false } 20 | public static var inherited: Bool { false } 21 | 22 | public static func decode(_ attribute: Attribute) throws -> ObsoleteAttribute { 23 | let arguments = try attribute.arguments 24 | 25 | guard arguments.count <= 2 else { throw InvalidMetadataError.attributeArguments } 26 | 27 | var result = ObsoleteAttribute() 28 | if arguments.count >= 1 { 29 | guard case .constant(let messageConstant) = arguments[0], 30 | case .string(let message) = messageConstant else { throw InvalidMetadataError.attributeArguments } 31 | result.message = message 32 | } 33 | if arguments.count == 2 { 34 | guard case .constant(let isErrorConstant) = arguments[1], 35 | case .boolean(let isError) = isErrorConstant else { throw InvalidMetadataError.attributeArguments } 36 | result.isError = isError 37 | } 38 | 39 | for namedArg in try attribute.namedArguments { 40 | guard case .property(let property) = namedArg.target, 41 | case .constant(let valueConstant) = namedArg.value, 42 | case .string(let value) = valueConstant else { 43 | throw InvalidMetadataError.attributeArguments 44 | } 45 | 46 | switch property.name { 47 | case "DiagnosticId": result.diagnosticId = value 48 | case "UrlFormat": result.urlFormat = value 49 | default: throw InvalidMetadataError.attributeArguments 50 | } 51 | } 52 | 53 | return result 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Sources/DotNetMetadata/BaseInterface.swift: -------------------------------------------------------------------------------- 1 | import DotNetMetadataFormat 2 | 3 | public final class BaseInterface: Attributable { 4 | public private(set) weak var inheritingType: TypeDefinition! 5 | internal let tableRowIndex: TableRowIndex // In InterfaceImpl table 6 | 7 | init(inheritingType: TypeDefinition, tableRowIndex: TableRowIndex) { 8 | self.inheritingType = inheritingType 9 | self.tableRowIndex = tableRowIndex 10 | } 11 | 12 | public var assembly: Assembly { inheritingType.assembly } 13 | internal var moduleFile: ModuleFile { inheritingType.moduleFile } 14 | private var tableRow: InterfaceImplTable.Row { moduleFile.interfaceImplTable[tableRowIndex] } 15 | 16 | private var cachedInterface: BoundInterface? 17 | public var interface: BoundInterface { get throws { 18 | try cachedInterface.lazyInit { 19 | guard let boundType = try assembly.resolveTypeDefOrRefToBoundType(tableRow.interface, typeContext: inheritingType), 20 | let interfaceDefinition = boundType.definition as? InterfaceDefinition else { throw InvalidFormatError.tableConstraint } 21 | return interfaceDefinition.bind(genericArgs: boundType.genericArgs) 22 | } 23 | } } 24 | 25 | public var attributeTarget: AttributeTargets { .interfaceImpl } 26 | private var cachedAttributes: [Attribute]? 27 | public var attributes: [Attribute] { 28 | cachedAttributes.lazyInit { 29 | assembly.getAttributes(owner: .init(tag: .interfaceImpl, rowIndex: tableRowIndex)) 30 | } 31 | } 32 | 33 | internal func breakReferenceCycles() { 34 | if let attributes = cachedAttributes { 35 | for attribute in attributes { 36 | attribute.breakReferenceCycles() 37 | } 38 | } 39 | 40 | cachedInterface = nil 41 | } 42 | } -------------------------------------------------------------------------------- /Sources/DotNetMetadata/BoundMethod.swift: -------------------------------------------------------------------------------- 1 | /// A method with all generic parameters bound to type arguments. 2 | public struct BoundMethod: Hashable { 3 | public let definition: Method 4 | public let genericArgs: [TypeNode] 5 | 6 | public init(_ definition: Method, genericArgs: [TypeNode]) { 7 | precondition(definition.genericParams.count == genericArgs.count) 8 | self.definition = definition 9 | self.genericArgs = genericArgs 10 | } 11 | } 12 | 13 | extension Method { 14 | public func bind(genericArgs: [TypeNode] = []) -> BoundMethod { 15 | .init(self, genericArgs: genericArgs) 16 | } 17 | } -------------------------------------------------------------------------------- /Sources/DotNetMetadata/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | file(GLOB_RECURSE SOURCES "*.swift") 2 | add_library(DotNetMetadata STATIC ${SOURCES}) 3 | target_link_libraries(DotNetMetadata PUBLIC DotNetMetadataFormat) -------------------------------------------------------------------------------- /Sources/DotNetMetadata/Collection+single.swift: -------------------------------------------------------------------------------- 1 | extension Collection { 2 | func singleIndex(where: (Element) throws -> Bool) rethrows -> Index? { 3 | var foundIndex: Index? = nil 4 | for i in indices { 5 | if try `where`(self[i]) { 6 | guard foundIndex == nil else { return nil } 7 | foundIndex = i 8 | } 9 | } 10 | return foundIndex 11 | } 12 | 13 | func single(where: (Element) throws -> Bool) rethrows -> Element? { 14 | guard let index = try singleIndex(where: `where`) else { return nil } 15 | return self[index] 16 | } 17 | } -------------------------------------------------------------------------------- /Sources/DotNetMetadata/ExportedType.swift: -------------------------------------------------------------------------------- 1 | import DotNetMetadataFormat 2 | 3 | /// A type that is exported but whose definition is in another assembly. 4 | public final class ExportedType { 5 | public private(set) weak var assembly: Assembly! 6 | internal let tableRowIndex: TableRowIndex // In ExportedType table 7 | private var tableRow: ExportedTypeTable.Row { moduleFile.exportedTypeTable[tableRowIndex] } 8 | 9 | internal init(assembly: Assembly, tableRowIndex: TableRowIndex) { 10 | self.assembly = assembly 11 | self.tableRowIndex = tableRowIndex 12 | } 13 | 14 | internal var moduleFile: ModuleFile { assembly.moduleFile } 15 | public var context: AssemblyLoadContext { assembly.context } 16 | 17 | public var name: String { moduleFile.resolve(tableRow.typeName) } 18 | 19 | public var namespace: String? { 20 | let tableRow = tableRow 21 | // Normally, no namespace is represented by a zero string heap index 22 | guard tableRow.typeNamespace.value != 0 else { return nil } 23 | let value = moduleFile.resolve(tableRow.typeNamespace) 24 | return value.isEmpty ? nil : value 25 | } 26 | 27 | public private(set) lazy var fullName: String = { 28 | // TODO: Support nested exported types 29 | TypeName.toFullName(namespace: namespace, shortName: name) 30 | }() 31 | 32 | private var cachedDefinition: TypeDefinition? 33 | public var definition: TypeDefinition { get throws { 34 | try cachedDefinition.lazyInit { 35 | let implementationCodedIndex = tableRow.implementation 36 | guard let implementationRowIndex = implementationCodedIndex.rowIndex else { 37 | throw DotNetMetadataFormat.InvalidFormatError.tableConstraint 38 | } 39 | switch try implementationCodedIndex.tag { 40 | case .assemblyRef: 41 | let assemblyReference = try self.assembly.resolveAssemblyRef(rowIndex: implementationRowIndex) 42 | // TODO: Optimize using the typeDefId field 43 | // TODO: Support recursive exported types 44 | return try context.resolveType( 45 | assembly: assemblyReference.identity, 46 | assemblyFlags: assemblyReference.flags, 47 | name: TypeName(namespace: namespace, shortName: name)) 48 | default: 49 | fatalError("Not implemented: \(#function)") 50 | } 51 | } 52 | } } 53 | 54 | public func breakReferenceCycles() { 55 | cachedDefinition = nil 56 | } 57 | } -------------------------------------------------------------------------------- /Sources/DotNetMetadata/Field.swift: -------------------------------------------------------------------------------- 1 | import DotNetMetadataFormat 2 | 3 | public final class Field: Member { 4 | internal let tableRowIndex: TableRowIndex // In Field table 5 | private var tableRow: FieldTable.Row { moduleFile.fieldTable[tableRowIndex] } 6 | private var flags: FieldAttributes { tableRow.flags } 7 | 8 | init(definingType: TypeDefinition, tableRowIndex: TableRowIndex) { 9 | self.tableRowIndex = tableRowIndex 10 | super.init(definingType: definingType) 11 | } 12 | 13 | public override var metadataToken: MetadataToken { .init(tableID: .field, rowIndex: tableRowIndex) } 14 | internal override var nameStringHeapOffset: StringHeap.Offset { tableRow.name } 15 | public override var nameKind: NameKind { flags.nameKind } 16 | public override var isStatic: Bool { flags.contains(.`static`) } 17 | public override var attributeTarget: AttributeTargets { .field } 18 | internal override var attributesKeyTag: CodedIndices.HasCustomAttribute.Tag { .field } 19 | public var visibility: Visibility { flags.visibility } 20 | public var isPublic: Bool { visibility == .public } 21 | public var isInitOnly: Bool { flags.contains(.initOnly) } 22 | public var isLiteral: Bool { flags.contains(.literal) } 23 | 24 | public private(set) lazy var explicitOffset: Int? = { () -> Int? in 25 | guard let fieldLayoutRowIndex = moduleFile.fieldLayoutTable.findAny( 26 | primaryKey: .init(index: tableRowIndex)) 27 | else { return nil } 28 | 29 | let fieldLayoutRow = moduleFile.fieldLayoutTable[fieldLayoutRowIndex] 30 | return Int(fieldLayoutRow.offset) 31 | }() 32 | 33 | private var cachedSignature: FieldSig? 34 | public var signature: FieldSig { get throws { 35 | try cachedSignature.lazyInit { 36 | let signatureBlob = moduleFile.resolve(tableRow.signature) 37 | return try FieldSig(blob: signatureBlob) 38 | } 39 | } } 40 | 41 | private var cachedType: TypeNode? 42 | public var type: TypeNode { get throws { 43 | try cachedType.lazyInit { 44 | try assembly.resolveTypeSig(signature.type, typeContext: definingType) 45 | } 46 | } } 47 | 48 | private var cachedLiteralValue: Constant?? 49 | public var literalValue: Constant? { get throws { 50 | try cachedLiteralValue.lazyInit { 51 | guard tableRow.flags.contains(.literal) else { return nil as Constant? } 52 | return try Constant(moduleFile: moduleFile, owner: .init(tag: .field, rowIndex: tableRowIndex)) 53 | } 54 | } } 55 | 56 | internal override func breakReferenceCycles() { 57 | // cachedSignature is POD 58 | cachedType = nil 59 | // cachedLiteralValue is POD 60 | 61 | super.breakReferenceCycles() 62 | } 63 | } -------------------------------------------------------------------------------- /Sources/DotNetMetadata/InvalidMetadataError.swift: -------------------------------------------------------------------------------- 1 | public enum InvalidMetadataError: Error { 2 | case attributeArguments 3 | } -------------------------------------------------------------------------------- /Sources/DotNetMetadata/Member.swift: -------------------------------------------------------------------------------- 1 | import DotNetMetadataFormat 2 | 3 | public class Member: Attributable { 4 | public private(set) weak var definingType: TypeDefinition! 5 | 6 | internal init(definingType: TypeDefinition) { 7 | self.definingType = definingType 8 | } 9 | 10 | public var assembly: Assembly { definingType.assembly } 11 | internal var moduleFile: ModuleFile { definingType.moduleFile } 12 | public var context: AssemblyLoadContext { assembly.context } 13 | 14 | public var metadataToken: MetadataToken { fatalError() } // abstract 15 | 16 | internal var nameStringHeapOffset: StringHeap.Offset { fatalError() } // abstract 17 | private var _cachedName: String? 18 | public var name: String { 19 | if let name = _cachedName { return name } 20 | _cachedName = moduleFile.resolve(nameStringHeapOffset) 21 | return _cachedName! 22 | } 23 | 24 | public var nameKind: NameKind { fatalError() } // abstract 25 | public var isStatic: Bool { fatalError() } // abstract 26 | public var isInstance: Bool { !isStatic } 27 | 28 | public var attributeTarget: AttributeTargets { fatalError() } // abstract 29 | internal var attributesKeyTag: CodedIndices.HasCustomAttribute.Tag { fatalError() } // abstract 30 | 31 | private var cachedAttributes: [Attribute]? 32 | public var attributes: [Attribute] { 33 | cachedAttributes.lazyInit { 34 | assembly.getAttributes(owner: .init(tag: attributesKeyTag, rowIndex: metadataToken.rowIndex)) 35 | } 36 | } 37 | 38 | internal func breakReferenceCycles() { 39 | if let attributes = cachedAttributes { 40 | for attribute in attributes { 41 | attribute.breakReferenceCycles() 42 | } 43 | } 44 | } 45 | } 46 | 47 | extension Member: Hashable { 48 | public func hash(into hasher: inout Hasher) { hasher.combine(ObjectIdentifier(self)) } 49 | public static func == (lhs: Member, rhs: Member) -> Bool { lhs === rhs } 50 | } 51 | -------------------------------------------------------------------------------- /Sources/DotNetMetadata/ModuleFile+resolve.swift: -------------------------------------------------------------------------------- 1 | import DotNetMetadataFormat 2 | 3 | extension ModuleFile { 4 | func getTypeDefinitionKind(_ tableRow: TypeDefTable.Row) throws -> TypeDefinitionKind { 5 | if tableRow.flags.contains(.interface) { 6 | return .interface 7 | } 8 | 9 | // We must check the base type, but before doing so exclude special cases 10 | if resolve(tableRow.typeNamespace) == "System" { 11 | switch resolve(tableRow.typeName) { 12 | case "Object": return .class 13 | case "Enum": return .class 14 | case "MulticastDelegate": return .class 15 | default: break 16 | } 17 | } 18 | 19 | return try getTypeDefinitionFromBase(tableRow.extends) 20 | } 21 | 22 | private func getTypeDefinitionFromBase(_ extends: CodedIndices.TypeDefOrRef) throws -> TypeDefinitionKind { 23 | let systemTypeName: String 24 | guard let rowIndex = extends.rowIndex else { return .class } 25 | switch try extends.tag { 26 | case .typeDef: 27 | let typeDefRow = typeDefTable[rowIndex] 28 | guard resolve(typeDefRow.typeNamespace) == "System" else { return .class } 29 | systemTypeName = resolve(typeDefRow.typeName) 30 | case .typeRef: 31 | let typeRefRow = typeRefTable[rowIndex] 32 | guard resolve(typeRefRow.typeNamespace) == "System" else { return .class } 33 | systemTypeName = resolve(typeRefRow.typeName) 34 | case .typeSpec: 35 | // Assume no special base type can be referred through a typeSpec 36 | return .class 37 | } 38 | 39 | switch systemTypeName { 40 | case "ValueType": return .struct 41 | case "Enum": return .enum 42 | case "Delegate", "MulticastDelegate": return .delegate 43 | default: return .class 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /Sources/DotNetMetadata/Optional+lazyInit.swift: -------------------------------------------------------------------------------- 1 | extension Optional { 2 | internal func unwrapOrThrow(_ error: @autoclosure () -> E) throws -> Wrapped { 3 | guard let value = self else { throw error() } 4 | return value 5 | } 6 | 7 | internal mutating func lazyInit(_ lazyInit: () throws -> Wrapped) rethrows -> Wrapped { 8 | if let value = self { return value } 9 | let value = try lazyInit() 10 | self = value 11 | return value 12 | } 13 | } -------------------------------------------------------------------------------- /Sources/DotNetMetadata/Reexported.swift: -------------------------------------------------------------------------------- 1 | import DotNetMetadataFormat 2 | 3 | @_exported import struct DotNetMetadataFormat.ArrayShape 4 | @_exported import struct DotNetMetadataFormat.AssemblyIdentity 5 | @_exported import enum DotNetMetadataFormat.AssemblyPublicKey 6 | @_exported import enum DotNetMetadataFormat.Constant 7 | @_exported import struct DotNetMetadataFormat.FourPartVersion 8 | @_exported import enum DotNetMetadataFormat.IntegerSize 9 | @_exported import enum DotNetMetadataFormat.LayoutKind 10 | @_exported import enum DotNetMetadataFormat.NameKind 11 | @_exported import enum DotNetMetadataFormat.Visibility -------------------------------------------------------------------------------- /Sources/DotNetMetadata/SystemAssemblies.swift: -------------------------------------------------------------------------------- 1 | import class Foundation.ProcessInfo 2 | import WinSDK 3 | 4 | public enum SystemAssemblies { 5 | public enum DotNetFramework4 { 6 | public private(set) static var directoryPath: String? = { 7 | #"SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full"#.withCString(encodedAs: UTF16.self) { lpSubKey in 8 | "InstallPath".withCString(encodedAs: UTF16.self) { lpValue in 9 | var cbData: DWORD = 0 10 | guard RegGetValueW(HKEY_LOCAL_MACHINE, lpSubKey, lpValue, UInt32(RRF_RT_REG_SZ), nil, nil, &cbData) == ERROR_SUCCESS 11 | else { return nil } 12 | 13 | var vData: [WCHAR] = [WCHAR](repeating: 0, count: Int(cbData) / MemoryLayout.size) 14 | guard RegGetValueW(HKEY_LOCAL_MACHINE, lpSubKey, lpValue, UInt32(RRF_RT_REG_SZ), nil, &vData, &cbData) == ERROR_SUCCESS 15 | else { return nil } 16 | 17 | // https://learn.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-reggetvaluew 18 | // > If the data has the REG_SZ, REG_MULTI_SZ or REG_EXPAND_SZ type, 19 | // > this size includes any terminating null character or characters. 20 | while vData.last == 0 || vData.last == "\\".utf16.first { vData.removeLast() } 21 | 22 | return String(decoding: vData, as: Unicode.UTF16.self) 23 | } 24 | } 25 | }() 26 | 27 | public static var mscorlibPath: String? { directoryPath.flatMap { "\($0)\\mscorlib.dll" } } 28 | } 29 | 30 | public enum WinMetadata { 31 | public private(set) static var directoryPath: String? = { 32 | var path = [WCHAR](repeating: 0, count: Int(MAX_PATH + 1)) 33 | guard WinSDK.SHGetFolderPathW(nil, CSIDL_SYSTEM, nil, DWORD(SHGFP_TYPE_CURRENT.rawValue), &path) >= 0 else { return nil } 34 | return String(decodingCString: path, as: UTF16.self) + "\\WinMetadata" 35 | }() 36 | 37 | public static var windowsFoundationPath: String? { directoryPath.flatMap { "\($0)\\Windows.Foundation.winmd" } } 38 | } 39 | } -------------------------------------------------------------------------------- /Sources/DotNetMetadata/TableHelpers.swift: -------------------------------------------------------------------------------- 1 | import DotNetMetadataFormat 2 | 3 | func getChildRowRange( 4 | parent: Table, 5 | parentRowIndex: TableRowIndex, 6 | childTable: Table, 7 | childSelector: (Parent) -> Table.RowRef) -> Range 8 | where Parent : TableRow, Child: TableRow { 9 | guard let firstChildIndex = childSelector(parent[parentRowIndex]).index else { 10 | return childTable.endIndex ..< childTable.endIndex 11 | } 12 | 13 | let nextParentRowIndex = parent.index(after: parentRowIndex) 14 | if nextParentRowIndex == parent.endIndex { 15 | return firstChildIndex ..< childTable.endIndex 16 | } 17 | else { 18 | let endChildIndex = childSelector(parent[nextParentRowIndex]).index! 19 | return firstChildIndex ..< endChildIndex 20 | } 21 | } -------------------------------------------------------------------------------- /Sources/DotNetMetadata/TypeDefinitionKind.swift: -------------------------------------------------------------------------------- 1 | public enum TypeDefinitionKind: Hashable { 2 | case `class` 3 | case interface 4 | case delegate 5 | case `struct` 6 | case `enum` 7 | } 8 | 9 | extension TypeDefinitionKind { 10 | public var isReferenceType: Bool { 11 | switch self { 12 | case .class, .interface, .delegate: return true 13 | case .struct, .enum: return false 14 | } 15 | } 16 | 17 | public var isValueType: Bool { !isReferenceType } 18 | } -------------------------------------------------------------------------------- /Sources/DotNetMetadata/TypeLayout.swift: -------------------------------------------------------------------------------- 1 | import DotNetMetadataFormat 2 | 3 | public enum TypeLayout: Hashable { 4 | case auto 5 | case sequential(pack: Int?, minSize: Int) 6 | case explicit(minSize: Int) 7 | } -------------------------------------------------------------------------------- /Sources/DotNetMetadata/TypeName.swift: -------------------------------------------------------------------------------- 1 | /// Represents the name of a .NET Type, including its namespace and nested type names. 2 | public struct TypeName: Hashable, CustomStringConvertible { 3 | public static let namespaceSeparator: Character = "." 4 | public static let nestedTypeSeparator: Character = "/" 5 | public static let genericAritySeparator: Character = "`" 6 | 7 | public let namespace: String? 8 | public let outermostShortName: String 9 | public let nestedNames: [String] 10 | 11 | public init(namespace: String?, outermostShortName: String, nestedNames: [String]) { 12 | assert(!outermostShortName.contains(Self.namespaceSeparator)) 13 | assert(!outermostShortName.contains(Self.nestedTypeSeparator)) 14 | self.namespace = namespace 15 | self.outermostShortName = outermostShortName 16 | self.nestedNames = nestedNames 17 | } 18 | 19 | public init(namespace: String?, shortName: String) { 20 | self.init(namespace: namespace, outermostShortName: shortName, nestedNames: []) 21 | } 22 | 23 | public init(fullName: String) { 24 | let namespaceEnd = fullName.lastIndex(of: Self.namespaceSeparator) 25 | let shortNamesStart = namespaceEnd.map { fullName.index(after: $0) } ?? fullName.startIndex 26 | let shortNamesPart = fullName[shortNamesStart...] 27 | if let outermostShortNameEnd = shortNamesPart.firstIndex(of: Self.nestedTypeSeparator) { 28 | let nestedNamesPart = shortNamesPart[shortNamesPart.index(after: outermostShortNameEnd)...] 29 | self.init( 30 | namespace: namespaceEnd.map { String(fullName[...$0]) }, 31 | outermostShortName: String(shortNamesPart[.. String { 60 | if let namespace { return "\(namespace)\(namespaceSeparator)\(shortName)" } 61 | else { return shortName } 62 | } 63 | 64 | public static func toFullName(namespace: String?, outermostShortName: String, nestedNames: [String]) -> String { 65 | Self(namespace: namespace, outermostShortName: outermostShortName, nestedNames: nestedNames).fullName 66 | } 67 | 68 | public static func trimGenericArity(_ name: String) -> String { 69 | guard let genericAritySeparatorIndex = name.lastIndex(of: genericAritySeparator) else { return name } 70 | return String(name[.. 5 | 6 | typedef struct { 7 | uint32_t Signature; 8 | uint16_t MajorVersion, MinorVersion; 9 | uint32_t Reserved; 10 | uint32_t Length; 11 | // Null-padded version string of the given length follows 12 | } MetadataRoot_BeforeVersion; 13 | 14 | typedef struct { 15 | uint16_t Flags; 16 | uint16_t Streams; 17 | // Stream header array follows 18 | } MetadataRoot_AfterVersion; 19 | 20 | typedef struct { 21 | uint32_t Offset; 22 | uint32_t Size; 23 | // Null-terminated name string follows 24 | } MetadataStreamHeader; 25 | 26 | /// Represents the "#~" stream, as defined in ECMA 335 II.24.2.6. 27 | typedef struct { 28 | uint32_t Reserved0; // Always 0 29 | uint8_t MajorVersion, MinorVersion; 30 | uint8_t HeapSizes; 31 | uint8_t Reserved1; // Always 1 32 | uint64_t Valid; 33 | uint64_t Sorted; 34 | // Row counts per table follow 35 | // Metadata tables follow 36 | } MetadataTablesStreamHeader; 37 | 38 | typedef struct { 39 | uint32_t Offset; 40 | uint32_t RowCount; 41 | uint32_t BytesPerRow; 42 | } MetadataTableInfo; 43 | 44 | #endif -------------------------------------------------------------------------------- /Sources/DotNetMetadataCInterop/include/module.modulemap: -------------------------------------------------------------------------------- 1 | module DotNetMetadataCInterop { 2 | header "PE.h" 3 | header "Metadata.h" 4 | export * 5 | } -------------------------------------------------------------------------------- /Sources/DotNetMetadataCInterop/shim.c: -------------------------------------------------------------------------------- 1 | // Dummy export to make sure the C interop shim is included in the binary 2 | extern int _; -------------------------------------------------------------------------------- /Sources/DotNetMetadataFormat/ArrayShape.swift: -------------------------------------------------------------------------------- 1 | /// Represents static type information about the shape of a single- or multi-dimensional array. 2 | public struct ArrayShape: Hashable, CustomStringConvertible { 3 | /// Represents one dimension of the array shape, with a lower bound and an optional size. 4 | /// Though it is not obvious in ECMA-335, the lower bound is always known statically. 5 | public struct Dimension: Hashable, CustomStringConvertible { 6 | public static var zeroBased: Dimension { Dimension(lowerBound: 0) } 7 | 8 | public var lowerBound: Int32 9 | public var size: UInt32? 10 | 11 | public init(lowerBound: Int32) { 12 | self.lowerBound = lowerBound 13 | self.size = nil 14 | } 15 | 16 | public init(lowerBound: Int32, size: UInt32?) { 17 | self.lowerBound = lowerBound 18 | self.size = size 19 | } 20 | 21 | public init(lowerBound: Int32, upperBound: Int32?) { 22 | self.lowerBound = lowerBound 23 | if let upperBound { 24 | precondition(upperBound >= lowerBound) 25 | self.size = UInt32(upperBound - lowerBound + 1) 26 | } else { 27 | self.size = nil 28 | } 29 | } 30 | 31 | public var upperBound: Int32? { 32 | guard let size else { return nil } 33 | return lowerBound + Int32(size) - 1 34 | } 35 | 36 | public var description: String { 37 | guard let upperBound else { return "\(lowerBound)..." } 38 | return "\(lowerBound)...\(upperBound)" 39 | } 40 | } 41 | 42 | /// Gets the dimensions 43 | public var dimensions: [Dimension] { 44 | willSet { 45 | precondition(newValue.count > 0) 46 | } 47 | } 48 | 49 | public init(dimensions: [Dimension]) { 50 | precondition(dimensions.count > 0) 51 | self.dimensions = dimensions 52 | } 53 | 54 | public init(_ sig: ArrayShapeSig) { 55 | precondition(sig.rank > 0) 56 | if sig.isVector { 57 | // Reuse the allocated dimensions array. 58 | self.dimensions = Self.vector.dimensions 59 | return 60 | } 61 | 62 | var dimensions: [Dimension] = [] 63 | for i in 0.. 0 { text += "," } 78 | if dimension != .zeroBased { text += dimension.description } 79 | } 80 | text += "]" 81 | return text 82 | } 83 | 84 | public static let vector: ArrayShape = ArrayShape(dimensions: [.zeroBased]) 85 | } 86 | -------------------------------------------------------------------------------- /Sources/DotNetMetadataFormat/AssemblyEnums.swift: -------------------------------------------------------------------------------- 1 | public struct AssemblyFlags: OptionSet { 2 | public let rawValue: UInt32 3 | public init(rawValue: UInt32) { 4 | self.rawValue = rawValue 5 | } 6 | 7 | public static let publicKey = Self(rawValue: 0x1) 8 | public static let sideBySideCompatible = Self(rawValue: 0x2) 9 | public static let reserved = Self(rawValue: 0x30) 10 | public static let retargetable = Self(rawValue: 0x100) 11 | public static let windowsRuntime = Self(rawValue: 0x200) 12 | public static let disableJITcompileOptimizer = Self(rawValue: 0x4000) 13 | public static let enableJITCompileTracking = Self(rawValue: 0x8000) 14 | } 15 | 16 | public struct AssemblyHashAlgorithm: Hashable, Equatable, RawRepresentable { 17 | public typealias RawValue = UInt32 18 | 19 | public let rawValue: RawValue 20 | 21 | public init?(rawValue: RawValue) { self.rawValue = rawValue } 22 | public init(_ rawValue: RawValue) { self.rawValue = rawValue } 23 | 24 | public static let none = Self(0) 25 | public static let reserved_MD5 = Self(0x8003) 26 | public static let sha1 = Self(0x8004) 27 | } -------------------------------------------------------------------------------- /Sources/DotNetMetadataFormat/AssemblyPublicKey.swift: -------------------------------------------------------------------------------- 1 | public enum AssemblyPublicKey: Hashable { 2 | case full([UInt8]) // Full strong name key (.snk) file 3 | case token([UInt8]) // Last 8 bytes of SHA-1 of full key 4 | } 5 | 6 | extension AssemblyPublicKey { 7 | public static func from(bytes: [UInt8], isToken: Bool) -> AssemblyPublicKey { 8 | return isToken ? .token(bytes) : .full(bytes) 9 | } 10 | } -------------------------------------------------------------------------------- /Sources/DotNetMetadataFormat/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | file(GLOB_RECURSE SOURCES "*.swift") 2 | add_library(DotNetMetadataFormat STATIC ${SOURCES}) 3 | target_link_libraries(DotNetMetadataFormat PUBLIC DotNetMetadataCInterop) -------------------------------------------------------------------------------- /Sources/DotNetMetadataFormat/CodedIndex.swift: -------------------------------------------------------------------------------- 1 | /// Represent a coded index, i.e. a row index into one of several metadata tables based on a tag value. 2 | /// Can also represent a null index into a specific metadata table. 3 | /// See ECMA-335 §II.24.2.6: "Coded indices". 4 | public struct CodedIndex: Hashable, Comparable { 5 | public typealias Tag = Tag 6 | 7 | public let value: UInt32 8 | 9 | public init(value: UInt32) { 10 | self.value = value 11 | } 12 | 13 | public init(tag: Tag, rowIndex: TableRowIndex?) { 14 | self.init(value: (rowIndex.oneBased << Tag.bitCount) | UInt32(tag.rawValue)) 15 | } 16 | 17 | public static func null(tag: Tag) -> CodedIndex { .init(tag: tag, rowIndex: nil) } 18 | 19 | public var rowIndex: TableRowIndex? { TableRowIndex(oneBased: value >> Tag.bitCount) } 20 | public var isNull: Bool { rowIndex == nil } 21 | 22 | public var tag: Tag { 23 | get throws { 24 | try Tag(value: UInt8(value & Tag.bitMask)) 25 | } 26 | } 27 | 28 | public var metadataToken: MetadataToken { 29 | get throws { 30 | let rawTag = Int(value & Tag.bitMask) 31 | guard rawTag < Tag.tables.count, let tableID = Tag.tables[rawTag] else { 32 | throw InvalidFormatError.tableConstraint 33 | } 34 | return MetadataToken(tableID: tableID, rowIndex: rowIndex) 35 | } 36 | } 37 | 38 | public static func < (lhs: CodedIndex, rhs: CodedIndex) -> Bool { 39 | lhs.value < rhs.value 40 | } 41 | } 42 | 43 | /// Protocol for an enum which identifies one of several metadata tables that a coded index can point to. 44 | public protocol CodedIndexTag: Hashable, RawRepresentable where RawValue == UInt8 { 45 | static var tables: [TableID?] { get } 46 | init(value: UInt8) throws 47 | } 48 | 49 | extension CodedIndexTag { 50 | // ECMA-335 §II.24.2.6: "The actual table is encoded into the low [N] bits of the number" 51 | public static var bitCount: Int { Int.bitWidth - (tables.count - 1).leadingZeroBitCount } 52 | public static var bitMask: UInt32 { (UInt32(1) << bitCount) - 1 } 53 | } -------------------------------------------------------------------------------- /Sources/DotNetMetadataFormat/Constant.swift: -------------------------------------------------------------------------------- 1 | public enum Constant: Hashable { 2 | case boolean(Bool) 3 | case char(UTF16.CodeUnit) 4 | case int8(Int8) 5 | case uint8(UInt8) 6 | case int16(Int16) 7 | case uint16(UInt16) 8 | case int32(Int32) 9 | case uint32(UInt32) 10 | case int64(Int64) 11 | case uint64(UInt64) 12 | case single(Float) 13 | case double(Double) 14 | case string(String) 15 | case null 16 | } 17 | 18 | extension Constant { 19 | public init(buffer: UnsafeRawBufferPointer, type: ConstantType) throws { 20 | switch (type, buffer.count) { 21 | case (.boolean, 1): self = .boolean(buffer.loadUnaligned(as: UInt8.self) != 0) 22 | case (.char, 2): self = .char(buffer.loadUnaligned(as: UTF16.CodeUnit.self)) 23 | case (.i1, 1): self = .int8(buffer.loadUnaligned(as: Int8.self)) 24 | case (.u1, 1): self = .uint8(buffer.loadUnaligned(as: UInt8.self)) 25 | case (.i2, 2): self = .int16(buffer.loadUnaligned(as: Int16.self)) 26 | case (.u2, 2): self = .uint16(buffer.loadUnaligned(as: UInt16.self)) 27 | case (.i4, 4): self = .int32(buffer.loadUnaligned(as: Int32.self)) 28 | case (.u4, 4): self = .uint32(buffer.loadUnaligned(as: UInt32.self)) 29 | case (.i8, 8): self = .int64(buffer.loadUnaligned(as: Int64.self)) 30 | case (.u8, 8): self = .uint64(buffer.loadUnaligned(as: UInt64.self)) 31 | case (.r4, 4): self = .single(buffer.loadUnaligned(as: Float.self)) 32 | case (.r8, 8): self = .double(buffer.loadUnaligned(as: Double.self)) 33 | case (.string, _): 34 | if let firstCharPointer = buffer.baseAddress?.assumingMemoryBound(to: UTF16.CodeUnit.self) { 35 | let charBuffer = UnsafeBufferPointer(start: firstCharPointer, count: buffer.count / 2) 36 | self = .string(String(decoding: charBuffer, as: UTF16.self)) 37 | } 38 | else { 39 | self = .string("") 40 | } 41 | case (.nullRef, 0): self = .null 42 | default: 43 | throw InvalidFormatError.signatureBlob 44 | } 45 | } 46 | } 47 | 48 | extension Constant { 49 | public init?(moduleFile: ModuleFile, owner: CodedIndices.HasConstant) throws { 50 | guard let rowIndex = moduleFile.constantTable.findAny(primaryKey: owner) else { 51 | return nil 52 | } 53 | 54 | let constantRow = moduleFile.constantTable[rowIndex] 55 | guard constantRow.type != .nullRef else { 56 | self = .null 57 | return 58 | } 59 | 60 | let blob = moduleFile.resolve(constantRow.value) 61 | self = try Constant(buffer: blob, type: constantRow.type) 62 | } 63 | } -------------------------------------------------------------------------------- /Sources/DotNetMetadataFormat/FourPartVersion.swift: -------------------------------------------------------------------------------- 1 | public struct FourPartVersion: Comparable, Hashable, CustomStringConvertible { 2 | public static let zero = FourPartVersion() 3 | public static let all255 = FourPartVersion(major: 255, minor: 255, buildNumber: 255, revisionNumber: 255) 4 | 5 | public var major: UInt16 6 | public var minor: UInt16 7 | public var buildNumber: UInt16 8 | public var revisionNumber: UInt16 9 | 10 | public var description: String { "\(major).\(minor).\(buildNumber).\(revisionNumber)" } 11 | 12 | public init() { 13 | self.major = 0 14 | self.minor = 0 15 | self.buildNumber = 0 16 | self.revisionNumber = 0 17 | } 18 | 19 | public init(major: UInt16, minor: UInt16, buildNumber: UInt16 = 0, revisionNumber: UInt16 = 0) { 20 | self.major = major 21 | self.minor = minor 22 | self.buildNumber = buildNumber 23 | self.revisionNumber = revisionNumber 24 | } 25 | 26 | public init?(parsing str: String) { 27 | let components = str.split(separator: ".") 28 | guard components.count == 4 else { return nil } 29 | guard let major = UInt16(components[0]), 30 | let minor = UInt16(components[1]), 31 | let buildNumber = UInt16(components[2]), 32 | let revisionNumber = UInt16(components[3]) else { return nil } 33 | self.major = major 34 | self.minor = minor 35 | self.buildNumber = buildNumber 36 | self.revisionNumber = revisionNumber 37 | } 38 | 39 | public static func < (lhs: FourPartVersion, rhs: FourPartVersion) -> Bool { 40 | if lhs.major != rhs.major { 41 | return lhs.major < rhs.major 42 | } else if lhs.minor != rhs.minor { 43 | return lhs.minor < rhs.minor 44 | } else if lhs.buildNumber != rhs.buildNumber { 45 | return lhs.buildNumber < rhs.buildNumber 46 | } else { 47 | return lhs.revisionNumber < rhs.revisionNumber 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /Sources/DotNetMetadataFormat/Heaps.swift: -------------------------------------------------------------------------------- 1 | import struct Foundation.UUID 2 | 3 | public protocol Heap { 4 | associatedtype Value 5 | 6 | func resolve(at: UInt32) -> Value 7 | } 8 | 9 | public class StringHeap: Heap { 10 | public typealias Value = String 11 | 12 | private let buffer: UnsafeRawBufferPointer 13 | private var cache: [UInt32: String] = [:] 14 | 15 | init(buffer: UnsafeRawBufferPointer) { 16 | self.buffer = buffer 17 | } 18 | 19 | public func resolve(at offset: UInt32) -> String { 20 | if let existing = cache[offset] { return existing } 21 | let result = String(bytes: buffer.sub(offset: Int(offset)).prefix { $0 != 0 }, encoding: .utf8)! 22 | cache[offset] = result 23 | return result 24 | } 25 | } 26 | 27 | public class GuidHeap: Heap { 28 | public typealias Value = UUID 29 | 30 | var buffer: UnsafeRawBufferPointer 31 | 32 | init(buffer: UnsafeRawBufferPointer) { 33 | self.buffer = buffer 34 | } 35 | 36 | public func resolve(at offset: UInt32) -> UUID { 37 | buffer.bindMemory(offset: Int(offset), to: UUID.self).pointee 38 | } 39 | } 40 | 41 | public class BlobHeap: Heap { 42 | public typealias Value = UnsafeRawBufferPointer 43 | 44 | var buffer: UnsafeRawBufferPointer 45 | 46 | init(buffer: UnsafeRawBufferPointer) { 47 | self.buffer = buffer 48 | } 49 | 50 | public func resolve(at offset: UInt32) -> UnsafeRawBufferPointer { 51 | var remainder = buffer.sub(offset: Int(offset)) 52 | let size = consumeCompressedUInt(buffer: &remainder)! 53 | return remainder.sub(offset: 0, count: Int(size)) 54 | } 55 | } 56 | 57 | public struct HeapOffset where Type: Heap { 58 | public var value: UInt32 59 | 60 | public init(_ value: UInt32) { 61 | self.value = value 62 | } 63 | 64 | public func resolve(_ heap: Type) -> Type.Value { 65 | heap.resolve(at: value) 66 | } 67 | } 68 | 69 | extension Heap { 70 | public typealias Offset = HeapOffset 71 | } -------------------------------------------------------------------------------- /Sources/DotNetMetadataFormat/IntegerSize.swift: -------------------------------------------------------------------------------- 1 | public enum IntegerSize { 2 | case int8 3 | case int16 4 | case int32 5 | case int64 6 | case intPtr 7 | } -------------------------------------------------------------------------------- /Sources/DotNetMetadataFormat/InvalidFormatError.swift: -------------------------------------------------------------------------------- 1 | public enum InvalidFormatError: Error { 2 | case dosHeader 3 | case ntHeader 4 | case cliHeader 5 | case heapOffset 6 | case tableID 7 | case signatureBlob 8 | case tableConstraint 9 | } -------------------------------------------------------------------------------- /Sources/DotNetMetadataFormat/LayoutKind.swift: -------------------------------------------------------------------------------- 1 | public enum LayoutKind: Hashable { 2 | case auto 3 | case sequential 4 | case explicit 5 | } -------------------------------------------------------------------------------- /Sources/DotNetMetadataFormat/MetadataToken.swift: -------------------------------------------------------------------------------- 1 | public struct MetadataToken: Hashable, Comparable { 2 | public var rawValue: UInt32 3 | 4 | public init(rawValue: UInt32) { 5 | self.rawValue = rawValue 6 | } 7 | 8 | public init(tableID: TableID, oneBasedRowIndex: UInt32) { 9 | precondition(oneBasedRowIndex < 0x1000000) 10 | rawValue = (UInt32(tableID.rawValue) << 24) | oneBasedRowIndex 11 | } 12 | 13 | public init(tableID: TableID, rowIndex: TableRowIndex?) { 14 | self.init(tableID: tableID, oneBasedRowIndex: rowIndex?.oneBased ?? 0) 15 | } 16 | 17 | public init(nullOf tableID: TableID) { 18 | rawValue = UInt32(tableID.rawValue) << 24 19 | } 20 | 21 | public init(_ tableRowRef: TableRowRef) { 22 | self.init(tableID: Row.tableID, rowIndex: tableRowRef.index) 23 | } 24 | 25 | // TODO: This is not necessarily a table index. Other tokens are possible: string = 0x70, name = 0x71, baseType = 0x72 26 | public var tableID: TableID { .init(rawValue: UInt8(rawValue >> 24))! } 27 | public var oneBasedRowIndex: UInt32 { rawValue & 0xFFFFFF } 28 | public var rowIndex: TableRowIndex? { .init(oneBased: oneBasedRowIndex) } 29 | public var isNull: Bool { rowIndex == nil } 30 | 31 | public static func < (lhs: MetadataToken, rhs: MetadataToken) -> Bool { 32 | lhs.rawValue < rhs.rawValue 33 | } 34 | } -------------------------------------------------------------------------------- /Sources/DotNetMetadataFormat/NameKind.swift: -------------------------------------------------------------------------------- 1 | public enum NameKind: Hashable { 2 | case regular 3 | case special 4 | case runtime 5 | } -------------------------------------------------------------------------------- /Sources/DotNetMetadataFormat/PEView+Section.swift: -------------------------------------------------------------------------------- 1 | import DotNetMetadataCInterop 2 | 3 | extension PEView { 4 | public struct Section { 5 | let header: UnsafePointer 6 | let data: UnsafeRawBufferPointer 7 | 8 | var name: String { 9 | let chars = header.asRawBuffer().bindMemory(offset: 0, to: UInt8.self, count: 8) 10 | return String(bytes: chars.prefix { $0 != 0 }, encoding: .utf8)! 11 | } 12 | 13 | func contains(virtualAddress: UInt32) -> Bool { 14 | virtualAddress >= header.pointee.VirtualAddress && virtualAddress < header.pointee.VirtualAddress + header.pointee.Misc.VirtualSize 15 | } 16 | 17 | func resolve(virtualAddress: UInt32, size: UInt32) -> UnsafeRawBufferPointer { 18 | precondition(self.contains(virtualAddress: virtualAddress)) 19 | return data.sub(offset: Int(virtualAddress - header.pointee.VirtualAddress), count: Int(size)) 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /Sources/DotNetMetadataFormat/Table+Collection.swift: -------------------------------------------------------------------------------- 1 | extension Table: RandomAccessCollection { 2 | public typealias RowRef = TableRowRef 3 | public typealias Element = Row 4 | public typealias Index = TableRowIndex 5 | 6 | public var startIndex: TableRowIndex { .init(zeroBased: 0) } 7 | public var endIndex: TableRowIndex { .init(zeroBased: UInt32(count)) } 8 | 9 | public func index(after i: TableRowIndex) -> TableRowIndex { 10 | .init(zeroBased: i.zeroBased + 1) 11 | } 12 | 13 | public func index(before i: TableRowIndex) -> TableRowIndex { 14 | .init(zeroBased: i.zeroBased - 1) 15 | } 16 | 17 | public func index(_ i: TableRowIndex, offsetBy offset: Int) -> TableRowIndex { 18 | .init(zeroBased: UInt32(Int(i.zeroBased) + offset)) 19 | } 20 | } -------------------------------------------------------------------------------- /Sources/DotNetMetadataFormat/Table+find.swift: -------------------------------------------------------------------------------- 1 | extension Table where Row: KeyedTableRow { 2 | public func findAny(primaryKey: Row.PrimaryKey) -> TableRowIndex? { 3 | binarySearch(for: primaryKey, selector: { $0.primaryKey }).asOptional 4 | } 5 | 6 | public func findFirst(primaryKey: Row.PrimaryKey) -> TableRowIndex? { 7 | binarySearch(for: primaryKey, selector: { $0.primaryKey }, matchPreference: .first).asOptional 8 | } 9 | 10 | public func findAll(primaryKey: Row.PrimaryKey) -> Range { 11 | binarySearchRange(for: primaryKey, selector: { $0.primaryKey }) 12 | } 13 | } 14 | 15 | extension Table where Row: DoublyKeyedTableRow { 16 | public func findAny(primaryKey: Row.PrimaryKey, secondaryKey: Row.SecondaryKey) -> TableRowIndex? { 17 | self.binarySearch( 18 | for: (primaryKey, secondaryKey), 19 | selector: { ($0.primaryKey, $0.secondaryKey) }, 20 | lessThan: <).asOptional 21 | } 22 | 23 | public func findFirst(primaryKey: Row.PrimaryKey, secondaryKey: Row.SecondaryKey) -> TableRowIndex? { 24 | self.binarySearch( 25 | for: (primaryKey, secondaryKey), 26 | selector: { ($0.primaryKey, $0.secondaryKey) }, 27 | lessThan: <, 28 | matchPreference: .first).asOptional 29 | } 30 | } 31 | 32 | extension NestedClassTable { 33 | public func findAllNested(enclosing: TypeDefTable.RowRef) -> Range { 34 | binarySearchRange(for: enclosing, selector: { $0.enclosingClass }) 35 | } 36 | } -------------------------------------------------------------------------------- /Sources/DotNetMetadataFormat/Table.swift: -------------------------------------------------------------------------------- 1 | public final class Table where Row: TableRow { 2 | public typealias Row = Row 3 | 4 | private let buffer: UnsafeRawBufferPointer 5 | private let sizes: TableSizes 6 | public let isSorted: Bool 7 | 8 | init(buffer: UnsafeRawBufferPointer, sizes: TableSizes, sorted: Bool) { 9 | self.buffer = buffer 10 | self.sizes = sizes 11 | self.isSorted = sorted 12 | } 13 | 14 | public static var id: TableID { Row.tableID } 15 | public var count: Int { sizes.getRowCount(Row.tableID) } 16 | public var rowSize: Int { buffer.count / count } 17 | 18 | public subscript(_ index: TableRowIndex) -> Row { 19 | let rowBuffer = buffer.sub(offset: Int(index.zeroBased) * rowSize, count: rowSize) 20 | return Row(reading: rowBuffer, sizes: sizes) 21 | } 22 | } -------------------------------------------------------------------------------- /Sources/DotNetMetadataFormat/TableID.swift: -------------------------------------------------------------------------------- 1 | // Identifies a metadata table by the value that identifies it in metadata tokens, 2 | // which is also the order of appearance in the metadata stream. 3 | public enum TableID: UInt8 { 4 | case module = 0x00 5 | case typeRef = 0x01 6 | case typeDef = 0x02 7 | case field = 0x04 8 | case methodDef = 0x06 9 | case param = 0x08 10 | case interfaceImpl = 0x09 11 | case memberRef = 0x0A 12 | case constant = 0x0B 13 | case customAttribute = 0x0C 14 | case fieldMarshal = 0x0D 15 | case declSecurity = 0x0E 16 | case classLayout = 0x0F 17 | case fieldLayout = 0x10 18 | case standAloneSig = 0x11 19 | case eventMap = 0x12 20 | case event = 0x14 21 | case propertyMap = 0x15 22 | case property = 0x17 23 | case methodSemantics = 0x18 24 | case methodImpl = 0x19 25 | case moduleRef = 0x1A 26 | case typeSpec = 0x1B 27 | case implMap = 0x1C 28 | case fieldRva = 0x1D 29 | case assembly = 0x20 30 | case assemblyProcessor = 0x21 31 | case assemblyOS = 0x22 32 | case assemblyRef = 0x23 33 | case assemblyRefProcessor = 0x24 34 | case assemblyRefOS = 0x25 35 | case file = 0x26 36 | case exportedType = 0x27 37 | case manifestResource = 0x28 38 | case nestedClass = 0x29 39 | case genericParam = 0x2A 40 | case methodSpec = 0x2B 41 | case genericParamConstraint = 0x2C 42 | 43 | public var intValue: Int { Int(rawValue) } 44 | 45 | public static let count = 64 46 | public typealias BitSet = UInt64 47 | } -------------------------------------------------------------------------------- /Sources/DotNetMetadataFormat/TableRow.swift: -------------------------------------------------------------------------------- 1 | /// A row in a metadata table 2 | /// The size of rows isn't constant because the size of columns which index 3 | /// into heaps or other metadata tables depends on the size of these heaps/tables. 4 | public protocol TableRow { 5 | static var tableID: TableID { get } 6 | static func getSize(sizes: TableSizes) -> Int 7 | init(reading: UnsafeRawBufferPointer, sizes: TableSizes) 8 | } 9 | 10 | public protocol KeyedTableRow: TableRow { 11 | associatedtype PrimaryKey: Comparable 12 | var primaryKey: PrimaryKey { get } 13 | } 14 | 15 | public protocol DoublyKeyedTableRow: KeyedTableRow { 16 | associatedtype SecondaryKey: Comparable 17 | var secondaryKey: SecondaryKey { get } 18 | } -------------------------------------------------------------------------------- /Sources/DotNetMetadataFormat/TableRowIndex.swift: -------------------------------------------------------------------------------- 1 | /// A strongly-typed, 3-byte index of a metadata table row. 2 | /// In CLI (§II.22), they are one-based where zero means a null row: 3 | /// > Indexes to tables begin at 1, so index 1 means the first row in any given metadata table. 4 | /// > (An index value of zero denotes that it does not index a row at all; 5 | /// > that is, it behaves like a null reference.) 6 | /// To leverage the Swift type system better, we represent them as zero-based 7 | /// and use Optional to represent nullability. 8 | public struct TableRowIndex: Hashable, Comparable { 9 | public static var first: TableRowIndex { .init(zeroBased: 0) } 10 | 11 | // Imitate a UInt24 using 3 bytes since values never exceed 0xFF_FF_FF and so Optional is 4 bytes. 12 | private let zeroBased_low16: UInt16 13 | private let zeroBased_high8: UInt8 14 | 15 | public init(zeroBased: UInt32) { 16 | precondition(zeroBased < 0xFF_FF_FF) 17 | self.zeroBased_low16 = UInt16(zeroBased & 0xFF_FF) 18 | self.zeroBased_high8 = UInt8((zeroBased >> 16) & 0xFF) 19 | } 20 | 21 | public init?(oneBased: UInt32) { 22 | precondition(oneBased < 0x1_00_00_00) 23 | guard oneBased != 0 else { return nil } 24 | self.init(zeroBased: oneBased - 1) 25 | } 26 | 27 | public var zeroBased: UInt32 { (UInt32(zeroBased_high8) << 16) | UInt32(zeroBased_low16) } 28 | public var oneBased: UInt32 { zeroBased + 1 } 29 | 30 | public static func < (lhs: TableRowIndex, rhs: TableRowIndex) -> Bool { 31 | return lhs.zeroBased < rhs.zeroBased 32 | } 33 | } 34 | 35 | extension TableRowIndex: Strideable { 36 | public typealias Stride = Int 37 | 38 | public func distance(to other: Self) -> Stride { 39 | Int(other.zeroBased) - Int(zeroBased) 40 | } 41 | 42 | public func advanced(by n: Stride) -> Self { 43 | .init(zeroBased: UInt32(Int(zeroBased) + n)) 44 | } 45 | } 46 | 47 | extension Optional where Wrapped == TableRowIndex { 48 | public var oneBased: UInt32 { self?.oneBased ?? 0 } 49 | } -------------------------------------------------------------------------------- /Sources/DotNetMetadataFormat/TableRowReader.swift: -------------------------------------------------------------------------------- 1 | /// Reads a table row one column value at a time. 2 | struct TableRowReader { 3 | private var remainder: UnsafeRawBufferPointer 4 | private let sizes: TableSizes 5 | 6 | private init(buffer: UnsafeRawBufferPointer, sizes: TableSizes) { 7 | self.remainder = buffer 8 | self.sizes = sizes 9 | } 10 | 11 | public static func read(buffer: UnsafeRawBufferPointer, sizes: TableSizes, callback: (inout TableRowReader) -> Row) -> Row { 12 | var reader = TableRowReader(buffer: buffer, sizes: sizes) 13 | let result = callback(&reader) 14 | if !reader.remainder.isEmpty { fatalError() } 15 | return result 16 | } 17 | 18 | mutating func readConstant() -> T { 19 | return remainder.consume(type: T.self).pointee 20 | } 21 | 22 | mutating func readHeapOffset() -> HeapOffset { 23 | return .init( 24 | sizes.getHeapOffsetSize(T.self) == 2 25 | ? UInt32(remainder.consume(type: UInt16.self).pointee) 26 | : remainder.consume(type: UInt32.self).pointee) 27 | } 28 | 29 | mutating func readTableRowRef() -> TableRowRef { 30 | return .init( 31 | oneBasedIndex: sizes.getTableRowIndexSize(OtherRow.self) == 2 32 | ? UInt32(remainder.consume(type: UInt16.self).pointee) 33 | : remainder.consume(type: UInt32.self).pointee) 34 | } 35 | 36 | mutating func readCodedIndex() -> CodedIndex { 37 | CodedIndex(value: sizes.getCodedIndexSize(Tag.self) == 2 38 | ? UInt32(remainder.consume(type: UInt16.self).pointee) 39 | : remainder.consume(type: UInt32.self).pointee) 40 | } 41 | } -------------------------------------------------------------------------------- /Sources/DotNetMetadataFormat/TableRowRef.swift: -------------------------------------------------------------------------------- 1 | /// A reference to a metadata table row, which might be null. 2 | /// This is a wrapper for an Optional that encodes table information, 3 | /// and which can be used as a sorting key for table lookups. 4 | public struct TableRowRef: Hashable, Comparable { 5 | public static var null: TableRowRef { .init() } 6 | 7 | public let oneBasedIndex: UInt32 8 | 9 | private init() { 10 | self.oneBasedIndex = 0 11 | } 12 | 13 | public init(index: TableRowIndex?) { 14 | self.init(oneBasedIndex: index.oneBased) 15 | } 16 | 17 | public init(index: TableRowIndex) { 18 | self.init(oneBasedIndex: index.oneBased) 19 | } 20 | 21 | public init(oneBasedIndex: UInt32) { 22 | precondition(oneBasedIndex < 0x1_00_00_00) 23 | self.oneBasedIndex = oneBasedIndex 24 | } 25 | 26 | public var isNull: Bool { oneBasedIndex == 0 } 27 | public var index: TableRowIndex? { .init(oneBased: oneBasedIndex) } 28 | public var zeroBasedIndex: UInt32? { index?.zeroBased } 29 | public var metadataToken: MetadataToken { .init(self) } 30 | 31 | public static func < (lhs: TableRowRef, rhs: TableRowRef) -> Bool { 32 | lhs.oneBasedIndex < rhs.oneBasedIndex 33 | } 34 | } 35 | 36 | -------------------------------------------------------------------------------- /Sources/DotNetMetadataFormat/TableRowSizeBuilder.swift: -------------------------------------------------------------------------------- 1 | /// Computes the size of a table row by adding the size of its column values. 2 | internal struct TableRowSizeBuilder where Row: TableRow { 3 | let sizes: TableSizes 4 | let size: Int 5 | 6 | init(sizes: TableSizes, size: Int = 0) { 7 | self.sizes = sizes 8 | self.size = size 9 | } 10 | 11 | private func adding(size: Int) -> Self { 12 | return Self(sizes: sizes, size: self.size + size) 13 | } 14 | 15 | public func addingConstant(_: KeyPath) -> Self { 16 | adding(size: MemoryLayout.stride) 17 | } 18 | 19 | public func addingHeapOffset(_: KeyPath>) -> Self { 20 | adding(size: sizes.getHeapOffsetSize(T.self)) 21 | } 22 | 23 | public func addingTableRowRef(_: KeyPath>) -> Self { 24 | adding(size: sizes.getTableRowIndexSize(OtherRow.tableID)) 25 | } 26 | 27 | public func addingCodedIndex(_: KeyPath>) -> Self { 28 | adding(size: sizes.getCodedIndexSize(Tag.self)) 29 | } 30 | 31 | public func addingPaddingByte() -> Self { 32 | return adding(size: 1) 33 | } 34 | } -------------------------------------------------------------------------------- /Sources/DotNetMetadataFormat/TableSizes.swift: -------------------------------------------------------------------------------- 1 | /// Holds size information about metadata tables, 2 | /// allowing for computing column sizes. 3 | public final class TableSizes { 4 | public let tableRowCounts: [UInt32] 5 | public let heapSizingBits: UInt8 6 | 7 | public init(heapSizingBits: UInt8, tableRowCounts: [UInt32]) { 8 | precondition(tableRowCounts.count == TableID.count) 9 | self.heapSizingBits = heapSizingBits 10 | self.tableRowCounts = tableRowCounts 11 | } 12 | 13 | public var stringHeapOffsetSize: Int { (heapSizingBits & 1) == 0 ? 2 : 4 } 14 | public var guidHeapOffsetSize: Int { (heapSizingBits & 2) == 0 ? 2 : 4 } 15 | public var blobHeapOffsetSize: Int { (heapSizingBits & 4) == 0 ? 2 : 4 } 16 | 17 | public func getRowCount(_ tableID: Int) -> Int { 18 | Int(tableRowCounts[tableID]) 19 | } 20 | 21 | public func getRowCount(_ tableID: TableID) -> Int { 22 | Int(tableRowCounts[Int(tableID.rawValue)]) 23 | } 24 | 25 | public func getRowCount(_: Row.Type) -> Int where Row: TableRow { 26 | getRowCount(Row.tableID) 27 | } 28 | 29 | public func getHeapOffsetSize(_: T.Type) -> Int where T: Heap { 30 | if T.self == StringHeap.self { return stringHeapOffsetSize } 31 | if T.self == GuidHeap.self { return guidHeapOffsetSize } 32 | if T.self == BlobHeap.self { return blobHeapOffsetSize } 33 | fatalError("Unexpeted heap type \(T.self)") 34 | } 35 | 36 | public func getTableRowIndexSize(_ tableID: TableID) -> Int { 37 | // §II.24.2.6: 38 | // > If e is a simple index into a table with index i, 39 | // > it is stored using 2 bytes if table i has less than 2^16 rows, 40 | // > otherwise it is stored using 4 bytes. 41 | getRowCount(tableID) < 0x10000 ? 2 : 4 42 | } 43 | 44 | public func getTableRowIndexSize(_: Row.Type) -> Int where Row: TableRow { 45 | getTableRowIndexSize(Row.tableID) 46 | } 47 | 48 | public func getCodedIndexSize(_: Tag.Type) -> Int where Tag: CodedIndexTag { 49 | // The most significant bits are reserved for the tag, 50 | // depending on how many different tables this can index into. 51 | // The rest of the bits are the row index. 52 | // The coded index is 32 bits iff at least one indexed table 53 | // has more rows than could be indexed using the row index bits 54 | // if the coded index was 16 bits. 55 | let maxRowCount = Tag.tables.compactMap { $0 }.map { tableRowCounts[Int($0.rawValue)] }.max()! 56 | return maxRowCount < (1 << (16 - Tag.bitCount)) ? 2 : 4 57 | } 58 | } -------------------------------------------------------------------------------- /Sources/DotNetMetadataFormat/Tables.swift: -------------------------------------------------------------------------------- 1 | public typealias ModuleTable = Table 2 | public typealias TypeRefTable = Table 3 | public typealias TypeDefTable = Table 4 | public typealias FieldTable = Table 5 | public typealias MethodDefTable = Table 6 | public typealias ParamTable = Table 7 | public typealias InterfaceImplTable = Table 8 | public typealias MemberRefTable = Table 9 | public typealias ConstantTable = Table 10 | public typealias CustomAttributeTable = Table 11 | public typealias FieldMarshalTable = Table 12 | public typealias DeclSecurityTable = Table 13 | public typealias ClassLayoutTable = Table 14 | public typealias FieldLayoutTable = Table 15 | public typealias StandAloneSigTable = Table 16 | public typealias EventMapTable = Table 17 | public typealias EventTable = Table 18 | public typealias PropertyMapTable = Table 19 | public typealias PropertyTable = Table 20 | public typealias MethodSemanticsTable = Table 21 | public typealias MethodImplTable = Table 22 | public typealias ModuleRefTable = Table 23 | public typealias TypeSpecTable = Table 24 | public typealias ImplMapTable = Table 25 | public typealias FieldRvaTable = Table 26 | public typealias AssemblyTable = Table 27 | //public typealias AssemblyProcessorTable = Table 28 | //public typealias AssemblyOSTable = Table 29 | public typealias AssemblyRefTable = Table 30 | //public typealias AssemblyRefProcessorTable = Table 31 | //public typealias AssemblyRefOSTable = Table 32 | public typealias FileTable = Table 33 | public typealias ExportedTypeTable = Table 34 | public typealias ManifestResourceTable = Table 35 | public typealias NestedClassTable = Table 36 | public typealias GenericParamTable = Table 37 | public typealias MethodSpecTable = Table 38 | public typealias GenericParamConstraintTable = Table -------------------------------------------------------------------------------- /Sources/DotNetMetadataFormat/TypeDefTable+findMethodOwner.swift: -------------------------------------------------------------------------------- 1 | extension TypeDefTable { 2 | public func findMethodDefOwner(rowIndex methodDefRowIndex: TableRowIndex) -> TableRowIndex? { 3 | let result = self.binarySearch(for: .init(index: methodDefRowIndex), selector: { $0.methodList }, lessThan: { $0 < $1 }) 4 | switch result { 5 | case .present(at: let foundIndex): 6 | var index = foundIndex 7 | while true { 8 | let nextIndex = self.index(after: index) 9 | if nextIndex == endIndex { break } 10 | let nextRow: Row = self[nextIndex] 11 | if nextRow.methodList.index != methodDefRowIndex { break } 12 | index = nextIndex 13 | } 14 | return index 15 | 16 | case .absent(insertAt: let index): 17 | return index == startIndex ? nil : self.index(before: index) 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /Sources/DotNetMetadataFormat/UnsafePointers+Extensions.swift: -------------------------------------------------------------------------------- 1 | extension UnsafeRawBufferPointer { 2 | static var empty: UnsafeRawBufferPointer { 3 | UnsafeRawBufferPointer(start: nil, count: 0) 4 | } 5 | 6 | func sub(offset: Int) -> UnsafeRawBufferPointer { 7 | UnsafeRawBufferPointer(rebasing: self[offset...]) 8 | } 9 | 10 | func sub(offset: Int, count: Int) -> UnsafeRawBufferPointer { 11 | UnsafeRawBufferPointer(rebasing: self[offset ..< (offset + count)]) 12 | } 13 | 14 | func bindMemory(offset: Int, to: T.Type) -> UnsafePointer { 15 | sub(offset: offset).bindMemory(to: T.self).baseAddress! 16 | } 17 | 18 | func bindMemory(offset: Int, to: T.Type, count: Int) -> UnsafeBufferPointer { 19 | let rebased = UnsafeRawBufferPointer(rebasing: self[offset...]) 20 | let bound = rebased.bindMemory(to: T.self) 21 | return UnsafeBufferPointer(rebasing: bound[0 ..< count]) 22 | } 23 | 24 | @discardableResult mutating func consume(count: Int) -> UnsafeRawBufferPointer { 25 | let result = self.sub(offset: 0, count: count) 26 | self = self.sub(offset: count) 27 | return result 28 | } 29 | 30 | @discardableResult mutating func consume(type: T.Type) -> UnsafePointer { 31 | let result = bindMemory(offset: 0, to: type) 32 | self = self.sub(offset: MemoryLayout.stride) 33 | return result 34 | } 35 | 36 | @discardableResult mutating func consume(type: T.Type, count: Int) -> UnsafeBufferPointer { 37 | let result = bindMemory(offset: 0, to: type, count: count) 38 | self = self.sub(offset: MemoryLayout.stride * count) 39 | return result 40 | } 41 | 42 | @discardableResult mutating func consumeDwordPaddedUTF8String() -> String { 43 | let length = self.firstIndex(of: 0)! 44 | let result = String(bytes: self.sub(offset: 0, count: length), encoding: .utf8)! 45 | self = sub(offset: (length + 4) & ~0x3) 46 | return result 47 | } 48 | } 49 | 50 | extension UnsafePointer { 51 | func asRawBuffer() -> UnsafeRawBufferPointer { 52 | UnsafeRawBufferPointer(start: self, count: MemoryLayout.stride) 53 | } 54 | } -------------------------------------------------------------------------------- /Sources/DotNetMetadataFormat/Visibility.swift: -------------------------------------------------------------------------------- 1 | public enum Visibility: Hashable { 2 | case compilerControlled 3 | case `private` 4 | case assembly // internal 5 | case familyAndAssembly // private protected 6 | case familyOrAssembly // protected internal 7 | case family // protected 8 | case `public` 9 | } -------------------------------------------------------------------------------- /Sources/DotNetXMLDocs/AssemblyDocumentation+init.swift: -------------------------------------------------------------------------------- 1 | import FoundationXML 2 | import struct Foundation.URL 3 | 4 | extension AssemblyDocumentation { 5 | public init(readingFileAtPath filePath: String) throws { 6 | let url = URL(fileURLWithPath: filePath) 7 | let document = try XMLDocument(contentsOf: url, options: []) 8 | try self.init(parsing: document) 9 | } 10 | 11 | public init(parsing document: XMLDocument) throws { 12 | var builder = Builder() 13 | Self.parse(document: document, to: &builder) 14 | guard let result = builder.result else { throw DocumentationFormatError() } 15 | self = result 16 | } 17 | 18 | fileprivate struct Builder: AssemblyDocumentationSink { 19 | var result: AssemblyDocumentation? 20 | 21 | mutating func setAssemblyName(_ name: String) { 22 | guard result == nil else { return } 23 | self.result = AssemblyDocumentation(assemblyName: name) 24 | } 25 | 26 | mutating func addMember(_ member: MemberDocumentation, forKey key: MemberDocumentationKey) { 27 | guard result != nil else { return } 28 | result!.members[key] = member 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Sources/DotNetXMLDocs/AssemblyDocumentation+parse.swift: -------------------------------------------------------------------------------- 1 | import FoundationXML 2 | 3 | extension AssemblyDocumentation { 4 | public static func parse(document: XMLDocument, to sink: inout Sink) { 5 | if let root = document.rootElement(), root.name == "doc" { 6 | if let assembly = root.singleElement(forName: "assembly") { 7 | if let name = assembly.singleElement(forName: "name")?.stringValue { 8 | sink.setAssemblyName(name) 9 | } 10 | } 11 | 12 | if let members = root.singleElement(forName: "members") { 13 | for member in members.elements(forName: "member") { 14 | if let memberName = member.attribute(forName: "name")?.stringValue, 15 | let memberDocKey = try? MemberDocumentationKey(parsing: memberName) { 16 | let memberDoc = MemberDocumentation(parsing: member) 17 | sink.addMember(memberDoc, forKey: memberDocKey) 18 | } 19 | } 20 | } 21 | } 22 | } 23 | } 24 | 25 | extension XMLElement { 26 | fileprivate func firstElement(forName name: String) -> XMLElement? { 27 | let elements = self.elements(forName: name) 28 | return elements.count > 0 ? elements[0] : nil 29 | } 30 | 31 | fileprivate func singleElement(forName name: String) -> XMLElement? { 32 | let elements = self.elements(forName: name) 33 | return elements.count == 1 ? elements[0] : nil 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Sources/DotNetXMLDocs/AssemblyDocumentation.swift: -------------------------------------------------------------------------------- 1 | public struct AssemblyDocumentation { 2 | public var assemblyName: String 3 | public var members: [MemberDocumentationKey: MemberDocumentation] = [:] 4 | 5 | public init(assemblyName: String) { 6 | self.assemblyName = assemblyName 7 | } 8 | } -------------------------------------------------------------------------------- /Sources/DotNetXMLDocs/AssemblyDocumentationSink.swift: -------------------------------------------------------------------------------- 1 | public protocol AssemblyDocumentationSink { 2 | mutating func setAssemblyName(_ name: String) 3 | mutating func addMember(_ member: MemberDocumentation, forKey key: MemberDocumentationKey) 4 | } -------------------------------------------------------------------------------- /Sources/DotNetXMLDocs/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | file(GLOB_RECURSE SOURCES "*.swift") 2 | add_library(DotNetXMLDocs STATIC ${SOURCES}) -------------------------------------------------------------------------------- /Sources/DotNetXMLDocs/DocumentationFormatError.swift: -------------------------------------------------------------------------------- 1 | public struct DocumentationFormatError: Error {} -------------------------------------------------------------------------------- /Sources/DotNetXMLDocs/DocumentationText+parsing.swift: -------------------------------------------------------------------------------- 1 | import FoundationXML 2 | import struct Foundation.URL 3 | 4 | extension DocumentationText { 5 | public init(parsing nodes: [XMLNode]) throws { 6 | try self.init(nodes: nodes.map { try .init(parsing: $0) }) 7 | } 8 | } 9 | 10 | extension DocumentationText.Node { 11 | public init(parsing node: XMLNode) throws { 12 | self = try Self.parse(node: node) 13 | } 14 | 15 | private static func parse(node: XMLNode) throws -> Self { 16 | guard node.kind != .text else { return .plain(node.stringValue ?? "") } 17 | 18 | guard let element = node as? XMLElement else { throw DocumentationFormatError() } 19 | switch element.name! { 20 | // Blocks 21 | case "para": return .paragraph(try DocumentationText(parsing: element.children ?? [])) 22 | case "code": return .codeBlock(element.xmlString) 23 | case "list": 24 | let type = element.attribute(forName: "type")?.stringValue.flatMap { ListType(rawValue: $0) } 25 | let items = try element.elements(forName: "item").map { try DocumentationText(parsing: $0.children ?? []) } 26 | return .list(type: type, items: items) 27 | case "example": return .example(try DocumentationText(parsing: element.children ?? [])) 28 | 29 | // Inline 30 | case "c": return .codeSpan(text: element.xmlString) // TODO: Handle properly 31 | case "paramref", "typeparamref": 32 | let name = element.attribute(forName: "name")?.stringValue ?? "" 33 | return element.name == "paramref" ? .paramReference(name: name) : .typeParamReference(name: name) 34 | case "see", "seealso": 35 | let cref = element.attribute(forName: "cref")?.stringValue ?? "" 36 | let url = element.attribute(forName: "url")?.stringValue.flatMap { URL(string: $0) } 37 | let also = element.name == "seealso" 38 | return .see(codeReference: try MemberDocumentationKey(parsing: cref), url: url, also: also) 39 | 40 | default: 41 | // TODO: Handle unknown elements 42 | throw DocumentationFormatError() 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /Sources/DotNetXMLDocs/DocumentationText.swift: -------------------------------------------------------------------------------- 1 | import struct Foundation.URL 2 | 3 | /// A structured documentation text. 4 | public struct DocumentationText: Equatable { 5 | public var nodes: [Node] 6 | 7 | public init(nodes: [Node]) { 8 | self.nodes = nodes 9 | } 10 | 11 | public static func plain(_ text: String) -> DocumentationText { 12 | .init(nodes: [.plain(text)]) 13 | } 14 | 15 | /// A structural node in a documentation text, roughly corresponding to an XML element. 16 | public enum Node: Equatable { 17 | // Logically there are two levels of nodes: blocks and inline, 18 | // but the XML nesting for doc comments is weakly defined, so we are permissive (e.g. a list can contain a paragraph). 19 | 20 | case plain(String) 21 | 22 | // Blocks 23 | case paragraph(DocumentationText) // 24 | case list(type: ListType? = nil, items: [DocumentationText]) // 25 | case codeBlock(String) // 26 | case example(DocumentationText) // 27 | 28 | // Inlines 29 | case codeSpan(text: String) // 30 | case paramReference(name: String) // 31 | case typeParamReference(name: String) // 32 | case see(codeReference: MemberDocumentationKey, url: URL? = nil, also: Bool = false) // 33 | 34 | public enum ListType: String, Equatable { 35 | case bullet 36 | case number 37 | // case table // Not supported 38 | } 39 | } 40 | } 41 | 42 | -------------------------------------------------------------------------------- /Sources/DotNetXMLDocs/DocumentationTypeNode+parsing.swift: -------------------------------------------------------------------------------- 1 | 2 | extension DocumentationTypeNode { 3 | public init(parsing str: String) throws { 4 | var remainder = Substring(str) 5 | self = try Self(consuming: &remainder) 6 | guard remainder.isEmpty else { throw DocumentationFormatError() } 7 | } 8 | 9 | internal init(consuming str: inout Substring) throws { 10 | self = try Self.consume(&str) 11 | } 12 | 13 | internal static func consume(_ remainder: inout Substring) throws -> Self { 14 | var typeNode: Self 15 | if remainder.tryConsume("`") { 16 | let kind = remainder.tryConsume("`") ? GenericParamKind.method : .type 17 | let digits = remainder.consume(while: { $0.isNumber }) 18 | guard let index = Int(digits) else { throw DocumentationFormatError() } 19 | typeNode = .genericParam(index: index, kind: kind) 20 | } 21 | else { 22 | let typeReference = try DocumentationTypeReference(consuming: &remainder) 23 | guard case .bound = typeReference.genericity else { throw DocumentationFormatError() } 24 | typeNode = .bound(typeReference) 25 | } 26 | 27 | while true { 28 | if remainder.tryConsume("[") { 29 | guard remainder.tryConsume("]") else { throw DocumentationFormatError() } 30 | typeNode = .array(of: typeNode) 31 | } 32 | else if remainder.tryConsume("*") { 33 | typeNode = .pointer(to: typeNode) 34 | } 35 | else { 36 | break 37 | } 38 | } 39 | 40 | return typeNode 41 | } 42 | } -------------------------------------------------------------------------------- /Sources/DotNetXMLDocs/DocumentationTypeNode.swift: -------------------------------------------------------------------------------- 1 | public enum DocumentationTypeNode: Hashable { 2 | case bound(DocumentationTypeReference) 3 | indirect case array(of: DocumentationTypeNode) 4 | indirect case pointer(to: DocumentationTypeNode) 5 | case genericParam(index: Int, kind: GenericParamKind) 6 | 7 | public enum GenericParamKind: Hashable { 8 | case type 9 | case method 10 | } 11 | } 12 | 13 | extension DocumentationTypeNode { 14 | public static func bound( 15 | namespace: String? = nil, 16 | nameWithoutGenericArity: String, 17 | genericity: DocumentationTypeReference.Genericity = .bound([])) -> Self { 18 | .bound(DocumentationTypeReference( 19 | namespace: namespace, 20 | nameWithoutGenericArity: nameWithoutGenericArity, 21 | genericity: genericity)) 22 | } 23 | } -------------------------------------------------------------------------------- /Sources/DotNetXMLDocs/DocumentationTypeReference.swift: -------------------------------------------------------------------------------- 1 | public struct DocumentationTypeReference: Hashable { 2 | public var namespace: String? 3 | public var nameWithoutGenericArity: String 4 | public var genericity: Genericity 5 | 6 | public init(namespace: String? = nil, nameWithoutGenericArity: String, genericity: Genericity = .bound([])) { 7 | self.namespace = namespace 8 | self.nameWithoutGenericArity = nameWithoutGenericArity 9 | self.genericity = genericity 10 | } 11 | 12 | public init(namespace: String? = nil, nameWithoutGenericArity: String, genericArity: Int) { 13 | self.init( 14 | namespace: namespace, 15 | nameWithoutGenericArity: nameWithoutGenericArity, 16 | genericity: genericArity == 0 ? .bound([]) : .unbound(arity: genericArity)) 17 | } 18 | 19 | public init(namespace: String? = nil, nameWithoutGenericArity: String, genericArgs: [DocumentationTypeNode]) { 20 | self.init( 21 | namespace: namespace, 22 | nameWithoutGenericArity: nameWithoutGenericArity, 23 | genericity: .bound(genericArgs)) 24 | } 25 | 26 | public var genericArity: Int { 27 | switch genericity { 28 | case .unbound(let arity): return arity 29 | case .bound(let args): return args.count 30 | } 31 | } 32 | 33 | public enum Genericity: Hashable { 34 | case unbound(arity: Int) // System.Action`1, arity should not be zero 35 | case bound([DocumentationTypeNode]) // System.String or System.Action`1{System.String} 36 | } 37 | } -------------------------------------------------------------------------------- /Sources/DotNetXMLDocs/MemberDocumentation+parsing.swift: -------------------------------------------------------------------------------- 1 | import FoundationXML 2 | 3 | extension MemberDocumentation { 4 | internal init(parsing element: XMLElement) { 5 | for child in element.children ?? [] { 6 | guard let element = child as? XMLElement else { continue } 7 | 8 | switch element.name { 9 | case "summary": summary = parseContentText(element) 10 | case "remarks": remarks = parseContentText(element) 11 | case "value": value = parseContentText(element) 12 | case "returns": returns = parseContentText(element) 13 | case "typeparam": 14 | if let name = element.attribute(forName: "name")?.stringValue, 15 | let content = parseContentText(element) { 16 | typeParams.append(.init(name: name, description: content)) 17 | } 18 | case "param": 19 | if let name = element.attribute(forName: "name")?.stringValue, 20 | let content = parseContentText(element) { 21 | params.append(.init(name: name, description: content)) 22 | } 23 | case "exception": 24 | if let crefString = element.attribute(forName: "cref")?.stringValue, 25 | let cref = try? MemberDocumentationKey(parsing: crefString), 26 | case .type(let type) = cref, 27 | let content = parseContentText(element) { 28 | exceptions.append(.init(type: type, description: content)) 29 | } 30 | default: break 31 | } 32 | } 33 | } 34 | 35 | fileprivate func parseContentText(_ element: XMLElement) -> DocumentationText? { 36 | guard let children = element.children else { return nil } 37 | return try? DocumentationText(parsing: children) 38 | } 39 | } -------------------------------------------------------------------------------- /Sources/DotNetXMLDocs/MemberDocumentation.swift: -------------------------------------------------------------------------------- 1 | public struct MemberDocumentation { 2 | /// The tag should be used to describe a type or a type member. 3 | public var summary: DocumentationText? 4 | 5 | /// The tag is used to add information about a type or a type member, 6 | /// supplementing the information specified with . 7 | public var remarks: DocumentationText? 8 | 9 | /// The tag lets you describe the value that a property represents. 10 | public var value: DocumentationText? 11 | 12 | /// The tag should be used in the comment for a generic type or method declaration to describe a type parameter. 13 | public var typeParams: [Param] = [] 14 | 15 | /// The tag should be used in the comment for a method declaration to describe one of the parameters for the method. 16 | public var params: [Param] = [] 17 | 18 | /// The tag should be used in the comment for a method declaration to describe the return value. 19 | public var returns: DocumentationText? 20 | 21 | /// This tag provides a way to document the exceptions a method can throw. 22 | public var exceptions: [Exception] = [] 23 | 24 | public init() {} 25 | 26 | public struct Param: Equatable { 27 | public var name: String 28 | public var description: DocumentationText 29 | 30 | public init(name: String, description: DocumentationText) { 31 | self.name = name 32 | self.description = description 33 | } 34 | } 35 | 36 | public struct Exception: Equatable { 37 | public var type: DocumentationTypeReference 38 | public var description: DocumentationText 39 | 40 | public init(type: DocumentationTypeReference, description: DocumentationText) { 41 | self.type = type 42 | self.description = description 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /Sources/DotNetXMLDocs/MemberDocumentationKey.swift: -------------------------------------------------------------------------------- 1 | public enum MemberDocumentationKey: Hashable { 2 | public static let constructorName: String = "#ctor" 3 | 4 | case namespace(name: String) 5 | case type(DocumentationTypeReference) 6 | case field(declaringType: DocumentationTypeReference, name: String) 7 | case method(declaringType: DocumentationTypeReference, name: String, params: [Param] = [], conversionTarget: Param? = nil) 8 | case property(declaringType: DocumentationTypeReference, name: String, params: [Param] = []) 9 | case event(declaringType: DocumentationTypeReference, name: String) 10 | case unresolved(String) 11 | 12 | public struct Param: Hashable { 13 | public var isByRef: Bool 14 | public var type: DocumentationTypeNode 15 | public var customModifiers: [DocumentationTypeNode] 16 | 17 | public init(type: DocumentationTypeNode, isByRef: Bool = false, customModifiers: [DocumentationTypeNode] = []) { 18 | self.type = type 19 | self.isByRef = isByRef 20 | self.customModifiers = customModifiers 21 | } 22 | } 23 | } 24 | 25 | extension MemberDocumentationKey { 26 | public static func type( 27 | namespace: String? = nil, 28 | nameWithoutGenericArity: String, 29 | genericity: DocumentationTypeReference.Genericity = .bound([])) -> Self { 30 | .type(DocumentationTypeReference( 31 | namespace: namespace, 32 | nameWithoutGenericArity: nameWithoutGenericArity, 33 | genericity: genericity)) 34 | } 35 | } -------------------------------------------------------------------------------- /Sources/DotNetXMLDocs/Substring+parsing.swift: -------------------------------------------------------------------------------- 1 | extension Substring { 2 | internal mutating func consume(while predicate: (Character) -> Bool) -> Substring { 3 | var index = startIndex 4 | while index < endIndex && predicate(self[index]) { 5 | index = self.index(after: index) 6 | } 7 | 8 | let result = self[.. Bool { 14 | guard self.first == prefix else { return false } 15 | self = self.dropFirst() 16 | return true 17 | } 18 | } 19 | 20 | internal func consumeIdentifier(_ remainder: inout Substring, allowConstructor: Bool = false) throws -> Substring { 21 | guard let identifier = tryConsumeIdentifier(&remainder, allowConstructor: allowConstructor) else { 22 | throw DocumentationFormatError() 23 | } 24 | return identifier 25 | } 26 | 27 | internal func tryConsumeIdentifier(_ remainder: inout Substring, allowConstructor: Bool = false) -> Substring? { 28 | let constructorName = "#ctor" 29 | if allowConstructor && remainder.starts(with: constructorName) { 30 | let original = remainder 31 | remainder.removeFirst(constructorName.count) 32 | return original[.. MemberDocumentation? { 6 | members[MemberDocumentationKey(forTypeDefinition: typeDefinition)] 7 | } 8 | 9 | public func lookup(member: Member) throws -> MemberDocumentation? { 10 | members[try MemberDocumentationKey(forMember: member)] 11 | } 12 | } -------------------------------------------------------------------------------- /Sources/DotNetXMLDocsFromMetadata/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | file(GLOB_RECURSE SOURCES "*.swift") 2 | add_library(DotNetXMLDocsFromMetadata STATIC ${SOURCES}) 3 | target_link_libraries(DotNetXMLDocsFromMetadata PUBLIC DotNetMetadata DotNetXMLDocs) -------------------------------------------------------------------------------- /Sources/DotNetXMLDocsFromMetadata/DocumentationTypeNode+init.swift: -------------------------------------------------------------------------------- 1 | import DotNetMetadata 2 | import DotNetXMLDocs 3 | 4 | extension DocumentationTypeNode { 5 | public init(forTypeNode typeNode: TypeNode) { 6 | switch typeNode { 7 | case .bound(let boundType): 8 | self = .bound(DocumentationTypeReference(forBoundType: boundType)) 9 | case .array(of: let elementType, shape: let shape): 10 | guard shape == .vector else { 11 | fatalError("Not implemented: multidimensional arrays in XML documentation") 12 | } 13 | self = .array(of: Self(forTypeNode: elementType)) 14 | case .pointer(to: let pointeeType): 15 | self = .pointer(to: Self(forTypeNode: pointeeType!)) // TODO: Handle void* 16 | case .genericParam(let param): 17 | self = .genericParam(index: param.index, kind: param is GenericTypeParam ? .type : .method) 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /Sources/DotNetXMLDocsFromMetadata/DocumentationTypeReference+init.swift: -------------------------------------------------------------------------------- 1 | import DotNetMetadata 2 | import DotNetXMLDocs 3 | 4 | extension DocumentationTypeReference { 5 | public init(forTypeDefinition typeDefinition: TypeDefinition, genericArgs: [TypeNode]? = nil) { 6 | self.init( 7 | namespace: typeDefinition.namespace, 8 | nameWithoutGenericArity: typeDefinition.nameWithoutGenericArity, 9 | genericity: Genericity(forTypeDefinition: typeDefinition, genericArgs: genericArgs)) 10 | } 11 | 12 | public init(forBoundType boundType: BoundType) { 13 | self.init(forTypeDefinition: boundType.definition, genericArgs: boundType.genericArgs) 14 | } 15 | } 16 | 17 | extension DocumentationTypeReference.Genericity { 18 | public init(forTypeDefinition typeDefinition: TypeDefinition, genericArgs: [TypeNode]?) { 19 | precondition(typeDefinition.genericArity == (genericArgs?.count ?? typeDefinition.genericArity)) 20 | if let genericArgs { 21 | self = .bound(genericArgs.map { .init(forTypeNode: $0) }) 22 | } 23 | else { 24 | self = typeDefinition.genericArity == 0 25 | ? .bound([]) 26 | : .unbound(arity: typeDefinition.genericArity) 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /Sources/DotNetXMLDocsFromMetadata/MemberDocumentationKey+init.swift: -------------------------------------------------------------------------------- 1 | import DotNetMetadata 2 | import DotNetXMLDocs 3 | 4 | extension MemberDocumentationKey { 5 | public init(forTypeDefinition typeDefinition: TypeDefinition) { 6 | self = .type(DocumentationTypeReference(forTypeDefinition: typeDefinition)) 7 | } 8 | 9 | public init(forMember member: Member) throws { 10 | let declaringType = DocumentationTypeReference(forTypeDefinition: member.definingType) 11 | switch member { 12 | case let field as Field: 13 | self = .field(declaringType: declaringType, name: field.name) 14 | case let event as Event: 15 | self = .event(declaringType: declaringType, name: event.name) 16 | case let property as Property: 17 | assert((try? property.getter?.arity ?? 0) == 0, "Indexers not implemented") 18 | self = .property(declaringType: declaringType, name: property.name) 19 | case let method as Method: 20 | let memberName = method is Constructor ? "#ctor" : method.name 21 | self = try .method(declaringType: declaringType, name: memberName, 22 | params: method.params.map { try .init(forParam: $0) } ) 23 | default: 24 | fatalError("Unexpected member type") 25 | } 26 | } 27 | } 28 | 29 | extension MemberDocumentationKey.Param { 30 | public init(forParam param: DotNetMetadata.Param) throws { 31 | try self.init(type: DocumentationTypeNode(forTypeNode: param.type), isByRef: param.isByRef) 32 | } 33 | } -------------------------------------------------------------------------------- /Sources/WindowsMetadata/Attributes/ActivatableAttribute.swift: -------------------------------------------------------------------------------- 1 | import DotNetMetadata 2 | 3 | /// Indicates that the class is an activatable runtime class. 4 | public struct ActivatableAttribute: AttributeType { 5 | public var factory: InterfaceDefinition? = nil 6 | public var applicability: VersionApplicability 7 | 8 | public init(factory: InterfaceDefinition? = nil, applicability: VersionApplicability) { 9 | self.factory = factory 10 | self.applicability = applicability 11 | } 12 | 13 | public static var namespace: String? { "Windows.Foundation.Metadata" } 14 | public static var name: String { "ActivatableAttribute" } 15 | public static var validOn: AttributeTargets { .class } 16 | public static var allowMultiple: Bool { true } 17 | public static var inherited: Bool { true } 18 | 19 | public static func decode(_ attribute: Attribute) throws -> ActivatableAttribute { 20 | let arguments = try attribute.arguments 21 | guard arguments.count >= 1 else { throw InvalidMetadataError.attributeArguments } 22 | 23 | if case .type(let factoryDefinition) = arguments[0] { 24 | guard let factory = factoryDefinition as? InterfaceDefinition else { throw InvalidMetadataError.attributeArguments } 25 | return ActivatableAttribute( 26 | factory: factory, 27 | applicability: try VersionApplicability.decode(arguments[1...])) 28 | } 29 | else { 30 | return ActivatableAttribute( 31 | applicability: try VersionApplicability.decode(arguments[...])) 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /Sources/WindowsMetadata/Attributes/AllowMultipleAttribute.swift: -------------------------------------------------------------------------------- 1 | import DotNetMetadata 2 | 3 | /// Indicates that multiple instances of a custom attribute can be applied to a target. 4 | public struct AllowMultipleAttribute: AttributeType { 5 | public static var namespace: String? { "Windows.Foundation.Metadata" } 6 | public static var name: String { "AllowMultipleAttribute" } 7 | public static var validOn: AttributeTargets { .class } 8 | public static var allowMultiple: Bool { false } 9 | public static var inherited: Bool { true } 10 | 11 | public static func decode(_ attribute: Attribute) throws -> Self { .init() } 12 | } -------------------------------------------------------------------------------- /Sources/WindowsMetadata/Attributes/ApiContractAttribute.swift: -------------------------------------------------------------------------------- 1 | import DotNetMetadata 2 | 3 | /// Specifies that the type represents an API contract. 4 | public struct ApiContractAttribute: AttributeType { 5 | public static var namespace: String? { "Windows.Foundation.Metadata" } 6 | public static var name: String { "ApiContractAttribute" } 7 | public static var validOn: AttributeTargets { .enum } 8 | public static var allowMultiple: Bool { false } 9 | public static var inherited: Bool { true } 10 | 11 | public static func decode(_ attribute: Attribute) throws -> Self { .init() } 12 | } -------------------------------------------------------------------------------- /Sources/WindowsMetadata/Attributes/AttributeUsageAttribute.swift: -------------------------------------------------------------------------------- 1 | import DotNetMetadata 2 | 3 | /// Windows Runtime-specific variant of [AttributeUsage]. 4 | public struct AttributeUsageAttribute: AttributeType { 5 | public var validOn: AttributeTargets 6 | 7 | public init(_ validOn: AttributeTargets) { 8 | self.validOn = validOn 9 | } 10 | 11 | public static var namespace: String? { "Windows.Foundation.Metadata" } 12 | public static var name: String { "AttributeUsageAttribute" } 13 | public static var validOn: AttributeTargets { .class } 14 | public static var allowMultiple: Bool { false } 15 | public static var inherited: Bool { true } 16 | 17 | public static func decode(_ attribute: Attribute) throws -> AttributeUsageAttribute { 18 | let arguments = try attribute.arguments 19 | guard arguments.count == 1, 20 | case .constant(let validOnConstant) = arguments[1], 21 | case .int32(let validOnValue) = validOnConstant else { throw InvalidMetadataError.attributeArguments } 22 | return AttributeUsageAttribute( 23 | AttributeTargets(rawValue: validOnValue)) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Sources/WindowsMetadata/Attributes/ComposableAttribute.swift: -------------------------------------------------------------------------------- 1 | import DotNetMetadata 2 | 3 | /// Indicates how a programming element is composed. 4 | public struct ComposableAttribute: AttributeType { 5 | public enum Kind: Int32, Hashable { 6 | /// Indicates that access to the programming element is limited to other elements 7 | /// in the containing class or types derived from the containing class. 8 | case protected = 1 9 | /// Indicates that access to the programming element is not restricted. 10 | case `public` = 2 11 | } 12 | 13 | public var factory: InterfaceDefinition 14 | public var kind: Kind 15 | public var applicability: VersionApplicability 16 | 17 | public init(factory: InterfaceDefinition, kind: Kind, applicability: VersionApplicability) { 18 | self.factory = factory 19 | self.kind = kind 20 | self.applicability = applicability 21 | } 22 | 23 | public static var namespace: String? { "Windows.Foundation.Metadata" } 24 | public static var name: String { "ComposableAttribute" } 25 | public static var validOn: AttributeTargets { .class } 26 | public static var allowMultiple: Bool { true } 27 | public static var inherited: Bool { true } 28 | 29 | public static func decode(_ attribute: Attribute) throws -> ComposableAttribute { 30 | let arguments = try attribute.arguments 31 | guard arguments.count >= 3 else { throw InvalidMetadataError.attributeArguments } 32 | 33 | guard case .type(let factoryDefinition) = arguments[0], 34 | let factory = factoryDefinition as? InterfaceDefinition, 35 | case .constant(let typeConstant) = arguments[1], 36 | case .int32(let typeValue) = typeConstant, 37 | let kind = Kind(rawValue: typeValue) else { throw InvalidMetadataError.attributeArguments } 38 | 39 | return ComposableAttribute( 40 | factory: factory, kind: kind, 41 | applicability: try VersionApplicability.decode(arguments[2...])) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Sources/WindowsMetadata/Attributes/ContractVersionAttribute.swift: -------------------------------------------------------------------------------- 1 | import DotNetMetadata 2 | 3 | public struct ContractVersionAttribute: AttributeType { 4 | public enum Contract { 5 | case type(TypeDefinition) 6 | case name(String) 7 | } 8 | 9 | public var contract: Contract? 10 | public var version: TwoPartVersion 11 | 12 | public init(contract: Contract? = nil, version: TwoPartVersion) { 13 | self.contract = contract 14 | self.version = version 15 | } 16 | 17 | public init(contract: String, version: TwoPartVersion) { 18 | self.init(contract: .name(contract), version: version) 19 | } 20 | 21 | public init(contract: TypeDefinition, version: TwoPartVersion) { 22 | self.init(contract: .type(contract), version: version) 23 | } 24 | 25 | public static var namespace: String? { "Windows.Foundation.Metadata" } 26 | public static var name: String { "ContractVersionAttribute" } 27 | public static var validOn: AttributeTargets { .allTypes | .allMembers } 28 | public static var allowMultiple: Bool { true } 29 | public static var inherited: Bool { true } 30 | 31 | public static func decode(_ attribute: Attribute) throws -> ContractVersionAttribute { 32 | // Three possible constructors: 33 | // - ContractVersionAttribute(uint version) 34 | // - ContractVersionAttribute(Type contract, uint version) 35 | // - ContractVersionAttribute(string contract, uint version) 36 | let arguments = try attribute.arguments 37 | guard arguments.count == 1 || arguments.count == 2 else { throw InvalidMetadataError.attributeArguments } 38 | 39 | let contract: Contract? 40 | if arguments.count == 2 { 41 | switch arguments[0] { 42 | case .constant(let contractConstant): 43 | guard case .string(let name) = contractConstant else { throw InvalidMetadataError.attributeArguments } 44 | contract = .name(name) 45 | case .type(let definition): 46 | contract = .type(definition) 47 | default: 48 | throw InvalidMetadataError.attributeArguments 49 | } 50 | } 51 | else { 52 | contract = nil 53 | } 54 | 55 | guard case .constant(let versionConstant) = arguments.last!, 56 | case .uint32(let version) = versionConstant else { throw InvalidMetadataError.attributeArguments } 57 | 58 | return ContractVersionAttribute(contract: contract, version: .init(unpacking: version)) 59 | } 60 | } -------------------------------------------------------------------------------- /Sources/WindowsMetadata/Attributes/DefaultAttribute.swift: -------------------------------------------------------------------------------- 1 | import DotNetMetadata 2 | 3 | /// Indicates the default interface for a runtime class. 4 | public struct DefaultAttribute: AttributeType { 5 | public static var namespace: String? { "Windows.Foundation.Metadata" } 6 | public static var name: String { "DefaultAttribute" } 7 | public static var validOn: AttributeTargets { .interfaceImpl } 8 | public static var allowMultiple: Bool { false } 9 | public static var inherited: Bool { true } 10 | 11 | public static func decode(_ attribute: Attribute) throws -> Self { .init() } 12 | 13 | public static func getDefaultInterface(_ class: ClassDefinition) throws -> BoundInterface? { 14 | try `class`.baseInterfaces.first { try $0.hasAttribute(DefaultAttribute.self) }?.interface 15 | } 16 | } -------------------------------------------------------------------------------- /Sources/WindowsMetadata/Attributes/DefaultOverloadAttribute.swift: -------------------------------------------------------------------------------- 1 | import DotNetMetadata 2 | 3 | /// Indicates that a method is the default overload method. 4 | /// This attribute must be used with OverloadAttribute. 5 | public struct DefaultOverloadAttribute: AttributeType { 6 | public static var namespace: String? { "Windows.Foundation.Metadata" } 7 | public static var name: String { "DefaultOverloadAttribute" } 8 | public static var validOn: AttributeTargets { .method } 9 | public static var allowMultiple: Bool { false } 10 | public static var inherited: Bool { true } 11 | 12 | public static func decode(_ attribute: Attribute) throws -> Self { .init() } 13 | } -------------------------------------------------------------------------------- /Sources/WindowsMetadata/Attributes/DeprecatedAttribute.swift: -------------------------------------------------------------------------------- 1 | import DotNetMetadata 2 | 3 | public struct DeprecatedAttribute: AttributeType { 4 | public enum Kind: Int32, Hashable { 5 | /// Compilers and other tools should treat the entity as deprecated. 6 | /// This is the default. 7 | case deprecate = 0 8 | /// Compilers and other tools should treat the entity as removed. 9 | case remove = 1 10 | } 11 | 12 | public var message: String 13 | public var kind: Kind 14 | public var applicability: VersionApplicability 15 | 16 | public init(message: String, kind: Kind, applicability: VersionApplicability) { 17 | self.message = message 18 | self.kind = kind 19 | self.applicability = applicability 20 | } 21 | 22 | public static var namespace: String? { "Windows.Foundation.Metadata" } 23 | public static var name: String { "DeprecatedAttribute" } 24 | public static var validOn: AttributeTargets { .allTypes | .allMembers } 25 | public static var allowMultiple: Bool { true } 26 | public static var inherited: Bool { true } 27 | 28 | public static func decode(_ attribute: Attribute) throws -> DeprecatedAttribute { 29 | let arguments = try attribute.arguments 30 | guard arguments.count >= 3 else { throw InvalidMetadataError.attributeArguments } 31 | 32 | guard case .constant(let messageConstant) = arguments[0], 33 | case .string(let message) = messageConstant, 34 | case .constant(let typeConstant) = arguments[1], 35 | case .int32(let typeValue) = typeConstant, 36 | let kind = Kind(rawValue: typeValue) else { throw InvalidMetadataError.attributeArguments } 37 | 38 | return DeprecatedAttribute( 39 | message: message, kind: kind, 40 | applicability: try VersionApplicability.decode(arguments[2...])) 41 | } 42 | } -------------------------------------------------------------------------------- /Sources/WindowsMetadata/Attributes/DualApiPartitionAttribute.swift: -------------------------------------------------------------------------------- 1 | import DotNetMetadata 2 | 3 | public struct DualApiPartitionAttribute: AttributeType { 4 | public var version: TwoPartVersion 5 | 6 | public init(_ version: TwoPartVersion) { 7 | self.version = version 8 | } 9 | 10 | public static var namespace: String? { "Windows.Foundation.Metadata" } 11 | public static var name: String { "DualApiPartitionAttribute" } 12 | public static var validOn: AttributeTargets { .class } 13 | public static var allowMultiple: Bool { false } 14 | public static var inherited: Bool { true } 15 | 16 | public static func decode(_ attribute: Attribute) throws -> Self { 17 | let namedArguments = try attribute.namedArguments 18 | if namedArguments.count == 0 { return .init(TwoPartVersion(major: 0, minor: 0)) } 19 | guard namedArguments.count <= 1, 20 | case .field(let field) = namedArguments[0].target, 21 | field.name == "version", 22 | case .constant(let versionConstant) = namedArguments[0].value, 23 | case .uint32(let versionValue) = versionConstant else { throw InvalidMetadataError.attributeArguments } 24 | return .init(TwoPartVersion(unpacking: versionValue)) 25 | } 26 | } -------------------------------------------------------------------------------- /Sources/WindowsMetadata/Attributes/ExclusiveToAttribute.swift: -------------------------------------------------------------------------------- 1 | import DotNetMetadata 2 | 3 | public struct ExclusiveToAttribute: AttributeType { 4 | public var target: ClassDefinition 5 | 6 | public init(_ target: ClassDefinition) { 7 | self.target = target 8 | } 9 | 10 | public static var namespace: String? { "Windows.Foundation.Metadata" } 11 | public static var name: String { "ExclusiveToAttribute" } 12 | public static var validOn: AttributeTargets { .interface } 13 | public static var allowMultiple: Bool { false } 14 | public static var inherited: Bool { true } 15 | 16 | public static func decode(_ attribute: Attribute) throws -> ExclusiveToAttribute { 17 | let arguments = try attribute.arguments 18 | guard arguments.count == 1, 19 | case .type(let target) = arguments[0], 20 | let targetClass = target as? ClassDefinition else { throw InvalidMetadataError.attributeArguments } 21 | return .init(targetClass) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Sources/WindowsMetadata/Attributes/ExperimentalAttribute.swift: -------------------------------------------------------------------------------- 1 | import DotNetMetadata 2 | 3 | /// Indicates that a type or member should be marked in metadata as experimental, 4 | /// and consequently may not be present in the final, released version of an SDK or library. 5 | public struct ExperimentalAttribute: AttributeType { 6 | public static var namespace: String? { "Windows.Foundation.Metadata" } 7 | public static var name: String { "ExperimentalAttribute" } 8 | public static var validOn: AttributeTargets { .allTypes } 9 | public static var allowMultiple: Bool { false } 10 | public static var inherited: Bool { true } 11 | 12 | public static func decode(_ attribute: Attribute) throws -> Self { .init() } 13 | } -------------------------------------------------------------------------------- /Sources/WindowsMetadata/Attributes/GuidAttribute.swift: -------------------------------------------------------------------------------- 1 | import DotNetMetadata 2 | import struct Foundation.UUID 3 | 4 | public struct GuidAttribute: AttributeType { 5 | public var value: UUID 6 | 7 | public init(_ value: UUID) { 8 | self.value = value 9 | } 10 | 11 | public static var namespace: String? { "Windows.Foundation.Metadata" } 12 | public static var name: String { "GuidAttribute" } 13 | public static var validOn: AttributeTargets { .interface | .delegate } 14 | public static var allowMultiple: Bool { false } 15 | public static var inherited: Bool { true } 16 | 17 | public static func decode(_ attribute: Attribute) throws -> Self { 18 | // [Windows.Foundation.Metadata.Guid(1516535814u, 33850, 19881, 134, 91, 157, 38, 229, 223, 173, 123)] 19 | let arguments = try attribute.arguments 20 | guard arguments.count == 11 else { throw InvalidMetadataError.attributeArguments } 21 | 22 | func toConstant(_ value: Attribute.Value) throws -> Constant { 23 | switch value { 24 | case let .constant(constant): return constant 25 | default: throw InvalidMetadataError.attributeArguments 26 | } 27 | } 28 | 29 | guard case .uint32(let a) = try toConstant(arguments[0]) else { throw InvalidMetadataError.attributeArguments } 30 | guard case .uint16(let b) = try toConstant(arguments[1]) else { throw InvalidMetadataError.attributeArguments } 31 | guard case .uint16(let c) = try toConstant(arguments[2]) else { throw InvalidMetadataError.attributeArguments } 32 | let rest = try arguments[3...].map { 33 | guard case .uint8(let value) = try toConstant($0) else { throw InvalidMetadataError.attributeArguments } 34 | return value 35 | } 36 | 37 | return .init(UUID(uuid: ( 38 | UInt8((a >> 24) & 0xFF), UInt8((a >> 16) & 0xFF), UInt8((a >> 8) & 0xFF), UInt8((a >> 0) & 0xFF), 39 | UInt8((b >> 8) & 0xFF), UInt8((b >> 0) & 0xFF), 40 | UInt8((c >> 8) & 0xFF), UInt8((c >> 0) & 0xFF), 41 | rest[0], rest[1], rest[2], rest[3], rest[4], rest[5], rest[6], rest[7] 42 | ))) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Sources/WindowsMetadata/Attributes/InternalAttribute.swift: -------------------------------------------------------------------------------- 1 | import DotNetMetadata 2 | 3 | /// Indicates that the interface contains internal methods. 4 | public struct InternalAttribute: AttributeType { 5 | public static var namespace: String? { "Windows.Foundation.Metadata" } 6 | public static var name: String { "InternalAttribute" } 7 | public static var validOn: AttributeTargets { .interfaceImpl } // No attribute target for interface implementations 8 | public static var allowMultiple: Bool { false } 9 | public static var inherited: Bool { true } 10 | 11 | public static func decode(_ attribute: Attribute) throws -> Self { .init() } 12 | } -------------------------------------------------------------------------------- /Sources/WindowsMetadata/Attributes/LengthIsAttribute.swift: -------------------------------------------------------------------------------- 1 | import DotNetMetadata 2 | 3 | /// Indicates the number of array elements. 4 | public struct LengthIsAttribute: AttributeType { 5 | public var paramIndex: Int32 6 | 7 | public init(_ paramIndex: Int32) { 8 | self.paramIndex = paramIndex 9 | } 10 | 11 | public static var namespace: String? { "Windows.Foundation.Metadata" } 12 | public static var name: String { "LengthIsAttribute" } 13 | public static var validOn: AttributeTargets { .parameter } 14 | public static var allowMultiple: Bool { false } 15 | public static var inherited: Bool { true } 16 | 17 | public static func decode(_ attribute: Attribute) throws -> Self { 18 | let arguments = try attribute.arguments 19 | guard arguments.count == 1, 20 | case .constant(let constant) = arguments[0], 21 | case .int32(let value) = constant else { throw InvalidMetadataError.attributeArguments } 22 | return .init(value) 23 | } 24 | } -------------------------------------------------------------------------------- /Sources/WindowsMetadata/Attributes/MarshalingBehaviorAttribute.swift: -------------------------------------------------------------------------------- 1 | import DotNetMetadata 2 | 3 | /// Indicates the threading model of a Windows Runtime component. 4 | public struct MarshalingBehaviorAttribute: AttributeType { 5 | public var type: MarshalingType 6 | 7 | public init(type: MarshalingType) { 8 | self.type = type 9 | } 10 | 11 | public static var namespace: String? { "Windows.Foundation.Metadata" } 12 | public static var name: String { "MarshalingBehaviorAttribute" } 13 | public static var validOn: AttributeTargets { .class } 14 | public static var allowMultiple: Bool { false } 15 | public static var inherited: Bool { true } 16 | 17 | public static func decode(_ attribute: Attribute) throws -> Self { 18 | let arguments = try attribute.arguments 19 | guard arguments.count == 1, 20 | case .constant(let constant) = arguments[0], 21 | case .int32(let value) = constant, 22 | let marshalingType = MarshalingType(rawValue: value) else { throw InvalidMetadataError.attributeArguments } 23 | return .init(type: marshalingType) 24 | } 25 | } 26 | 27 | public enum MarshalingType: Int32, Hashable { 28 | /// The class prevents marshaling on all interfaces. 29 | case none = 1 30 | /// The class marshals and unmarshals to the same pointer value on all interfaces. 31 | case agile = 2 32 | /// The class does not implement IMarshal or forwards to CoGetStandardMarshal on all interfaces. 33 | case standard = 3 34 | } -------------------------------------------------------------------------------- /Sources/WindowsMetadata/Attributes/NoExceptionAttribute.swift: -------------------------------------------------------------------------------- 1 | import DotNetMetadata 2 | 3 | /// Indicates that the interface contains protected methods. 4 | public struct NoExceptionAttribute: AttributeType { 5 | public static var namespace: String? { "Windows.Foundation.Metadata" } 6 | public static var name: String { "NoExceptionAttribute" } 7 | public static var validOn: AttributeTargets { .method | .property } 8 | public static var allowMultiple: Bool { false } 9 | public static var inherited: Bool { true } 10 | 11 | public static func decode(_ attribute: Attribute) throws -> Self { .init() } 12 | } -------------------------------------------------------------------------------- /Sources/WindowsMetadata/Attributes/OverloadAttribute.swift: -------------------------------------------------------------------------------- 1 | import DotNetMetadata 2 | 3 | /// Identifies the method as an overload in a language that supports overloading. 4 | public struct OverloadAttribute: AttributeType { 5 | public var methodName: String 6 | 7 | public init(_ methodName: String) { 8 | self.methodName = methodName 9 | } 10 | 11 | public static var namespace: String? { "Windows.Foundation.Metadata" } 12 | public static var name: String { "OverloadAttribute" } 13 | public static var validOn: AttributeTargets { .method } 14 | public static var allowMultiple: Bool { false } 15 | public static var inherited: Bool { true } 16 | 17 | public static func decode(_ attribute: Attribute) throws -> Self { 18 | let arguments = try attribute.arguments 19 | guard arguments.count == 1, 20 | case .constant(let constant) = arguments[0], 21 | case .string(let name) = constant else { throw InvalidMetadataError.attributeArguments } 22 | return .init(name) 23 | } 24 | } -------------------------------------------------------------------------------- /Sources/WindowsMetadata/Attributes/OverridableAttribute.swift: -------------------------------------------------------------------------------- 1 | import DotNetMetadata 2 | 3 | /// Indicates that the interface contains overridable methods. 4 | public struct OverridableAttribute: AttributeType { 5 | public static var namespace: String? { "Windows.Foundation.Metadata" } 6 | public static var name: String { "OverridableAttribute" } 7 | public static var validOn: AttributeTargets { .interfaceImpl } 8 | public static var allowMultiple: Bool { false } 9 | public static var inherited: Bool { true } 10 | 11 | public static func decode(_ attribute: Attribute) throws -> Self { .init() } 12 | } -------------------------------------------------------------------------------- /Sources/WindowsMetadata/Attributes/ProtectedAttribute.swift: -------------------------------------------------------------------------------- 1 | import DotNetMetadata 2 | 3 | /// Indicates that the interface contains protected methods. 4 | public struct ProtectedAttribute: AttributeType { 5 | public static var namespace: String? { "Windows.Foundation.Metadata" } 6 | public static var name: String { "ProtectedAttribute" } 7 | public static var validOn: AttributeTargets { .interfaceImpl } 8 | public static var allowMultiple: Bool { false } 9 | public static var inherited: Bool { true } 10 | 11 | public static func decode(_ attribute: Attribute) throws -> Self { .init() } 12 | } -------------------------------------------------------------------------------- /Sources/WindowsMetadata/Attributes/StaticAttribute.swift: -------------------------------------------------------------------------------- 1 | import DotNetMetadata 2 | 3 | /// Indicates an interface that contains only static methods. 4 | public struct StaticAttribute: AttributeType { 5 | public var interface: InterfaceDefinition 6 | public var applicability: VersionApplicability 7 | 8 | public init(interface: InterfaceDefinition, applicability: VersionApplicability) { 9 | self.interface = interface 10 | self.applicability = applicability 11 | } 12 | 13 | public static var namespace: String? { "Windows.Foundation.Metadata" } 14 | public static var name: String { "StaticAttribute" } 15 | public static var validOn: AttributeTargets { .class } 16 | public static var allowMultiple: Bool { true } 17 | public static var inherited: Bool { true } 18 | 19 | public static func decode(_ attribute: Attribute) throws -> StaticAttribute { 20 | let arguments = try attribute.arguments 21 | guard arguments.count >= 2 else { throw InvalidMetadataError.attributeArguments } 22 | guard case .type(let definition) = arguments[0], 23 | let interface = definition as? InterfaceDefinition else { throw InvalidMetadataError.attributeArguments } 24 | return StaticAttribute( 25 | interface: interface, 26 | applicability: try VersionApplicability.decode(arguments[1...])) 27 | } 28 | } -------------------------------------------------------------------------------- /Sources/WindowsMetadata/Attributes/ThreadingAttribute.swift: -------------------------------------------------------------------------------- 1 | import DotNetMetadata 2 | 3 | /// Indicates the threading model of a Windows Runtime component. 4 | public struct ThreadingAttribute: AttributeType { 5 | public var model: ThreadingModel 6 | 7 | public init(_ model: ThreadingModel) { 8 | self.model = model 9 | } 10 | 11 | public static var namespace: String? { "Windows.Foundation.Metadata" } 12 | public static var name: String { "ThreadingAttribute" } 13 | public static var validOn: AttributeTargets { .class } 14 | public static var allowMultiple: Bool { false } 15 | public static var inherited: Bool { true } 16 | 17 | public static func decode(_ attribute: Attribute) throws -> Self { 18 | let arguments = try attribute.arguments 19 | guard arguments.count == 1, 20 | case .constant(let constant) = arguments[0], 21 | case .int32(let value) = constant, 22 | let threadingModel = ThreadingModel(rawValue: value) else { throw InvalidMetadataError.attributeArguments } 23 | return .init(threadingModel) 24 | } 25 | } 26 | 27 | public enum ThreadingModel: Int32, Hashable { 28 | /// Single-threaded apartment 29 | case sta = 1 30 | /// Multithreaded apartment 31 | case mta = 2 32 | /// Both single-threaded and multithreaded apartments 33 | case both = 3 34 | } -------------------------------------------------------------------------------- /Sources/WindowsMetadata/Attributes/VariantAttribute.swift: -------------------------------------------------------------------------------- 1 | import DotNetMetadata 2 | 3 | /// Indicates that the item is an instance of a variant **IInspectable**. 4 | /// Applies to method parameters, properties, and return values of types. 5 | public struct VariantAttribute: AttributeType { 6 | public static var namespace: String? { "Windows.Foundation.Metadata" } 7 | public static var name: String { "VariantAttribute" } 8 | public static var validOn: AttributeTargets { .property | .parameter } 9 | public static var allowMultiple: Bool { false } 10 | public static var inherited: Bool { true } 11 | 12 | public static func decode(_ attribute: Attribute) throws -> Self { .init() } 13 | } -------------------------------------------------------------------------------- /Sources/WindowsMetadata/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | file(GLOB_RECURSE SOURCES "*.swift") 2 | add_library(WindowsMetadata STATIC ${SOURCES}) 3 | target_link_libraries(WindowsMetadata PUBLIC DotNetMetadata) -------------------------------------------------------------------------------- /Sources/WindowsMetadata/InterfaceID.swift: -------------------------------------------------------------------------------- 1 | import DotNetMetadata 2 | import struct Foundation.UUID 3 | 4 | public func getInterfaceID(_ typeDefinition: TypeDefinition, genericArgs: [TypeNode]? = nil) throws -> UUID { 5 | guard typeDefinition is InterfaceDefinition || typeDefinition is DelegateDefinition else { 6 | throw UnexpectedTypeError(typeDefinition.fullName, context: #function, reason: "Only interfaces and delegates have interface IDs") 7 | } 8 | 9 | /// Generic interfaces/delegates have a GUID computed based on the generic arguments. 10 | if let genericArgs, genericArgs.count > 0 { 11 | let signature = try WinRTTypeSignature(typeDefinition.bindType(genericArgs: genericArgs)) 12 | return signature.parameterizedID 13 | } 14 | /// Non-generic interfaces/delegates defined in winmd files specify their GUID via Windows.Foundation.Metadata.GuidAttribute. 15 | else if let attribute = try typeDefinition.findAttribute(WindowsMetadata.GuidAttribute.self) { 16 | return attribute.value 17 | } 18 | else { 19 | throw WinMDError.missingAttribute 20 | } 21 | } 22 | 23 | public func getInterfaceID(_ type: BoundType) throws -> UUID { 24 | try getInterfaceID(type.definition, genericArgs: type.genericArgs) 25 | } -------------------------------------------------------------------------------- /Sources/WindowsMetadata/Platform.swift: -------------------------------------------------------------------------------- 1 | public enum Platform: Int32, Hashable { 2 | /// For use by Windows metadata. 3 | case windows = 0 4 | /// For use by Windows Phone metadata. 5 | case windowsPhone = 1 6 | } -------------------------------------------------------------------------------- /Sources/WindowsMetadata/TwoPartVersion.swift: -------------------------------------------------------------------------------- 1 | public struct TwoPartVersion: Comparable, Hashable, CustomStringConvertible { 2 | public var major: UInt16 3 | public var minor: UInt16 4 | 5 | public init(major: UInt16, minor: UInt16 = 0) { 6 | self.major = major 7 | self.minor = minor 8 | } 9 | 10 | public init?(parsing str: String) { 11 | let components = str.split(separator: ".") 12 | guard components.count == 2 else { return nil } 13 | guard let major = UInt16(components[0]), 14 | let minor = UInt16(components[1]) else { return nil } 15 | self.major = major 16 | self.minor = minor 17 | } 18 | 19 | public init(unpacking value: UInt32) { 20 | self.major = UInt16(value >> 16) 21 | self.minor = UInt16(value & 0xFFFF) 22 | } 23 | 24 | public var packed: UInt32 { UInt32(major) << 16 | UInt32(minor) } 25 | public var description: String { "\(major).\(minor)" } 26 | 27 | public static func < (lhs: TwoPartVersion, rhs: TwoPartVersion) -> Bool { 28 | if lhs.major != rhs.major { 29 | return lhs.major < rhs.major 30 | } else { 31 | return lhs.minor < rhs.minor 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /Sources/WindowsMetadata/VersionApplicability.swift: -------------------------------------------------------------------------------- 1 | import DotNetMetadata 2 | 3 | public struct VersionApplicability: Hashable { 4 | public enum Context: Hashable { 5 | case contract(name: String) 6 | case platform(Platform) 7 | } 8 | 9 | public var version: TwoPartVersion 10 | public var context: Context? 11 | 12 | public init(version: TwoPartVersion, context: Context? = nil) { 13 | self.version = version 14 | self.context = context 15 | } 16 | 17 | public init(version: TwoPartVersion, contractName: String) { 18 | self.version = version 19 | self.context = .contract(name: contractName) 20 | } 21 | 22 | public init(version: TwoPartVersion, platform: Platform) { 23 | self.version = version 24 | self.context = .platform(platform) 25 | } 26 | 27 | public static func decode(_ arguments: ArraySlice) throws -> VersionApplicability { 28 | guard arguments.count >= 1 && arguments.count <= 2 else { throw InvalidMetadataError.attributeArguments } 29 | 30 | var context: VersionApplicability.Context? 31 | if arguments.count == 2 { 32 | guard case .constant(let contextConstant) = arguments.last! else { throw InvalidMetadataError.attributeArguments } 33 | switch contextConstant { 34 | case .string(let contractName): 35 | context = .contract(name: contractName) 36 | case .int32(let platformValue): 37 | guard let platform = Platform(rawValue: platformValue) else { throw InvalidMetadataError.attributeArguments } 38 | context = .platform(platform) 39 | default: 40 | throw InvalidMetadataError.attributeArguments 41 | } 42 | } 43 | 44 | guard case .constant(let versionConstant) = arguments.first!, 45 | case .uint32(let version) = versionConstant else { throw InvalidMetadataError.attributeArguments } 46 | 47 | return VersionApplicability(version: .init(unpacking: version), context: context) 48 | } 49 | } -------------------------------------------------------------------------------- /Sources/WindowsMetadata/WinMDError.swift: -------------------------------------------------------------------------------- 1 | public enum WinMDError: Hashable, Error { 2 | case missingAttribute 3 | } -------------------------------------------------------------------------------- /Sources/WindowsMetadata/WinRTIntegerType.swift: -------------------------------------------------------------------------------- 1 | import DotNetMetadata 2 | 3 | public enum WinRTIntegerType: Hashable { 4 | // No Int8 5 | case uint8 6 | case int16 7 | case uint16 8 | case int32 9 | case uint32 10 | case int64 11 | case uint64 12 | // No IntPtr/UIntPtr 13 | } 14 | 15 | extension WinRTIntegerType { 16 | public init?(size: IntegerSize, signed: Bool) { 17 | switch (size, signed) { 18 | case (.int8, false): self = .uint8 19 | case (.int16, true): self = .int16 20 | case (.int16, false): self = .uint16 21 | case (.int32, true): self = .int32 22 | case (.int32, false): self = .uint32 23 | case (.int64, true): self = .int64 24 | case (.int64, false): self = .uint64 25 | default: return nil 26 | } 27 | } 28 | 29 | public var size: IntegerSize { 30 | switch self { 31 | case .uint8: return .int8 32 | case .int16: return .int16 33 | case .uint16: return .int16 34 | case .int32: return .int32 35 | case .uint32: return .int32 36 | case .int64: return .int64 37 | case .uint64: return .int64 38 | } 39 | } 40 | 41 | public var isSigned: Bool { 42 | return self == .int16 || self == .int32 || self == .int64 43 | } 44 | } 45 | 46 | extension WinRTIntegerType: CustomStringConvertible { 47 | public var name: String { 48 | switch self { 49 | case .uint8: return "UInt8" 50 | case .int16: return "Int16" 51 | case .uint16: return "UInt16" 52 | case .int32: return "Int32" 53 | case .uint32: return "UInt32" 54 | case .int64: return "Int64" 55 | case .uint64: return "UInt64" 56 | } 57 | } 58 | 59 | public var description: String { name } 60 | } -------------------------------------------------------------------------------- /Sources/WindowsMetadata/WinRTPrimitiveType.swift: -------------------------------------------------------------------------------- 1 | import DotNetMetadata 2 | 3 | // Types from the mscorlib System namespace which are usable from WinRT 4 | public enum WinRTPrimitiveType: Hashable { 5 | case boolean 6 | case integer(WinRTIntegerType) 7 | case float(double: Bool) 8 | case char16 9 | case guid 10 | case string 11 | } 12 | 13 | extension WinRTPrimitiveType { 14 | public static var uint8: WinRTPrimitiveType { .integer(.uint8) } 15 | public static var int16: WinRTPrimitiveType { .integer(.int16) } 16 | public static var uint16: WinRTPrimitiveType { .integer(.uint16) } 17 | public static var int32: WinRTPrimitiveType { .integer(.int32) } 18 | public static var uint32: WinRTPrimitiveType { .integer(.uint32) } 19 | public static var int64: WinRTPrimitiveType { .integer(.int64) } 20 | public static var uint64: WinRTPrimitiveType { .integer(.uint64) } 21 | public static var single: WinRTPrimitiveType { .float(double: false) } 22 | public static var double: WinRTPrimitiveType { .float(double: true) } 23 | } 24 | 25 | extension WinRTPrimitiveType: CustomStringConvertible { 26 | public var name: String { 27 | switch self { 28 | case .boolean: return "Boolean" 29 | case .integer(let type): return type.name 30 | case .float(let double): return double ? "Double" : "Single" 31 | case .char16: return "Char16" 32 | case .guid: return "Guid" 33 | case .string: return "String" 34 | } 35 | } 36 | 37 | public var description: String { name } 38 | } 39 | 40 | extension WinRTPrimitiveType { 41 | public init?(fromName name: String) { 42 | switch name { 43 | case "Boolean": self = .boolean 44 | case "Byte": self = .uint8 45 | case "Int16": self = .int16 46 | case "UInt16": self = .uint16 47 | case "Int32": self = .int32 48 | case "UInt32": self = .uint32 49 | case "Int64": self = .int64 50 | case "UInt64": self = .uint64 51 | case "Single": self = .single 52 | case "Double": self = .double 53 | case "Char16": self = .char16 54 | case "Guid": self = .guid 55 | case "String": self = .string 56 | default: return nil 57 | } 58 | } 59 | 60 | public init?(fromSystemNamespaceType name: String) { 61 | // System namespace names match WinRT names except for Char16 62 | switch name { 63 | case "Char": self = .char16 64 | case "Char16": return nil 65 | default: self.init(fromName: name) 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Sources/WindowsMetadata/WinRTTypeName+fromType.swift: -------------------------------------------------------------------------------- 1 | import DotNetMetadata 2 | 3 | extension WinRTTypeName { 4 | public static func from(type: BoundType) throws -> WinRTTypeName { 5 | if type.definition.namespace == "System" { 6 | if let primitiveType = WinRTPrimitiveType(fromSystemNamespaceType: type.definition.name) { 7 | return .primitive(primitiveType) 8 | } else if type.definition.name == "Object" { 9 | return .object 10 | } else { 11 | throw UnexpectedTypeError(type.description, reason: "Not a well-known WinRT System type") 12 | } 13 | } 14 | 15 | // https://learn.microsoft.com/en-us/uwp/winrt-cref/winrt-type-system 16 | // > All types—except for the fundamental types—must be contained within a namespace. 17 | // > It's not valid for a type to be in the global namespace. 18 | guard let namespace = type.definition.namespace else { 19 | throw UnexpectedTypeError(type.description, reason: "Namespaceless WinRT type") 20 | } 21 | 22 | if type.definition.genericArity > 0 { 23 | guard let parameterizedType = WinRTParameterizedType.from(namespace: namespace, name: type.definition.name) else { 24 | throw UnexpectedTypeError(type.description, reason: "Not a well-known WinRT parameterized type") 25 | } 26 | 27 | var genericArgs = [WinRTTypeName]() 28 | for genericArg in type.genericArgs { 29 | guard case .bound(let genericArgBoundType) = genericArg else { 30 | throw UnexpectedTypeError(genericArg.description, reason: "WinRT generic argument is not a bound type") 31 | } 32 | genericArgs.append(try from(type: genericArgBoundType)) 33 | } 34 | return .parameterized(parameterizedType, args: genericArgs) 35 | } 36 | 37 | return .declared( 38 | namespace: namespace, 39 | name: type.definition is DelegateDefinition ? "I" + type.definition.name : type.definition.name) 40 | } 41 | } -------------------------------------------------------------------------------- /Sources/WindowsMetadata/WinRTTypeName.swift: -------------------------------------------------------------------------------- 1 | public enum WinRTTypeName: Hashable { 2 | case object 3 | case primitive(WinRTPrimitiveType) 4 | case parameterized(WinRTParameterizedType, args: [WinRTTypeName] = []) 5 | 6 | // https://learn.microsoft.com/en-us/uwp/winrt-cref/winrt-type-system 7 | // > All types—except for the fundamental types—must be contained within a namespace. 8 | // > It's not valid for a type to be in the global namespace. 9 | // WinRT types do not support nesting either. 10 | case declared(namespace: String, name: String) 11 | } 12 | 13 | extension WinRTTypeName: CustomStringConvertible, TextOutputStreamable { 14 | public var description: String { toString() } 15 | 16 | public func toString(useIInspectable: Bool = false, useAritySuffixes: Bool = false) -> String { 17 | var output = String() 18 | write(useIInspectable: useIInspectable, useAritySuffixes: useAritySuffixes, to: &output) 19 | return output 20 | } 21 | 22 | public func write(to output: inout some TextOutputStream) { 23 | write(useIInspectable: false, useAritySuffixes: false, to: &output) 24 | } 25 | 26 | public func write(useIInspectable: Bool = false, useAritySuffixes: Bool = false, to output: inout some TextOutputStream) { 27 | switch self { 28 | case .object: 29 | output.write(useIInspectable ? "IInspectable" : "Object") 30 | case let .primitive(primitiveType): 31 | output.write(primitiveType.name) 32 | case let .parameterized(type, args: args): 33 | let name = useAritySuffixes ? type.nameWithAritySuffix : type.nameWithoutAritySuffix 34 | write(namespace: type.namespace, name: name, genericArgs: args, to: &output) 35 | case let .declared(namespace, name): 36 | write(namespace: namespace, name: name, genericArgs: [], to: &output) 37 | } 38 | } 39 | 40 | private func write(namespace: String, name: String, genericArgs: [WinRTTypeName], to output: inout some TextOutputStream) { 41 | output.write(namespace) 42 | output.write(".") 43 | output.write(name) 44 | if !genericArgs.isEmpty { 45 | output.write("<") 46 | for (index, genericArg) in genericArgs.enumerated() { 47 | if index > 0 { output.write(", ") } 48 | genericArg.write(to: &output) 49 | } 50 | output.write(">") 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /Sources/WindowsMetadata/WinRTTypeSignature+parameterizedID.swift: -------------------------------------------------------------------------------- 1 | import struct Foundation.UUID 2 | 3 | // https://learn.microsoft.com/en-us/uwp/winrt-cref/winrt-type-system#guid-generation-for-parameterized-types 4 | fileprivate let parameterizedInterfaceGuidBytes: [UInt8] = [ 5 | 0x11, 0xf4, 0x7a, 0xd5, 6 | 0x7b, 0x73, 7 | 0x42, 0xc0, 8 | 0xab, 0xae, 0x87, 0x8b, 0x1e, 0x16, 0xad, 0xee 9 | ]; 10 | 11 | extension WinRTTypeSignature { 12 | public var parameterizedID: UUID { 13 | get { 14 | switch self { 15 | case let .interface(_, args), let .delegate(_, args): precondition(!args.isEmpty) 16 | default: preconditionFailure("Only interfaces and delegates have parameterized IDs") 17 | } 18 | 19 | var sha1 = SHA1() 20 | sha1.process(parameterizedInterfaceGuidBytes) 21 | sha1.process(Array(self.toString().utf8)) 22 | let hash = sha1.finalize() 23 | 24 | return UUID(uuid: ( 25 | hash[0], hash[1], hash[2], hash[3], 26 | hash[4], hash[5], 27 | (hash[6] & 0x0F) | 0x50, hash[7], 28 | (hash[8] & 0x3F) | 0x80, hash[9], 29 | hash[10], hash[11], hash[12], hash[13], hash[14], hash[15])) 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /Sources/WindowsMetadata/WinRTTypeSignature+string.swift: -------------------------------------------------------------------------------- 1 | import struct Foundation.UUID 2 | 3 | extension WinRTTypeSignature { 4 | public func toString() -> String { 5 | var string = "" 6 | appendString(&string) 7 | return string 8 | } 9 | 10 | public func appendString(_ string: inout String) { 11 | switch self { 12 | case let .interface(id, args): 13 | if args.isEmpty { 14 | appendGuid(id, to: &string) 15 | } 16 | else { 17 | appendParameterizedInterface(id, args: args, to: &string) 18 | } 19 | 20 | case let .delegate(id, args): 21 | if args.isEmpty { 22 | string.append("delegate(") 23 | appendGuid(id, to: &string) 24 | string.append(")") 25 | } 26 | else { 27 | appendParameterizedInterface(id, args: args, to: &string) 28 | } 29 | 30 | case let .baseType(baseType): 31 | string.append(baseType.rawValue) 32 | 33 | case .comInterface: 34 | string.append("cinterface(IInspectable)") 35 | 36 | case let .interfaceGroup(name, defaultInterface): 37 | string.append("ig(") 38 | string.append(name) 39 | string.append(";") 40 | defaultInterface.appendString(&string) 41 | string.append(")") 42 | 43 | case let .runtimeClass(name, defaultInterface): 44 | string.append("rc(") 45 | string.append(name) 46 | string.append(";") 47 | defaultInterface.appendString(&string) 48 | string.append(")") 49 | 50 | case let .struct(name, fields): 51 | string.append("struct(") 52 | string.append(name) 53 | for field in fields { 54 | string.append(";") 55 | field.appendString(&string) 56 | } 57 | string.append(")") 58 | 59 | case let .enum(name, flags): 60 | string.append("enum(") 61 | string.append(name) 62 | string.append(";") 63 | string.append(flags ? "u4" : "i4") 64 | string.append(")") 65 | } 66 | } 67 | } 68 | 69 | fileprivate func appendParameterizedInterface(_ id: UUID, args: [WinRTTypeSignature], to string: inout String) { 70 | string.append("pinterface(") 71 | appendGuid(id, to: &string) 72 | for arg in args { 73 | string.append(";") 74 | arg.appendString(&string) 75 | } 76 | string.append(")") 77 | } 78 | 79 | fileprivate func appendGuid(_ guid: UUID, to string: inout String) { 80 | string.append("{") 81 | string.append(guid.uuidString.lowercased()) 82 | string.append("}") 83 | } -------------------------------------------------------------------------------- /Sources/WindowsMetadata/WinRTTypeSignature.swift: -------------------------------------------------------------------------------- 1 | import struct Foundation.UUID 2 | 3 | /// A WinRT type signature, used to generate GUIDs for parameterized interface and delegate types. 4 | /// See https://learn.microsoft.com/en-us/uwp/winrt-cref/winrt-type-system#guid-generation-for-parameterized-types 5 | public enum WinRTTypeSignature: Hashable { 6 | case interface(id: UUID, args: [WinRTTypeSignature] = []) 7 | case delegate(id: UUID, args: [WinRTTypeSignature] = []) 8 | case baseType(BaseType) 9 | case comInterface 10 | indirect case interfaceGroup(name: String, default: WinRTTypeSignature) 11 | indirect case runtimeClass(name: String, defaultInterface: WinRTTypeSignature) 12 | case `struct`(name: String, fields: [WinRTTypeSignature]) 13 | case `enum`(name: String, flags: Bool) 14 | 15 | public enum BaseType: String, Hashable { 16 | case boolean = "b1" 17 | case uint8 = "u1" 18 | case int16 = "i2" // Undocumented but presumably valid 19 | case uint16 = "u2" // Undocumented but presumably valid 20 | case int32 = "i4" 21 | case uint32 = "u4" 22 | case int64 = "i8" 23 | case uint64 = "u8" 24 | case single = "f4" 25 | case double = "f8" 26 | case char16 = "c2" 27 | case string = "string" 28 | case guid = "g16" 29 | } 30 | } -------------------------------------------------------------------------------- /Sources/WindowsMetadata/WindowsKit+ApplicationPlatform.swift: -------------------------------------------------------------------------------- 1 | import DotNetMetadata 2 | import struct Foundation.URL 3 | import FoundationXML 4 | 5 | extension WindowsKit { 6 | // From Platform.xml or PreviousPlatforms.xml 7 | public struct ApplicationPlatform { 8 | public var name: String 9 | public var friendlyName: String 10 | public var version: FourPartVersion 11 | public var apiContracts: [String: FourPartVersion] 12 | 13 | public init(readingFileAtPath filePath: String) throws { 14 | let url = URL(fileURLWithPath: filePath) 15 | let document = try XMLDocument(contentsOf: url, options: []) 16 | try self.init(parsing: document) 17 | } 18 | 19 | public init(parsing document: XMLDocument) throws { 20 | guard let rootElement = document.rootElement(), rootElement.name == "ApplicationPlatform" else { 21 | fatalError() 22 | } 23 | self.name = rootElement.attribute(forName: "name")!.stringValue! 24 | self.friendlyName = rootElement.attribute(forName: "friendlyName")!.stringValue! 25 | self.version = FourPartVersion(parsing: rootElement.attribute(forName: "version")!.stringValue!)! 26 | self.apiContracts = [:] 27 | for apiContractElement in rootElement.singleElement(forName: "ContainedApiContracts")!.elements(forName: "ApiContract") { 28 | let name = apiContractElement.attribute(forName: "name")!.stringValue! 29 | let version = FourPartVersion(parsing: apiContractElement.attribute(forName: "version")!.stringValue!)! 30 | self.apiContracts[name] = version 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Sources/WindowsMetadata/WindowsKit+Extension.swift: -------------------------------------------------------------------------------- 1 | extension WindowsKit { 2 | public final class Extension { 3 | private unowned let kit: WindowsKit! 4 | public let name: String 5 | 6 | internal init(kit: WindowsKit, name: String) { 7 | self.kit = kit 8 | self.name = name 9 | } 10 | 11 | public var manifestXMLPath: String { 12 | "\(kit.rootDirectory)\\Extension SDKs\\\(name)\\\(kit.version)\\SDKManifest.xml" 13 | } 14 | 15 | public func readManifest() throws -> ExtensionManifest { 16 | try ExtensionManifest(readingFileAtPath: manifestXMLPath) 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /Sources/WindowsMetadata/WindowsKit+ExtensionManifest.swift: -------------------------------------------------------------------------------- 1 | import DotNetMetadata 2 | import struct Foundation.URL 3 | import FoundationXML 4 | 5 | extension WindowsKit { 6 | // From ExtensionManifest.xml 7 | public struct ExtensionManifest { 8 | public var targetPlatform: String 9 | public var targetPlatformMinVersion: FourPartVersion 10 | public var targetPlatformVersion: FourPartVersion 11 | public var sdkType: String 12 | public var displayName: String 13 | public var appliesTo: String 14 | public var productFamilyName: String 15 | public var apiContracts: [String: FourPartVersion] 16 | 17 | public init(readingFileAtPath filePath: String) throws { 18 | let url = URL(fileURLWithPath: filePath) 19 | let document = try XMLDocument(contentsOf: url, options: []) 20 | try self.init(parsing: document) 21 | } 22 | 23 | public init(parsing document: XMLDocument) throws { 24 | guard let rootElement = document.rootElement(), rootElement.name == "FileList" else { 25 | fatalError() 26 | } 27 | self.targetPlatform = rootElement.attribute(forName: "TargetPlatform")!.stringValue! 28 | self.targetPlatformMinVersion = FourPartVersion(parsing: rootElement.attribute(forName: "TargetPlatformMinVersion")!.stringValue!)! 29 | self.targetPlatformVersion = FourPartVersion(parsing: rootElement.attribute(forName: "TargetPlatformVersion")!.stringValue!)! 30 | self.sdkType = rootElement.attribute(forName: "SDKType")!.stringValue! 31 | self.displayName = rootElement.attribute(forName: "DisplayName")!.stringValue! 32 | self.appliesTo = rootElement.attribute(forName: "AppliesTo")!.stringValue! 33 | self.productFamilyName = rootElement.attribute(forName: "ProductFamilyName")!.stringValue! 34 | self.apiContracts = [:] 35 | for apiContractElement in rootElement.singleElement(forName: "ContainedApiContracts")!.elements(forName: "ApiContract") { 36 | let name = apiContractElement.attribute(forName: "name")!.stringValue! 37 | let version = FourPartVersion(parsing: apiContractElement.attribute(forName: "version")!.stringValue!)! 38 | self.apiContracts[name] = version 39 | } 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /Sources/WindowsMetadata/XMLElement+singleElement.swift: -------------------------------------------------------------------------------- 1 | import FoundationXML 2 | 3 | extension XMLElement { 4 | internal func singleElement(forName name: String) -> XMLElement? { 5 | let elements = self.elements(forName: name) 6 | return elements.count == 1 ? elements[0] : nil 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Tests/DotNetMetadata/EnumTests.swift: -------------------------------------------------------------------------------- 1 | @testable import DotNetMetadata 2 | import XCTest 3 | 4 | internal final class EnumTests: CompiledAssemblyTestCase { 5 | internal override class var csharpCode: String { 6 | """ 7 | enum MyEnum { A = 1, B = 2 } 8 | [System.Flags] enum MyFlagsEnum { None = 0, A = 1 } 9 | enum MyShortEnum: short { A = 42 } 10 | """ 11 | } 12 | 13 | 14 | public func testEnumerantNames() throws { 15 | let enumDefinition = try XCTUnwrap(assembly.resolveTypeDefinition(fullName: "MyEnum") as? EnumDefinition) 16 | XCTAssertEqual(enumDefinition.fields.filter { $0.isStatic }.map { $0.name }.sorted(), ["A", "B"]) 17 | } 18 | 19 | public func testUnderlyingType() throws { 20 | XCTAssertEqual( 21 | try XCTUnwrap(assembly.resolveTypeDefinition(fullName: "MyEnum") as? EnumDefinition).underlyingType.fullName, 22 | "System.Int32") 23 | XCTAssertEqual( 24 | try XCTUnwrap(assembly.resolveTypeDefinition(fullName: "MyShortEnum") as? EnumDefinition).underlyingType.fullName, 25 | "System.Int16") 26 | } 27 | 28 | public func testEnumerantValues() throws { 29 | let enumDefinition = try XCTUnwrap(assembly.resolveTypeDefinition(fullName: "MyEnum") as? EnumDefinition) 30 | XCTAssertEqual(try XCTUnwrap(XCTUnwrap(enumDefinition.findField(name: "A")).literalValue), .int32(1)) 31 | XCTAssertEqual(try XCTUnwrap(XCTUnwrap(enumDefinition.findField(name: "B")).literalValue), .int32(2)) 32 | 33 | let shortEnumDefinition = try XCTUnwrap(assembly.resolveTypeDefinition(fullName: "MyShortEnum") as? EnumDefinition) 34 | XCTAssertEqual(try XCTUnwrap(XCTUnwrap(shortEnumDefinition.findField(name: "A")).literalValue), .int16(42)) 35 | } 36 | 37 | public func testIsFlags() throws { 38 | XCTAssertFalse(try XCTUnwrap(assembly.resolveTypeDefinition(fullName: "MyEnum") as? EnumDefinition).isFlags) 39 | XCTAssertTrue(try XCTUnwrap(assembly.resolveTypeDefinition(fullName: "MyFlagsEnum") as? EnumDefinition).isFlags) 40 | } 41 | } -------------------------------------------------------------------------------- /Tests/DotNetMetadata/EventTests.swift: -------------------------------------------------------------------------------- 1 | @testable import DotNetMetadata 2 | import XCTest 3 | 4 | internal final class EventTests: CompiledAssemblyTestCase { 5 | internal override class var csharpCode: String { 6 | """ 7 | delegate void Delegate(); 8 | class Events { 9 | public event Delegate PublicInstance; 10 | private static event Delegate PrivateStatic; 11 | } 12 | """ 13 | } 14 | 15 | private var delegateDefinition: DelegateDefinition! 16 | private var eventsClassDefinition: ClassDefinition! 17 | 18 | public override func setUpWithError() throws { 19 | try super.setUpWithError() 20 | delegateDefinition = try XCTUnwrap(assembly.resolveTypeDefinition(fullName: "Delegate") as? DelegateDefinition) 21 | eventsClassDefinition = try XCTUnwrap(assembly.resolveTypeDefinition(fullName: "Events") as? ClassDefinition) 22 | } 23 | 24 | public func testEnumeration() throws { 25 | XCTAssertEqual( 26 | eventsClassDefinition.events.map { $0.name }, 27 | ["PublicInstance", "PrivateStatic"]) 28 | } 29 | 30 | public func testAccessors() throws { 31 | let event = try XCTUnwrap(eventsClassDefinition.findEvent(name: "PublicInstance")) 32 | XCTAssertEqual(try XCTUnwrap(event.addAccessor).name, "add_PublicInstance") 33 | XCTAssertEqual(try XCTUnwrap(event.removeAccessor).name, "remove_PublicInstance") 34 | } 35 | 36 | public func testHandlerType() throws { 37 | let event = try XCTUnwrap(eventsClassDefinition.findEvent(name: "PublicInstance")) 38 | XCTAssertEqual(try event.handlerType, delegateDefinition.bind()) 39 | } 40 | } -------------------------------------------------------------------------------- /Tests/DotNetMetadata/NetFX45MscorlibTests+typeDefinitions.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import XCTest 3 | @testable import DotNetMetadata 4 | 5 | extension NetFX45MscorlibTests { 6 | func testTypeGenericParamEnumeration() throws { 7 | // Interface with 1 generic parameter 8 | XCTAssertEqual( 9 | try Self.assembly.resolveTypeDefinition(fullName: "System.Action`1")?.genericParams.map({ $0.name }), 10 | [ "T" ]) 11 | 12 | // Delegate with 2 generic parameters 13 | XCTAssertEqual( 14 | try Self.assembly.resolveTypeDefinition(fullName: "System.Action`2")?.genericParams.map({ $0.name }), 15 | [ "T1", "T2" ]) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Tests/DotNetMetadata/NetFX45MscorlibTests.swift: -------------------------------------------------------------------------------- 1 | @testable import DotNetMetadata 2 | import XCTest 3 | 4 | final class NetFX45MscorlibTests: XCTestCase { 5 | internal static var context: AssemblyLoadContext! 6 | internal static var assembly: Assembly! 7 | 8 | override class func setUp() { 9 | guard let mscorlibPath = SystemAssemblies.DotNetFramework4.mscorlibPath else { return } 10 | 11 | context = AssemblyLoadContext() 12 | assembly = try? context.load(path: mscorlibPath) 13 | } 14 | 15 | override func setUpWithError() throws { 16 | try XCTSkipIf(Self.assembly == nil) 17 | } 18 | 19 | internal var coreLibrary: CoreLibrary { get throws { try Self.context.coreLibrary } } 20 | 21 | func testTypeLookup() throws { 22 | XCTAssertNotNil(try Self.assembly.resolveTypeDefinition(fullName: "System.Object")) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Tests/DotNetMetadata/PropertyTests.swift: -------------------------------------------------------------------------------- 1 | @testable import DotNetMetadata 2 | import XCTest 3 | 4 | internal final class PropertyTests: CompiledAssemblyTestCase { 5 | internal override class var csharpCode: String { 6 | """ 7 | struct PropertyType {} 8 | abstract class Properties { 9 | public abstract PropertyType PublicAbstractInstanceGetSet { get; set; } 10 | private static PropertyType PrivateStaticGet { get { return new PropertyType(); } } 11 | } 12 | """ 13 | } 14 | 15 | private var typeDefinition: TypeDefinition! 16 | private var publicAbstractInstanceGetSetProperty: Property! 17 | private var privateStaticGetProperty: Property! 18 | 19 | public override func setUpWithError() throws { 20 | try super.setUpWithError() 21 | typeDefinition = try XCTUnwrap(assembly.resolveTypeDefinition(fullName: "Properties")) 22 | publicAbstractInstanceGetSetProperty = try XCTUnwrap(typeDefinition.findProperty(name: "PublicAbstractInstanceGetSet")) 23 | privateStaticGetProperty = try XCTUnwrap(typeDefinition.findProperty(name: "PrivateStaticGet")) 24 | } 25 | 26 | public override func tearDown() { 27 | typeDefinition = nil 28 | publicAbstractInstanceGetSetProperty = nil 29 | privateStaticGetProperty = nil 30 | super.tearDown() 31 | } 32 | 33 | public func testEnumeration() throws { 34 | XCTAssertEqual( 35 | typeDefinition.properties.map { $0.name }, 36 | ["PublicAbstractInstanceGetSet", "PrivateStaticGet"]) 37 | } 38 | 39 | public func testName() throws { 40 | XCTAssertEqual(publicAbstractInstanceGetSetProperty.name, "PublicAbstractInstanceGetSet") 41 | } 42 | 43 | public func testType() throws { 44 | try XCTAssertEqual( 45 | XCTUnwrap(publicAbstractInstanceGetSetProperty.type.asDefinition), 46 | XCTUnwrap(assembly.resolveTypeDefinition(fullName: "PropertyType"))) 47 | } 48 | 49 | public func testAccessors() throws { 50 | try XCTAssertNotNil(publicAbstractInstanceGetSetProperty.getter) 51 | try XCTAssertNotNil(publicAbstractInstanceGetSetProperty.setter) 52 | try XCTAssertNotNil(privateStaticGetProperty.getter) 53 | try XCTAssertNil(privateStaticGetProperty.setter) 54 | } 55 | } -------------------------------------------------------------------------------- /Tests/DotNetMetadata/ReferenceCycleTests.swift: -------------------------------------------------------------------------------- 1 | @testable import DotNetMetadata 2 | import XCTest 3 | 4 | internal final class ReferenceCycleTests: XCTestCase { 5 | public func testNoLeak() throws { 6 | var compilation: CSharpCompilation! = try CSharpCompilation(code: 7 | """ 8 | class Class { Class other; } 9 | """) 10 | 11 | weak var assembly = compilation.assembly 12 | weak var typeDefinition = try XCTUnwrap(XCTUnwrap(assembly).resolveTypeDefinition(fullName: "Class")) 13 | weak var field = try XCTUnwrap(XCTUnwrap(typeDefinition).findField(name: "other")) 14 | try XCTAssertEqual(XCTUnwrap(field).type, XCTUnwrap(typeDefinition).bindNode()) 15 | 16 | // Reference cycle established: TypeDefinition > Field > TypeNode > BoundType > TypeDefinition 17 | XCTAssertNotNil(typeDefinition) 18 | XCTAssertNotNil(field) 19 | 20 | withExtendedLifetime(compilation) {} 21 | compilation = nil 22 | 23 | XCTAssertNil(assembly) 24 | XCTAssertNil(typeDefinition) 25 | XCTAssertNil(field) 26 | } 27 | } -------------------------------------------------------------------------------- /Tests/DotNetMetadata/SystemAssemblyPathsTests.swift: -------------------------------------------------------------------------------- 1 | @testable import DotNetMetadata 2 | import XCTest 3 | import Foundation 4 | 5 | final class SystemAssemblyPathsTests: XCTestCase { 6 | func testFramework4MscorlibExists() throws { 7 | let path = try XCTUnwrap(SystemAssemblies.DotNetFramework4.mscorlibPath) 8 | XCTAssert(FileManager.default.fileExists(atPath: path)) 9 | } 10 | 11 | func testWindowsMetadataExists() throws { 12 | let path = try XCTUnwrap(SystemAssemblies.WinMetadata.windowsFoundationPath) 13 | XCTAssert(FileManager.default.fileExists(atPath: path)) 14 | } 15 | } -------------------------------------------------------------------------------- /Tests/DotNetMetadata/TypeNameTests.swift: -------------------------------------------------------------------------------- 1 | @testable import DotNetMetadata 2 | import XCTest 3 | 4 | internal final class TypeNameTests: CompiledAssemblyTestCase { 5 | internal override class var csharpCode: String { 6 | """ 7 | namespace Namespace.Nested { class Namespaced { class Nested {} } } 8 | class TopLevel {} 9 | class EnclosingGeneric { class NestedGeneric {} } 10 | """ 11 | } 12 | 13 | public func testNamespacedClass() throws { 14 | let classDefinition = try XCTUnwrap(assembly.resolveTypeDefinition(fullName: "Namespace.Nested.Namespaced")) 15 | XCTAssertEqual(classDefinition.name, "Namespaced") 16 | XCTAssertEqual(classDefinition.namespace, "Namespace.Nested") 17 | XCTAssertEqual(classDefinition.fullName, "Namespace.Nested.Namespaced") 18 | } 19 | 20 | public func testNestedClass() throws { 21 | let classDefinition = try XCTUnwrap(assembly.resolveTypeDefinition(fullName: "Namespace.Nested.Namespaced/Nested")) 22 | XCTAssertEqual(classDefinition.name, "Nested") 23 | XCTAssertEqual(classDefinition.namespace, nil) 24 | XCTAssertEqual(classDefinition.fullName, "Namespace.Nested.Namespaced/Nested") 25 | } 26 | 27 | public func testTopLevelClass() throws { 28 | let classDefinition = try XCTUnwrap(assembly.resolveTypeDefinition(fullName: "TopLevel")) 29 | XCTAssertEqual(classDefinition.name, "TopLevel") 30 | XCTAssertEqual(classDefinition.namespace, nil) 31 | XCTAssertEqual(classDefinition.fullName, "TopLevel") 32 | } 33 | 34 | public func testGeneric() throws { 35 | let enclosingClass = try XCTUnwrap(assembly.resolveTypeDefinition(fullName: "EnclosingGeneric`1")) 36 | XCTAssertEqual(enclosingClass.name, "EnclosingGeneric`1") 37 | 38 | let nestedClass = try XCTUnwrap(assembly.resolveTypeDefinition(fullName: "EnclosingGeneric`1/NestedGeneric`1")) 39 | XCTAssertEqual(nestedClass.name, "NestedGeneric`1") 40 | XCTAssertEqual(nestedClass.fullName, "EnclosingGeneric`1/NestedGeneric`1") 41 | } 42 | } -------------------------------------------------------------------------------- /Tests/DotNetMetadata/TypeTests.swift: -------------------------------------------------------------------------------- 1 | @testable import DotNetMetadata 2 | import XCTest 3 | 4 | /// Tests that the library is able to describe all kinds of types. 5 | internal final class TypeTests: CompiledAssemblyTestCase { 6 | internal override class var csharpCode: String { 7 | """ 8 | class Members 9 | { 10 | Struct DirectField; 11 | Struct[] ArrayField; 12 | unsafe Struct* PointerField; 13 | unsafe void* VoidPointerField; 14 | GenericClass GenericInstanceField; 15 | U ReturnMethodGenericParam() => default; 16 | } 17 | 18 | struct Struct {} 19 | 20 | class GenericClass 21 | { 22 | T TypeGenericParamField; 23 | } 24 | """ 25 | } 26 | 27 | private var membersTypeDefinition: TypeDefinition! 28 | private var structDefinition: TypeDefinition! 29 | private var genericClassDefinition: TypeDefinition! 30 | 31 | public override func setUpWithError() throws { 32 | try super.setUpWithError() 33 | membersTypeDefinition = try XCTUnwrap(assembly.resolveTypeDefinition(fullName: "Members")) 34 | structDefinition = try XCTUnwrap(assembly.resolveTypeDefinition(fullName: "Struct")) 35 | genericClassDefinition = try XCTUnwrap(assembly.resolveTypeDefinition(fullName: "GenericClass`1")) 36 | } 37 | 38 | public override func tearDown() { 39 | membersTypeDefinition = nil 40 | structDefinition = nil 41 | genericClassDefinition = nil 42 | super.tearDown() 43 | } 44 | 45 | public func testBoundType() throws { 46 | try XCTAssertEqual( 47 | XCTUnwrap(membersTypeDefinition.findField(name: "DirectField")).type, 48 | structDefinition.bindNode()) 49 | } 50 | 51 | public func testArray() throws { 52 | try XCTAssertEqual( 53 | XCTUnwrap(membersTypeDefinition.findField(name: "ArrayField")).type, 54 | .array(of: structDefinition.bindNode())) 55 | } 56 | 57 | public func testPointer() throws { 58 | try XCTAssertEqual( 59 | XCTUnwrap(membersTypeDefinition.findField(name: "PointerField")).type, 60 | .pointer(to: structDefinition.bindNode())) 61 | } 62 | 63 | public func testVoidPointer() throws { 64 | try XCTAssertEqual( 65 | XCTUnwrap(membersTypeDefinition.findField(name: "VoidPointerField")).type, 66 | .pointer(to: nil)) 67 | } 68 | 69 | public func testGenericInstance() throws { 70 | try XCTAssertEqual( 71 | XCTUnwrap(membersTypeDefinition.findField(name: "GenericInstanceField")).type, 72 | genericClassDefinition.bindNode(genericArgs: [ structDefinition.bindNode() ])) 73 | } 74 | 75 | public func testTypeGenericParams() throws { 76 | try XCTAssertEqual( 77 | XCTUnwrap(genericClassDefinition.findField(name: "TypeGenericParamField")).type, 78 | .genericParam(genericClassDefinition.genericParams[0])) 79 | } 80 | 81 | public func testMethodGenericParams() throws { 82 | let genericMethod = try XCTUnwrap(membersTypeDefinition.findMethod(name: "ReturnMethodGenericParam")) 83 | try XCTAssertEqual(genericMethod.returnType, .genericParam(genericMethod.genericParams[0])) 84 | } 85 | } -------------------------------------------------------------------------------- /Tests/DotNetMetadata/Utilities/CSharpCompilation.swift: -------------------------------------------------------------------------------- 1 | import DotNetMetadata 2 | import DotNetMetadataFormat 3 | import Foundation 4 | import WinSDK 5 | 6 | /// A class that compiles C# code and loads the resulting assembly. 7 | class CSharpCompilation { 8 | enum ToolNotFoundError: Error { 9 | case dotNetSDK 10 | case dotNetRuntime 11 | } 12 | 13 | struct CompilerError: Error, CustomStringConvertible { 14 | public var message: String 15 | 16 | public var description: String { message } 17 | } 18 | 19 | public let assemblyLoadContext: AssemblyLoadContext 20 | public let assembly: Assembly 21 | 22 | public init(code: String) throws { 23 | var tempPathChars = [UTF16.CodeUnit](repeating: 0, count: Int(MAX_PATH + 1)) 24 | GetTempPathW(DWORD(tempPathChars.count), &tempPathChars); 25 | var tempPath = String(decodingCString: tempPathChars, as: UTF16.self) 26 | if tempPath.hasSuffix("\\") { tempPath.removeLast() } 27 | 28 | let filenameWithoutExtension = UUID().uuidString 29 | let codeFilePath = "\(tempPath)\\\(filenameWithoutExtension).cs" 30 | let assemblyFilePath = "\(tempPath)\\\(filenameWithoutExtension).dll" 31 | try code.write(toFile: codeFilePath, atomically: false, encoding: .utf8) 32 | defer { try? FileManager.default.removeItem(atPath: codeFilePath) } 33 | 34 | guard let sdk = try DotNetTool.listSDKs().last else { throw ToolNotFoundError.dotNetSDK } 35 | guard let runtime = try DotNetTool.listRuntimes().last(where: { $0.name == "Microsoft.NETCore.App" }) else { 36 | throw ToolNotFoundError.dotNetRuntime 37 | } 38 | let refsPath = runtime.refsPath 39 | let result = try DotNetTool.exec( 40 | path: sdk.cscPath, 41 | args: CSharpCompilerArgs( 42 | nologo: true, nostdlib: true, optimize: false, debug: false, unsafe: true, target: .library, 43 | references: [ "\(refsPath)\\System.Runtime.dll" ], 44 | output: assemblyFilePath, sources: [codeFilePath]).buildCommandLineArgs()) 45 | guard result.exitCode == 0 else { throw CompilerError(message: result.standardOutput) } 46 | 47 | // Resolve the core library if tests require it 48 | assemblyLoadContext = AssemblyLoadContext(referenceResolver: { identity, flags in 49 | guard identity.name.starts(with: "System.") else { throw AssemblyLoadError.notFound(message: "Unexpected assembly reference.") } 50 | return try ModuleFile(path: "\(refsPath)\\\(identity.name).dll") 51 | }) 52 | 53 | assembly = try assemblyLoadContext.load(path: assemblyFilePath) 54 | } 55 | 56 | deinit { 57 | // TODO: Delete dll from temp dir 58 | } 59 | } -------------------------------------------------------------------------------- /Tests/DotNetMetadata/Utilities/CSharpCompilerArgs.swift: -------------------------------------------------------------------------------- 1 | import DotNetMetadata 2 | import Foundation 3 | import WinSDK 4 | 5 | internal struct CSharpCompilerArgs { 6 | enum Target { case library } 7 | 8 | public var nologo: Bool = false 9 | public var nostdlib: Bool? 10 | public var optimize: Bool? 11 | public var debug: Bool? 12 | public var unsafe: Bool? 13 | public var target: Target = .library 14 | public var references: [String] = [] 15 | public var output: String? 16 | public var sources: [String] = [] 17 | 18 | public func buildCommandLineArgs() -> [String] { 19 | var args = [String]() 20 | 21 | if nologo { args.append("-nologo") } 22 | if let nostdlib { args.append(nostdlib ? "-nostdlib+" : "-nostdlib-") } 23 | switch target { 24 | case .library: args.append("-target:library") 25 | } 26 | if let optimize { args.append(optimize ? "-optimize+" : "-optimize-") } 27 | if let debug { args.append(debug ? "-debug+" : "-debug-") } 28 | if let unsafe { args.append(unsafe ? "-unsafe+" : "-unsafe-") } 29 | for reference in references { args.append("-reference:\(reference)") } 30 | if let output { args.append("-out:\(output)") } 31 | for source in sources { args.append(source) } 32 | 33 | return args 34 | } 35 | } -------------------------------------------------------------------------------- /Tests/DotNetMetadata/Utilities/CompiledAssemblyTestCase.swift: -------------------------------------------------------------------------------- 1 | @testable import DotNetMetadata 2 | import DotNetMetadataFormat 3 | import XCTest 4 | import Foundation 5 | import WinSDK 6 | 7 | internal class CompiledAssemblyTestCase: XCTestCase { 8 | internal class var csharpCode: String { "" } 9 | 10 | private static var data: Result! 11 | 12 | internal var assemblyLoadContext: AssemblyLoadContext { try! Self.data.get().assemblyLoadContext } 13 | internal var assembly: Assembly { try! Self.data.get().assembly } 14 | 15 | public override class func setUp() { 16 | data = Result { try CSharpCompilation(code: csharpCode) } 17 | } 18 | 19 | public override func setUpWithError() throws { 20 | _ = try XCTUnwrap(Self.data).get() 21 | } 22 | 23 | public override class func tearDown() { 24 | data = nil 25 | } 26 | } -------------------------------------------------------------------------------- /Tests/DotNetMetadata/Utilities/DotNetTool.swift: -------------------------------------------------------------------------------- 1 | enum DotNetTool { 2 | struct Version { 3 | public var major: UInt 4 | public var minor: UInt 5 | public var patch: UInt 6 | 7 | public var string: String { "\(major).\(minor).\(patch)" } 8 | } 9 | 10 | struct SDK { 11 | public var version: Version 12 | public var path: String 13 | 14 | public var cscPath: String { "\(path)\\Roslyn\\bincore\\csc.dll" } 15 | } 16 | 17 | struct Runtime { 18 | public var name: String 19 | public var version: Version 20 | public var path: String 21 | 22 | public var refsPath: String { 23 | // The runtime has a path like dotnet\shared\Microsoft.NETCore.App\#.#.# 24 | // Reference assemblies are under dotnet\packs\Microsoft.NETCore.App.Ref\#.#.#\ref\net7.0 25 | var path = self.path.replacingOccurrences(of: "\\shared\\\(name)\\", with: "\\packs\\\(name).Ref\\") 26 | path += "\\ref\\net\(version.major).\(version.minor)" 27 | return path 28 | } 29 | } 30 | 31 | static func exec(path: String, args: [String]) throws -> Win32Process.Result { 32 | return try Win32Process.run(application: "dotnet", args: ["exec", path] + args) 33 | } 34 | 35 | static func listSDKs() throws -> [SDK] { 36 | let dotnetResult = try Win32Process.run(application: "dotnet", args: ["--list-sdks"]) 37 | guard dotnetResult.exitCode == 0 else { return [] } 38 | 39 | let lineRegex = try Regex(#"^(?\d+)\.(?\d+)\.(?\d+)\s+\[(?[^\]]+)\]\s*?$"#).anchorsMatchLineEndings() 40 | var searchFrom = dotnetResult.standardOutput.startIndex 41 | var sdks = [SDK]() 42 | while let lineMatch = try lineRegex.firstMatch(in: dotnetResult.standardOutput[searchFrom...]) { 43 | let version = Version( 44 | major: UInt(lineMatch["major"]!.substring!)!, 45 | minor: UInt(lineMatch["minor"]!.substring!)!, 46 | patch: UInt(lineMatch["patch"]!.substring!)!) 47 | sdks.append(SDK( 48 | version: version, 49 | path: lineMatch["path"]!.substring! + "\\" + version.string)) 50 | searchFrom = lineMatch.range.upperBound 51 | } 52 | 53 | return sdks 54 | } 55 | 56 | static func listRuntimes() throws -> [Runtime] { 57 | let dotnetResult = try Win32Process.run(application: "dotnet", args: ["--list-runtimes"]) 58 | guard dotnetResult.exitCode == 0 else { return [] } 59 | 60 | let lineRegex = try Regex(#"^(?[^\s]+)\s+(?\d+)\.(?\d+)\.(?\d+)\s+\[(?[^\]]+)\]\s*?$"#).anchorsMatchLineEndings() 61 | var searchFrom = dotnetResult.standardOutput.startIndex 62 | var runtimes = [Runtime]() 63 | while let lineMatch = try lineRegex.firstMatch(in: dotnetResult.standardOutput[searchFrom...]) { 64 | let version = Version( 65 | major: UInt(lineMatch["major"]!.substring!)!, 66 | minor: UInt(lineMatch["minor"]!.substring!)!, 67 | patch: UInt(lineMatch["patch"]!.substring!)!) 68 | runtimes.append(Runtime( 69 | name: String(lineMatch["name"]!.substring!), 70 | version: version, 71 | path: lineMatch["path"]!.substring! + "\\" + version.string)) 72 | searchFrom = lineMatch.range.upperBound 73 | } 74 | 75 | return runtimes 76 | } 77 | } -------------------------------------------------------------------------------- /Tests/DotNetMetadataFormat/AssemblyIdentityTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import DotNetMetadataFormat 3 | 4 | final class AssemblyIdentityTests: XCTestCase { 5 | func testToString() { 6 | let value = AssemblyIdentity( 7 | name: "mscorlib", 8 | version: .init(major: 2, minor: 0, buildNumber: 0, revisionNumber: 0), 9 | culture: "neutral", 10 | publicKey: .token([ 0xb7, 0x7a, 0x5c, 0x56, 0x19, 0x34, 0xe0, 0x89 ])) 11 | XCTAssertEqual(value.description, 12 | "mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089") 13 | } 14 | 15 | func testParse() throws { 16 | let value = try AssemblyIdentity.parse("mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089") 17 | XCTAssertEqual(value.name, "mscorlib") 18 | XCTAssertEqual(value.version, FourPartVersion(major: 2, minor: 0, buildNumber: 0, revisionNumber: 0)) 19 | XCTAssertEqual(value.culture, "neutral") 20 | XCTAssertEqual(value.publicKey, .token([ 0xb7, 0x7a, 0x5c, 0x56, 0x19, 0x34, 0xe0, 0x89 ])) 21 | } 22 | 23 | func testParsePublicKey() throws { 24 | XCTAssertEqual( 25 | try AssemblyIdentity.parse("name, PublicKey=1234").publicKey, 26 | .full([ 0x12, 0x34 ])) 27 | 28 | XCTAssertEqual( 29 | try AssemblyIdentity.parse("name, PublicKeyToken=b77a5c561934e089").publicKey, 30 | .token([ 0xb7, 0x7a, 0x5c, 0x56, 0x19, 0x34, 0xe0, 0x89 ])) 31 | 32 | XCTAssertNil(try AssemblyIdentity.parse("name").publicKey) 33 | XCTAssertNil(try AssemblyIdentity.parse("name, PublicKey=null").publicKey) 34 | XCTAssertNil(try AssemblyIdentity.parse("name, PublicKeyToken=null").publicKey) 35 | } 36 | } -------------------------------------------------------------------------------- /Tests/DotNetMetadataFormat/BinarySearchTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import DotNetMetadataFormat 3 | 4 | final class BinarySearchTests: XCTestCase { 5 | func testOddCount() { 6 | let list = [ "B", "C", "E" ] 7 | XCTAssertEqual(list.binarySearch(for: "A"), .absent(insertAt: 0)) 8 | XCTAssertEqual(list.binarySearch(for: "B"), .present(at: 0)) 9 | XCTAssertEqual(list.binarySearch(for: "C"), .present(at: 1)) 10 | XCTAssertEqual(list.binarySearch(for: "D"), .absent(insertAt: 2)) 11 | XCTAssertEqual(list.binarySearch(for: "E"), .present(at: 2)) 12 | XCTAssertEqual(list.binarySearch(for: "F"), .absent(insertAt: 3)) 13 | } 14 | 15 | func testEvenCount() { 16 | let list = [ "B", "D" ] 17 | XCTAssertEqual(list.binarySearch(for: "A"), .absent(insertAt: 0)) 18 | XCTAssertEqual(list.binarySearch(for: "B"), .present(at: 0)) 19 | XCTAssertEqual(list.binarySearch(for: "C"), .absent(insertAt: 1)) 20 | XCTAssertEqual(list.binarySearch(for: "D"), .present(at: 1)) 21 | XCTAssertEqual(list.binarySearch(for: "E"), .absent(insertAt: 2)) 22 | } 23 | 24 | func testMatchPreference() { 25 | let listAAB = [ "A", "A", "B" ] 26 | XCTAssertEqual(listAAB.binarySearch(for: "A", matchPreference: .first), .present(at: 0)) 27 | XCTAssertEqual(listAAB.binarySearch(for: "A", matchPreference: .last), .present(at: 1)) 28 | 29 | let listABB = [ "A", "B", "B" ] 30 | XCTAssertEqual(listABB.binarySearch(for: "B", matchPreference: .first), .present(at: 1)) 31 | XCTAssertEqual(listABB.binarySearch(for: "B", matchPreference: .last), .present(at: 2)) 32 | } 33 | 34 | func testRange() { 35 | let list = [ "A", "B", "B", "D" ] 36 | XCTAssertEqual(list.binarySearchRange(for: "B"), 1..<3) 37 | XCTAssertEqual(list.binarySearchRange(for: "C"), 3..<3) 38 | } 39 | } -------------------------------------------------------------------------------- /Tests/DotNetMetadataFormat/CompressedIntsTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import DotNetMetadataFormat 3 | 4 | final class CompressedIntsTests: XCTestCase { 5 | func testUnsigned() { 6 | func decompressUnsigned(_ bytes: UInt8...) -> UInt32? { 7 | bytes.withUnsafeBufferPointer { buffer in 8 | var remainder = UnsafeRawBufferPointer(buffer) 9 | let result = consumeCompressedUInt(buffer: &remainder) 10 | XCTAssertEqual(remainder.count, 0) 11 | return result 12 | } 13 | } 14 | 15 | XCTAssertEqual(decompressUnsigned(0x00), 0x00) 16 | 17 | // From §II.23.2 18 | XCTAssertEqual(decompressUnsigned(0x03), 0x03) 19 | XCTAssertEqual(decompressUnsigned(0x7F), 0x7F) 20 | XCTAssertEqual(decompressUnsigned(0x80, 0x80), 0x80) 21 | XCTAssertEqual(decompressUnsigned(0xAE, 0x57), 0x2E57) 22 | XCTAssertEqual(decompressUnsigned(0xBF, 0xFF), 0x3FFF) 23 | XCTAssertEqual(decompressUnsigned(0xC0, 0x00, 0x40, 0x00), 0x4000) 24 | XCTAssertEqual(decompressUnsigned(0xDF, 0xFF, 0xFF, 0xFF), 0x1FFF_FFFF) 25 | } 26 | 27 | func testSigned() { 28 | func decompressSigned(_ bytes: UInt8...) -> Int32? { 29 | bytes.withUnsafeBufferPointer { buffer in 30 | var remainder = UnsafeRawBufferPointer(buffer) 31 | let result = consumeCompressedInt(buffer: &remainder) 32 | XCTAssertEqual(remainder.count, 0) 33 | return result 34 | } 35 | } 36 | 37 | XCTAssertEqual(decompressSigned(0x00), 0x00) 38 | 39 | // From §II.23.2 40 | XCTAssertEqual(decompressSigned(0x06), 3) 41 | XCTAssertEqual(decompressSigned(0x7B), -3) 42 | XCTAssertEqual(decompressSigned(0x80, 0x80), 64) 43 | XCTAssertEqual(decompressSigned(0x01), -64) 44 | XCTAssertEqual(decompressSigned(0xC0, 0x00, 0x40, 0x00), 8192) 45 | XCTAssertEqual(decompressSigned(0x80, 0x01), -8192) 46 | XCTAssertEqual(decompressSigned(0xDF, 0xFF, 0xFF, 0xFE), 268435455) 47 | XCTAssertEqual(decompressSigned(0xC0, 0x00, 0x00, 0x01), -268435456) 48 | } 49 | } -------------------------------------------------------------------------------- /Tests/DotNetMetadataFormat/SignatureTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import DotNetMetadataFormat 3 | 4 | final class SignatureTests: XCTestCase { 5 | static func decodeCustomAttrib(_ bytes: [UInt8], paramTypes: [CustomAttribSig.ElemType]) throws -> CustomAttribSig { 6 | try bytes.withUnsafeBufferPointer { buffer in 7 | try CustomAttribSig( 8 | blob: UnsafeRawBufferPointer(buffer), 9 | paramTypes: paramTypes, 10 | memberTypeResolver: { _, _, _ in throw InvalidFormatError.signatureBlob }) 11 | } 12 | } 13 | 14 | func testCustomAttrib_byte() throws { 15 | let sig = try Self.decodeCustomAttrib( 16 | [ 17 | 0x01, 0x00, // Prolog 18 | 0x42, // Byte fixed arg 19 | 0x00, 0x00, // Num named args 20 | ], 21 | paramTypes: [ .integer(size: .int8, signed: true) ] 22 | ) 23 | 24 | XCTAssertEqual(sig.fixedArgs.count, 1) 25 | XCTAssertEqual(sig.namedArgs.count, 0) 26 | } 27 | } -------------------------------------------------------------------------------- /Tests/DotNetMetadataFormat/TableColumnSizeTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import DotNetMetadataFormat 3 | 4 | final class TableColumnSizeTests: XCTestCase { 5 | func testCodedIndexEnumTagBitCount() throws { 6 | enum TwoCaseCodedIndexTag: UInt8, CodedIndexTag { 7 | case case1, case2 8 | static let tables: [TableID?] = [ nil, nil ] 9 | init(value: UInt8) throws { fatalError() } 10 | } 11 | 12 | XCTAssertEqual(TwoCaseCodedIndexTag.bitCount, 1) 13 | 14 | enum ThreeCaseCodedIndexTag: UInt8, CodedIndexTag { 15 | case case1, case2, case3 16 | static let tables: [TableID?] = [ nil, nil, nil ] 17 | init(value: UInt8) throws { fatalError() } 18 | } 19 | 20 | XCTAssertEqual(ThreeCaseCodedIndexTag.bitCount, 2) 21 | 22 | enum FourCaseCodedIndexTag: UInt8, CodedIndexTag { 23 | case case1, case2, case3, case4 24 | static let tables: [TableID?] = [ nil, nil, nil, nil ] 25 | init(value: UInt8) throws { fatalError() } 26 | } 27 | 28 | XCTAssertEqual(FourCaseCodedIndexTag.bitCount, 2) 29 | } 30 | 31 | func testRowIndexSize() throws { 32 | func getRowIndexSize(rowCount: UInt32) -> Int { 33 | let tableID = TableID.typeDef 34 | var tableRowCounts = Array(repeating: UInt32(0), count: TableID.count) 35 | tableRowCounts[tableID.intValue] = rowCount 36 | return TableSizes(heapSizingBits: 0, tableRowCounts: tableRowCounts).getTableRowIndexSize(tableID) 37 | } 38 | 39 | XCTAssertEqual(getRowIndexSize(rowCount: 0), 2) 40 | XCTAssertEqual(getRowIndexSize(rowCount: 1), 2) 41 | XCTAssertEqual(getRowIndexSize(rowCount: 0x100), 2) 42 | XCTAssertEqual(getRowIndexSize(rowCount: 0x1000), 2) 43 | XCTAssertEqual(getRowIndexSize(rowCount: 0xFFFE), 2) 44 | XCTAssertEqual(getRowIndexSize(rowCount: 0x10001), 4) 45 | } 46 | 47 | func testAttributeEnumSize() throws { 48 | XCTAssertEqual(MemoryLayout.stride, MemoryLayout.stride) 49 | XCTAssertEqual(MemoryLayout.stride, 2) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Tests/DotNetXMLDocs/AssemblyDocumentationTests.swift: -------------------------------------------------------------------------------- 1 | @testable import DotNetXMLDocs 2 | import XCTest 3 | import FoundationXML 4 | 5 | final class AssemblyDocumentationTests: XCTestCase { 6 | func testParseFullDocument() throws { 7 | let xmlDocument = try XMLDocument(xmlString: #""" 8 | 9 | 10 | 11 | AssemblyName 12 | 13 | 14 | 15 | Summary 16 | 17 | 18 | Summary 19 | 20 | 21 | 22 | """#) 23 | 24 | let assemblyDocumentation = try AssemblyDocumentation(parsing: xmlDocument) 25 | 26 | XCTAssertEqual(assemblyDocumentation.assemblyName, "AssemblyName") 27 | XCTAssertEqual(assemblyDocumentation.members.count, 2) 28 | 29 | XCTAssertNotNil(assemblyDocumentation.members[.type(nameWithoutGenericArity: "TypeA")]) 30 | XCTAssertNotNil(assemblyDocumentation.members[.type(nameWithoutGenericArity: "TypeB")]) 31 | } 32 | } -------------------------------------------------------------------------------- /Tests/DotNetXMLDocs/DocumentationTypeNodeTests.swift: -------------------------------------------------------------------------------- 1 | @testable import DotNetXMLDocs 2 | import XCTest 3 | 4 | final class DocumentationTypeNodeTests: XCTestCase { 5 | func testParseBound() throws { 6 | XCTAssertEqual( 7 | try DocumentationTypeNode(parsing: "Name"), 8 | .bound(nameWithoutGenericArity: "Name")) 9 | } 10 | 11 | func testParseArray() throws { 12 | XCTAssertEqual( 13 | try DocumentationTypeNode(parsing: "Name[]"), 14 | .array(of: .bound(nameWithoutGenericArity: "Name"))) 15 | } 16 | 17 | func testParsePointer() throws { 18 | XCTAssertEqual( 19 | try DocumentationTypeNode(parsing: "Name*"), 20 | .pointer(to: .bound(nameWithoutGenericArity: "Name"))) 21 | } 22 | 23 | func testParseGenericParam() throws { 24 | XCTAssertEqual( 25 | try DocumentationTypeNode(parsing: "`0"), 26 | .genericParam(index: 0, kind: .type)) 27 | XCTAssertEqual( 28 | try DocumentationTypeNode(parsing: "``42"), 29 | .genericParam(index: 42, kind: .method)) 30 | } 31 | } -------------------------------------------------------------------------------- /Tests/DotNetXMLDocs/DocumentationTypeReferenceTests.swift: -------------------------------------------------------------------------------- 1 | @testable import DotNetXMLDocs 2 | import XCTest 3 | 4 | final class DocumentationTypeReferenceTests: XCTestCase { 5 | func testParseNonGeneric() throws { 6 | XCTAssertEqual( 7 | try DocumentationTypeReference(parsing: "Name"), 8 | .init(nameWithoutGenericArity: "Name")) 9 | } 10 | 11 | func testParseUnboundGeneric() throws { 12 | XCTAssertEqual( 13 | try DocumentationTypeReference(parsing: "Name`1"), 14 | .init(nameWithoutGenericArity: "Name", genericity: .unbound(arity: 1))) 15 | } 16 | 17 | func testParseBoundGeneric() throws { 18 | XCTAssertEqual( 19 | try DocumentationTypeReference(parsing: "Name`1{Name2}"), 20 | .init(nameWithoutGenericArity: "Name", genericArgs: [ .bound(nameWithoutGenericArity: "Name2") ])) 21 | } 22 | 23 | func testParseNamespaced() throws { 24 | XCTAssertEqual( 25 | try DocumentationTypeReference(parsing: "Namespace.Name"), 26 | .init(namespace: "Namespace", nameWithoutGenericArity: "Name")) 27 | } 28 | } -------------------------------------------------------------------------------- /Tests/DotNetXMLDocs/InstalledWindowsSDKTests.swift: -------------------------------------------------------------------------------- 1 | @testable import DotNetXMLDocs 2 | import XCTest 3 | import Foundation 4 | import FoundationXML 5 | 6 | final class InstalledWindowsSDKTests: XCTestCase { 7 | func testParseWindowsUniversalApiContract() throws { 8 | let programFilesX86Path = ProcessInfo.processInfo.environment["ProgramFiles(x86)"] 9 | ?? ((ProcessInfo.processInfo.environment["SystemDrive"] ?? "C:") + "\\Program Files (x86)") 10 | let windowsKits10Path = programFilesX86Path + "\\Windows Kits\\10" 11 | guard let referencesPath = Self.firstSubdirectory(parent: windowsKits10Path + "\\References"), 12 | let universalAPIContractPath = Self.firstSubdirectory(parent: referencesPath + "\\Windows.Foundation.UniversalApiContract"), 13 | let xmlString = try? String(contentsOf: URL(fileURLWithPath: universalAPIContractPath + "\\en\\Windows.Foundation.UniversalApiContract.xml")), 14 | let xmlDocument = try? XMLDocument(xmlString: xmlString) else { 15 | throw XCTSkip("Could not find an installed Windows SDK with Windows.Foundation.UniversalApiContract.xml") 16 | } 17 | 18 | _ = try AssemblyDocumentation(parsing: xmlDocument) 19 | } 20 | 21 | static func firstSubdirectory(parent: String) -> String? { 22 | do { 23 | return try FileManager.default.contentsOfDirectory(atPath: parent) 24 | .map { "\(parent)\\\($0)" } 25 | .first { 26 | var isDirectory: ObjCBool = false 27 | return FileManager.default.fileExists(atPath: $0, isDirectory: &isDirectory) && isDirectory.boolValue 28 | } 29 | } catch { 30 | return nil 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /Tests/DotNetXMLDocs/MemberDocumentationTests.swift: -------------------------------------------------------------------------------- 1 | @testable import DotNetXMLDocs 2 | import XCTest 3 | import FoundationXML 4 | 5 | final class MemberDocumentationTests: XCTestCase { 6 | private static func parse(xmlString: String) throws -> MemberDocumentation { 7 | let xmlDocument = try XMLDocument(xmlString: #""# + "\n" + xmlString) 8 | return MemberDocumentation(parsing: xmlDocument.rootElement()!) 9 | } 10 | 11 | func testParseFields() throws { 12 | let memberDocumentation = try Self.parse(xmlString: #""" 13 | 14 | Summary 15 | Remarks 16 | Value 17 | TypeParamDesc 18 | ParamDesc 19 | Returns 20 | Exception 21 | 22 | """#) 23 | 24 | XCTAssertEqual(memberDocumentation.summary, .plain("Summary")) 25 | XCTAssertEqual(memberDocumentation.remarks, .plain("Remarks")) 26 | XCTAssertEqual(memberDocumentation.value, .plain("Value")) 27 | XCTAssertEqual(memberDocumentation.typeParams, [.init(name: "TypeParamName", description: .plain("TypeParamDesc"))]) 28 | XCTAssertEqual(memberDocumentation.params, [.init(name: "ParamName", description: .plain("ParamDesc"))]) 29 | XCTAssertEqual(memberDocumentation.returns, .plain("Returns")) 30 | XCTAssertEqual(memberDocumentation.exceptions, [ 31 | .init(type: .init(nameWithoutGenericArity: "MyException"), description: .plain("Exception")) 32 | ]) 33 | } 34 | } -------------------------------------------------------------------------------- /Tests/WindowsMetadata/MidlNameManglingTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import WindowsMetadata 3 | 4 | final class MidlNameManglingTests: XCTestCase { 5 | func testObject() throws { 6 | XCTAssertEqual(MidlNameMangling.get(.object), "IInspectable") 7 | } 8 | 9 | func testWinRTType() throws { 10 | let typeName: WinRTTypeName = .declared(namespace: "Windows.Foundation", name: "Uri") 11 | XCTAssertEqual(MidlNameMangling.get(typeName), "__x_ABI_CWindows_CFoundation_CUri") 12 | } 13 | 14 | func testGenericOfWinRTType() throws { 15 | let typeName: WinRTTypeName = .parameterized(.collections_ivector, 16 | args: [ .declared(namespace: "Windows.Foundation", name: "Uri") ]) 17 | XCTAssertEqual(MidlNameMangling.get(typeName), "__FIVector_1_Windows__CFoundation__CUri") 18 | } 19 | 20 | func testGenericOfPrimitiveType() throws { 21 | let typeName: WinRTTypeName = .parameterized(.collections_ivector, 22 | args: [ .primitive(.string) ]) 23 | XCTAssertEqual(MidlNameMangling.get(typeName), "__FIVector_1_HSTRING") 24 | } 25 | 26 | func testGenericWithTwoArgs() throws { 27 | let typeName: WinRTTypeName = .parameterized(.collections_imap, 28 | args: [ .primitive(.string), .primitive(.string) ]) 29 | XCTAssertEqual(MidlNameMangling.get(typeName), "__FIMap_2_HSTRING_HSTRING") 30 | } 31 | 32 | func testNestedGenerics() throws { 33 | let typeName: WinRTTypeName = .parameterized(.iasyncOperation, 34 | args: [ .parameterized(.collections_ivectorView, args: [ .primitive(.string) ]) ]) 35 | XCTAssertEqual(MidlNameMangling.get(typeName), "__FIAsyncOperation_1___FIVectorView_1_HSTRING") 36 | } 37 | 38 | func testGenericDelegates() throws { 39 | // Should add an I prefix 40 | XCTAssertEqual( 41 | MidlNameMangling.get(WinRTTypeName.parameterized(.asyncOperationCompletedHandler, args: [ .primitive(.string) ])), 42 | "__FIAsyncOperationCompletedHandler_1_HSTRING") 43 | 44 | // Except for the collections changed event handlers 45 | XCTAssertEqual( 46 | MidlNameMangling.get(WinRTTypeName.parameterized(.collections_vectorChangedEventHandler, args: [ .primitive(.string) ])), 47 | "__FVectorChangedEventHandler_1_HSTRING") 48 | } 49 | } -------------------------------------------------------------------------------- /Tests/WindowsMetadata/SHA1Tests.swift: -------------------------------------------------------------------------------- 1 | @testable import WindowsMetadata 2 | import XCTest 3 | 4 | final class SHA1Tests: XCTestCase { 5 | func toHex(_ bytes: [UInt8]) -> String { 6 | bytes.map { String(format: "%02x", $0) }.joined() 7 | } 8 | 9 | func testValueOfEmpty() throws { 10 | XCTAssertEqual(toHex(SHA1.get([])), "da39a3ee5e6b4b0d3255bfef95601890afd80709") 11 | } 12 | 13 | func testValueOfSingleByte() throws { 14 | XCTAssertEqual(toHex(SHA1.get([0x42])), "ae4f281df5a5d0ff3cad6371f76d5c29b6d953ec") 15 | } 16 | 17 | func testValuesAroundPaddingLength() throws { 18 | // Pad with 0x80 0x00, then message length 19 | XCTAssertEqual( 20 | toHex(SHA1.get([UInt8](repeating: 0x42, count: SHA1.blockSize - 10))), 21 | "9f577f3425985e9b9ec5b11c4ed76675eb4a2aeb") 22 | // Pad with 0x80, then message length 23 | XCTAssertEqual( 24 | toHex(SHA1.get([UInt8](repeating: 0x42, count: SHA1.blockSize - 9))), 25 | "f42fc57c149118d6307f96b17acc00f19b4c8de7") 26 | // Pad with 0x80 + 0x00*64, then message length 27 | XCTAssertEqual( 28 | toHex(SHA1.get([UInt8](repeating: 0x42, count: SHA1.blockSize - 8))), 29 | "021f99328a6a79566f055914466ae1654d16ab01") 30 | } 31 | 32 | func testValueOfOneBlockPlusOneByte() throws { 33 | XCTAssertEqual( 34 | toHex(SHA1.get([UInt8](repeating: 0x42, count: 65))), 35 | "550fdc7cb0c34885cf8632c33c7057947578142b") 36 | } 37 | 38 | func testIntraBlockAppending() throws { 39 | let oneThenOneByte = { 40 | var sha1 = SHA1() 41 | sha1.process([0x00]) 42 | sha1.process([0x01]) 43 | return sha1.finalize() 44 | }() 45 | 46 | let twoBytes = SHA1.get([0x00, 0x01]) 47 | XCTAssertEqual(oneThenOneByte, twoBytes) 48 | } 49 | 50 | func testBlockSplitting() throws { 51 | let oneAndAHalfBlock = { 52 | var sha1 = SHA1() 53 | sha1.process([UInt8](repeating: 0, count: SHA1.blockSize * 3 / 2)) 54 | return sha1.finalize() 55 | }() 56 | 57 | let oneThenHalfBlock = { 58 | var sha1 = SHA1() 59 | sha1.process([UInt8](repeating: 0, count: SHA1.blockSize)) 60 | sha1.process([UInt8](repeating: 0, count: SHA1.blockSize / 2)) 61 | return sha1.finalize() 62 | }() 63 | 64 | let halfThenOneBlock = { 65 | var sha1 = SHA1() 66 | sha1.process([UInt8](repeating: 0, count: SHA1.blockSize / 2)) 67 | sha1.process([UInt8](repeating: 0, count: SHA1.blockSize)) 68 | return sha1.finalize() 69 | }() 70 | 71 | XCTAssertEqual(oneAndAHalfBlock, oneThenHalfBlock) 72 | XCTAssertEqual(oneAndAHalfBlock, halfThenOneBlock) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Tests/WindowsMetadata/WinMetadataTests.swift: -------------------------------------------------------------------------------- 1 | @testable import DotNetMetadata 2 | import WindowsMetadata 3 | import DotNetMetadataFormat 4 | import struct Foundation.UUID 5 | import class Foundation.Bundle 6 | import XCTest 7 | 8 | final class WinMetadataTests: XCTestCase { 9 | internal static var setUpError: Error? 10 | internal static var context: AssemblyLoadContext! 11 | internal static var mscorlib: Assembly! 12 | internal static var assembly: Assembly! 13 | 14 | override class func setUp() { 15 | do { 16 | context = WinMDLoadContext() 17 | 18 | // Expect mscorlib.winmd side-by-side with the test executable 19 | let mscorlibURL = Bundle.main.bundleURL.appendingPathComponent("mscorlib.winmd", isDirectory: false) 20 | mscorlib = try context.load(url: mscorlibURL) 21 | 22 | guard let windowsFoundationPath = SystemAssemblies.WinMetadata.windowsFoundationPath else { 23 | try XCTSkipIf(true, "System Windows.Foundation.winmd not found") 24 | return 25 | } 26 | 27 | assembly = try context.load(url: URL(fileURLWithPath: windowsFoundationPath)) 28 | } catch { 29 | setUpError = error 30 | XCTFail("Failed to set up test: \(error)") 31 | } 32 | } 33 | 34 | override func setUpWithError() throws { 35 | if let error = Self.setUpError { 36 | throw error 37 | } 38 | } 39 | 40 | override class func tearDown() { 41 | assembly = nil 42 | mscorlib = nil 43 | context = nil 44 | setUpError = nil 45 | } 46 | 47 | func testMscorlibTypeReference() throws { 48 | let pointTypeDefinition = try XCTUnwrap(Self.assembly.resolveTypeDefinition(fullName: "Windows.Foundation.Point")) 49 | XCTAssertEqual( 50 | try XCTUnwrap(pointTypeDefinition.base).definition.fullName, 51 | "System.ValueType") 52 | } 53 | 54 | func testParameterizedInterfaceID() throws { 55 | let iasyncOperation = try XCTUnwrap(Self.assembly.resolveTypeDefinition(fullName: "Windows.Foundation.IAsyncOperation`1") as? InterfaceDefinition) 56 | XCTAssertEqual( 57 | try WindowsMetadata.getInterfaceID(iasyncOperation, genericArgs: [try Self.context.coreLibrary.systemBoolean.bindNode()]), 58 | UUID(uuidString: "cdb5efb3-5788-509d-9be1-71ccb8a3362a")) 59 | } 60 | 61 | func testWinRTTypeNameFromType() throws { 62 | let imemoryReference = try XCTUnwrap(Self.assembly.resolveTypeDefinition(fullName: "Windows.Foundation.IMemoryBufferReference") as? InterfaceDefinition) 63 | let closedEvent = try XCTUnwrap(imemoryReference.findEvent(name: "Closed")) 64 | let typeName = try WinRTTypeName.from(type: closedEvent.handlerType.asBoundType) 65 | XCTAssertEqual(typeName.description, "Windows.Foundation.TypedEventHandler") 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Tests/WindowsMetadata/WinRTTypeSignatureTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import WindowsMetadata 3 | 4 | final class WinRTTypeSignatureTests: XCTestCase { 5 | public func testInterfaceID() throws { 6 | let ireferenceInterfaceID = UUID(uuidString: "61c17706-2d65-11e0-9ae8-d48564015472")! 7 | let signature: WinRTTypeSignature = .interface(id: ireferenceInterfaceID, args: [.baseType(.int32)]) 8 | XCTAssertEqual(signature.parameterizedID, UUID(uuidString: "548cefbd-bc8a-5fa0-8df2-957440fc8bf4")) // IReference 9 | } 10 | } -------------------------------------------------------------------------------- /Tests/WindowsMetadata/WindowsKitTests.swift: -------------------------------------------------------------------------------- 1 | @testable import WindowsMetadata 2 | import XCTest 3 | 4 | final class WindowsKitTests: XCTestCase { 5 | public func testReadApplicationPlatformXml() throws { 6 | let kits = try WindowsKit.getInstalled() 7 | try XCTSkipIf(kits.isEmpty, "No Windows Kits found") 8 | 9 | let applicationPlatform = try kits[0].readApplicationPlatform() 10 | XCTAssertNotNil(applicationPlatform.apiContracts["Windows.Foundation.UniversalApiContract"]) 11 | } 12 | 13 | public func testReadExtensionManifestXml() throws { 14 | let kits = try WindowsKit.getInstalled() 15 | try XCTSkipIf(kits.isEmpty, "No Windows Kits found") 16 | 17 | let desktopExtension = try XCTUnwrap(kits[0].extensions.first { $0.name == "WindowsDesktop" }) 18 | let manifest = try desktopExtension.readManifest() 19 | XCTAssertEqual(manifest.productFamilyName, "Windows.Desktop") 20 | } 21 | } -------------------------------------------------------------------------------- /WindowsMetadataCoreLibrary/Assemble.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding(PositionalBinding=$false)] 2 | param( 3 | [Parameter(Mandatory=$false)] 4 | [string] $SourcePath = "", 5 | [Parameter(Mandatory=$true)] 6 | [string] $OutputPath 7 | ) 8 | 9 | if (!$SourcePath) { 10 | $SourcePath = "$PSScriptRoot\\mscorlib.il" 11 | } 12 | 13 | & "$Env:windir\\Microsoft.NET\\Framework64\\v4.0.30319\\ilasm.exe" ` 14 | /nologo /quiet ` 15 | /noautoinherit /dll ` 16 | /output=$OutputPath ` 17 | $SourcePath -------------------------------------------------------------------------------- /WindowsMetadataCoreLibrary/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_custom_command( 2 | OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/mscorlib.winmd" 3 | COMMAND powershell.exe -File "${CMAKE_CURRENT_SOURCE_DIR}/Assemble.ps1" 4 | -SourcePath "${CMAKE_CURRENT_SOURCE_DIR}/mscorlib.il" 5 | -OutputPath "${CMAKE_CURRENT_BINARY_DIR}/mscorlib.winmd" 6 | DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/mscorlib.il") 7 | add_custom_target(mscorlib_winmd ALL 8 | DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/mscorlib.winmd") --------------------------------------------------------------------------------