[ffigen] Parameter names in function pointer fields (#537)
diff --git a/pkgs/ffigen/CHANGELOG.md b/pkgs/ffigen/CHANGELOG.md index 6dd3b69..2cad2a0 100644 --- a/pkgs/ffigen/CHANGELOG.md +++ b/pkgs/ffigen/CHANGELOG.md
@@ -1,3 +1,7 @@ +# 7.2.10 + +- Generate parameter names in function pointer fields + # 7.2.9 - Detect LLVM installed using Scoop on Windows machines.
diff --git a/pkgs/ffigen/example/c_json/cjson_generated_bindings.dart b/pkgs/ffigen/example/c_json/cjson_generated_bindings.dart index ad704b5..0571e62 100644 --- a/pkgs/ffigen/example/c_json/cjson_generated_bindings.dart +++ b/pkgs/ffigen/example/c_json/cjson_generated_bindings.dart
@@ -1257,13 +1257,12 @@ } class cJSON_Hooks extends ffi.Struct { - external ffi - .Pointer<ffi.NativeFunction<ffi.Pointer<ffi.Void> Function(ffi.Size)>> + external ffi.Pointer< + ffi.NativeFunction<ffi.Pointer<ffi.Void> Function(ffi.Size sz)>> malloc_fn; - external ffi - .Pointer<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Void>)>> - free_fn; + external ffi.Pointer< + ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Void> ptr)>> free_fn; } typedef cJSON_bool = ffi.Int;
diff --git a/pkgs/ffigen/example/libclang-example/generated_bindings.dart b/pkgs/ffigen/example/libclang-example/generated_bindings.dart index e41aaf9..800c580 100644 --- a/pkgs/ffigen/example/libclang-example/generated_bindings.dart +++ b/pkgs/ffigen/example/libclang-example/generated_bindings.dart
@@ -9748,7 +9748,9 @@ /// The visitor should return one of the \c CXChildVisitResult values /// to direct clang_visitCursorChildren(). typedef CXCursorVisitor = ffi.Pointer< - ffi.NativeFunction<ffi.Int32 Function(CXCursor, CXCursor, CXClientData)>>; + ffi.NativeFunction< + ffi.Int32 Function( + CXCursor cursor, CXCursor parent, CXClientData client_data)>>; /// Describes how the traversal of the children of a particular /// cursor should proceed after visiting a particular child cursor. @@ -10425,8 +10427,11 @@ /// the first element refers to the location that included 'included_file'. typedef CXInclusionVisitor = ffi.Pointer< ffi.NativeFunction< - ffi.Void Function(CXFile, ffi.Pointer<CXSourceLocation>, - ffi.UnsignedInt, CXClientData)>>; + ffi.Void Function( + CXFile included_file, + ffi.Pointer<CXSourceLocation> inclusion_stack, + ffi.UnsignedInt include_len, + CXClientData client_data)>>; typedef NativeClang_getInclusions = ffi.Void Function( CXTranslationUnit tu, CXInclusionVisitor visitor, CXClientData client_data); typedef DartClang_getInclusions = void Function( @@ -10943,8 +10948,10 @@ /// Called periodically to check whether indexing should be aborted. /// Should return 0 to continue, and non-zero to abort. external ffi.Pointer< - ffi.NativeFunction< - ffi.Int Function(CXClientData, ffi.Pointer<ffi.Void>)>> abortQuery; + ffi.NativeFunction< + ffi.Int Function( + CXClientData client_data, ffi.Pointer<ffi.Void> reserved)>> + abortQuery; /// Called at the end of indexing; passes the complete diagnostic set. external ffi.Pointer< @@ -10955,8 +10962,8 @@ external ffi.Pointer< ffi.NativeFunction< - CXIdxClientFile Function( - CXClientData, CXFile, ffi.Pointer<ffi.Void>)>> enteredMainFile; + CXIdxClientFile Function(CXClientData client_data, CXFile mainFile, + ffi.Pointer<ffi.Void> reserved)>> enteredMainFile; /// Called when a file gets \#included/\#imported. external ffi.Pointer< @@ -10979,9 +10986,10 @@ /// Called at the beginning of indexing a translation unit. external ffi.Pointer< - ffi.NativeFunction< - CXIdxClientContainer Function( - CXClientData, ffi.Pointer<ffi.Void>)>> startedTranslationUnit; + ffi.NativeFunction< + CXIdxClientContainer Function( + CXClientData client_data, ffi.Pointer<ffi.Void> reserved)>> + startedTranslationUnit; external ffi.Pointer< ffi.NativeFunction< @@ -11095,8 +11103,9 @@ /// /// The visitor should return one of the \c CXVisitorResult values /// to direct \c clang_Type_visitFields. -typedef CXFieldVisitor = ffi - .Pointer<ffi.NativeFunction<ffi.Int32 Function(CXCursor, CXClientData)>>; +typedef CXFieldVisitor = ffi.Pointer< + ffi.NativeFunction< + ffi.Int32 Function(CXCursor C, CXClientData client_data)>>; typedef NativeClang_Type_visitFields = ffi.UnsignedInt Function( CXType T, CXFieldVisitor visitor, CXClientData client_data); typedef DartClang_Type_visitFields = int Function(
diff --git a/pkgs/ffigen/lib/src/code_generator/func_type.dart b/pkgs/ffigen/lib/src/code_generator/func_type.dart index 3025bd2..5402028 100644 --- a/pkgs/ffigen/lib/src/code_generator/func_type.dart +++ b/pkgs/ffigen/lib/src/code_generator/func_type.dart
@@ -3,6 +3,7 @@ // BSD-style license that can be found in the LICENSE file. import 'package:ffigen/src/code_generator.dart'; +import 'package:ffigen/src/code_generator/utils.dart'; import 'writer.dart'; @@ -54,6 +55,21 @@ p.type.addDependencies(dependencies); } } + + void addParameterNames(List<String> names) { + if (names.length != parameters.length) { + return; + } + final paramNamer = UniqueNamer({}); + for (int i = 0; i < parameters.length; i++) { + final finalName = paramNamer.makeUnique(names[i]); + parameters[i] = Parameter( + type: parameters[i].type, + originalName: names[i], + name: finalName, + ); + } + } } /// Represents a NativeFunction<Function>.
diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/compounddecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/compounddecl_parser.dart index 5a7709c..a88f5fc 100644 --- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/compounddecl_parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/compounddecl_parser.dart
@@ -253,7 +253,7 @@ parsed.maxChildAlignment = align; } - final mt = cursor.type().toCodeGenType(); + final mt = cursor.toCodeGenType(); if (mt is IncompleteArray) { // TODO(68): Structs with flexible Array Members are not supported. parsed.flexibleArrayMember = true; @@ -271,7 +271,6 @@ if (mt.baseType is UnimplementedType) { parsed.unimplementedMemberType = true; } - parsed.compound.members.add( Member( dartDoc: getCursorDocComment( @@ -295,7 +294,7 @@ break; case clang_types.CXCursorKind.CXCursor_UnionDecl: case clang_types.CXCursorKind.CXCursor_StructDecl: - final mt = cursor.type().toCodeGenType(); + final mt = cursor.toCodeGenType(); // If the union/struct are anonymous, then we need to add them now, // otherwise they will be added in the next iteration.
diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/function_type_param_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/function_type_param_parser.dart new file mode 100644 index 0000000..739b3b1 --- /dev/null +++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/function_type_param_parser.dart
@@ -0,0 +1,54 @@ +// Copyright (c) 2021, 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. + +import 'dart:ffi'; + +import '../clang_bindings/clang_bindings.dart'; +import '../data.dart'; +import '../utils.dart'; + +/// This type holds the list of `ParmDecl` nodes of a function type declaration. +class FunctionTypeParams { + final List<String> paramNames; + final Map<String, CXCursor> params; + FunctionTypeParams() + : paramNames = [], + params = {}; +} + +FunctionTypeParams? _params; + +int _functionPointerFieldVisitor( + CXCursor cursor, CXCursor parent, Pointer<Void> clientData) { + if (cursor.kind == CXCursorKind.CXCursor_ParmDecl) { + final spelling = cursor.spelling(); + if (spelling.isNotEmpty) { + _params!.paramNames.add(spelling); + _params!.params[spelling] = cursor; + return CXChildVisitResult.CXChildVisit_Continue; + } else { + // A parameter's spelling is empty, do not continue further traversal. + _params!.paramNames.clear(); + _params!.params.clear(); + return CXChildVisitResult.CXChildVisit_Break; + } + } + // The cursor itself may be a pointer etc.. + return CXChildVisitResult.CXChildVisit_Recurse; +} + +/// Returns `ParmDecl` nodes of function pointer declaration +/// directly or indirectly pointed to by [cursor]. +FunctionTypeParams parseFunctionPointerParamNames(CXCursor cursor) { + _params = FunctionTypeParams(); + clang.clang_visitChildren( + cursor, + Pointer.fromFunction( + _functionPointerFieldVisitor, exceptional_visitor_return), + nullptr, + ); + final result = _params; + _params = null; + return result!; +}
diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/functiondecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/functiondecl_parser.dart index 848f60b..d2d3412 100644 --- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/functiondecl_parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/functiondecl_parser.dart
@@ -127,5 +127,5 @@ } Type _getParameterType(clang_types.CXCursor cursor) { - return cursor.type().toCodeGenType(); + return cursor.toCodeGenType(); }
diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/typedefdecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/typedefdecl_parser.dart index 988a45e..85df348 100644 --- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/typedefdecl_parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/typedefdecl_parser.dart
@@ -40,7 +40,8 @@ final typedefUsr = cursor.usr(); if (shouldIncludeTypealias(typedefUsr, typedefName)) { final ct = clang.clang_getTypedefDeclUnderlyingType(cursor); - final s = getCodeGenType(ct, pointerReference: pointerReference); + final s = getCodeGenType(ct, + pointerReference: pointerReference, originalCursor: cursor); if (bindingsIndex.isSeenUnsupportedTypealias(typedefUsr)) { // Do not process unsupported typealiases again.
diff --git a/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart b/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart index d6b87f2..c6a0951 100644 --- a/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart +++ b/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart
@@ -4,6 +4,7 @@ /// Extracts code_gen Type from type. import 'package:ffigen/src/code_generator.dart'; +import 'package:ffigen/src/header_parser/sub_parsers/function_type_param_parser.dart'; import 'package:ffigen/src/header_parser/sub_parsers/typedefdecl_parser.dart'; import 'package:ffigen/src/strings.dart' as strings; import 'package:logging/logging.dart'; @@ -21,6 +22,8 @@ final _logger = Logger('ffigen.header_parser.extractor'); const _padding = ' '; +const maxRecursionDepth = 5; + /// Converts cxtype to a typestring code_generator can accept. Type getCodeGenType( clang_types.CXType cxtype, { @@ -30,6 +33,10 @@ /// Passed on if a value was marked as a pointer before this one. bool pointerReference = false, + + /// Cursor of the declaration, currently this is useful only to extract + /// parameter names in function types. + clang_types.CXCursor? originalCursor, }) { _logger.fine('${_padding}getCodeGenType ${cxtype.completeStringRepr()}'); @@ -89,7 +96,11 @@ switch (cxtype.kind) { case clang_types.CXTypeKind.CXType_Pointer: final pt = clang.clang_getPointeeType(cxtype); - final s = getCodeGenType(pt, pointerReference: true); + final s = getCodeGenType( + pt, + pointerReference: true, + originalCursor: originalCursor, + ); // Replace Pointer<_Dart_Handle> with Handle. if (config.useDartHandle && @@ -101,10 +112,10 @@ return PointerType(s); case clang_types.CXTypeKind.CXType_FunctionProto: // Primarily used for function pointers. - return _extractFromFunctionProto(cxtype); + return _extractFromFunctionProto(cxtype, cursor: originalCursor); case clang_types.CXTypeKind.CXType_FunctionNoProto: // Primarily used for function types with zero arguments. - return _extractFromFunctionProto(cxtype); + return _extractFromFunctionProto(cxtype, cursor: originalCursor); case clang_types.CXTypeKind .CXType_ConstantArray: // Primarily used for constant array in struct members. return ConstantArray( @@ -295,7 +306,8 @@ } // Used for function pointer arguments. -Type _extractFromFunctionProto(clang_types.CXType cxtype) { +Type _extractFromFunctionProto(clang_types.CXType cxtype, + {clang_types.CXCursor? cursor}) { final parameters = <Parameter>[]; final totalArgs = clang.clang_getNumArgTypes(cxtype); for (var i = 0; i < totalArgs; i++) { @@ -314,8 +326,43 @@ ); } - return NativeFunc(FunctionType( + final functionType = FunctionType( parameters: parameters, returnType: clang.clang_getResultType(cxtype).toCodeGenType(), - )); + ); + _parseAndMergeParamNames(functionType, cursor, maxRecursionDepth); + return NativeFunc(functionType); +} + +void _parseAndMergeParamNames( + FunctionType functionType, + clang_types.CXCursor? cursor, + int recursionDepth, +) { + if (cursor == null) { + return; + } + if (recursionDepth == 0) { + final cursorRepr = cursor.completeStringRepr(); + _logger.warning('Recursion depth exceeded when merging function parameters.' + ' Last cursor encountered was $cursorRepr'); + return; + } + + final paramsInfo = parseFunctionPointerParamNames(cursor); + functionType.addParameterNames(paramsInfo.paramNames); + + for (final param in functionType.parameters) { + final paramRealType = param.type.typealiasType; + final paramBaseType = paramRealType.baseType.typealiasType; + if (paramBaseType is NativeFunc && param.name.isNotEmpty) { + final paramFunctionType = paramBaseType.type; + final paramCursor = paramsInfo.params[param.name]; + _parseAndMergeParamNames( + paramFunctionType, + paramCursor, + recursionDepth - 1, + ); + } + } }
diff --git a/pkgs/ffigen/lib/src/header_parser/utils.dart b/pkgs/ffigen/lib/src/header_parser/utils.dart index 0b4770f..a674618 100644 --- a/pkgs/ffigen/lib/src/header_parser/utils.dart +++ b/pkgs/ffigen/lib/src/header_parser/utils.dart
@@ -80,6 +80,11 @@ .toStringAndDispose(); } + /// Get code_gen [Type] representation of [clang_types.CXType]. + Type toCodeGenType() { + return getCodeGenType(type(), originalCursor: this); + } + /// for debug: returns [spelling] [kind] [kindSpelling] [type] [typeSpelling]. String completeStringRepr() { final cxtype = type();
diff --git a/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_functions_bindings.dart b/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_functions_bindings.dart index f9e8c1a..c4a01b3 100644 --- a/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_functions_bindings.dart +++ b/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_functions_bindings.dart
@@ -138,4 +138,4 @@ } typedef shortHand = ffi.NativeFunction< - ffi.Void Function(ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>; + ffi.Void Function(ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> b)>;
diff --git a/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_native_func_typedef_bindings.dart b/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_native_func_typedef_bindings.dart index 7153ce4..6ef30ff 100644 --- a/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_native_func_typedef_bindings.dart +++ b/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_native_func_typedef_bindings.dart
@@ -24,7 +24,8 @@ ffi.Pointer< ffi.NativeFunction< ffi.Void Function( - ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>> + ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> + unnamed2)>> unnamed1, ) { return _func( @@ -38,16 +39,15 @@ ffi.Pointer< ffi.NativeFunction< ffi.Void Function( - ffi.Pointer< - ffi.NativeFunction< - ffi.Void Function()>>)>>)>>('func'); + ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> + unnamed2)>>)>>('func'); late final _func = _funcPtr.asFunction< void Function( ffi.Pointer< ffi.NativeFunction< ffi.Void Function( - ffi.Pointer< - ffi.NativeFunction<ffi.Void Function()>>)>>)>(); + ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> + unnamed2)>>)>(); void funcWithNativeFunc( WithTypedefReturnType named, @@ -68,7 +68,8 @@ external ffi.Pointer< ffi.NativeFunction< ffi.Void Function( - ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>> unnamed1; + ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> + unnamed2)>> unnamed1; } typedef WithTypedefReturnType
diff --git a/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_struct_fptr_fields_bindings.dart b/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_struct_fptr_fields_bindings.dart new file mode 100644 index 0000000..b0490b2 --- /dev/null +++ b/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_struct_fptr_fields_bindings.dart
@@ -0,0 +1,72 @@ +// AUTO GENERATED FILE, DO NOT EDIT. +// +// Generated by `package:ffigen`. +// ignore_for_file: type=lint +import 'dart:ffi' as ffi; + +class S extends ffi.Struct { + external ffi.Pointer<ffi.NativeFunction<ffi.Int Function()>> func1; + + external ffi + .Pointer<ffi.NativeFunction<ffi.Int Function(ffi.Int a, ffi.Int b)>> + comparator; + + external ffi.Pointer< + ffi.NativeFunction< + ffi.Int Function(ffi.Double a, ffi.Float b, ffi.Pointer<ffi.Char> c, + ffi.Int d, ffi.LongLong e)>> veryManyArguments; + + external ffi.Pointer< + ffi.NativeFunction< + ffi.Int Function( + ffi.Int, ffi.Int, ffi.Int, ffi.Float, ffi.Pointer<ffi.Char>)>> + argsDontHaveNames; + + external ArithmeticOperation operation; + + external ffi.Pointer< + ffi.Pointer< + ffi.NativeFunction< + ffi.Void Function(ffi.Pointer<ffi.Int> array, ffi.Int len)>>> + sortPtr; + + external ffi.Pointer< + ffi.NativeFunction< + ffi.Void Function( + ffi.Pointer<ffi.Int> array, + ffi.Int len, + ffi.Pointer<ffi.NativeFunction<ffi.Int Function(ffi.Int x)>> + evaluator)>> sortBy; + + external ffi.Pointer< + ffi.NativeFunction<ffi.Void Function(ffi.Int, ffi.Int, ffi.Char)>> + improperlyDeclaredParams; + + external ffi.Pointer< + ffi.NativeFunction< + ffi.Void Function( + ffi.Pointer<ffi.Int> array, + ffi.Pointer<ffi.NativeFunction<ffi.Int Function(ffi.Int x)>> + primaryEvaluator, + ffi.Pointer<ffi.NativeFunction<ffi.Int Function(ffi.Int x)>> + fallbackEvaluator)>> sortByWithFallback; + + @ffi.Array.multi([2]) + external ffi.Array< + ffi.Pointer< + ffi.NativeFunction<ffi.Void Function(ffi.Char, ffi.Char)>>> + manyFunctions; + + external ffi.Pointer< + ffi.NativeFunction< + ffi.Pointer<ffi.NativeFunction<ffi.Int Function(ffi.Int, ffi.Int)>> + Function(ffi.Int, ffi.Int)>> functionReturningFunction; + + external ffi.Pointer< + ffi.NativeFunction< + ffi.Pointer<ffi.NativeFunction<ffi.Int Function(ffi.Int, ffi.Int)>> + Function(ffi.Int, ffi.Int)>> functionReturningFunctionImproper; +} + +typedef ArithmeticOperation + = ffi.Pointer<ffi.NativeFunction<ffi.Int Function(ffi.Int a, ffi.Int b)>>;
diff --git a/pkgs/ffigen/test/header_parser_tests/struct_fptr_fields.h b/pkgs/ffigen/test/header_parser_tests/struct_fptr_fields.h new file mode 100644 index 0000000..e95d1ca --- /dev/null +++ b/pkgs/ffigen/test/header_parser_tests/struct_fptr_fields.h
@@ -0,0 +1,41 @@ +// Copyright (c) 2021, 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. + +typedef int (*ArithmeticOperation)(int a, int b); + +struct S +{ + // Function pointer field, but no parameters. + int (*func1)(void); + // Function pointer field with parameters. + int (*comparator)(int a, int b); + // Function pointer field with lot of parameters + int (*veryManyArguments)(double a, float b, char *c, int d, long long e); + // Function pointer field with parameters, but no names + int (*argsDontHaveNames)(int, int, int, float, char *); + // Function pointer through typedef + ArithmeticOperation operation; + // Pointer to function pointer + void (**sortPtr)(int *array, int len); + // Function pointer with a function pointer parameter + void (*sortBy)(int *array, int len, int (*evaluator)(int x)); + // Function where few parameters are named. This should not + // produce parameters in output. + void (*improperlyDeclaredParams)(int a, int, char); + // Function pointer with 2 function pointer parameters + void (*sortByWithFallback)(int *array, + int (*primaryEvaluator)(int x), + int (*fallbackEvaluator)(int x)); + + // TODO(#545): Handle remaining cases of parsing param names + // --- + // Array of function pointers. Does not produce proper output right now. + void (*manyFunctions[2])(char a, char b); + // Function pointer returning function pointer. Does not produce valid output. + int (*(*functionReturningFunction)(int a, int b))(int c, int d); + // Function pointer returning function pointer. The return type has param + // names, but the function itself doesn't. This also shouldn't produce + // any parameters in output. + int (*(*functionReturningFunctionImproper)(int a, int b))(int, int); +};
diff --git a/pkgs/ffigen/test/header_parser_tests/struct_fptr_fields_test.dart b/pkgs/ffigen/test/header_parser_tests/struct_fptr_fields_test.dart new file mode 100644 index 0000000..765b8b0 --- /dev/null +++ b/pkgs/ffigen/test/header_parser_tests/struct_fptr_fields_test.dart
@@ -0,0 +1,42 @@ +// Copyright (c) 2021, 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. + +import 'package:ffigen/src/code_generator.dart'; +import 'package:ffigen/src/config_provider.dart'; +import 'package:ffigen/src/header_parser.dart' as parser; +import 'package:ffigen/src/strings.dart' as strings; +import 'package:logging/logging.dart'; +import 'package:test/test.dart'; +import 'package:yaml/yaml.dart' as yaml; + +import '../test_utils.dart'; + +late Library actual; +void main() { + group('Function pointer parameters parsing test', () { + setUpAll(() { + logWarnings(Level.SEVERE); + actual = parser.parse( + Config.fromYaml(yaml.loadYaml(''' +${strings.name}: 'NativeLibrary' +${strings.description}: 'Function pointer fields in structs Test' +${strings.output}: 'unused' +${strings.headers}: + ${strings.entryPoints}: + - 'test/header_parser_tests/struct_fptr_fields.h' + ''') as yaml.YamlMap), + ); + }); + + test('Expected bindings', () { + matchLibraryWithExpected( + actual, 'header_parser_struct_fptr_fields_output.dart', [ + 'test', + 'header_parser_tests', + 'expected_bindings', + '_expected_struct_fptr_fields_bindings.dart', + ]); + }); + }); +}
diff --git a/pkgs/ffigen/test/large_integration_tests/_expected_cjson_bindings.dart b/pkgs/ffigen/test/large_integration_tests/_expected_cjson_bindings.dart index 98db26e..fe6b008 100644 --- a/pkgs/ffigen/test/large_integration_tests/_expected_cjson_bindings.dart +++ b/pkgs/ffigen/test/large_integration_tests/_expected_cjson_bindings.dart
@@ -1237,13 +1237,12 @@ } class cJSON_Hooks extends ffi.Struct { - external ffi - .Pointer<ffi.NativeFunction<ffi.Pointer<ffi.Void> Function(ffi.Size)>> + external ffi.Pointer< + ffi.NativeFunction<ffi.Pointer<ffi.Void> Function(ffi.Size sz)>> malloc_fn; - external ffi - .Pointer<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Void>)>> - free_fn; + external ffi.Pointer< + ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Void> ptr)>> free_fn; } typedef cJSON_bool = ffi.Int;
diff --git a/pkgs/ffigen/test/large_integration_tests/_expected_libclang_bindings.dart b/pkgs/ffigen/test/large_integration_tests/_expected_libclang_bindings.dart index 47e997a..510e0b0 100644 --- a/pkgs/ffigen/test/large_integration_tests/_expected_libclang_bindings.dart +++ b/pkgs/ffigen/test/large_integration_tests/_expected_libclang_bindings.dart
@@ -7431,7 +7431,9 @@ /// Visitor invoked for each cursor found by a traversal. typedef CXCursorVisitor = ffi.Pointer< - ffi.NativeFunction<ffi.Int32 Function(CXCursor, CXCursor, CXClientData)>>; + ffi.NativeFunction< + ffi.Int32 Function( + CXCursor cursor, CXCursor parent, CXClientData client_data)>>; /// Opaque pointer representing client data that will be passed through to /// various callbacks and visitors. @@ -7761,8 +7763,11 @@ /// clang_getInclusions()). typedef CXInclusionVisitor = ffi.Pointer< ffi.NativeFunction< - ffi.Void Function(CXFile, ffi.Pointer<CXSourceLocation>, - ffi.UnsignedInt, CXClientData)>>; + ffi.Void Function( + CXFile included_file, + ffi.Pointer<CXSourceLocation> inclusion_stack, + ffi.UnsignedInt include_len, + CXClientData client_data)>>; abstract class CXEvalResultKind { static const int CXEval_Int = 1; @@ -8124,8 +8129,10 @@ /// Called periodically to check whether indexing should be aborted. Should /// return 0 to continue, and non-zero to abort. external ffi.Pointer< - ffi.NativeFunction< - ffi.Int Function(CXClientData, ffi.Pointer<ffi.Void>)>> abortQuery; + ffi.NativeFunction< + ffi.Int Function( + CXClientData client_data, ffi.Pointer<ffi.Void> reserved)>> + abortQuery; /// Called at the end of indexing; passes the complete diagnostic set. external ffi.Pointer< @@ -8136,8 +8143,8 @@ external ffi.Pointer< ffi.NativeFunction< - CXIdxClientFile Function( - CXClientData, CXFile, ffi.Pointer<ffi.Void>)>> enteredMainFile; + CXIdxClientFile Function(CXClientData client_data, CXFile mainFile, + ffi.Pointer<ffi.Void> reserved)>> enteredMainFile; /// Called when a file gets #included/#imported. external ffi.Pointer< @@ -8155,9 +8162,10 @@ /// Called at the beginning of indexing a translation unit. external ffi.Pointer< - ffi.NativeFunction< - CXIdxClientContainer Function( - CXClientData, ffi.Pointer<ffi.Void>)>> startedTranslationUnit; + ffi.NativeFunction< + CXIdxClientContainer Function( + CXClientData client_data, ffi.Pointer<ffi.Void> reserved)>> + startedTranslationUnit; external ffi.Pointer< ffi.NativeFunction< @@ -8216,8 +8224,9 @@ } /// Visitor invoked for each field found by a traversal. -typedef CXFieldVisitor = ffi - .Pointer<ffi.NativeFunction<ffi.Int32 Function(CXCursor, CXClientData)>>; +typedef CXFieldVisitor = ffi.Pointer< + ffi.NativeFunction< + ffi.Int32 Function(CXCursor C, CXClientData client_data)>>; const int CINDEX_VERSION_MAJOR = 0;
diff --git a/pkgs/ffigen/test/large_integration_tests/_expected_sqlite_bindings.dart b/pkgs/ffigen/test/large_integration_tests/_expected_sqlite_bindings.dart index 62480fb..d3b48db 100644 --- a/pkgs/ffigen/test/large_integration_tests/_expected_sqlite_bindings.dart +++ b/pkgs/ffigen/test/large_integration_tests/_expected_sqlite_bindings.dart
@@ -9585,7 +9585,8 @@ ffi.Pointer<sqlite3> pBlocked, ffi.Pointer< ffi.NativeFunction< - ffi.Void Function(ffi.Pointer<ffi.Pointer<ffi.Void>>, ffi.Int)>> + ffi.Void Function( + ffi.Pointer<ffi.Pointer<ffi.Void>> apArg, ffi.Int nArg)>> xNotify, ffi.Pointer<ffi.Void> pNotifyArg, ) { @@ -9603,7 +9604,8 @@ ffi.Pointer< ffi.NativeFunction< ffi.Void Function( - ffi.Pointer<ffi.Pointer<ffi.Void>>, ffi.Int)>>, + ffi.Pointer<ffi.Pointer<ffi.Void>> apArg, + ffi.Int nArg)>>, ffi.Pointer<ffi.Void>)>>('sqlite3_unlock_notify'); late final _sqlite3_unlock_notify = _sqlite3_unlock_notifyPtr.asFunction< int Function( @@ -9611,7 +9613,7 @@ ffi.Pointer< ffi.NativeFunction< ffi.Void Function( - ffi.Pointer<ffi.Pointer<ffi.Void>>, ffi.Int)>>, + ffi.Pointer<ffi.Pointer<ffi.Void>> apArg, ffi.Int nArg)>>, ffi.Pointer<ffi.Void>)>(); /// CAPI3REF: String Comparison @@ -10969,16 +10971,17 @@ xBestIndex; external ffi.Pointer< - ffi.NativeFunction<ffi.Int Function(ffi.Pointer<sqlite3_vtab>)>> + ffi.NativeFunction<ffi.Int Function(ffi.Pointer<sqlite3_vtab> pVTab)>> xDisconnect; external ffi.Pointer< - ffi.NativeFunction<ffi.Int Function(ffi.Pointer<sqlite3_vtab>)>> xDestroy; + ffi.NativeFunction<ffi.Int Function(ffi.Pointer<sqlite3_vtab> pVTab)>> + xDestroy; external ffi.Pointer< ffi.NativeFunction< - ffi.Int Function(ffi.Pointer<sqlite3_vtab>, - ffi.Pointer<ffi.Pointer<sqlite3_vtab_cursor>>)>> xOpen; + ffi.Int Function(ffi.Pointer<sqlite3_vtab> pVTab, + ffi.Pointer<ffi.Pointer<sqlite3_vtab_cursor>> ppCursor)>> xOpen; external ffi.Pointer< ffi.NativeFunction< @@ -11020,37 +11023,42 @@ ffi.Pointer<sqlite3_int64>)>> xUpdate; external ffi.Pointer< - ffi.NativeFunction<ffi.Int Function(ffi.Pointer<sqlite3_vtab>)>> xBegin; + ffi.NativeFunction<ffi.Int Function(ffi.Pointer<sqlite3_vtab> pVTab)>> + xBegin; external ffi.Pointer< - ffi.NativeFunction<ffi.Int Function(ffi.Pointer<sqlite3_vtab>)>> xSync; + ffi.NativeFunction<ffi.Int Function(ffi.Pointer<sqlite3_vtab> pVTab)>> + xSync; external ffi.Pointer< - ffi.NativeFunction<ffi.Int Function(ffi.Pointer<sqlite3_vtab>)>> xCommit; + ffi.NativeFunction<ffi.Int Function(ffi.Pointer<sqlite3_vtab> pVTab)>> + xCommit; external ffi.Pointer< - ffi.NativeFunction<ffi.Int Function(ffi.Pointer<sqlite3_vtab>)>> + ffi.NativeFunction<ffi.Int Function(ffi.Pointer<sqlite3_vtab> pVTab)>> xRollback; external ffi.Pointer< ffi.NativeFunction< ffi.Int Function( - ffi.Pointer<sqlite3_vtab>, - ffi.Int, - ffi.Pointer<ffi.Char>, + ffi.Pointer<sqlite3_vtab> pVtab, + ffi.Int nArg, + ffi.Pointer<ffi.Char> zName, ffi.Pointer< - ffi.Pointer< - ffi.NativeFunction< - ffi.Void Function( - ffi.Pointer<sqlite3_context>, - ffi.Int, - ffi.Pointer<ffi.Pointer<sqlite3_value>>)>>>, - ffi.Pointer<ffi.Pointer<ffi.Void>>)>> xFindFunction; + ffi.Pointer< + ffi.NativeFunction< + ffi.Void Function( + ffi.Pointer<sqlite3_context>, + ffi.Int, + ffi.Pointer<ffi.Pointer<sqlite3_value>>)>>> + pxFunc, + ffi.Pointer<ffi.Pointer<ffi.Void>> ppArg)>> xFindFunction; external ffi.Pointer< - ffi.NativeFunction< - ffi.Int Function( - ffi.Pointer<sqlite3_vtab>, ffi.Pointer<ffi.Char>)>> xRename; + ffi.NativeFunction< + ffi.Int Function( + ffi.Pointer<sqlite3_vtab> pVtab, ffi.Pointer<ffi.Char> zNew)>> + xRename; /// The methods above are in version 1 of the sqlite_module object. Those /// below are for version 2 and greater. @@ -11344,9 +11352,9 @@ xShutdown; external ffi.Pointer< - ffi.NativeFunction< - ffi.Pointer<sqlite3_pcache> Function(ffi.Int, ffi.Int, ffi.Int)>> - xCreate; + ffi.NativeFunction< + ffi.Pointer<sqlite3_pcache> Function( + ffi.Int szPage, ffi.Int szExtra, ffi.Int bPurgeable)>> xCreate; external ffi.Pointer< ffi.NativeFunction< @@ -11401,7 +11409,8 @@ external ffi.Pointer< ffi.NativeFunction< - ffi.Pointer<sqlite3_pcache> Function(ffi.Int, ffi.Int)>> xCreate; + ffi.Pointer<sqlite3_pcache> Function( + ffi.Int szPage, ffi.Int bPurgeable)>> xCreate; external ffi.Pointer< ffi.NativeFunction< @@ -11952,48 +11961,48 @@ /// Create a new tokenizer external ffi.Pointer< - ffi.NativeFunction< - ffi.Int Function( - ffi.Pointer<fts5_api>, - ffi.Pointer<ffi.Char>, - ffi.Pointer<ffi.Void>, - ffi.Pointer<fts5_tokenizer>, - ffi.Pointer< + ffi.NativeFunction< + ffi.Int Function( + ffi.Pointer<fts5_api> pApi, + ffi.Pointer<ffi.Char> zName, + ffi.Pointer<ffi.Void> pContext, + ffi.Pointer<fts5_tokenizer> pTokenizer, + ffi.Pointer< ffi.NativeFunction< - ffi.Void Function(ffi.Pointer<ffi.Void>)>>)>> - xCreateTokenizer; + ffi.Void Function(ffi.Pointer<ffi.Void>)>> + xDestroy)>> xCreateTokenizer; /// Find an existing tokenizer external ffi.Pointer< ffi.NativeFunction< ffi.Int Function( - ffi.Pointer<fts5_api>, - ffi.Pointer<ffi.Char>, - ffi.Pointer<ffi.Pointer<ffi.Void>>, - ffi.Pointer<fts5_tokenizer>)>> xFindTokenizer; + ffi.Pointer<fts5_api> pApi, + ffi.Pointer<ffi.Char> zName, + ffi.Pointer<ffi.Pointer<ffi.Void>> ppContext, + ffi.Pointer<fts5_tokenizer> pTokenizer)>> xFindTokenizer; /// Create a new auxiliary function external ffi.Pointer< - ffi.NativeFunction< - ffi.Int Function( - ffi.Pointer<fts5_api>, - ffi.Pointer<ffi.Char>, - ffi.Pointer<ffi.Void>, - fts5_extension_function, - ffi.Pointer< + ffi.NativeFunction< + ffi.Int Function( + ffi.Pointer<fts5_api> pApi, + ffi.Pointer<ffi.Char> zName, + ffi.Pointer<ffi.Void> pContext, + fts5_extension_function xFunction, + ffi.Pointer< ffi.NativeFunction< - ffi.Void Function(ffi.Pointer<ffi.Void>)>>)>> - xCreateFunction; + ffi.Void Function(ffi.Pointer<ffi.Void>)>> + xDestroy)>> xCreateFunction; } typedef fts5_extension_function = ffi.Pointer< ffi.NativeFunction< ffi.Void Function( - ffi.Pointer<Fts5ExtensionApi>, - ffi.Pointer<Fts5Context>, - ffi.Pointer<sqlite3_context>, - ffi.Int, - ffi.Pointer<ffi.Pointer<sqlite3_value>>)>>; + ffi.Pointer<Fts5ExtensionApi> pApi, + ffi.Pointer<Fts5Context> pFts, + ffi.Pointer<sqlite3_context> pCtx, + ffi.Int nVal, + ffi.Pointer<ffi.Pointer<sqlite3_value>> apVal)>>; const String SQLITE_VERSION = '3.32.3';