`FfiNative` support (#447)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5c91860..0fe4436 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,8 @@
+# 6.1.1
+
+- _EXPERIMENTAL_ support for `FfiNative`. The API and output
+  might change at any point.
+
 # 6.1.0
 
 - Added `exclude-all-by-default` config flag, which changes the default behavior
diff --git a/README.md b/README.md
index 6442b42..96a88d0 100644
--- a/README.md
+++ b/README.md
@@ -489,6 +489,23 @@
       'dart-type': 'CustomType3'
 ```
   </td>
+  </tr>
+  <tr>
+    <td>ffi-native</td>
+    <td>
+      <b>WARNING:</b> FfiNative support is EXPERIMENTAL. The API may change
+      in a breaking way without notice.
+      <br><br>
+      Generate `@FfiNative` bindings instead of bindings using `DynamicLibrary` or `lookup`.
+    </td>
+    <td>
+
+```yaml
+ffi-native:
+  asset: 'myasset' # Optional.
+```
+  </td>
+  </tr>
   <tr>
     <td>language</td>
     <td>
diff --git a/example/ffinative/.gitignore b/example/ffinative/.gitignore
new file mode 100644
index 0000000..1b05164
--- /dev/null
+++ b/example/ffinative/.gitignore
@@ -0,0 +1,11 @@
+# Files and directories created by pub.
+.dart_tool/
+.packages
+# Remove the following pattern if you wish to check in your lock file.
+pubspec.lock
+
+# Conventional directory for build outputs.
+build/
+
+# Directory created by dartdoc.
+doc/api/
diff --git a/example/ffinative/README.md b/example/ffinative/README.md
new file mode 100644
index 0000000..c146940
--- /dev/null
+++ b/example/ffinative/README.md
@@ -0,0 +1,10 @@
+# FfiNatives example
+
+A simple example generating `FfiNative` bindings for a very small header file (`headers/example.h`).
+
+## Generating bindings
+At the root of this example (`example/simple`), run -
+```
+dart run ffigen
+```
+This will generate bindings in a file: [generated_bindings.dart](./generated_bindings.dart).
diff --git a/example/ffinative/generated_bindings.dart b/example/ffinative/generated_bindings.dart
new file mode 100644
index 0000000..794f28f
--- /dev/null
+++ b/example/ffinative/generated_bindings.dart
@@ -0,0 +1,41 @@
+// AUTO GENERATED FILE, DO NOT EDIT.
+//
+// Generated by `package:ffigen`.
+import 'dart:ffi' as ffi;
+
+/// Adds 2 integers.
+@ffi.FfiNative<ffi.Int Function(ffi.Int, ffi.Int)>('sum')
+external int sum(
+  int a,
+  int b,
+);
+
+/// Subtracts 2 integers.
+@ffi.FfiNative<ffi.Int Function(ffi.Pointer<ffi.Int>, ffi.Int)>('subtract')
+external int subtract(
+  ffi.Pointer<ffi.Int> a,
+  int b,
+);
+
+/// Multiplies 2 integers, returns pointer to an integer,.
+@ffi.FfiNative<ffi.Pointer<ffi.Int> Function(ffi.Int, ffi.Int)>('multiply')
+external ffi.Pointer<ffi.Int> multiply(
+  int a,
+  int b,
+);
+
+/// Divides 2 integers, returns pointer to a float.
+@ffi.FfiNative<ffi.Pointer<ffi.Float> Function(ffi.Int, ffi.Int)>('divide')
+external ffi.Pointer<ffi.Float> divide(
+  int a,
+  int b,
+);
+
+/// Divides 2 floats, returns a pointer to double.
+@ffi.FfiNative<
+    ffi.Pointer<ffi.Double> Function(
+        ffi.Pointer<ffi.Float>, ffi.Pointer<ffi.Float>)>('dividePrecision')
+external ffi.Pointer<ffi.Double> dividePrecision(
+  ffi.Pointer<ffi.Float> a,
+  ffi.Pointer<ffi.Float> b,
+);
diff --git a/example/ffinative/headers/example.h b/example/ffinative/headers/example.h
new file mode 100644
index 0000000..40c6b9e
--- /dev/null
+++ b/example/ffinative/headers/example.h
@@ -0,0 +1,18 @@
+// 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.
+
+/** Adds 2 integers. */
+int sum(int a, int b);
+
+/** Subtracts 2 integers. */
+int subtract(int *a, int b);
+
+/** Multiplies 2 integers, returns pointer to an integer,. */
+int *multiply(int a, int b);
+
+/** Divides 2 integers, returns pointer to a float. */
+float *divide(int a, int b);
+
+/** Divides 2 floats, returns a pointer to double. */
+double *dividePrecision(float *a, float *b);
diff --git a/example/ffinative/pubspec.yaml b/example/ffinative/pubspec.yaml
new file mode 100644
index 0000000..5ba0423
--- /dev/null
+++ b/example/ffinative/pubspec.yaml
@@ -0,0 +1,25 @@
+# Copyright (c) 2022, 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.
+
+name: ffinative_example
+
+environment:
+  sdk: '>=2.17.0 <3.0.0'
+
+dependencies:
+  ffi: ^2.0.1
+dev_dependencies:
+  ffigen:
+    path: '../../'
+  lints: ^2.0.0
+
+ffigen:
+  name: NativeLibrary
+  ffi-native:
+    # asset: 'assetname' # (optional)
+  description: Bindings to `headers/example.h`.
+  output: 'generated_bindings.dart'
+  headers:
+    entry-points:
+      - 'headers/example.h'
diff --git a/example/simple/generated_bindings.dart b/example/simple/generated_bindings.dart
index 1612b64..b4c885a 100644
--- a/example/simple/generated_bindings.dart
+++ b/example/simple/generated_bindings.dart
@@ -86,21 +86,21 @@
       _dividePtr.asFunction<ffi.Pointer<ffi.Float> Function(int, int)>();
 
   /// Divides 2 floats, returns a pointer to double.
-  ffi.Pointer<ffi.Double> dividePercision(
+  ffi.Pointer<ffi.Double> dividePrecision(
     ffi.Pointer<ffi.Float> a,
     ffi.Pointer<ffi.Float> b,
   ) {
-    return _dividePercision(
+    return _dividePrecision(
       a,
       b,
     );
   }
 
-  late final _dividePercisionPtr = _lookup<
+  late final _dividePrecisionPtr = _lookup<
       ffi.NativeFunction<
           ffi.Pointer<ffi.Double> Function(ffi.Pointer<ffi.Float>,
-              ffi.Pointer<ffi.Float>)>>('dividePercision');
-  late final _dividePercision = _dividePercisionPtr.asFunction<
+              ffi.Pointer<ffi.Float>)>>('dividePrecision');
+  late final _dividePrecision = _dividePrecisionPtr.asFunction<
       ffi.Pointer<ffi.Double> Function(
           ffi.Pointer<ffi.Float>, ffi.Pointer<ffi.Float>)>();
 }
diff --git a/example/simple/headers/example.h b/example/simple/headers/example.h
index 0f936ee..40c6b9e 100644
--- a/example/simple/headers/example.h
+++ b/example/simple/headers/example.h
@@ -15,4 +15,4 @@
 float *divide(int a, int b);
 
 /** Divides 2 floats, returns a pointer to double. */
-double *dividePercision(float *a, float *b);
+double *dividePrecision(float *a, float *b);
diff --git a/lib/src/code_generator/func.dart b/lib/src/code_generator/func.dart
index c10b0c8..4496e71 100644
--- a/lib/src/code_generator/func.dart
+++ b/lib/src/code_generator/func.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/config_provider/config_types.dart';
 
 import 'binding_string.dart';
 import 'utils.dart';
@@ -10,11 +11,14 @@
 
 /// A binding for C function.
 ///
-/// For a C function -
+/// For example, take the following C function.
+///
 /// ```c
 /// int sum(int a, int b);
 /// ```
-/// The Generated dart code is -
+///
+/// The generated Dart code for this function (without `FfiNative`) is as follows.
+///
 /// ```dart
 /// int sum(int a, int b) {
 ///   return _sum(a, b);
@@ -26,11 +30,19 @@
 ///
 /// typedef _dart_sum = int Function(int a, int b);
 /// ```
+///
+/// When using `FfiNative`, the code is as follows.
+///
+/// ```dart
+/// @ffi.FfiNative<ffi.Int32 Function(ffi.Int32 a, ffi.Int32 b)>('sum')
+/// external int sum(int a, int b);
+/// ```
 class Func extends LookUpBinding {
   final FunctionType functionType;
   final bool exposeSymbolAddress;
   final bool exposeFunctionTypedefs;
   final bool isLeaf;
+  final FfiNativeConfig ffiNativeConfig;
   late final String funcPointerName;
 
   /// Contains typealias for function type if [exposeFunctionTypedefs] is true.
@@ -50,6 +62,7 @@
     this.exposeFunctionTypedefs = false,
     this.isLeaf = false,
     bool isInternal = false,
+    this.ffiNativeConfig = const FfiNativeConfig(enabled: false),
   })  : functionType = FunctionType(
           returnType: returnType,
           parameters: parameters ?? const [],
@@ -97,20 +110,6 @@
     for (final p in functionType.parameters) {
       p.name = paramNamer.makeUnique(p.name);
     }
-    // Write enclosing function.
-    s.write('${functionType.returnType.getDartType(w)} $enclosingFuncName(\n');
-    for (final p in functionType.parameters) {
-      s.write('  ${p.type.getDartType(w)} ${p.name},\n');
-    }
-    s.write(') {\n');
-    s.write('return $funcVarName');
-
-    s.write('(\n');
-    for (final p in functionType.parameters) {
-      s.write('    ${p.name},\n');
-    }
-    s.write('  );\n');
-    s.write('}\n');
 
     final cType = exposeFunctionTypedefs
         ? _exposedCFunctionTypealias!.name
@@ -119,21 +118,53 @@
         ? _exposedDartFunctionTypealias!.name
         : functionType.getDartType(w, writeArgumentNames: false);
 
-    if (exposeSymbolAddress) {
-      // Add to SymbolAddress in writer.
-      w.symbolAddressWriter.addSymbol(
-        type:
-            '${w.ffiLibraryPrefix}.Pointer<${w.ffiLibraryPrefix}.NativeFunction<$cType>>',
-        name: name,
-        ptrName: funcPointerName,
-      );
+    if (ffiNativeConfig.enabled) {
+      final assetString = ffiNativeConfig.asset != null
+          ? ", asset: '${ffiNativeConfig.asset}'"
+          : '';
+      final isLeafString = isLeaf ? ', isLeaf: true' : '';
+      s.write(
+          "@${w.ffiLibraryPrefix}.FfiNative<$cType>('$originalName'$assetString$isLeafString)\n");
+
+      s.write(
+          'external ${functionType.returnType.getDartType(w)} $enclosingFuncName(\n');
+      for (final p in functionType.parameters) {
+        s.write('  ${p.type.getDartType(w)} ${p.name},\n');
+      }
+      s.write(');\n\n');
+    } else {
+      // Write enclosing function.
+      s.write(
+          '${functionType.returnType.getDartType(w)} $enclosingFuncName(\n');
+      for (final p in functionType.parameters) {
+        s.write('  ${p.type.getDartType(w)} ${p.name},\n');
+      }
+      s.write(') {\n');
+      s.write('return $funcVarName');
+
+      s.write('(\n');
+      for (final p in functionType.parameters) {
+        s.write('    ${p.name},\n');
+      }
+      s.write('  );\n');
+      s.write('}\n');
+
+      if (exposeSymbolAddress) {
+        // Add to SymbolAddress in writer.
+        w.symbolAddressWriter.addSymbol(
+          type:
+              '${w.ffiLibraryPrefix}.Pointer<${w.ffiLibraryPrefix}.NativeFunction<$cType>>',
+          name: name,
+          ptrName: funcPointerName,
+        );
+      }
+      // Write function pointer.
+      s.write(
+          "late final $funcPointerName = ${w.lookupFuncIdentifier}<${w.ffiLibraryPrefix}.NativeFunction<$cType>>('$originalName');\n");
+      final isLeafString = isLeaf ? 'isLeaf:true' : '';
+      s.write(
+          'late final $funcVarName = $funcPointerName.asFunction<$dartType>($isLeafString);\n\n');
     }
-    // Write function pointer.
-    s.write(
-        "late final $funcPointerName = ${w.lookupFuncIdentifier}<${w.ffiLibraryPrefix}.NativeFunction<$cType>>('$originalName');\n");
-    final isLeafString = isLeaf ? 'isLeaf:true' : '';
-    s.write(
-        'late final $funcVarName = $funcPointerName.asFunction<$dartType>($isLeafString);\n\n');
 
     return BindingString(type: BindingStringType.func, string: s.toString());
   }
diff --git a/lib/src/code_generator/library.dart b/lib/src/code_generator/library.dart
index a651871..7c7ce47 100644
--- a/lib/src/code_generator/library.dart
+++ b/lib/src/code_generator/library.dart
@@ -5,12 +5,12 @@
 import 'dart:io';
 
 import 'package:cli_util/cli_util.dart';
+import 'package:ffigen/src/code_generator.dart';
 import 'package:ffigen/src/config_provider/config_types.dart';
 import 'package:logging/logging.dart';
 import 'package:path/path.dart' as p;
-import 'binding.dart';
-import 'imports.dart';
-import 'struct.dart';
+
+import '../strings.dart' as strings;
 import 'utils.dart';
 import 'writer.dart';
 
@@ -46,11 +46,12 @@
       _sort();
     }
 
