-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Use CFNumberCreate()
as factory initializer on NSNumber
#1093
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
Use CFNumberCreate()
as factory initializer on NSNumber
#1093
Conversation
Expected results are sampled from macOS
…sing `_Factory` protocol
…SNumber` is Boolean
…edicate.test_NSCoding()`
Foundation/NSNumber.swift
Outdated
@@ -198,14 +198,20 @@ extension NSNumber : ExpressibleByFloatLiteral, ExpressibleByIntegerLiteral, Exp | |||
|
|||
} | |||
|
|||
@_silgen_name("_CFNumberGetType2") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We really should not use silgen_name. Instead can you just put this in ForSwiftFoundationOnly.h?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the difference between ForSwiftFoundationOnly.h
and ForFoundationOnly.h
?
It seems _CFNumberInit*()
functions are placed in ForFoundationOnly.h
.
Should _CFNumberGetType2
also be placed in ForFoundationOnly.h
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ForFoundationOnly.h is used in both the CoreFoundation shipped on Darwin as well as swift-corlibs-foundation, whereas ForSwiftFoundationOnly.h is used for swift-corelibs-foundation exclusively.
Putting it in ForSwitFoundationOnly.h means that I can merge the closed source version and the open source version easier ;)
var high: Int64 | ||
var low: UInt64 | ||
} | ||
|
||
open class NSNumber : NSValue { | ||
typealias CFType = CFNumber | ||
// This layout MUST be the same as CFNumber so that they are bridgeable | ||
private var _base = _CFInfo(typeID: CFNumberGetTypeID()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we are using factories for all numbers then we can delete this!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These properties are necessary to maintain memory layout compatibility of NSDecimalNumber
.
@@ -78,7 +78,10 @@ internal class __NSCFBoolean : NSNumber { | |||
override var objCType: UnsafePointer<Int8> { | |||
// This must never be fixed to be "B", although that would | |||
// cause correct old-style archiving when this is unarchived. | |||
return UnsafePointer<Int8>(bitPattern: UInt(_NSSimpleObjCType.Char.rawValue.value))! | |||
func _objCType(_ staticString: StaticString) -> UnsafePointer<Int8> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this trick better than the way I was thinking, however I wonder if we have a guarantee this is safe?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't the static string need to be a static property to guarantee its lifetime?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not fully certain which way this falls, constant strings should emit static references in the binary so it is probably safe-ish?
We have a few alternatives; we could thunk out to C and use the real @encode
since we always have clang available. We could thunk out to C and reference C strings as opaque memory blobs. This approach. Or the other thought I had was something like this https://gist.github.com/phausler/2630092f1b391d6a5db0a562288514bf
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking along the same lines, but simpler:
fileprivate let boolEncoding: StaticString = "B"
fileprivate let boolEncodingPtr: UnsafePointer<Int8> = {
precondition(boolEncoding.isASCII)
return UnsafeRawPointer(boolEncoding.utf8Start).assumingMemoryBound(to: Int8.self)
}()
// etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yea, I think there is also value in abstracting it out to an isolated file to do that work in and provide a shared internal (minimal) version of @encode
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I discarded following codes that rewrote _NSSimpleObjCType
in NSObjCRuntime.swift
, since memory layout is not compatible with current implementation.
https://gist.github.com/norio-nomura/a1383f258c38bb1e081597a538f46e8f
Should we create another static values for @encode
?
I feel we should do re-writing _NSSimpleObjCType
instead of adding them, and doing that with this PR will make it too large.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I tested, utf8Start
points directly cstring in __TEXT,__cstring,cstring_literals
whether StaticString
is instantiated as static variable or function parameter.
return UnsafeRawPointer(staticString.utf8Start).assumingMemoryBound(to: Int8.self) | ||
} | ||
let numberType = _CFNumberGetType2(_cfObject) | ||
switch numberType { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Iirc gettype2 will only emit SInt* and Float* types
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't know. It looks __CFNumberTypeTable.canonicalType
contains only those types. 👍
The sil_gen part is the only absolute thing that is required |
Can we remove |
lets hold off on removing _CFNumberInit just yet, I need to verify we are not using that in the objc version (which I was considering) |
Ah, sorry, it seems I have to fix |
CFNumberCreate()
as factory initializer on NSNumber
CFNumberCreate()
as factory initializer on NSNumber
This reverts commit bb182da.
Removing stored instance properties caused issues on |
CFNumberCreate()
as factory initializer on NSNumber
CFNumberCreate()
as factory initializer on NSNumber
I can follow up with the removals of _CFNumberInit* and the ivars. |
@swift-ci please test and merge |
Thanks! 🙏 |
I forgot that I have to ask a question. The results of test are different between
On
Is not this a problem? |
For now that is probably fine, my changes will hopefully bring our strange conditionals per 64 vs 32 bit in more pairity |
This replaces #1025 by further utilizing the Factory Initializer Method (SR-5255).
Use
CFNumberCreate()
as factory initializer onNSNumber.init(value:)
NSNumber.init(value:)
to returning instance created byCFNumberCreate()
NSNumber._objCType
Change the properties of
NSNumber
to callCFNumberGetValue ()
in the same way asFoundation
.Fixes SR-4907
TestNSNumber.test_objCType()
reproducing crashExpected results are sampled from macOS
objCType
property ofNSNumber
Change
NSNumber.objCType
to returning same values with darwin platform versionNSNumber.objCType
to returning same values with darwinNSNumber.init(value: Bool)
to returning__NSCFBoolean
by using_Factory
protocolSince
NSNumber.objCType
returnsc
with both ofBool
andInt8
on darwin platform,NSNumber.init(value: Bool)
is changed to returns__NSCFBoolean
by using_Factory
protocol, and it changed to use_cfTypeID
to distinguish between Bool and Int8.JSONSerialization
to use_cfTypeID
for checking whetherNSNumber
is Boolean or not_JSONDecoder
to use_cfTypeID
for checking whetherNSNumber
is Boolean or notNSNumber.compare(_:)
to useobjCType
TestNumber.allTests
NSKeyedUnarchiver.decodeBool(forKey:)
and add test toTestNSPredicate.test_NSCoding()