NSCoding methods


(Daniel Strokis) #1

Hi All,

I’ve noticed that for many classes in Foundation, encodeWithCoder and init?(coder aDecoder: NSCoder) call NSUnimplemented. Are these methods that just haven’t been implemented yet, or are we not interested in implementing these moving forward? Apologies if this has already been discussed before and I’m just out of the loop.

Thanks,
Daniel Strokis


(Philippe Hausler) #2

These were not implemented yet since we did not have a way to actually verify archives. Part of the goal is to have archives be serializable on Darwin platforms and de-serializable on linux (and visa-versa). That way someone could presumably archive objects on an iPhone and send the archive over the wire to a linux machine and that machine would be able to de-serialize it and yield the appropriate structure. This means that we need to make certain while implementing these that they mimic the same coder keys and structural serialization order (when not initing with a keyed archiver). NSCoder itself has a start of an implementation but NSKeyedArchiver has a limitation in that we cannot yet build construction of objects dynamically from their class name. Specifically there is no NSClassFromString yet. I would say if you are looking for a place to start, perhaps coming up with a good strategy for accomplishing that in a uniform manner (for both Foundation classes as well as user classes) would be a good step in the right direction to getting this started.

It might also be a good start to build a verification NSCoder subclass to verify the order and keys/values for any abstract class. That way we can test and verify the coding/decoding on all platforms.

···

On Dec 12, 2015, at 11:58 AM, Daniel Strokis via swift-corelibs-dev <swift-corelibs-dev@swift.org> wrote:

Hi All,

I’ve noticed that for many classes in Foundation, encodeWithCoder and init?(coder aDecoder: NSCoder) call NSUnimplemented. Are these methods that just haven’t been implemented yet, or are we not interested in implementing these moving forward? Apologies if this has already been discussed before and I’m just out of the loop.

Thanks,
Daniel Strokis

_______________________________________________
swift-corelibs-dev mailing list
swift-corelibs-dev@swift.org
https://lists.swift.org/mailman/listinfo/swift-corelibs-dev


(Daniel Strokis) #3

Is anyone currently working on this who would like to collaborate?

Daniel Strokis

···

Sent from my iPhone

On Dec 12, 2015, at 3:47 PM, Philippe Hausler <phausler@apple.com> wrote:

These were not implemented yet since we did not have a way to actually verify archives. Part of the goal is to have archives be serializable on Darwin platforms and de-serializable on linux (and visa-versa). That way someone could presumably archive objects on an iPhone and send the archive over the wire to a linux machine and that machine would be able to de-serialize it and yield the appropriate structure. This means that we need to make certain while implementing these that they mimic the same coder keys and structural serialization order (when not initing with a keyed archiver). NSCoder itself has a start of an implementation but NSKeyedArchiver has a limitation in that we cannot yet build construction of objects dynamically from their class name. Specifically there is no NSClassFromString yet. I would say if you are looking for a place to start, perhaps coming up with a good strategy for accomplishing that in a uniform manner (for both Foundation classes as well as user classes) would be a good step in the right direction to getting this started.

It might also be a good start to build a verification NSCoder subclass to verify the order and keys/values for any abstract class. That way we can test and verify the coding/decoding on all platforms.

On Dec 12, 2015, at 11:58 AM, Daniel Strokis via swift-corelibs-dev <swift-corelibs-dev@swift.org> wrote:

Hi All,

I’ve noticed that for many classes in Foundation, encodeWithCoder and init?(coder aDecoder: NSCoder) call NSUnimplemented. Are these methods that just haven’t been implemented yet, or are we not interested in implementing these moving forward? Apologies if this has already been discussed before and I’m just out of the loop.

Thanks,
Daniel Strokis

_______________________________________________
swift-corelibs-dev mailing list
swift-corelibs-dev@swift.org
https://lists.swift.org/mailman/listinfo/swift-corelibs-dev


(Luke Howard) #4

Specifically there is no NSClassFromString yet. I would say if you are looking for a place to start, perhaps coming up with a good strategy for accomplishing that in a uniform manner (for both Foundation classes as well as user classes) would be a good step in the right direction to getting this started.

Does Swift have enough runtime metadata to do this for native Swift classes?

-- Luke


(Philippe Hausler) #5

Somewhat; the mangled symbols are technically searchable by dlsym but that seems like a hack. Perhaps one of the stdlib/compiler team might be able to speak more on this than myself and they may have suggestions for a better way. Foundation is at a special spot in which we can sometimes get special runtime considerations for these types of things.

The tricksy spot would be cases where you would need to fetch a class without the module name.

For example NSUUID is defined by it’s module name Foundation.NSUUID; in that a program could have it’s own NSUUID that is totally different (naming it the same thing would be confusing to read and potentially viewed as bad form but it can be done…). That would result in MyApplication.NSUUID to define the symbolic name of the item. From the perspective of NSKeyedArchiver it will encode things as NSUUID (without the namespace) in that in the realm of objc there can be only one.

The tl;dr is that there is no manifest of classes or table of names stored in the binaries; just symbols.

···

On Dec 18, 2015, at 10:57 PM, Luke Howard <lukeh@padl.com> wrote:

Specifically there is no NSClassFromString yet. I would say if you are looking for a place to start, perhaps coming up with a good strategy for accomplishing that in a uniform manner (for both Foundation classes as well as user classes) would be a good step in the right direction to getting this started.

Does Swift have enough runtime metadata to do this for native Swift classes?

-- Luke


(Luke Howard) #6

Somewhat; the mangled symbols are technically searchable by dlsym but that seems like a hack. Perhaps one of the stdlib/compiler team might be able to speak more on this than myself and they may have suggestions for a better way. Foundation is at a special spot in which we can sometimes get special runtime considerations for these types of things.

It’d arguably be more of a hack to add a global manifest of classes if the dynamic linker already has one :wink: Here’s a toy implementation of swift_classFromString():

https://github.com/lhoward/SwiftRuntimeTest

Disclaimer: I know very little about Swift nor its implementation beyond a bit of poking around.

For example NSUUID is defined by it’s module name Foundation.NSUUID; in that a program could have it’s own NSUUID that is totally different (naming it the same thing would be confusing to read and potentially viewed as bad form but it can be done…). That would result in MyApplication.NSUUID to define the symbolic name of the item. From the perspective of NSKeyedArchiver it will encode things as NSUUID (without the namespace) in that in the realm of objc there can be only one.

What if you (re-)added the ability to annotate Swift classes with their Objective-C name on non-Apple platforms? I don’t know what the runtime costs of doing this are but it seems like it’s either this, or only allow non-namespaced names for Foundation objects.

— Luke


(Jordan Rose) #7

