Added workaround to map typedef names to a NativeType. (#119)

* Iterate over all typedefs rather than jumping to the base type.
* Added typedef-map to config.
* Updated version, changelog, and readme.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d24647f..38c346e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,6 @@
+# 1.1.0
+- `typedef-map` can now be used to map a typedef name to a native type directly.
+
 # 1.0.6
 - Fixed missing typedefs nested in another typedef's return types.
 
diff --git a/README.md b/README.md
index 4c5f335..2bc7aa6 100644
--- a/README.md
+++ b/README.md
@@ -244,6 +244,20 @@
 </td>
   </tr>
   <tr>
+    <td>typedef-map</td>
+    <td>Map typedefs to Native Types.<br> Values can only be
+    <i>Void, Uint8, Int8, Uint16, Int16, Uint32, Int32, Uint64, Int64, IntPtr, Float and Double.</i>
+    </td>
+    <td>
+
+```yaml
+typedef-map:
+  'my_custom_type': 'IntPtr'
+  'size_t': 'Int64'
+```
+  </td>
+  </tr>
+  <tr>
     <td>size-map</td>
     <td>Size of integers to use (in bytes).<br>
     <b>The defaults (see example) <i>may</i> not be portable on all OS.
diff --git a/lib/src/config_provider/config.dart b/lib/src/config_provider/config.dart
index ad8db21..eae3edc 100644
--- a/lib/src/config_provider/config.dart
+++ b/lib/src/config_provider/config.dart
@@ -59,6 +59,11 @@
   bool get useSupportedTypedefs => _useSupportedTypedefs;
   bool _useSupportedTypedefs;
 
+  /// Stores typedef name to NativeType mappings specified by user.
+  Map<String, SupportedNativeType> get typedefNativeTypeMappings =>
+      _typedefNativeTypeMappings;
+  Map<String, SupportedNativeType> _typedefNativeTypeMappings;
+
   /// Extracted Doc comment type.
   CommentType get commentType => _commentType;
   CommentType _commentType;
@@ -223,6 +228,13 @@
           }
         },
       ),
+      strings.typedefmap: Specification<Map<String, SupportedNativeType>>(
+        validator: typedefmapValidator,
+        extractor: typedefmapExtractor,
+        defaultValue: () => <String, SupportedNativeType>{},
+        extractedResult: (dynamic result) => _typedefNativeTypeMappings =
+            result as Map<String, SupportedNativeType>,
+      ),
       strings.sort: Specification<bool>(
         requirement: Requirement.no,
         validator: booleanValidator,
diff --git a/lib/src/config_provider/spec_utils.dart b/lib/src/config_provider/spec_utils.dart
index 1400c2f..3ed5767 100644
--- a/lib/src/config_provider/spec_utils.dart
+++ b/lib/src/config_provider/spec_utils.dart
@@ -71,6 +71,37 @@
   return true;
 }
 
+Map<String, SupportedNativeType> typedefmapExtractor(dynamic yamlConfig) {
+  final resultMap = <String, SupportedNativeType>{};
+  final typedefmap = yamlConfig as YamlMap;
+  if (typedefmap != null) {
+    for (final typeName in typedefmap.keys) {
+      if (typedefmap[typeName] is String &&
+          strings.supportedNativeType_mappings
+              .containsKey(typedefmap[typeName])) {
+        // Map this typename to specified supportedNativeType.
+        resultMap[typeName as String] =
+            strings.supportedNativeType_mappings[typedefmap[typeName]];
+      }
+    }
+  }
+  return resultMap;
+}
+
+bool typedefmapValidator(String name, dynamic yamlConfig) {
+  if (!checkType<YamlMap>([name], yamlConfig)) {
+    return false;
+  }
+  for (final value in (yamlConfig as YamlMap).values) {
+    if (value is! String ||
+        !strings.supportedNativeType_mappings.containsKey(value)) {
+      _logger.severe("Unknown value of subkey '$value' in '$name'.");
+    }
+  }
+
+  return true;
+}
+
 List<String> compilerOptsExtractor(dynamic value) =>
     (value as String)?.split(' ');
 
diff --git a/lib/src/header_parser/type_extractor/extractor.dart b/lib/src/header_parser/type_extractor/extractor.dart
index f2ec345..eedb119 100644
--- a/lib/src/header_parser/type_extractor/extractor.dart
+++ b/lib/src/header_parser/type_extractor/extractor.dart
@@ -30,9 +30,13 @@
       pt.dispose();
       return Type.pointer(s);
     case clang_types.CXTypeKind.CXType_Typedef:
-      // Get name from typedef name if config allows.
+      final spelling = cxtype.spelling();
+      if (config.typedefNativeTypeMappings.containsKey(spelling)) {
+        _logger.fine('  Type Mapped from typedef-map');
+        return Type.nativeType(config.typedefNativeTypeMappings[spelling]);
+      }
+      // Get name from supported typedef name if config allows.
       if (config.useSupportedTypedefs) {
-        final spelling = cxtype.spelling();
         if (suportedTypedefToSuportedNativeType.containsKey(spelling)) {
           _logger.fine('  Type Mapped from supported typedef');
           return Type.nativeType(suportedTypedefToSuportedNativeType[spelling]);
@@ -40,7 +44,8 @@
       }
 
       // This is important or we get stuck in infinite recursion.
-      final ct = clang.clang_getCanonicalType_wrap(cxtype);
+      final ct = clang.clang_getTypedefDeclUnderlyingType_wrap(
+          clang.clang_getTypeDeclaration_wrap(cxtype));
 
       final s = getCodeGenType(ct, parentName: parentName ?? cxtype.spelling());
       ct.dispose();
diff --git a/lib/src/strings.dart b/lib/src/strings.dart
index 5a81450..60981bc 100644
--- a/lib/src/strings.dart
+++ b/lib/src/strings.dart
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 import 'dart:io';
 
+import 'package:ffigen/src/code_generator/type.dart';
 import 'package:ffigen/src/find_resource.dart';
 import 'package:ffigen/src/header_parser/clang_bindings/clang_bindings.dart'
     as clang;
@@ -49,6 +50,7 @@
 const rename = 'rename';
 const memberRename = 'member-rename';
 const sizemap = 'size-map';
+const typedefmap = 'typedef-map';
 
 // Sizemap values.
 const SChar = 'char';
@@ -78,6 +80,21 @@
   Enum: clang.CXTypeKind.CXType_Enum
 };
 
+const supportedNativeType_mappings = <String, SupportedNativeType>{
+  'Void': SupportedNativeType.Void,
+  'Uint8': SupportedNativeType.Uint8,
+  'Uint16': SupportedNativeType.Uint16,
+  'Uint32': SupportedNativeType.Uint32,
+  'Uint64': SupportedNativeType.Uint64,
+  'Int8': SupportedNativeType.Int8,
+  'Int16': SupportedNativeType.Int16,
+  'Int32': SupportedNativeType.Int32,
+  'Int64': SupportedNativeType.Int64,
+  'IntPtr': SupportedNativeType.IntPtr,
+  'Float': SupportedNativeType.Float,
+  'Double': SupportedNativeType.Double,
+};
+
 // Boolean flags.
 const sort = 'sort';
 const useSupportedTypedefs = 'use-supported-typedefs';
diff --git a/pubspec.yaml b/pubspec.yaml
index 2d21dc5..b0f6b4b 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.0.6
+version: 1.1.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/native_func_typedef_test.dart b/test/header_parser_tests/native_func_typedef_test.dart
index 1676817..c1f2d37 100644
--- a/test/header_parser_tests/native_func_typedef_test.dart
+++ b/test/header_parser_tests/native_func_typedef_test.dart
@@ -98,10 +98,10 @@
   ffi.Pointer<ffi.NativeFunction<_typedefC_4>> unnamed1,
 );
 
-typedef _typedefC_5 = ffi.Void Function(
+typedef insideReturnType = ffi.Void Function(
 );
 
-typedef withTypedefReturnType = ffi.Pointer<ffi.NativeFunction<_typedefC_5>> Function(
+typedef withTypedefReturnType = ffi.Pointer<ffi.NativeFunction<insideReturnType>> Function(
 );
 
 typedef _c_funcWithNativeFunc = ffi.Void Function(
diff --git a/test/header_parser_tests/typedef.h b/test/header_parser_tests/typedef.h
index 9dda096..68ad426 100644
--- a/test/header_parser_tests/typedef.h
+++ b/test/header_parser_tests/typedef.h
@@ -48,3 +48,9 @@
 {
     b=0
 } NamedEnumInTypedef;
+
+// Should be treated as IntPtr when used.
+typedef char specified_type_as_IntPtr;
+typedef specified_type_as_IntPtr nesting_a_specified_type;
+
+void func3(specified_type_as_IntPtr, nesting_a_specified_type b);
diff --git a/test/header_parser_tests/typedef_test.dart b/test/header_parser_tests/typedef_test.dart
index 6b3b971..eeeb37c 100644
--- a/test/header_parser_tests/typedef_test.dart
+++ b/test/header_parser_tests/typedef_test.dart
@@ -31,6 +31,8 @@
   ${strings.exclude}:
     - ExcludedStruct
     - _ExcludedStruct
+${strings.typedefmap}:
+  'specified_type_as_IntPtr': 'IntPtr'
         ''') as yaml.YamlMap),
       );
     });
@@ -109,6 +111,17 @@
           EnumConstant(name: 'b', value: 0),
         ],
       ),
+      Func(
+        name: 'func3',
+        returnType: Type.nativeType(SupportedNativeType.Void),
+        parameters: [
+          Parameter(type: Type.nativeType(SupportedNativeType.IntPtr)),
+          Parameter(
+            type: Type.nativeType(SupportedNativeType.IntPtr),
+            name: 'b',
+          ),
+        ],
+      ),
     ],
   );
 }