-    /// Handle any declaration-declaration name conflicts.
+    /// Handle any declaration-declaration name conflicts and emit warnings.
     final declConflictHandler = UniqueNamer({});
     for (final b in this.bindings) {
       _warnIfPrivateDeclaration(b);
       _resolveIfNameConflicts(declConflictHandler, b);
+      _warnIfExposeSymbolAddressAndFfiNative(b);
     }
 
     // Override pack values according to config. We do this after declaration
@@ -64,12 +65,23 @@
     }
 
     // Seperate bindings which require lookup.
-    final lookUpBindings = this.bindings.whereType<LookUpBinding>().toList();
+    final lookUpBindings = this.bindings.whereType<LookUpBinding>().where((e) {
+      if (e is Func) {
+        return !e.ffiNativeConfig.enabled;
+      }
+      return true;
+    }).toList();
+    final ffiNativeBindings = this
+        .bindings
+        .whereType<Func>()
+        .where((e) => e.ffiNativeConfig.enabled)
+        .toList();
     final noLookUpBindings =
         this.bindings.whereType<NoLookUpBinding>().toList();
 
     _writer = Writer(
       lookUpBindings: lookUpBindings,
+      ffiNativeBindings: ffiNativeBindings,
       noLookUpBindings: noLookUpBindings,
       className: name,
       classDocComment: description,
@@ -100,6 +112,16 @@
     }
   }
 
