dart-sdk/tests/ffi/vmspecific_ffi_native_test.dart
Daco Harkes 3de5d25429 [vm] Class modifiers for NativeFieldWrapper subtypes
Split off https://dart-review.googlesource.com/c/sdk/+/291761.
Landing separately, so that the breaking change itself is a smaller
CL.

TEST=ci build (see touched _test files)

bug: https://github.com/dart-lang/sdk/issues/51896
Change-Id: Ic8d218845ccb6a277689e911b2c34c44639e13cf
Cq-Include-Trybots: luci.dart.try:flutter-linux-try,vm-aot-linux-debug-x64c-try,vm-ffi-android-debug-arm-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/292000
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Daco Harkes <dacoharkes@google.com>
2023-03-31 13:20:19 +00:00

173 lines
6.3 KiB
Dart

// 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
// NOTE: There is no `test/ffi_2/...` version of this test since annotations
// with type arguments isn't supported in that version of Dart.
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');
@FfiNative<Handle Function(Handle, IntPtr, IntPtr)>(
'Dart_SetNativeInstanceField')
external Object setNativeInstanceField(Object obj, int index, int ptr);
// Basic FfiNative test functions.
@FfiNative<IntPtr Function(IntPtr)>('ReturnIntPtr')
external int returnIntPtr(int x);
@FfiNative<IntPtr Function(IntPtr)>('ReturnIntPtr', isLeaf: true)
external int returnIntPtrLeaf(int x);
@FfiNative<IntPtr Function()>('IsThreadInGenerated')
external int isThreadInGenerated();
@FfiNative<IntPtr Function()>('IsThreadInGenerated', isLeaf: true)
external int isThreadInGeneratedLeaf();
class Classy {
@FfiNative<IntPtr Function(IntPtr)>('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).
@FfiNative<IntPtr Function(Pointer<Void>, IntPtr)>('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.
@FfiNative<IntPtr Function(Handle, IntPtr)>('AddHandleFieldAndInt')
external int addSelfHandleFieldAndIntMethod(int x);
@FfiNative<IntPtr Function(Pointer<Void>, Pointer<Void>)>('AddPtrAndPtr')
external int addSelfPtrAndPtrMethod(ClassWithNativeField other);
@FfiNative<IntPtr Function(Handle, Pointer<Void>)>('AddHandleFieldAndPtr')
external int addSelfHandleFieldAndPtrMethod(ClassWithNativeField other);
@FfiNative<IntPtr Function(Handle, Handle)>('AddHandleFieldAndHandleField')
external int addSelfHandleFieldAndHandleFieldMethod(
ClassWithNativeField other);
@FfiNative<IntPtr Function(Pointer<Void>, Handle)>('AddPtrAndHandleField')
external int addselfPtrAndHandleFieldMethod(ClassWithNativeField other);
}
class ClassWithoutNativeField {
// Instance methods implicitly pass their handle as the first arg.
@FfiNative<IntPtr Function(Handle, IntPtr)>('ReturnIntPtrMethod')
external int returnIntPtrMethod(int x);
}
// Native function takes a Handle, so a Handle is passed as-is.
@FfiNative<IntPtr Function(Handle)>('PassAsHandle')
external int passAsHandle(NativeFieldWrapperClass1 obj);
// FFI signature takes Pointer, Dart signature takes NativeFieldWrapperClass1.
// This implies automatic conversion.
@FfiNative<IntPtr Function(Pointer<Void>)>('PassAsPointer')
external int passAsPointer(NativeFieldWrapperClass1 obj);
// Pass Pointer automatically, and return value.
@FfiNative<IntPtr Function(Pointer<Void>, IntPtr)>('PassAsPointerAndValue')
external int passAsPointerAndValue(NativeFieldWrapperClass1 obj, int value);
// Pass Pointer automatically, and return value.
@FfiNative<IntPtr Function(IntPtr, Pointer<Void>)>('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 FfiNative functions.
Expect.equals(123, returnIntPtr(123));
Expect.equals(123, returnIntPtrLeaf(123));
Expect.equals(123, Classy.returnIntPtrStatic(123));
// Test FfiNative 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
// FfiNative 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));
}