Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 22 additions & 6 deletions Sources/StateStruct/Source.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,10 @@ public macro WeakTrackingProperty() =
)
@attached(peer, names: prefixed(`_backing_`))
public macro PrimitiveTrackingProperty() =
#externalMacro(module: "StateStructMacros", type: "PrimitiveTrackingPropertyMacro")

#externalMacro(module: "StateStructMacros", type: "PrimitiveTrackingPropertyMacro")

#if DEBUG


@Tracking
struct OptinalPropertyState {

Expand Down Expand Up @@ -101,19 +99,37 @@ public macro PrimitiveTrackingProperty() =
stored_1
}

var subState: MySubState = .init()
var subState: MyTrackingSubState = .init()

var noTrackingSubState: MyNoTrackingSubState = .init()

}

@Tracking
struct MySubState {
struct MyTrackingSubState {

var stored_1: Int = 18

var computed_1: Int {
stored_1
}


weak var weak_stored: Ref?

init() {

}

}

struct MyNoTrackingSubState {

var stored_1: Int = 18

var computed_1: Int {
stored_1
}

weak var weak_stored: Ref?

init() {
Expand Down
197 changes: 197 additions & 0 deletions Sources/StateStruct/Tracking.swift
Original file line number Diff line number Diff line change
@@ -1,2 +1,199 @@
import Foundation
import os.lock

public enum Tracking {

/// no tracking
@usableFromInline
static func dynamic_processGet<Value>(
component: consuming PropertyPath.Component,
value: Value,
trackingContext: borrowing _TrackingContext
) -> Value {

trackingContext.trackingResultRef?.accessorRead(path: trackingContext.path?.pushed(component))

if var value = value as? TrackingObject, let ref = trackingContext.trackingResultRef {

if value._tracking_context.trackingResultRef !== ref {
value._tracking_context = _TrackingContext(trackingResultRef: ref)
}

value._tracking_context.path = trackingContext.path?.pushed(component)

return value as! Value
} else {
return value
}

}

/// no tracking
@inlinable
public static func processGet<Value>(
component: consuming PropertyPath.Component,
value: Value,
trackingContext: borrowing _TrackingContext
) -> Value {

return dynamic_processGet(component: component, value: value, trackingContext: trackingContext)

}

/// no tracking
@inlinable
public static func processGet<WrappedValue>(
component: consuming PropertyPath.Component,
value: Optional<WrappedValue>,
trackingContext: borrowing _TrackingContext
) -> WrappedValue? {

guard let value = value else {
return nil
}

return dynamic_processGet(component: component, value: value, trackingContext: trackingContext)

}

/// tracking
@usableFromInline
static func static_processGet<Value: TrackingObject>(
component: consuming PropertyPath.Component,
value: Value,
trackingContext: borrowing _TrackingContext
) -> Value {

trackingContext.trackingResultRef?.accessorRead(path: trackingContext.path?.pushed(component))

var value = value

if let ref = trackingContext.trackingResultRef {

if value._tracking_context.trackingResultRef !== ref {
value._tracking_context = _TrackingContext(trackingResultRef: ref)
}

value._tracking_context.path = trackingContext.path?.pushed(component)

return value
} else {
return value
}

}

/// tracking
@inlinable
public static func processGet<Value: TrackingObject>(
component: consuming PropertyPath.Component,
value: Value,
trackingContext: borrowing _TrackingContext
) -> Value {
static_processGet(component: component, value: value, trackingContext: trackingContext)
}

/// tracking
@inlinable
public static func processGet<WrappedValue: TrackingObject>(
component: consuming PropertyPath.Component,
value: Optional<WrappedValue>,
trackingContext: borrowing _TrackingContext
) -> WrappedValue? {
guard let value = value else {
return nil
}
return static_processGet(component: component, value: value, trackingContext: trackingContext)
}


/// no tracking
@usableFromInline
static func dynamic_processModify<Value>(
component: consuming PropertyPath.Component,
trackingContext: borrowing _TrackingContext,
storage: borrowing _BackingStorage<Value>
) {

trackingContext.trackingResultRef?.accessorModify(path: trackingContext.path?.pushed(component))

if var value = storage.value as? TrackingObject, let ref = trackingContext.trackingResultRef {

if value._tracking_context.trackingResultRef !== ref {
value._tracking_context = _TrackingContext(trackingResultRef: ref)
}
value._tracking_context.path = trackingContext.path?.pushed(component)

storage.value = value as! Value
}
}

/// no tracking
public static func processModify<Value>(
component: consuming PropertyPath.Component,
trackingContext: borrowing _TrackingContext,
storage: borrowing _BackingStorage<Value>
) {

dynamic_processModify(component: component, trackingContext: trackingContext, storage: storage)
}

/// no tracking
public static func processModify<WrappedValue>(
component: consuming PropertyPath.Component,
trackingContext: borrowing _TrackingContext,
storage: borrowing _BackingStorage<Optional<WrappedValue>>
) {

dynamic_processModify(component: component, trackingContext: trackingContext, storage: storage)
}

/// no tracking
public static func processModify<Value: TrackingObject>(
component: consuming PropertyPath.Component,
trackingContext: borrowing _TrackingContext,
storage: borrowing _BackingStorage<Value>
) {

trackingContext.trackingResultRef?.accessorModify(path: trackingContext.path?.pushed(component))

var value = storage.value

if let ref = trackingContext.trackingResultRef {

if value._tracking_context.trackingResultRef !== ref {
value._tracking_context = _TrackingContext(trackingResultRef: ref)
}
value._tracking_context.path = trackingContext.path?.pushed(component)

storage.value = value
}

}

/// no tracking
public static func processModify<WrappedValue: TrackingObject>(
component: consuming PropertyPath.Component,
trackingContext: borrowing _TrackingContext,
storage: borrowing _BackingStorage<Optional<WrappedValue>>
) {

trackingContext.trackingResultRef?.accessorModify(path: trackingContext.path?.pushed(component))

guard var value = storage.value else {
return
}

if let ref = trackingContext.trackingResultRef {

if value._tracking_context.trackingResultRef !== ref {
value._tracking_context = _TrackingContext(trackingResultRef: ref)
}
value._tracking_context.path = trackingContext.path?.pushed(component)

storage.value = value
}

}

}
60 changes: 18 additions & 42 deletions Sources/StateStructMacros/COWTrackingPropertyMacro.swift
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,8 @@ extension COWTrackingPropertyMacro: AccessorMacro {

let isConstant = variableDecl.bindingSpecifier.tokenKind == .keyword(.let)
let propertyName = identifierPattern.identifier.text
let typeName = variableDecl.typeSyntax!.trimmed
let backingName = "_backing_" + propertyName
let hasDidSet = variableDecl.didSetBlock != nil
let hasWillSet = variableDecl.willSetBlock != nil

let initAccessor = AccessorDeclSyntax(
Expand All @@ -158,23 +158,12 @@ extension COWTrackingPropertyMacro: AccessorMacro {

let readAccessor = AccessorDeclSyntax(
"""
get {

let component = PropertyPath.Component.init("\(raw: propertyName)")
_tracking_context.trackingResultRef?.accessorRead(path: _tracking_context.path?.pushed(component))

if var value = \(raw: backingName).value as? TrackingObject, let ref = _tracking_context.trackingResultRef {

if value._tracking_context.trackingResultRef !== ref {
value._tracking_context = _TrackingContext(trackingResultRef: ref)
}

value._tracking_context.path = _tracking_context.path?.pushed(component)

return value as! \(typeName)
}

return \(raw: backingName).value
get {
return Tracking.processGet(
component: .init("\(raw: propertyName)"),
value: \(raw: backingName).value,
trackingContext: _tracking_context
)
}
"""
)
Expand All @@ -186,10 +175,10 @@ extension COWTrackingPropertyMacro: AccessorMacro {
// willset
\(variableDecl.makeWillSetDoBlock())

if let ref = _tracking_context.trackingResultRef {
ref.accessorSet(path: _tracking_context.path?.pushed(.init("\(raw: propertyName)")))
}
_tracking_context.trackingResultRef?.accessorSet(path: _tracking_context.path?.pushed(.init("\(raw: propertyName)")))

\(raw: hasDidSet ? "let oldValue = \(backingName).value" : "")

if !isKnownUniquelyReferenced(&\(raw: backingName)) {
\(raw: backingName) = .init(newValue)
} else {
Expand All @@ -206,32 +195,19 @@ extension COWTrackingPropertyMacro: AccessorMacro {
"""
_modify {

if let ref = _tracking_context.trackingResultRef {
ref.accessorModify(path: _tracking_context.path?.pushed(.init("\(raw: propertyName)")))
}

if !isKnownUniquelyReferenced(&\(raw: backingName)) {
\(raw: backingName) = .init(\(raw: backingName).value)
}

\(raw: hasDidSet ? "let oldValue = \(backingName).value" : "")

let oldValue = \(raw: backingName).value
Tracking.processModify(
component: .init("\(raw: propertyName)"),
trackingContext: _tracking_context,
storage: \(raw: backingName)
)

if var value = \(raw: backingName).value as? TrackingObject,
let ref = _tracking_context.trackingResultRef {

let component = PropertyPath.Component.init("\(raw: propertyName)")

if value._tracking_context.trackingResultRef !== ref {
value._tracking_context = _TrackingContext(trackingResultRef: ref)
}
value._tracking_context.path = _tracking_context.path?.pushed(component)

\(raw: backingName).value = value as! \(typeName)

yield &\(raw: backingName).value
} else {
yield &\(raw: backingName).value
}
yield &\(raw: backingName).value

// didSet
\(variableDecl.makeDidSetDoBlock())
Expand Down
Loading