IMHO on Linux NSKeyedArchiver should always use mangled names. If we want cross-platform archives, we should set up standard substitutions, but given that Swift classes exposed to Objective-C are archived with their full names it doesn't make sense to use "half the name" in the archive.

Jordan

···

On Dec 19, 2015, at 13:23 , Philippe Hausler via swift-corelibs-dev <swift-corelibs-dev@swift.org> wrote:

Somewhat; the mangled symbols are technically searchable by dlsym but that seems like a hack. Perhaps one of the stdlib/compiler team might be able to speak more on this than myself and they may have suggestions for a better way. Foundation is at a special spot in which we can sometimes get special runtime considerations for these types of things.

The tricksy spot would be cases where you would need to fetch a class without the module name.

For example NSUUID is defined by it’s module name Foundation.NSUUID; in that a program could have it’s own NSUUID that is totally different (naming it the same thing would be confusing to read and potentially viewed as bad form but it can be done…). That would result in MyApplication.NSUUID to define the symbolic name of the item. From the perspective of NSKeyedArchiver it will encode things as NSUUID (without the namespace) in that in the realm of objc there can be only one.

The tl;dr is that there is no manifest of classes or table of names stored in the binaries; just symbols.

On Dec 18, 2015, at 10:57 PM, Luke Howard <lukeh@padl.com> wrote:

Specifically there is no NSClassFromString yet. I would say if you are looking for a place to start, perhaps coming up with a good strategy for accomplishing that in a uniform manner (for both Foundation classes as well as user classes) would be a good step in the right direction to getting this started.

Does Swift have enough runtime metadata to do this for native Swift classes?

-- Luke

_______________________________________________
swift-corelibs-dev mailing list
swift-corelibs-dev@swift.org
https://lists.swift.org/mailman/listinfo/swift-corelibs-dev


(Luke Howard) #8

It's not too hard to figure out (and there are a couple of other ObjC implementations) but if Apple have any specs for the archive format, I would be keen to take a look.

Also regarding interoperability - perhaps it might be reasonable, for non-Foundation classes that have non-namespaced names (on non-ObjC platforms) for the caller to configure the NSKeyedArchiver with explicit class mappings?

-- Luke

···

Sent from my iPhone

On 21 Dec 2015, at 00:02, Luke Howard via swift-corelibs-dev <swift-corelibs-dev@swift.org> wrote:

Somewhat; the mangled symbols are technically searchable by dlsym but that seems like a hack. Perhaps one of the stdlib/compiler team might be able to speak more on this than myself and they may have suggestions for a better way. Foundation is at a special spot in which we can sometimes get special runtime considerations for these types of things.

It’d arguably be more of a hack to add a global manifest of classes if the dynamic linker already has one :wink: Here’s a toy implementation of swift_classFromString():

https://github.com/lhoward/SwiftRuntimeTest

Disclaimer: I know very little about Swift nor its implementation beyond a bit of poking around.

For example NSUUID is defined by it’s module name Foundation.NSUUID; in that a program could have it’s own NSUUID that is totally different (naming it the same thing would be confusing to read and potentially viewed as bad form but it can be done…). That would result in MyApplication.NSUUID to define the symbolic name of the item. From the perspective of NSKeyedArchiver it will encode things as NSUUID (without the namespace) in that in the realm of objc there can be only one.

What if you (re-)added the ability to annotate Swift classes with their Objective-C name on non-Apple platforms? I don’t know what the runtime costs of doing this are but it seems like it’s either this, or only allow non-namespaced names for Foundation objects.

— Luke
_______________________________________________
swift-corelibs-dev mailing list
swift-corelibs-dev@swift.org
https://lists.swift.org/mailman/listinfo/swift-corelibs-dev


(Luke Howard) #9

You mean namespaced but unmangled yes? If so I agree.

BTW I found a couple of small CF nits:

* in CFDictionaryGetKeysAndValues(), keybuf and valuebuf are transposed in the call to CF_SWIFT_FUNCDISPATCHV(NSDictionary.getObjects())

* _CFSwiftDictionaryGetKeysAndValues() does not handle keybuf or valbuf being NULL (either of which are valid when calling CFDictionaryGetKeysAndValues())

— Luke

···

On 22 Dec 2015, at 5:50 AM, Jordan Rose <jordan_rose@apple.com> wrote:

IMHO on Linux NSKeyedArchiver should always use mangled names. If we want cross-platform archives, we should set up standard substitutions, but given that Swift classes exposed to Objective-C are archived with their full names it doesn't make sense to use "half the name" in the archive.


(Philippe Hausler) #10

To clarify the goals: I think it is reasonable for us to have a goal to be able to encode/decode archives from foreign targets; e.g. linux encodes an archive and mac os x decodes or iOS encodes and linux decodes. This will allow for server architecture to transmit binary archives across the wire. This will mean that we will want to have the encoded class names from the application scope to be encoded as the non mangled name but with the namespace. However this presents a problem; Foundation will have a namespace which will need to be inferred both for encoding and decoding. Thankfully there may be a reasonable way to approach this;

public class func classNameForClass(cls: AnyClass) -> String?
public class func setClassName(codedName: String?, forClass cls: AnyClass)

These methods can be used to allow for translation of classes by registering the appropriate classes for a “shortened” name that drops the Foundation/SwiftFoundation namespace prefix during encoding.

IMHO on Linux NSKeyedArchiver should always use mangled names. If we want cross-platform archives, we should set up standard substitutions, but given that Swift classes exposed to Objective-C are archived with their full names it doesn't make sense to use "half the name" in the archive.

You mean namespaced but unmangled yes? If so I agree.

BTW I found a couple of small CF nits:

* in CFDictionaryGetKeysAndValues(), keybuf and valuebuf are transposed in the call to CF_SWIFT_FUNCDISPATCHV(NSDictionary.getObjects())

* _CFSwiftDictionaryGetKeysAndValues() does not handle keybuf or valbuf being NULL (either of which are valid when calling CFDictionaryGetKeysAndValues())

This is a bit un-related to NSCoding and the transposition is probably a mistake if it is inverted (the CF method should be reversed from the NS method to mimic the objc counterpart)

···

On Dec 22, 2015, at 2:45 AM, Luke Howard via swift-corelibs-dev <swift-corelibs-dev@swift.org> wrote:

On 22 Dec 2015, at 5:50 AM, Jordan Rose <jordan_rose@apple.com> wrote:

— Luke
_______________________________________________
swift-corelibs-dev mailing list
swift-corelibs-dev@swift.org
https://lists.swift.org/mailman/listinfo/swift-corelibs-dev


(Luke Howard) #11

Also, I’m sure this is just my lack of Swift-fu but I’m occasionally getting EXC_BAD_INSTRUCTION in NSString.hash() where the hash code is cast to an Int. If I use unsafeBitCast() instead it seems to work, but I have no idea whether this is safe or not.

