Skip to content
This repository has been archived by the owner on Jan 28, 2024. It is now read-only.

Handle ObjC nullable annotations #624

Merged
merged 9 commits into from
Sep 28, 2023
Merged

Handle ObjC nullable annotations #624

merged 9 commits into from
Sep 28, 2023

Conversation

liamappelbe
Copy link
Contributor

@liamappelbe liamappelbe commented Sep 26, 2023

Also, make ObjCNullable a proper type, rather than just a nullability flag on objc methods.

Fixes dart-lang/native#262

Example (from nullable_test):

@interface NullableInterface  : NSObject {
}

+(BOOL) isNullWithNullableNSObjectArg:(nullable NSObject *)x;
+(BOOL) isNullWithNotNullableNSObjectPtrArg:(NSObject *)x;
+(nullable NSObject *) returnNil:(BOOL)r;

@property (nullable, retain) NSObject *nullableObjectProperty;

@end

Generates:

class NullableInterface extends NSObject {
  NullableInterface._(ffi.Pointer<ObjCObject> id, NullableTestObjCLibrary lib,
      {bool retain = false, bool release = false})
      : super._(id, lib, retain: retain, release: release);

  /// Returns a [NullableInterface] that points to the same underlying object as [other].
  static NullableInterface castFrom<T extends _ObjCWrapper>(T other) {
    return NullableInterface._(other._id, other._lib,
        retain: true, release: true);
  }

  /// Returns a [NullableInterface] that wraps the given raw object pointer.
  static NullableInterface castFromPointer(
      NullableTestObjCLibrary lib, ffi.Pointer<ObjCObject> other,
      {bool retain = false, bool release = false}) {
    return NullableInterface._(other, lib, retain: retain, release: release);
  }

  /// Returns whether [obj] is an instance of [NullableInterface].
  static bool isInstance(_ObjCWrapper obj) {
    return obj._lib._objc_msgSend_0(obj._id, obj._lib._sel_isKindOfClass_1,
        obj._lib._class_NullableInterface1);
  }

  static bool isNullWithNullableNSObjectArg_(
      NullableTestObjCLibrary _lib, NSObject? x) {
    return _lib._objc_msgSend_22(_lib._class_NullableInterface1,
        _lib._sel_isNullWithNullableNSObjectArg_1, x?._id ?? ffi.nullptr);
  }

  static bool isNullWithNotNullableNSObjectPtrArg_(
      NullableTestObjCLibrary _lib, NSObject x) {
    return _lib._objc_msgSend_23(_lib._class_NullableInterface1,
        _lib._sel_isNullWithNotNullableNSObjectPtrArg_1, x._id);
  }

  static NSObject? returnNil_(NullableTestObjCLibrary _lib, bool r) {
    final _ret = _lib._objc_msgSend_24(
        _lib._class_NullableInterface1, _lib._sel_returnNil_1, r);
    return _ret.address == 0
        ? null
        : NSObject._(_ret, _lib, retain: true, release: true);
  }

  NSObject? get nullableObjectProperty {
    final _ret = _lib._objc_msgSend_25(_id, _lib._sel_nullableObjectProperty1);
    return _ret.address == 0
        ? null
        : NSObject._(_ret, _lib, retain: true, release: true);
  }

  set nullableObjectProperty(NSObject? value) {
    return _lib._objc_msgSend_26(
        _id, _lib._sel_setNullableObjectProperty_1, value?._id ?? ffi.nullptr);
  }

  @override
  NullableInterface init() {
    final _ret = _lib._objc_msgSend_2(_id, _lib._sel_init1);
    return NullableInterface._(_ret, _lib, retain: true, release: true);
  }

  static NullableInterface new1(NullableTestObjCLibrary _lib) {
    final _ret =
        _lib._objc_msgSend_2(_lib._class_NullableInterface1, _lib._sel_new1);
    return NullableInterface._(_ret, _lib, retain: false, release: true);
  }

  static NullableInterface allocWithZone_(
      NullableTestObjCLibrary _lib, ffi.Pointer<_NSZone> zone) {
    final _ret = _lib._objc_msgSend_3(
        _lib._class_NullableInterface1, _lib._sel_allocWithZone_1, zone);
    return NullableInterface._(_ret, _lib, retain: false, release: true);
  }

  static NullableInterface alloc(NullableTestObjCLibrary _lib) {
    final _ret =
        _lib._objc_msgSend_2(_lib._class_NullableInterface1, _lib._sel_alloc1);
    return NullableInterface._(_ret, _lib, retain: false, release: true);
  }
}

Diff:

32,34c32,34
<       NullableTestObjCLibrary _lib, NSObject? x) {
<     return _lib._objc_msgSend_22(_lib._class_NullableInterface1,
<         _lib._sel_isNullWithNotNullableNSObjectPtrArg_1, x?._id ?? ffi.nullptr);
---
>       NullableTestObjCLibrary _lib, NSObject x) {
>     return _lib._objc_msgSend_23(_lib._class_NullableInterface1,
>         _lib._sel_isNullWithNotNullableNSObjectPtrArg_1, x._id);
37,38c37,38
<   static NSObject returnNil_(NullableTestObjCLibrary _lib, bool r) {
---
>   static NSObject? returnNil_(NullableTestObjCLibrary _lib, bool r) {
40c40,42
<     return NSObject._(_ret, _lib, retain: true, release: true);
---
>     return _ret.address == 0
>         ? null
>         : NSObject._(_ret, _lib, retain: true, release: true);

@dcharkes
Copy link
Contributor

The CI is still red, is this ready for review or WIP?

@liamappelbe
Copy link
Contributor Author

The CI is still red, is this ready for review or WIP?

Ready for review. The fixes to these tests should be pretty minor. I'm working on them now.

Copy link
Contributor

@dcharkes dcharkes left a comment

Choose a reason for hiding this comment

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

lgtm

nit: now that we don't check in generated files anymore, please add a pre-and-post FFIgen output of a small example to the PR description so it's easy to see the diff.

lib/src/code_generator/objc_nullable.dart Outdated Show resolved Hide resolved
import 'writer.dart';

/// An ObjC type annotated with nullable.
class ObjCNullable extends Type {
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice, much cleaner!

@liamappelbe liamappelbe merged commit 6ca8e68 into main Sep 28, 2023
6 checks passed
@liamappelbe liamappelbe deleted the objcnullable branch September 28, 2023 20:12
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Development

Successfully merging this pull request may close these issues.

ObjC: Honor nullable annotations
2 participants