blob: a6388b4c6e4774e4f22ce76c6272aeb167791530 [file] [log] [blame] [edit]
// Copyright (c) 2020, 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 'package:expect/expect.dart';
import 'dylib_utils.dart';
void main() {
testHandle();
testHandleWithInteger();
testReadField();
testTrueHandle();
testClosureCallback();
testReturnHandleInCallback();
testPropagateError();
testCallbackReturnException();
testDeepException();
testDeepException2();
testNull();
testDeepRecursive();
testNoHandlePropagateError();
testThrowOnReturnOfError();
}
void testHandle() {
print("testHandle");
final s = SomeClass(123);
print("passObjectToC($s)");
final result = passObjectToC(s);
print("result = $result");
Expect.isTrue(identical(s, result));
}
void testHandleWithInteger() {
print("testHandleWithInteger");
final s = 1337;
print("passObjectToC($s)");
final result = passObjectToC(s);
print("result = $result");
Expect.isTrue(identical(s, result));
}
void testReadField() {
final s = SomeClass(123);
final result = handleReadFieldValue(s);
Expect.equals(s.a, result);
}
void testTrueHandle() {
final result = trueHandle();
Expect.isTrue(result);
}
int globalCounter = 0;
void increaseCounter() {
print("increaseCounter");
globalCounter++;
}
void doClosureCallback(Object callback) {
print("doClosureCallback");
print(callback.runtimeType);
print(callback);
final callback_as_function = callback as void Function();
callback_as_function();
}
final closureCallbackPointer = Pointer.fromFunction<Void Function(Handle)>(
doClosureCallback,
);
void testClosureCallback() {
print("testClosureCallback $closureCallbackPointer");
Expect.equals(0, globalCounter);
closureCallbackThroughHandle(closureCallbackPointer, increaseCounter);
Expect.equals(1, globalCounter);
closureCallbackThroughHandle(closureCallbackPointer, increaseCounter);
Expect.equals(2, globalCounter);
}
final someObject = SomeClass(12356789);
Object returnHandleCallback() {
print("returnHandleCallback returning $someObject");
return someObject;
}
final returnHandleCallbackPointer = Pointer.fromFunction<Handle Function()>(
returnHandleCallback,
);
void testReturnHandleInCallback() {
print("testReturnHandleInCallback");
final result = returnHandleInCallback(returnHandleCallbackPointer);
Expect.isTrue(identical(someObject, result));
}
class SomeClass {
// We use this getter in the native api, don't tree shake it.
@pragma("vm:entry-point")
final int a;
SomeClass(this.a);
}
void testPropagateError() {
final s = SomeOtherClass(123);
Expect.throws(() => handleReadFieldValue(s));
}
class SomeOtherClass {
final int notA;
SomeOtherClass(this.notA);
}
final someException = Exception("exceptionHandleCallback exception");
Object exceptionHandleCallback() {
print("exceptionHandleCallback throwing ($someException)");
throw someException;
}
final exceptionHandleCallbackPointer = Pointer.fromFunction<Handle Function()>(
exceptionHandleCallback,
);
void testCallbackReturnException() {
print("testCallbackReturnException");
bool throws = false;
try {
final result = returnHandleInCallback(exceptionHandleCallbackPointer);
print(result);
} catch (e) {
throws = true;
print("caught ($e)");
Expect.isTrue(identical(someException, e));
}
Expect.isTrue(throws);
}
Object callCAgainFromCallback() {
print("callCAgainFromCallback");
final s = SomeOtherClass(123);
Expect.throws(() => handleReadFieldValue(s));
return someObject;
}
final callCAgainFromCallbackPointer = Pointer.fromFunction<Handle Function()>(
callCAgainFromCallback,
);
void testDeepException() {
print("testDeepException");
final result = returnHandleInCallback(callCAgainFromCallbackPointer);
Expect.isTrue(identical(someObject, result));
}
Object callCAgainFromCallback2() {
print("callCAgainFromCallback2");
final s = SomeOtherClass(123);
handleReadFieldValue(s); // throws.
return someObject;
}
final callCAgainFromCallbackPointer2 = Pointer.fromFunction<Handle Function()>(
callCAgainFromCallback2,
);
void testDeepException2() {
print("testDeepException2");
Expect.throws(() => returnHandleInCallback(callCAgainFromCallbackPointer2));
}
Object? returnNullHandleCallback() {
print("returnHandleCallback returning null");
return null;
}
final returnNullHandleCallbackPointer = Pointer.fromFunction<Handle Function()>(
returnNullHandleCallback,
);
void testNull() {
print("testNull");
final result = passObjectToC(null);
Expect.isNull(result);
final result2 = returnHandleInCallback(returnNullHandleCallbackPointer);
Expect.isNull(result2);
}
Object recurseAbove0(int i) {
print("recurseAbove0($i)");
if (i == 0) {
print("throwing");
throw someException;
}
if (i < 0) {
print("returning");
return someObject;
}
final result = handleRecursion(
SomeClassWithMethod(),
recurseAbove0Pointer,
i - 1,
);
print("return $i");
return result;
}
final recurseAbove0Pointer = Pointer.fromFunction<Handle Function(Int64)>(
recurseAbove0,
);
class SomeClassWithMethod {
// We use this method in the native api, don't tree shake it.
@pragma("vm:entry-point")
Object a(int i) => recurseAbove0(i);
}
void testDeepRecursive() {
// works on arm.
Expect.throws(() {
handleRecursion(123, recurseAbove0Pointer, 1);
});
Expect.throws(() {
handleRecursion(SomeClassWithMethod(), recurseAbove0Pointer, 1);
});
Expect.throws(() {
recurseAbove0(100);
});
final result = recurseAbove0(101);
Expect.isTrue(identical(someObject, result));
}
void testNoHandlePropagateError() {
bool throws = false;
try {
final result = propagateErrorWithoutHandle(exceptionHandleCallbackPointer);
print(result.runtimeType);
print(result);
} catch (e) {
throws = true;
print("caught ($e, ${e.runtimeType})");
Expect.isTrue(identical(someException, e));
}
Expect.isTrue(throws);
}
void testThrowOnReturnOfError() {
bool throws = false;
try {
final result = autoPropagateErrorInHandle(exceptionHandleCallbackPointer);
print(result.runtimeType);
print(result);
} catch (e) {
throws = true;
print("caught ($e)");
Expect.isTrue(identical(someException, e));
}
Expect.isTrue(throws);
}
final testLibrary = dlopenPlatformSpecific("ffi_test_functions");
final passObjectToC = testLibrary
.lookupFunction<Handle Function(Handle), Object? Function(Object?)>(
"PassObjectToC",
);
final handleReadFieldValue = testLibrary
.lookupFunction<Int64 Function(Handle), int Function(Object)>(
"HandleReadFieldValue",
);
final trueHandle = testLibrary
.lookupFunction<Handle Function(), Object Function()>("TrueHandle");
final closureCallbackThroughHandle = testLibrary.lookupFunction<
Void Function(Pointer<NativeFunction<Void Function(Handle)>>, Handle),
void Function(Pointer<NativeFunction<Void Function(Handle)>>, Object)
>("ClosureCallbackThroughHandle");
final returnHandleInCallback = testLibrary.lookupFunction<
Handle Function(Pointer<NativeFunction<Handle Function()>>),
Object Function(Pointer<NativeFunction<Handle Function()>>)
>("ReturnHandleInCallback");
final handleRecursion = testLibrary.lookupFunction<
Handle Function(
Handle,
Pointer<NativeFunction<Handle Function(Int64)>>,
Int64,
),
Object Function(Object, Pointer<NativeFunction<Handle Function(Int64)>>, int)
>("HandleRecursion");
final propagateErrorWithoutHandle = testLibrary.lookupFunction<
Int64 Function(Pointer<NativeFunction<Handle Function()>>),
int Function(Pointer<NativeFunction<Handle Function()>>)
>("PropagateErrorWithoutHandle");
final autoPropagateErrorInHandle = testLibrary.lookupFunction<
Handle Function(Pointer<NativeFunction<Handle Function()>>),
Object Function(Pointer<NativeFunction<Handle Function()>>)
>("ThrowOnReturnOfError");