I’m on OS X with the 2015-12-18 snapshot.

— Luke


(Luke Howard) #12

Right, I only noticed this whilst implementing NSKeyedArchiver.

Should _CFSwiftDictionaryGetKeysAndValues() (== NSDictionary.getObjects) follow the ObjC or the CF parameter order? The latter makes more sense to me.

— Luke

···

On 23 Dec 2015, at 5:16 AM, Philippe Hausler <phausler@apple.com> wrote:

BTW I found a couple of small CF nits:

* in CFDictionaryGetKeysAndValues(), keybuf and valuebuf are transposed in the call to CF_SWIFT_FUNCDISPATCHV(NSDictionary.getObjects())

* _CFSwiftDictionaryGetKeysAndValues() does not handle keybuf or valbuf being NULL (either of which are valid when calling CFDictionaryGetKeysAndValues())

This is a bit un-related to NSCoding and the transposition is probably a mistake if it is inverted (the CF method should be reversed from the NS method to mimic the objc counterpart)


(Jordan Rose) #13

No, we cannot encode things "non-mangled but with the namespace". For any type other than top-level non-generic class types, using a non-mangled name is not unique. The only correct answer for arbitrary classes is to use mangled names, or something that maps one-to-one with mangled names.

Now, Foundation classes are not arbitrary classes, but then I don't see why we'd need to use mangled names for those. We can just use the plain old Objective-C names that the OS X classes use today.

Jordan

···

On Dec 22, 2015, at 10:16, Philippe Hausler via swift-corelibs-dev <swift-corelibs-dev@swift.org> wrote:

To clarify the goals: I think it is reasonable for us to have a goal to be able to encode/decode archives from foreign targets; e.g. linux encodes an archive and mac os x decodes or iOS encodes and linux decodes. This will allow for server architecture to transmit binary archives across the wire. This will mean that we will want to have the encoded class names from the application scope to be encoded as the non mangled name but with the namespace. However this presents a problem; Foundation will have a namespace which will need to be inferred both for encoding and decoding. Thankfully there may be a reasonable way to approach this;

public class func classNameForClass(cls: AnyClass) -> String?
public class func setClassName(codedName: String?, forClass cls: AnyClass)

These methods can be used to allow for translation of classes by registering the appropriate classes for a “shortened” name that drops the Foundation/SwiftFoundation namespace prefix during encoding.

On Dec 22, 2015, at 2:45 AM, Luke Howard via swift-corelibs-dev <swift-corelibs-dev@swift.org> wrote:

On 22 Dec 2015, at 5:50 AM, Jordan Rose <jordan_rose@apple.com> wrote:

IMHO on Linux NSKeyedArchiver should always use mangled names. If we want cross-platform archives, we should set up standard substitutions, but given that Swift classes exposed to Objective-C are archived with their full names it doesn't make sense to use "half the name" in the archive.

You mean namespaced but unmangled yes? If so I agree.

BTW I found a couple of small CF nits:

* in CFDictionaryGetKeysAndValues(), keybuf and valuebuf are transposed in the call to CF_SWIFT_FUNCDISPATCHV(NSDictionary.getObjects())

* _CFSwiftDictionaryGetKeysAndValues() does not handle keybuf or valbuf being NULL (either of which are valid when calling CFDictionaryGetKeysAndValues())

This is a bit un-related to NSCoding and the transposition is probably a mistake if it is inverted (the CF method should be reversed from the NS method to mimic the objc counterpart)

— Luke
_______________________________________________
swift-corelibs-dev mailing list
swift-corelibs-dev@swift.org <mailto:swift-corelibs-dev@swift.org>
https://lists.swift.org/mailman/listinfo/swift-corelibs-dev

_______________________________________________
swift-corelibs-dev mailing list
swift-corelibs-dev@swift.org <mailto:swift-corelibs-dev@swift.org>
https://lists.swift.org/mailman/listinfo/swift-corelibs-dev


(Philippe Hausler) #14

The archiving format encodes the names of the classes in the archive itself. Here are a few code examples and a quasi readable output from them:

let uuid = NSUUID()
let data = NSKeyedArchiver.archivedDataWithRootObject(uuid)
let archive = try! NSPropertyListSerialization.propertyListWithData(data, options: [], format: nil)
print(archive)

prints the following:

{
    "$archiver" = NSKeyedArchiver;
    "$objects" = (
        "$null",
                {
            "$class" = "<CFKeyedArchiverUID 0x1030025e0 [0x7fff7ab33bb0]>{value = 2}";
            "NS.uuidbytes" = <797639fe dad74b14 902afab3 c490448b>;
        },
                {
            "$classes" = (
                NSUUID,
                NSObject
            );
            "$classname" = NSUUID;
        }
    );
    "$top" = {
        root = "<CFKeyedArchiverUID 0x103002a80 [0x7fff7ab33bb0]>{value = 1}";
    };
    "$version" = 100000;
}

Note the $classes and $classname objects; which are what tell the internal implementation of NSKeyedUnarchiver what to construct; moreover you can create your own classes..

// I don’t really think this is a good naming for an application’s class but hey it might happen...
class NSUUID : NSObject, NSCoding {
    let uuid: Foundation.NSUUID
    required init?(coder aDecoder: NSCoder) {
        uuid = aDecoder.decodeObjectForKey("my.uuid") as! Foundation.NSUUID
    }
    override init() {
        uuid = Foundation.NSUUID()
    }
    func encodeWithCoder(aCoder: NSCoder) {
        aCoder.encodeObject(uuid, forKey: "my.uuid")
    }
}

let uuid = NSUUID()
let data = NSKeyedArchiver.archivedDataWithRootObject(uuid)
let archive = try! NSPropertyListSerialization.propertyListWithData(data, options: [], format: nil)
print(archive)

prints the following:

{
    "$archiver" = NSKeyedArchiver;
    "$objects" = (
        "$null",
                {
            "$class" = "<CFKeyedArchiverUID 0x100709630 [0x7fff7ab33bb0]>{value = 4}";
            "my.uuid" = "<CFKeyedArchiverUID 0x100708e60 [0x7fff7ab33bb0]>{value = 2}";
        },
                {
            "$class" = "<CFKeyedArchiverUID 0x100709740 [0x7fff7ab33bb0]>{value = 3}";
            "NS.uuidbytes" = <546e5b5e 15c244a1 aa96eb90 30c3f7f6>;
        },
                {
            "$classes" = (
                NSUUID,
                NSObject
            );
            "$classname" = NSUUID;
        },
                {
            "$classes" = (
                "Archiver.NSUUID",
                NSObject
            );
            "$classname" = "Archiver.NSUUID";
        }
    );
    "$top" = {
        root = "<CFKeyedArchiverUID 0x100709b70 [0x7fff7ab33bb0]>{value = 1}";
    };
    "$version" = 100000;
}

