Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ConditionalMetadata #207

Merged
merged 7 commits into from
Feb 25, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
// Audited for iOS 18.0
// Status: Complete

public import OpenGraphShims
package import OpenGraphShims

extension Metadata {
package var isValueType: Bool {
Expand All @@ -15,24 +15,36 @@ extension Metadata {
}
}

public func genericType(at index: Int) -> any Any.Type {
package func genericType(at index: Int) -> any Any.Type {
UnsafeRawPointer(rawValue)
.advanced(by: index &* 8)
.advanced(by: 16)
.assumingMemoryBound(to: Any.Type.self)
.pointee
}

#if OPENSWIFTUI_SUPPORT_2024_API
@inline(__always)
package func projectEnum(
at ptr: UnsafeRawPointer,
tag: Int,
_ body: (UnsafeRawPointer) -> Void
) {
#if OPENSWIFTUI_SUPPORT_2024_API
projectEnumData(UnsafeMutableRawPointer(mutating: ptr))
body(ptr)
injectEnumTag(tag: UInt32(tag), UnsafeMutableRawPointer(mutating: ptr))
#endif
}
#endif
}

@inline(__always)
package func compareEnumTags<T>(_ v1: T, _ v2: T) -> Bool {
func tag(of value: T) -> Int {
withUnsafePointer(to: value) {
Int(Metadata(T.self).enumTag($0))
}
}
let tag1 = tag(of: v1)
let tag2 = tag(of: v2)
return tag1 == tag2
}
230 changes: 230 additions & 0 deletions Sources/OpenSwiftUICore/Runtime/ConditionalMetadata.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
//
// ConditionalMetadata.swift
// OpenSwiftUICore
//
// Audited for iOS 18.0
// Status: Complete
// ID: 2319071E64CA2FA820BFB26F46C6ECC6 (SwiftUICore)

import OpenGraphShims

// MARK: - ConditionalProtocolDescriptor

package protocol ConditionalProtocolDescriptor: ProtocolDescriptor {
static func fetchConditionalType(key: ObjectIdentifier) -> ConditionalTypeDescriptor<Self>?

static func insertConditionalType(key: ObjectIdentifier, value: ConditionalTypeDescriptor<Self>)
}

// MARK: - ConditionalMetadata

package struct ConditionalMetadata<P> where P: ConditionalProtocolDescriptor {
var desc: ConditionalTypeDescriptor<P>
var ids: [UniqueID]

init(_ desc: ConditionalTypeDescriptor<P>) {
self.desc = desc
self.ids = (0..<desc.count).map { _ in UniqueID() }
}

func childInfo<V>(ptr: UnsafePointer<V>, emptyType: any Any.Type) -> (any Any.Type, UniqueID?) {
var targetType: (any Any.Type)?
var targetIndex = 0
desc.project(at: UnsafeRawPointer(ptr), baseIndex: 0) { index, conformance, ptr in
targetIndex = index
targetType = conformance?.type
}
return (targetType ?? emptyType, ids[targetIndex])
}
}

// MARK: - ConditionalTypeDescriptor

package struct ConditionalTypeDescriptor<P> where P: ConditionalProtocolDescriptor {
fileprivate enum Storage {
case atom(TypeConformance<P>)
indirect case optional(any Any.Type, ConditionalTypeDescriptor<P>)
indirect case either(any Any.Type, f: ConditionalTypeDescriptor<P>, t: ConditionalTypeDescriptor<P>)
}

private var storage: Storage

var count: Int

fileprivate static func descriptor(type: any Any.Type) -> Self {
if let descriptor = P.fetchConditionalType(key: ObjectIdentifier(type)) {
return descriptor
} else {
let descriptor = ConditionalTypeDescriptor(type)
P.insertConditionalType(key: ObjectIdentifier(type), value: descriptor)
return descriptor
}
}

fileprivate init(storage: Storage, count: Int) {
self.storage = storage
self.count = count
}

init(_ type: any Any.Type) {
let storage: Storage
let count: Int

let metadata = Metadata(type)
let descriptor = metadata.nominalDescriptor

if descriptor == conditionalTypeDescriptor {
let falseDescriptor = Self.descriptor(type: metadata.genericType(at: 1))
let trueDescriptor = Self.descriptor(type: metadata.genericType(at: 0))
storage = .either(type, f: falseDescriptor, t: trueDescriptor)
count = falseDescriptor.count + trueDescriptor.count
} else if descriptor == optionalTypeDescriptor {
let wrappedDescriptor = Self.descriptor(type: metadata.genericType(at: 0))
storage = .optional(type, wrappedDescriptor)
count = wrappedDescriptor.count + 1
} else {
storage = .atom(P.conformance(of: type)!)
count = 1
}

self.init(storage: storage, count: count)
}

fileprivate func project(at base: UnsafeRawPointer, baseIndex: Int, _ body: (Int, TypeConformance<P>?, UnsafeRawPointer?) -> ()) {
#if OPENSWIFTUI_SUPPORT_2024_API
switch storage {
case let .atom(conformance):
body(baseIndex, conformance, base)
case let .optional(type, descriptor):
let metadata = Metadata(type)
let tag = Int(metadata.enumTag(base))
if tag == 1 {
body(baseIndex, nil, nil)
} else {
metadata.projectEnum(
at: base,
tag: tag
) { ptr in
descriptor.project(at: ptr, baseIndex: baseIndex &+ 1, body)
}
}
case let .either(type, falseDescriptor, trueDescriptor):
let metadata = Metadata(type)
let tag = Int(metadata.enumTag(base))
metadata.projectEnum(
at: base,
tag: tag
) { ptr in
if tag == 1 {
falseDescriptor.project(at: ptr, baseIndex: baseIndex, body)
} else {
trueDescriptor.project(at: ptr, baseIndex: baseIndex + falseDescriptor.count, body)
}
}
}
#endif
}
}

private let optionalTypeDescriptor: UnsafeRawPointer = Metadata(Void?.self).nominalDescriptor!
private let conditionalTypeDescriptor: UnsafeRawPointer = Metadata(_ConditionalContent<Void, Void>.self).nominalDescriptor!

// MARK: - Optional + ConditionalMetadata

extension Optional {
package static func makeConditionalMetadata<P>(_ protocolDescriptor: P.Type) -> ConditionalMetadata<P> where P: ConditionalProtocolDescriptor {
let descriptor: ConditionalTypeDescriptor<P>
if let result = P.fetchConditionalType(key: ObjectIdentifier(Self.self)) {
descriptor = result
} else {
descriptor = {
let wrappedDescriptor = ConditionalTypeDescriptor<P>.descriptor(type: Wrapped.self)
return ConditionalTypeDescriptor(
storage: .optional(Self.self, wrappedDescriptor),
count: wrappedDescriptor.count + 1
)
}()
P.insertConditionalType(key: ObjectIdentifier(Self.self), value: descriptor)
}
return ConditionalMetadata(descriptor)
}
}

// MARK: ConditionalMetadata + ViewDescriptor

extension ConditionalMetadata where P == ViewDescriptor {
func makeView<V>(ptr: UnsafePointer<V>, view: Attribute<V>, inputs: _ViewInputs) -> _ViewOutputs {
var visitor = MakeView(desc: desc, view: view, inputs: inputs)
desc.project(at: ptr, baseIndex: 0) { index, conformance, ptr in
guard let conformance, let ptr else { return }
visitor.index = index
visitor.ptr = ptr
conformance.visitType(visitor: &visitor)
}
return visitor.outputs ?? .init()
}

func makeViewList<V>(ptr: UnsafePointer<V>, view: Attribute<V>, inputs: _ViewListInputs) -> _ViewListOutputs {
var visitor = MakeList(desc: desc, view: view, inputs: inputs)
desc.project(at: ptr, baseIndex: 0) { index, conformance, ptr in
guard let conformance, let ptr else { return }
visitor.index = index
visitor.ptr = ptr
conformance.visitType(visitor: &visitor)
}
return visitor.outputs ?? .emptyViewList(inputs: inputs)
}

private struct MakeView<Source>: ViewTypeVisitor {
var desc: ConditionalTypeDescriptor<ViewDescriptor>
var view: Attribute<Source>
var index: Int = 0
var ptr: UnsafeRawPointer?
var inputs: _ViewInputs
var outputs: _ViewOutputs?

mutating func visit<V>(type: V.Type) where V: View {
inputs.base.pushStableID(index)
let unwrapConditional = UnwrapConditional<P, Source, V>(source: view, desc: desc, index: index)
let attribute = Attribute(unwrapConditional)
attribute.value = ptr!.assumingMemoryBound(to: V.self).pointee
outputs = V.makeDebuggableView(view: _GraphValue(attribute), inputs: inputs)
}
}


private struct MakeList<Source>: ViewTypeVisitor {
var desc: ConditionalTypeDescriptor<ViewDescriptor>
var view: Attribute<Source>
var index: Int = 0
var ptr: UnsafeRawPointer?
var inputs: _ViewListInputs
var outputs: _ViewListOutputs?

mutating func visit<V>(type: V.Type) where V: View {
inputs.base.pushStableID(index)
let unwrapConditional = UnwrapConditional<P, Source, V>(source: view, desc: desc, index: index)
let attribute = Attribute(unwrapConditional)
attribute.value = ptr!.assumingMemoryBound(to: V.self).pointee
outputs = V.makeDebuggableViewList(view: _GraphValue(attribute), inputs: inputs)
}
}
}

// MARK: - UnwrapConditional

private struct UnwrapConditional<P, Source, Value>: StatefulRule, AsyncAttribute where P: ConditionalProtocolDescriptor {
@Attribute var source: Source
let desc: ConditionalTypeDescriptor<P>
let index: Int

func updateValue() {
withUnsafePointer(to: source) { ptr in
desc.project(at: ptr, baseIndex: 0) { index, conformance, ptr in
guard self.index == index else { return }
value = ptr!.assumingMemoryBound(to: Value.self).pointee
}
}
}
}

60 changes: 0 additions & 60 deletions Sources/OpenSwiftUICore/Runtime/ProtocolDescriptor.swift

This file was deleted.

51 changes: 51 additions & 0 deletions Sources/OpenSwiftUICore/Runtime/TupleTypeDescription.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//
// TupleTypeDescription.swift
// OpenSwiftUICore
//
// Audited for iOS 18.0
// Status: Complete

package import OpenGraphShims

// MARK: - TupleDescriptor

package protocol TupleDescriptor: ProtocolDescriptor {
static var typeCache: [ObjectIdentifier: TupleTypeDescription<Self>] { get set }
}

extension TupleDescriptor {
package static func tupleDescription(_ type: TupleType) -> TupleTypeDescription<Self> {
let id = ObjectIdentifier(type.type)
if let cache = typeCache[id] {
return cache
} else {
let description = TupleTypeDescription<Self>(type)
typeCache[id] = description
return description
}
}
}

// MARK: - TupleTypeDescription

package struct TupleTypeDescription<P> where P: ProtocolDescriptor {
package let contentTypes: [(Int, TypeConformance<P>)]

package init(_ type: TupleType) {
var contentTypes: [(Int, TypeConformance<P>)] = []
for index in type.indices {
let type = type.type(at: index)
guard let comformance = P.conformance(of: type) else {
let message = "Ignoring invalid type at index \(index), type \(type)"
#if OPENSWIFTUI_SWIFT_LOG
Log.unlocatedIssuesLog.error("\(message)")
#else
Log.unlocatedIssuesLog.fault("\(message, privacy: .public)")
#endif
continue
}
contentTypes.append((index, comformance))
}
self.contentTypes = contentTypes
}
}
Loading
Loading