+  /// Logs a warning if generated declaration will be private.
+  void _warnIfExposeSymbolAddressAndFfiNative(Binding b) {
+    if (b is Func) {
+      if (b.exposeSymbolAddress && b.ffiNativeConfig.enabled) {
+        _logger.warning(
+            "Ignoring ${strings.symbolAddress} for '${b.name}' because it is generated as FfiNative.");
+      }
+    }
+  }
+
   /// Sort all bindings in alphabetical order.
   void _sort() {
     bindings.sort((b1, b2) => b1.name.compareTo(b2.name));
diff --git a/lib/src/code_generator/writer.dart b/lib/src/code_generator/writer.dart
index 77e9e83..1f054fc 100644
--- a/lib/src/code_generator/writer.dart
+++ b/lib/src/code_generator/writer.dart
@@ -2,11 +2,9 @@
 // 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/imports.dart';
+import 'package:ffigen/src/code_generator.dart';
 import 'package:ffigen/src/code_generator/utils.dart';
 
-import 'binding.dart';
-
 /// To store generated String bindings.
 class Writer {
   final String? header;
@@ -14,6 +12,9 @@
   /// Holds bindings, which lookup symbols.
   final List<Binding> lookUpBindings;
 
+  /// Holds bindings, which lookup symbols through `FfiNative`.
+  final List<Binding> ffiNativeBindings;
+
   /// Holds bindings which don't lookup symbols.
   final List<Binding> noLookUpBindings;
 
@@ -78,6 +79,7 @@
   /// already used. This is used to avoid name collisions.
   Writer({
     required this.lookUpBindings,
+    required this.ffiNativeBindings,
     required this.noLookUpBindings,
     required String className,
     Set<LibraryImport>? additionalImports,
@@ -233,6 +235,10 @@
       s.write('}\n\n');
     }
 
+    for (final b in ffiNativeBindings) {
+      s.write(b.toBindingString(this).string);
+    }
+
     if (symbolAddressWriter.shouldGenerate) {
       s.write(symbolAddressWriter.writeClass(this));
     }
diff --git a/lib/src/config_provider/config.dart b/lib/src/config_provider/config.dart
index 56b2df1..1fdb77e 100644
--- a/lib/src/config_provider/config.dart
+++ b/lib/src/config_provider/config.dart
@@ -151,6 +151,9 @@
   Includer get leafFunctions => _leafFunctions;
   late Includer _leafFunctions;
 
+  FfiNativeConfig get ffiNativeConfig => _ffiNativeConfig;
+  late FfiNativeConfig _ffiNativeConfig;
+
   Config._();
 
   /// Create config from Yaml map.
@@ -505,6 +508,14 @@
         extractedResult: (dynamic result) =>
             _leafFunctions = result as Includer,
       ),
+      [strings.ffiNative]: Specification<FfiNativeConfig>(
+        requirement: Requirement.no,
+        validator: ffiNativeValidator,
+        extractor: ffiNativeExtractor,
+        defaultValue: () => FfiNativeConfig(enabled: false),
+        extractedResult: (dynamic result) =>
+            _ffiNativeConfig = result as FfiNativeConfig,
+      )
     };
   }
 }
