// 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 function pointers with callbacks. // // VMOptions=--enable-testing-pragmas // SharedObjects=ffi_test_functions library FfiTest; import 'dart:io'; import 'dart:ffi'; import 'dart:isolate'; import 'dylib_utils.dart'; import "package:expect/expect.dart"; import 'ffi_test_helpers.dart'; typedef NativeCallbackTest = Int32 Function(Pointer); typedef NativeCallbackTestFn = int Function(Pointer); final DynamicLibrary testLibrary = dlopenPlatformSpecific("ffi_test_functions"); class Test { final String name; final Pointer callback; final bool skip; Test(this.name, this.callback, {bool skipIf: false}) : skip = skipIf {} void run() { if (skip) return; final NativeCallbackTestFn tester = testLibrary .lookupFunction("Test$name"); final int testCode = tester(callback); if (testCode != 0) { Expect.fail("Test $name failed."); } } } typedef SimpleAdditionType = Int32 Function(Int32, Int32); int simpleAddition(int x, int y) => x + y; typedef IntComputationType = Int64 Function(Int8, Int16, Int32, Int64); int intComputation(int a, int b, int c, int d) => d - c + b - a; typedef UintComputationType = Uint64 Function(Uint8, Uint16, Uint32, Uint64); int uintComputation(int a, int b, int c, int d) => d - c + b - a; typedef SimpleMultiplyType = Double Function(Double); double simpleMultiply(double x) => x * 1.337; typedef SimpleMultiplyFloatType = Float Function(Float); double simpleMultiplyFloat(double x) => x * 1.337; typedef ManyIntsType = IntPtr Function(IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr); int manyInts( int a, int b, int c, int d, int e, int f, int g, int h, int i, int j) { return a + b + c + d + e + f + g + h + i + j; } typedef ManyDoublesType = Double Function(Double, Double, Double, Double, Double, Double, Double, Double, Double, Double); double manyDoubles(double a, double b, double c, double d, double e, double f, double g, double h, double i, double j) { return a + b + c + d + e + f + g + h + i + j; } typedef ManyArgsType = Double Function( IntPtr, Float, IntPtr, Double, IntPtr, Float, IntPtr, Double, IntPtr, Float, IntPtr, Double, IntPtr, Float, IntPtr, Double, IntPtr, Float, IntPtr, Double); double manyArgs( int _1, double _2, int _3, double _4, int _5, double _6, int _7, double _8, int _9, double _10, int _11, double _12, int _13, double _14, int _15, double _16, int _17, double _18, int _19, double _20) { return _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13 + _14 + _15 + _16 + _17 + _18 + _19 + _20; } typedef StoreType = Pointer Function(Pointer); Pointer store(Pointer ptr) => ptr.elementAt(1)..store(1337); typedef NullPointersType = Pointer Function(Pointer); Pointer nullPointers(Pointer ptr) => ptr?.elementAt(1); typedef ReturnNullType = Int32 Function(); int returnNull() { print('Expect "unhandled exception" error message to follow.'); return null; } typedef ReturnVoid = Void Function(); void returnVoid() {} void testGC() { triggerGc(); } final List testcases = [ Test("SimpleAddition", fromFunction(simpleAddition)), Test("IntComputation", fromFunction(intComputation)), Test("UintComputation", fromFunction(uintComputation)), Test("SimpleMultiply", fromFunction(simpleMultiply)), Test("SimpleMultiplyFloat", fromFunction(simpleMultiplyFloat)), Test("ManyInts", fromFunction(manyInts)), Test("ManyDoubles", fromFunction(manyDoubles)), Test("ManyArgs", fromFunction(manyArgs)), Test("Store", fromFunction(store)), Test("NullPointers", fromFunction(nullPointers)), Test("ReturnNull", fromFunction(returnNull)), Test("GC", fromFunction(testGC)), ]; testCallbackWrongThread() => Test("CallbackWrongThread", fromFunction(returnVoid)).run(); testCallbackOutsideIsolate() => Test("CallbackOutsideIsolate", fromFunction(returnVoid)).run(); isolateHelper(int callbackPointer) { final Pointer ptr = fromAddress(callbackPointer); final NativeCallbackTestFn tester = testLibrary.lookupFunction( "TestCallbackWrongIsolate"); Expect.equals(0, tester(ptr)); } testCallbackWrongIsolate() async { final int callbackPointer = fromFunction(returnVoid).address; final ReceivePort exitPort = ReceivePort(); await Isolate.spawn(isolateHelper, callbackPointer, errorsAreFatal: true, onExit: exitPort.sendPort); await exitPort.first; } void main() async { testcases.forEach((t) => t.run()); //# 00: ok // These tests terminate the process after successful completion, so we have // to run them separately. // // Since they use signal handlers they only run on Linux. if (Platform.isLinux && !const bool.fromEnvironment("dart.vm.product")) { testCallbackWrongThread(); //# 01: ok testCallbackOutsideIsolate(); //# 02: ok await testCallbackWrongIsolate(); //# 03: ok } }