dart-sdk/tests/ffi_2/vmspecific_handle_test.dart
Daco Harkes da39a4abff [vm/ffi] Remove try-catch from ffi trampoline if no handle scope
https://dart-review.googlesource.com/c/sdk/+/145591 introduced a try
catch into FFI calls to call ExitHandleScope on the exception path.
However, we only need this try-catch if we actually need to exit the
handle scope on the exception path, which is not the case if we have
no handles in the signature. So this CL makes the try catch optional.

This speeds up ffi calls without handles (tested on JIT x64):
FfiCall.Uint8x01(RunTime): 206.4801280066068 us.
->
FfiCall.Uint8x01(RunTime): 203.7240782236708 us.

Also adds a test that checks that an exception can still be propagated
with Dart_PropagateError from native code when the FFI trampoline has
no try catch.

Change-Id: I9fac7078381c60fb8055b64fff29ea364fbc948f
Cq-Include-Trybots: luci.dart.try:vm-ffi-android-debug-arm-try,vm-ffi-android-debug-arm64-try,app-kernel-linux-debug-x64-try,vm-kernel-linux-debug-ia32-try,vm-kernel-win-debug-x64-try,vm-kernel-win-debug-ia32-try,vm-kernel-precomp-linux-debug-x64-try,vm-dartkb-linux-release-x64-abi-try,vm-kernel-precomp-android-release-arm64-try,vm-kernel-asan-linux-release-x64-try,vm-kernel-msan-linux-release-x64-try,vm-kernel-precomp-msan-linux-release-x64-try,vm-kernel-linux-release-simarm-try,vm-kernel-linux-release-simarm64-try,vm-kernel-precomp-android-release-arm_x64-try,vm-kernel-precomp-obfuscate-linux-release-x64-try,dart-sdk-linux-try,analyzer-analysis-server-linux-try,analyzer-linux-release-try,front-end-linux-release-x64-try,vm-kernel-precomp-win-release-x64-try,vm-kernel-mac-debug-x64-try,vm-precomp-ffi-qemu-linux-release-arm-try,vm-kernel-nnbd-linux-debug-x64-try,analyzer-nnbd-linux-release-try,front-end-nnbd-linux-release-x64-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/151239
Commit-Queue: Daco Harkes <dacoharkes@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
2020-06-26 12:03:02 +00:00

269 lines
7.1 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=--enable-testing-pragmas
import 'dart:ffi';
import 'package:expect/expect.dart';
import 'dylib_utils.dart';
void main() {
testHandle();
testReadField();
testTrueHandle();
testClosureCallback();
testReturnHandleInCallback();
testPropagateError();
testCallbackReturnException();
testDeepException();
testDeepException2();
testNull();
testDeepRecursive();
testNoHandlePropagateError();
}
void testHandle() {
print("testHandle");
final s = SomeClass(123);
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);
} 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");