diff --git a/lib/src/config_provider/config_types.dart b/lib/src/config_provider/config_types.dart
index 8146917..e3965c0 100644
--- a/lib/src/config_provider/config_types.dart
+++ b/lib/src/config_provider/config_types.dart
@@ -402,3 +402,10 @@
     return className;
   }
 }
+
+class FfiNativeConfig {
+  final bool enabled;
+  final String? asset;
+
+  const FfiNativeConfig({required this.enabled, this.asset});
+}
diff --git a/lib/src/config_provider/spec_utils.dart b/lib/src/config_provider/spec_utils.dart
index ac154ce..149bd9f 100644
--- a/lib/src/config_provider/spec_utils.dart
+++ b/lib/src/config_provider/spec_utils.dart
@@ -49,7 +49,8 @@
       return false;
     }
   }
-  return last != null;
+  // The entry for the last key may be null.
+  return true;
 }
 
 /// Extracts value of nested [key] from [map].
@@ -918,3 +919,32 @@
 
   return _result;
 }
+
+FfiNativeConfig ffiNativeExtractor(dynamic yamlConfig) {
+  final yamlMap = yamlConfig as YamlMap?;
+  return FfiNativeConfig(
+    enabled: true,
+    asset: yamlMap?[strings.ffiNativeAsset] as String?,
+  );
+}
+
+bool ffiNativeValidator(List<String> name, dynamic yamlConfig) {
+  if (!checkType<YamlMap?>(name, yamlConfig)) {
+    return false;
+  }
+  // ignore: prefer_void_to_null
+  if (checkType<Null>(name, yamlConfig)) {
+    return true;
+  }
+  for (final key in (yamlConfig as YamlMap).keys) {
+    if (!checkType<String>([...name, key as String], yamlConfig[key])) {
+      return false;
+    }
+    if (key != strings.ffiNativeAsset) {
+      _logger.severe("'$name -> $key' must be one of the following - ${[
+        strings.ffiNativeAsset
+      ]}");
+    }
+  }
+  return true;
+}
diff --git a/lib/src/header_parser/sub_parsers/enumdecl_parser.dart b/lib/src/header_parser/sub_parsers/enumdecl_parser.dart
index 4e5e150..7eb65a8 100644
--- a/lib/src/header_parser/sub_parsers/enumdecl_parser.dart
+++ b/lib/src/header_parser/sub_parsers/enumdecl_parser.dart
@@ -10,7 +10,6 @@
 import 'package:logging/logging.dart';
 
 import '../clang_bindings/clang_bindings.dart' as clang_types;
