blob: 5264901bd211c18f952d918db6dd62fe88c4027b [file] [log] [blame]
// 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');
final triggerGC = nativeLib
.lookupFunction<Void Function(IntPtr), void Function(int)>('TriggerGC');
@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.
// Sets the native (dummy) resource and the object's finalizer.
@FfiNative<Void Function(Handle, IntPtr)>('SetSharedResource')
external void setSharedResource(NativeFieldWrapperClass1 obj, int value);
// Return the native (dummy) resource.
@FfiNative<IntPtr Function()>('GetSharedResource')
external int getSharedResource();
class ClassWithNativeField extends NativeFieldWrapperClass1 {
ClassWithNativeField(int value) {
setNativeInstanceField(this, 0, value);
setSharedResource(this, value);
}
}
// 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);
// Helper to embed triggerGC(..) as an expression.
int triggerGCWrap() {
triggerGC(0);
return 0;
}
// Helpers for testing argumnent evaluation order is preserved.
int state = 0;
int setState(int value) {
state = value;
return 0;
}
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 transform to wrap NativeFieldWrapperClass1 objects in
// getNativeField(..) doesn't violate the original argument's liveness.
Expect.equals(
314159,
passAsPointerAndValue(
// 1: Locally alloc. instance.
// If this gets wrapped in another call the instance does not live
// past the return of the wrapper call.
ClassWithNativeField(314159),
// 2: Force GC, to collect the above if it isn't being kept alive.
// 3: Check that the underlying (dummy) resource hasn't been
// "collected" (i.e. reset to 0).
triggerGCWrap() + getSharedResource()));
// 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);
}