mirror of
https://github.com/dart-lang/sdk
synced 2024-11-05 18:22:09 +00:00
6544c69e23
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>
478 lines
13 KiB
Dart
478 lines
13 KiB
Dart
// Copyright (c) 2019, 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.
|
|
//
|
|
// Dart test program for testing dart:ffi extra checks
|
|
//
|
|
// SharedObjects=ffi_test_dynamic_library ffi_test_functions
|
|
|
|
import 'dart:ffi';
|
|
|
|
import "package:ffi/ffi.dart";
|
|
|
|
import 'dylib_utils.dart';
|
|
|
|
void main() {
|
|
testGetGeneric();
|
|
testGetGeneric2();
|
|
testGetVoid();
|
|
testGetNativeFunction();
|
|
testGetNativeType();
|
|
testGetTypeMismatch();
|
|
testSetGeneric();
|
|
testSetGeneric2();
|
|
testSetVoid();
|
|
testSetNativeFunction();
|
|
testSetNativeType();
|
|
testSetTypeMismatch();
|
|
testAsFunctionGeneric();
|
|
testAsFunctionGeneric2();
|
|
testAsFunctionWrongNativeFunctionSignature();
|
|
testAsFunctionTypeMismatch();
|
|
testFromFunctionGeneric();
|
|
testFromFunctionGeneric2();
|
|
testFromFunctionWrongNativeFunctionSignature();
|
|
testFromFunctionTypeMismatch();
|
|
testFromFunctionClosure();
|
|
testFromFunctionTearOff();
|
|
testFromFunctionAbstract();
|
|
testLookupFunctionGeneric();
|
|
testLookupFunctionGeneric2();
|
|
testLookupFunctionWrongNativeFunctionSignature();
|
|
testLookupFunctionTypeMismatch();
|
|
testNativeFunctionSignatureInvalidReturn();
|
|
testNativeFunctionSignatureInvalidParam();
|
|
testNativeFunctionSignatureInvalidOptionalNamed();
|
|
testNativeFunctionSignatureInvalidOptionalPositional();
|
|
testHandleVariance();
|
|
}
|
|
|
|
typedef Int8UnOp = Int8 Function(Int8);
|
|
typedef IntUnOp = int Function(int);
|
|
|
|
void testGetGeneric() {
|
|
int generic(Pointer p) {
|
|
int result = -1;
|
|
result = p.value; //# 20: compile-time error
|
|
return result;
|
|
}
|
|
|
|
Pointer<Int8> p = allocate();
|
|
p.value = 123;
|
|
Pointer loseType = p;
|
|
generic(loseType);
|
|
free(p);
|
|
}
|
|
|
|
void testGetGeneric2() {
|
|
T? generic<T extends Object>() {
|
|
Pointer<Int8> p = allocate();
|
|
p.value = 123;
|
|
T? result;
|
|
result = p.value; //# 21: compile-time error
|
|
free(p);
|
|
return result;
|
|
}
|
|
|
|
generic<int>();
|
|
}
|
|
|
|
void testGetVoid() {
|
|
Pointer<IntPtr> p1 = allocate();
|
|
Pointer<Void> p2 = p1.cast();
|
|
|
|
p2.value; //# 22: compile-time error
|
|
|
|
free(p1);
|
|
}
|
|
|
|
void testGetNativeFunction() {
|
|
Pointer<NativeFunction<Int8UnOp>> p = Pointer.fromAddress(1337);
|
|
IntUnOp f = p.value; //# 23: compile-time error
|
|
}
|
|
|
|
void testGetNativeType() {
|
|
// Is it possible to obtain a Pointer<NativeType> at all?
|
|
}
|
|
|
|
void testGetTypeMismatch() {
|
|
Pointer<Pointer<Int16>> p = allocate();
|
|
Pointer<Int16> typedNull = nullptr;
|
|
p.value = typedNull;
|
|
|
|
// this fails to compile due to type mismatch
|
|
Pointer<Int8> p2 = p.value; //# 25: compile-time error
|
|
|
|
free(p);
|
|
}
|
|
|
|
void testSetGeneric() {
|
|
void generic(Pointer p) {
|
|
p.value = 123; //# 26: compile-time error
|
|
}
|
|
|
|
Pointer<Int8> p = allocate();
|
|
p.value = 123;
|
|
Pointer loseType = p;
|
|
generic(loseType);
|
|
free(p);
|
|
}
|
|
|
|
void testSetGeneric2() {
|
|
void generic<T extends Object>(T arg) {
|
|
Pointer<Int8> p = allocate();
|
|
p.value = arg; //# 27: compile-time error
|
|
free(p);
|
|
}
|
|
|
|
generic<int>(123);
|
|
}
|
|
|
|
void testSetVoid() {
|
|
Pointer<IntPtr> p1 = allocate();
|
|
Pointer<Void> p2 = p1.cast();
|
|
|
|
p2.value = 1234; //# 28: compile-time error
|
|
|
|
free(p1);
|
|
}
|
|
|
|
void testSetNativeFunction() {
|
|
Pointer<NativeFunction<Int8UnOp>> p = Pointer.fromAddress(1337);
|
|
IntUnOp f = (a) => a + 1;
|
|
p.value = f; //# 29: compile-time error
|
|
}
|
|
|
|
void testSetNativeType() {
|
|
// Is it possible to obtain a Pointer<NativeType> at all?
|
|
}
|
|
|
|
void testSetTypeMismatch() {
|
|
// the pointer to pointer types must match up
|
|
Pointer<Int8> pHelper = allocate();
|
|
pHelper.value = 123;
|
|
|
|
Pointer<Pointer<Int16>> p = allocate();
|
|
|
|
// this fails to compile due to type mismatch
|
|
p.value = pHelper; //# 40: compile-time error
|
|
|
|
free(pHelper);
|
|
free(p);
|
|
}
|
|
|
|
void testAsFunctionGeneric() {
|
|
T generic<T extends Function>(T defaultFunction) {
|
|
Pointer<NativeFunction<Int8UnOp>> p = Pointer.fromAddress(1337);
|
|
T f = defaultFunction;
|
|
f = p.asFunction<T>(); //# 11: compile-time error
|
|
return f;
|
|
}
|
|
|
|
generic<IntUnOp>((int a) => a + 1);
|
|
}
|
|
|
|
void testAsFunctionGeneric2() {
|
|
generic(Pointer<NativeFunction> p) {
|
|
Function f = () => "dummy";
|
|
f = p.asFunction<IntUnOp>(); //# 12: compile-time error
|
|
return f;
|
|
}
|
|
|
|
Pointer<NativeFunction<Int8UnOp>> p = Pointer.fromAddress(1337);
|
|
generic(p);
|
|
}
|
|
|
|
void testAsFunctionWrongNativeFunctionSignature() {
|
|
Pointer<NativeFunction<IntUnOp>> p;
|
|
Function f = p.asFunction<IntUnOp>(); //# 13: compile-time error
|
|
}
|
|
|
|
typedef IntBinOp = int Function(int, int);
|
|
|
|
void testAsFunctionTypeMismatch() {
|
|
Pointer<NativeFunction<Int8UnOp>> p = Pointer.fromAddress(1337);
|
|
IntBinOp f = p.asFunction(); //# 14: compile-time error
|
|
}
|
|
|
|
typedef NativeDoubleUnOp = Double Function(Double);
|
|
typedef DoubleUnOp = double Function(double);
|
|
|
|
double myTimesThree(double d) => d * 3;
|
|
|
|
int myTimesFour(int i) => i * 4;
|
|
|
|
void testFromFunctionGeneric() {
|
|
Pointer<NativeFunction> generic<T extends Function>(T f) {
|
|
Pointer<NativeFunction<NativeDoubleUnOp>> result = nullptr;
|
|
result = Pointer.fromFunction(f); //# 70: compile-time error
|
|
return result;
|
|
}
|
|
|
|
generic(myTimesThree);
|
|
}
|
|
|
|
void testFromFunctionGeneric2() {
|
|
Pointer<NativeFunction<T>> generic<T extends Function>() {
|
|
Pointer<NativeFunction<T>> result = nullptr;
|
|
result = Pointer.fromFunction(myTimesThree); //# 71: compile-time error
|
|
return result;
|
|
}
|
|
|
|
generic<NativeDoubleUnOp>();
|
|
}
|
|
|
|
void testFromFunctionWrongNativeFunctionSignature() {
|
|
Pointer.fromFunction<IntUnOp>(myTimesFour); //# 72: compile-time error
|
|
}
|
|
|
|
void testFromFunctionTypeMismatch() {
|
|
Pointer<NativeFunction<NativeDoubleUnOp>> p;
|
|
p = Pointer.fromFunction(myTimesFour); //# 73: compile-time error
|
|
}
|
|
|
|
void testFromFunctionClosure() {
|
|
DoubleUnOp someClosure = (double z) => z / 27.0;
|
|
Pointer<NativeFunction<NativeDoubleUnOp>> p;
|
|
p = Pointer.fromFunction(someClosure); //# 74: compile-time error
|
|
}
|
|
|
|
class X {
|
|
double tearoff(double d) => d / 27.0;
|
|
}
|
|
|
|
void testFromFunctionTearOff() {
|
|
DoubleUnOp fld = X().tearoff;
|
|
Pointer<NativeFunction<NativeDoubleUnOp>> p;
|
|
p = Pointer.fromFunction(fld); //# 75: compile-time error
|
|
}
|
|
|
|
void testFromFunctionAbstract() {
|
|
Pointer.fromFunction<Function>(//# 76: compile-time error
|
|
testFromFunctionAbstract); //# 76: compile-time error
|
|
}
|
|
|
|
void testLookupFunctionGeneric() {
|
|
Function generic<T extends Function>() {
|
|
DynamicLibrary l = dlopenPlatformSpecific("ffi_test_dynamic_library");
|
|
Function result = () => "dummy";
|
|
result = l.lookupFunction<T, DoubleUnOp>("cos"); //# 15: compile-time error
|
|
return result;
|
|
}
|
|
|
|
generic<NativeDoubleUnOp>();
|
|
}
|
|
|
|
void testLookupFunctionGeneric2() {
|
|
Function generic<T extends Function>() {
|
|
DynamicLibrary l = dlopenPlatformSpecific("ffi_test_dynamic_library");
|
|
Function result = () => "dummy";
|
|
result = //# 16: compile-time error
|
|
l.lookupFunction<NativeDoubleUnOp, T>("cos"); //# 16: compile-time error
|
|
return result;
|
|
}
|
|
|
|
generic<DoubleUnOp>();
|
|
}
|
|
|
|
void testLookupFunctionWrongNativeFunctionSignature() {
|
|
DynamicLibrary l = dlopenPlatformSpecific("ffi_test_dynamic_library");
|
|
l.lookupFunction<IntUnOp, IntUnOp>("cos"); //# 17: compile-time error
|
|
}
|
|
|
|
void testLookupFunctionTypeMismatch() {
|
|
DynamicLibrary l = dlopenPlatformSpecific("ffi_test_dynamic_library");
|
|
l.lookupFunction<NativeDoubleUnOp, IntUnOp>("cos"); //# 18: compile-time error
|
|
}
|
|
|
|
// TODO(dacoharkes): make the next 4 test compile errors
|
|
typedef Invalid1 = int Function(Int8);
|
|
typedef Invalid2 = Int8 Function(int);
|
|
typedef Invalid3 = Int8 Function({Int8 named});
|
|
typedef Invalid4 = Int8 Function([Int8 positional]);
|
|
|
|
void testNativeFunctionSignatureInvalidReturn() {
|
|
// Pointer<NativeFunction<Invalid1>> p = fromAddress(999);
|
|
}
|
|
|
|
void testNativeFunctionSignatureInvalidParam() {
|
|
// Pointer<NativeFunction<Invalid2>> p = fromAddress(999);
|
|
}
|
|
|
|
void testNativeFunctionSignatureInvalidOptionalNamed() {
|
|
// Pointer<NativeFunction<Invalid3>> p = fromAddress(999);
|
|
}
|
|
|
|
void testNativeFunctionSignatureInvalidOptionalPositional() {
|
|
// Pointer<NativeFunction<Invalid4>> p = fromAddress(999);
|
|
}
|
|
|
|
// error on missing field annotation
|
|
class TestStruct extends Struct {
|
|
@Double()
|
|
external double x;
|
|
|
|
external double y; //# 50: compile-time error
|
|
}
|
|
|
|
// Cannot extend structs.
|
|
class TestStruct3 extends TestStruct {} //# 52: compile-time error
|
|
|
|
// error on double annotation
|
|
class TestStruct4 extends Struct {
|
|
@Double()
|
|
@Double() //# 53: compile-time error
|
|
external double z;
|
|
}
|
|
|
|
// error on annotation not matching up
|
|
class TestStruct5 extends Struct {
|
|
@Int64() //# 54: compile-time error
|
|
external double z; //# 54: compile-time error
|
|
}
|
|
|
|
// error on annotation not matching up
|
|
class TestStruct6 extends Struct {
|
|
@Void() //# 55: compile-time error
|
|
external double z; //# 55: compile-time error
|
|
}
|
|
|
|
// error on annotation not matching up
|
|
class TestStruct7 extends Struct {
|
|
@NativeType() //# 56: compile-time error
|
|
external double z; //# 56: compile-time error
|
|
}
|
|
|
|
// error on field initializer on field
|
|
class TestStruct8 extends Struct {
|
|
@Double() //# 57: compile-time error
|
|
double z = 10.0; //# 57: compile-time error
|
|
}
|
|
|
|
// error on field initializer in constructor
|
|
class TestStruct9 extends Struct {
|
|
@Double() //# 58: compile-time error
|
|
double z; //# 58: compile-time error
|
|
|
|
TestStruct9() : z = 0.0 {} //# 58: compile-time error
|
|
}
|
|
|
|
// Struct classes may not be generic.
|
|
class TestStruct11<T> extends //# 60: compile-time error
|
|
Struct<TestStruct11<dynamic>> {} //# 60: compile-time error
|
|
|
|
// Structs may not appear inside structs (currently, there is no suitable
|
|
// annotation).
|
|
class TestStruct12 extends Struct {
|
|
@Pointer //# 61: compile-time error
|
|
external TestStruct9 struct; //# 61: compile-time error
|
|
}
|
|
|
|
class DummyAnnotation {
|
|
const DummyAnnotation();
|
|
}
|
|
|
|
// Structs fields may have other annotations.
|
|
class TestStruct13 extends Struct {
|
|
@DummyAnnotation()
|
|
@Double()
|
|
external double z;
|
|
}
|
|
|
|
// Cannot extend native types.
|
|
|
|
class ENativeType extends NativeType {} //# 90: compile-time error
|
|
|
|
class EInt8 extends Int8 {} //# 91: compile-time error
|
|
|
|
class EInt16 extends Int16 {} //# 92: compile-time error
|
|
|
|
class EInt32 extends Int32 {} //# 93: compile-time error
|
|
|
|
class EInt64 extends Int64 {} //# 94: compile-time error
|
|
|
|
class EUint8 extends Uint8 {} //# 95: compile-time error
|
|
|
|
class EUint16 extends Uint16 {} //# 96: compile-time error
|
|
|
|
class EUint32 extends Uint32 {} //# 97: compile-time error
|
|
|
|
class EUint64 extends Uint64 {} //# 98: compile-time error
|
|
|
|
class EIntPtr extends IntPtr {} //# 99: compile-time error
|
|
|
|
class EFloat extends Float {} //# 910: compile-time error
|
|
|
|
class EDouble extends Double {} //# 911: compile-time error
|
|
|
|
class EVoid extends Void {} //# 912: compile-time error
|
|
|
|
class ENativeFunction extends NativeFunction {} //# 913: compile-time error
|
|
|
|
class EPointer extends Pointer {} //# 914: compile-time error
|
|
|
|
// Cannot implement native natives or Struct.
|
|
|
|
// Cannot extend native types.
|
|
|
|
class INativeType implements NativeType {} //# 80: compile-time error
|
|
|
|
class IInt8 implements Int8 {} //# 81: compile-time error
|
|
|
|
class IInt16 implements Int16 {} //# 82: compile-time error
|
|
|
|
class IInt32 implements Int32 {} //# 83: compile-time error
|
|
|
|
class IInt64 implements Int64 {} //# 84: compile-time error
|
|
|
|
class IUint8 implements Uint8 {} //# 85: compile-time error
|
|
|
|
class IUint16 implements Uint16 {} //# 86: compile-time error
|
|
|
|
class IUint32 implements Uint32 {} //# 87: compile-time error
|
|
|
|
class IUint64 implements Uint64 {} //# 88: compile-time error
|
|
|
|
class IIntPtr implements IntPtr {} //# 88: compile-time error
|
|
|
|
class IFloat implements Float {} //# 810: compile-time error
|
|
|
|
class IDouble implements Double {} //# 811: compile-time error
|
|
|
|
class IVoid implements Void {} //# 812: compile-time error
|
|
|
|
class INativeFunction //# 813: compile-time error
|
|
implements //# 813: compile-time error
|
|
NativeFunction {} //# 813: compile-time error
|
|
|
|
class IPointer implements Pointer {} //# 814: compile-time error
|
|
|
|
class IStruct implements Struct {} //# 815: compile-time error
|
|
|
|
class MyClass {
|
|
int x;
|
|
MyClass(this.x);
|
|
}
|
|
|
|
final testLibrary = dlopenPlatformSpecific("ffi_test_functions");
|
|
|
|
void testHandleVariance() {
|
|
// Taking a more specific argument is okay.
|
|
testLibrary.lookupFunction<Handle Function(Handle), Object Function(MyClass)>(
|
|
"PassObjectToC");
|
|
|
|
// Requiring a more specific return type is not, this requires a cast from
|
|
// the user.
|
|
testLibrary.lookupFunction< //# 1000: compile-time error
|
|
Handle Function(Handle), //# 1000: compile-time error
|
|
MyClass Function(Object)>("PassObjectToC"); //# 1000: compile-time error
|
|
}
|
|
|
|
class TestStruct1001 extends Struct {
|
|
Handle handle; //# 1001: compile-time error
|
|
}
|
|
|
|
class TestStruct1002 extends Struct {
|
|
@Handle() //# 1002: compile-time error
|
|
Object handle; //# 1002: compile-time error
|
|
}
|