-import '../data.dart';
 import '../includer.dart';
 import '../utils.dart';
 
diff --git a/lib/src/header_parser/sub_parsers/functiondecl_parser.dart b/lib/src/header_parser/sub_parsers/functiondecl_parser.dart
index b4c40fc..848f60b 100644
--- a/lib/src/header_parser/sub_parsers/functiondecl_parser.dart
+++ b/lib/src/header_parser/sub_parsers/functiondecl_parser.dart
@@ -80,6 +80,7 @@
       exposeFunctionTypedefs:
           config.exposeFunctionTypedefs.shouldInclude(funcName),
       isLeaf: config.leafFunctions.shouldInclude(funcName),
+      ffiNativeConfig: config.ffiNativeConfig,
     );
     bindingsIndex.addFuncToSeen(funcUsr, _stack.top.func!);
   } else if (bindingsIndex.isSeenFunc(funcUsr)) {
diff --git a/lib/src/header_parser/sub_parsers/macro_parser.dart b/lib/src/header_parser/sub_parsers/macro_parser.dart
index cdfe078..753a4b5 100644
--- a/lib/src/header_parser/sub_parsers/macro_parser.dart
+++ b/lib/src/header_parser/sub_parsers/macro_parser.dart
@@ -15,7 +15,6 @@
 import 'package:path/path.dart' as p;
 
 import '../clang_bindings/clang_bindings.dart' as clang_types;
-import '../data.dart';
 import '../utils.dart';
 
 final _logger = Logger('ffigen.header_parser.macro_parser');
diff --git a/lib/src/header_parser/sub_parsers/unnamed_enumdecl_parser.dart b/lib/src/header_parser/sub_parsers/unnamed_enumdecl_parser.dart
index 940369a..81c0a77 100644
--- a/lib/src/header_parser/sub_parsers/unnamed_enumdecl_parser.dart
+++ b/lib/src/header_parser/sub_parsers/unnamed_enumdecl_parser.dart
@@ -10,7 +10,6 @@
 import 'package:logging/logging.dart';
 
 import '../clang_bindings/clang_bindings.dart' as clang_types;
-import '../data.dart';
 import '../utils.dart';
 
 final _logger = Logger('ffigen.header_parser.unnamed_enumdecl_parser');
diff --git a/lib/src/header_parser/sub_parsers/var_parser.dart b/lib/src/header_parser/sub_parsers/var_parser.dart
index 8376199..a8d46d3 100644
--- a/lib/src/header_parser/sub_parsers/var_parser.dart
+++ b/lib/src/header_parser/sub_parsers/var_parser.dart
@@ -8,7 +8,6 @@
 import 'package:logging/logging.dart';
 
 import '../clang_bindings/clang_bindings.dart' as clang_types;
-import '../data.dart';
 import '../utils.dart';
 
 final _logger = Logger('ffigen.header_parser.var_parser');
@@ -34,6 +33,12 @@
     return null;
   }
 
