migueldeicaza / SwiftGodot

New Godot bindings for Swift
https://migueldeicaza.github.io/SwiftGodotDocs/tutorials/swiftgodot-tutorials/
MIT License
1.08k stars 71 forks source link

Variant crash #531

Open migueldeicaza opened 2 weeks ago

migueldeicaza commented 2 weeks ago

The following example that implements an encoding for Godot crashes, due to attempting to access freed information:

//
//  main.swift
//
// Very trivial example of using SwiftGodotKit
//
//  Created by Miguel de Icaza on 4/1/23.
//

//import Foundation
import SwiftGodot
import SwiftGodotKit

struct Foo: Codable {
    var myInt: Int
    var myText: String
    var myIntArray: [Int]
}

struct FooN: Codable {
    var myInt: Int
    var myText: String
    var myFoo: [Foo]
}

protocol GodotEncodingContainer {
    var data: Variant { get }
}

extension Vector2: Codable {
    enum CodingKeys: String, CodingKey {
        case x
        case y
    }

    public init (from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        let x = try values.decode(Float.self, forKey: .x)
        let y = try values.decode(Float.self, forKey: .y)
        self.init (x: x, y: y)
    }

    public func encode(to encoder: any Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(x, forKey: .x)
        try container.encode(x, forKey: .y)
    }
}

/// Notes on encoding:
///   - Int8 is encoded as an Int
public class GodotEncoder: Encoder {
    public var codingPath: [any CodingKey] = []
    public var userInfo: [CodingUserInfoKey : Any] = [:]
    var container: GodotEncodingContainer? = nil

    public init () {

    }

    public func container<Key>(keyedBy type: Key.Type) -> KeyedEncodingContainer<Key> where Key : CodingKey {

        var container = GodotKeyedContainer<Key>(codingPath: codingPath, userInfo: userInfo)
        self.container = container
        return KeyedEncodingContainer(container)
    }

    public var data: Variant {
        return container?.data ?? Variant()
    }

    func encode(key codingKey: [CodingKey], value: Variant) {
        let key = codingKey.map { $0.stringValue }.joined(separator: ".")
        fatalError()
        //dict [key] = value
    }

    public func unkeyedContainer() -> any UnkeyedEncodingContainer {
        let container = GodotUnkeyedContainer(codingPath: codingPath, userInfo: userInfo)
        self.container = container
        return container
    }

    public func singleValueContainer() -> any SingleValueEncodingContainer {
        let container = GodotSingleValueContainer(codingPath: codingPath, userInfo: userInfo)
        self.container = container
        return container
    }

    class GodotKeyedContainer<Key:CodingKey>: KeyedEncodingContainerProtocol, GodotEncodingContainer {
        func encodeNil(forKey key: Key) throws {
            var container = self.nestedSingleValueContainer(forKey: key)
            fatalError()
            //try container.encode(Variant())
        }

        var codingPath: [any CodingKey] = []
        var userInfo: [CodingUserInfoKey: Any]
        var storage: [String:GodotEncodingContainer] = [:]

        var data: Variant {
            let dict = GDictionary()
            for (k,v) in storage {
                dict [k] = Variant(v.data)
            }
            return Variant(dict)
        }

        init (codingPath: [any CodingKey], userInfo: [CodingUserInfoKey: Any]) {
            self.codingPath = codingPath
            self.userInfo = userInfo
        }

        func encode(_ value: String, forKey key: Key) throws {
            var container = self.nestedSingleValueContainer(forKey: key)
            try container.encode(value)
            self.storage[key.stringValue] = container
        }

        func encode(_ value: Int, forKey key: Key) throws {
            var container = self.nestedSingleValueContainer(forKey: key)
            try container.encode(value)
            self.storage[key.stringValue] = container
        }

        func nestedSingleValueContainer(forKey key: Key)
            -> GodotSingleValueContainer
        {
            let container = GodotSingleValueContainer(
                codingPath: self.codingPath + [key],
                userInfo: self.userInfo
            )
            return container
        }

        func encode<T>(_ value: T, forKey key: Key) throws where T : Encodable {
            var container = self.nestedSingleValueContainer(forKey: key)
            try container.encode(value)
            self.storage[key.stringValue] = container

        }

        func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer<NestedKey> where NestedKey : CodingKey {
            fatalError()
        }

        func nestedUnkeyedContainer(forKey key: Key) -> any UnkeyedEncodingContainer {
            fatalError()
        }

        func superEncoder() -> any Encoder {
            fatalError()
        }

        func superEncoder(forKey key: Key) -> any Encoder {
            fatalError()
        }
    }

    class GodotUnkeyedContainer: UnkeyedEncodingContainer, GodotEncodingContainer {
        var codingPath: [any CodingKey] = []
        var userInfo: [CodingUserInfoKey: Any]

        var storage = GArray()

        var data: Variant {
            return Variant(storage)
        }

        init (codingPath: [any CodingKey], userInfo: [CodingUserInfoKey: Any]) {
            self.codingPath = codingPath
            self.userInfo = userInfo
        }

        var count: Int {
            Int(storage.size())
        }

