[ffigen] Added support for Dart_Handle to be generated as Handle (#121)
diff --git a/pkgs/ffigen/CHANGELOG.md b/pkgs/ffigen/CHANGELOG.md index 38c346e..a919a1a 100644 --- a/pkgs/ffigen/CHANGELOG.md +++ b/pkgs/ffigen/CHANGELOG.md
@@ -1,3 +1,6 @@ +# 1.2.0 +- Added support for `Dart_Handle` from `dart_api.h`. + # 1.1.0 - `typedef-map` can now be used to map a typedef name to a native type directly.
diff --git a/pkgs/ffigen/README.md b/pkgs/ffigen/README.md index 2bc7aa6..a48185c 100644 --- a/pkgs/ffigen/README.md +++ b/pkgs/ffigen/README.md
@@ -230,7 +230,19 @@ ``` </td> </tr> - <tr> + <tr> + <td>use-dart-handle</td> + <td>Should map `Dart_Handle` to `Handle`.<br> + <b>Default: true</b> + </td> + <td> + +```yaml +use-dart-handle: true +``` + </td> + </tr> + <tr> <td>preamble</td> <td>Raw header of the file, pasted as-it-is.</td> <td>
diff --git a/pkgs/ffigen/lib/src/code_generator/type.dart b/pkgs/ffigen/lib/src/code_generator/type.dart index 9ccc2b1..5922b38 100644 --- a/pkgs/ffigen/lib/src/code_generator/type.dart +++ b/pkgs/ffigen/lib/src/code_generator/type.dart
@@ -39,6 +39,9 @@ Struct, NativeFunction, + /// Represents a Dart_Handle. + Handle, + /// Stores its element type in NativeType as only those are supported. ConstantArray, IncompleteArray, @@ -130,6 +133,9 @@ return Type._( broadType: BroadType.Unimplemented, unimplementedReason: reason); } + factory Type.handle() { + return Type._(broadType: BroadType.Handle); + } /// Get base type for any type. /// @@ -176,6 +182,8 @@ return '${w.ffiLibraryPrefix}.Pointer<${child.getCType(w)}>'; case BroadType.Boolean: // Booleans are treated as uint8. return '${w.ffiLibraryPrefix}.${_primitives[SupportedNativeType.Uint8].c}'; + case BroadType.Handle: + return '${w.ffiLibraryPrefix}.Handle'; default: throw Exception('cType unknown'); } @@ -199,6 +207,8 @@ return '${w.ffiLibraryPrefix}.Pointer<${child.getCType(w)}>'; case BroadType.Boolean: // Booleans are treated as uint8. return _primitives[SupportedNativeType.Uint8].dart; + case BroadType.Handle: + return 'Object'; default: throw Exception('dart type unknown for ${broadType.toString()}'); }
diff --git a/pkgs/ffigen/lib/src/config_provider/config.dart b/pkgs/ffigen/lib/src/config_provider/config.dart index eae3edc..04ae2a9 100644 --- a/pkgs/ffigen/lib/src/config_provider/config.dart +++ b/pkgs/ffigen/lib/src/config_provider/config.dart
@@ -92,6 +92,10 @@ String _preamble; Config._(); + /// If `Dart_Handle` should be mapped with Handle/Object. + bool get useDartHandle => _useDartHandle; + bool _useDartHandle; + /// Create config from Yaml map. factory Config.fromYaml(YamlMap map) { final configspecs = Config._(); @@ -293,6 +297,13 @@ extractor: stringExtractor, extractedResult: (dynamic result) => _preamble = result as String, ), + strings.useDartHandle: Specification<bool>( + requirement: Requirement.no, + validator: booleanValidator, + extractor: booleanExtractor, + defaultValue: () => true, + extractedResult: (dynamic result) => _useDartHandle = result as bool, + ), }; } }
diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/structdecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/structdecl_parser.dart index 9b19d54..334349c 100644 --- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/structdecl_parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/structdecl_parser.dart
@@ -22,6 +22,7 @@ bool flexibleArrayMember = false; bool arrayMember = false; bool bitFieldMember = false; + bool dartHandleMember = false; _ParsedStruc(); } @@ -115,6 +116,12 @@ _logger.warning( 'Removed All Struct Members from ${_stack.top.struc.name}(${_stack.top.struc.originalName}), Bit Field members not supported.'); return _stack.top.struc.members.clear(); + } else if (_stack.top.dartHandleMember) { + _logger.fine( + '---- Removed Struct members, reason: Dart_Handle member. ${cursor.completeStringRepr()}'); + _logger.warning( + 'Removed All Struct Members from ${_stack.top.struc.name}(${_stack.top.struc.originalName}), Dart_Handle member not supported.'); + return _stack.top.struc.members.clear(); } } @@ -146,6 +153,8 @@ } else if (clang.clang_getFieldDeclBitWidth_wrap(cursor) != -1) { // TODO(84): Struct with bitfields are not suppoorted. _stack.top.bitFieldMember = true; + } else if (mt.broadType == BroadType.Handle) { + _stack.top.dartHandleMember = true; } if (mt.getBaseType().broadType == BroadType.Unimplemented) {
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 eedb119..4137e77 100644 --- a/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart +++ b/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart
@@ -6,6 +6,7 @@ import 'dart:ffi'; import 'package:ffigen/src/code_generator.dart'; +import 'package:ffigen/src/strings.dart' as strings; import 'package:logging/logging.dart'; import '../clang_bindings/clang_bindings.dart' as clang_types; @@ -28,6 +29,13 @@ final pt = clang.clang_getPointeeType_wrap(cxtype); final s = getCodeGenType(pt, parentName: parentName); pt.dispose(); + + // Replace Pointer<_Dart_Handle> with Handle. + if (config.useDartHandle && + s.broadType == BroadType.Struct && + s.struc.usr == strings.dartHandleUsr) { + return Type.handle(); + } return Type.pointer(s); case clang_types.CXTypeKind.CXType_Typedef: final spelling = cxtype.spelling(); @@ -116,8 +124,11 @@ final struc = parseStructDeclaration(cursor, name: structName, ignoreFilter: true); type = Type.struct(struc); - // Add to bindings. - addToBindings(struc); + + // Add to bindings if it's not Dart_Handle. + if (!(config.useDartHandle && structUsr == strings.dartHandleUsr)) { + addToBindings(struc); + } } cxtype.dispose();
diff --git a/pkgs/ffigen/lib/src/strings.dart b/pkgs/ffigen/lib/src/strings.dart index 60981bc..74dc0fa 100644 --- a/pkgs/ffigen/lib/src/strings.dart +++ b/pkgs/ffigen/lib/src/strings.dart
@@ -101,19 +101,20 @@ const warnWhenRemoving = 'warn-when-removing'; const arrayWorkaround = 'array-workaround'; const dartBool = 'dart-bool'; +const useDartHandle = 'use-dart-handle'; const comments = 'comments'; -// Sub-fields of comments +// Sub-fields of comments. const style = 'style'; const length = 'length'; -// Sub-fields of style +// Sub-fields of style. const doxygen = 'doxygen'; const any = 'any'; -// Sub-fields of length +// Sub-fields of length. const brief = 'brief'; const full = 'full'; -// Cmd line comment option +// Cmd line comment option. const fparseAllComments = '-fparse-all-comments'; // Library input. @@ -126,7 +127,10 @@ const libclang_dylib_macos = 'libwrapped_clang.dylib'; const libclang_dylib_windows = 'wrapped_clang.dll'; -// Writen doubles +// Writen doubles. const doubleInfinity = 'double.infinity'; const doubleNegativeInfinity = 'double.negativeInfinity'; const doubleNaN = 'double.nan'; + +/// USR for struct `_Dart_Handle`. +const dartHandleUsr = 'c:@S@_Dart_Handle';
diff --git a/pkgs/ffigen/pubspec.yaml b/pkgs/ffigen/pubspec.yaml index b0f6b4b..80feda7 100644 --- a/pkgs/ffigen/pubspec.yaml +++ b/pkgs/ffigen/pubspec.yaml
@@ -3,7 +3,7 @@ # BSD-style license that can be found in the LICENSE file. name: ffigen -version: 1.1.0 +version: 1.2.0 homepage: https://github.com/dart-lang/ffigen description: Experimental generator for FFI bindings, using LibClang to parse C header files.
diff --git a/pkgs/ffigen/test/header_parser_tests/dart_handle.h b/pkgs/ffigen/test/header_parser_tests/dart_handle.h new file mode 100644 index 0000000..72fa445 --- /dev/null +++ b/pkgs/ffigen/test/header_parser_tests/dart_handle.h
@@ -0,0 +1,25 @@ +// 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. + +#include <dart_api.h> + +void func1(Dart_Handle); +Dart_Handle func2(); +Dart_Handle **func3(Dart_Handle *); + +typedef void (*typedef1)(Dart_Handle); +void func4(typedef1); + +// Dart_Handle isn't supported directly, so all members are removed. +struct struc1 +{ + Dart_Handle h; + int a; +}; + +// Pointer<Handle> works. +struct struc2 +{ + Dart_Handle *h; +};
diff --git a/pkgs/ffigen/test/header_parser_tests/dart_handle_test.dart b/pkgs/ffigen/test/header_parser_tests/dart_handle_test.dart new file mode 100644 index 0000000..3fdc12c --- /dev/null +++ b/pkgs/ffigen/test/header_parser_tests/dart_handle_test.dart
@@ -0,0 +1,120 @@ +// 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. + +import 'package:cli_util/cli_util.dart'; +import 'package:path/path.dart' as path; +import 'package:ffigen/src/code_generator.dart'; +import 'package:ffigen/src/header_parser.dart' as parser; +import 'package:ffigen/src/config_provider.dart'; +import 'package:test/test.dart'; +import 'package:yaml/yaml.dart' as yaml; +import 'package:ffigen/src/strings.dart' as strings; + +import '../test_utils.dart'; + +Library actual, expected; + +void main() { + group('dart_handle_test', () { + setUpAll(() { + logWarnings(); + expected = expectedLibrary(); + actual = parser.parse( + Config.fromYaml(yaml.loadYaml(''' +${strings.name}: 'NativeLibrary' +${strings.description}: 'Dart_Handle Test' +${strings.output}: 'unused' +${strings.compilerOpts}: '-I${path.join(getSdkPath(), "include")} -I/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Kernel.framework/Headers/' + +${strings.headers}: + ${strings.entryPoints}: + - 'test/header_parser_tests/dart_handle.h' + ${strings.includeDirectives}: + - '**dart_handle.h' + ''') as yaml.YamlMap), + ); + }); + test('Total bindings count', () { + expect(actual.bindings.length, expected.bindings.length); + }); + + test('func1', () { + expect(actual.getBindingAsString('func1'), + expected.getBindingAsString('func1')); + }); + test('func2', () { + expect(actual.getBindingAsString('func2'), + expected.getBindingAsString('func2')); + }); + test('func3', () { + expect(actual.getBindingAsString('func3'), + expected.getBindingAsString('func3')); + }); + test('func4', () { + expect(actual.getBindingAsString('func4'), + expected.getBindingAsString('func4')); + }); + test('struc1', () { + expect(actual.getBindingAsString('struc1'), + expected.getBindingAsString('struc1')); + }); + test('struc2', () { + expect(actual.getBindingAsString('struc2'), + expected.getBindingAsString('struc2')); + }); + }); +} + +Library expectedLibrary() { + final namedTypedef = Typedef( + name: 'typedef1', + typedefType: TypedefType.C, + returnType: Type.nativeType(SupportedNativeType.Void), + parameters: [Parameter(type: Type.handle())], + ); + return Library( + name: 'NativeLibrary', + bindings: [ + Func( + name: 'func1', + returnType: Type.nativeType( + SupportedNativeType.Void, + ), + parameters: [ + Parameter(type: Type.handle()), + ], + ), + Func( + name: 'func2', + returnType: Type.handle(), + ), + Func( + name: 'func3', + returnType: Type.pointer(Type.pointer(Type.handle())), + parameters: [ + Parameter( + type: Type.pointer(Type.handle()), + ), + ], + ), + Func( + name: 'func4', + returnType: Type.nativeType(SupportedNativeType.Void), + parameters: [ + Parameter( + type: Type.pointer(Type.nativeFunc(namedTypedef)), + ), + ], + ), + // struc1 should have no members. + Struc(name: 'struc1'), + Struc( + name: 'struc2', + members: [ + Member(name: 'h', type: Type.pointer(Type.handle())), + ], + ), + ], + ); +}
diff --git a/pkgs/ffigen/test/native_test/config.yaml b/pkgs/ffigen/test/native_test/config.yaml index 55b0b0b..2b2ded2 100644 --- a/pkgs/ffigen/test/native_test/config.yaml +++ b/pkgs/ffigen/test/native_test/config.yaml
@@ -18,4 +18,4 @@ array-workaround: true # Needed for stdbool.h in MacOS -compiler-opts: '-I/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Kernel.framework/Headers/' +compiler-opts: '-I/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Kernel.framework/Headers/ -Wno-nullability-completeness'