11import 'dart:async' ;
2- import 'dart:ffi' as ffi ;
2+ import 'dart:ffi' ;
33import 'dart:io' show Platform;
44import 'dart:io' ;
55import 'dart:isolate' ;
@@ -15,101 +15,98 @@ import 'package:path/path.dart' as Path;
1515class Binding {
1616 static final String _callFuncName = 'OpenPGPBridgeCall' ;
1717 static final String _libraryName = 'libopenpgp_bridge' ;
18- static final Binding _singleton = Binding ._internal ();
18+ static final Binding _instance = Binding ._internal ();
1919
20- late ffi.DynamicLibrary _library;
20+ late final DynamicLibrary _library;
21+
22+ late final BridgeCallDart _bridgeCall;
2123
2224 factory Binding () {
23- return _singleton ;
25+ return _instance ;
2426 }
2527
2628 Binding ._internal () {
2729 _library = openLib ();
30+ _bridgeCall =
31+ _library.lookupFunction <BridgeCallC , BridgeCallDart >(_callFuncName);
2832 }
2933
30- static void callBridge (IsolateArguments args) async {
31- var result = await Binding ().call (args.name, args.payload);
34+ @pragma ('vm:entry-point' )
35+ static void _callBridge (IsolateArguments args) {
36+ var result = _instance.call (args.name, args.payload);
3237 args.port.send (result);
3338 }
3439
3540 Future <Uint8List > callAsync (String name, Uint8List payload) async {
36- final port = ReceivePort ('${_libraryName }_port' );
37- final args = IsolateArguments (name, payload, port.sendPort);
38- final completer = new Completer <Uint8List >();
39-
40- final isolate = await Isolate .spawn (
41- callBridge,
42- args,
43- errorsAreFatal: false ,
44- debugName: '${_libraryName }_isolate' ,
45- onError: port.sendPort,
46- );
47-
48- port.listen (
49- (message) async {
50- if (message is Uint8List ) {
51- completer.complete (message);
52- } else if (message is List ) {
53- completer.completeError (message.firstOrNull ?? "internal error" );
54- } else {
55- completer.completeError (message ?? "spawn error" );
41+ final port = ReceivePort ();
42+ final completer = Completer <Uint8List >();
43+
44+ try {
45+ final isolate = await Isolate .spawn (
46+ _callBridge,
47+ IsolateArguments (name, payload, port.sendPort),
48+ errorsAreFatal: false ,
49+ debugName: '${_libraryName }_isolate' ,
50+ onError: port.sendPort,
51+ );
52+
53+ port.listen ((message) {
54+ try {
55+ if (message is Uint8List ) {
56+ completer.complete (message);
57+ } else if (message is List && message.isNotEmpty) {
58+ completer.completeError (message.first ?? "internal error" );
59+ } else {
60+ completer.completeError ("spawn error" );
61+ }
62+ } finally {
63+ port.close ();
64+ isolate.kill (priority: Isolate .beforeNextEvent);
5665 }
57- port.close ();
58- },
59- onDone: () {
60- isolate.kill (priority: Isolate .beforeNextEvent);
61- },
62- );
63-
64- return completer.future;
66+ });
67+
68+ return completer.future;
69+ } catch (e) {
70+ port.close ();
71+ throw OpenPGPException ("Failed to start isolate: $e " );
72+ }
6573 }
6674
67- Future <Uint8List > call (String name, Uint8List payload) async {
68- final callable = _library
69- .lookup< ffi.NativeFunction <call_func>> (_callFuncName)
70- .asFunction <Call >();
75+ Uint8List call (String name, Uint8List payload) {
76+ if (_bridgeCall == null ) {
77+ throw OpenPGPException (
78+ "FFI function ${_callFuncName } is not initialized. Check library loading." );
79+ }
7180
72- final pointer = malloc< ffi.Uint8 > (payload.length);
81+ final namePointer = name.toNativeUtf8 ();
82+ final payloadPointer = malloc.allocate <Uint8 >(payload.length);
83+ payloadPointer.asTypedList (payload.length).setAll (0 , payload);
7384
74- // https://github.com/dart-lang/ffi/issues/27
75- // https://github.com/objectbox/objectbox-dart/issues/69
76- for (var i = 0 ; i < payload.length; i++ ) {
77- pointer[i] = payload[i];
85+ final result =
86+ _bridgeCall (namePointer, payloadPointer.cast <Void >(), payload.length);
87+ if (result.address == 0 ) {
88+ throw OpenPGPException (
89+ "FFI function ${_callFuncName } returned null pointer. Check openpgp-mobile implementation." );
7890 }
79- final payloadPointer = pointer.cast< ffi.Void > ();
80- final namePointer = toUtf8 (name);
81-
82- final result = callable (namePointer, payloadPointer, payload.length);
8391
8492 malloc.free (namePointer);
85- malloc.free (pointer);
86-
87- handleError (result.ref.error, result);
93+ malloc.free (payloadPointer);
8894
89- final output =
90- result.ref.message.cast < ffi. Uint8 > (). asTypedList (result.ref.size );
95+ handleError (result.ref.errorMessage, result);
96+ final output = result.ref.toUint8List ( );
9197 freeResult (result);
98+
9299 return output;
93100 }
94101
95- void handleError (
96- ffi.Pointer <Utf8 > error, ffi.Pointer <FFIBytesReturn > result) {
97- if (error.address != ffi.nullptr.address) {
98- var message = fromUtf8 (error);
102+ void handleError (String ? error, Pointer <BytesReturn > result) {
103+ if (error != null && error.isNotEmpty) {
99104 freeResult (result);
100- throw new OpenPGPException (message );
105+ throw OpenPGPException (error );
101106 }
102107 }
103108
104- ffi.Pointer <Utf8 > toUtf8 (String ? text) {
105- return text == null ? "" .toNativeUtf8 () : text.toNativeUtf8 ();
106- }
107-
108- String fromUtf8 (ffi.Pointer <Utf8 >? text) {
109- return text == null ? "" : text.toDartString ();
110- }
111-
112- void freeResult (ffi.Pointer <FFIBytesReturn > result) {
109+ void freeResult (Pointer <BytesReturn > result) {
113110 if (! Platform .isWindows) {
114111 malloc.free (result);
115112 }
@@ -143,7 +140,7 @@ class Binding {
143140 }
144141 }
145142
146- ffi. DynamicLibrary openLib () {
143+ DynamicLibrary openLib () {
147144 var isFlutterTest = Platform .environment.containsKey ('FLUTTER_TEST' );
148145
149146 if (Platform .isMacOS || Platform .isIOS) {
@@ -153,13 +150,13 @@ class Binding {
153150 var ffiFile = Path .join (
154151 appDirectory.path, "Contents" , "Frameworks" , "$_libraryName .dylib" );
155152 validateTestFFIFile (ffiFile);
156- return ffi. DynamicLibrary .open (ffiFile);
153+ return DynamicLibrary .open (ffiFile);
157154 }
158155 if (Platform .isMacOS) {
159- return ffi. DynamicLibrary .open ("$_libraryName .dylib" );
156+ return DynamicLibrary .open ("$_libraryName .dylib" );
160157 }
161158 if (Platform .isIOS) {
162- return ffi. DynamicLibrary .process ();
159+ return DynamicLibrary .process ();
163160 }
164161 }
165162
@@ -170,24 +167,24 @@ class Binding {
170167
171168 var ffiFile = 'build/linux/$arch /debug/bundle/lib/$_libraryName .so' ;
172169 validateTestFFIFile (ffiFile);
173- return ffi. DynamicLibrary .open (ffiFile);
170+ return DynamicLibrary .open (ffiFile);
174171 }
175172
176173 if (Platform .isLinux) {
177174 try {
178- return ffi. DynamicLibrary .open ("$_libraryName .so" );
175+ return DynamicLibrary .open ("$_libraryName .so" );
179176 } catch (e) {
180177 print (e);
181178 var binary = File ("/proc/self/cmdline" ).readAsStringSync ();
182179 var suggestedFile =
183180 Path .join (Path .dirname (binary), "lib" , "$_libraryName .so" );
184- return ffi. DynamicLibrary .open (suggestedFile);
181+ return DynamicLibrary .open (suggestedFile);
185182 }
186183 }
187184
188185 if (Platform .isAndroid) {
189186 try {
190- return ffi. DynamicLibrary .open ("$_libraryName .so" );
187+ return DynamicLibrary .open ("$_libraryName .so" );
191188 } catch (e) {
192189 print ("fallback to open DynamicLibrary on older devices" );
193190 //fallback for devices that cannot load dynamic libraries by name: load the library with an absolute path
@@ -198,7 +195,7 @@ class Binding {
198195 appid = String .fromCharCodes (
199196 appid.codeUnits.where ((element) => element != 0 ));
200197 final loadPath = "/data/data/$appid /lib/$_libraryName .so" ;
201- return ffi. DynamicLibrary .open (loadPath);
198+ return DynamicLibrary .open (loadPath);
202199 }
203200 }
204201 }
@@ -211,9 +208,9 @@ class Binding {
211208 var ffiFile = Path .canonicalize (Path .join (
212209 r'build\windows' , arch, r'runner\Debug' , '$_libraryName .dll' ));
213210 validateTestFFIFile (ffiFile);
214- return ffi. DynamicLibrary .open (ffiFile);
211+ return DynamicLibrary .open (ffiFile);
215212 }
216- return ffi. DynamicLibrary .open ("$_libraryName .dll" );
213+ return DynamicLibrary .open ("$_libraryName .dll" );
217214 }
218215
219216 throw UnsupportedError ('Unknown platform: ${Platform .operatingSystem }' );
0 commit comments