Granted this is a questionable name for a class but it illustrates which class names are encoded where and how they should be interpreted in the pre-existing archive format; which we will have to figure out some sensible way of inflating and deflating to/from disk/network etc.

···

On Dec 23, 2015, at 2:37 PM, Jordan Rose <jordan_rose@apple.com> wrote:

No, we cannot encode things "non-mangled but with the namespace". For any type other than top-level non-generic class types, using a non-mangled name is not unique. The only correct answer for arbitrary classes is to use mangled names, or something that maps one-to-one with mangled names.

Now, Foundation classes are not arbitrary classes, but then I don't see why we'd need to use mangled names for those. We can just use the plain old Objective-C names that the OS X classes use today.

Jordan

On Dec 22, 2015, at 10:16, Philippe Hausler via swift-corelibs-dev <swift-corelibs-dev@swift.org <mailto:swift-corelibs-dev@swift.org>> wrote:

To clarify the goals: I think it is reasonable for us to have a goal to be able to encode/decode archives from foreign targets; e.g. linux encodes an archive and mac os x decodes or iOS encodes and linux decodes. This will allow for server architecture to transmit binary archives across the wire. This will mean that we will want to have the encoded class names from the application scope to be encoded as the non mangled name but with the namespace. However this presents a problem; Foundation will have a namespace which will need to be inferred both for encoding and decoding. Thankfully there may be a reasonable way to approach this;

public class func classNameForClass(cls: AnyClass) -> String?
public class func setClassName(codedName: String?, forClass cls: AnyClass)

These methods can be used to allow for translation of classes by registering the appropriate classes for a “shortened” name that drops the Foundation/SwiftFoundation namespace prefix during encoding.

On Dec 22, 2015, at 2:45 AM, Luke Howard via swift-corelibs-dev <swift-corelibs-dev@swift.org <mailto:swift-corelibs-dev@swift.org>> wrote:

On 22 Dec 2015, at 5:50 AM, Jordan Rose <jordan_rose@apple.com <mailto:jordan_rose@apple.com>> wrote:

IMHO on Linux NSKeyedArchiver should always use mangled names. If we want cross-platform archives, we should set up standard substitutions, but given that Swift classes exposed to Objective-C are archived with their full names it doesn't make sense to use "half the name" in the archive.

You mean namespaced but unmangled yes? If so I agree.

BTW I found a couple of small CF nits:

* in CFDictionaryGetKeysAndValues(), keybuf and valuebuf are transposed in the call to CF_SWIFT_FUNCDISPATCHV(NSDictionary.getObjects())

* _CFSwiftDictionaryGetKeysAndValues() does not handle keybuf or valbuf being NULL (either of which are valid when calling CFDictionaryGetKeysAndValues())

This is a bit un-related to NSCoding and the transposition is probably a mistake if it is inverted (the CF method should be reversed from the NS method to mimic the objc counterpart)

— Luke
_______________________________________________
swift-corelibs-dev mailing list
swift-corelibs-dev@swift.org <mailto:swift-corelibs-dev@swift.org>
https://lists.swift.org/mailman/listinfo/swift-corelibs-dev

_______________________________________________
swift-corelibs-dev mailing list
swift-corelibs-dev@swift.org <mailto:swift-corelibs-dev@swift.org>
https://lists.swift.org/mailman/listinfo/swift-corelibs-dev


(Luke Howard) #15

Some work on NSKeyedArchiver below:

https://github.com/lhoward/swift-corelibs-foundation/tree/lhoward/nscoding

Very incomplete (most classes don’t have coders, there is no unarchiver, inexperienced Swift programmer).

— Luke


(Luke Howard) #16

Ah, thanks for pointing this out!

NSClassFromString() – which it appears Foundation uses when coding – returns a non-mangled name for a one/top-level non-generic class type (e.g. “scoderTest.CodableTest”) but otherwise the mangled type name (e.g. “_TtCC10scoderTest11CodableTest6Nested”).

Is there an equivalent function in Swift stdlib?

— Luke

···

On 24 Dec 2015, at 9:37 AM, Jordan Rose <jordan_rose@apple.com> wrote:

No, we cannot encode things "non-mangled but with the namespace". For any type other than top-level non-generic class types, using a non-mangled name is not unique. The only correct answer for arbitrary classes is to use mangled names, or something that maps one-to-one with mangled names.


(Jordan Rose) #17

Here's another example on OS X:

import Foundation

class Outer {
    class Inner : NSObject, NSCoding {
        let uuid: Foundation.NSUUID
        required init?(coder aDecoder: NSCoder) {
            uuid = aDecoder.decodeObjectForKey("my.uuid") as! Foundation.NSUUID
        }
        override init() {
            uuid = Foundation.NSUUID()
        }
        func encodeWithCoder(aCoder: NSCoder) {
            aCoder.encodeObject(uuid, forKey: "my.uuid")
        }
    }
}

NSKeyedArchiver.archiveRootObject(Outer.Inner(), toFile: "/Users/jrose/Desktop/test-archive")

Which results in this archive:

{
  "$version" => 100000
  "$objects" => [
    0 => "$null"
    1 => {
      "my.uuid" => <CFKeyedArchiverUID 0x7f8992c0e9d0 [0x7fff7c5acd80]>{value = 2}
      "$class" => <CFKeyedArchiverUID 0x7f8992c0ec90 [0x7fff7c5acd80]>{value = 4}
    }
    2 => {
      "NS.uuidbytes" => <67f0b08b c8274f8c b0c78d90 bd4627dc>
      "$class" => <CFKeyedArchiverUID 0x7f8992c0eda0 [0x7fff7c5acd80]>{value = 3}
    }
    3 => {
      "$classname" => "NSUUID"
      "$classes" => [
        0 => "NSUUID"
        1 => "NSObject"
      ]
    }
    4 => {
      "$classname" => "_TtCC4main5Outer5Inner"
      "$classes" => [
        0 => "_TtCC4main5Outer5Inner"
        1 => "NSObject"
      ]
    }
  ]
  "$archiver" => "NSKeyedArchiver"
  "$top" => {
    "root" => <CFKeyedArchiverUID 0x7f8992c0f0c0 [0x7fff7c5acd80]>{value = 1}
  }
}

NSStringFromClass makes pretty names when they fall into the "simple" category, but that's not an arbitrarily extensible transformation, and NSCoding shouldn't have to know anything about it.

Jordan

···

On Dec 23, 2015, at 14:48, Philippe Hausler <phausler@apple.com> wrote:

The archiving format encodes the names of the classes in the archive itself. Here are a few code examples and a quasi readable output from them:

