Added support for Dart_Handle to be generated as Handle (#121)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 38c346e..a919a1a 100644
--- a/CHANGELOG.md
+++ b/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/README.md b/README.md
index 2bc7aa6..a48185c 100644
--- a/README.md
+++ b/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/lib/src/code_generator/type.dart b/lib/src/code_generator/type.dart
index 9ccc2b1..5922b38 100644
--- a/lib/src/code_generator/type.dart
+++ b/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/lib/src/config_provider/config.dart b/lib/src/config_provider/config.dart
index eae3edc..04ae2a9 100644
--- a/lib/src/config_provider/config.dart
+++ b/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/lib/src/header_parser/sub_parsers/structdecl_parser.dart b/lib/src/header_parser/sub_parsers/structdecl_parser.dart
index 9b19d54..334349c 100644
--- a/lib/src/header_parser/sub_parsers/structdecl_parser.dart
+++ b/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/lib/src/header_parser/type_extractor/extractor.dart b/lib/src/header_parser/type_extractor/extractor.dart
index eedb119..4137e77 100644
--- a/lib/src/header_parser/type_extractor/extractor.dart
+++ b/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/lib/src/strings.dart b/lib/src/strings.dart
index 60981bc..74dc0fa 100644
--- a/lib/src/strings.dart
+++ b/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/pubspec.yaml b/pubspec.yaml
index b0f6b4b..80feda7 100644
--- a/pubspec.yaml
+++ b/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/test/header_parser_tests/dart_handle.h b/test/header_parser_tests/dart_handle.h
new file mode 100644
index 0000000..72fa445
--- /dev/null
+++ b/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/test/header_parser_tests/dart_handle_test.dart b/test/header_parser_tests/dart_handle_test.dart
new file mode 100644
index 0000000..3fdc12c
--- /dev/null
+++ b/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/test/native_test/config.yaml b/test/native_test/config.yaml
index 55b0b0b..2b2ded2 100644
--- a/test/native_test/config.yaml
+++ b/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'