Skip to content

Fixed infinite recursion in NSCharacterSet.isEqual #1092

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

Merged
merged 1 commit into from
Jul 9, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions Foundation/NSCharacterSet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,18 @@ open class NSCharacterSet : NSObject, NSCopying, NSMutableCopying, NSCoding {
}

open override func isEqual(_ value: Any?) -> Bool {
guard let runtimeClass = _CFRuntimeGetClassWithTypeID(CFCharacterSetGetTypeID()) else {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is rather sub-optimal of a way to approach the type testing

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@phausler Could you please explain a bit more? Isn't this what CFEqual does under the hood?

I'm open for suggestions.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, but the problem is that it is reaching into CF internals pretty heavily - I have a patch in the works that will resolve this (and passes your unit test) without needing to reach into CF guts so much. This is going the opposite direction of the factory version of NSCharacterSet (which generalizes this method without needing to reach into CF internals)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@phausler I assumed it was good enough because I didn't need to expose anything from CF.
I also noticed you were working around this area and expected your changed would address this as well, but was not sure if and when.

When do you expect to have those in?
And do you suggest dropping this PR?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this branch I am working in I have those tests enabled as well (and they are passing). I have a few more things to wrap up before pushing that as an addendum to the factory_generalizations. (it isn't ready for pushing yet since I am working on getting a few more goodies published as well)

fatalError("Could not obtain CFRuntimeClass of CFCharacterSet")
}

guard let equalFunction = runtimeClass.pointee.equal else {
fatalError("Could not obtain equal function from CFRuntimeClass of CFCharacterSet")
}

switch value {
case let other as CharacterSet:
return CFEqual(_cfObject, other._cfObject)
case let other as NSCharacterSet:
return CFEqual(_cfObject, other._cfObject)
default:
return false
case let other as CharacterSet: return equalFunction(self._cfObject, other._cfObject) == true
case let other as NSCharacterSet: return equalFunction(self._cfObject, other._cfObject) == true
default: return false
}
}

Expand Down
24 changes: 24 additions & 0 deletions TestFoundation/TestNSCharacterSet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class TestNSCharacterSet : XCTestCase {
("test_AnnexPlanes", test_AnnexPlanes),
("test_Planes", test_Planes),
("test_InlineBuffer", test_InlineBuffer),
("test_Equatable", test_Equatable),
// The following tests must remain disabled until SR-2509 is resolved.
// ("test_Subtracting", test_Subtracting),
// ("test_SubtractEmptySet", test_SubtractEmptySet),
Expand Down Expand Up @@ -282,5 +283,28 @@ class TestNSCharacterSet : XCTestCase {
let expected = CharacterSet(charactersIn: "abc")
XCTAssertEqual(expected, symmetricDifference)
}

func test_Equatable() {
XCTAssertEqual(NSCharacterSet(charactersIn: ""), NSCharacterSet(charactersIn: ""))
XCTAssertEqual(NSCharacterSet(charactersIn: "a"), NSCharacterSet(charactersIn: "a"))
XCTAssertEqual(NSCharacterSet(charactersIn: "ab"), NSCharacterSet(charactersIn: "ab"))

XCTAssertNotEqual(NSCharacterSet(charactersIn: "abc"), NSCharacterSet(charactersIn: "123"))
XCTAssertNotEqual(NSCharacterSet(charactersIn: "123"), NSCharacterSet(charactersIn: "abc"))

XCTAssertNotEqual(NSCharacterSet(charactersIn: ""), nil)

/*
Tests disabled due to CoreFoundation bug?
These NSCharacterSet pairs are (wrongly?) evaluated to be equal. Same behaviour can be observed on macOS 10.12.
Interestingly, on iOS 11 Simulator, they are evaluted to be _not_ equal,
while on iOS 10.3.1 Simulator, they are evaluted to be equal.
*/
// XCTAssertNotEqual(NSCharacterSet(charactersIn: "ab"), NSCharacterSet(charactersIn: "abc"))
// XCTAssertNotEqual(NSCharacterSet(charactersIn: "abc"), NSCharacterSet(charactersIn: "ab"))
// XCTAssertNotEqual(NSCharacterSet(charactersIn: "abc"), NSCharacterSet(charactersIn: ""))
// XCTAssertNotEqual(NSCharacterSet(charactersIn: ""), NSCharacterSet(charactersIn: "abc"))
}

}