Skip to content

Commit

Permalink
Follow-up to Availability PR (#54)
Browse files Browse the repository at this point in the history
  • Loading branch information
christiangnrd authored Feb 13, 2025
1 parent a37c331 commit 8c78cc7
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 20 deletions.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name = "ObjectiveC"
uuid = "e86c9b32-1129-44ac-8ea0-90d5bb39ded9"
version = "3.4.0"
version = "3.4.1"

[deps]
CEnum = "fa961155-64e5-5f13-b03f-caf6b980ea82"
Expand Down
35 changes: 20 additions & 15 deletions src/availability.jl
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
export PlatformAvailability, UnavailableError

# Each platform tuple has a symbol representing the constructor, a pretty name for errors,
# and a symbol of the function used to check the version for that platform
const SUPPORTED_PLATFORMS = [(:macos, "macOS", :macos_version), (:darwin, "Darwin", :darwin_version)]
# Each platform tuple has a symbol representing the constructor, a pretty_name name for errors,
# a symbol of the function used to check the version for that platform, and the function that
# returns whether that statement applies for this device
const SUPPORTED_PLATFORMS = Dict(
:macos => ("macOS", :macos_version, Sys.isapple),
:darwin => ("Darwin", :darwin_version, Sys.isapple),
:test => ("Never applicable", :error, () -> false)
)

# Based off of Clang's `CXPlatformAvailability`
"""
Expand All @@ -21,20 +26,20 @@ struct PlatformAvailability{P}
obsoleted::Union{Nothing, VersionNumber}
unavailable::Bool

function PlatformAvailability(platform::Symbol, introduced, deprecated = nothing, obsoleted = nothing, unavailable = false)
platform in first.(SUPPORTED_PLATFORMS) || throw(ArgumentError(lazy"`:$platform` is not a supported platform for `PlatformAvailability`, see `?PlatformAvailability` for more information."))
return new{platform}(introduced, deprecated, obsoleted, unavailable)
function PlatformAvailability(p::Symbol, introduced, deprecated = nothing, obsoleted = nothing, unavailable = false)
haskey(SUPPORTED_PLATFORMS, p) || throw(ArgumentError(lazy"`:$p` is not a supported platform for `PlatformAvailability`, see `?PlatformAvailability` for more information."))
return new{p}(introduced, deprecated, obsoleted, unavailable)
end
end
PlatformAvailability(platform; introduced = nothing, deprecated = nothing, obsoleted = nothing, unavailable = false) =
PlatformAvailability(platform, introduced, deprecated, obsoleted, unavailable)

function is_unavailable(f::Function, avail::PlatformAvailability)
return avail.unavailable ||
(!isnothing(avail.obsoleted) && f() >= avail.obsoleted) ||
(!isnothing(avail.introduced) && f() < avail.introduced)
function is_available(f::Function, avail::PlatformAvailability)
return !avail.unavailable &&
(isnothing(avail.obsoleted) || f() < avail.obsoleted) &&
(isnothing(avail.introduced) || f() >= avail.introduced)
end
is_unavailable(avails::Vector{<:PlatformAvailability}) = any(is_unavailable.(avails))
is_available(avails::Vector{<:PlatformAvailability}) = all(is_available.(avails))

"""
UnavailableError(symbol::Symbol, minver::VersionNumber)
Expand All @@ -59,7 +64,7 @@ function UnavailableError(f::Function, symbol::Symbol, platform::String, avail::
return UnavailableError(symbol, msg)
end
function UnavailableError(symbol::Symbol, avails::Vector{<:PlatformAvailability})
firsterror = findfirst(is_unavailable, avails)
firsterror = findfirst(!is_available, avails)
return UnavailableError(symbol, avails[firsterror])
end

Expand All @@ -69,11 +74,11 @@ function Base.showerror(io::IO, e::UnavailableError)
end

# Platform-specific definitions
for (name, pretty_name, version_function) in SUPPORTED_PLATFORMS
for (name, (pretty_name, ver_func, plat_func)) in SUPPORTED_PLATFORMS
quotname = Meta.quot(name)
@eval begin
is_unavailable(avail::PlatformAvailability{$quotname}) = is_unavailable($version_function, avail)
UnavailableError(symbol::Symbol, avail::PlatformAvailability{$quotname}) = UnavailableError($version_function, symbol, $pretty_name, avail)
is_available(avail::PlatformAvailability{$quotname}) = !$plat_func() || is_available($ver_func, avail)
UnavailableError(symbol::Symbol, avail::PlatformAvailability{$quotname}) = UnavailableError($ver_func, symbol, $pretty_name, avail)
end
end

Expand Down
8 changes: 4 additions & 4 deletions src/syntax.jl
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ macro objcwrapper(ex...)
end
immutable = something(immutable, true)
comparison = something(comparison, !immutable)
availability = something(availability, PlatformAvailability(:macos, v"0"))
availability = something(availability, PlatformAvailability[])

# parse class definition
if Meta.isexpr(def, :(<:))
Expand Down Expand Up @@ -352,7 +352,7 @@ macro objcwrapper(ex...)

# add a pseudo constructor to the abstract type that also checks for nil pointers.
function $name(ptr::id)
@static if !Sys.isapple() || ObjectiveC.is_unavailable($availability)
@static if !ObjectiveC.is_available($availability)
throw($UnavailableError(Symbol($name), $availability))
end

Expand Down Expand Up @@ -506,7 +506,7 @@ macro objcproperties(typ, ex)
if haskey(kwargs, :availability)
availability = get_avail_exprs(__module__, kwargs[:availability])
end
availability = something(availability, PlatformAvailability(:macos, v"0"))
availability = something(availability, PlatformAvailability[])

getterproperty = if haskey(kwargs, :getter)
kwargs[:getter]
Expand All @@ -515,7 +515,7 @@ macro objcproperties(typ, ex)
end
getproperty_ex = objcm(__module__, :([object::id{$(esc(typ))} $getterproperty]::$srcTyp))
getproperty_ex = quote
@static if !Sys.isapple() || ObjectiveC.is_unavailable($availability)
@static if !ObjectiveC.is_available($availability)
throw($UnavailableError(Symbol($(esc(typ)), ".", field), $availability))
end
value = $(Expr(:var"hygienic-scope", getproperty_ex, @__MODULE__, __source__))
Expand Down
10 changes: 10 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ end
# Availability
@objcwrapper availability = macos(v"1000") TestWrapperNoIntro1 <: Object
@objcwrapper availability = macos(introduced = v"1000") TestWrapperNoIntro2 <: Object
@objcwrapper availability = test(introduced = v"1000") TestIgnore <: Object
@objcwrapper availability = macos(deprecated = v"1", obsoleted = v"2.3.4") TestWrapperObsolete <: Object
@objcwrapper availability = macos(introduced = v"1000", unavailable = true) TestWrapperUnavailable <: Object
@objcwrapper availability = macos(v"0") TestPropAvail <: Object
Expand All @@ -25,6 +26,7 @@ end
@objcwrapper availability = [macos(v"1000"), darwin(v"0")] TestVectMultiple1 <: Object
@objcwrapper availability = [macos(v"0"), darwin(v"1000")] TestVectMultiple2 <: Object
@objcwrapper availability = [macos(v"0"), darwin(v"0")] TestVectMultiple3 <: Object
@objcwrapper availability = [macos(v"0"), test(v"1000")] TestVectMultiple4 <: Object
@objcwrapper availability = [macos(v"0")] TestVectAvail <: Object
@objcproperties TestVectAvail begin
@autoproperty length::Culong
Expand All @@ -41,6 +43,10 @@ end
fakeidwrap = id{TestWrapperNoIntro2}(1)
@test_throws "UnavailableError: `TestWrapperNoIntro2` was introduced on macOS v1000.0.0" TestWrapperNoIntro2(fakeidwrap)
end
let # Not-applicable platform ignored
fakeidwrap = id{TestIgnore}(1)
@test TestIgnore(fakeidwrap) isa TestIgnore
end
let # obsolete
fakeidwrap = id{TestWrapperObsolete}(1)
@test_throws "UnavailableError: `TestWrapperObsolete` is obsolete since macOS v2.3.4" TestWrapperObsolete(fakeidwrap)
Expand All @@ -65,6 +71,10 @@ end
fakeidwrap = id{TestVectMultiple3}(1)
@test TestVectMultiple3(fakeidwrap) isa TestVectMultiple3
end
let # Not-applicable platform ignored
fakeidwrap = id{TestVectMultiple4}(1)
@test TestVectMultiple4(fakeidwrap) isa TestVectMultiple4
end

# property
str1 = "foo"
Expand Down

0 comments on commit 8c78cc7

Please sign in to comment.