dart-sdk/tests/ffi_2/vmspecific_handle_test.dart

270 lines
7.1 KiB
Dart
Raw Normal View History

[vm/ffi] Convert Objects to Dart_Handles in FFI calls This includes support for calling Dart_PropagateError in native code when doing FFI calls, and catching uncaught exceptions with Dart_IsError when doing FFI callbacks. The support for Dart_PropagateError adds a catch entry to the FFI trampoline, which prevents inlining these trampolines in AOT. This regresses the FfiCall benchmarks by 1-2% in AOT. In addition, Dart_PropagateError requires maintaining a bit whether we entered native/VM code from generated code through FFI or not. That way we can do the proper transition on the exception path. When entering generated code, we store this bit on the stack, right after the entry frame. Design: http://go/dart-ffi-handles Issue: https://github.com/dart-lang/sdk/issues/36858 Issue: https://github.com/dart-lang/sdk/issues/41319 Change-Id: Idfd7ff69132fb29cc730931a4113d914d4437396 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-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/+/145591 Commit-Queue: Daco Harkes <dacoharkes@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com> Reviewed-by: Alexander Markov <alexmarkov@google.com>
2020-06-12 11:14:22 +00:00
// 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
// @dart = 2.9
[vm/ffi] Convert Objects to Dart_Handles in FFI calls This includes support for calling Dart_PropagateError in native code when doing FFI calls, and catching uncaught exceptions with Dart_IsError when doing FFI callbacks. The support for Dart_PropagateError adds a catch entry to the FFI trampoline, which prevents inlining these trampolines in AOT. This regresses the FfiCall benchmarks by 1-2% in AOT. In addition, Dart_PropagateError requires maintaining a bit whether we entered native/VM code from generated code through FFI or not. That way we can do the proper transition on the exception path. When entering generated code, we store this bit on the stack, right after the entry frame. Design: http://go/dart-ffi-handles Issue: https://github.com/dart-lang/sdk/issues/36858 Issue: https://github.com/dart-lang/sdk/issues/41319 Change-Id: Idfd7ff69132fb29cc730931a4113d914d4437396 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-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/+/145591 Commit-Queue: Daco Harkes <dacoharkes@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com> Reviewed-by: Alexander Markov <alexmarkov@google.com>
2020-06-12 11:14:22 +00:00
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();
[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
testNoHandlePropagateError();
[vm/ffi] Convert Objects to Dart_Handles in FFI calls This includes support for calling Dart_PropagateError in native code when doing FFI calls, and catching uncaught exceptions with Dart_IsError when doing FFI callbacks. The support for Dart_PropagateError adds a catch entry to the FFI trampoline, which prevents inlining these trampolines in AOT. This regresses the FfiCall benchmarks by 1-2% in AOT. In addition, Dart_PropagateError requires maintaining a bit whether we entered native/VM code from generated code through FFI or not. That way we can do the proper transition on the exception path. When entering generated code, we store this bit on the stack, right after the entry frame. Design: http://go/dart-ffi-handles Issue: https://github.com/dart-lang/sdk/issues/36858 Issue: https://github.com/dart-lang/sdk/issues/41319 Change-Id: Idfd7ff69132fb29cc730931a4113d914d4437396 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-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/+/145591 Commit-Queue: Daco Harkes <dacoharkes@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com> Reviewed-by: Alexander Markov <alexmarkov@google.com>
2020-06-12 11:14:22 +00:00
}
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));
}
[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
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);
}
[vm/ffi] Convert Objects to Dart_Handles in FFI calls This includes support for calling Dart_PropagateError in native code when doing FFI calls, and catching uncaught exceptions with Dart_IsError when doing FFI callbacks. The support for Dart_PropagateError adds a catch entry to the FFI trampoline, which prevents inlining these trampolines in AOT. This regresses the FfiCall benchmarks by 1-2% in AOT. In addition, Dart_PropagateError requires maintaining a bit whether we entered native/VM code from generated code through FFI or not. That way we can do the proper transition on the exception path. When entering generated code, we store this bit on the stack, right after the entry frame. Design: http://go/dart-ffi-handles Issue: https://github.com/dart-lang/sdk/issues/36858 Issue: https://github.com/dart-lang/sdk/issues/41319 Change-Id: Idfd7ff69132fb29cc730931a4113d914d4437396 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-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/+/145591 Commit-Queue: Daco Harkes <dacoharkes@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com> Reviewed-by: Alexander Markov <alexmarkov@google.com>
2020-06-12 11:14:22 +00:00
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");
[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
final propagateErrorWithoutHandle = testLibrary.lookupFunction<
Int64 Function(Pointer<NativeFunction<Handle Function()>>),
int Function(Pointer<NativeFunction<Handle Function()>>)>(
"PropagateErrorWithoutHandle");