let uuid = NSUUID()
let data = NSKeyedArchiver.archivedDataWithRootObject(uuid)
let archive = try! NSPropertyListSerialization.propertyListWithData(data, options: [], format: nil)
print(archive)

prints the following:

{
    "$archiver" = NSKeyedArchiver;
    "$objects" = (
        "$null",
                {
            "$class" = "<CFKeyedArchiverUID 0x1030025e0 [0x7fff7ab33bb0]>{value = 2}";
            "NS.uuidbytes" = <797639fe dad74b14 902afab3 c490448b>;
        },
                {
            "$classes" = (
                NSUUID,
                NSObject
            );
            "$classname" = NSUUID;
        }
    );
    "$top" = {
        root = "<CFKeyedArchiverUID 0x103002a80 [0x7fff7ab33bb0]>{value = 1}";
    };
    "$version" = 100000;
}

Note the $classes and $classname objects; which are what tell the internal implementation of NSKeyedUnarchiver what to construct; moreover you can create your own classes..

// I don’t really think this is a good naming for an application’s class but hey it might happen...
class NSUUID : NSObject, NSCoding {
    let uuid: Foundation.NSUUID
    required init?(coder aDecoder: NSCoder) {
        uuid = aDecoder.decodeObjectForKey("my.uuid") as! Foundation.NSUUID
    }
    override init() {
        uuid = Foundation.NSUUID()
    }
    func encodeWithCoder(aCoder: NSCoder) {
        aCoder.encodeObject(uuid, forKey: "my.uuid")
    }
}

let uuid = NSUUID()
let data = NSKeyedArchiver.archivedDataWithRootObject(uuid)
let archive = try! NSPropertyListSerialization.propertyListWithData(data, options: [], format: nil)
print(archive)

prints the following:

{
    "$archiver" = NSKeyedArchiver;
    "$objects" = (
        "$null",
                {
            "$class" = "<CFKeyedArchiverUID 0x100709630 [0x7fff7ab33bb0]>{value = 4}";
            "my.uuid" = "<CFKeyedArchiverUID 0x100708e60 [0x7fff7ab33bb0]>{value = 2}";
        },
                {
            "$class" = "<CFKeyedArchiverUID 0x100709740 [0x7fff7ab33bb0]>{value = 3}";
            "NS.uuidbytes" = <546e5b5e 15c244a1 aa96eb90 30c3f7f6>;
        },
                {
            "$classes" = (
                NSUUID,
                NSObject
            );
            "$classname" = NSUUID;
        },
                {
            "$classes" = (
                "Archiver.NSUUID",
                NSObject
            );
            "$classname" = "Archiver.NSUUID";
        }
    );
    "$top" = {
        root = "<CFKeyedArchiverUID 0x100709b70 [0x7fff7ab33bb0]>{value = 1}";
    };
    "$version" = 100000;
}

Granted this is a questionable name for a class but it illustrates which class names are encoded where and how they should be interpreted in the pre-existing archive format; which we will have to figure out some sensible way of inflating and deflating to/from disk/network etc.

On Dec 23, 2015, at 2:37 PM, Jordan Rose <jordan_rose@apple.com <mailto:jordan_rose@apple.com>> wrote:

No, we cannot encode things "non-mangled but with the namespace". For any type other than top-level non-generic class types, using a non-mangled name is not unique. The only correct answer for arbitrary classes is to use mangled names, or something that maps one-to-one with mangled names.

Now, Foundation classes are not arbitrary classes, but then I don't see why we'd need to use mangled names for those. We can just use the plain old Objective-C names that the OS X classes use today.

Jordan

On Dec 22, 2015, at 10:16, Philippe Hausler via swift-corelibs-dev <swift-corelibs-dev@swift.org <mailto:swift-corelibs-dev@swift.org>> wrote:

To clarify the goals: I think it is reasonable for us to have a goal to be able to encode/decode archives from foreign targets; e.g. linux encodes an archive and mac os x decodes or iOS encodes and linux decodes. This will allow for server architecture to transmit binary archives across the wire. This will mean that we will want to have the encoded class names from the application scope to be encoded as the non mangled name but with the namespace. However this presents a problem; Foundation will have a namespace which will need to be inferred both for encoding and decoding. Thankfully there may be a reasonable way to approach this;

public class func classNameForClass(cls: AnyClass) -> String?
public class func setClassName(codedName: String?, forClass cls: AnyClass)

These methods can be used to allow for translation of classes by registering the appropriate classes for a “shortened” name that drops the Foundation/SwiftFoundation namespace prefix during encoding.

On Dec 22, 2015, at 2:45 AM, Luke Howard via swift-corelibs-dev <swift-corelibs-dev@swift.org <mailto:swift-corelibs-dev@swift.org>> wrote:

On 22 Dec 2015, at 5:50 AM, Jordan Rose <jordan_rose@apple.com <mailto:jordan_rose@apple.com>> wrote:

IMHO on Linux NSKeyedArchiver should always use mangled names. If we want cross-platform archives, we should set up standard substitutions, but given that Swift classes exposed to Objective-C are archived with their full names it doesn't make sense to use "half the name" in the archive.

You mean namespaced but unmangled yes? If so I agree.

BTW I found a couple of small CF nits:

* in CFDictionaryGetKeysAndValues(), keybuf and valuebuf are transposed in the call to CF_SWIFT_FUNCDISPATCHV(NSDictionary.getObjects())

* _CFSwiftDictionaryGetKeysAndValues() does not handle keybuf or valbuf being NULL (either of which are valid when calling CFDictionaryGetKeysAndValues())

This is a bit un-related to NSCoding and the transposition is probably a mistake if it is inverted (the CF method should be reversed from the NS method to mimic the objc counterpart)

— Luke
_______________________________________________
swift-corelibs-dev mailing list
swift-corelibs-dev@swift.org <mailto:swift-corelibs-dev@swift.org>
https://lists.swift.org/mailman/listinfo/swift-corelibs-dev

_______________________________________________
swift-corelibs-dev mailing list
swift-corelibs-dev@swift.org <mailto:swift-corelibs-dev@swift.org>
https://lists.swift.org/mailman/listinfo/swift-corelibs-dev


(Philippe Hausler) #18

NSCoding will have to use something to transform from strings to classes, and that satisfy the two cases (or more) that we have already shown, currently there is no thing that does that in either form; either mangled or non mangled. Basically we need something to implement NSClassFromString with. Which we have clearly shown that dlsym does not fully meet the needs since there are cases that will emit as “module.classname” and others that emit as the mangled name. The simple case is probably going to be the a very common usage pattern for consumers (and of previously built applications). The inner class should definitely be handled in addition to this case.

