objc2/macros/define_class.rs
1/// Create a new Objective-C class.
2///
3/// This is useful in many cases since Objective-C frameworks tend to favour a
4/// design pattern using "delegates", where to hook into a piece of
5/// functionality in a class, you implement that class' delegate protocol in
6/// a custom class.
7///
8/// This macro is the declarative way of creating classes, in contrast with
9/// [`ClassBuilder`], which allows creating classes in an imperative fashion.
10/// It is highly recommended that you use this macro though, since it contains
11/// a lot of extra debug assertions and niceties that help ensure the
12/// soundness of your code.
13///
14/// The class is guaranteed to have been created and registered with the
15/// Objective-C runtime after the [`ClassType::class`] function has been
16/// called.
17///
18/// See [Apple's documentation] on defining classes for a more in-depth
19/// introduction.
20///
21/// [Apple's documentation]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/DefiningClasses/DefiningClasses.html
22/// [`ClassBuilder`]: crate::runtime::ClassBuilder
23/// [`ClassType::class`]: crate::ClassType::class
24///
25///
26/// # Specification
27///
28/// This macro consists of the following parts:
29/// - The type definition, along with special attributes.
30/// - Any number of inherent implementations.
31/// - Any number of protocol implementations.
32///
33/// With the syntax generally resembling a combination of that in
34/// [`extern_class!`] and [`extern_methods!`].
35///
36/// This macro creates an opaque struct with implementations in [a similar
37/// manner as the `extern_class!` macro][ec_spec]. Additionally, it implements
38/// the [`DefinedClass`] trait, as well as any protocols specified in the
39/// protocol implementations.
40///
41/// If the type implements [`Drop`], the macro will generate a `dealloc`
42/// method for you, which will call `drop` automatically.
43///
44/// The macro does not support generic types.
45///
46/// [`extern_class!`]: crate::extern_class
47/// [`extern_methods!`]: crate::extern_methods
48/// [ec_spec]: crate::extern_class#specification
49/// [`DefinedClass`]: crate::DefinedClass
50///
51///
52/// ## Attributes
53///
54/// You can add most normal attributes to the class, including `#[cfg(...)]`,
55/// `#[allow(...)]` and doc comments.
56///
57/// Exceptions and special attributes are noted below.
58///
59///
60/// ### `#[unsafe(super(...))]` (required)
61///
62/// Same [as in `extern_class!`](crate::extern_class#unsafesuper-required).
63///
64///
65/// ### `#[thread_kind = ...]` (optional)
66///
67/// Same [as in `extern_class!`](crate::extern_class#thread_kind---optional).
68///
69///
70/// ### `#[name = "..."]` (optional)
71///
72/// Specify the runtime-name for the class. Must be unique across the entire
73/// application. This is useful if the name of a class is used elsewhere, such
74/// as when defining a delegate that needs to be named in e.g. `Info.plist`.
75///
76/// If not set, this will default to:
77/// ```ignore
78/// concat!(module_path!(), "::", $class, env!("CARGO_PKG_VERSION"));
79/// ```
80///
81/// E.g. for example `"my_crate::my_module::MyClass0.1.0"`.
82///
83/// If you're developing a library, it is recommended that you do not set
84/// this, and instead rely on the default naming, since that usually works
85/// better with users having multiple SemVer-incompatible versions of your
86/// library in the same binary.
87///
88///
89/// ### `#[ivars = ...]` (optional)
90///
91/// Controls [the instance variables] of the class; this is the intended way
92/// to specify the data your class stores. If you don't set this attribute,
93/// the macro will default to [`()`][unit].
94///
95/// It is recommended that you wrap your instance variables in [`Cell`],
96/// [`RefCell`], atomics or other similar interior mutability abstractions to
97/// allow mutating your instance variables. See [the docs on interior
98/// mutability][interior_mutability] for further details.
99///
100/// Beware that if you want to use the class' inherited initializers (such as
101/// `init`), you must override the subclass' designated initializers, and
102/// initialize your ivars properly in there.
103///
104/// [the instance variables]: crate::DefinedClass::Ivars
105/// [`Cell`]: core::cell::Cell
106/// [`RefCell`]: core::cell::RefCell
107/// [interior_mutability]: crate::topics::interior_mutability
108///
109///
110/// ### `#[derive(...)]`
111///
112/// This is overridden, and only works with [`PartialEq`], [`Eq`], [`Hash`]
113/// and [`Debug`].
114///
115/// The implementations delegate to the superclass' implementation, so if you
116/// want to change how they work, you should override the [`isEqual:`] and
117/// [`hash`] methods instead.
118///
119/// The `Debug` implementation currently also debug print your ivars, but that
120/// may change in the future. Prefer to override [`description`] (and
121/// potentially [`debugDescription`]) instead.
122///
123/// [`Hash`]: std::hash::Hash
124/// [`Debug`]: std::fmt::Debug
125/// [`isEqual:`]: crate::runtime::NSObjectProtocol::isEqual
126/// [`hash`]: crate::runtime::NSObjectProtocol::hash
127/// [`description`]: crate::runtime::NSObjectProtocol::description
128/// [`debugDescription`]: crate::runtime::NSObjectProtocol::debugDescription
129///
130///
131/// ### `#[cfg_attr(..., ...)]`
132///
133/// Same [as in `extern_class!`](crate::extern_class#cfg_attr-).
134///
135///
136/// ### `#[repr(...)]`
137///
138/// Same [as in `extern_class!`](crate::extern_class#repr).
139///
140///
141/// ## Inherent method definitions
142///
143/// Within the `impl` block you can define two types of functions;
144/// ["associated functions"] and ["methods"]. These are then mapped to the
145/// Objective-C equivalents "class methods" and "instance methods". In
146/// particular, if you use `self` or the special name `this` (or `_this`),
147/// your method will be registered as an instance method, and if you don't it
148/// will be registered as a class method.
149///
150/// On instance methods, you can freely choose between different types of
151/// receivers, e.g. `&self`, `self: *const Self`, `this: *const Self`, and so
152/// on. Note that using `&mut self` is not possible, if you need mutation of
153/// your class' instance variables, consider using [`Cell`] or similar
154/// instead.
155///
156/// The desired selector can be specified using the
157/// `#[unsafe(method(my:selector:))]` or `#[unsafe(method_id(my:selector:))]`
158/// attributes, similar to the [`extern_methods!`] macro.
159///
160/// If the `#[unsafe(method_id(...))]` attribute is used, the return type must
161/// be `Option<Retained<T>>` or `Retained<T>`. Additionally, if the selector
162/// is in the "init"-family, the `self`/`this` parameter must be
163/// `Allocated<Self>`.
164///
165/// Putting other attributes on the method such as `cfg`, `allow`, `doc`,
166/// `deprecated` and so on is supported. However, note that `cfg_attr` may not
167/// work correctly, due to implementation difficulty - if you have a concrete
168/// use-case, please [open an issue], then we can discuss it.
169///
170/// A transformation step is performed on the functions (to make them have the
171/// correct ABI) and hence they shouldn't really be called manually. (You
172/// can't mark them as `pub` for the same reason). Instead, use the
173/// [`extern_methods!`] macro to create a Rust interface to the methods.
174///
175/// If the parameter or return type is [`bool`], a conversion is performed to
176/// make it behave similarly to the Objective-C `BOOL`. Use [`runtime::Bool`]
177/// if you want to control this manually.
178///
179/// Note that `&mut Retained<_>` and other such out parameters are not yet
180/// supported, and may generate a panic at runtime.
181///
182/// ["associated functions"]: https://doc.rust-lang.org/reference/items/associated-items.html#methods
183/// ["methods"]: https://doc.rust-lang.org/reference/items/associated-items.html#methods
184/// [`Cell`]: core::cell::Cell
185/// [open an issue]: https://github.com/madsmtm/objc2/issues/new
186/// [`msg_send!`]: crate::msg_send
187/// [`runtime::Bool`]: crate::runtime::Bool
188///
189///
190/// ## Protocol implementations
191///
192/// You can specify protocols that the class should implement, along with any
193/// required/optional methods for said protocols.
194///
195/// The protocol must have been previously defined with [`extern_protocol!`].
196///
197/// The methods work exactly as normal, they're only put "under" the protocol
198/// definition to make things easier to read.
199///
200/// Putting attributes on the `impl` item such as `cfg`, `allow`, `doc`,
201/// `deprecated` and so on is supported.
202///
203/// [`extern_protocol!`]: crate::extern_protocol
204///
205///
206/// # Panics
207///
208/// The implemented `ClassType::class` method may panic in a few cases, such
209/// as if:
210/// - A class with the specified name already exists.
211/// - Debug assertions are enabled, and an overridden method's signature is not
212/// equal to the one on the superclass.
213/// - Debug assertions are enabled, and the protocol's required methods are not
214/// implemented.
215///
216/// And possibly more similar cases in the future.
217///
218///
219/// # Safety
220///
221/// Using this macro requires writing a lot of `unsafe` markers:
222///
223/// When writing `#[unsafe(super(...))]`, you must ensure that:
224/// - Any invariants that the superclass [`ClassType::Super`] may have must be
225/// upheld.
226/// - If your type implements `Drop`, the implementation must abide by the
227/// following rules:
228/// - It must not call any overridden methods.
229/// - It must not `retain` the object past the lifetime of the drop.
230/// - It must not `retain` in the same scope that `&mut self` is active.
231/// - TODO: And probably a few more. [Open an issue] if you would like
232/// guidance on whether your implementation is correct.
233///
234/// `#[unsafe(method(...))]` asserts that the types match those that are
235/// expected when the method is invoked from Objective-C. Note that unlike
236/// with [`extern_methods!`], there are no safe-guards here; you can write
237/// `i8`, but if Objective-C thinks it's an `u32`, it will cause UB when
238/// called!
239///
240/// `unsafe impl P for T { ... }` requires that all required methods of the
241/// specified protocol is implemented, and that any extra requirements
242/// (implicit or explicit) that the protocol has are upheld.
243///
244/// [`ClassType::Super`]: crate::ClassType::Super
245/// [Open an issue]: https://github.com/madsmtm/objc2/issues/new
246///
247///
248/// # Thread safety
249///
250/// The Objective-C runtime is thread-safe, so any classes you create yourself
251/// will automatically be [`Send`] and [`Sync`] (via auto traits) provided
252/// that:
253/// 1. The superclass is thread-safe, or is [`NSObject`].
254/// 2. The ivars are thread-safe.
255/// 3. The thread kind is not [`MainThreadOnly`].
256///
257/// Note though that in many cases, [the frameworks] you will be interacting
258/// with will not be thread-safe, and so in many cases it will make sense to
259/// [use interior mutability] in your custom classes.
260///
261/// [`NSObject`]: crate::runtime::NSObject
262/// [`MainThreadOnly`]: crate::MainThreadOnly
263/// [the frameworks]: crate::topics::about_generated
264/// [use interior mutability]: crate::topics::interior_mutability
265///
266///
267/// # Examples
268///
269/// Define a class `MyCustomObject` that inherits `NSObject`, has a few
270/// instance variables and methods, and implements the `NSCopying` protocol.
271///
272/// ```
273/// use std::ffi::c_int;
274///
275/// use objc2_foundation::{CopyingHelper, NSCopying, NSObject, NSObjectProtocol, NSZone};
276/// use objc2::rc::{Allocated, Retained};
277/// use objc2::{
278/// define_class, extern_methods, extern_protocol, msg_send, AnyThread,
279/// ClassType, DefinedClass, ProtocolType,
280/// };
281///
282/// #[derive(Clone)]
283/// struct Ivars {
284/// foo: u8,
285/// bar: c_int,
286/// object: Retained<NSObject>,
287/// }
288///
289/// define_class!(
290/// // SAFETY:
291/// // - The superclass NSObject does not have any subclassing requirements.
292/// // - `MyCustomObject` does not implement `Drop`.
293/// #[unsafe(super(NSObject))]
294///
295/// // If we were implementing delegate methods like `NSApplicationDelegate`,
296/// // we would specify the object to only be usable on the main thread:
297/// // #[thread_kind = MainThreadOnly]
298///
299/// // If we needed to refer to the class from elsewhere, we'd give it a
300/// // name here explicitly.
301/// // #[name = "MyCustomObject"]
302///
303/// // Specify the instance variables this class has.
304/// #[ivars = Ivars]
305/// struct MyCustomObject;
306///
307/// impl MyCustomObject {
308/// #[unsafe(method(foo))]
309/// fn __get_foo(&self) -> u8 {
310/// self.ivars().foo
311/// }
312///
313/// #[unsafe(method_id(object))]
314/// fn __get_object(&self) -> Retained<NSObject> {
315/// self.ivars().object.clone()
316/// }
317///
318/// #[unsafe(method(myClassMethod))]
319/// fn __my_class_method() -> bool {
320/// true
321/// }
322/// }
323///
324/// unsafe impl NSObjectProtocol for MyCustomObject {}
325///
326/// unsafe impl NSCopying for MyCustomObject {
327/// #[unsafe(method_id(copyWithZone:))]
328/// fn copyWithZone(&self, _zone: *const NSZone) -> Retained<Self> {
329/// let new = Self::alloc().set_ivars(self.ivars().clone());
330/// unsafe { msg_send![super(new), init] }
331/// }
332///
333/// // If we have tried to add other methods here, or had forgotten
334/// // to implement the method, we would have gotten an error.
335/// }
336/// );
337///
338/// // Specially required for `NSCopying`, but otherwise not needed.
339/// unsafe impl CopyingHelper for MyCustomObject {
340/// type Result = Self;
341/// }
342///
343/// // Add creation method.
344/// impl MyCustomObject {
345/// fn new(foo: u8) -> Retained<Self> {
346/// // Initialize instance variables.
347/// let this = Self::alloc().set_ivars(Ivars {
348/// foo,
349/// bar: 42,
350/// object: NSObject::new(),
351/// });
352/// // Call `NSObject`'s `init` method.
353/// unsafe { msg_send![super(this), init] }
354/// }
355/// }
356///
357/// // Make an interface to the methods we defined.
358/// impl MyCustomObject {
359/// extern_methods!(
360/// #[unsafe(method(foo))]
361/// pub fn get_foo(&self) -> u8;
362///
363/// #[unsafe(method(object))]
364/// pub fn get_object(&self) -> Retained<NSObject>;
365///
366/// #[unsafe(method(myClassMethod))]
367/// pub fn my_class_method() -> bool;
368/// );
369/// }
370///
371/// # // Intentionally use `fn main` for clarity
372/// fn main() {
373/// let obj = MyCustomObject::new(3);
374/// assert_eq!(obj.ivars().foo, 3);
375/// assert_eq!(obj.ivars().bar, 42);
376/// assert!(obj.ivars().object.isKindOfClass(NSObject::class()));
377///
378/// let obj = obj.copy();
379///
380/// assert_eq!(obj.get_foo(), 3);
381/// assert!(obj.get_object().isKindOfClass(NSObject::class()));
382///
383/// assert!(MyCustomObject::my_class_method());
384/// }
385/// ```
386///
387/// Approximately equivalent to the following ARC-enabled Objective-C code.
388///
389/// ```text
390/// #import <Foundation/Foundation.h>
391///
392/// @interface MyCustomObject: NSObject <NSCopying>
393/// - (instancetype)initWithFoo:(uint8_t)foo;
394/// - (uint8_t)foo;
395/// - (NSObject*)object;
396/// + (BOOL)myClassMethod;
397/// @end
398///
399///
400/// @implementation MyCustomObject {
401/// // Instance variables
402/// uint8_t foo;
403/// int bar;
404/// NSObject* _Nonnull object;
405/// }
406///
407/// - (instancetype)initWithFoo:(uint8_t)foo_arg {
408/// self = [super init];
409/// if (self) {
410/// self->foo = foo_arg;
411/// self->bar = 42;
412/// self->object = [NSObject new];
413/// }
414/// return self;
415/// }
416///
417/// - (uint8_t)foo {
418/// return self->foo;
419/// }
420///
421/// - (NSObject*)object {
422/// return self->object;
423/// }
424///
425/// + (BOOL)myClassMethod {
426/// return YES;
427/// }
428///
429/// // NSCopying
430///
431/// - (id)copyWithZone:(NSZone *)_zone {
432/// MyCustomObject* new = [[MyCustomObject alloc] initWithFoo: self->foo];
433/// new->bar = self->bar;
434/// new->obj = self->obj;
435/// return new;
436/// }
437///
438/// @end
439/// ```
440#[doc(alias = "@interface")]
441#[doc(alias = "@implementation")]
442#[macro_export]
443macro_rules! define_class {
444 {
445 // The following special attributes are supported:
446 // - #[unsafe(super($($superclasses:path),*))]
447 // - #[unsafe(super = $superclass:path)]
448 // - #[thread_kind = $thread_kind:path]
449 // - #[name = $name:literal]
450 // - #[ivars = $ivars:path]
451 $(#[$($attrs:tt)*])*
452 $v:vis struct $class:ident;
453
454 // unsafe impl Protocol for $class { ... }
455 // impl $class { ... }
456 $($impls:tt)*
457 } => {
458 // Struct and various impls.
459 $crate::__extract_struct_attributes! {
460 ($(#[$($attrs)*])*)
461
462 ($crate::__define_class_inner)
463 ($v)
464 ($class)
465 ($($impls)*)
466 }
467
468 // Methods.
469 $crate::__define_class_output_impls! {
470 $($impls)*
471 }
472 };
473}
474
475#[doc(hidden)]
476#[macro_export]
477macro_rules! declare_class {
478 {
479 $(#[$m:meta])*
480 $v:vis struct $name:ident;
481
482 unsafe impl ClassType for $for_class:ty {
483 $(#[inherits($($inheritance_rest:ty),+)])?
484 type Super = $superclass:ty;
485
486 type Mutability = $mutability:ty;
487
488 const NAME: &'static str = $name_const:expr;
489 }
490
491 impl DefinedClass for $for_defined:ty {
492 $(type Ivars = $ivars:ty;)?
493 }
494
495 $($impls:tt)*
496 } => {
497 // For slightly better diagnostics
498 $(#[$m])*
499 $v struct $name;
500
501 $crate::__macro_helpers::compile_error!("declare_class! has been renamed to define_class!, and the syntax has changed")
502 }
503}
504
505#[doc(hidden)]
506#[macro_export]
507macro_rules! __define_class_inner {
508 (
509 ($v:vis)
510 ($class:ident)
511 ($($impls:tt)*)
512
513 ($($safety:tt $superclass:path $(, $superclasses:path)* $(,)?)?)
514 ($($($thread_kind:tt)+)?)
515 ($($name:tt)*)
516 ($($ivars:tt)*)
517 ($($derives:tt)*)
518 ($($attr_struct:tt)*)
519 ($($attr_impl:tt)*)
520 ) => {
521 // Ensure that the type has the same layout as the superclass.
522 // #[repr(transparent)] doesn't work because the superclass is a ZST.
523 #[repr(C)]
524 $($attr_struct)*
525 $v struct $class {
526 // Superclasses are deallocated by calling `[super dealloc]`.
527 //
528 // Auto traits are taken from `__SubclassingType` (which is
529 // usually the super class).
530 __superclass: $crate::__macro_helpers::ManuallyDrop<$crate::__fallback_if_not_set! {
531 ($(<$superclass as $crate::ClassType>::__SubclassingType)?)
532 // For better diagnostics, see also __extern_class_inner!
533 ($crate::runtime::NSObject)
534 }>,
535 __phantom: $crate::__macro_helpers::PhantomData<(
536 // Include ivars for auto traits.
537 <Self as $crate::DefinedClass>::Ivars,
538 // Translate thread kind to appropriate auto traits.
539 $crate::__macro_helpers::ThreadKindAutoTraits<<Self as $crate::ClassType>::ThreadKind>,
540 )>,
541 }
542
543 $crate::__extern_class_impl_traits! {
544 ($($attr_impl)*)
545 (unsafe impl)
546 ($class)
547 ($($superclass, $($superclasses,)*)? $crate::runtime::AnyObject)
548 }
549
550 $crate::__define_class_derives! {
551 ($($attr_impl)*)
552 ($class)
553 ($($derives)*)
554 }
555
556 // Anonymous block to hide the shared statics
557 $($attr_impl)*
558 const _: () = {
559 static __OBJC2_CLASS: $crate::__macro_helpers::SyncUnsafeCell<
560 $crate::__macro_helpers::MaybeUninit<&'static $crate::runtime::AnyClass>
561 > = $crate::__macro_helpers::SyncUnsafeCell::new($crate::__macro_helpers::MaybeUninit::uninit());
562 static __OBJC2_IVAR_OFFSET: $crate::__macro_helpers::SyncUnsafeCell<
563 $crate::__macro_helpers::MaybeUninit<$crate::__macro_helpers::isize>
564 > = $crate::__macro_helpers::SyncUnsafeCell::new($crate::__macro_helpers::MaybeUninit::uninit());
565 static __OBJC2_DROP_FLAG_OFFSET: $crate::__macro_helpers::SyncUnsafeCell<
566 $crate::__macro_helpers::MaybeUninit<$crate::__macro_helpers::isize>
567 > = $crate::__macro_helpers::SyncUnsafeCell::new($crate::__macro_helpers::MaybeUninit::uninit());
568
569 // Creation
570 unsafe impl $crate::ClassType for $class {
571 type Super = $crate::__fallback_if_not_set! {
572 ($($superclass)?)
573 // For better diagnostics, see also __extern_class_inner!
574 ($crate::runtime::NSObject)
575 };
576
577 type ThreadKind = $crate::__fallback_if_not_set! {
578 ($(dyn ($($thread_kind)+))?)
579 // Default to the super class' thread kind
580 (<<Self as $crate::ClassType>::Super as $crate::ClassType>::ThreadKind)
581 };
582
583 const NAME: &'static $crate::__macro_helpers::str = $crate::__fallback_if_not_set! {
584 ($($name)*)
585 (
586 $crate::__macro_helpers::concat!(
587 // Module path includes crate name when in library.
588 $crate::__macro_helpers::module_path!(),
589 "::",
590 $crate::__macro_helpers::stringify!($class),
591 $crate::__macro_helpers::env!("CARGO_PKG_VERSION"),
592 )
593 )
594 };
595
596 fn class() -> &'static $crate::runtime::AnyClass {
597 let _ = <Self as $crate::__macro_helpers::ValidThreadKind<Self::ThreadKind>>::check;
598 let _ = <Self as $crate::__macro_helpers::MainThreadOnlyDoesNotImplSendSync<_>>::check;
599
600 // TODO: Use `std::sync::OnceLock`
601 static REGISTER_CLASS: $crate::__macro_helpers::Once = $crate::__macro_helpers::Once::new();
602
603 REGISTER_CLASS.call_once(|| {
604 let mut __objc2_builder = $crate::__macro_helpers::ClassBuilderHelper::<Self>::new();
605
606 // Implement protocols and methods
607 $crate::__define_class_register_impls! {
608 (__objc2_builder)
609 $($impls)*
610 }
611
612 let (__objc2_cls, __objc2_ivar_offset, __objc2_drop_flag_offset) = __objc2_builder.register();
613
614 // SAFETY: Modification is ensured by `Once` to happen
615 // before any access to the variables.
616 unsafe {
617 __OBJC2_CLASS.get().write($crate::__macro_helpers::MaybeUninit::new(__objc2_cls));
618 if <Self as $crate::__macro_helpers::DefinedIvarsHelper>::HAS_IVARS {
619 __OBJC2_IVAR_OFFSET.get().write($crate::__macro_helpers::MaybeUninit::new(__objc2_ivar_offset));
620 }
621 if <Self as $crate::__macro_helpers::DefinedIvarsHelper>::HAS_DROP_FLAG {
622 __OBJC2_DROP_FLAG_OFFSET.get().write($crate::__macro_helpers::MaybeUninit::new(__objc2_drop_flag_offset));
623 }
624 }
625 });
626
627 // SAFETY: We just registered the class, so is now available
628 unsafe { __OBJC2_CLASS.get().read().assume_init() }
629 }
630
631 #[inline]
632 fn as_super(&self) -> &Self::Super {
633 &*self.__superclass
634 }
635
636 const __INNER: () = ();
637
638 type __SubclassingType = Self;
639 }
640
641 impl $crate::DefinedClass for $class {
642 type Ivars = $crate::__select_ivars!($($ivars)?);
643
644 #[inline]
645 fn __ivars_offset() -> $crate::__macro_helpers::isize {
646 // Only access ivar offset if we have an ivar.
647 //
648 // This makes the offset not be included in the final
649 // executable if it's not needed.
650 if <Self as $crate::__macro_helpers::DefinedIvarsHelper>::HAS_IVARS {
651 // SAFETY: Accessing the offset is guaranteed to only be
652 // done after the class has been initialized.
653 unsafe { __OBJC2_IVAR_OFFSET.get().read().assume_init() }
654 } else {
655 // Fall back to an offset of zero.
656 //
657 // This is fine, since any reads here will only be via zero-sized
658 // ivars, where the actual pointer doesn't matter.
659 0
660 }
661 }
662
663 #[inline]
664 fn __drop_flag_offset() -> $crate::__macro_helpers::isize {
665 if <Self as $crate::__macro_helpers::DefinedIvarsHelper>::HAS_DROP_FLAG {
666 // SAFETY: Same as above.
667 unsafe { __OBJC2_DROP_FLAG_OFFSET.get().read().assume_init() }
668 } else {
669 // Fall back to an offset of zero.
670 //
671 // This is fine, since the drop flag is never actually used in the
672 // cases where it was not added.
673 0
674 }
675 }
676
677 // SAFETY: The offsets are implemented correctly
678 const __UNSAFE_OFFSETS_CORRECT: () = ();
679 }
680 };
681
682 // SAFETY: This macro only allows non-generic classes and non-generic
683 // classes are always valid downcast targets.
684 $($attr_impl)*
685 unsafe impl $crate::DowncastTarget for $class {}
686
687 $($attr_impl)*
688 $crate::__extern_class_check_super_unsafe!($($safety $superclass)?);
689 };
690}
691
692/// Mirror of [`crate::__extern_class_derives`].
693#[doc(hidden)]
694#[macro_export]
695macro_rules! __define_class_derives {
696 // Base case
697 (
698 ($($attr_impl:tt)*)
699 ($for:path)
700 ($(,)*)
701 ) => {};
702
703 // Debug
704 (
705 ($($attr_impl:tt)*)
706 ($for:path)
707 (
708 $(,)*
709 Debug
710 $($rest:tt)*
711 )
712 ) => {
713 $($attr_impl)*
714 #[automatically_derived]
715 impl $crate::__macro_helpers::fmt::Debug for $for {
716 fn fmt(&self, f: &mut $crate::__macro_helpers::fmt::Formatter<'_>) -> $crate::__macro_helpers::fmt::Result {
717 f.debug_struct($crate::__macro_helpers::stringify!($for))
718 .field("super", &**self.__superclass)
719 .field("ivars", <Self as $crate::DefinedClass>::ivars(self))
720 .finish()
721 }
722 }
723
724 $crate::__define_class_derives! {
725 ($($attr_impl)*)
726 ($for)
727 ($($rest)*)
728 }
729 };
730
731 // PartialEq
732 (
733 ($($attr_impl:tt)*)
734 ($for:path)
735 (
736 $(,)*
737 PartialEq
738 $($rest:tt)*
739 )
740 ) => {
741 $($attr_impl)*
742 #[automatically_derived]
743 impl $crate::__macro_helpers::PartialEq for $for {
744 #[inline]
745 fn eq(&self, other: &Self) -> $crate::__macro_helpers::bool {
746 // Delegate to the superclass (referential equality)
747 $crate::__macro_helpers::PartialEq::eq(&self.__superclass, &other.__superclass)
748 }
749 }
750
751 $crate::__define_class_derives! {
752 ($($attr_impl)*)
753 ($for)
754 ($($rest)*)
755 }
756 };
757
758 // Eq
759 (
760 ($($attr_impl:tt)*)
761 ($for:path)
762 (
763 $(,)*
764 Eq
765 $($rest:tt)*
766 )
767 ) => {
768 $($attr_impl)*
769 #[automatically_derived]
770 impl $crate::__macro_helpers::Eq for $for {}
771
772 $crate::__define_class_derives! {
773 ($($attr_impl)*)
774 ($for)
775 ($($rest)*)
776 }
777 };
778
779 // Hash
780 (
781 ($($attr_impl:tt)*)
782 ($for:path)
783 (
784 $(,)*
785 Hash
786 $($rest:tt)*
787 )
788 ) => {
789 $($attr_impl)*
790 #[automatically_derived]
791 impl $crate::__macro_helpers::Hash for $for {
792 #[inline]
793 fn hash<H: $crate::__macro_helpers::Hasher>(&self, state: &mut H) {
794 // Delegate to the superclass (which hashes the reference)
795 $crate::__macro_helpers::Hash::hash(&self.__superclass, state)
796 }
797 }
798
799 $crate::__define_class_derives! {
800 ($($attr_impl)*)
801 ($for)
802 ($($rest)*)
803 }
804 };
805
806 // Unhandled derive
807 (
808 ($($attr_impl:tt)*)
809 ($for:path)
810 (
811 $(,)*
812 $derive:path
813 $(, $($rest:tt)*)?
814 )
815 ) => {
816 const _: () = {
817 // For better diagnostics.
818 #[derive($derive)]
819 struct Derive;
820 };
821 $crate::__macro_helpers::compile_error!($crate::__macro_helpers::stringify!(
822 #[derive($derive)] is not supported in define_class!
823 ));
824
825 $crate::__define_class_derives! {
826 ($($attr_impl)*)
827 ($for)
828 ($($($rest)*)?)
829 }
830 };
831}
832
833#[doc(hidden)]
834#[macro_export]
835macro_rules! __select_ivars {
836 ($ivars:ty) => {
837 $ivars
838 };
839 () => {
840 // Default ivars to unit
841 ()
842 };
843}
844
845#[doc(hidden)]
846#[macro_export]
847macro_rules! __define_class_output_impls {
848 // Base-case
849 () => {};
850
851 // With protocol
852 (
853 $(#[$m:meta])*
854 unsafe impl $protocol:ident for $for:ty {
855 $($methods:tt)*
856 }
857
858 $($rest:tt)*
859 ) => {
860 // SAFETY: Upheld by caller
861 $(#[$m])*
862 unsafe impl $protocol for $for {}
863
864 $(#[$m])*
865 impl $for {
866 $crate::__define_class_output_methods! {
867 $($methods)*
868 }
869 }
870
871 $crate::__define_class_output_impls!{
872 $($rest)*
873 }
874 };
875
876 // Without protocol
877 (
878 $(#[$m:meta])*
879 impl $for:ty {
880 $($methods:tt)*
881 }
882
883 $($rest:tt)*
884 ) => {
885 $(#[$m])*
886 impl $for {
887 $crate::__define_class_output_methods! {
888 $($methods)*
889 }
890 }
891
892 $crate::__define_class_output_impls! {
893 $($rest)*
894 }
895 };
896}
897
898#[doc(hidden)]
899#[macro_export]
900macro_rules! __define_class_output_methods {
901 // Base case
902 {} => {};
903
904 // Unsafe variant
905 {
906 $(#[$($m:tt)*])*
907 unsafe fn $name:ident($($params:tt)*) $(-> $ret:ty)? $body:block
908
909 $($rest:tt)*
910 } => {
911 $crate::__rewrite_self_param! {
912 ($($params)*)
913
914 ($crate::__extract_method_attributes)
915 ($(#[$($m)*])*)
916
917 ($crate::__define_class_method_out)
918 (unsafe)
919 ($name)
920 ($($ret)?)
921 ($body)
922 }
923
924 $crate::__define_class_output_methods! {
925 $($rest)*
926 }
927 };
928
929 // Safe variant
930 {
931 $(#[$($m:tt)*])*
932 fn $name:ident($($params:tt)*) $(-> $ret:ty)? $body:block
933
934 $($rest:tt)*
935 } => {
936 $crate::__rewrite_self_param! {
937 ($($params)*)
938
939 ($crate::__extract_method_attributes)
940 ($(#[$($m)*])*)
941
942 ($crate::__define_class_method_out)
943 ()
944 ($name)
945 ($($ret)?)
946 ($body)
947 }
948
949 $crate::__define_class_output_methods! {
950 $($rest)*
951 }
952 };
953}
954
955#[doc(hidden)]
956#[macro_export]
957macro_rules! __define_class_register_impls {
958 // Base-case
959 (
960 ($builder:ident)
961 ) => {};
962
963 // With protocol
964 (
965 ($builder:ident)
966
967 $(#[$($m:tt)*])*
968 unsafe impl $protocol:ident for $for:ty {
969 $($methods:tt)*
970 }
971
972 $($rest:tt)*
973 ) => {
974 $crate::__extract_and_apply_cfg_attributes! {
975 ($(#[$($m)*])*)
976
977 // Implement protocol
978 #[allow(unused_mut)]
979 let mut __objc2_protocol_builder = $builder.add_protocol_methods::<dyn $protocol>();
980
981 // In case the user's function is marked `deprecated`
982 #[allow(deprecated)]
983 // In case the user did not specify any methods
984 #[allow(unused_unsafe)]
985 // SAFETY: Upheld by caller
986 unsafe {
987 $crate::__define_class_register_methods! {
988 (__objc2_protocol_builder)
989
990 $($methods)*
991 }
992 }
993
994 // Finished creating protocol; get error message if any
995 __objc2_protocol_builder.finish();
996 }
997
998 $crate::__define_class_register_impls! {
999 ($builder)
1000 $($rest)*
1001 }
1002 };
1003
1004 // Without protocol
1005 (
1006 ($builder:ident)
1007
1008 $(#[$($m:tt)*])*
1009 impl $for:ty {
1010 $($methods:tt)*
1011 }
1012
1013 $($rest:tt)*
1014 ) => {
1015 $crate::__extract_and_apply_cfg_attributes! {
1016 ($(#[$($m)*])*)
1017
1018 // In case the user's function is marked `deprecated`
1019 #[allow(deprecated)]
1020 // In case the user did not specify any methods
1021 #[allow(unused_unsafe)]
1022 // SAFETY: Upheld by caller
1023 unsafe {
1024 $crate::__define_class_register_methods! {
1025 ($builder)
1026
1027 $($methods)*
1028 }
1029 }
1030 }
1031
1032 $crate::__define_class_register_impls! {
1033 ($builder)
1034 $($rest)*
1035 }
1036 };
1037}
1038
1039#[doc(hidden)]
1040#[macro_export]
1041macro_rules! __define_class_register_methods {
1042 // Base case
1043 {
1044 ($builder:ident)
1045 } => {};
1046
1047 // Unsafe variant
1048 {
1049 ($builder:ident)
1050
1051 $(#[$($m:tt)*])*
1052 unsafe fn $name:ident($($params:tt)*) $(-> $ret:ty)? $body:block
1053
1054 $($rest:tt)*
1055 } => {
1056 $crate::__rewrite_self_param! {
1057 ($($params)*)
1058
1059 ($crate::__extract_method_attributes)
1060 ($(#[$($m)*])*)
1061
1062 ($crate::__define_class_register_out)
1063 ($builder)
1064 (unsafe)
1065 ($name)
1066 ($($ret)?)
1067 ($body)
1068 }
1069
1070 $crate::__define_class_register_methods! {
1071 ($builder)
1072
1073 $($rest)*
1074 }
1075 };
1076
1077 // Safe variant
1078 {
1079 ($builder:ident)
1080
1081 $(#[$($m:tt)*])*
1082 fn $name:ident($($params:tt)*) $(-> $ret:ty)? $body:block
1083
1084 $($rest:tt)*
1085 } => {
1086 $crate::__rewrite_self_param! {
1087 ($($params)*)
1088
1089 ($crate::__extract_method_attributes)
1090 ($(#[$($m)*])*)
1091
1092 ($crate::__define_class_register_out)
1093 ($builder)
1094 ()
1095 ($name)
1096 ($($ret)?)
1097 ($body)
1098 }
1099
1100 $crate::__define_class_register_methods! {
1101 ($builder)
1102
1103 $($rest)*
1104 }
1105 };
1106
1107 // Consume associated items for better UI.
1108 //
1109 // This will still fail inside __define_class_output_methods!
1110 {
1111 ($builder:ident)
1112
1113 $_associated_item:item
1114
1115 $($rest:tt)*
1116 } => {
1117 $crate::__define_class_output_methods! {
1118 ($builder)
1119
1120 $($rest)*
1121 }
1122 }
1123}
1124
1125#[doc(hidden)]
1126#[macro_export]
1127macro_rules! __define_class_method_out {
1128 {
1129 ($($qualifiers:tt)*)
1130 ($name:ident)
1131 ($($ret:ty)?)
1132 ($body:block)
1133
1134 ($builder_method:ident)
1135 ($receiver:expr)
1136 ($receiver_ty:ty)
1137 ($($params_prefix:tt)*)
1138 ($($params_rest:tt)*)
1139
1140 ($($m_method:tt)*)
1141 ($($method_family:tt)*)
1142 ($($optional:tt)*)
1143 ($($attr_method:tt)*)
1144 ($($attr_use:tt)*)
1145 } => {
1146 $crate::__define_class_rewrite_params! {
1147 ($($params_rest)*)
1148 ()
1149 ()
1150
1151 ($crate::__define_class_method_out_inner)
1152
1153 ($($qualifiers)*)
1154 ($name)
1155 ($($ret)?)
1156 ($body)
1157
1158 ($builder_method)
1159 ($receiver)
1160 ($receiver_ty)
1161 ($($params_prefix)*)
1162
1163 ($($m_method)*)
1164 ($($method_family)*)
1165 ($($optional)*)
1166 ($($attr_method)*)
1167 ($($attr_use)*)
1168 }
1169 };
1170}
1171
1172#[doc(hidden)]
1173#[macro_export]
1174macro_rules! __define_class_rewrite_params {
1175 // Convert _
1176 {
1177 (_ : $param_ty:ty $(, $($params_rest:tt)*)?)
1178 ($($params_converted:tt)*)
1179 ($($body_prefix:tt)*)
1180
1181 ($out_macro:path)
1182 $($macro_args:tt)*
1183 } => {
1184 $crate::__define_class_rewrite_params! {
1185 ($($($params_rest)*)?)
1186 ($($params_converted)* _ : <$param_ty as $crate::__macro_helpers::ConvertArgument>::__Inner,)
1187 ($($body_prefix)*)
1188
1189 ($out_macro)
1190 $($macro_args)*
1191 }
1192 };
1193 // Convert mut
1194 {
1195 (mut $param:ident : $param_ty:ty $(, $($params_rest:tt)*)?)
1196 ($($params_converted:tt)*)
1197 ($($body_prefix:tt)*)
1198
1199 ($out_macro:path)
1200 $($macro_args:tt)*
1201 } => {
1202 $crate::__define_class_rewrite_params! {
1203 ($($($params_rest)*)?)
1204 ($($params_converted)* $param : <$param_ty as $crate::__macro_helpers::ConvertArgument>::__Inner,)
1205 (
1206 $($body_prefix)*
1207 let mut $param = <$param_ty as $crate::__macro_helpers::ConvertArgument>::__from_defined_param($param);
1208 )
1209
1210 ($out_macro)
1211 $($macro_args)*
1212 }
1213 };
1214 // Convert
1215 {
1216 ($param:ident : $param_ty:ty $(, $($params_rest:tt)*)?)
1217 ($($params_converted:tt)*)
1218 ($($body_prefix:tt)*)
1219
1220 ($out_macro:path)
1221 $($macro_args:tt)*
1222 } => {
1223 $crate::__define_class_rewrite_params! {
1224 ($($($params_rest)*)?)
1225 ($($params_converted)* $param : <$param_ty as $crate::__macro_helpers::ConvertArgument>::__Inner,)
1226 (
1227 $($body_prefix)*
1228 let $param = <$param_ty as $crate::__macro_helpers::ConvertArgument>::__from_defined_param($param);
1229 )
1230
1231 ($out_macro)
1232 $($macro_args)*
1233 }
1234 };
1235 // Output result
1236 {
1237 ()
1238 ($($params_converted:tt)*)
1239 ($($body_prefix:tt)*)
1240
1241 ($out_macro:path)
1242 $($macro_args:tt)*
1243 } => {
1244 $out_macro! {
1245 $($macro_args)*
1246
1247 ($($params_converted)*)
1248 ($($body_prefix)*)
1249 }
1250 };
1251}
1252
1253#[doc(hidden)]
1254#[macro_export]
1255macro_rules! __define_class_method_out_inner {
1256 // #[unsafe(method(...))]
1257 {
1258 ($($qualifiers:tt)*)
1259 ($name:ident)
1260 ($($ret:ty)?)
1261 ($body:block)
1262
1263 ($__builder_method:ident)
1264 ($__receiver:expr)
1265 ($__receiver_ty:ty)
1266 ($($params_prefix:tt)*)
1267
1268 (method($($__sel:tt)*))
1269 ($($method_family:tt)*)
1270 ($($optional:tt)*)
1271 ($($attr_method:tt)*)
1272 ($($attr_use:tt)*)
1273
1274 ($($params_converted:tt)*)
1275 ($($body_prefix:tt)*)
1276 } => {
1277 $($attr_method)*
1278 #[allow(clippy::diverging_sub_expression)]
1279 $($qualifiers)* extern "C-unwind" fn $name(
1280 $($params_prefix)*
1281 $($params_converted)*
1282 ) $(-> <$ret as $crate::__macro_helpers::ConvertReturn<()>>::Inner)? {
1283 $crate::__define_class_no_method_family!($($method_family)*);
1284 $($body_prefix)*
1285 $crate::__convert_result! {
1286 $body $(; $ret)?
1287 }
1288 }
1289 };
1290
1291 // #[unsafe(method_id(...))]
1292 {
1293 ($($qualifiers:tt)*)
1294 ($name:ident)
1295 ($ret:ty)
1296 ($body:block)
1297
1298 ($__builder_method:ident)
1299 ($__receiver:expr)
1300 ($receiver_ty:ty)
1301 ($($params_prefix:tt)*)
1302
1303 (method_id($($sel:tt)*))
1304 ($($method_family:tt)*)
1305 ($($optional:tt)*)
1306 ($($attr_method:tt)*)
1307 ($($attr_use:tt)*)
1308
1309 ($($params_converted:tt)*)
1310 ($($body_prefix:tt)*)
1311 } => {
1312 $($attr_method)*
1313 #[allow(clippy::diverging_sub_expression)]
1314 $($qualifiers)* extern "C-unwind" fn $name(
1315 $($params_prefix)*
1316 $($params_converted)*
1317 ) -> $crate::__macro_helpers::RetainedReturnValue {
1318 // TODO: Somehow tell the compiler that `this: Allocated<Self>` is non-null.
1319
1320 $($body_prefix)*
1321
1322 let __objc2_result = $body;
1323
1324 #[allow(unreachable_code)]
1325 <$crate::__method_family!(($($method_family)*) ($($sel)*)) as $crate::__macro_helpers::MessageReceiveRetained<
1326 $receiver_ty,
1327 $ret,
1328 >>::into_return(__objc2_result)
1329 }
1330 };
1331
1332 {
1333 ($($qualifiers:tt)*)
1334 ($name:ident)
1335 ()
1336 ($body:block)
1337
1338 ($__builder_method:ident)
1339 ($__receiver:expr)
1340 ($__receiver_ty:ty)
1341 ($($params_prefix:tt)*)
1342
1343 (method_id($($sel:tt)*))
1344 ($($method_family:tt)*)
1345 ($($optional:tt)*)
1346 ($($attr_method:tt)*)
1347 ($($attr_use:tt)*)
1348
1349 ($($params_converted:tt)*)
1350 ($($body_prefix:tt)*)
1351 } => {
1352 $($attr_method)*
1353 $($qualifiers)* extern "C-unwind" fn $name() {
1354 $crate::__macro_helpers::compile_error!("`#[unsafe(method_id(...))]` must have a return type")
1355 }
1356 };
1357}
1358
1359#[doc(hidden)]
1360#[macro_export]
1361macro_rules! __convert_result {
1362 ($body:block) => {
1363 $body
1364 };
1365 ($body:block; $ret:ty) => {
1366 let __objc2_result = $body;
1367 #[allow(unreachable_code)]
1368 <$ret as $crate::__macro_helpers::ConvertReturn<()>>::convert_defined_return(__objc2_result)
1369 };
1370}
1371
1372#[doc(hidden)]
1373#[macro_export]
1374macro_rules! __define_class_register_out {
1375 {
1376 ($builder:ident)
1377 ($($qualifiers:tt)*)
1378 ($name:ident)
1379 ($($__ret:ty)?)
1380 ($__body:block)
1381
1382 ($builder_method:ident)
1383 ($__receiver:expr)
1384 ($__receiver_ty:ty)
1385 ($($__params_prefix:tt)*)
1386 ($($params_rest:tt)*)
1387
1388 ($method_or_method_id:ident($($sel:tt)*))
1389 ($($method_family:tt)*)
1390 ($($optional:tt)*)
1391 ($($attr_method:tt)*)
1392 ($($attr_use:tt)*)
1393 } => {
1394 $($attr_use)*
1395 {
1396 $crate::__define_class_invalid_selectors!($method_or_method_id($($sel)*));
1397 $crate::__define_class_no_optional!($($optional)*);
1398
1399 $builder.$builder_method(
1400 $crate::sel!($($sel)*),
1401 Self::$name as $crate::__fn_ptr! {
1402 ($($qualifiers)*)
1403 (_, _,)
1404 $($params_rest)*
1405 },
1406 );
1407 }
1408 };
1409}
1410
1411#[doc(hidden)]
1412#[macro_export]
1413macro_rules! __define_class_invalid_selectors {
1414 (method(dealloc)) => {
1415 $crate::__macro_helpers::compile_error!(
1416 "`#[unsafe(method(dealloc))]` is not supported. Implement `Drop` for the type instead"
1417 )
1418 };
1419 (method_id(dealloc)) => {
1420 $crate::__macro_helpers::compile_error!(
1421 "`#[unsafe(method_id(dealloc))]` is not supported. Implement `Drop` for the type instead"
1422 )
1423 };
1424 (method_id(alloc)) => {
1425 $crate::__macro_helpers::compile_error!($crate::__macro_helpers::concat!(
1426 "`#[unsafe(method_id(alloc))]` is not supported. ",
1427 "Use `#[unsafe(method(alloc))]` and do the memory management yourself",
1428 ))
1429 };
1430 (method_id(retain)) => {
1431 $crate::__macro_helpers::compile_error!($crate::__macro_helpers::concat!(
1432 "`#[unsafe(method_id(retain))]` is not supported. ",
1433 "Use `#[unsafe(method(retain))]` and do the memory management yourself",
1434 ))
1435 };
1436 (method_id(release)) => {
1437 $crate::__macro_helpers::compile_error!($crate::__macro_helpers::concat!(
1438 "`#[unsafe(method_id(release))]` is not supported. ",
1439 "Use `#[unsafe(method(release))]` and do the memory management yourself",
1440 ))
1441 };
1442 (method_id(autorelease)) => {
1443 $crate::__macro_helpers::compile_error!($crate::__macro_helpers::concat!(
1444 "`#[unsafe(method_id(autorelease))]` is not supported. ",
1445 "Use `#[unsafe(method(autorelease))]` and do the memory management yourself",
1446 ))
1447 };
1448 ($method_or_method_id:ident($($sel:tt)*)) => {};
1449}
1450
1451#[doc(hidden)]
1452#[macro_export]
1453macro_rules! __define_class_no_method_family {
1454 () => {};
1455 ($($t:tt)+) => {
1456 $crate::__macro_helpers::compile_error!(
1457 "`#[unsafe(method_family = ...)]` is not yet supported in `define_class!` together with `#[unsafe(method(...))]`"
1458 )
1459 };
1460}
1461
1462#[doc(hidden)]
1463#[macro_export]
1464macro_rules! __define_class_no_optional {
1465 () => {};
1466 (#[optional]) => {
1467 $crate::__macro_helpers::compile_error!(
1468 "`#[optional]` is only supported in `extern_protocol!`"
1469 )
1470 };
1471}
1472
1473/// Create function pointer type with inferred parameters.
1474#[doc(hidden)]
1475#[macro_export]
1476macro_rules! __fn_ptr {
1477 (
1478 ($($qualifiers:tt)*)
1479 ($($output:tt)*)
1480 $(,)?
1481 ) => {
1482 $($qualifiers)* extern "C-unwind" fn($($output)*) -> _
1483 };
1484 (
1485 ($($qualifiers:tt)*)
1486 ($($output:tt)*)
1487 _ : $param_ty:ty $(, $($rest:tt)*)?
1488 ) => {
1489 $crate::__fn_ptr! {
1490 ($($qualifiers)*)
1491 ($($output)* _,)
1492 $($($rest)*)?
1493 }
1494 };
1495 (
1496 ($($qualifiers:tt)*)
1497 ($($output:tt)*)
1498 mut $param:ident : $param_ty:ty $(, $($rest:tt)*)?
1499 ) => {
1500 $crate::__fn_ptr! {
1501 ($($qualifiers)*)
1502 ($($output)* _,)
1503 $($($rest)*)?
1504 }
1505 };
1506 (
1507 ($($qualifiers:tt)*)
1508 ($($output:tt)*)
1509 $param:ident : $param_ty:ty $(, $($rest:tt)*)?
1510 ) => {
1511 $crate::__fn_ptr! {
1512 ($($qualifiers)*)
1513 ($($output)* _,)
1514 $($($rest)*)?
1515 }
1516 };
1517}