Open NSExceptional opened 3 years ago
What's really weird to me is if I break right here on the kind
declaration and do po type(of: value)
, I get the class I expect. But when I po type
inside Kind(type:)
I get Any
func withValuePointer<Value, Result>(of value: inout Value, _ body: ...) throws -> Result {
--> let kind = Kind(type: Value.self) // po type(of: value) → SwiftEXTests.SwiftEXTests
let obj = value as AnyObject
let cls: AnyClass = object_getClass(obj)!
let same = cls === cls
switch kind {
case .struct:
return try withUnsafePointer(to: &value) { try body($0.mutable.raw) }
case .class:
return try withClassValuePointer(of: &value, body)
case .existential:
return try withClassValuePointer(of: &value, body)
default:
throw RuntimeError.couldNotGetPointer(type: Value.self, value: value)
}
}
(Ignore the code I added for myself for testing)
public enum Kind {
...
init(type: Any.Type) {
--> let pointer = metadataPointer(type: type) // po type(of: value) → Any
self.init(flag: pointer.pointee)
}
}
Update: even if I force it to return Kind.class
, it looks like you're not accounting for inheritance… the reported offset of this particular ivar is 56
and the actual offset reported by ivar_getOffset
is 272
So Value.self
in that context is actually an Any
so existential
is correct. The Property.get
takes an Any
and so the original type would be boxed in the existential container.
As for inheritance, it may be an objc thing but I am not too sure at the moment, Ill keep looking into it though. The offset
stored on the field descriptor should already take that into account.
I wrote a quick unit test in your project to verify:
class Foo {
let a: Int = 1
}
class Bar: Foo {
let b = Baz()
}
class Baz {}
let bar = Bar()
let info = try typeInfo(of: Bar.self)
let aProp = try info.property(named: "a")
let a: Int = try aProp.get(from: bar) as! Int
let bProp = try info.property(named: "b")
let b = try bProp.get(from: bar) as! Baz
XCTAssert(a == 1)
XCTAssert(b === bar.b)
I think the rules are different across module boundaries or when subclassing an objc class
So Value.self in that context is actually an Any so existential is correct. The Property.get takes an Any and so the original type would be boxed in the existential container.
Shouldn't Value.self
be equivalent to type(of: value)
in that context? The debugger showed me what I expected. If it's always an existential, would it still be able to find and access fields? o_O
In a generic context things get a bit tricky, type of type(of: value)
will return Value.self
which is Any
. To get the actual under lying type you'd have to do type(of: value as Any)
.
If it's always an existential, would it still be able to find and access fields? o_O
It manually unboxes the value. Its done like this to simplify things. Having it always boxed made it a bit more predicable.
Check out the docs on it https://developer.apple.com/documentation/swift/2885064-type . See the "Finding the Dynamic Type in a Generic Context" section.
Also here's an interesting example to help illustrate:
struct Foo {}
func holdMyBeer<T>(val: T) {
print(T.self)
print(type(of: val))
print(type(of: val as Any))
}
let val = 1
holdMyBeer(val: val) // Int, Int, Int
holdMyBeer(val: val as Any) // Any, Any, Int
Sorry didn't mean to close it
That's crazy. Well then I guess the only problem is the ivar offset thing, which I've been working on.
Tell me, is one of the members in ClassMetadataLayout
equivalent to startOfImmediateMembers
? I think that's the flag Joe was referring to (or something like it anyway) but I'm not sure, and I can't tell if your struct has it defined already under a slightly different name or not
I'm not sure what's going on, but whatever it is I think I might be doing something wrong.
Here is a project that demonstrates the issue: https://github.com/FLEXTool/SwiftEX
Select the
SwiftEXTests
scheme and run thetestMirror()
test. It fails. My testing shows that for some reason,Value.self
inside the call towithValuePointer()
is seen as an existential instead of a class type. It appears to be correct, too, which tells me I'm the one doing something wrong.The gist of what I'm trying to do is to wrap your APIs and make it work like a readwrite
Mirror
, so that I don't have to fumble with.property(named:)
andproperty.set(value:on:)
orproperty.get(on:)
every time I want to do something.Any help is appreciated!