Are there any methods that can fetch the name (either the symbolic or the readable) given a AnyClass in the runtime to get work started here? I think it is definitely sensible as a start to restrict this just to descendants of the class NSObject. I would presume that since the Metadata is potentially volatile contents we should use something along the lines of swift_getTypeName etc?

···

On Dec 23, 2015, at 2:53 PM, Jordan Rose <jordan_rose@apple.com> wrote:

Here's another example on OS X:

import Foundation

class Outer {
    class Inner : NSObject, NSCoding {
        let uuid: Foundation.NSUUID
        required init?(coder aDecoder: NSCoder) {
            uuid = aDecoder.decodeObjectForKey("my.uuid") as! Foundation.NSUUID
        }
        override init() {
            uuid = Foundation.NSUUID()
        }
        func encodeWithCoder(aCoder: NSCoder) {
            aCoder.encodeObject(uuid, forKey: "my.uuid")
        }
    }
}

NSKeyedArchiver.archiveRootObject(Outer.Inner(), toFile: "/Users/jrose/Desktop/test-archive")

Which results in this archive:

{
  "$version" => 100000
  "$objects" => [
    0 => "$null"
    1 => {
      "my.uuid" => <CFKeyedArchiverUID 0x7f8992c0e9d0 [0x7fff7c5acd80]>{value = 2}
      "$class" => <CFKeyedArchiverUID 0x7f8992c0ec90 [0x7fff7c5acd80]>{value = 4}
    }
    2 => {
      "NS.uuidbytes" => <67f0b08b c8274f8c b0c78d90 bd4627dc>
      "$class" => <CFKeyedArchiverUID 0x7f8992c0eda0 [0x7fff7c5acd80]>{value = 3}
    }
    3 => {
      "$classname" => "NSUUID"
      "$classes" => [
        0 => "NSUUID"
        1 => "NSObject"
      ]
    }
    4 => {
      "$classname" => "_TtCC4main5Outer5Inner"
      "$classes" => [
        0 => "_TtCC4main5Outer5Inner"
        1 => "NSObject"
      ]
    }
  ]
  "$archiver" => "NSKeyedArchiver"
  "$top" => {
    "root" => <CFKeyedArchiverUID 0x7f8992c0f0c0 [0x7fff7c5acd80]>{value = 1}
  }
}

NSStringFromClass makes pretty names when they fall into the "simple" category, but that's not an arbitrarily extensible transformation, and NSCoding shouldn't have to know anything about it.

Jordan

On Dec 23, 2015, at 14:48, Philippe Hausler <phausler@apple.com <mailto:phausler@apple.com>> wrote:

The archiving format encodes the names of the classes in the archive itself. Here are a few code examples and a quasi readable output from them:

let uuid = NSUUID()
let data = NSKeyedArchiver.archivedDataWithRootObject(uuid)
let archive = try! NSPropertyListSerialization.propertyListWithData(data, options: [], format: nil)
print(archive)

prints the following:

{
    "$archiver" = NSKeyedArchiver;
    "$objects" = (
        "$null",
                {
            "$class" = "<CFKeyedArchiverUID 0x1030025e0 [0x7fff7ab33bb0]>{value = 2}";
            "NS.uuidbytes" = <797639fe dad74b14 902afab3 c490448b>;
        },
                {
            "$classes" = (
                NSUUID,
                NSObject
            );
            "$classname" = NSUUID;
        }
    );
    "$top" = {
        root = "<CFKeyedArchiverUID 0x103002a80 [0x7fff7ab33bb0]>{value = 1}";
    };
    "$version" = 100000;
}

Note the $classes and $classname objects; which are what tell the internal implementation of NSKeyedUnarchiver what to construct; moreover you can create your own classes..

// I don’t really think this is a good naming for an application’s class but hey it might happen...
class NSUUID : NSObject, NSCoding {
    let uuid: Foundation.NSUUID
    required init?(coder aDecoder: NSCoder) {
        uuid = aDecoder.decodeObjectForKey("my.uuid") as! Foundation.NSUUID
    }
    override init() {
        uuid = Foundation.NSUUID()
    }
    func encodeWithCoder(aCoder: NSCoder) {
        aCoder.encodeObject(uuid, forKey: "my.uuid")
    }
}

let uuid = NSUUID()
let data = NSKeyedArchiver.archivedDataWithRootObject(uuid)
let archive = try! NSPropertyListSerialization.propertyListWithData(data, options: [], format: nil)
print(archive)

prints the following:

{
    "$archiver" = NSKeyedArchiver;
    "$objects" = (
        "$null",
                {
            "$class" = "<CFKeyedArchiverUID 0x100709630 [0x7fff7ab33bb0]>{value = 4}";
            "my.uuid" = "<CFKeyedArchiverUID 0x100708e60 [0x7fff7ab33bb0]>{value = 2}";
        },
                {
            "$class" = "<CFKeyedArchiverUID 0x100709740 [0x7fff7ab33bb0]>{value = 3}";
            "NS.uuidbytes" = <546e5b5e 15c244a1 aa96eb90 30c3f7f6>;
        },
                {
            "$classes" = (
                NSUUID,
                NSObject
            );
            "$classname" = NSUUID;
        },
                {
            "$classes" = (
                "Archiver.NSUUID",
                NSObject
            );
            "$classname" = "Archiver.NSUUID";
        }
    );
    "$top" = {
        root = "<CFKeyedArchiverUID 0x100709b70 [0x7fff7ab33bb0]>{value = 1}";
    };
    "$version" = 100000;
}

Granted this is a questionable name for a class but it illustrates which class names are encoded where and how they should be interpreted in the pre-existing archive format; which we will have to figure out some sensible way of inflating and deflating to/from disk/network etc.

On Dec 23, 2015, at 2:37 PM, Jordan Rose <jordan_rose@apple.com <mailto:jordan_rose@apple.com>> wrote:

No, we cannot encode things "non-mangled but with the namespace". For any type other than top-level non-generic class types, using a non-mangled name is not unique. The only correct answer for arbitrary classes is to use mangled names, or something that maps one-to-one with mangled names.

Now, Foundation classes are not arbitrary classes, but then I don't see why we'd need to use mangled names for those. We can just use the plain old Objective-C names that the OS X classes use today.

Jordan

On Dec 22, 2015, at 10:16, Philippe Hausler via swift-corelibs-dev <swift-corelibs-dev@swift.org <mailto:swift-corelibs-dev@swift.org>> wrote:

To clarify the goals: I think it is reasonable for us to have a goal to be able to encode/decode archives from foreign targets; e.g. linux encodes an archive and mac os x decodes or iOS encodes and linux decodes. This will allow for server architecture to transmit binary archives across the wire. This will mean that we will want to have the encoded class names from the application scope to be encoded as the non mangled name but with the namespace. However this presents a problem; Foundation will have a namespace which will need to be inferred both for encoding and decoding. Thankfully there may be a reasonable way to approach this;