+  if (config.ffiNativeConfig.enabled) {
+    _logger.warning(
+        "Skipped global variable '$name', not supported in FfiNatives.");
+    return null;
+  }
+
   final global = Global(
     originalName: name,
     name: config.globals.renameUsingConfig(name),
diff --git a/lib/src/strings.dart b/lib/src/strings.dart
index 6995ada..96e3c34 100644
--- a/lib/src/strings.dart
+++ b/lib/src/strings.dart
@@ -232,3 +232,6 @@
 
 /// USR for struct `_Dart_Handle`.
 const dartHandleUsr = 'c:@S@_Dart_Handle';
+
+const ffiNative = 'ffi-native';
+const ffiNativeAsset = 'asset';
diff --git a/pubspec.yaml b/pubspec.yaml
index d46f5fd..3de14a4 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: 6.1.0
+version: 6.1.1
 description: Generator for FFI bindings, using LibClang to parse C header files.
 repository: https://github.com/dart-lang/ffigen
 
diff --git a/test/example_tests/ffinative_example_test.dart b/test/example_tests/ffinative_example_test.dart
new file mode 100644
index 0000000..67962a0
--- /dev/null
+++ b/test/example_tests/ffinative_example_test.dart
@@ -0,0 +1,39 @@
+// Copyright (c) 2022, 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/config_provider/config.dart';
+import 'package:ffigen/src/header_parser.dart';
+import 'package:ffigen/src/strings.dart' as strings;
+import 'package:logging/logging.dart';
+import 'package:test/test.dart';
+import 'package:yaml/yaml.dart';
+
+import '../test_utils.dart';
+
+void main() {
+  group('ffinative_example_test', () {
+    setUpAll(() {
+      logWarnings(Level.SEVERE);
+    });
+
+    test('ffinative', () {
+      final config = Config.fromYaml(loadYaml('''
+${strings.name}: NativeLibrary
+${strings.ffiNative}:
+${strings.description}: Bindings to `headers/example.h`.
+${strings.output}: 'generated_bindings.dart'
+${strings.headers}:
+  ${strings.entryPoints}:
+    - 'example/ffinative/headers/example.h'
+''') as YamlMap);
+      final library = parse(config);
+
+      matchLibraryWithExpected(
+        library,
+        ['test', 'debug_generated', 'example_ffinative.dart'],
+        ['example', 'ffinative', config.output],
+      );
+    });
+  });
+}
diff --git a/test/large_integration_tests/_expected_cjson_bindings.dart b/test/large_integration_tests/_expected_cjson_bindings.dart
index ad8b2b5..2abffa6 100644
--- a/test/large_integration_tests/_expected_cjson_bindings.dart
+++ b/test/large_integration_tests/_expected_cjson_bindings.dart
@@ -4,7 +4,6 @@
 //
 // Generated by `package:ffigen`.
 import 'dart:ffi' as ffi;
-import 'package:ffi/ffi.dart' as pkg_ffi;
 
 /// Bindings to Cjson.
 class CJson {
@@ -1195,7 +1194,7 @@
   }
 
   late final _cJSON_mallocPtr =
-      _lookup<ffi.NativeFunction<ffi.Pointer<ffi.Void> Function(pkg_ffi.Size)>>(
+      _lookup<ffi.NativeFunction<ffi.Pointer<ffi.Void> Function(ffi.Size)>>(
           'cJSON_malloc');
   late final _cJSON_malloc =
       _cJSON_mallocPtr.asFunction<ffi.Pointer<ffi.Void> Function(int)>();
@@ -1237,8 +1236,8 @@
 }
 
 class cJSON_Hooks extends ffi.Struct {
-  external ffi.Pointer<
-          ffi.NativeFunction<ffi.Pointer<ffi.Void> Function(pkg_ffi.Size)>>
+  external ffi
+          .Pointer<ffi.NativeFunction<ffi.Pointer<ffi.Void> Function(ffi.Size)>>
       malloc_fn;
 
   external ffi
diff --git a/test/large_integration_tests/_expected_libclang_bindings.dart b/test/large_integration_tests/_expected_libclang_bindings.dart
index d060bd4..f5f332e 100644
--- a/test/large_integration_tests/_expected_libclang_bindings.dart
+++ b/test/large_integration_tests/_expected_libclang_bindings.dart
@@ -4,7 +4,6 @@
 //
 // Generated by `package:ffigen`.
 import 'dart:ffi' as ffi;
-import 'package:ffi/ffi.dart' as pkg_ffi;
 
 /// Bindings to LibClang.
 class LibClang {
@@ -482,7 +481,7 @@
   ffi.Pointer<ffi.Char> clang_getFileContents(
     CXTranslationUnit tu,
     CXFile file,
-    ffi.Pointer<pkg_ffi.Size> size,
+    ffi.Pointer<ffi.Size> size,
   ) {
     return _clang_getFileContents(
       tu,
@@ -494,10 +493,10 @@
   late final _clang_getFileContentsPtr = _lookup<
       ffi.NativeFunction<
           ffi.Pointer<ffi.Char> Function(CXTranslationUnit, CXFile,
-              ffi.Pointer<pkg_ffi.Size>)>>('clang_getFileContents');
+              ffi.Pointer<ffi.Size>)>>('clang_getFileContents');
   late final _clang_getFileContents = _clang_getFileContentsPtr.asFunction<
       ffi.Pointer<ffi.Char> Function(
-          CXTranslationUnit, CXFile, ffi.Pointer<pkg_ffi.Size>)>();
+          CXTranslationUnit, CXFile, ffi.Pointer<ffi.Size>)>();
 
   /// Returns non-zero if the file1 and file2 point to the same file, or they
   /// are both NULL.
diff --git a/test/large_integration_tests/large_test.dart b/test/large_integration_tests/large_test.dart
index 14c54bf..1638951 100644
--- a/test/large_integration_tests/large_test.dart
+++ b/test/large_integration_tests/large_test.dart
@@ -39,10 +39,6 @@
     - '**Index.h'
 ${strings.typeMap}:
   ${strings.typeMapTypedefs}:
-    'size_t':
-      lib: 'pkg_ffi'
-      c-type: 'Size'
-      dart-type: 'int'
     'time_t':
       lib: 'ffi'
       c-type: 'Int64'
@@ -71,12 +67,6 @@
     - third_party/cjson_library/cJSON.h
   ${strings.includeDirectives}:
     - '**cJSON.h'
-${strings.typeMap}:
-  ${strings.typeMapTypedefs}:
-    'size_t':
-      lib: 'pkg_ffi'
-      c-type: 'Size'
-      dart-type: 'int'
 ${strings.preamble}: |
   // ignore_for_file: camel_case_types, non_constant_identifier_names
       ''') as YamlMap);