| // Copyright (c) 2021, 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. |
| // |
| // SharedObjects=ffi_test_functions |
| |
| import 'dart:ffi'; |
| import 'dart:nativewrappers'; |
| |
| import 'package:expect/expect.dart'; |
| |
| import 'dylib_utils.dart'; |
| |
| final nativeLib = dlopenPlatformSpecific('ffi_test_functions'); |
| |
| final getRootLibraryUrl = nativeLib |
| .lookupFunction<Handle Function(), Object Function()>('GetRootLibraryUrl'); |
| |
| final setFfiNativeResolverForTest = |
| nativeLib.lookupFunction<Void Function(Handle), void Function(Object)>( |
| 'SetFfiNativeResolverForTest'); |
| |
| @Native<Handle Function(Handle, IntPtr, IntPtr)>( |
| symbol: 'Dart_SetNativeInstanceField') |
| external Object setNativeInstanceField(Object obj, int index, int ptr); |
| |
| // Basic Native test functions. |
| |
| @Native<IntPtr Function(IntPtr)>(symbol: 'ReturnIntPtr') |
| external int returnIntPtr(int x); |
| |
| @Native<IntPtr Function(IntPtr)>(symbol: 'ReturnIntPtr', isLeaf: true) |
| external int returnIntPtrLeaf(int x); |
| |
| @Native<IntPtr Function()>(symbol: 'IsThreadInGenerated') |
| external int isThreadInGenerated(); |
| |
| @Native<IntPtr Function()>(symbol: 'IsThreadInGenerated', isLeaf: true) |
| external int isThreadInGeneratedLeaf(); |
| |
| class Classy { |
| @Native<IntPtr Function(IntPtr)>(symbol: 'ReturnIntPtr') |
| external static int returnIntPtrStatic(int x); |
| } |
| |
| // For automatic transform of NativeFieldWrapperClass1 to Pointer. |
| |
| base class ClassWithNativeField extends NativeFieldWrapperClass1 { |
| ClassWithNativeField(int value) { |
| setNativeInstanceField(this, 0, value); |
| } |
| |
| // Instance methods implicitly pass a 'self' reference as the first argument. |
| // Passed as Pointer if the native function takes that (and the class can be |
| // converted). |
| @Native<IntPtr Function(Pointer<Void>, IntPtr)>(symbol: 'AddPtrAndInt') |
| external int addSelfPtrAndIntMethod(int x); |
| |
| // Instance methods implicitly pass a 'self' reference as the first argument. |
| // Passed as Handle if the native function takes that. |
| @Native<IntPtr Function(Handle, IntPtr)>(symbol: 'AddHandleFieldAndInt') |
| external int addSelfHandleFieldAndIntMethod(int x); |
| |
| @Native<IntPtr Function(Pointer<Void>, Pointer<Void>)>(symbol: 'AddPtrAndPtr') |
| external int addSelfPtrAndPtrMethod(ClassWithNativeField other); |
| |
| @Native<IntPtr Function(Handle, Pointer<Void>)>( |
| symbol: 'AddHandleFieldAndPtr') |
| external int addSelfHandleFieldAndPtrMethod(ClassWithNativeField other); |
| |
| @Native<IntPtr Function(Handle, Handle)>( |
| symbol: 'AddHandleFieldAndHandleField') |
| external int addSelfHandleFieldAndHandleFieldMethod( |
| ClassWithNativeField other); |
| |
| @Native<IntPtr Function(Pointer<Void>, Handle)>( |
| symbol: 'AddPtrAndHandleField') |
| external int addselfPtrAndHandleFieldMethod(ClassWithNativeField other); |
| } |
| |
| class ClassWithoutNativeField { |
| // Instance methods implicitly pass their handle as the first arg. |
| @Native<IntPtr Function(Handle, IntPtr)>(symbol: 'ReturnIntPtrMethod') |
| external int returnIntPtrMethod(int x); |
| } |
| |
| // Native function takes a Handle, so a Handle is passed as-is. |
| @Native<IntPtr Function(Handle)>(symbol: 'PassAsHandle') |
| external int passAsHandle(NativeFieldWrapperClass1 obj); |
| // FFI signature takes Pointer, Dart signature takes NativeFieldWrapperClass1. |
| // This implies automatic conversion. |
| @Native<IntPtr Function(Pointer<Void>)>(symbol: 'PassAsPointer') |
| external int passAsPointer(NativeFieldWrapperClass1 obj); |
| // Pass Pointer automatically, and return value. |
| @Native<IntPtr Function(Pointer<Void>, IntPtr)>(symbol: 'PassAsPointerAndValue') |
| external int passAsPointerAndValue(NativeFieldWrapperClass1 obj, int value); |
| // Pass Pointer automatically, and return value. |
| @Native<IntPtr Function(IntPtr, Pointer<Void>)>(symbol: 'PassAsValueAndPointer') |
| external int passAsValueAndPointer(int value, NativeFieldWrapperClass1 obj); |
| |
| // Helpers for testing argument evaluation order is preserved. |
| int state = 0; |
| int setState(int value) { |
| state = value; |
| return 0; |
| } |
| |
| base class StateSetter extends NativeFieldWrapperClass1 { |
| StateSetter(int value) { |
| setNativeInstanceField(this, 0, 0); |
| state = value; |
| } |
| } |
| |
| void main() { |
| // Register test resolver for top-level functions above. |
| setFfiNativeResolverForTest(getRootLibraryUrl()); |
| |
| // Test we can call Native functions. |
| Expect.equals(123, returnIntPtr(123)); |
| Expect.equals(123, returnIntPtrLeaf(123)); |
| Expect.equals(123, Classy.returnIntPtrStatic(123)); |
| |
| // Test Native leaf calls remain in generated code. |
| // Regular calls should transition generated -> native. |
| Expect.equals(0, isThreadInGenerated()); |
| // Leaf calls should remain in generated state. |
| Expect.equals(1, isThreadInGeneratedLeaf()); |
| |
| // Test that objects extending NativeFieldWrapperClass1 can be passed to |
| // Native functions that take Pointer. |
| // Such objects should automatically be converted and pass as Pointer. |
| { |
| final cwnf = ClassWithNativeField(123456); |
| Expect.equals(123456, passAsHandle(cwnf)); |
| Expect.equals(123456, passAsPointer(cwnf)); |
| } |
| |
| // Test that the order of argument evaluation is being preserved through the |
| // transform wrapping NativeFieldWrapperClass1 objects. |
| state = 0; |
| passAsValueAndPointer(setState(7), StateSetter(3)); |
| Expect.equals(3, state); |
| |
| // Test transforms of instance methods. |
| Expect.equals(234, ClassWithoutNativeField().returnIntPtrMethod(234)); |
| Expect.equals(1012, ClassWithNativeField(12).addSelfPtrAndIntMethod(1000)); |
| Expect.equals( |
| 2021, ClassWithNativeField(21).addSelfHandleFieldAndIntMethod(2000)); |
| Expect.equals( |
| 3031, |
| ClassWithNativeField(31) |
| .addSelfPtrAndPtrMethod(ClassWithNativeField(3000))); |
| Expect.equals( |
| 4041, |
| ClassWithNativeField(41) |
| .addSelfHandleFieldAndPtrMethod(ClassWithNativeField(4000))); |
| Expect.equals( |
| 5051, |
| ClassWithNativeField(51) |
| .addSelfHandleFieldAndHandleFieldMethod(ClassWithNativeField(5000))); |
| Expect.equals( |
| 6061, |
| ClassWithNativeField(61) |
| .addselfPtrAndHandleFieldMethod(ClassWithNativeField(6000))); |
| Expect.throws(() { |
| ClassWithNativeField(nullptr.address) |
| .addSelfPtrAndPtrMethod(ClassWithNativeField(7000)); |
| }); |
| // Does not throw. |
| ClassWithNativeField(8000) |
| .addSelfPtrAndPtrMethod(ClassWithNativeField(nullptr.address)); |
| } |