public class func classNameForClass(cls: AnyClass) -> String?
public class func setClassName(codedName: String?, forClass cls: AnyClass)

These methods can be used to allow for translation of classes by registering the appropriate classes for a “shortened” name that drops the Foundation/SwiftFoundation namespace prefix during encoding.

On Dec 22, 2015, at 2:45 AM, Luke Howard via swift-corelibs-dev <swift-corelibs-dev@swift.org <mailto:swift-corelibs-dev@swift.org>> wrote:

On 22 Dec 2015, at 5:50 AM, Jordan Rose <jordan_rose@apple.com <mailto:jordan_rose@apple.com>> wrote:

IMHO on Linux NSKeyedArchiver should always use mangled names. If we want cross-platform archives, we should set up standard substitutions, but given that Swift classes exposed to Objective-C are archived with their full names it doesn't make sense to use "half the name" in the archive.

You mean namespaced but unmangled yes? If so I agree.

BTW I found a couple of small CF nits:

* in CFDictionaryGetKeysAndValues(), keybuf and valuebuf are transposed in the call to CF_SWIFT_FUNCDISPATCHV(NSDictionary.getObjects())

* _CFSwiftDictionaryGetKeysAndValues() does not handle keybuf or valbuf being NULL (either of which are valid when calling CFDictionaryGetKeysAndValues())

This is a bit un-related to NSCoding and the transposition is probably a mistake if it is inverted (the CF method should be reversed from the NS method to mimic the objc counterpart)

— Luke
_______________________________________________
swift-corelibs-dev mailing list
swift-corelibs-dev@swift.org <mailto:swift-corelibs-dev@swift.org>
https://lists.swift.org/mailman/listinfo/swift-corelibs-dev

_______________________________________________
swift-corelibs-dev mailing list
swift-corelibs-dev@swift.org <mailto:swift-corelibs-dev@swift.org>
https://lists.swift.org/mailman/listinfo/swift-corelibs-dev


(Jordan Rose) #19

"We need to get ahold of a class given a name" is definitely a requirement to do NSCoding right. I'm not at all convinced dlsym is a valid long-term answer for that, though. If you have an 'internal' class, it doesn't (currently) have a public symbol that you can use dlsym for.

This sort of goes with the existing problem of static registration: there's no pure Swift way to get all subclasses of a class, or to get a class from a name for any other reason. That's a general language problem, though, and we should discuss it on swift-dev.

Jordan

···

On Dec 23, 2015, at 15:12, Philippe Hausler <phausler@apple.com> wrote:

NSCoding will have to use something to transform from strings to classes, and that satisfy the two cases (or more) that we have already shown, currently there is no thing that does that in either form; either mangled or non mangled. Basically we need something to implement NSClassFromString with. Which we have clearly shown that dlsym does not fully meet the needs since there are cases that will emit as “module.classname” and others that emit as the mangled name. The simple case is probably going to be the a very common usage pattern for consumers (and of previously built applications). The inner class should definitely be handled in addition to this case.

Are there any methods that can fetch the name (either the symbolic or the readable) given a AnyClass in the runtime to get work started here? I think it is definitely sensible as a start to restrict this just to descendants of the class NSObject. I would presume that since the Metadata is potentially volatile contents we should use something along the lines of swift_getTypeName etc?

On Dec 23, 2015, at 2:53 PM, Jordan Rose <jordan_rose@apple.com <mailto:jordan_rose@apple.com>> wrote:

Here's another example on OS X:

import Foundation

class Outer {
    class Inner : NSObject, NSCoding {
        let uuid: Foundation.NSUUID
        required init?(coder aDecoder: NSCoder) {
            uuid = aDecoder.decodeObjectForKey("my.uuid") as! Foundation.NSUUID
        }
        override init() {
            uuid = Foundation.NSUUID()
        }
        func encodeWithCoder(aCoder: NSCoder) {
            aCoder.encodeObject(uuid, forKey: "my.uuid")
        }
    }
}

NSKeyedArchiver.archiveRootObject(Outer.Inner(), toFile: "/Users/jrose/Desktop/test-archive")

Which results in this archive:

{
  "$version" => 100000
  "$objects" => [
    0 => "$null"
    1 => {
      "my.uuid" => <CFKeyedArchiverUID 0x7f8992c0e9d0 [0x7fff7c5acd80]>{value = 2}
      "$class" => <CFKeyedArchiverUID 0x7f8992c0ec90 [0x7fff7c5acd80]>{value = 4}
    }
    2 => {
      "NS.uuidbytes" => <67f0b08b c8274f8c b0c78d90 bd4627dc>
      "$class" => <CFKeyedArchiverUID 0x7f8992c0eda0 [0x7fff7c5acd80]>{value = 3}
    }
    3 => {
      "$classname" => "NSUUID"
      "$classes" => [
        0 => "NSUUID"
        1 => "NSObject"
      ]
    }
    4 => {
      "$classname" => "_TtCC4main5Outer5Inner"
      "$classes" => [
        0 => "_TtCC4main5Outer5Inner"
        1 => "NSObject"
      ]
    }
  ]
  "$archiver" => "NSKeyedArchiver"
  "$top" => {
    "root" => <CFKeyedArchiverUID 0x7f8992c0f0c0 [0x7fff7c5acd80]>{value = 1}
  }
}

NSStringFromClass makes pretty names when they fall into the "simple" category, but that's not an arbitrarily extensible transformation, and NSCoding shouldn't have to know anything about it.

Jordan

On Dec 23, 2015, at 14:48, Philippe Hausler <phausler@apple.com <mailto:phausler@apple.com>> wrote:

The archiving format encodes the names of the classes in the archive itself. Here are a few code examples and a quasi readable output from them:

let uuid = NSUUID()
let data = NSKeyedArchiver.archivedDataWithRootObject(uuid)
let archive = try! NSPropertyListSerialization.propertyListWithData(data, options: [], format: nil)
print(archive)

prints the following:

{
    "$archiver" = NSKeyedArchiver;
    "$objects" = (
        "$null",
                {
            "$class" = "<CFKeyedArchiverUID 0x1030025e0 [0x7fff7ab33bb0]>{value = 2}";
            "NS.uuidbytes" = <797639fe dad74b14 902afab3 c490448b>;
        },
                {
            "$classes" = (
                NSUUID,
                NSObject
            );
            "$classname" = NSUUID;
        }
    );
    "$top" = {
        root = "<CFKeyedArchiverUID 0x103002a80 [0x7fff7ab33bb0]>{value = 1}";
    };
    "$version" = 100000;
}

