Skip to content

Commit

Permalink
Clean up metadata reflection code (#372)
Browse files Browse the repository at this point in the history
Our OpenCombine fork no longer depends on Runtime, and we don't need much from it other than struct metadata. I removed the unused bits and bobs and kept only a minimal subset of it that we really need. This should make it easier for us to test and debug, as #367 has shown that some weird stuff may still lurk in that area.

* Add a test for environment injection

We had some issues in this code area previously and I'm thinking of refactoring it in attempt to fix #367. Would be great to increase the test coverage here before further refactoring.

* Update copyright years in `MountedElement.swift`

* Update copyright years in the rest of the files

* Vend the Runtime library directly

* Remove unused class, enum, tuple, func reflection

* Remove unused models and protocol metadata

* Remove unused MetadataType and NominalMetadataType

* Remove unused protocols, rename RelativePointer

* Remove more unused protocols

* Use immutable pointers for reflection

* Update copyright headers
  • Loading branch information
MaxDesiatov committed Jan 27, 2021
1 parent 07ccef8 commit 0e89ea9
Show file tree
Hide file tree
Showing 37 changed files with 1,453 additions and 79 deletions.
9 changes: 0 additions & 9 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,6 @@
"version": "0.0.2"
}
},
{
"package": "Runtime",
"repositoryURL": "https://github.com/MaxDesiatov/Runtime.git",
"state": {
"branch": null,
"revision": "a617ead8a125a97e69d6100e4d27922006e82e0a",
"version": "2.1.2"
}
},
{
"package": "swift-argument-parser",
"repositoryURL": "https://github.com/apple/swift-argument-parser",
Expand Down
3 changes: 1 addition & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ let package = Package(
url: "https://github.com/swiftwasm/JavaScriptKit.git",
.upToNextMinor(from: "0.9.0")
),
.package(url: "https://github.com/MaxDesiatov/Runtime.git", from: "2.1.2"),
.package(url: "https://github.com/TokamakUI/OpenCombine.git", from: "0.12.0-alpha3"),
.package(url: "https://github.com/swiftwasm/OpenCombineJS.git", .upToNextMinor(from: "0.0.2")),
.package(name: "Benchmark", url: "https://github.com/google/swift-benchmark", from: "0.1.0"),
Expand All @@ -73,7 +72,7 @@ let package = Package(
),
.target(
name: "TokamakCore",
dependencies: ["CombineShim", "Runtime"]
dependencies: ["CombineShim"]
),
.target(
name: "TokamakShim",
Expand Down
8 changes: 8 additions & 0 deletions Sources/TokamakCore/Modifiers/ModifiedContent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.

protocol ModifierContainer {
var environmentModifier: EnvironmentModifier? { get }
}

/// A value with a modifier applied to it.
public struct ModifiedContent<Content, Modifier> {
@Environment(\.self) public var environment
Expand All @@ -25,6 +29,10 @@ public struct ModifiedContent<Content, Modifier> {
}
}

extension ModifiedContent: ModifierContainer {
var environmentModifier: EnvironmentModifier? { modifier as? EnvironmentModifier }
}

extension ModifiedContent: EnvironmentReader where Modifier: EnvironmentReader {
mutating func setContent(from values: EnvironmentValues) {
modifier.setContent(from: values)
Expand Down
37 changes: 14 additions & 23 deletions Sources/TokamakCore/MountedViews/MountedElement.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@
// Created by Max Desiatov on 28/11/2018.
//

import Runtime

/// The container for any of the possible `MountedElement` types
private enum MountedElementKind {
case app(_AnyApp)
Expand Down Expand Up @@ -162,38 +160,32 @@ public class MountedElement<R: Renderer> {

extension EnvironmentValues {
mutating func inject(into element: inout Any, _ type: Any.Type) {
// swiftlint:disable:next force_try
let info = try! typeInfo(of: type)
guard let info = typeInfo(of: type) else { return }

// swiftlint:disable force_try
// Extract the view from the AnyView for modification, apply Environment changes:
if info.genericTypes.contains(where: { $0 is EnvironmentModifier.Type }),
let modifier = try! info.property(named: "modifier")
.get(from: element) as? EnvironmentModifier
{
modifier.modifyEnvironment(&self)
if let container = element as? ModifierContainer {
container.environmentModifier?.modifyEnvironment(&self)
}

// Inject @Environment values
// swiftlint:disable force_cast
// `DynamicProperty`s can have `@Environment` properties contained in them,
// so we have to inject into them as well.
for dynamicProp in info.properties.filter({ $0.type is DynamicProperty.Type }) {
let propInfo = try! typeInfo(of: dynamicProp.type)
var propWrapper = try! dynamicProp.get(from: element) as! DynamicProperty
guard let propInfo = typeInfo(of: dynamicProp.type) else { return }
var propWrapper = dynamicProp.get(from: element) as! DynamicProperty
for prop in propInfo.properties.filter({ $0.type is EnvironmentReader.Type }) {
var wrapper = try! prop.get(from: propWrapper) as! EnvironmentReader
var wrapper = prop.get(from: propWrapper) as! EnvironmentReader
wrapper.setContent(from: self)
try! prop.set(value: wrapper, on: &propWrapper)
prop.set(value: wrapper, on: &propWrapper)
}
try! dynamicProp.set(value: propWrapper, on: &element)
dynamicProp.set(value: propWrapper, on: &element)
}
for prop in info.properties.filter({ $0.type is EnvironmentReader.Type }) {
var wrapper = try! prop.get(from: element) as! EnvironmentReader
var wrapper = prop.get(from: element) as! EnvironmentReader
wrapper.setContent(from: self)
try! prop.set(value: wrapper, on: &element)
prop.set(value: wrapper, on: &element)
}
// swiftlint:enable force_try
// swiftlint:enable force_cast
}
}
Expand All @@ -209,10 +201,10 @@ extension TypeInfo {
var dynamicProps = [PropertyInfo]()
for prop in properties where prop.type is DynamicProperty.Type {
dynamicProps.append(prop)
// swiftlint:disable force_try
let propInfo = try! typeInfo(of: prop.type)
guard let propInfo = typeInfo(of: prop.type) else { continue }

environment.inject(into: &source, prop.type)
var extracted = try! prop.get(from: source)
var extracted = prop.get(from: source)
dynamicProps.append(
contentsOf: propInfo.dynamicProperties(
&environment,
Expand All @@ -222,8 +214,7 @@ extension TypeInfo {
// swiftlint:disable:next force_cast
var extractedDynamicProp = extracted as! DynamicProperty
extractedDynamicProp.update()
try! prop.set(value: extractedDynamicProp, on: &source)
// swiftlint:enable force_try
prop.set(value: extractedDynamicProp, on: &source)
}
return dynamicProps
}
Expand Down
39 changes: 39 additions & 0 deletions Sources/TokamakCore/Reflection/Layouts/ExistentialContainter.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// MIT License
//
// Copyright (c) 2017-2021 Wesley Wickwire and Tokamak contributors
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

struct ExistentialContainer {
let buffer: ExistentialContainerBuffer
let type: Any.Type
let witnessTable: Int
}

struct ExistentialContainerBuffer {
let buffer1: Int
let buffer2: Int
let buffer3: Int
}

extension ExistentialContainerBuffer {
static func size() -> Int {
MemoryLayout<ExistentialContainerBuffer>.size
}
}
102 changes: 102 additions & 0 deletions Sources/TokamakCore/Reflection/Layouts/FieldDescriptor.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// MIT License
//
// Copyright (c) 2017-2021 Wesley Wickwire and Tokamak contributors
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

#if canImport(CRuntime)
import CRuntime
#endif

@_silgen_name("swift_getTypeByMangledNameInContext")
func _getTypeByMangledNameInContext(
_ name: UnsafePointer<UInt8>,
_ nameLength: UInt,
_ genericContext: UnsafeRawPointer?,
_ genericArguments: UnsafeRawPointer?
)
-> Any.Type?

/// https://github.com/apple/swift/blob/f2c42509628bed66bf5b8ee02fae778a2ba747a1/include/swift/Reflection/Records.h#L160
struct FieldDescriptor {
let mangledTypeNameOffset: Int32
let superClassOffset: Int32
let _kind: UInt16
let fieldRecordSize: Int16
let numFields: Int32
let fields: FieldRecord

var kind: FieldDescriptorKind {
FieldDescriptorKind(rawValue: _kind)!
}
}

extension UnsafePointer where Pointee == FieldRecord {
func fieldName() -> String {
String(cString: advance(offset: \._fieldName))
}

func type(
genericContext: UnsafeRawPointer?,
genericArguments: UnsafeRawPointer?
) -> Any.Type {
let typeName = advance(offset: \._mangledTypeName)
return _getTypeByMangledNameInContext(
typeName,
getSymbolicMangledNameLength(typeName),
genericContext,
genericArguments?.assumingMemoryBound(to: UnsafeRawPointer?.self)
)!
}
}

private func getSymbolicMangledNameLength(_ base: UnsafeRawPointer) -> UInt {
var end = base
while let current = Optional(end.load(as: UInt8.self)), current != 0 {
end += 1
if current >= 0x1 && current <= 0x17 {
end += 4
} else if current >= 0x18 && current <= 0x1F {
end += MemoryLayout<Int>.size
}
}

return UInt(end - base)
}

struct FieldRecord {
let fieldRecordFlags: Int32
let _mangledTypeName: MetadataOffset<UInt8>
let _fieldName: MetadataOffset<UInt8>

var isVar: Bool {
(fieldRecordFlags & 0x2) == 0x2
}
}

enum FieldDescriptorKind: UInt16 {
case `struct`
case `class`
case `enum`
case multiPayloadEnum
case `protocol`
case classProtocol
case objcProtocol
case objcClass
}
26 changes: 26 additions & 0 deletions Sources/TokamakCore/Reflection/Layouts/ProtocolTypeContainer.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// MIT License
//
// Copyright (c) 2017-2021 Wesley Wickwire and Tokamak contributors
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

struct ProtocolTypeContainer {
let type: Any.Type
let witnessTable: Int
}
26 changes: 26 additions & 0 deletions Sources/TokamakCore/Reflection/Layouts/StructMetadataLayout.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// MIT License
//
// Copyright (c) 2017-2021 Wesley Wickwire and Tokamak contributors
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

struct StructMetadataLayout {
let _kind: Int
let typeDescriptor: UnsafePointer<StructTypeDescriptor>
}
34 changes: 34 additions & 0 deletions Sources/TokamakCore/Reflection/Layouts/StructTypeDescriptor.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// MIT License
//
// Copyright (c) 2017-2021 Wesley Wickwire and Tokamak contributors
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

typealias FieldTypeAccessor = @convention(c) (UnsafePointer<Int>) -> UnsafePointer<Int>

struct StructTypeDescriptor {
let flags: Int32
let parent: Int32
let mangledName: MetadataOffset<CChar>
let accessFunctionPtr: MetadataOffset<UnsafeRawPointer>
let fieldDescriptor: MetadataOffset<FieldDescriptor>
let numberOfFields: Int32
let offsetToTheFieldOffsetVector: RelativeVectorPointer<Int32, Int32>
let genericContextHeader: TargetTypeGenericContextDescriptorHeader
}
Loading

0 comments on commit 0e89ea9

Please sign in to comment.