84704290ab
The C side can return any object, including `null`, we should therefore change those declarations to return top type / `Object?`. Issue https://github.com/dart-lang/sdk/issues/63411 TEST=https://dartbug.com/63411 CoreLibraryReviewExempt: Internal only changes Change-Id: I6478e39113fba5d422059de62b88fe738ddbfc05 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/504920 Commit-Queue: Martin Kustermann <kustermann@google.com> Reviewed-by: Slava Egorov <vegorov@google.com>
359 lines
9.2 KiB
Dart
359 lines
9.2 KiB
Dart
// 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
|
|
// VMOptions=
|
|
// VMOptions=--use_slow_path
|
|
|
|
import 'dart:ffi';
|
|
|
|
import 'package:expect/expect.dart';
|
|
|
|
import 'dylib_utils.dart';
|
|
|
|
void main() {
|
|
testHandle();
|
|
testHandleInReturn();
|
|
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 testHandleInReturn() {
|
|
final string = 'foo';
|
|
Expect.identical(string, stringFromString(string));
|
|
Expect.identical(string, stringFromObject(string));
|
|
Expect.identical(42, intFromInt(42));
|
|
Expect.identical(42, intFromObject(42));
|
|
// TODO(https://dartbug.com/49518): Uncomment the lines below when the
|
|
// runtime checks are added.
|
|
//
|
|
// Expect.throws(() => stringFromObject(Object()));
|
|
// Expect.throws(() => stringFromObject(42));
|
|
// Expect.throws(() => intFromObject(Object()));
|
|
// Expect.throws(() => intFromObject(string));
|
|
}
|
|
|
|
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 stringFromString = testLibrary
|
|
.lookupFunction<Handle Function(Handle), String Function(String)>(
|
|
"PassObjectToC",
|
|
);
|
|
final stringFromObject = testLibrary
|
|
.lookupFunction<Handle Function(Handle), String Function(Object)>(
|
|
"PassObjectToC",
|
|
);
|
|
final intFromInt = testLibrary
|
|
.lookupFunction<Handle Function(Handle), int Function(int)>(
|
|
"PassObjectToC",
|
|
);
|
|
final intFromObject = testLibrary
|
|
.lookupFunction<Handle Function(Handle), int 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");
|