Note the $classes and $classname objects; which are what tell the internal implementation of NSKeyedUnarchiver what to construct; moreover you can create your own classes..

// I don’t really think this is a good naming for an application’s class but hey it might happen...
class NSUUID : NSObject, NSCoding {
    let uuid: Foundation.NSUUID
    required init?(coder aDecoder: NSCoder) {
        uuid = aDecoder.decodeObjectForKey("my.uuid") as! Foundation.NSUUID
    }
    override init() {
        uuid = Foundation.NSUUID()
    }
    func encodeWithCoder(aCoder: NSCoder) {
        aCoder.encodeObject(uuid, forKey: "my.uuid")
    }
}

let uuid = NSUUID()
let data = NSKeyedArchiver.archivedDataWithRootObject(uuid)
let archive = try! NSPropertyListSerialization.propertyListWithData(data, options: [], format: nil)
print(archive)

prints the following:

{
    "$archiver" = NSKeyedArchiver;
    "$objects" = (
        "$null",
                {
            "$class" = "<CFKeyedArchiverUID 0x100709630 [0x7fff7ab33bb0]>{value = 4}";
            "my.uuid" = "<CFKeyedArchiverUID 0x100708e60 [0x7fff7ab33bb0]>{value = 2}";
        },
                {
            "$class" = "<CFKeyedArchiverUID 0x100709740 [0x7fff7ab33bb0]>{value = 3}";
            "NS.uuidbytes" = <546e5b5e 15c244a1 aa96eb90 30c3f7f6>;
        },
                {
            "$classes" = (
                NSUUID,
                NSObject
            );
            "$classname" = NSUUID;
        },
                {
            "$classes" = (
                "Archiver.NSUUID",
                NSObject
            );
            "$classname" = "Archiver.NSUUID";
        }
    );
    "$top" = {
        root = "<CFKeyedArchiverUID 0x100709b70 [0x7fff7ab33bb0]>{value = 1}";
    };
    "$version" = 100000;
}

Granted this is a questionable name for a class but it illustrates which class names are encoded where and how they should be interpreted in the pre-existing archive format; which we will have to figure out some sensible way of inflating and deflating to/from disk/network etc.

On Dec 23, 2015, at 2:37 PM, Jordan Rose <jordan_rose@apple.com <mailto:jordan_rose@apple.com>> wrote:

No, we cannot encode things "non-mangled but with the namespace". For any type other than top-level non-generic class types, using a non-mangled name is not unique. The only correct answer for arbitrary classes is to use mangled names, or something that maps one-to-one with mangled names.

Now, Foundation classes are not arbitrary classes, but then I don't see why we'd need to use mangled names for those. We can just use the plain old Objective-C names that the OS X classes use today.

Jordan

On Dec 22, 2015, at 10:16, Philippe Hausler via swift-corelibs-dev <swift-corelibs-dev@swift.org <mailto:swift-corelibs-dev@swift.org>> wrote:

To clarify the goals: I think it is reasonable for us to have a goal to be able to encode/decode archives from foreign targets; e.g. linux encodes an archive and mac os x decodes or iOS encodes and linux decodes. This will allow for server architecture to transmit binary archives across the wire. This will mean that we will want to have the encoded class names from the application scope to be encoded as the non mangled name but with the namespace. However this presents a problem; Foundation will have a namespace which will need to be inferred both for encoding and decoding. Thankfully there may be a reasonable way to approach this;

public class func classNameForClass(cls: AnyClass) -> String?
public class func setClassName(codedName: String?, forClass cls: AnyClass)

These methods can be used to allow for translation of classes by registering the appropriate classes for a “shortened” name that drops the Foundation/SwiftFoundation namespace prefix during encoding.

On Dec 22, 2015, at 2:45 AM, Luke Howard via swift-corelibs-dev <swift-corelibs-dev@swift.org <mailto:swift-corelibs-dev@swift.org>> wrote:

On 22 Dec 2015, at 5:50 AM, Jordan Rose <jordan_rose@apple.com <mailto:jordan_rose@apple.com>> wrote:

IMHO on Linux NSKeyedArchiver should always use mangled names. If we want cross-platform archives, we should set up standard substitutions, but given that Swift classes exposed to Objective-C are archived with their full names it doesn't make sense to use "half the name" in the archive.

You mean namespaced but unmangled yes? If so I agree.

BTW I found a couple of small CF nits:

* in CFDictionaryGetKeysAndValues(), keybuf and valuebuf are transposed in the call to CF_SWIFT_FUNCDISPATCHV(NSDictionary.getObjects())

* _CFSwiftDictionaryGetKeysAndValues() does not handle keybuf or valbuf being NULL (either of which are valid when calling CFDictionaryGetKeysAndValues())

This is a bit un-related to NSCoding and the transposition is probably a mistake if it is inverted (the CF method should be reversed from the NS method to mimic the objc counterpart)

— Luke
_______________________________________________
swift-corelibs-dev mailing list
swift-corelibs-dev@swift.org <mailto:swift-corelibs-dev@swift.org>
https://lists.swift.org/mailman/listinfo/swift-corelibs-dev

_______________________________________________
swift-corelibs-dev mailing list
swift-corelibs-dev@swift.org <mailto:swift-corelibs-dev@swift.org>
https://lists.swift.org/mailman/listinfo/swift-corelibs-dev


(Luke Howard) #20

NSCoding will have to use something to transform from strings to classes, and that satisfy the two cases (or more) that we have already shown, currently there is no thing that does that in either form; either mangled or non mangled. Basically we need something to implement NSClassFromString with. Which we have clearly shown that dlsym does not fully meet the needs since there are cases that will emit as “module.classname” and others that emit as the mangled name. The simple case is probably going to be the a very common usage pattern for consumers (and of previously built applications). The inner class should definitely be handled in addition to this case.

* If the mangled name is present in the archive, you can re-mangle it to get the metadata accessor
* If it’s a one-level unmangled name representing a class, it can be mangled
* If it’s a zero-level unmangled name, then it seems reasonable for a first implementation to assume it’s a SwiftFoundation class (or that the caller has set an explicit mapping)

Noted that dlsym() will only work with public symbols.

Are there any methods that can fetch the name (either the symbolic or the readable) given a AnyClass in the runtime to get work started here? I think it is definitely sensible as a start to restrict this just to descendants of the class NSObject. I would presume that since the Metadata is potentially volatile contents we should use something along the lines of swift_getTypeName etc?

I have been using _typeName() but it always demangles – for interop with existing archives we need to match the behaviour of libobjc's class_getName() (equivalent to NSStringFromClass), which appears to demangle one-level classes.

— Luke

···

On 24 Dec 2015, at 10:12 AM, Philippe Hausler <phausler@apple.com> wrote: