Skip to content

Commit

Permalink
Change property naming scheme (#376)
Browse files Browse the repository at this point in the history
  • Loading branch information
tristanlabelle authored Nov 1, 2024
1 parent 100dbcb commit bafdacd
Show file tree
Hide file tree
Showing 34 changed files with 250 additions and 271 deletions.
10 changes: 5 additions & 5 deletions Docs/Projection Design.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,17 +80,17 @@ Given a `getObject() -> Base` that actually returns a `Derived`, there is opt-in

## Members
### Properties and accessors
Properties are generated as both throwing accessors methods and nonthrowing properties (returning implicitly unwrapped optionals).
Properties are generated one throwing getter-only property, one throwing setter func, and one suffixed nonthrowing get-set property (when relevant).

**Rationale**: Swift does not support throwing property setters. This is a compromise between exposing errors from property accessors and supporting the convenient property syntax.
**Rationale**: Swift does not support throwing property setters. This compromise supports a natural syntax for reading properties while handling errors, a reasonable way for writing properties while handling errors, and a way to use the property setter syntax (with some friction to point out that exceptions cannot be handled).

**Example**:
```swift
func _myProperty() throws -> IClosable // Getter
func _myProperty(_ value: IClosable) throws // Setter
var myProperty: IClosable { get throws } // Getter
func myProperty(_ value: IClosable) throws // Setter

extension {
var myProperty: IClosable! { get set } // Nonthrowing property
var myProperty_: IClosable! { get set } // Nonthrowing property
}
```

Expand Down
5 changes: 2 additions & 3 deletions Generator/Sources/ProjectionModel/Projection+conversion.swift
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,12 @@ extension Projection {

public static func toMemberName(_ member: Member) -> String {
let name = member.name

if member is Method, member.nameKind == .special {
if let prefixEndIndex = name.findPrefixEndIndex("get_")
?? name.findPrefixEndIndex("set_")
?? name.findPrefixEndIndex("put_") {
// get_Foo() -> _foo()
return "_" + Casing.pascalToCamel(String(name[prefixEndIndex...]))
// get_Foo() -> foo
return Casing.pascalToCamel(String(name[prefixEndIndex...]))
}
else if let prefixEndIndex = name.findPrefixEndIndex("add_")
?? name.findPrefixEndIndex("remove_") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,23 +27,25 @@ internal class SequenceIterator<I: IteratorProtocol>: WinRTPrimaryExport<IInspec
typealias T = I.Element

private var iterator: I
private var current: T?
private var _current: T?

init(_ iterator: I) {
self.iterator = iterator
self.current = self.iterator.next()
self._current = self.iterator.next()
}

func _hasCurrent() throws -> Bool { current != nil }
var hasCurrent: Bool { get throws { _current != nil } }

func _current() throws -> T {
guard let current else { throw COMError.illegalMethodCall }
return current
var current: T {
get throws {
guard let _current else { throw COMError.illegalMethodCall }
return _current
}
}

func moveNext() throws -> Bool {
current = iterator.next()
return current != nil
_current = iterator.next()
return _current != nil
}

func getMany(_ items: [I.Element]) throws -> UInt32 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,7 @@ fileprivate class CollectionVectorView<C: Collection>: WinRTPrimaryExport<IInspe
self.elementEquals = elementEquals
}

public func _size() throws -> UInt32 {
UInt32(collection.count)
}
public var size: UInt32 { get throws { UInt32(collection.count) } }

public func first() throws -> WindowsFoundationCollections_IIterator<T> {
SequenceIterator(collection.makeIterator())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,7 @@ public class ArrayVector<T>: WinRTPrimaryExport<IInspectableBinding>,
self.elementEquals = elementEquals
}

public func _size() throws -> UInt32 {
UInt32(array.count)
}
public var size: UInt32 { get throws { UInt32(array.count) } }

public func first() throws -> WindowsFoundationCollections_IIterator<T> {
SequenceIterator(array.makeIterator())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import WindowsRuntime

extension WindowsFoundation_IAsyncActionWithProgressProtocol {
public func get() async throws {
if try _status() == .started {
if try self.status == .started {
// We can't await if the completed handler is already set
guard try COM.NullResult.catch(_completed()) == nil else { throw COM.COMError.illegalMethodCall }
guard try COM.NullResult.catch(self.completed) == nil else { throw COM.COMError.illegalMethodCall }
let awaiter = WindowsRuntime.AsyncAwaiter()
try _completed({ _, _ in _Concurrency.Task { await awaiter.signal() } })
try self.completed { _, _ in _Concurrency.Task { await awaiter.signal() } }
await awaiter.wait()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import WindowsRuntime

extension WindowsFoundation_IAsyncActionProtocol {
public func get() async throws {
if try _status() == .started {
if try self.status == .started {
// We can't await if the completed handler is already set
guard try COM.NullResult.catch(_completed()) == nil else { throw COM.COMError.illegalMethodCall }
guard try COM.NullResult.catch(self.completed) == nil else { throw COM.COMError.illegalMethodCall }
let awaiter = WindowsRuntime.AsyncAwaiter()
try _completed({ _, _ in _Concurrency.Task { await awaiter.signal() } })
try self.completed { _, _ in _Concurrency.Task { await awaiter.signal() } }
await awaiter.wait()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import WindowsRuntime

extension WindowsFoundation_IAsyncOperationWithProgressProtocol {
public func get() async throws -> TResult {
if try _status() == .started {
if try self.status == .started {
// We can't await if the completed handler is already set
guard try COM.NullResult.catch(_completed()) == nil else { throw COM.COMError.illegalMethodCall }
guard try COM.NullResult.catch(self.completed) == nil else { throw COM.COMError.illegalMethodCall }
let awaiter = WindowsRuntime.AsyncAwaiter()
try _completed({ _, _ in _Concurrency.Task { await awaiter.signal() } })
try self.completed { _, _ in _Concurrency.Task { await awaiter.signal() } }
await awaiter.wait()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import WindowsRuntime

extension WindowsFoundation_IAsyncOperationProtocol {
public func get() async throws -> TResult {
if try _status() == .started {
if try self.status == .started {
// We can't await if the completed handler is already set
guard try COM.NullResult.catch(_completed()) == nil else { throw COM.COMError.illegalMethodCall }
guard try COM.NullResult.catch(self.completed) == nil else { throw COM.COMError.illegalMethodCall }
let awaiter = WindowsRuntime.AsyncAwaiter()
try _completed({ _, _ in _Concurrency.Task { await awaiter.signal() } })
try self.completed { _, _ in _Concurrency.Task { await awaiter.signal() } }
await awaiter.wait()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import WindowsRuntime
extension WindowsStorageStreams_Buffer {
public convenience init(_ bytes: [UInt8]) throws {
try self.init(UInt32(bytes.count))
try self._length(UInt32(bytes.count))
try self.length(UInt32(bytes.count))
let byteAccess = try self._queryInterface(IBufferByteAccessBinding.self)
let bufferPointer = try UnsafeMutableBufferPointer(start: byteAccess.interop.buffer(), count: bytes.count)
_ = bufferPointer.update(fromContentsOf: bytes)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ extension WindowsStorageStreams_IBufferProtocol {

extension Array where Element == UInt8 {
public init(_ buffer: WindowsStorageStreams_IBuffer) throws {
self.init(try UnsafeBufferPointer(start: buffer.bytes, count: Int(buffer._length())))
self.init(try UnsafeBufferPointer(start: buffer.bytes, count: Int(buffer.length)))
}
}
4 changes: 2 additions & 2 deletions Generator/Sources/SwiftWinRT/Writing/COMImportClass.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ internal func writeCOMImportClass(
try writeGenericTypeAliases(interfaces: interfaces, projection: projection, to: writer)

// Primary interface implementation
try writeInterfaceImplementation(
try writeMemberDefinitions(
abiType: type, documentation: false, thisPointer: .init(name: "_interop"),
projection: projection, to: writer)

Expand All @@ -56,7 +56,7 @@ internal func writeCOMImportClass(
for secondaryInterface in secondaryInterfaces {
let secondaryInterfaceName = try WinRTTypeName.from(type: secondaryInterface.asBoundType).description
writer.writeMarkComment("\(secondaryInterfaceName) members")
try writeInterfaceImplementation(
try writeMemberDefinitions(
abiType: secondaryInterface.asBoundType, documentation: false,
thisPointer: .init(name: SecondaryInterfaces.getPropertyName(secondaryInterface), lazy: true),
projection: projection, to: writer)
Expand Down
6 changes: 3 additions & 3 deletions Generator/Sources/SwiftWinRT/Writing/ClassDefinition.swift
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ fileprivate func writeInterfaceImplementations(
let thisPointer: ThisPointer = try classDefinition.isSealed && getRuntimeClassBase(classDefinition) == nil
? .init(name: "_interop")
: .init(name: SecondaryInterfaces.getPropertyName(defaultInterface), lazy: true)
try writeInterfaceImplementation(
try writeMemberDefinitions(
abiType: defaultInterface.asBoundType, classDefinition: classDefinition,
thisPointer: thisPointer, projection: projection, to: writer)
}
Expand All @@ -186,7 +186,7 @@ fileprivate func writeInterfaceImplementations(
try writeMarkComment(forInterface: secondaryInterface.interface, to: writer)
}

try writeInterfaceImplementation(
try writeMemberDefinitions(
abiType: secondaryInterface.interface.asBoundType,
classDefinition: classDefinition,
overridable: secondaryInterface.overridable,
Expand All @@ -201,7 +201,7 @@ fileprivate func writeInterfaceImplementations(
try writeMarkComment(forInterface: staticInterface.bind(), to: writer)
}

try writeInterfaceImplementation(
try writeMemberDefinitions(
abiType: staticInterface.bindType(), classDefinition: classDefinition, static: true,
thisPointer: .init(name: SecondaryInterfaces.getPropertyName(staticInterface.bind()), lazy: true),
projection: projection, to: writer)
Expand Down
68 changes: 0 additions & 68 deletions Generator/Sources/SwiftWinRT/Writing/ExtensionProperties.swift

This file was deleted.

37 changes: 25 additions & 12 deletions Generator/Sources/SwiftWinRT/Writing/InterfaceDefinition.swift
Original file line number Diff line number Diff line change
Expand Up @@ -104,33 +104,31 @@ fileprivate func writeProtocol(_ interfaceDefinition: InterfaceDefinition, proje
}
}

// Write properties as "_getFoo() throws -> T" and "_setFoo(newValue: T) throws",
// to provide a way to handle errors.
// We'll generate non-throwing "var foo: T { get set }" as an extension.
// Write properties as "var foo: T { get throws}" and "func setFoo(_ newValue: T) throws",
// to provide a way to handle errors (Swift does not support throwing settable properties)
// We'll generate non-throwing "var foo_: T! { get set }" as an extension.
for property in interfaceDefinition.properties {
if let getter = try property.getter {
try writer.writeFunc(
try writer.writeProperty(
documentation: projection.getDocumentationComment(property, accessor: .getter),
name: Projection.toMemberName(getter),
throws: true,
returnType: projection.toReturnType(property.type))
name: Projection.toMemberName(property),
type: projection.toReturnType(getter.returnType),
throws: true)
}

if let setter = try property.setter {
try writer.writeFunc(
groupAsProperty: true,
documentation: projection.getDocumentationComment(property, accessor: .setter),
name: Projection.toMemberName(setter),
name: Projection.toMemberName(property),
params: setter.params.map { try projection.toParameter($0) },
throws: true)
}
}
}

// Write fatalError'ing properties as an extension
try writeExtensionProperties(
typeDefinition: interfaceDefinition, interfaces: [interfaceDefinition], static: false,
projection: projection, to: writer)
// Write non-throwing properties as an extension
try writeNonthrowingPropertiesExtension(interfaceDefinition, projection: projection, to: writer)
}

fileprivate func writeProtocolTypeAlias(_ interfaceDefinition: InterfaceDefinition, projection: Projection, to writer: SwiftSourceFileWriter) throws {
Expand All @@ -144,3 +142,18 @@ fileprivate func writeProtocolTypeAlias(_ interfaceDefinition: InterfaceDefiniti
name: try projection.toProtocolName(interfaceDefinition),
genericArgs: interfaceDefinition.genericParams.map { .identifier(name: $0.name) }))
}

fileprivate func writeNonthrowingPropertiesExtension(
_ interfaceDefinition: InterfaceDefinition, projection: Projection, to writer: SwiftSourceFileWriter) throws {
// Only write the extension if we have at least one property having both a getter and setter
let getSetProperties = try interfaceDefinition.properties.filter { try $0.getter != nil && $0.setter != nil }
guard !getSetProperties.isEmpty else { return }

let typeName: String = try projection.toProtocolName(interfaceDefinition)
try writer.writeExtension(type: .identifier(typeName)) { writer in
for property in getSetProperties {
try writeNonthrowingPropertyImplementation(
property: property, static: false, projection: projection, to: writer)
}
}
}
Loading

0 comments on commit bafdacd

Please sign in to comment.