        func encode(_ value: String) throws {
            fatalError()
        }

        func encode(_ value: Double) throws {
            fatalError()
        }

        func encode(_ value: Float) throws {
            fatalError()
        }

        func encode(_ value: Int) throws {
            fatalError()
        }

        func encode(_ value: Int8) throws {
            fatalError()
        }

        func encode(_ value: Int16) throws {
            fatalError()
        }

        func encode(_ value: Int32) throws {
            fatalError()
        }

        func encode(_ value: Int64) throws {
            fatalError()
        }

        func encode(_ value: UInt) throws {
            fatalError()
        }

        func encode(_ value: UInt8) throws {
            fatalError()
        }

        func encode(_ value: UInt16) throws {
            fatalError()
        }

        func encode(_ value: UInt32) throws {
            fatalError()
        }

        func encode(_ value: UInt64) throws {
            fatalError()
        }

        func encode<T>(_ value: T) throws where T : Encodable {
            let base = Int(storage.size())

            if let v = value as? Array<Encodable> {
                _ = storage.resize(size: storage.size () + Int64(v.count))
                for i in 0..<v.count {
                    let v = v [i]
                    let nested = GodotSingleValueContainer(codingPath: codingPath, userInfo: userInfo)
                    try nested.encode(v)
                    storage [base+i] = nested.data
                }
            } else {
                _ = storage.resize(size: storage.size () + 1)
                let nested = GodotSingleValueContainer(codingPath: codingPath, userInfo: userInfo)
                try nested.encode (value)
                storage [base] = nested.data
            }
        }

        func encode(_ value: Bool) throws {
            fatalError()
        }

        private struct IndexedCodingKey: CodingKey {
            let intValue: Int?
            let stringValue: String

            init?(intValue: Int) {
                self.intValue = intValue
                self.stringValue = intValue.description
            }

            init?(stringValue: String) {
                return nil
            }
        }

        func encodeNil() throws {
            fatalError()
        }

        func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer<NestedKey> where NestedKey : CodingKey {
            fatalError()
        }

        func nestedUnkeyedContainer() -> any UnkeyedEncodingContainer {
            fatalError()
        }

        func superEncoder() -> any Encoder {
            fatalError()
        }

    }

    class GodotSingleValueContainer: SingleValueEncodingContainer, GodotEncodingContainer {
        func encode<T>(_ value: T) throws where T : Encodable {
            if value is Array<Any> {
                var container = GodotUnkeyedContainer (codingPath: codingPath, userInfo: userInfo)
                try container.encode(value)
                self.value = container.data
            } else if let i = value as? Int {
                try self.encode (i)
            } else if let str = value as? String {
                try self.encode (str)
            } else {
                var nested = GodotEncoder()
                try value.encode(to: nested)
                self.value = nested.container?.data
            }
        }

        enum foo: Error {
            case nope
        }

        var codingPath: [any CodingKey] = []
        var userInfo: [CodingUserInfoKey: Any]
        var value: Variant?

        var data: Variant {
            value ?? Variant()
        }

        init (codingPath: [any CodingKey], userInfo: [CodingUserInfoKey: Any]) {
            self.codingPath = codingPath
            self.userInfo = userInfo
        }

        func encodeNil() throws {
            fatalError()
        }

        func encode(_ value: Bool) throws {
            fatalError()
        }

        func encode(_ value: String) throws {
            self.value = Variant(value)
        }

        func encode(_ value: Double) throws {
            fatalError()
        }

        func encode(_ value: Float) throws {
            fatalError()
        }

        func encode(_ value: Int) throws {
            self.value = Variant(Int(value))
        }

        func encode(_ value: Int8) throws {
            fatalError()
        }

        func encode(_ value: Int16) throws {
            fatalError()
        }

        func encode(_ value: Int32) throws {
            fatalError()
        }

        func encode(_ value: Int64) throws {
            fatalError()
        }

        func encode(_ value: UInt) throws {
            fatalError()
        }

        func encode(_ value: UInt8) throws {
            fatalError()
        }

        func encode(_ value: UInt16) throws {
            fatalError()
        }

        func encode(_ value: UInt32) throws {
            fatalError()
        }

        func encode(_ value: UInt64) throws {
            fatalError()
        }
    }
}

var last: Node? = nil
func loadScene (scene: SceneTree) {
    let g = GodotEncoder()
    let foon = FooN(myInt: 9, myText: "nine", myFoo: [
        Foo (myInt: 10, myText: "Nested1", myIntArray: [1,1,1]),
        Foo (myInt: 30, myText: "Nested2", myIntArray: [2,2,2])])
    try? foon.encode (to:g)
    print (g.data.description)
}

func registerTypes (level: GDExtension.InitializationLevel) {
}

runGodot(args: [], initHook: registerTypes, loadScene: loadScene, loadProjectSettings: { settings in })
TCROC commented 1 week ago

I'm also currently encountering crashes with SwiftGodot. I'm not currently at my computer so I can't grab the error, but it sounds very similar to this. Has any progress been made on this issue?

migueldeicaza commented 1 week ago

If you have a crash, please file a bug with all the information, including stack traces.