mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 15:17:07 +00:00
3de5d25429
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>
173 lines
6.3 KiB
Dart
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));
|
|
}
|