diff --git a/pkgs/swiftgen/test/integration/regress_2871.swift b/pkgs/swiftgen/test/integration/regress_2871.swift new file mode 100644 index 000000000..b04b17b4a --- /dev/null +++ b/pkgs/swiftgen/test/integration/regress_2871.swift @@ -0,0 +1,9 @@ +import Foundation + +@objc public class TestEmptyClass: NSObject {} + +@objc public class TestFuncs: NSObject { + @objc public static func echoObject() -> NSObject? { + return TestEmptyClass() + } +} diff --git a/pkgs/swiftgen/test/integration/regress_2871_bindings.dart b/pkgs/swiftgen/test/integration/regress_2871_bindings.dart new file mode 100644 index 000000000..1a9ac5282 --- /dev/null +++ b/pkgs/swiftgen/test/integration/regress_2871_bindings.dart @@ -0,0 +1,373 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// coverage:ignore-file + +// AUTO GENERATED FILE, DO NOT EDIT. +// +// Generated by `package:ffigen`. +// ignore_for_file: type=lint, unused_import +import 'dart:ffi' as ffi; +import 'package:objective_c/objective_c.dart' as objc; +import 'package:ffi/ffi.dart' as pkg_ffi; + +late final _class_TestEmptyClass = objc.getClass("regress_2871.TestEmptyClass"); +late final _sel_isKindOfClass_ = objc.registerName("isKindOfClass:"); +final _objc_msgSend_19nvye5 = objc.msgSendPointer + .cast< + ffi.NativeFunction< + ffi.Bool Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ) + > + >() + .asFunction< + bool Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ) + >(); +typedef instancetype = ffi.Pointer; +typedef Dartinstancetype = objc.ObjCObject; +late final _sel_init = objc.registerName("init"); +final _objc_msgSend_151sglz = objc.msgSendPointer + .cast< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ) + > + >() + .asFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ) + >(); +late final _sel_new = objc.registerName("new"); +late final _sel_allocWithZone_ = objc.registerName("allocWithZone:"); +final _objc_msgSend_1cwp428 = objc.msgSendPointer + .cast< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ) + > + >() + .asFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ) + >(); +late final _sel_alloc = objc.registerName("alloc"); + +/// TestEmptyClass +extension type TestEmptyClass._(objc.ObjCObject object$) + implements objc.ObjCObject, objc.NSObject { + /// Constructs a [TestEmptyClass] that points to the same underlying object as [other]. + TestEmptyClass.as(objc.ObjCObject other) : object$ = other { + assert(isA(object$)); + } + + /// Constructs a [TestEmptyClass] that wraps the given raw object pointer. + TestEmptyClass.fromPointer( + ffi.Pointer other, { + bool retain = false, + bool release = false, + }) : object$ = objc.ObjCObject(other, retain: retain, release: release) { + assert(isA(object$)); + } + + /// Returns whether [obj] is an instance of [TestEmptyClass]. + static bool isA(objc.ObjCObject obj) => _objc_msgSend_19nvye5( + obj.ref.pointer, + _sel_isKindOfClass_, + _class_TestEmptyClass, + ); + + /// alloc + static TestEmptyClass alloc() { + final $ret = _objc_msgSend_151sglz(_class_TestEmptyClass, _sel_alloc); + return TestEmptyClass.fromPointer($ret, retain: false, release: true); + } + + /// allocWithZone: + static TestEmptyClass allocWithZone(ffi.Pointer zone) { + final $ret = _objc_msgSend_1cwp428( + _class_TestEmptyClass, + _sel_allocWithZone_, + zone, + ); + return TestEmptyClass.fromPointer($ret, retain: false, release: true); + } + + /// new + static TestEmptyClass new$() { + final $ret = _objc_msgSend_151sglz(_class_TestEmptyClass, _sel_new); + return TestEmptyClass.fromPointer($ret, retain: false, release: true); + } + + /// Returns a new instance of TestEmptyClass constructed with the default `new` method. + TestEmptyClass() : this.as(new$().object$); +} + +extension TestEmptyClass$Methods on TestEmptyClass { + /// init + TestEmptyClass init() { + objc.checkOsVersionInternal( + 'TestEmptyClass.init', + iOS: (false, (2, 0, 0)), + macOS: (false, (10, 0, 0)), + ); + final $ret = _objc_msgSend_151sglz( + object$.ref.retainAndReturnPointer(), + _sel_init, + ); + return TestEmptyClass.fromPointer($ret, retain: false, release: true); + } +} + +late final _class_TestEmptyClassWrapper = objc.getClass( + "regress_2871.TestEmptyClassWrapper", +); + +/// TestEmptyClassWrapper +extension type TestEmptyClassWrapper._(objc.ObjCObject object$) + implements objc.ObjCObject, objc.NSObject { + /// Constructs a [TestEmptyClassWrapper] that points to the same underlying object as [other]. + TestEmptyClassWrapper.as(objc.ObjCObject other) : object$ = other { + assert(isA(object$)); + } + + /// Constructs a [TestEmptyClassWrapper] that wraps the given raw object pointer. + TestEmptyClassWrapper.fromPointer( + ffi.Pointer other, { + bool retain = false, + bool release = false, + }) : object$ = objc.ObjCObject(other, retain: retain, release: release) { + assert(isA(object$)); + } + + /// Returns whether [obj] is an instance of [TestEmptyClassWrapper]. + static bool isA(objc.ObjCObject obj) => _objc_msgSend_19nvye5( + obj.ref.pointer, + _sel_isKindOfClass_, + _class_TestEmptyClassWrapper, + ); + + /// alloc + static TestEmptyClassWrapper alloc() { + final $ret = _objc_msgSend_151sglz( + _class_TestEmptyClassWrapper, + _sel_alloc, + ); + return TestEmptyClassWrapper.fromPointer( + $ret, + retain: false, + release: true, + ); + } + + /// allocWithZone: + static TestEmptyClassWrapper allocWithZone(ffi.Pointer zone) { + final $ret = _objc_msgSend_1cwp428( + _class_TestEmptyClassWrapper, + _sel_allocWithZone_, + zone, + ); + return TestEmptyClassWrapper.fromPointer( + $ret, + retain: false, + release: true, + ); + } + + /// new + static TestEmptyClassWrapper new$() { + final $ret = _objc_msgSend_151sglz(_class_TestEmptyClassWrapper, _sel_new); + return TestEmptyClassWrapper.fromPointer( + $ret, + retain: false, + release: true, + ); + } + + /// Returns a new instance of TestEmptyClassWrapper constructed with the default `new` method. + TestEmptyClassWrapper() : this.as(new$().object$); +} + +extension TestEmptyClassWrapper$Methods on TestEmptyClassWrapper { + /// init + TestEmptyClassWrapper init() { + objc.checkOsVersionInternal( + 'TestEmptyClassWrapper.init', + iOS: (false, (2, 0, 0)), + macOS: (false, (10, 0, 0)), + ); + final $ret = _objc_msgSend_151sglz( + object$.ref.retainAndReturnPointer(), + _sel_init, + ); + return TestEmptyClassWrapper.fromPointer( + $ret, + retain: false, + release: true, + ); + } +} + +late final _class_TestFuncs = objc.getClass("regress_2871.TestFuncs"); +late final _sel_echoObject = objc.registerName("echoObject"); + +/// TestFuncs +extension type TestFuncs._(objc.ObjCObject object$) + implements objc.ObjCObject, objc.NSObject { + /// Constructs a [TestFuncs] that points to the same underlying object as [other]. + TestFuncs.as(objc.ObjCObject other) : object$ = other { + assert(isA(object$)); + } + + /// Constructs a [TestFuncs] that wraps the given raw object pointer. + TestFuncs.fromPointer( + ffi.Pointer other, { + bool retain = false, + bool release = false, + }) : object$ = objc.ObjCObject(other, retain: retain, release: release) { + assert(isA(object$)); + } + + /// Returns whether [obj] is an instance of [TestFuncs]. + static bool isA(objc.ObjCObject obj) => _objc_msgSend_19nvye5( + obj.ref.pointer, + _sel_isKindOfClass_, + _class_TestFuncs, + ); + + /// alloc + static TestFuncs alloc() { + final $ret = _objc_msgSend_151sglz(_class_TestFuncs, _sel_alloc); + return TestFuncs.fromPointer($ret, retain: false, release: true); + } + + /// allocWithZone: + static TestFuncs allocWithZone(ffi.Pointer zone) { + final $ret = _objc_msgSend_1cwp428( + _class_TestFuncs, + _sel_allocWithZone_, + zone, + ); + return TestFuncs.fromPointer($ret, retain: false, release: true); + } + + /// echoObject + static objc.NSObject? echoObject() { + final $ret = _objc_msgSend_151sglz(_class_TestFuncs, _sel_echoObject); + return $ret.address == 0 + ? null + : objc.NSObject.fromPointer($ret, retain: true, release: true); + } + + /// new + static TestFuncs new$() { + final $ret = _objc_msgSend_151sglz(_class_TestFuncs, _sel_new); + return TestFuncs.fromPointer($ret, retain: false, release: true); + } + + /// Returns a new instance of TestFuncs constructed with the default `new` method. + TestFuncs() : this.as(new$().object$); +} + +extension TestFuncs$Methods on TestFuncs { + /// init + TestFuncs init() { + objc.checkOsVersionInternal( + 'TestFuncs.init', + iOS: (false, (2, 0, 0)), + macOS: (false, (10, 0, 0)), + ); + final $ret = _objc_msgSend_151sglz( + object$.ref.retainAndReturnPointer(), + _sel_init, + ); + return TestFuncs.fromPointer($ret, retain: false, release: true); + } +} + +late final _class_TestFuncsWrapper = objc.getClass( + "regress_2871.TestFuncsWrapper", +); + +/// TestFuncsWrapper +extension type TestFuncsWrapper._(objc.ObjCObject object$) + implements objc.ObjCObject, objc.NSObject { + /// Constructs a [TestFuncsWrapper] that points to the same underlying object as [other]. + TestFuncsWrapper.as(objc.ObjCObject other) : object$ = other { + assert(isA(object$)); + } + + /// Constructs a [TestFuncsWrapper] that wraps the given raw object pointer. + TestFuncsWrapper.fromPointer( + ffi.Pointer other, { + bool retain = false, + bool release = false, + }) : object$ = objc.ObjCObject(other, retain: retain, release: release) { + assert(isA(object$)); + } + + /// Returns whether [obj] is an instance of [TestFuncsWrapper]. + static bool isA(objc.ObjCObject obj) => _objc_msgSend_19nvye5( + obj.ref.pointer, + _sel_isKindOfClass_, + _class_TestFuncsWrapper, + ); + + /// alloc + static TestFuncsWrapper alloc() { + final $ret = _objc_msgSend_151sglz(_class_TestFuncsWrapper, _sel_alloc); + return TestFuncsWrapper.fromPointer($ret, retain: false, release: true); + } + + /// allocWithZone: + static TestFuncsWrapper allocWithZone(ffi.Pointer zone) { + final $ret = _objc_msgSend_1cwp428( + _class_TestFuncsWrapper, + _sel_allocWithZone_, + zone, + ); + return TestFuncsWrapper.fromPointer($ret, retain: false, release: true); + } + + /// new + static TestFuncsWrapper new$() { + final $ret = _objc_msgSend_151sglz(_class_TestFuncsWrapper, _sel_new); + return TestFuncsWrapper.fromPointer($ret, retain: false, release: true); + } + + /// Returns a new instance of TestFuncsWrapper constructed with the default `new` method. + TestFuncsWrapper() : this.as(new$().object$); +} + +extension TestFuncsWrapper$Methods on TestFuncsWrapper { + /// init + TestFuncsWrapper init() { + objc.checkOsVersionInternal( + 'TestFuncsWrapper.init', + iOS: (false, (2, 0, 0)), + macOS: (false, (10, 0, 0)), + ); + final $ret = _objc_msgSend_151sglz( + object$.ref.retainAndReturnPointer(), + _sel_init, + ); + return TestFuncsWrapper.fromPointer($ret, retain: false, release: true); + } +} diff --git a/pkgs/swiftgen/test/integration/regress_2871_test.dart b/pkgs/swiftgen/test/integration/regress_2871_test.dart new file mode 100644 index 000000000..a7eb0c2af --- /dev/null +++ b/pkgs/swiftgen/test/integration/regress_2871_test.dart @@ -0,0 +1,29 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +@Timeout(Duration(minutes: 2)) +library; + +import 'dart:ffi'; + +import 'package:test/test.dart'; + +import 'regress_2871_bindings.dart'; +import 'util.dart'; + +void main() { + group('Regression test for #2871', () { + setUpAll(() async { + final gen = TestGenerator('regress_2871'); + await gen.generateAndVerifyBindings(); + DynamicLibrary.open(gen.dylibFile); + }); + + test('method invocation', () { + final obj = TestFuncs.echoObject(); + expect(obj, isNotNull); + expect(TestEmptyClass.isA(obj!), isTrue); + }); + }); +}