Skip to content

Commit

Permalink
Rework pointer conversion and NSArray construction to prevent misuse.
Browse files Browse the repository at this point in the history
  • Loading branch information
maleadt committed Feb 14, 2024
1 parent 52bee4c commit 8ffbf57
Show file tree
Hide file tree
Showing 3 changed files with 21 additions and 7 deletions.
9 changes: 6 additions & 3 deletions src/foundation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -179,9 +179,12 @@ end

NSArray() = NSArray(@objc [NSArray array]::id{NSArray})

function NSArray(elements::Vector)
arr = @objc [NSArray arrayWithObjects:elements::Ptr{id{Object}}
count:length(elements)::NSUInteger]::id{NSArray}
function NSArray(elements::Vector{<:NSObject})
arr = GC.@preserve elements begin
pointers = [element.ptr for element in elements]
@objc [NSArray arrayWithObjects:pointers::id{Object}
count:length(elements)::NSUInteger]::id{NSArray}
end
return NSArray(arr)
end

Expand Down
17 changes: 13 additions & 4 deletions src/syntax.jl
Original file line number Diff line number Diff line change
Expand Up @@ -269,8 +269,6 @@ macro objcwrapper(ex...)
ex = quote
$(ex.args...)

Base.unsafe_convert(T::Type{<:id}, dev::$instance) = convert(T, dev.ptr)

# add a pseudo constructor to the abstract type that also checks for nil pointers.
function $name(ptr::id)
ptr == nil && throw(UndefRefError())
Expand All @@ -283,14 +281,25 @@ macro objcwrapper(ex...)
ex = quote
$(ex.args...)

Base.:(==)(a::$instance, b::$instance) = a.ptr == b.ptr
Base.hash(dev::$instance, h::UInt) = hash(dev.ptr, h)
Base.:(==)(a::$instance, b::$instance) = pointer(a) == pointer(b)
Base.hash(obj::$instance, h::UInt) = hash(pointer(obj), h)

Check warning on line 285 in src/syntax.jl

View check run for this annotation

Codecov / codecov/patch

src/syntax.jl#L284-L285

Added lines #L284 - L285 were not covered by tests
end
end

esc(ex)
end

Base.pointer(obj::Object) = obj.ptr

# when passing a single object, we automatically convert it to a pointer
Base.unsafe_convert(T::Type{<:id}, obj::Object) = convert(T, pointer(obj))

# in the case of a vector of objects, we expect the caller to have converted them
# XXX: it's too bad `cconvert` cannot do the `[pointer(obj) for obj in objs]` for us
# (because we can only derive unsafe references in `unsafe_convert`)
Base.unsafe_convert(T::Type{<:id}, ptrs::Vector{<:id}) =
reinterpret(T, pointer(ptrs))


# Property Accesors

Expand Down
2 changes: 2 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ end
@test reinterpret(NSString, arr2[1]) == "Hello"
@test reinterpret(NSString, arr2[2]) == "World"
@test Vector{NSString}(arr2) == arr1

@test_throws MethodError NSArray([NSUInteger(42)])
end

@testset "NSDictionary" begin
Expand Down

0 comments on commit 8ffbf57

Please sign in to comment.