[ffigen] Use stret and fpret variants of objc_msgSend where appropriate (#613)
* Use stret and fpret variants of objc_msgSend where appropriate
* Changelog
* Daco's comment
diff --git a/pkgs/ffigen/CHANGELOG.md b/pkgs/ffigen/CHANGELOG.md
index 7036a8c..b90e619 100644
--- a/pkgs/ffigen/CHANGELOG.md
+++ b/pkgs/ffigen/CHANGELOG.md
@@ -2,6 +2,9 @@
- Add support for ObjC Blocks that can be invoked from any thread, using
NativeCallable.listener.
+- Fix invalid exceptional return value ObjCBlocks that return floats.
+- Fix return_of_invalid_type analysis error for ObjCBlocks.
+- Fix crash in ObjC methods and blocks that return structs by value.
- Bump min SDK version to 3.2.0-114.0.dev.
# 9.0.1
diff --git a/pkgs/ffigen/example/swift/swift_api.h b/pkgs/ffigen/example/swift/swift_api.h
index 0928c49..14ebedf 100644
--- a/pkgs/ffigen/example/swift/swift_api.h
+++ b/pkgs/ffigen/example/swift/swift_api.h
@@ -1,4 +1,4 @@
-// Generated by Apple Swift version 5.7.2 (swiftlang-5.7.2.135.5 clang-1400.0.29.51)
+// Generated by Apple Swift version 5.8.1 (swiftlang-5.8.0.124.5 clang-1403.0.22.11.100)
#ifndef SWIFT_MODULE_SWIFT_H
#define SWIFT_MODULE_SWIFT_H
#pragma clang diagnostic push
@@ -21,7 +21,6 @@
# include <swift/objc-prologue.h>
#endif
-#pragma clang diagnostic ignored "-Wduplicate-method-match"
#pragma clang diagnostic ignored "-Wauto-import"
#if defined(__OBJC__)
#include <Foundation/Foundation.h>
@@ -30,10 +29,24 @@
#include <cstdint>
#include <cstddef>
#include <cstdbool>
+#include <cstring>
+#include <stdlib.h>
+#include <new>
+#include <type_traits>
#else
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
+#include <string.h>
+#endif
+#if defined(__cplusplus)
+#if __has_include(<ptrauth.h>)
+# include <ptrauth.h>
+#else
+# ifndef __ptrauth_swift_value_witness_function_pointer
+# define __ptrauth_swift_value_witness_function_pointer(x)
+# endif
+#endif
#endif
#if !defined(SWIFT_TYPEDEFS)
@@ -69,53 +82,66 @@
# if __has_feature(objc_class_property)
# define SWIFT_CLASS_PROPERTY(...) __VA_ARGS__
# else
-# define SWIFT_CLASS_PROPERTY(...)
+# define SWIFT_CLASS_PROPERTY(...)
# endif
#endif
-
-#if __has_attribute(objc_runtime_name)
-# define SWIFT_RUNTIME_NAME(X) __attribute__((objc_runtime_name(X)))
-#else
-# define SWIFT_RUNTIME_NAME(X)
+#if !defined(SWIFT_RUNTIME_NAME)
+# if __has_attribute(objc_runtime_name)
+# define SWIFT_RUNTIME_NAME(X) __attribute__((objc_runtime_name(X)))
+# else
+# define SWIFT_RUNTIME_NAME(X)
+# endif
#endif
-#if __has_attribute(swift_name)
-# define SWIFT_COMPILE_NAME(X) __attribute__((swift_name(X)))
-#else
-# define SWIFT_COMPILE_NAME(X)
+#if !defined(SWIFT_COMPILE_NAME)
+# if __has_attribute(swift_name)
+# define SWIFT_COMPILE_NAME(X) __attribute__((swift_name(X)))
+# else
+# define SWIFT_COMPILE_NAME(X)
+# endif
#endif
-#if __has_attribute(objc_method_family)
-# define SWIFT_METHOD_FAMILY(X) __attribute__((objc_method_family(X)))
-#else
-# define SWIFT_METHOD_FAMILY(X)
+#if !defined(SWIFT_METHOD_FAMILY)
+# if __has_attribute(objc_method_family)
+# define SWIFT_METHOD_FAMILY(X) __attribute__((objc_method_family(X)))
+# else
+# define SWIFT_METHOD_FAMILY(X)
+# endif
#endif
-#if __has_attribute(noescape)
-# define SWIFT_NOESCAPE __attribute__((noescape))
-#else
-# define SWIFT_NOESCAPE
+#if !defined(SWIFT_NOESCAPE)
+# if __has_attribute(noescape)
+# define SWIFT_NOESCAPE __attribute__((noescape))
+# else
+# define SWIFT_NOESCAPE
+# endif
#endif
-#if __has_attribute(ns_consumed)
-# define SWIFT_RELEASES_ARGUMENT __attribute__((ns_consumed))
-#else
-# define SWIFT_RELEASES_ARGUMENT
+#if !defined(SWIFT_RELEASES_ARGUMENT)
+# if __has_attribute(ns_consumed)
+# define SWIFT_RELEASES_ARGUMENT __attribute__((ns_consumed))
+# else
+# define SWIFT_RELEASES_ARGUMENT
+# endif
#endif
-#if __has_attribute(warn_unused_result)
-# define SWIFT_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
-#else
-# define SWIFT_WARN_UNUSED_RESULT
+#if !defined(SWIFT_WARN_UNUSED_RESULT)
+# if __has_attribute(warn_unused_result)
+# define SWIFT_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
+# else
+# define SWIFT_WARN_UNUSED_RESULT
+# endif
#endif
-#if __has_attribute(noreturn)
-# define SWIFT_NORETURN __attribute__((noreturn))
-#else
-# define SWIFT_NORETURN
+#if !defined(SWIFT_NORETURN)
+# if __has_attribute(noreturn)
+# define SWIFT_NORETURN __attribute__((noreturn))
+# else
+# define SWIFT_NORETURN
+# endif
#endif
#if !defined(SWIFT_CLASS_EXTRA)
-# define SWIFT_CLASS_EXTRA
+# define SWIFT_CLASS_EXTRA
#endif
#if !defined(SWIFT_PROTOCOL_EXTRA)
-# define SWIFT_PROTOCOL_EXTRA
+# define SWIFT_PROTOCOL_EXTRA
#endif
#if !defined(SWIFT_ENUM_EXTRA)
-# define SWIFT_ENUM_EXTRA
+# define SWIFT_ENUM_EXTRA
#endif
#if !defined(SWIFT_CLASS)
# if __has_attribute(objc_subclassing_restricted)
@@ -135,28 +161,25 @@
# define SWIFT_RESILIENT_CLASS_NAMED(SWIFT_NAME) SWIFT_CLASS_NAMED(SWIFT_NAME)
# endif
#endif
-
#if !defined(SWIFT_PROTOCOL)
# define SWIFT_PROTOCOL(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA
# define SWIFT_PROTOCOL_NAMED(SWIFT_NAME) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA
#endif
-
#if !defined(SWIFT_EXTENSION)
# define SWIFT_EXTENSION(M) SWIFT_PASTE(M##_Swift_, __LINE__)
#endif
-
#if !defined(OBJC_DESIGNATED_INITIALIZER)
# if __has_attribute(objc_designated_initializer)
# define OBJC_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer))
# else
-# define OBJC_DESIGNATED_INITIALIZER
+# define OBJC_DESIGNATED_INITIALIZER
# endif
#endif
#if !defined(SWIFT_ENUM_ATTR)
-# if defined(__has_attribute) && __has_attribute(enum_extensibility)
+# if __has_attribute(enum_extensibility)
# define SWIFT_ENUM_ATTR(_extensibility) __attribute__((enum_extensibility(_extensibility)))
# else
-# define SWIFT_ENUM_ATTR(_extensibility)
+# define SWIFT_ENUM_ATTR(_extensibility)
# endif
#endif
#if !defined(SWIFT_ENUM)
@@ -185,14 +208,16 @@
#if !defined(SWIFT_DEPRECATED_MSG)
# define SWIFT_DEPRECATED_MSG(...) __attribute__((deprecated(__VA_ARGS__)))
#endif
-#if __has_feature(attribute_diagnose_if_objc)
-# define SWIFT_DEPRECATED_OBJC(Msg) __attribute__((diagnose_if(1, Msg, "warning")))
-#else
-# define SWIFT_DEPRECATED_OBJC(Msg) SWIFT_DEPRECATED_MSG(Msg)
+#if !defined(SWIFT_DEPRECATED_OBJC)
+# if __has_feature(attribute_diagnose_if_objc)
+# define SWIFT_DEPRECATED_OBJC(Msg) __attribute__((diagnose_if(1, Msg, "warning")))
+# else
+# define SWIFT_DEPRECATED_OBJC(Msg) SWIFT_DEPRECATED_MSG(Msg)
+# endif
#endif
#if defined(__OBJC__)
#if !defined(IBSegueAction)
-# define IBSegueAction
+# define IBSegueAction
#endif
#endif
#if !defined(SWIFT_EXTERN)
@@ -205,26 +230,31 @@
#if !defined(SWIFT_CALL)
# define SWIFT_CALL __attribute__((swiftcall))
#endif
-#if defined(__cplusplus)
-#if !defined(SWIFT_NOEXCEPT)
-# define SWIFT_NOEXCEPT noexcept
+#if !defined(SWIFT_INDIRECT_RESULT)
+# define SWIFT_INDIRECT_RESULT __attribute__((swift_indirect_result))
#endif
+#if !defined(SWIFT_CONTEXT)
+# define SWIFT_CONTEXT __attribute__((swift_context))
+#endif
+#if !defined(SWIFT_ERROR_RESULT)
+# define SWIFT_ERROR_RESULT __attribute__((swift_error_result))
+#endif
+#if defined(__cplusplus)
+# define SWIFT_NOEXCEPT noexcept
#else
-#if !defined(SWIFT_NOEXCEPT)
# define SWIFT_NOEXCEPT
#endif
+#if defined(_WIN32)
+#if !defined(SWIFT_IMPORT_STDLIB_SYMBOL)
+# define SWIFT_IMPORT_STDLIB_SYMBOL __declspec(dllimport)
#endif
-#if defined(__cplusplus)
-#if !defined(SWIFT_CXX_INT_DEFINED)
-#define SWIFT_CXX_INT_DEFINED
-namespace swift {
-using Int = ptrdiff_t;
-using UInt = size_t;
-}
+#else
+#if !defined(SWIFT_IMPORT_STDLIB_SYMBOL)
+# define SWIFT_IMPORT_STDLIB_SYMBOL
#endif
#endif
#if defined(__OBJC__)
-#if __has_feature(modules)
+#if __has_feature(objc_modules)
#if __has_warning("-Watimport-in-framework-header")
#pragma clang diagnostic ignored "-Watimport-in-framework-header"
#endif
diff --git a/pkgs/ffigen/lib/src/code_generator/objc_built_in_functions.dart b/pkgs/ffigen/lib/src/code_generator/objc_built_in_functions.dart
index 05cd130..b092e73 100644
--- a/pkgs/ffigen/lib/src/code_generator/objc_built_in_functions.dart
+++ b/pkgs/ffigen/lib/src/code_generator/objc_built_in_functions.dart
@@ -94,24 +94,18 @@
_blockReleaseFunc,
);
- // We need to load a separate instance of objc_msgSend for each signature.
- final _msgSendFuncs = <String, Func>{};
- Func getMsgSendFunc(Type returnType, List<ObjCMethodParam> params) {
+ // We need to load a separate instance of objc_msgSend for each signature. If
+ // the return type is a struct, we need to use objc_msgSend_stret instead, and
+ // for float return types we need objc_msgSend_fpret.
+ final _msgSendFuncs = <String, ObjCMsgSendFunc>{};
+ ObjCMsgSendFunc getMsgSendFunc(
+ Type returnType, List<ObjCMethodParam> params) {
var key = returnType.cacheKey();
for (final p in params) {
key += ' ${p.type.cacheKey()}';
}
- return _msgSendFuncs[key] ??= Func(
- name: '_objc_msgSend_${_msgSendFuncs.length}',
- originalName: 'objc_msgSend',
- returnType: returnType,
- parameters: [
- Parameter(name: 'obj', type: PointerType(objCObjectType)),
- Parameter(name: 'sel', type: PointerType(objCSelType)),
- for (final p in params) Parameter(name: p.name, type: p.type),
- ],
- isInternal: true,
- );
+ return _msgSendFuncs[key] ??= ObjCMsgSendFunc(
+ '_objc_msgSend_${_msgSendFuncs.length}', returnType, params);
}
final _selObjects = <String, ObjCInternalGlobal>{};
@@ -285,8 +279,8 @@
_retainFunc.addDependencies(dependencies);
_releaseFunc.addDependencies(dependencies);
_releaseFinalizer.addDependencies(dependencies);
- for (final func in _msgSendFuncs.values) {
- func.addDependencies(dependencies);
+ for (final msgSendFunc in _msgSendFuncs.values) {
+ msgSendFunc.func.addDependencies(dependencies);
}
for (final sel in _selObjects.values) {
sel.addDependencies(dependencies);
@@ -396,3 +390,50 @@
binding?.addDependencies(dependencies);
}
}
+
+enum ObjCMsgSendVariant {
+ normal('objc_msgSend'),
+ stret('objc_msgSend_stret'),
+ fpret('objc_msgSend_fpret');
+
+ final String name;
+ const ObjCMsgSendVariant(this.name);
+
+ static ObjCMsgSendVariant fromReturnType(Type returnType) {
+ if (returnType is Compound && returnType.isStruct) {
+ return ObjCMsgSendVariant.stret;
+ } else if (returnType == floatType || returnType == doubleType) {
+ return ObjCMsgSendVariant.fpret;
+ }
+ return ObjCMsgSendVariant.normal;
+ }
+}
+
+/// A wrapper around the objc_msgSend function, or the stret or fpret variants.
+class ObjCMsgSendFunc {
+ final ObjCMsgSendVariant variant;
+ late final Func func;
+
+ ObjCMsgSendFunc(String name, Type returnType, List<ObjCMethodParam> params)
+ : variant = ObjCMsgSendVariant.fromReturnType(returnType) {
+ func = Func(
+ name: name,
+ originalName: variant.name,
+ returnType: isStret ? voidType : returnType,
+ parameters: [
+ if (isStret) Parameter(name: 'stret', type: PointerType(returnType)),
+ Parameter(name: 'obj', type: PointerType(objCObjectType)),
+ Parameter(name: 'sel', type: PointerType(objCSelType)),
+ for (final p in params) Parameter(name: p.name, type: p.type),
+ ],
+ isInternal: true,
+ );
+ }
+
+ String get name => func.name;
+ bool get isStret => variant == ObjCMsgSendVariant.stret;
+
+ void addDependencies(Set<Binding> dependencies) {
+ func.addDependencies(dependencies);
+ }
+}
diff --git a/pkgs/ffigen/lib/src/code_generator/objc_interface.dart b/pkgs/ffigen/lib/src/code_generator/objc_interface.dart
index 2cf44af..bf4d887 100644
--- a/pkgs/ffigen/lib/src/code_generator/objc_interface.dart
+++ b/pkgs/ffigen/lib/src/code_generator/objc_interface.dart
@@ -46,7 +46,7 @@
final ObjCBuiltInFunctions builtInFunctions;
late final ObjCInternalGlobal _classObject;
late final ObjCInternalGlobal _isKindOfClass;
- late final Func _isKindOfClassMsgSend;
+ late final ObjCMsgSendFunc _isKindOfClassMsgSend;
ObjCInterface({
String? usr,
@@ -130,7 +130,14 @@
for (final m in methods.values) {
final methodName = m._getDartMethodName(uniqueNamer);
final isStatic = m.isClass;
- final returnType = m.returnType;
+ final isStret = m.msgSend!.isStret;
+
+ var returnType = m.returnType;
+ var params = m.params;
+ if (isStret) {
+ params = [ObjCMethodParam(PointerType(returnType), 'stret'), ...params];
+ returnType = voidType;
+ }
// The method declaration.
if (m.dartDoc != null) {
@@ -159,7 +166,7 @@
s.write(methodName[0].toUpperCase() + methodName.substring(1));
break;
}
- s.write(paramsToString(m.params, isStatic: true));
+ s.write(paramsToString(params, isStatic: true));
} else {
if (superType?.methods[m.originalName]?.sameAs(m) ?? false) {
s.write('@override\n ');
@@ -170,18 +177,25 @@
s.write(_getConvertedReturnType(
returnType, w, name, m.isNullableReturn));
s.write(' $methodName');
- s.write(paramsToString(m.params, isStatic: false));
+ s.write(paramsToString(params, isStatic: false));
break;
case ObjCMethodKind.propertyGetter:
- // returnType get methodName
s.write(_getConvertedReturnType(
returnType, w, name, m.isNullableReturn));
- s.write(' get $methodName');
+ if (isStret) {
+ // void getMethodName(Pointer<returnType> stret, NativeLibrary _lib)
+ s.write(' get');
+ s.write(methodName[0].toUpperCase() + methodName.substring(1));
+ s.write(paramsToString(params, isStatic: false));
+ } else {
+ // returnType get methodName
+ s.write(' get $methodName');
+ }
break;
case ObjCMethodKind.propertySetter:
// set methodName(...)
s.write(' set $methodName');
- s.write(paramsToString(m.params, isStatic: false));
+ s.write(paramsToString(params, isStatic: false));
break;
}
}
@@ -192,10 +206,13 @@
final convertReturn = m.kind != ObjCMethodKind.propertySetter &&
_needsConverting(returnType);
- if (returnType != NativeType(SupportedNativeType.Void)) {
+ if (returnType != voidType) {
s.write(' ${convertReturn ? 'final _ret = ' : 'return '}');
}
s.write('_lib.${m.msgSend!.name}(');
+ if (isStret) {
+ s.write('stret, ');
+ }
s.write(isStatic ? '_lib.${_classObject.name}' : '_id');
s.write(', _lib.${m.selObject!.name}');
for (final p in m.params) {
@@ -427,7 +444,7 @@
final bool isClass;
bool returnsRetained = false;
ObjCInternalGlobal? selObject;
- Func? msgSend;
+ ObjCMsgSendFunc? msgSend;
ObjCMethod({
required this.originalName,
diff --git a/pkgs/ffigen/test/native_objc_test/block_test.dart b/pkgs/ffigen/test/native_objc_test/block_test.dart
index 87a735a..908e8fc 100644
--- a/pkgs/ffigen/test/native_objc_test/block_test.dart
+++ b/pkgs/ffigen/test/native_objc_test/block_test.dart
@@ -109,6 +109,52 @@
expect(BlockTester.callFloatBlock_(lib, block), closeTo(5.79, 1e-6));
});
+ test('Double block', () {
+ final block = ObjCBlock3.fromFunction(lib, (double x) {
+ return x + 4.56;
+ });
+ expect(block(1.23), closeTo(5.79, 1e-6));
+ expect(BlockTester.callDoubleBlock_(lib, block), closeTo(5.79, 1e-6));
+ });
+
+ test('Struct block', () {
+ final inputPtr = calloc<Vec4>();
+ final input = inputPtr.ref;
+ input.x = 1.2;
+ input.y = 3.4;
+ input.z = 5.6;
+ input.w = 7.8;
+
+ final tempPtr = calloc<Vec4>();
+ final temp = tempPtr.ref;
+ final block = ObjCBlock4.fromFunction(lib, (Vec4 v) {
+ // Twiddle the Vec4 components.
+ temp.x = v.y;
+ temp.y = v.z;
+ temp.z = v.w;
+ temp.w = v.x;
+ return temp;
+ });
+
+ final result1 = block(input);
+ expect(result1.x, 3.4);
+ expect(result1.y, 5.6);
+ expect(result1.z, 7.8);
+ expect(result1.w, 1.2);
+
+ final result2Ptr = calloc<Vec4>();
+ final result2 = result2Ptr.ref;
+ BlockTester.callVec4Block_(lib, result2Ptr, block);
+ expect(result2.x, 3.4);
+ expect(result2.y, 5.6);
+ expect(result2.z, 7.8);
+ expect(result2.w, 1.2);
+
+ calloc.free(inputPtr);
+ calloc.free(tempPtr);
+ calloc.free(result2Ptr);
+ });
+
Pointer<Void> funcPointerBlockRefCountTest() {
final block = ObjCBlock1.fromFunctionPointer(
lib, Pointer.fromFunction(_add100, 999));
diff --git a/pkgs/ffigen/test/native_objc_test/block_test.m b/pkgs/ffigen/test/native_objc_test/block_test.m
index 9e619b6..a864f2e 100644
--- a/pkgs/ffigen/test/native_objc_test/block_test.m
+++ b/pkgs/ffigen/test/native_objc_test/block_test.m
@@ -5,8 +5,17 @@
#import <Foundation/NSObject.h>
#import <Foundation/NSThread.h>
+typedef struct {
+ double x;
+ double y;
+ double z;
+ double w;
+} Vec4;
+
typedef int32_t (^IntBlock)(int32_t);
typedef float (^FloatBlock)(float);
+typedef double (^DoubleBlock)(double);
+typedef Vec4 (^Vec4Block)(Vec4);
typedef void (^VoidBlock)();
// Wrapper around a block, so that our Dart code can test creating and invoking
@@ -23,6 +32,8 @@
+ (void)callOnSameThread:(VoidBlock)block;
+ (NSThread*)callOnNewThread:(VoidBlock)block;
+ (float)callFloatBlock:(FloatBlock)block;
++ (double)callDoubleBlock:(DoubleBlock)block;
++ (Vec4)callVec4Block:(Vec4Block)block;
@end
@implementation BlockTester
@@ -92,4 +103,17 @@
return block(1.23);
}
++ (double)callDoubleBlock:(DoubleBlock)block {
+ return block(1.23);
+}
+
++ (Vec4)callVec4Block:(Vec4Block)block {
+ Vec4 vec4;
+ vec4.x = 1.2;
+ vec4.y = 3.4;
+ vec4.z = 5.6;
+ vec4.w = 7.8;
+ return block(vec4);
+}
+
@end
diff --git a/pkgs/ffigen/test/native_objc_test/method_test.dart b/pkgs/ffigen/test/native_objc_test/method_test.dart
index ebb1fba..21ba7e0 100644
--- a/pkgs/ffigen/test/native_objc_test/method_test.dart
+++ b/pkgs/ffigen/test/native_objc_test/method_test.dart
@@ -8,13 +8,14 @@
import 'dart:ffi';
import 'dart:io';
+import 'package:ffi/ffi.dart';
import 'package:test/test.dart';
import '../test_utils.dart';
import 'method_bindings.dart';
import 'util.dart';
void main() {
- MethodInterface? testInstance;
+ late MethodInterface testInstance;
late MethodTestObjCLibrary lib;
group('method calls', () {
@@ -29,19 +30,19 @@
group('Instance methods', () {
test('No arguments', () {
- expect(testInstance!.add(), 5);
+ expect(testInstance.add(), 5);
});
test('One argument', () {
- expect(testInstance!.add_(23), 23);
+ expect(testInstance.add_(23), 23);
});
test('Two arguments', () {
- expect(testInstance!.add_Y_(23, 17), 40);
+ expect(testInstance.add_Y_(23, 17), 40);
});
test('Three arguments', () {
- expect(testInstance!.add_Y_Z_(23, 17, 60), 100);
+ expect(testInstance.add_Y_Z_(23, 17, 60), 100);
});
});
@@ -62,5 +63,35 @@
expect(MethodInterface.sub_Y_Z_(lib, 10, 7, 3), -20);
});
});
+
+ group('Regress #608', () {
+ test('Structs', () {
+ final inputPtr = calloc<Vec4>();
+ final input = inputPtr.ref;
+ input.x = 1.2;
+ input.y = 3.4;
+ input.z = 5.6;
+ input.w = 7.8;
+
+ final resultPtr = calloc<Vec4>();
+ final result = resultPtr.ref;
+ testInstance.twiddleVec4Components_(resultPtr, input);
+ expect(result.x, 3.4);
+ expect(result.y, 5.6);
+ expect(result.z, 7.8);
+ expect(result.w, 1.2);
+
+ calloc.free(inputPtr);
+ calloc.free(resultPtr);
+ });
+
+ test('Floats', () {
+ expect(testInstance.addFloats_Y_(1.23, 4.56), closeTo(5.79, 1e-6));
+ });
+
+ test('Doubles', () {
+ expect(testInstance.addDoubles_Y_(1.23, 4.56), closeTo(5.79, 1e-6));
+ });
+ });
});
}
diff --git a/pkgs/ffigen/test/native_objc_test/method_test.m b/pkgs/ffigen/test/native_objc_test/method_test.m
index 86ea090..179f4ef 100644
--- a/pkgs/ffigen/test/native_objc_test/method_test.m
+++ b/pkgs/ffigen/test/native_objc_test/method_test.m
@@ -1,5 +1,12 @@
#import <Foundation/NSObject.h>
+typedef struct {
+ double x;
+ double y;
+ double z;
+ double w;
+} Vec4;
+
@interface MethodInterface : NSObject {
}
@@ -14,6 +21,10 @@
+(int32_t)sub:(int32_t)x Y:(int32_t) y;
+(int32_t)sub:(int32_t)x Y:(int32_t) y Z:(int32_t) z;
+-(Vec4)twiddleVec4Components:(Vec4)v;
+-(float)addFloats:(float)x Y:(float) y;
+-(double)addDoubles:(double)x Y:(double) y;
+
@end
@implementation MethodInterface
@@ -50,4 +61,21 @@
return - x - y - z;
}
+-(Vec4)twiddleVec4Components:(Vec4)v {
+ Vec4 u;
+ u.x = v.y;
+ u.y = v.z;
+ u.z = v.w;
+ u.w = v.x;
+ return u;
+}
+
+-(float)addFloats:(float)x Y:(float) y {
+ return x + y;
+}
+
+-(double)addDoubles:(double)x Y:(double) y {
+ return x + y;
+}
+
@end
diff --git a/pkgs/ffigen/test/native_objc_test/property_test.dart b/pkgs/ffigen/test/native_objc_test/property_test.dart
index 72c11e0..9bd4f3c 100644
--- a/pkgs/ffigen/test/native_objc_test/property_test.dart
+++ b/pkgs/ffigen/test/native_objc_test/property_test.dart
@@ -8,13 +8,14 @@
import 'dart:ffi';
import 'dart:io';
+import 'package:ffi/ffi.dart';
import 'package:test/test.dart';
import '../test_utils.dart';
import 'property_bindings.dart';
import 'util.dart';
void main() {
- PropertyInterface? testInstance;
+ late PropertyInterface testInstance;
late PropertyTestObjCLibrary lib;
group('properties', () {
@@ -29,12 +30,12 @@
group('instance properties', () {
test('read-only property', () {
- expect(testInstance!.readOnlyProperty, 7);
+ expect(testInstance.readOnlyProperty, 7);
});
test('read-write property', () {
- testInstance!.readWriteProperty = 23;
- expect(testInstance!.readWriteProperty, 23);
+ testInstance.readWriteProperty = 23;
+ expect(testInstance.readWriteProperty, 23);
});
});
@@ -48,5 +49,38 @@
expect(PropertyInterface.getClassReadWriteProperty(lib), 101);
});
});
+
+ group('Regress #608', () {
+ test('Structs', () {
+ final inputPtr = calloc<Vec4>();
+ final input = inputPtr.ref;
+ input.x = 1.2;
+ input.y = 3.4;
+ input.z = 5.6;
+ input.w = 7.8;
+
+ final resultPtr = calloc<Vec4>();
+ final result = resultPtr.ref;
+ testInstance.structProperty = input;
+ testInstance.getStructProperty(resultPtr);
+ expect(result.x, 1.2);
+ expect(result.y, 3.4);
+ expect(result.z, 5.6);
+ expect(result.w, 7.8);
+
+ calloc.free(inputPtr);
+ calloc.free(resultPtr);
+ });
+
+ test('Floats', () {
+ testInstance.floatProperty = 1.23;
+ expect(testInstance.floatProperty, closeTo(1.23, 1e-6));
+ });
+
+ test('Doubles', () {
+ testInstance.doubleProperty = 1.23;
+ expect(testInstance.doubleProperty, 1.23);
+ });
+ });
});
}
diff --git a/pkgs/ffigen/test/native_objc_test/property_test.m b/pkgs/ffigen/test/native_objc_test/property_test.m
index 117c6d5..0085034 100644
--- a/pkgs/ffigen/test/native_objc_test/property_test.m
+++ b/pkgs/ffigen/test/native_objc_test/property_test.m
@@ -2,6 +2,13 @@
@class UndefinedTemplate<ObjectType>;
+typedef struct {
+ double x;
+ double y;
+ double z;
+ double w;
+} Vec4;
+
@interface PropertyInterface : NSObject {
}
@@ -10,6 +17,9 @@
@property (class, readonly, copy) UndefinedTemplate<NSString *> *regressGH436;
@property (class, readonly) int32_t classReadOnlyProperty;
@property (class) int32_t classReadWriteProperty;
+@property float floatProperty;
+@property double doubleProperty;
+@property Vec4 structProperty;
@end
@@ -33,4 +43,4 @@
_classReadWriteProperty = x;
}
-@end
\ No newline at end of file
+@end