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'