-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Decoupled parameterized interface ID generation from BoundTypes.
- Loading branch information
1 parent
f37f88e
commit 91047f4
Showing
6 changed files
with
229 additions
and
109 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
72 changes: 72 additions & 0 deletions
72
Sources/WindowsMetadata/WinRTTypeSignature+fromBoundType.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import DotNetMetadata | ||
|
||
extension WinRTTypeSignature { | ||
public init(_ type: BoundType) throws { | ||
self = try WinRTTypeSignature.fromBoundType(type) | ||
} | ||
|
||
private static func fromBoundType(_ type: BoundType) throws -> WinRTTypeSignature { | ||
if type.genericArgs.count > 0 { | ||
let id = try type.definition.findAttribute(GuidAttribute.self)! | ||
let args = try type.genericArgs.map { | ||
guard case .bound(let arg) = $0 else { | ||
throw UnexpectedTypeError($0.description, context: "WinRT generic argument", reason: "Not a bound type") | ||
} | ||
return try fromBoundType(arg) | ||
} | ||
return type.definition is DelegateDefinition | ||
? .delegate(id: id, args: args) | ||
: .interface(id: id, args: args) | ||
} | ||
|
||
if type.definition.namespace == "System" { | ||
switch type.definition.name { | ||
case "Boolean": return .baseType(.boolean) | ||
case "Byte": return .baseType(.uint8) | ||
case "Int16": return .baseType(.int16) | ||
case "UInt16": return .baseType(.uint16) | ||
case "Int32": return .baseType(.int32) | ||
case "UInt32": return .baseType(.uint32) | ||
case "Int64": return .baseType(.int64) | ||
case "UInt64": return .baseType(.uint64) | ||
case "Single": return .baseType(.single) | ||
case "Double": return .baseType(.double) | ||
case "Char": return .baseType(.char16) | ||
case "String": return .baseType(.string) | ||
case "Guid": return .baseType(.guid) | ||
case "Object": return .comInterface | ||
default: throw UnexpectedTypeError(type.definition.fullName, reason: "Not a well-known WinRT System type") | ||
} | ||
} | ||
|
||
switch type.definition { | ||
case is StructDefinition: | ||
let fields = try type.definition.fields.map { | ||
let type = try $0.type | ||
guard case .bound(let arg) = type else { | ||
throw UnexpectedTypeError(type.description, context: "WinRT field", reason: "Not a bound type") | ||
} | ||
return try fromBoundType(arg) | ||
} | ||
return .struct(name: type.definition.fullName, fields: fields) | ||
|
||
case is EnumDefinition: | ||
return .enum(name: type.definition.fullName, flags: try type.definition.hasAttribute(FlagsAttribute.self)) | ||
|
||
case is InterfaceDefinition: | ||
let id = try type.definition.findAttribute(GuidAttribute.self)! | ||
return .interface(id: id) | ||
|
||
case is DelegateDefinition: | ||
let id = try type.definition.findAttribute(GuidAttribute.self)! | ||
return .delegate(id: id) | ||
|
||
case let classDefinition as ClassDefinition: | ||
let defaultInterface = try DefaultAttribute.getDefaultInterface(classDefinition)! | ||
return .runtimeClass(name: classDefinition.fullName, defaultInterface: try fromBoundType(defaultInterface.asBoundType)) | ||
|
||
default: | ||
fatalError("Unexpected type definition: \(type.definition)") | ||
} | ||
} | ||
} |
32 changes: 32 additions & 0 deletions
32
Sources/WindowsMetadata/WinRTTypeSignature+parameterizedID.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import struct Foundation.UUID | ||
|
||
// https://learn.microsoft.com/en-us/uwp/winrt-cref/winrt-type-system#guid-generation-for-parameterized-types | ||
fileprivate let parameterizedInterfaceGuidBytes: [UInt8] = [ | ||
0x11, 0xf4, 0x7a, 0xd5, | ||
0x7b, 0x73, | ||
0x42, 0xc0, | ||
0xab, 0xae, 0x87, 0x8b, 0x1e, 0x16, 0xad, 0xee | ||
]; | ||
|
||
extension WinRTTypeSignature { | ||
public var parameterizedID: UUID { | ||
get { | ||
switch self { | ||
case let .interface(_, args), let .delegate(_, args): precondition(!args.isEmpty) | ||
default: preconditionFailure("Only interfaces and delegates have parameterized IDs") | ||
} | ||
|
||
var sha1 = SHA1() | ||
sha1.process(parameterizedInterfaceGuidBytes) | ||
sha1.process(Array(self.toString().utf8)) | ||
let hash = sha1.finalize() | ||
|
||
return UUID(uuid: ( | ||
hash[0], hash[1], hash[2], hash[3], | ||
hash[4], hash[5], | ||
(hash[6] & 0x0F) | 0x50, hash[7], | ||
(hash[8] & 0x3F) | 0x80, hash[9], | ||
hash[10], hash[11], hash[12], hash[13], hash[14], hash[15])) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
import struct Foundation.UUID | ||
|
||
extension WinRTTypeSignature { | ||
public func toString() -> String { | ||
var string = "" | ||
appendString(&string) | ||
return string | ||
} | ||
|
||
public func appendString(_ string: inout String) { | ||
switch self { | ||
case let .interface(id, args): | ||
if args.isEmpty { | ||
appendGuid(id, to: &string) | ||
} | ||
else { | ||
appendParameterizedInterface(id, args: args, to: &string) | ||
} | ||
|
||
case let .delegate(id, args): | ||
if args.isEmpty { | ||
string.append("delegate(") | ||
appendGuid(id, to: &string) | ||
string.append(")") | ||
} | ||
else { | ||
appendParameterizedInterface(id, args: args, to: &string) | ||
} | ||
|
||
case let .baseType(baseType): | ||
string.append(baseType.rawValue) | ||
|
||
case .comInterface: | ||
string.append("cinterface(IInspectable)") | ||
|
||
case let .interfaceGroup(name, defaultInterface): | ||
string.append("ig(") | ||
string.append(name) | ||
string.append(";") | ||
defaultInterface.appendString(&string) | ||
string.append(")") | ||
|
||
case let .runtimeClass(name, defaultInterface): | ||
string.append("rc(") | ||
string.append(name) | ||
string.append(";") | ||
defaultInterface.appendString(&string) | ||
string.append(")") | ||
|
||
case let .struct(name, fields): | ||
string.append("struct(") | ||
string.append(name) | ||
for field in fields { | ||
string.append(";") | ||
field.appendString(&string) | ||
} | ||
string.append(")") | ||
|
||
case let .enum(name, flags): | ||
string.append("enum(") | ||
string.append(name) | ||
string.append(";") | ||
string.append(flags ? "u4" : "i4") | ||
string.append(")") | ||
} | ||
} | ||
} | ||
|
||
fileprivate func appendParameterizedInterface(_ id: UUID, args: [WinRTTypeSignature], to string: inout String) { | ||
string.append("pinterface(") | ||
appendGuid(id, to: &string) | ||
for arg in args { | ||
string.append(";") | ||
arg.appendString(&string) | ||
} | ||
string.append(")") | ||
} | ||
|
||
fileprivate func appendGuid(_ guid: UUID, to string: inout String) { | ||
string.append("{") | ||
string.append(guid.uuidString.lowercased()) | ||
string.append("}") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import struct Foundation.UUID | ||
|
||
/// A WinRT type signature, used to generate GUIDs for parameterized interface and delegate types. | ||
/// See https://learn.microsoft.com/en-us/uwp/winrt-cref/winrt-type-system#guid-generation-for-parameterized-types | ||
public enum WinRTTypeSignature: Hashable { | ||
case interface(id: UUID, args: [WinRTTypeSignature] = []) | ||
case delegate(id: UUID, args: [WinRTTypeSignature] = []) | ||
case baseType(BaseType) | ||
case comInterface | ||
indirect case interfaceGroup(name: String, default: WinRTTypeSignature) | ||
indirect case runtimeClass(name: String, defaultInterface: WinRTTypeSignature) | ||
case `struct`(name: String, fields: [WinRTTypeSignature]) | ||
case `enum`(name: String, flags: Bool) | ||
|
||
public enum BaseType: String, Hashable { | ||
case boolean = "b1" | ||
case uint8 = "u1" | ||
case int16 = "i2" // Undocumented but presumably valid | ||
case uint16 = "u2" // Undocumented but presumably valid | ||
case int32 = "i4" | ||
case uint32 = "u4" | ||
case int64 = "i8" | ||
case uint64 = "u8" | ||
case single = "f4" | ||
case double = "f8" | ||
case char16 = "c2" | ||
case string = "string" | ||
case guid = "g16" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import XCTest | ||
import WindowsMetadata | ||
|
||
final class WinRTTypeSignatureTests: XCTestCase { | ||
public func testInterfaceID() throws { | ||
let ireferenceInterfaceID = UUID(uuidString: "61c17706-2d65-11e0-9ae8-d48564015472")! | ||
let signature: WinRTTypeSignature = .interface(id: ireferenceInterfaceID, args: [.baseType(.int32)]) | ||
XCTAssertEqual(signature.parameterizedID, UUID(uuidString: "548cefbd-bc8a-5fa0-8df2-957440fc8bf4")) // IReference<Int32> | ||
} | ||
} |