Skip to content

Commit 809e5e5

Browse files
committed
Update availability parsing
1 parent ae803cf commit 809e5e5

File tree

2 files changed

+32
-19
lines changed

2 files changed

+32
-19
lines changed

src/syntax.jl

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,13 @@ PlatformAvailability(; introduced = nothing, deprecated = nothing, obsoleted = n
269269
PlatformAvailability(introduced, deprecated, obsoleted, unavailable)
270270

271271
export macos
272+
273+
"""
274+
macos(introduced[, deprecated, obsoleted, unavailable])
275+
macos(; [introduced, deprecated, obsoleted, unavailable])
276+
277+
Represents an availability statement for Objective-C wrappers.
278+
"""
272279
struct macos
273280
availability::PlatformAvailability
274281
macos(args...) = new(PlatformAvailability(args...))
@@ -314,17 +321,17 @@ function Base.showerror(io::IO, e::UnavailableError)
314321
return
315322
end
316323

317-
function _getmacosavailabilityexpr(value)
318-
Meta.isexpr(value, :vect) || Meta.isexpr(value, :call) && value.args[1] == :macos || wrappererror("availability keyword argument must be a `macos(v\"x.y\")` statement")
319-
if Meta.isexpr(value, :vect)
320-
for expr in value.args
321-
if Meta.isexpr(expr, :call) && expr.args[1] == :macos
322-
return expr
323-
end
324-
end
325-
return nothing
326-
else
327-
return value
324+
function _getmacosavailability(expr)
325+
try
326+
# Don't run arbitrary code
327+
Meta.isexpr(expr, :vect) || Meta.isexpr(expr, :call) && expr.args[1] == :macos || error()
328+
329+
avail = eval(expr)
330+
# Returns the first `macos` object in the vector, otherwise
331+
# the error gets caught and a helpful message is displayed
332+
return avail isa macos ? avail : avail[findfirst(x -> x isa macos, avail)]
333+
catch
334+
wrappererror("`availability` keyword argument must be a valid `macos` constructor or a vector thereof")
328335
end
329336
end
330337

@@ -351,7 +358,7 @@ keyword arguments:
351358
352359
* `immutable`: if `true` (default), define the instance class as an immutable. Should be
353360
disabled when you want to use finalizers.
354-
* `availability`: A version string that represents the first macOS version where this object is available.
361+
* `availability`: A `macos` object that represents the availability of the object.
355362
* `comparison`: if `true` (default `false`), define `==` and `hash` methods for the
356363
wrapper class. This should not be necessary when using an immutable struct, in which
357364
case the default `==` and `hash` methods are sufficient.
@@ -374,7 +381,7 @@ macro objcwrapper(ex...)
374381
value isa Bool || wrappererror("immutable keyword argument must be a literal boolean")
375382
immutable = value
376383
elseif kw == :availability
377-
availability = ObjectiveC._getmacosavailabilityexpr(value)
384+
availability = ObjectiveC._getmacosavailability(value)
378385
else
379386
wrappererror("unrecognized keyword argument: $kw")
380387
end
@@ -384,7 +391,7 @@ macro objcwrapper(ex...)
384391
end
385392
immutable = something(immutable, true)
386393
comparison = something(comparison, !immutable)
387-
availability = something(availability, :(macos(v"0")))
394+
availability = something(availability, macos(v"0"))
388395

389396
# parse class definition
390397
if Meta.isexpr(def, :(<:))
@@ -498,9 +505,8 @@ contains a series of property declarations:
498505
- `setter`: specifies the name of the Objective-C setter method. Without this, no
499506
`setproperty!` definition will be generated.
500507
- `getter`: specifies the name of the Objective-C getter method. Without this, the
501-
getter method is assumed to be identical to the property
502-
- `availability`: specifies earliest macOS version where this property became available,
503-
similar to the Objective-C attribute of the same name
508+
getter method is assumed to be identical to the property.
509+
- `availability`: A `macos` object that represents the availability of the property.
504510
- `@getproperty myProperty function(obj) ... end`: define a custom getter for the property.
505511
The function should take a single argument `obj`, which is the object that the property is
506512
being accessed on. The function should return the property value.
@@ -578,9 +584,9 @@ macro objcproperties(typ, ex)
578584

579585
availability = nothing
580586
if haskey(kwargs, :availability)
581-
availability = ObjectiveC._getmacosavailabilityexpr(kwargs[:availability])
587+
availability = ObjectiveC._getmacosavailability(kwargs[:availability])
582588
end
583-
availability = something(availability, :(macos(v"0")))
589+
availability = something(availability, macos(v"0"))
584590

585591
getterproperty = if haskey(kwargs, :getter)
586592
kwargs[:getter]

test/runtests.jl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,13 @@ end
7272
vectprop = TestVectAvail(@objc [NSString stringWithUTF8String:str1::Ptr{UInt8}]::id{TestVectAvail})
7373
@test_throws "UnavailableError: `TestVectAvail.VectUnavailableProperty` was introduced on macOS v1000.0.0" vectprop.VectUnavailableProperty
7474

75+
@test_throws "UnavailableError: `TestVectAvail.VectUnavailableProperty` was introduced on macOS v1000.0.0" vectprop.VectUnavailableProperty
76+
@test_throws "UnavailableError: `TestVectAvail.VectUnavailableProperty` was introduced on macOS v1000.0.0" vectprop.VectUnavailableProperty
77+
78+
@test_throws "ObjectiveC wrapper: `availability`" macroexpand(@__MODULE__, :(@objcwrapper availability = templeos(v"1000") TestBadAvail2 <: Object))
79+
@test_throws "ObjectiveC wrapper: `availability`" macroexpand(@__MODULE__, :(@objcwrapper availability = [templeos(v"1000")] TestBadAvail3 <: Object))
80+
@test_throws "ObjectiveC wrapper: `availability`" macroexpand(@__MODULE__, :(@objcwrapper availability = [6] TestBadAvail4 <: Object))
81+
@test_throws "ObjectiveC wrapper: `availability`" macroexpand(@__MODULE__, :(@objcwrapper availability = 6 TestBadAvail5 <: Object))
7582
end
7683

7784
@testset "@objc macro" begin

0 commit comments

Comments
 (0)