[ffigen] Merge commit 'ac1b0498ecc81ef4f358c4fc93c770c7ff07d9a6' into stable
diff --git a/pkgs/ffigen/.github/dependabot.yml b/pkgs/ffigen/.github/dependabot.yml
index 1603cdd..725f03a 100644
--- a/pkgs/ffigen/.github/dependabot.yml
+++ b/pkgs/ffigen/.github/dependabot.yml
@@ -3,7 +3,9 @@
 version: 2
 
 updates:
-  - package-ecosystem: "github-actions"
-    directory: "/"
+  - package-ecosystem: github-actions
+    directory: /
     schedule:
-      interval: "monthly"
+      interval: monthly
+    labels:
+      - autosubmit
diff --git a/pkgs/ffigen/.github/workflows/test-package.yml b/pkgs/ffigen/.github/workflows/test-package.yml
index 8bcf6e6..9ee11ac 100644
--- a/pkgs/ffigen/.github/workflows/test-package.yml
+++ b/pkgs/ffigen/.github/workflows/test-package.yml
@@ -3,9 +3,9 @@
 on:
   # Run on PRs and pushes to the default branch.
   push:
-    branches: [ master, stable]
+    branches: [main, stable]
   pull_request:
-    branches: [ master, stable]
+    branches: [main, stable]
   schedule:
     - cron: "0 0 * * 0"
 
@@ -19,9 +19,9 @@
     strategy:
       fail-fast: false
       matrix:
-        sdk: [stable]
+        sdk: [3.0.0]
     steps:
-      - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3
+      - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9
       - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f
         with:
           sdk: ${{ matrix.sdk }}
@@ -41,10 +41,10 @@
     needs: analyze
     runs-on: ubuntu-22.04
     steps:
-      - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3
+      - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9
       - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f
         with:
-          sdk: stable
+          sdk: 3.0.0
       - name: Install dependencies
         run: dart pub get
       - name: Install libclang-14-dev
@@ -58,10 +58,10 @@
     needs: analyze
     runs-on: macos-latest
     steps:
-      - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3
+      - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9
       - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f
         with:
-          sdk: stable
+          sdk: 3.0.0
       - name: Install dependencies
         run: dart pub get
       - name: Build test dylib and bindings
@@ -71,7 +71,7 @@
       - name: Collect coverage
         run: ./tool/coverage.sh
       - name: Upload coverage
-        uses: coverallsapp/github-action@67662d24394fd74bffcf7b462d1b432814159afd
+        uses: coverallsapp/github-action@c7885c00cb7ec0b8f9f5ff3f53cddb980f7a4412
         with:
           github-token: ${{ secrets.GITHUB_TOKEN }}
           path-to-lcov: lcov.info
@@ -80,10 +80,10 @@
     needs: analyze
     runs-on: windows-latest
     steps:
-      - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3
+      - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9
       - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f
         with:
-          sdk: stable
+          sdk: 3.0.0
       - name: Install dependencies
         run: dart pub get
       - name: Build test dylib and bindings
@@ -105,10 +105,10 @@
     needs: analyze
     runs-on: windows-latest
     steps:
-      - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3
+      - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9
       - uses: subosito/flutter-action@48cafc24713cca54bbe03cdc3a423187d413aafa
         with:
-          channel: 'master'
+          channel: "master"
       - name: Install dependencies
         run: flutter pub get
       - name: Build test dylib and bindings
diff --git a/pkgs/ffigen/CHANGELOG.md b/pkgs/ffigen/CHANGELOG.md
index 45b5993..b4bdf28 100644
--- a/pkgs/ffigen/CHANGELOG.md
+++ b/pkgs/ffigen/CHANGELOG.md
@@ -1,3 +1,48 @@
+# 9.0.1
+
+- Fix doc comment missing on struct/union array fields.
+- Allow extern inline functions to be generated.
+
+# 9.0.0
+
+- Added a JSON schema for FFIgen config files.
+
+# 8.0.2
+
+- Fixed invalid code generated due to zero-length arrays in structs/union.
+
+# 8.0.1
+
+- Fixed invalid code generated due to anonymous structs/unions with unsupported types.
+
+# 8.0.0
+
+- Stable release for Dart 3.0 with support for class modifers, variadic arguments, and `@Native`s.
+
+# 8.0.0-dev.3
+
+- Added support for variadic functions using config `functions -> variadic-arguments`.
+
+# 8.0.0-dev.2
+
+- Use `@Native` syntax instead of deprecated `@FfiNative` syntax.
+
+
+# 8.0.0-dev.1
+
+- Fix invalid struct/enum member references due to multiple anonymous struct/enum in a declaration.
+
+# 8.0.0-dev.0
+
+- Adds `final` class modifier to generated sub types `Struct`, `Union` and
+  `Opaque`. A class modifier is required in Dart 3.0 because the classes
+  `dart:ffi` as marked `base`.
+  When migrating a package that uses FFIgen, _and_ exposes the generated code in
+  the public API of the package, to Dart 3.0, that package does not
+  need a major version bump. Sub typing `Struct`, `Union` and
+  `Opaque` sub types is already disallowed by `dart:ffi` pre 3.0, so adding the
+  `final` keyword is not a breaking change.
+- Bumps SDK lowerbound to 3.0.
 # 7.2.11
 
 - Fix invalid struct/enum member references due to multiple anonymous struct/enum in a declaration.
diff --git a/pkgs/ffigen/README.md b/pkgs/ffigen/README.md
index c4f5a02..38c66e5 100644
--- a/pkgs/ffigen/README.md
+++ b/pkgs/ffigen/README.md
@@ -1,6 +1,6 @@
 [![pub package](https://img.shields.io/pub/v/ffigen.svg)](https://pub.dev/packages/ffigen)
 [![Build Status](https://github.com/dart-lang/ffigen/workflows/Dart%20CI/badge.svg)](https://github.com/dart-lang/ffigen/actions?query=workflow%3A"Dart+CI")
-[![Coverage Status](https://coveralls.io/repos/github/dart-lang/ffigen/badge.svg?branch=master)](https://coveralls.io/github/dart-lang/ffigen?branch=master)
+[![Coverage Status](https://coveralls.io/repos/github/dart-lang/ffigen/badge.svg?branch=main)](https://coveralls.io/github/dart-lang/ffigen?branch=main)
 
 Binding generator for [FFI](https://dart.dev/guides/libraries/c-interop) bindings.
 
@@ -63,8 +63,12 @@
 ## Installing LLVM
 `package:ffigen` uses LLVM. Install LLVM (9+) in the following way.
 
-#### ubuntu/linux
-1. Install libclangdev - `sudo apt-get install libclang-dev`.
+#### Linux
+1. Install libclangdev.
+
+   With apt-get: `sudo apt-get install libclang-dev`.
+
+   With dnf: `sudo dnf install clang-devel`.
 
 #### Windows
 1. Install Visual Studio with C++ development support.
@@ -335,6 +339,26 @@
   </td>
   </tr>
   <tr>
+    <td>functions -> variadic-arguments</td>
+    <td>Generate multiple functions with different variadic arguments.<br>
+    <b>Default: var args for any function are ignored.</b>
+    </td>
+    <td>
+
+```yaml
+functions:
+  variadic-arguments:
+    myfunc:
+      // Native C types are supported
+      - [int, unsigned char, long*, float**]
+      // Common C typedefs (stddef.h) are supported too
+      - [uint8_t, intptr_t, size_t, wchar_t*]
+      // Structs/Unions/Typedefs from generated code or a library import can be referred too.
+      - [MyStruct*, my_custom_lib.CustomUnion]
+```
+  </td>
+  </tr>
+  <tr>
     <td>structs -> pack</td>
     <td>Override the @Packed(X) annotation for generated structs.<br><br>
     <i>Options - none, 1, 2, 4, 8, 16</i><br>
@@ -509,10 +533,10 @@
   <tr>
     <td>ffi-native</td>
     <td>
-      <b>WARNING:</b> FfiNative support is EXPERIMENTAL. The API may change
+      <b>WARNING:</b> Native 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`.
+      Generate `@Native` bindings instead of bindings using `DynamicLibrary` or `lookup`.
     </td>
     <td>
 
diff --git a/pkgs/ffigen/example/README.md b/pkgs/ffigen/example/README.md
index 91a8db8..cb1ce8c 100644
--- a/pkgs/ffigen/example/README.md
+++ b/pkgs/ffigen/example/README.md
@@ -1,7 +1,7 @@
 ## Examples
 
-- [Simple](https://github.com/dart-lang/ffigen/tree/master/example/simple)
-- [cJSON](https://github.com/dart-lang/ffigen/tree/master/example/c_json)
-- [LibClang](https://github.com/dart-lang/ffigen/tree/master/example/libclang-example)
-- [ObjectiveC](https://github.com/dart-lang/ffigen/tree/master/example/objective_c)
-- [Swift](https://github.com/dart-lang/ffigen/tree/master/example/swift)
+- [Simple](https://github.com/dart-lang/ffigen/tree/main/example/simple)
+- [cJSON](https://github.com/dart-lang/ffigen/tree/main/example/c_json)
+- [LibClang](https://github.com/dart-lang/ffigen/tree/main/example/libclang-example)
+- [ObjectiveC](https://github.com/dart-lang/ffigen/tree/main/example/objective_c)
+- [Swift](https://github.com/dart-lang/ffigen/tree/main/example/swift)
diff --git a/pkgs/ffigen/example/c_json/cjson_generated_bindings.dart b/pkgs/ffigen/example/c_json/cjson_generated_bindings.dart
index c520c20..0fb9e1a 100644
--- a/pkgs/ffigen/example/c_json/cjson_generated_bindings.dart
+++ b/pkgs/ffigen/example/c_json/cjson_generated_bindings.dart
@@ -1234,7 +1234,7 @@
       _cJSON_freePtr.asFunction<void Function(ffi.Pointer<ffi.Void>)>();
 }
 
-class cJSON extends ffi.Struct {
+final class cJSON extends ffi.Struct {
   external ffi.Pointer<cJSON> next;
 
   external ffi.Pointer<cJSON> prev;
@@ -1255,7 +1255,7 @@
   external ffi.Pointer<ffi.Char> string;
 }
 
-class cJSON_Hooks extends ffi.Struct {
+final class cJSON_Hooks extends ffi.Struct {
   external ffi.Pointer<
           ffi.NativeFunction<ffi.Pointer<ffi.Void> Function(ffi.Size sz)>>
       malloc_fn;
diff --git a/pkgs/ffigen/example/c_json/config.yaml b/pkgs/ffigen/example/c_json/config.yaml
index 08c5891..3d61cbd 100644
--- a/pkgs/ffigen/example/c_json/config.yaml
+++ b/pkgs/ffigen/example/c_json/config.yaml
@@ -1,3 +1,5 @@
+# yaml-language-server: $schema=../../ffigen.schema.json
+
 output: 'cjson_generated_bindings.dart'
 name: 'CJson'
 description: 'Holds bindings to cJSON.'
diff --git a/pkgs/ffigen/example/c_json/pubspec.yaml b/pkgs/ffigen/example/c_json/pubspec.yaml
index 1733b5f..3301013 100644
--- a/pkgs/ffigen/example/c_json/pubspec.yaml
+++ b/pkgs/ffigen/example/c_json/pubspec.yaml
@@ -5,7 +5,7 @@
 name: c_json_example
 
 environment:
-  sdk: '>=2.17.0 <4.0.0'
+  sdk: ">=3.0.0 <4.0.0"
 
 dependencies:
   ffi: ^2.0.1
@@ -13,5 +13,5 @@
 
 dev_dependencies:
   ffigen:
-    path: '../../'
+    path: "../../"
   lints: ^2.0.1
diff --git a/pkgs/ffigen/example/ffinative/README.md b/pkgs/ffigen/example/ffinative/README.md
index 82b0e88..7c5b169 100644
--- a/pkgs/ffigen/example/ffinative/README.md
+++ b/pkgs/ffigen/example/ffinative/README.md
@@ -1,6 +1,6 @@
-# FfiNatives example
+# Natives example
 
-A simple example generating `FfiNative` bindings for a very small header file (`headers/example.h`).
+A simple example generating `Native` bindings for a very small header file (`headers/example.h`).
 
 ## Generating bindings
 At the root of this example (`example/simple`), run -
diff --git a/pkgs/ffigen/example/ffinative/config.yaml b/pkgs/ffigen/example/ffinative/config.yaml
index 18aeb3f..397dbba 100644
--- a/pkgs/ffigen/example/ffinative/config.yaml
+++ b/pkgs/ffigen/example/ffinative/config.yaml
@@ -1,3 +1,5 @@
+# yaml-language-server: $schema=../../ffigen.schema.json
+
 name: NativeLibrary
 ffi-native:
   # asset: 'assetname' # (optional)
@@ -7,4 +9,4 @@
   entry-points:
     - 'headers/example.h'
 preamble: |
-  // ignore_for_file: deprecated_member_use
\ No newline at end of file
+  // ignore_for_file: deprecated_member_use
diff --git a/pkgs/ffigen/example/ffinative/generated_bindings.dart b/pkgs/ffigen/example/ffinative/generated_bindings.dart
index 1529564..389620d 100644
--- a/pkgs/ffigen/example/ffinative/generated_bindings.dart
+++ b/pkgs/ffigen/example/ffinative/generated_bindings.dart
@@ -7,36 +7,36 @@
 import 'dart:ffi' as ffi;
 
 /// Adds 2 integers.
-@ffi.FfiNative<ffi.Int Function(ffi.Int, ffi.Int)>('sum')
+@ffi.Native<ffi.Int Function(ffi.Int, ffi.Int)>(symbol: 'sum')
 external int sum(
   int a,
   int b,
 );
 
 /// Subtracts 2 integers.
-@ffi.FfiNative<ffi.Int Function(ffi.Int, ffi.Int)>('subtract')
+@ffi.Native<ffi.Int Function(ffi.Int, ffi.Int)>(symbol: 'subtract')
 external int subtract(
   int a,
   int b,
 );
 
 /// Multiplies 2 integers, returns pointer to an integer,.
-@ffi.FfiNative<ffi.Pointer<ffi.Int> Function(ffi.Int, ffi.Int)>('multiply')
+@ffi.Native<ffi.Pointer<ffi.Int> Function(ffi.Int, ffi.Int)>(symbol: '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')
+@ffi.Native<ffi.Pointer<ffi.Float> Function(ffi.Int, ffi.Int)>(symbol: '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.Float, ffi.Float)>(
-    'dividePrecision')
+@ffi.Native<ffi.Pointer<ffi.Double> Function(ffi.Float, ffi.Float)>(
+    symbol: 'dividePrecision')
 external ffi.Pointer<ffi.Double> dividePrecision(
   double a,
   double b,
diff --git a/pkgs/ffigen/example/ffinative/pubspec.yaml b/pkgs/ffigen/example/ffinative/pubspec.yaml
index 2247de4..ef452f8 100644
--- a/pkgs/ffigen/example/ffinative/pubspec.yaml
+++ b/pkgs/ffigen/example/ffinative/pubspec.yaml
@@ -5,11 +5,11 @@
 name: ffinative_example
 
 environment:
-  sdk: '>=2.17.0 <4.0.0'
+  sdk: ">=3.0.0 <4.0.0"
 
 dependencies:
   ffi: ^2.0.1
 dev_dependencies:
   ffigen:
-    path: '../../'
+    path: "../../"
   lints: ^2.0.0
diff --git a/pkgs/ffigen/example/libclang-example/config.yaml b/pkgs/ffigen/example/libclang-example/config.yaml
index f6517c0..69c111e 100644
--- a/pkgs/ffigen/example/libclang-example/config.yaml
+++ b/pkgs/ffigen/example/libclang-example/config.yaml
@@ -1,3 +1,5 @@
+# yaml-language-server: $schema=../../ffigen.schema.json
+
 output: 'generated_bindings.dart'
 
 # This will sort the bindings alphabetically.
diff --git a/pkgs/ffigen/example/libclang-example/custom_import.dart b/pkgs/ffigen/example/libclang-example/custom_import.dart
index df11af4..dd6196c 100644
--- a/pkgs/ffigen/example/libclang-example/custom_import.dart
+++ b/pkgs/ffigen/example/libclang-example/custom_import.dart
@@ -28,7 +28,7 @@
   Abi.windowsIA32: Uint32(),
   Abi.windowsX64: Uint64(),
 })
-class UintPtr extends AbiSpecificInteger {
+final class UintPtr extends AbiSpecificInteger {
   const UintPtr();
 }
 
@@ -56,8 +56,8 @@
   Abi.windowsIA32: Uint32(),
   Abi.windowsX64: Uint32(),
 })
-class UnsignedLong extends AbiSpecificInteger {
+final class UnsignedLong extends AbiSpecificInteger {
   const UnsignedLong();
 }
 
-class CXCursorSetImpl extends Opaque {}
+final class CXCursorSetImpl extends Opaque {}
diff --git a/pkgs/ffigen/example/libclang-example/generated_bindings.dart b/pkgs/ffigen/example/libclang-example/generated_bindings.dart
index 800c580..64de33c 100644
--- a/pkgs/ffigen/example/libclang-example/generated_bindings.dart
+++ b/pkgs/ffigen/example/libclang-example/generated_bindings.dart
@@ -7647,14 +7647,14 @@
 /// the ownership of that string might differ from one call to the next.
 /// Use \c clang_getCString() to retrieve the string data and, once finished
 /// with the string data, call \c clang_disposeString() to free the string.
-class CXString extends ffi.Struct {
+final class CXString extends ffi.Struct {
   external ffi.Pointer<ffi.Void> data;
 
   @ffi.UnsignedInt()
   external int private_flags;
 }
 
-class CXStringSet extends ffi.Struct {
+final class CXStringSet extends ffi.Struct {
   external ffi.Pointer<CXString> Strings;
 
   @ffi.UnsignedInt()
@@ -7671,16 +7671,16 @@
 typedef DartClang_disposeStringSet = void Function(
     ffi.Pointer<CXStringSet> set1);
 
-class CXTargetInfoImpl extends ffi.Opaque {}
+final class CXTargetInfoImpl extends ffi.Opaque {}
 
-class CXTranslationUnitImpl extends ffi.Opaque {}
+final class CXTranslationUnitImpl extends ffi.Opaque {}
 
 /// Provides the contents of a file that has not yet been saved to disk.
 ///
 /// Each CXUnsavedFile instance provides the name of a file on the
 /// system along with the current contents of that file that have not
 /// yet been saved to disk.
-class CXUnsavedFile extends ffi.Struct {
+final class CXUnsavedFile extends ffi.Struct {
   /// The file whose contents have not yet been saved.
   ///
   /// This file must already exist in the file system.
@@ -7695,7 +7695,7 @@
 }
 
 /// Describes a version number of the form major.minor.subminor.
-class CXVersion extends ffi.Struct {
+final class CXVersion extends ffi.Struct {
   /// The major version number, e.g., the '10' in '10.7.3'. A negative
   /// value indicates that there is no version number at all.
   @ffi.Int()
@@ -7768,7 +7768,7 @@
 
 /// Uniquely identifies a CXFile, that refers to the same underlying file,
 /// across an indexing session.
-class CXFileUniqueID extends ffi.Struct {
+final class CXFileUniqueID extends ffi.Struct {
   @ffi.Array.multi([3])
   external ffi.Array<ffi.UnsignedLongLong> data;
 }
@@ -7802,7 +7802,7 @@
 ///
 /// Use clang_getExpansionLocation() or clang_getSpellingLocation()
 /// to map a source location to a particular file, line, and column.
-class CXSourceLocation extends ffi.Struct {
+final class CXSourceLocation extends ffi.Struct {
   @ffi.Array.multi([2])
   external ffi.Array<ffi.Pointer<ffi.Void>> ptr_data;
 
@@ -7814,7 +7814,7 @@
 ///
 /// Use clang_getRangeStart() and clang_getRangeEnd() to retrieve the
 /// starting and end locations from a source range, respectively.
-class CXSourceRange extends ffi.Struct {
+final class CXSourceRange extends ffi.Struct {
   @ffi.Array.multi([2])
   external ffi.Array<ffi.Pointer<ffi.Void>> ptr_data;
 
@@ -7929,7 +7929,7 @@
 typedef DartClang_getRangeEnd = CXSourceLocation Function(CXSourceRange range);
 
 /// Identifies an array of ranges.
-class CXSourceRangeList extends ffi.Struct {
+final class CXSourceRangeList extends ffi.Struct {
   /// The number of ranges in the \c ranges array.
   @ffi.UnsignedInt()
   external int count;
@@ -8251,7 +8251,7 @@
 typedef DartClang_getTUResourceUsageName = ffi.Pointer<ffi.Char> Function(
     int kind);
 
-class CXTUResourceUsageEntry extends ffi.Struct {
+final class CXTUResourceUsageEntry extends ffi.Struct {
   @ffi.Int32()
   external int kind;
 
@@ -8260,7 +8260,7 @@
 }
 
 /// The memory usage of a CXTranslationUnit, broken into categories.
-class CXTUResourceUsage extends ffi.Struct {
+final class CXTUResourceUsage extends ffi.Struct {
   external ffi.Pointer<ffi.Void> data;
 
   @ffi.UnsignedInt()
@@ -8309,7 +8309,7 @@
 /// translation unit. clang_getCursor() maps from a physical source location
 /// to the entity that resides at that location, allowing one to map from the
 /// source code into the AST.
-class CXCursor extends ffi.Struct {
+final class CXCursor extends ffi.Struct {
   @ffi.Int32()
   external int kind;
 
@@ -9212,7 +9212,7 @@
 
 /// Describes the availability of a given entity on a particular platform, e.g.,
 /// a particular class might only be available on Mac OS 10.7 or newer.
-class CXPlatformAvailability extends ffi.Struct {
+final class CXPlatformAvailability extends ffi.Struct {
   /// A string that describes the platform for which this structure
   /// provides availability information.
   ///
@@ -9460,7 +9460,7 @@
 }
 
 /// The type of an element in the abstract syntax tree.
-class CXType extends ffi.Struct {
+final class CXType extends ffi.Struct {
   @ffi.Int32()
   external int kind;
 
@@ -10007,7 +10007,7 @@
     CXCursor C, int NameFlags, int PieceIndex);
 
 /// Describes a single preprocessing token.
-class CXToken extends ffi.Struct {
+final class CXToken extends ffi.Struct {
   @ffi.Array.multi([4])
   external ffi.Array<ffi.UnsignedInt> int_data;
 
@@ -10104,7 +10104,7 @@
     int stack_size);
 
 /// A single result of code completion.
-class CXCompletionResult extends ffi.Struct {
+final class CXCompletionResult extends ffi.Struct {
   /// The kind of entity that this completion refers to.
   ///
   /// The cursor kind will be a macro, keyword, or a declaration (one of the
@@ -10332,7 +10332,7 @@
 /// This data structure contains the results of code completion, as
 /// produced by \c clang_codeCompleteAt(). Its contents must be freed by
 /// \c clang_disposeCodeCompleteResults.
-class CXCodeCompleteResults extends ffi.Struct {
+final class CXCodeCompleteResults extends ffi.Struct {
   /// The code-completion results.
   external ffi.Pointer<CXCompletionResult> Results;
 
@@ -10501,7 +10501,7 @@
 typedef NativeClang_remap_dispose = ffi.Void Function(CXRemapping arg0);
 typedef DartClang_remap_dispose = void Function(CXRemapping arg0);
 
-class CXCursorAndRangeVisitor extends ffi.Struct {
+final class CXCursorAndRangeVisitor extends ffi.Struct {
   external ffi.Pointer<ffi.Void> context;
 
   external ffi.Pointer<
@@ -10540,7 +10540,7 @@
     CXTranslationUnit TU, CXFile file, CXCursorAndRangeVisitor visitor);
 
 /// Source location passed to index callbacks.
-class CXIdxLoc extends ffi.Struct {
+final class CXIdxLoc extends ffi.Struct {
   @ffi.Array.multi([2])
   external ffi.Array<ffi.Pointer<ffi.Void>> ptr_data;
 
@@ -10549,7 +10549,7 @@
 }
 
 /// Data for ppIncludedFile callback.
-class CXIdxIncludedFileInfo extends ffi.Struct {
+final class CXIdxIncludedFileInfo extends ffi.Struct {
   /// Location of '#' in the \#include/\#import directive.
   external CXIdxLoc hashLoc;
 
@@ -10572,7 +10572,7 @@
 }
 
 /// Data for IndexerCallbacks#importedASTFile.
-class CXIdxImportedASTFileInfo extends ffi.Struct {
+final class CXIdxImportedASTFileInfo extends ffi.Struct {
   /// Top level AST file containing the imported PCH, module or submodule.
   external CXFile file;
 
@@ -10588,7 +10588,7 @@
   external int isImplicit;
 }
 
-class CXIdxAttrInfo extends ffi.Struct {
+final class CXIdxAttrInfo extends ffi.Struct {
   @ffi.Int32()
   external int kind;
 
@@ -10604,7 +10604,7 @@
   static const int CXIdxAttr_IBOutletCollection = 3;
 }
 
-class CXIdxEntityInfo extends ffi.Struct {
+final class CXIdxEntityInfo extends ffi.Struct {
   @ffi.Int32()
   external int kind;
 
@@ -10679,11 +10679,11 @@
   static const int CXIdxEntityLang_Swift = 4;
 }
 
-class CXIdxContainerInfo extends ffi.Struct {
+final class CXIdxContainerInfo extends ffi.Struct {
   external CXCursor cursor;
 }
 
-class CXIdxIBOutletCollectionAttrInfo extends ffi.Struct {
+final class CXIdxIBOutletCollectionAttrInfo extends ffi.Struct {
   external ffi.Pointer<CXIdxAttrInfo> attrInfo;
 
   external ffi.Pointer<CXIdxEntityInfo> objcClass;
@@ -10693,7 +10693,7 @@
   external CXIdxLoc classLoc;
 }
 
-class CXIdxDeclInfo extends ffi.Struct {
+final class CXIdxDeclInfo extends ffi.Struct {
   external ffi.Pointer<CXIdxEntityInfo> entityInfo;
 
   external CXCursor cursor;
@@ -10731,7 +10731,7 @@
   external int flags;
 }
 
-class CXIdxObjCContainerDeclInfo extends ffi.Struct {
+final class CXIdxObjCContainerDeclInfo extends ffi.Struct {
   external ffi.Pointer<CXIdxDeclInfo> declInfo;
 
   @ffi.Int32()
@@ -10744,7 +10744,7 @@
   static const int CXIdxObjCContainer_Implementation = 2;
 }
 
-class CXIdxBaseClassInfo extends ffi.Struct {
+final class CXIdxBaseClassInfo extends ffi.Struct {
   external ffi.Pointer<CXIdxEntityInfo> base;
 
   external CXCursor cursor;
@@ -10752,7 +10752,7 @@
   external CXIdxLoc loc;
 }
 
-class CXIdxObjCProtocolRefInfo extends ffi.Struct {
+final class CXIdxObjCProtocolRefInfo extends ffi.Struct {
   external ffi.Pointer<CXIdxEntityInfo> protocol;
 
   external CXCursor cursor;
@@ -10760,14 +10760,14 @@
   external CXIdxLoc loc;
 }
 
-class CXIdxObjCProtocolRefListInfo extends ffi.Struct {
+final class CXIdxObjCProtocolRefListInfo extends ffi.Struct {
   external ffi.Pointer<ffi.Pointer<CXIdxObjCProtocolRefInfo>> protocols;
 
   @ffi.UnsignedInt()
   external int numProtocols;
 }
 
-class CXIdxObjCInterfaceDeclInfo extends ffi.Struct {
+final class CXIdxObjCInterfaceDeclInfo extends ffi.Struct {
   external ffi.Pointer<CXIdxObjCContainerDeclInfo> containerInfo;
 
   external ffi.Pointer<CXIdxBaseClassInfo> superInfo;
@@ -10775,7 +10775,7 @@
   external ffi.Pointer<CXIdxObjCProtocolRefListInfo> protocols;
 }
 
-class CXIdxObjCCategoryDeclInfo extends ffi.Struct {
+final class CXIdxObjCCategoryDeclInfo extends ffi.Struct {
   external ffi.Pointer<CXIdxObjCContainerDeclInfo> containerInfo;
 
   external ffi.Pointer<CXIdxEntityInfo> objcClass;
@@ -10787,7 +10787,7 @@
   external ffi.Pointer<CXIdxObjCProtocolRefListInfo> protocols;
 }
 
-class CXIdxObjCPropertyDeclInfo extends ffi.Struct {
+final class CXIdxObjCPropertyDeclInfo extends ffi.Struct {
   external ffi.Pointer<CXIdxDeclInfo> declInfo;
 
   external ffi.Pointer<CXIdxEntityInfo> getter;
@@ -10795,7 +10795,7 @@
   external ffi.Pointer<CXIdxEntityInfo> setter;
 }
 
-class CXIdxCXXClassDeclInfo extends ffi.Struct {
+final class CXIdxCXXClassDeclInfo extends ffi.Struct {
   external ffi.Pointer<CXIdxDeclInfo> declInfo;
 
   external ffi.Pointer<ffi.Pointer<CXIdxBaseClassInfo>> bases;
@@ -10805,7 +10805,7 @@
 }
 
 /// Data for IndexerCallbacks#indexEntityReference.
-class CXIdxEntityRefInfo extends ffi.Struct {
+final class CXIdxEntityRefInfo extends ffi.Struct {
   @ffi.Int32()
   external int kind;
 
@@ -10944,7 +10944,7 @@
 
 /// A group of callbacks used by #clang_indexSourceFile and
 /// #clang_indexTranslationUnit.
-class IndexerCallbacks extends ffi.Struct {
+final class IndexerCallbacks extends ffi.Struct {
   /// Called periodically to check whether indexing should be aborted.
   /// Should return 0 to continue, and non-zero to abort.
   external ffi.Pointer<
diff --git a/pkgs/ffigen/example/libclang-example/pubspec.yaml b/pkgs/ffigen/example/libclang-example/pubspec.yaml
index daef35b..9d7e8ab 100644
--- a/pkgs/ffigen/example/libclang-example/pubspec.yaml
+++ b/pkgs/ffigen/example/libclang-example/pubspec.yaml
@@ -5,11 +5,11 @@
 name: libclang_example
 
 environment:
-  sdk: '>=2.17.0 <4.0.0'
+  sdk: ">=3.0.0 <4.0.0"
 
 dependencies:
   ffi: ^2.0.1
 dev_dependencies:
   ffigen:
-    path: '../../'
+    path: "../../"
   lints: ^2.0.1
diff --git a/pkgs/ffigen/example/objective_c/avf_audio_bindings.dart b/pkgs/ffigen/example/objective_c/avf_audio_bindings.dart
index 972e8fe..ae0d3fd 100644
--- a/pkgs/ffigen/example/objective_c/avf_audio_bindings.dart
+++ b/pkgs/ffigen/example/objective_c/avf_audio_bindings.dart
@@ -30391,13 +30391,13 @@
   }
 }
 
-class ObjCSel extends ffi.Opaque {}
+final class ObjCSel extends ffi.Opaque {}
 
-class ObjCObject extends ffi.Opaque {}
+final class ObjCObject extends ffi.Opaque {}
 
 typedef instancetype = ffi.Pointer<ObjCObject>;
 
-class _NSZone extends ffi.Opaque {}
+final class _NSZone extends ffi.Opaque {}
 
 class Protocol extends _ObjCWrapper {
   Protocol._(ffi.Pointer<ObjCObject> id, AVFAudio lib,
@@ -33375,7 +33375,7 @@
 
 typedef NSRange = _NSRange;
 
-class _NSRange extends ffi.Struct {
+final class _NSRange extends ffi.Struct {
   @NSUInteger()
   external int location;
 
@@ -34616,7 +34616,7 @@
 
 typedef NSPoint = CGPoint;
 
-class CGPoint extends ffi.Struct {
+final class CGPoint extends ffi.Struct {
   @CGFloat()
   external double x;
 
@@ -34627,7 +34627,7 @@
 typedef CGFloat = ffi.Double;
 typedef NSSize = CGSize;
 
-class CGSize extends ffi.Struct {
+final class CGSize extends ffi.Struct {
   @CGFloat()
   external double width;
 
@@ -34637,13 +34637,13 @@
 
 typedef NSRect = CGRect;
 
-class CGRect extends ffi.Struct {
+final class CGRect extends ffi.Struct {
   external CGPoint origin;
 
   external CGSize size;
 }
 
-class NSEdgeInsets extends ffi.Struct {
+final class NSEdgeInsets extends ffi.Struct {
   @CGFloat()
   external double top;
 
@@ -35786,7 +35786,7 @@
   ffi.Pointer<_ObjCBlock> get pointer => _id;
 }
 
-class _ObjCBlockDesc extends ffi.Struct {
+final class _ObjCBlockDesc extends ffi.Struct {
   @ffi.UnsignedLong()
   external int reserved;
 
@@ -35800,7 +35800,7 @@
   external ffi.Pointer<ffi.Char> signature;
 }
 
-class _ObjCBlock extends ffi.Struct {
+final class _ObjCBlock extends ffi.Struct {
   external ffi.Pointer<ffi.Void> isa;
 
   @ffi.Int()
@@ -36434,7 +36434,7 @@
   }
 }
 
-class __va_list_tag extends ffi.Struct {
+final class __va_list_tag extends ffi.Struct {
   @ffi.UnsignedInt()
   external int gp_offset;
 
@@ -37121,7 +37121,7 @@
   ffi.Pointer<_ObjCBlock> get pointer => _id;
 }
 
-class NSFastEnumerationState extends ffi.Struct {
+final class NSFastEnumerationState extends ffi.Struct {
   @ffi.UnsignedLong()
   external int state;
 
@@ -44040,7 +44040,7 @@
 typedef NSRunLoopMode = ffi.Pointer<ObjCObject>;
 typedef CFRunLoopRef = ffi.Pointer<__CFRunLoop>;
 
-class __CFRunLoop extends ffi.Opaque {}
+final class __CFRunLoop extends ffi.Opaque {}
 
 class NSTimer extends NSObject {
   NSTimer._(ffi.Pointer<ObjCObject> id, AVFAudio lib,
@@ -45951,7 +45951,7 @@
 typedef __int32_t = ffi.Int;
 
 @ffi.Packed(2)
-class AEDesc extends ffi.Struct {
+final class AEDesc extends ffi.Struct {
   @DescType()
   external int descriptorType;
 
@@ -45961,7 +45961,7 @@
 typedef AEDataStorage = ffi.Pointer<AEDataStorageType>;
 typedef AEDataStorageType = ffi.Pointer<OpaqueAEDataStorageType>;
 
-class OpaqueAEDataStorageType extends ffi.Opaque {}
+final class OpaqueAEDataStorageType extends ffi.Opaque {}
 
 typedef AEKeyword = FourCharCode;
 
@@ -53059,7 +53059,7 @@
   }
 }
 
-class NSDecimal extends ffi.Opaque {}
+final class NSDecimal extends ffi.Opaque {}
 
 class NSException extends NSObject {
   NSException._(ffi.Pointer<ObjCObject> id, AVFAudio lib,
@@ -57469,7 +57469,7 @@
   }
 }
 
-class NSOperatingSystemVersion extends ffi.Struct {
+final class NSOperatingSystemVersion extends ffi.Struct {
   @NSInteger()
   external int majorVersion;
 
@@ -59371,11 +59371,11 @@
 
 typedef SecIdentityRef = ffi.Pointer<__SecIdentity>;
 
-class __SecIdentity extends ffi.Opaque {}
+final class __SecIdentity extends ffi.Opaque {}
 
 typedef SecTrustRef = ffi.Pointer<__SecTrust>;
 
-class __SecTrust extends ffi.Opaque {}
+final class __SecTrust extends ffi.Opaque {}
 
 class NSURLProtectionSpace extends NSObject {
   NSURLProtectionSpace._(ffi.Pointer<ObjCObject> id, AVFAudio lib,
@@ -67493,7 +67493,7 @@
   }
 }
 
-class AudioStreamBasicDescription extends ffi.Struct {
+final class AudioStreamBasicDescription extends ffi.Struct {
   @Float64()
   external double mSampleRate;
 
@@ -67680,7 +67680,7 @@
 
 typedef AudioChannelLayoutTag = UInt32;
 
-class AudioChannelLayout extends ffi.Struct {
+final class AudioChannelLayout extends ffi.Struct {
   @AudioChannelLayoutTag()
   external int mChannelLayoutTag;
 
@@ -67724,7 +67724,7 @@
   static const int kAudioChannelBit_RightTopRear = 67108864;
 }
 
-class AudioChannelDescription extends ffi.Struct {
+final class AudioChannelDescription extends ffi.Struct {
   @AudioChannelLabel()
   external int mChannelLabel;
 
@@ -67758,4 +67758,4 @@
 typedef CMAudioFormatDescriptionRef = CMFormatDescriptionRef;
 typedef CMFormatDescriptionRef = ffi.Pointer<opaqueCMFormatDescription>;
 
-class opaqueCMFormatDescription extends ffi.Opaque {}
+final class opaqueCMFormatDescription extends ffi.Opaque {}
diff --git a/pkgs/ffigen/example/objective_c/config.yaml b/pkgs/ffigen/example/objective_c/config.yaml
index 94764ca..b44cc69 100644
--- a/pkgs/ffigen/example/objective_c/config.yaml
+++ b/pkgs/ffigen/example/objective_c/config.yaml
@@ -1,3 +1,5 @@
+# yaml-language-server: $schema=../../ffigen.schema.json
+
 name: AVFAudio
 description: Bindings for AVFAudio.
 language: objc
diff --git a/pkgs/ffigen/example/objective_c/pubspec.yaml b/pkgs/ffigen/example/objective_c/pubspec.yaml
index 33b0c31..4cedaa3 100644
--- a/pkgs/ffigen/example/objective_c/pubspec.yaml
+++ b/pkgs/ffigen/example/objective_c/pubspec.yaml
@@ -5,11 +5,11 @@
 name: objective_c_example
 
 environment:
-  sdk: '>=2.17.0 <4.0.0'
+  sdk: ">=3.0.0 <4.0.0"
 
 dependencies:
   ffi: ^2.0.1
 dev_dependencies:
   ffigen:
-    path: '../../'
+    path: "../../"
   lints: ^2.0.0
diff --git a/pkgs/ffigen/example/shared_bindings/ffigen_configs/a.yaml b/pkgs/ffigen/example/shared_bindings/ffigen_configs/a.yaml
index 55a720e..a23faa1 100644
--- a/pkgs/ffigen/example/shared_bindings/ffigen_configs/a.yaml
+++ b/pkgs/ffigen/example/shared_bindings/ffigen_configs/a.yaml
@@ -2,6 +2,8 @@
 # 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.
 
+# yaml-language-server: $schema=../../../ffigen.schema.json
+
 name: NativeLibraryA
 description: Bindings to `headers/a.h`.
 output: '../lib/generated/a_gen.dart'
diff --git a/pkgs/ffigen/example/shared_bindings/ffigen_configs/a_shared_base.yaml b/pkgs/ffigen/example/shared_bindings/ffigen_configs/a_shared_base.yaml
index 0adcc20..aa4f1b1 100644
--- a/pkgs/ffigen/example/shared_bindings/ffigen_configs/a_shared_base.yaml
+++ b/pkgs/ffigen/example/shared_bindings/ffigen_configs/a_shared_base.yaml
@@ -2,6 +2,8 @@
 # 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.
 
+# yaml-language-server: $schema=../../../ffigen.schema.json
+
 name: NativeLibraryASharedB
 description: Bindings to `headers/a.h` with shared definitions from `headers/base.h`.
 output: '../lib/generated/a_shared_b_gen.dart'
diff --git a/pkgs/ffigen/example/shared_bindings/ffigen_configs/base.yaml b/pkgs/ffigen/example/shared_bindings/ffigen_configs/base.yaml
index 474d0f9..da03542 100644
--- a/pkgs/ffigen/example/shared_bindings/ffigen_configs/base.yaml
+++ b/pkgs/ffigen/example/shared_bindings/ffigen_configs/base.yaml
@@ -2,6 +2,8 @@
 # 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.
 
+# yaml-language-server: $schema=../../../ffigen.schema.json
+
 name: NativeLibraryBase
 description: Bindings to `headers/base.h`.
 output:
diff --git a/pkgs/ffigen/example/shared_bindings/lib/generated/a_gen.dart b/pkgs/ffigen/example/shared_bindings/lib/generated/a_gen.dart
index 6cd4f8a..4661c25 100644
--- a/pkgs/ffigen/example/shared_bindings/lib/generated/a_gen.dart
+++ b/pkgs/ffigen/example/shared_bindings/lib/generated/a_gen.dart
@@ -65,22 +65,22 @@
       .asFunction<void Function(BaseStruct2, BaseUnion2, BaseTypedef2)>();
 }
 
-class BaseStruct1 extends ffi.Struct {
+final class BaseStruct1 extends ffi.Struct {
   @ffi.Int()
   external int a;
 }
 
-class BaseUnion1 extends ffi.Union {
+final class BaseUnion1 extends ffi.Union {
   @ffi.Int()
   external int a;
 }
 
-class BaseStruct2 extends ffi.Struct {
+final class BaseStruct2 extends ffi.Struct {
   @ffi.Int()
   external int a;
 }
 
-class BaseUnion2 extends ffi.Union {
+final class BaseUnion2 extends ffi.Union {
   @ffi.Int()
   external int a;
 }
@@ -93,12 +93,12 @@
 typedef BaseTypedef1 = BaseStruct1;
 typedef BaseTypedef2 = BaseStruct2;
 
-class A_Struct1 extends ffi.Struct {
+final class A_Struct1 extends ffi.Struct {
   @ffi.Int()
   external int a;
 }
 
-class A_Union1 extends ffi.Union {
+final class A_Union1 extends ffi.Union {
   @ffi.Int()
   external int a;
 }
diff --git a/pkgs/ffigen/example/shared_bindings/lib/generated/a_shared_b_gen.dart b/pkgs/ffigen/example/shared_bindings/lib/generated/a_shared_b_gen.dart
index 1346bec..ba61da4 100644
--- a/pkgs/ffigen/example/shared_bindings/lib/generated/a_shared_b_gen.dart
+++ b/pkgs/ffigen/example/shared_bindings/lib/generated/a_shared_b_gen.dart
@@ -51,12 +51,12 @@
       void Function(imp1.BaseStruct2, imp1.BaseUnion2, imp1.BaseTypedef2)>();
 }
 
-class A_Struct1 extends ffi.Struct {
+final class A_Struct1 extends ffi.Struct {
   @ffi.Int()
   external int a;
 }
 
-class A_Union1 extends ffi.Union {
+final class A_Union1 extends ffi.Union {
   @ffi.Int()
   external int a;
 }
diff --git a/pkgs/ffigen/example/shared_bindings/lib/generated/base_gen.dart b/pkgs/ffigen/example/shared_bindings/lib/generated/base_gen.dart
index b31c2e8..9d90b53 100644
--- a/pkgs/ffigen/example/shared_bindings/lib/generated/base_gen.dart
+++ b/pkgs/ffigen/example/shared_bindings/lib/generated/base_gen.dart
@@ -39,22 +39,22 @@
       _base_func1Ptr.asFunction<void Function(BaseTypedef1, BaseTypedef2)>();
 }
 
-class BaseStruct1 extends ffi.Struct {
+final class BaseStruct1 extends ffi.Struct {
   @ffi.Int()
   external int a;
 }
 
-class BaseUnion1 extends ffi.Union {
+final class BaseUnion1 extends ffi.Union {
   @ffi.Int()
   external int a;
 }
 
-class BaseStruct2 extends ffi.Struct {
+final class BaseStruct2 extends ffi.Struct {
   @ffi.Int()
   external int a;
 }
 
-class BaseUnion2 extends ffi.Union {
+final class BaseUnion2 extends ffi.Union {
   @ffi.Int()
   external int a;
 }
diff --git a/pkgs/ffigen/example/shared_bindings/pubspec.yaml b/pkgs/ffigen/example/shared_bindings/pubspec.yaml
index fbe942e..2f270f1 100644
--- a/pkgs/ffigen/example/shared_bindings/pubspec.yaml
+++ b/pkgs/ffigen/example/shared_bindings/pubspec.yaml
@@ -5,7 +5,7 @@
 name: shared_bindings
 
 environment:
-  sdk: '>=2.17.0 <4.0.0'
+  sdk: ">=3.0.0 <4.0.0"
 
 dependencies:
   cli_util: ^0.4.0
@@ -14,5 +14,5 @@
 
 dev_dependencies:
   ffigen:
-    path: '../../'
+    path: "../../"
   lints: ^2.0.1
diff --git a/pkgs/ffigen/example/simple/config.yaml b/pkgs/ffigen/example/simple/config.yaml
index 82ed925..f12c349 100644
--- a/pkgs/ffigen/example/simple/config.yaml
+++ b/pkgs/ffigen/example/simple/config.yaml
@@ -1,3 +1,5 @@
+# yaml-language-server: $schema=../../ffigen.schema.json
+
 name: NativeLibrary
 description: Bindings to `headers/example.h`.
 output: 'generated_bindings.dart'
diff --git a/pkgs/ffigen/example/simple/pubspec.yaml b/pkgs/ffigen/example/simple/pubspec.yaml
index d59f53e..ea43572 100644
--- a/pkgs/ffigen/example/simple/pubspec.yaml
+++ b/pkgs/ffigen/example/simple/pubspec.yaml
@@ -5,12 +5,11 @@
 name: simple_example
 
 environment:
-  sdk: '>=2.17.0 <4.0.0'
+  sdk: ">=3.0.0 <4.0.0"
 
 dependencies:
   ffi: ^2.0.1
 dev_dependencies:
   ffigen:
-    path: '../../'
+    path: "../../"
   lints: ^2.0.1
-
diff --git a/pkgs/ffigen/example/swift/config.yaml b/pkgs/ffigen/example/swift/config.yaml
index e1a8322..6082e97 100644
--- a/pkgs/ffigen/example/swift/config.yaml
+++ b/pkgs/ffigen/example/swift/config.yaml
@@ -1,3 +1,5 @@
+# yaml-language-server: $schema=../../ffigen.schema.json
+
 name: SwiftLibrary
 description: Bindings for swift_api.
 language: objc
diff --git a/pkgs/ffigen/example/swift/pubspec.yaml b/pkgs/ffigen/example/swift/pubspec.yaml
index d00ffa9..07d93eb 100644
--- a/pkgs/ffigen/example/swift/pubspec.yaml
+++ b/pkgs/ffigen/example/swift/pubspec.yaml
@@ -5,11 +5,11 @@
 name: swift_example
 
 environment:
-  sdk: '>=2.17.0 <4.0.0'
+  sdk: ">=3.0.0 <4.0.0"
 
 dependencies:
   ffi: ^2.0.1
 dev_dependencies:
   ffigen:
-    path: '../../'
+    path: "../../"
   lints: ^2.0.0
diff --git a/pkgs/ffigen/example/swift/swift_api_bindings.dart b/pkgs/ffigen/example/swift/swift_api_bindings.dart
index 383944f..f741f7f 100644
--- a/pkgs/ffigen/example/swift/swift_api_bindings.dart
+++ b/pkgs/ffigen/example/swift/swift_api_bindings.dart
@@ -29122,13 +29122,13 @@
   }
 }
 
-class ObjCSel extends ffi.Opaque {}
+final class ObjCSel extends ffi.Opaque {}
 
-class ObjCObject extends ffi.Opaque {}
+final class ObjCObject extends ffi.Opaque {}
 
 typedef instancetype = ffi.Pointer<ObjCObject>;
 
-class _NSZone extends ffi.Opaque {}
+final class _NSZone extends ffi.Opaque {}
 
 class Protocol extends _ObjCWrapper {
   Protocol._(ffi.Pointer<ObjCObject> id, SwiftLibrary lib,
@@ -32115,7 +32115,7 @@
 
 typedef NSRange = _NSRange;
 
-class _NSRange extends ffi.Struct {
+final class _NSRange extends ffi.Struct {
   @NSUInteger()
   external int location;
 
@@ -33364,7 +33364,7 @@
 
 typedef NSPoint = CGPoint;
 
-class CGPoint extends ffi.Struct {
+final class CGPoint extends ffi.Struct {
   @CGFloat()
   external double x;
 
@@ -33375,7 +33375,7 @@
 typedef CGFloat = ffi.Double;
 typedef NSSize = CGSize;
 
-class CGSize extends ffi.Struct {
+final class CGSize extends ffi.Struct {
   @CGFloat()
   external double width;
 
@@ -33385,13 +33385,13 @@
 
 typedef NSRect = CGRect;
 
-class CGRect extends ffi.Struct {
+final class CGRect extends ffi.Struct {
   external CGPoint origin;
 
   external CGSize size;
 }
 
-class NSEdgeInsets extends ffi.Struct {
+final class NSEdgeInsets extends ffi.Struct {
   @CGFloat()
   external double top;
 
@@ -34538,7 +34538,7 @@
   ffi.Pointer<_ObjCBlock> get pointer => _id;
 }
 
-class _ObjCBlockDesc extends ffi.Struct {
+final class _ObjCBlockDesc extends ffi.Struct {
   @ffi.UnsignedLong()
   external int reserved;
 
@@ -34552,7 +34552,7 @@
   external ffi.Pointer<ffi.Char> signature;
 }
 
-class _ObjCBlock extends ffi.Struct {
+final class _ObjCBlock extends ffi.Struct {
   external ffi.Pointer<ffi.Void> isa;
 
   @ffi.Int()
@@ -35183,7 +35183,7 @@
   }
 }
 
-class __va_list_tag extends ffi.Struct {
+final class __va_list_tag extends ffi.Struct {
   @ffi.UnsignedInt()
   external int gp_offset;
 
@@ -35871,7 +35871,7 @@
   ffi.Pointer<_ObjCBlock> get pointer => _id;
 }
 
-class NSFastEnumerationState extends ffi.Struct {
+final class NSFastEnumerationState extends ffi.Struct {
   @ffi.UnsignedLong()
   external int state;
 
@@ -42805,7 +42805,7 @@
 typedef NSRunLoopMode = ffi.Pointer<ObjCObject>;
 typedef CFRunLoopRef = ffi.Pointer<__CFRunLoop>;
 
-class __CFRunLoop extends ffi.Opaque {}
+final class __CFRunLoop extends ffi.Opaque {}
 
 class NSTimer extends NSObject {
   NSTimer._(ffi.Pointer<ObjCObject> id, SwiftLibrary lib,
@@ -44721,7 +44721,7 @@
 typedef __int32_t = ffi.Int;
 
 @ffi.Packed(2)
-class AEDesc extends ffi.Struct {
+final class AEDesc extends ffi.Struct {
   @DescType()
   external int descriptorType;
 
@@ -44731,7 +44731,7 @@
 typedef AEDataStorage = ffi.Pointer<AEDataStorageType>;
 typedef AEDataStorageType = ffi.Pointer<OpaqueAEDataStorageType>;
 
-class OpaqueAEDataStorageType extends ffi.Opaque {}
+final class OpaqueAEDataStorageType extends ffi.Opaque {}
 
 typedef AEKeyword = FourCharCode;
 
@@ -51847,7 +51847,7 @@
   }
 }
 
-class NSDecimal extends ffi.Opaque {}
+final class NSDecimal extends ffi.Opaque {}
 
 class NSException extends NSObject {
   NSException._(ffi.Pointer<ObjCObject> id, SwiftLibrary lib,
@@ -56260,7 +56260,7 @@
   }
 }
 
-class NSOperatingSystemVersion extends ffi.Struct {
+final class NSOperatingSystemVersion extends ffi.Struct {
   @NSInteger()
   external int majorVersion;
 
@@ -58169,11 +58169,11 @@
 
 typedef SecIdentityRef = ffi.Pointer<__SecIdentity>;
 
-class __SecIdentity extends ffi.Opaque {}
+final class __SecIdentity extends ffi.Opaque {}
 
 typedef SecTrustRef = ffi.Pointer<__SecTrust>;
 
-class __SecTrust extends ffi.Opaque {}
+final class __SecTrust extends ffi.Opaque {}
 
 class NSURLProtectionSpace extends NSObject {
   NSURLProtectionSpace._(ffi.Pointer<ObjCObject> id, SwiftLibrary lib,
diff --git a/pkgs/ffigen/ffigen.schema.json b/pkgs/ffigen/ffigen.schema.json
new file mode 100644
index 0000000..a557a87
--- /dev/null
+++ b/pkgs/ffigen/ffigen.schema.json
@@ -0,0 +1,524 @@
+{
+  "$id": "https://json.schemastore.org/ffigen",
+  "$comment": "This file is generated. To regenerate run: dart tool/generate_json_schema.dart in github.com/dart-lang/ffigen",
+  "$schema": "https://json-schema.org/draft/2020-12/schema",
+  "type": "object",
+  "additionalProperties": false,
+  "properties": {
+    "llvm-path": {
+      "type": "array",
+      "items": {
+        "type": "string"
+      }
+    },
+    "output": {
+      "$oneOf": [
+        {
+          "$ref": "#/$defs/filePath"
+        },
+        {
+          "type": "object",
+          "additionalProperties": false,
+          "properties": {
+            "bindings": {
+              "$ref": "#/$defs/filePath"
+            },
+            "symbol-file": {
+              "type": "object",
+              "additionalProperties": false,
+              "properties": {
+                "output": {
+                  "$ref": "#/$defs/filePath"
+                },
+                "import-path": {
+                  "type": "string"
+                }
+              },
+              "required": [
+                "output",
+                "import-path"
+              ]
+            }
+          },
+          "required": [
+            "bindings"
+          ]
+        }
+      ]
+    },
+    "language": {
+      "enum": [
+        "c",
+        "objc"
+      ]
+    },
+    "headers": {
+      "type": "object",
+      "additionalProperties": false,
+      "properties": {
+        "entry-points": {
+          "type": "array",
+          "items": {
+            "type": "string"
+          }
+        },
+        "include-directives": {
+          "type": "array",
+          "items": {
+            "type": "string"
+          }
+        }
+      },
+      "required": [
+        "entry-points"
+      ]
+    },
+    "compiler-opts": {
+      "$oneOf": [
+        {
+          "type": "string"
+        },
+        {
+          "type": "array",
+          "items": {
+            "type": "string"
+          }
+        }
+      ]
+    },
+    "compiler-opts-automatic": {
+      "type": "object",
+      "additionalProperties": false,
+      "properties": {
+        "macos": {
+          "type": "object",
+          "additionalProperties": false,
+          "properties": {
+            "include-c-standard-library": {
+              "type": "boolean"
+            }
+          }
+        }
+      }
+    },
+    "library-imports": {
+      "type": "object",
+      "patternProperties": {
+        ".*": {
+          "type": "string"
+        }
+      }
+    },
+    "functions": {
+      "type": "object",
+      "additionalProperties": false,
+      "properties": {
+        "include": {
+          "$ref": "#/$defs/fullMatchOrRegexpList"
+        },
+        "exclude": {
+          "$ref": "#/$defs/fullMatchOrRegexpList"
+        },
+        "rename": {
+          "$ref": "#/$defs/rename"
+        },
+        "member-rename": {
+          "$ref": "#/$defs/memberRename"
+        },
+        "symbol-address": {
+          "$ref": "#/$defs/includeExclude"
+        },
+        "expose-typedefs": {
+          "$ref": "#/$defs/includeExclude"
+        },
+        "leaf": {
+          "$ref": "#/$defs/includeExclude"
+        },
+        "variadic-arguments": {
+          "type": "object",
+          "patternProperties": {
+            ".*": {
+              "type": "array",
+              "items": {
+                "$oneOf": [
+                  {
+                    "type": "array",
+                    "items": {
+                      "type": "string"
+                    }
+                  },
+                  {
+                    "type": "object",
+                    "additionalProperties": false,
+                    "properties": {
+                      "types": {
+                        "type": "array",
+                        "items": {
+                          "type": "string"
+                        }
+                      },
+                      "postfix": {
+                        "type": "string"
+                      }
+                    },
+                    "required": [
+                      "types"
+                    ]
+                  }
+                ]
+              }
+            }
+          }
+        }
+      }
+    },
+    "structs": {
+      "type": "object",
+      "additionalProperties": false,
+      "properties": {
+        "include": {
+          "$ref": "#/$defs/fullMatchOrRegexpList"
+        },
+        "exclude": {
+          "$ref": "#/$defs/fullMatchOrRegexpList"
+        },
+        "rename": {
+          "$ref": "#/$defs/rename"
+        },
+        "member-rename": {
+          "$ref": "#/$defs/memberRename"
+        },
+        "dependency-only": {
+          "$ref": "#/$defs/dependencyOnly"
+        },
+        "pack": {
+          "type": "object",
+          "patternProperties": {
+            ".*": {
+              "enum": [
+                "none",
+                1,
+                2,
+                4,
+                8,
+                16
+              ]
+            }
+          }
+        }
+      }
+    },
+    "unions": {
+      "type": "object",
+      "additionalProperties": false,
+      "properties": {
+        "include": {
+          "$ref": "#/$defs/fullMatchOrRegexpList"
+        },
+        "exclude": {
+          "$ref": "#/$defs/fullMatchOrRegexpList"
+        },
+        "rename": {
+          "$ref": "#/$defs/rename"
+        },
+        "member-rename": {
+          "$ref": "#/$defs/memberRename"
+        },
+        "dependency-only": {
+          "$ref": "#/$defs/dependencyOnly"
+        }
+      }
+    },
+    "enums": {
+      "type": "object",
+      "additionalProperties": false,
+      "properties": {
+        "include": {
+          "$ref": "#/$defs/fullMatchOrRegexpList"
+        },
+        "exclude": {
+          "$ref": "#/$defs/fullMatchOrRegexpList"
+        },
+        "rename": {
+          "$ref": "#/$defs/rename"
+        },
+        "member-rename": {
+          "$ref": "#/$defs/memberRename"
+        }
+      }
+    },
+    "unnamed-enums": {
+      "type": "object",
+      "additionalProperties": false,
+      "properties": {
+        "include": {
+          "$ref": "#/$defs/fullMatchOrRegexpList"
+        },
+        "exclude": {
+          "$ref": "#/$defs/fullMatchOrRegexpList"
+        },
+        "rename": {
+          "$ref": "#/$defs/rename"
+        }
+      }
+    },
+    "globals": {
+      "type": "object",
+      "additionalProperties": false,
+      "properties": {
+        "include": {
+          "$ref": "#/$defs/fullMatchOrRegexpList"
+        },
+        "exclude": {
+          "$ref": "#/$defs/fullMatchOrRegexpList"
+        },
+        "rename": {
+          "$ref": "#/$defs/rename"
+        },
+        "symbol-address": {
+          "$ref": "#/$defs/includeExclude"
+        }
+      }
+    },
+    "macros": {
+      "type": "object",
+      "additionalProperties": false,
+      "properties": {
+        "include": {
+          "$ref": "#/$defs/fullMatchOrRegexpList"
+        },
+        "exclude": {
+          "$ref": "#/$defs/fullMatchOrRegexpList"
+        },
+        "rename": {
+          "$ref": "#/$defs/rename"
+        }
+      }
+    },
+    "typedefs": {
+      "type": "object",
+      "additionalProperties": false,
+      "properties": {
+        "include": {
+          "$ref": "#/$defs/fullMatchOrRegexpList"
+        },
+        "exclude": {
+          "$ref": "#/$defs/fullMatchOrRegexpList"
+        },
+        "rename": {
+          "$ref": "#/$defs/rename"
+        }
+      }
+    },
+    "objc-interfaces": {
+      "type": "object",
+      "additionalProperties": false,
+      "properties": {
+        "include": {
+          "$ref": "#/$defs/fullMatchOrRegexpList"
+        },
+        "exclude": {
+          "$ref": "#/$defs/fullMatchOrRegexpList"
+        },
+        "rename": {
+          "$ref": "#/$defs/rename"
+        },
+        "member-rename": {
+          "$ref": "#/$defs/memberRename"
+        },
+        "module": {
+          "$ref": "#/$defs/objcInterfaceModule"
+        }
+      }
+    },
+    "import": {
+      "type": "object",
+      "additionalProperties": false,
+      "properties": {
+        "symbol-files": {
+          "type": "array",
+          "items": {
+            "type": "string"
+          }
+        }
+      }
+    },
+    "type-map": {
+      "type": "object",
+      "additionalProperties": false,
+      "properties": {
+        "typedefs": {
+          "$ref": "#/$defs/mappedTypes"
+        },
+        "structs": {
+          "$ref": "#/$defs/mappedTypes"
+        },
+        "unions": {
+          "$ref": "#/$defs/mappedTypes"
+        },
+        "native-types": {
+          "$ref": "#/$defs/mappedTypes"
+        }
+      }
+    },
+    "exclude-all-by-default": {
+      "type": "boolean"
+    },
+    "sort": {
+      "type": "boolean"
+    },
+    "use-supported-typedefs": {
+      "type": "boolean"
+    },
+    "comments": {
+      "$oneOf": [
+        {
+          "type": "boolean"
+        },
+        {
+          "type": "object",
+          "additionalProperties": false,
+          "properties": {
+            "style": {
+              "enum": [
+                "doxygen",
+                "any"
+              ]
+            },
+            "length": {
+              "enum": [
+                "brief",
+                "full"
+              ]
+            }
+          }
+        }
+      ]
+    },
+    "name": {
+      "$ref": "#/$defs/publicDartClass"
+    },
+    "description": {
+      "$ref": "#/$defs/nonEmptyString"
+    },
+    "preamble": {
+      "type": "string"
+    },
+    "use-dart-handle": {
+      "type": "boolean"
+    },
+    "ffi-native": {
+      "$oneOf": [
+        {
+          "enum": [
+            null
+          ]
+        },
+        {
+          "type": "object",
+          "additionalProperties": false,
+          "properties": {
+            "asset": {
+              "type": "string"
+            }
+          },
+          "required": [
+            "asset"
+          ]
+        }
+      ]
+    }
+  },
+  "required": [
+    "output",
+    "headers"
+  ],
+  "$defs": {
+    "filePath": {
+      "type": "string",
+      "description": "A file path"
+    },
+    "fullMatchOrRegexpList": {
+      "type": "array",
+      "items": {
+        "type": "string"
+      }
+    },
+    "rename": {
+      "type": "object",
+      "patternProperties": {
+        ".*": {
+          "type": "string"
+        }
+      }
+    },
+    "memberRename": {
+      "type": "object",
+      "patternProperties": {
+        ".*": {
+          "type": "object",
+          "patternProperties": {
+            ".*": {
+              "type": "string"
+            }
+          }
+        }
+      }
+    },
+    "includeExclude": {
+      "type": "object",
+      "additionalProperties": false,
+      "properties": {
+        "include": {
+          "$ref": "#/$defs/fullMatchOrRegexpList"
+        },
+        "exclude": {
+          "$ref": "#/$defs/fullMatchOrRegexpList"
+        }
+      }
+    },
+    "dependencyOnly": {
+      "enum": [
+        "full",
+        "opaque"
+      ]
+    },
+    "objcInterfaceModule": {
+      "type": "object",
+      "patternProperties": {
+        ".*": {
+          "type": "string"
+        }
+      }
+    },
+    "mappedTypes": {
+      "type": "object",
+      "patternProperties": {
+        ".*": {
+          "type": "object",
+          "additionalProperties": false,
+          "properties": {
+            "lib": {
+              "type": "string"
+            },
+            "c-type": {
+              "type": "string"
+            },
+            "dart-type": {
+              "type": "string"
+            }
+          }
+        }
+      }
+    },
+    "publicDartClass": {
+      "type": "string",
+      "description": "A public dart class name.",
+      "pattern": "^[a-zA-Z]+[_a-zA-Z0-9]*$"
+    },
+    "nonEmptyString": {
+      "type": "string",
+      "pattern": ".+"
+    }
+  }
+}
\ No newline at end of file
diff --git a/pkgs/ffigen/lib/src/code_generator/compound.dart b/pkgs/ffigen/lib/src/code_generator/compound.dart
index 2c915a2..db75582 100644
--- a/pkgs/ffigen/lib/src/code_generator/compound.dart
+++ b/pkgs/ffigen/lib/src/code_generator/compound.dart
@@ -129,22 +129,22 @@
     }
     final dartClassName = isStruct ? 'Struct' : 'Union';
     // Write class declaration.
-    s.write('class $enclosingClassName extends ');
+    s.write('final class $enclosingClassName extends ');
     s.write('${w.ffiLibraryPrefix}.${isOpaque ? 'Opaque' : dartClassName}{\n');
     const depth = '  ';
     for (final m in members) {
       m.name = localUniqueNamer.makeUnique(m.name);
+      if (m.dartDoc != null) {
+        s.write('$depth/// ');
+        s.writeAll(m.dartDoc!.split('\n'), '\n$depth/// ');
+        s.write('\n');
+      }
       if (m.type is ConstantArray) {
         s.write('$depth@${w.ffiLibraryPrefix}.Array.multi(');
         s.write('${_getArrayDimensionLengths(m.type)})\n');
         s.write('${depth}external ${_getInlineArrayTypeString(m.type, w)} ');
         s.write('${m.name};\n\n');
       } else {
-        if (m.dartDoc != null) {
-          s.write('$depth/// ');
-          s.writeAll(m.dartDoc!.split('\n'), '\n$depth/// ');
-          s.write('\n');
-        }
         if (!sameDartAndCType(m.type, w)) {
           s.write('$depth@${m.type.getCType(w)}()\n');
         }
diff --git a/pkgs/ffigen/lib/src/code_generator/func.dart b/pkgs/ffigen/lib/src/code_generator/func.dart
index 590f63c..f8d32e2 100644
--- a/pkgs/ffigen/lib/src/code_generator/func.dart
+++ b/pkgs/ffigen/lib/src/code_generator/func.dart
@@ -31,10 +31,10 @@
 /// typedef _dart_sum = int Function(int a, int b);
 /// ```
 ///
-/// When using `FfiNative`, the code is as follows.
+/// When using `Native`, the code is as follows.
 ///
 /// ```dart
-/// @ffi.FfiNative<ffi.Int32 Function(ffi.Int32 a, ffi.Int32 b)>('sum')
+/// @ffi.Native<ffi.Int32 Function(ffi.Int32 a, ffi.Int32 b)>('sum')
 /// external int sum(int a, int b);
 /// ```
 class Func extends LookUpBinding {
@@ -58,6 +58,7 @@
     String? dartDoc,
     required Type returnType,
     List<Parameter>? parameters,
+    List<Parameter>? varArgParameters,
     this.exposeSymbolAddress = false,
     this.exposeFunctionTypedefs = false,
     this.isLeaf = false,
@@ -66,6 +67,7 @@
   })  : functionType = FunctionType(
           returnType: returnType,
           parameters: parameters ?? const [],
+          varArgParameters: varArgParameters ?? const [],
         ),
         super(
           usr: usr,
@@ -109,7 +111,7 @@
     }
     // Resolve name conflicts in function parameter names.
     final paramNamer = UniqueNamer({});
-    for (final p in functionType.parameters) {
+    for (final p in functionType.dartTypeParameters) {
       p.name = paramNamer.makeUnique(p.name);
     }
 
@@ -126,11 +128,11 @@
           : '';
       final isLeafString = isLeaf ? ', isLeaf: true' : '';
       s.write(
-          "@${w.ffiLibraryPrefix}.FfiNative<$cType>('$originalName'$assetString$isLeafString)\n");
+          "@${w.ffiLibraryPrefix}.Native<$cType>(symbol: '$originalName'$assetString$isLeafString)\n");
 
       s.write(
           'external ${functionType.returnType.getDartType(w)} $enclosingFuncName(\n');
-      for (final p in functionType.parameters) {
+      for (final p in functionType.dartTypeParameters) {
         s.write('  ${p.type.getDartType(w)} ${p.name},\n');
       }
       s.write(');\n\n');
@@ -138,14 +140,14 @@
       // Write enclosing function.
       s.write(
           '${functionType.returnType.getDartType(w)} $enclosingFuncName(\n');
-      for (final p in functionType.parameters) {
+      for (final p in functionType.dartTypeParameters) {
         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) {
+      for (final p in functionType.dartTypeParameters) {
         s.write('    ${p.name},\n');
       }
       s.write('  );\n');
diff --git a/pkgs/ffigen/lib/src/code_generator/func_type.dart b/pkgs/ffigen/lib/src/code_generator/func_type.dart
index 5402028..d4c74f6 100644
--- a/pkgs/ffigen/lib/src/code_generator/func_type.dart
+++ b/pkgs/ffigen/lib/src/code_generator/func_type.dart
@@ -11,13 +11,19 @@
 class FunctionType extends Type {
   final Type returnType;
   final List<Parameter> parameters;
+  final List<Parameter> varArgParameters;
+
+  /// Get all the parameters for generating the dart type. This includes both
+  /// [parameters] and [varArgParameters].
+  List<Parameter> get dartTypeParameters => parameters + varArgParameters;
 
   FunctionType({
     required this.returnType,
     required this.parameters,
+    this.varArgParameters = const [],
   });
 
-  String _getTypeString(
+  String _getCacheKeyString(
       bool writeArgumentNames, String Function(Type) typeToString) {
     final sb = StringBuffer();
 
@@ -35,18 +41,51 @@
   }
 
   @override
-  String getCType(Writer w, {bool writeArgumentNames = true}) =>
-      _getTypeString(writeArgumentNames, (Type t) => t.getCType(w));
+  String getCType(Writer w, {bool writeArgumentNames = true}) {
+    final sb = StringBuffer();
+
+    // Write return Type.
+    sb.write(returnType.getCType(w));
+
+    // Write Function.
+    sb.write(' Function(');
+    sb.write((parameters).map<String>((p) {
+      return '${p.type.getCType(w)} ${writeArgumentNames ? p.name : ""}';
+    }).join(', '));
+    if (varArgParameters.isNotEmpty) {
+      sb.write(", ${w.ffiLibraryPrefix}.VarArgs<(");
+      sb.write((varArgParameters).map<String>((p) {
+        return '${p.type.getCType(w)} ${writeArgumentNames ? p.name : ""}';
+      }).join(', '));
+      sb.write(",)>");
+    }
+    sb.write(')');
+
+    return sb.toString();
+  }
 
   @override
-  String getDartType(Writer w, {bool writeArgumentNames = true}) =>
-      _getTypeString(writeArgumentNames, (Type t) => t.getDartType(w));
+  String getDartType(Writer w, {bool writeArgumentNames = true}) {
+    final sb = StringBuffer();
+
+    // Write return Type.
+    sb.write(returnType.getDartType(w));
+
+    // Write Function.
+    sb.write(' Function(');
+    sb.write(dartTypeParameters.map<String>((p) {
+      return '${p.type.getDartType(w)} ${writeArgumentNames ? p.name : ""}';
+    }).join(', '));
+    sb.write(')');
+
+    return sb.toString();
+  }
 
   @override
-  String toString() => _getTypeString(false, (Type t) => t.toString());
+  String toString() => _getCacheKeyString(false, (Type t) => t.toString());
 
   @override
-  String cacheKey() => _getTypeString(false, (Type t) => t.cacheKey());
+  String cacheKey() => _getCacheKeyString(false, (Type t) => t.cacheKey());
 
   @override
   void addDependencies(Set<Binding> dependencies) {
diff --git a/pkgs/ffigen/lib/src/code_generator/imports.dart b/pkgs/ffigen/lib/src/code_generator/imports.dart
index 41f7161..d6dcd06 100644
--- a/pkgs/ffigen/lib/src/code_generator/imports.dart
+++ b/pkgs/ffigen/lib/src/code_generator/imports.dart
@@ -49,6 +49,25 @@
   String? getDefaultValue(Writer w, String nativeLib) => defaultValue;
 }
 
+/// An unchecked type similar to [ImportedType] which exists in the generated
+/// binding itself.
+class SelfImportedType extends Type {
+  final String cType;
+  final String dartType;
+  final String? defaultValue;
+
+  SelfImportedType(this.cType, this.dartType, [this.defaultValue]);
+
+  @override
+  String getCType(Writer w) => cType;
+
+  @override
+  String getDartType(Writer w) => dartType;
+
+  @override
+  String toString() => cType;
+}
+
 final ffiImport = LibraryImport('ffi', 'dart:ffi');
 final ffiPkgImport = LibraryImport('pkg_ffi', 'package:ffi/ffi.dart');
 
diff --git a/pkgs/ffigen/lib/src/code_generator/objc_block.dart b/pkgs/ffigen/lib/src/code_generator/objc_block.dart
index 310fef1..1599748 100644
--- a/pkgs/ffigen/lib/src/code_generator/objc_block.dart
+++ b/pkgs/ffigen/lib/src/code_generator/objc_block.dart
@@ -139,9 +139,6 @@
     s.write(''');
   }''');
 
-    // Get the pointer to the underlying block.
-    s.write('  ${blockPtr.getCType(w)} get pointer => _id;\n');
-
     s.write('}\n');
     return BindingString(
         type: BindingStringType.objcBlock, string: s.toString());
diff --git a/pkgs/ffigen/lib/src/code_generator/objc_built_in_functions.dart b/pkgs/ffigen/lib/src/code_generator/objc_built_in_functions.dart
index ebb4b20..05cd130 100644
--- a/pkgs/ffigen/lib/src/code_generator/objc_built_in_functions.dart
+++ b/pkgs/ffigen/lib/src/code_generator/objc_built_in_functions.dart
@@ -242,6 +242,9 @@
 
   @override
   int get hashCode => _id.hashCode;
+
+  /// Return a pointer to this object.
+  $idType get pointer => _id;
 }
 ''');
   }
diff --git a/pkgs/ffigen/lib/src/code_generator/struct.dart b/pkgs/ffigen/lib/src/code_generator/struct.dart
index c65e04b..ae9e35b 100644
--- a/pkgs/ffigen/lib/src/code_generator/struct.dart
+++ b/pkgs/ffigen/lib/src/code_generator/struct.dart
@@ -16,7 +16,7 @@
 /// ```
 /// The generated dart code is -
 /// ```dart
-/// class Struct extends ffi.Struct{
+/// final class Struct extends ffi.Struct {
 ///  @ffi.Int32()
 ///  int a;
 ///
diff --git a/pkgs/ffigen/lib/src/code_generator/union.dart b/pkgs/ffigen/lib/src/code_generator/union.dart
index fc7be96..8455004 100644
--- a/pkgs/ffigen/lib/src/code_generator/union.dart
+++ b/pkgs/ffigen/lib/src/code_generator/union.dart
@@ -15,7 +15,7 @@
 /// ```
 /// The generated dart code is -
 /// ```dart
-/// class Union extends ffi.Union{
+/// final class Union extends ffi.Union{
 ///  @ffi.Int32()
 ///  int a;
 ///
diff --git a/pkgs/ffigen/lib/src/config_provider/config.dart b/pkgs/ffigen/lib/src/config_provider/config.dart
index e012e56..5109a0c 100644
--- a/pkgs/ffigen/lib/src/config_provider/config.dart
+++ b/pkgs/ffigen/lib/src/config_provider/config.dart
@@ -12,6 +12,7 @@
 import 'package:yaml/yaml.dart';
 
 import '../strings.dart' as strings;
+import 'config_spec.dart';
 import 'config_types.dart';
 import 'spec_utils.dart';
 
@@ -22,12 +23,10 @@
 /// Handles validation, extraction of configurations from a yaml file.
 class Config {
   /// Input filename.
-  String? get filename => _filename;
-  String? _filename;
+  final String? filename;
 
   /// Package config.
-  PackageConfig? get packageConfig => _packageConfig;
-  PackageConfig? _packageConfig;
+  final PackageConfig? packageConfig;
 
   /// Location for llvm/lib folder.
   String get libclangDylib => _libclangDylib;
@@ -53,6 +52,10 @@
   List<String> get compilerOpts => _compilerOpts;
   late List<String> _compilerOpts;
 
+  /// VarArg function handling.
+  Map<String, List<VarArgFunction>> get varArgFunctions => _varArgFunctions;
+  late Map<String, List<VarArgFunction>> _varArgFunctions = {};
+
   /// Declaration config for Functions.
   Declaration get functionDecl => _functionDecl;
   late Declaration _functionDecl;
@@ -172,23 +175,22 @@
   FfiNativeConfig get ffiNativeConfig => _ffiNativeConfig;
   late FfiNativeConfig _ffiNativeConfig;
 
-  Config._(this._filename, this._packageConfig);
+  Config._({required this.filename, required this.packageConfig});
 
   /// Create config from Yaml map.
   factory Config.fromYaml(YamlMap map,
       {String? filename, PackageConfig? packageConfig}) {
-    final configspecs = Config._(filename, packageConfig);
+    final config = Config._(filename: filename, packageConfig: packageConfig);
     _logger.finest('Config Map: $map');
 
-    final specs = configspecs._getSpecs();
-
-    final result = configspecs._checkConfigs(map, specs);
+    final ffigenConfigSpec = config._getRootConfigSpec();
+    final result = ffigenConfigSpec.validate(map);
     if (!result) {
       throw FormatException('Invalid configurations provided.');
     }
 
-    configspecs._extract(map, specs);
-    return configspecs;
+    ffigenConfigSpec.extract(map);
+    return config;
   }
 
   /// Create config from a file.
@@ -200,6 +202,12 @@
         filename: file.path, packageConfig: packageConfig);
   }
 
+  /// Returns the root ConfigSpec object.
+  static ConfigSpec getsRootConfigSpec() {
+    final configspecs = Config._(filename: null, packageConfig: null);
+    return configspecs._getRootConfigSpec();
+  }
+
   /// Add compiler options for clang. If [highPriority] is true these are added
   /// to the front of the list.
   void addCompilerOpts(String compilerOpts, {bool highPriority = false}) {
@@ -211,352 +219,698 @@
     }
   }
 
-  /// Validates Yaml according to given specs.
-  bool _checkConfigs(YamlMap map, Map<List<String>, Specification> specs) {
-    var result = true;
-    for (final key in specs.keys) {
-      final spec = specs[key];
-      if (checkKeyInYaml(key, map)) {
-        result = result && spec!.validator(key, getKeyValueFromYaml(key, map));
-      } else if (spec!.requirement == Requirement.yes) {
-        _logger.severe("Key '$key' is required.");
-        result = false;
-      } else if (spec.requirement == Requirement.prefer) {
-        _logger.warning("Prefer adding Key '$key' to your config.");
-      }
-    }
-    // Warn about unknown keys.
-    warnUnknownKeys(specs.keys.toList(), map);
-
-    return result;
+  ConfigSpec _getRootConfigSpec() {
+    return HeterogeneousMapConfigSpec(
+      entries: [
+        HeterogeneousMapEntry(
+          key: strings.llvmPath,
+          valueConfigSpec: ListConfigSpec<String, String>(
+            childConfigSpec: StringConfigSpec(),
+            transform: (node) => llvmPathExtractor(node.value),
+          ),
+          defaultValue: (node) => findDylibAtDefaultLocations(),
+          resultOrDefault: (node) => _libclangDylib = node.value as String,
+        ),
+        HeterogeneousMapEntry(
+            key: strings.output,
+            required: true,
+            valueConfigSpec: OneOfConfigSpec(
+              childConfigSpecs: [
+                _filePathStringConfigSpec(),
+                _outputFullConfigSpec(),
+              ],
+              transform: (node) =>
+                  outputExtractor(node.value, filename, packageConfig),
+              result: (node) {
+                _output = (node.value as OutputConfig).output;
+                _symbolFile = (node.value as OutputConfig).symbolFile;
+              },
+            )),
+        HeterogeneousMapEntry(
+          key: strings.language,
+          valueConfigSpec: EnumConfigSpec(
+            allowedValues: {strings.langC, strings.langObjC},
+            transform: (node) {
+              if ((node.value == strings.langObjC)) {
+                _logger.severe(
+                    'Objective C support is EXPERIMENTAL. The API may change '
+                    'in a breaking way without notice.');
+                return Language.objc;
+              } else {
+                return Language.c;
+              }
+            },
+          ),
+          defaultValue: (node) => Language.c,
+          resultOrDefault: (node) => _language = node.value as Language,
+        ),
+        HeterogeneousMapEntry(
+            key: strings.headers,
+            required: true,
+            valueConfigSpec: HeterogeneousMapConfigSpec<List<String>, Headers>(
+              entries: [
+                HeterogeneousMapEntry(
+                  key: strings.entryPoints,
+                  valueConfigSpec: ListConfigSpec<String, List<String>>(
+                      childConfigSpec: StringConfigSpec()),
+                  required: true,
+                ),
+                HeterogeneousMapEntry(
+                  key: strings.includeDirectives,
+                  valueConfigSpec: ListConfigSpec<String, List<String>>(
+                      childConfigSpec: StringConfigSpec()),
+                ),
+              ],
+              transform: (node) => headersExtractor(node.value, filename),
+              result: (node) => _headers = node.value,
+            )),
+        HeterogeneousMapEntry(
+          key: strings.compilerOpts,
+          valueConfigSpec: OneOfConfigSpec<List<String>, List<String>>(
+            childConfigSpecs: [
+              StringConfigSpec(
+                transform: (node) => [node.value],
+              ),
+              ListConfigSpec<String, List<String>>(
+                  childConfigSpec: StringConfigSpec())
+            ],
+            transform: (node) => compilerOptsExtractor(node.value),
+          ),
+          defaultValue: (node) => <String>[],
+          resultOrDefault: (node) => _compilerOpts = node.value as List<String>,
+        ),
+        HeterogeneousMapEntry(
+            key: strings.compilerOptsAuto,
+            valueConfigSpec: HeterogeneousMapConfigSpec(
+              entries: [
+                HeterogeneousMapEntry(
+                  key: strings.macos,
+                  valueConfigSpec: HeterogeneousMapConfigSpec(
+                    entries: [
+                      HeterogeneousMapEntry(
+                        key: strings.includeCStdLib,
+                        valueConfigSpec: BoolConfigSpec(),
+                        defaultValue: (node) => true,
+                      )
+                    ],
+                  ),
+                )
+              ],
+              transform: (node) => CompilerOptsAuto(
+                macIncludeStdLib: ((node.value)[strings.macos]
+                    as Map?)?[strings.includeCStdLib] as bool,
+              ),
+              result: (node) => _compilerOpts.addAll(
+                  (node.value as CompilerOptsAuto).extractCompilerOpts()),
+            )),
+        HeterogeneousMapEntry(
+          key: strings.libraryImports,
+          valueConfigSpec: MapConfigSpec<String, Map<String, LibraryImport>>(
+            keyValueConfigSpecs: [
+              (keyRegexp: ".*", valueConfigSpec: StringConfigSpec()),
+            ],
+            customValidation: _libraryImportsPredefinedValidation,
+            transform: (node) => libraryImportsExtractor(node.value.cast()),
+          ),
+          defaultValue: (node) => <String, LibraryImport>{},
+          resultOrDefault: (node) =>
+              _libraryImports = (node.value) as Map<String, LibraryImport>,
+        ),
+        HeterogeneousMapEntry(
+            key: strings.functions,
+            valueConfigSpec: HeterogeneousMapConfigSpec(
+              entries: [
+                ..._includeExcludeProperties(),
+                ..._renameProperties(),
+                ..._memberRenameProperties(),
+                HeterogeneousMapEntry(
+                  key: strings.symbolAddress,
+                  valueConfigSpec: _includeExcludeObject(),
+                  defaultValue: (node) => Includer.excludeByDefault(),
+                ),
+                HeterogeneousMapEntry(
+                  key: strings.exposeFunctionTypedefs,
+                  valueConfigSpec: _includeExcludeObject(),
+                  defaultValue: (node) => Includer.excludeByDefault(),
+                ),
+                HeterogeneousMapEntry(
+                  key: strings.leafFunctions,
+                  valueConfigSpec: _includeExcludeObject(),
+                  defaultValue: (node) => Includer.excludeByDefault(),
+                ),
+                HeterogeneousMapEntry(
+                  key: strings.varArgFunctions,
+                  valueConfigSpec: _functionVarArgsConfigSpec(),
+                  defaultValue: (node) => <String, List<RawVarArgFunction>>{},
+                  resultOrDefault: (node) {
+                    _varArgFunctions = makeVarArgFunctionsMapping(
+                        node.value as Map<String, List<RawVarArgFunction>>,
+                        _libraryImports);
+                  },
+                ),
+              ],
+              result: (node) {
+                _functionDecl = declarationConfigExtractor(
+                    node.value as Map<dynamic, dynamic>);
+                _exposeFunctionTypedefs = (node.value
+                    as Map)[strings.exposeFunctionTypedefs] as Includer;
+                _leafFunctions =
+                    (node.value as Map)[strings.leafFunctions] as Includer;
+              },
+            )),
+        HeterogeneousMapEntry(
+            key: strings.structs,
+            valueConfigSpec: HeterogeneousMapConfigSpec(
+              entries: [
+                ..._includeExcludeProperties(),
+                ..._renameProperties(),
+                ..._memberRenameProperties(),
+                _dependencyOnlyHeterogeneousMapKey(),
+                HeterogeneousMapEntry(
+                  key: strings.structPack,
+                  valueConfigSpec: MapConfigSpec(
+                    keyValueConfigSpecs: [
+                      (
+                        keyRegexp: '.*',
+                        valueConfigSpec: EnumConfigSpec(
+                          allowedValues: {'none', 1, 2, 4, 8, 16},
+                          transform: (node) =>
+                              node.value == 'none' ? null : node.value,
+                        ),
+                      )
+                    ],
+                    transform: (node) =>
+                        structPackingOverrideExtractor(node.value),
+                  ),
+                  defaultValue: (node) => StructPackingOverride(),
+                  resultOrDefault: (node) => _structPackingOverride =
+                      node.value as StructPackingOverride,
+                ),
+              ],
+              result: (node) {
+                _structDecl = declarationConfigExtractor(
+                    node.value as Map<dynamic, dynamic>);
+                _structDependencies = (node.value
+                    as Map)[strings.dependencyOnly] as CompoundDependencies;
+              },
+            )),
+        HeterogeneousMapEntry(
+            key: strings.unions,
+            valueConfigSpec: HeterogeneousMapConfigSpec(
+              entries: [
+                ..._includeExcludeProperties(),
+                ..._renameProperties(),
+                ..._memberRenameProperties(),
+                _dependencyOnlyHeterogeneousMapKey(),
+              ],
+              result: (node) {
+                _unionDecl = declarationConfigExtractor(
+                    node.value as Map<dynamic, dynamic>);
+                _unionDependencies = (node.value as Map)[strings.dependencyOnly]
+                    as CompoundDependencies;
+              },
+            )),
+        HeterogeneousMapEntry(
+            key: strings.enums,
+            valueConfigSpec: HeterogeneousMapConfigSpec(
+              entries: [
+                ..._includeExcludeProperties(),
+                ..._renameProperties(),
+                ..._memberRenameProperties(),
+              ],
+              result: (node) {
+                _enumClassDecl = declarationConfigExtractor(
+                    node.value as Map<dynamic, dynamic>);
+              },
+            )),
+        HeterogeneousMapEntry(
+            key: strings.unnamedEnums,
+            valueConfigSpec: HeterogeneousMapConfigSpec(
+              entries: [
+                ..._includeExcludeProperties(),
+                ..._renameProperties(),
+              ],
+              result: (node) {
+                _unnamedEnumConstants = declarationConfigExtractor(
+                    node.value as Map<dynamic, dynamic>);
+              },
+            )),
+        HeterogeneousMapEntry(
+            key: strings.globals,
+            valueConfigSpec: HeterogeneousMapConfigSpec(
+              entries: [
+                ..._includeExcludeProperties(),
+                ..._renameProperties(),
+                HeterogeneousMapEntry(
+                  key: strings.symbolAddress,
+                  valueConfigSpec: _includeExcludeObject(),
+                  defaultValue: (node) => Includer.excludeByDefault(),
+                )
+              ],
+              result: (node) {
+                _globals = declarationConfigExtractor(
+                    node.value as Map<dynamic, dynamic>);
+              },
+            )),
+        HeterogeneousMapEntry(
+            key: strings.macros,
+            valueConfigSpec: HeterogeneousMapConfigSpec(
+              entries: [
+                ..._includeExcludeProperties(),
+                ..._renameProperties(),
+              ],
+              result: (node) {
+                _macroDecl = declarationConfigExtractor(
+                    node.value as Map<dynamic, dynamic>);
+              },
+            )),
+        HeterogeneousMapEntry(
+            key: strings.typedefs,
+            valueConfigSpec: HeterogeneousMapConfigSpec(
+              entries: [
+                ..._includeExcludeProperties(),
+                ..._renameProperties(),
+              ],
+              result: (node) {
+                _typedefs = declarationConfigExtractor(
+                    node.value as Map<dynamic, dynamic>);
+              },
+            )),
+        HeterogeneousMapEntry(
+            key: strings.objcInterfaces,
+            valueConfigSpec: HeterogeneousMapConfigSpec(
+              entries: [
+                ..._includeExcludeProperties(),
+                ..._renameProperties(),
+                ..._memberRenameProperties(),
+                HeterogeneousMapEntry(
+                  key: strings.objcModule,
+                  valueConfigSpec: _objcInterfaceModuleObject(),
+                  defaultValue: (node) => ObjCModulePrefixer({}),
+                )
+              ],
+              result: (node) {
+                _objcInterfaces = declarationConfigExtractor(
+                    node.value as Map<dynamic, dynamic>);
+                _objcModulePrefixer = (node.value as Map)[strings.objcModule]
+                    as ObjCModulePrefixer;
+              },
+            )),
+        HeterogeneousMapEntry(
+            key: strings.import,
+            valueConfigSpec: HeterogeneousMapConfigSpec(
+              entries: [
+                HeterogeneousMapEntry(
+                  key: strings.symbolFilesImport,
+                  valueConfigSpec:
+                      ListConfigSpec<String, Map<String, ImportedType>>(
+                    childConfigSpec: StringConfigSpec(),
+                    transform: (node) => symbolFileImportExtractor(
+                        node.value, _libraryImports, filename, packageConfig),
+                  ),
+                  defaultValue: (node) => <String, ImportedType>{},
+                  resultOrDefault: (node) => _usrTypeMappings =
+                      node.value as Map<String, ImportedType>,
+                )
+              ],
+            )),
+        HeterogeneousMapEntry(
+            key: strings.typeMap,
+            valueConfigSpec: HeterogeneousMapConfigSpec(
+              entries: [
+                HeterogeneousMapEntry(
+                  key: strings.typeMapTypedefs,
+                  valueConfigSpec: _mappedTypeObject(),
+                  defaultValue: (node) => <String, List<String>>{},
+                ),
+                HeterogeneousMapEntry(
+                  key: strings.typeMapStructs,
+                  valueConfigSpec: _mappedTypeObject(),
+                  defaultValue: (node) => <String, List<String>>{},
+                ),
+                HeterogeneousMapEntry(
+                  key: strings.typeMapUnions,
+                  valueConfigSpec: _mappedTypeObject(),
+                  defaultValue: (node) => <String, List<String>>{},
+                ),
+                HeterogeneousMapEntry(
+                  key: strings.typeMapNativeTypes,
+                  valueConfigSpec: _mappedTypeObject(),
+                  defaultValue: (node) => <String, List<String>>{},
+                ),
+              ],
+              result: (node) {
+                final nodeValue = node.value as Map;
+                _typedefTypeMappings = makeImportTypeMapping(
+                  (nodeValue[strings.typeMapTypedefs])
+                      as Map<String, List<String>>,
+                  _libraryImports,
+                );
+                _structTypeMappings = makeImportTypeMapping(
+                  (nodeValue[strings.typeMapStructs])
+                      as Map<String, List<String>>,
+                  _libraryImports,
+                );
+                _unionTypeMappings = makeImportTypeMapping(
+                  (nodeValue[strings.typeMapUnions])
+                      as Map<String, List<String>>,
+                  _libraryImports,
+                );
+                _nativeTypeMappings = makeImportTypeMapping(
+                  (nodeValue[strings.typeMapNativeTypes])
+                      as Map<String, List<String>>,
+                  _libraryImports,
+                );
+              },
+            )),
+        HeterogeneousMapEntry(
+          key: strings.excludeAllByDefault,
+          valueConfigSpec: BoolConfigSpec(),
+          defaultValue: (node) => false,
+          resultOrDefault: (node) => _excludeAllByDefault = node.value as bool,
+        ),
+        HeterogeneousMapEntry(
+          key: strings.sort,
+          valueConfigSpec: BoolConfigSpec(),
+          defaultValue: (node) => false,
+          resultOrDefault: (node) => _sort = node.value as bool,
+        ),
+        HeterogeneousMapEntry(
+          key: strings.useSupportedTypedefs,
+          valueConfigSpec: BoolConfigSpec(),
+          defaultValue: (node) => true,
+          resultOrDefault: (node) => _useSupportedTypedefs = node.value as bool,
+        ),
+        HeterogeneousMapEntry(
+          key: strings.comments,
+          valueConfigSpec: _commentConfigSpec(),
+          defaultValue: (node) => CommentType.def(),
+          resultOrDefault: (node) => _commentType = node.value as CommentType,
+        ),
+        HeterogeneousMapEntry(
+          key: strings.name,
+          valueConfigSpec: _dartClassNameStringConfigSpec(),
+          defaultValue: (node) {
+            _logger.warning(
+                "Prefer adding Key '${node.pathString}' to your config.");
+            return 'NativeLibrary';
+          },
+          resultOrDefault: (node) => _wrapperName = node.value as String,
+        ),
+        HeterogeneousMapEntry(
+          key: strings.description,
+          valueConfigSpec: _nonEmptyStringConfigSpec(),
+          defaultValue: (node) {
+            _logger.warning(
+                "Prefer adding Key '${node.pathString}' to your config.");
+            return null;
+          },
+          resultOrDefault: (node) => _wrapperDocComment = node.value as String?,
+        ),
+        HeterogeneousMapEntry(
+            key: strings.preamble,
+            valueConfigSpec: StringConfigSpec(
+              result: (node) => _preamble = node.value as String?,
+            )),
+        HeterogeneousMapEntry(
+          key: strings.useDartHandle,
+          valueConfigSpec: BoolConfigSpec(),
+          defaultValue: (node) => true,
+          resultOrDefault: (node) => _useDartHandle = node.value as bool,
+        ),
+        HeterogeneousMapEntry(
+          key: strings.ffiNative,
+          valueConfigSpec: OneOfConfigSpec(
+            childConfigSpecs: [
+              EnumConfigSpec(allowedValues: {null}),
+              HeterogeneousMapConfigSpec(
+                entries: [
+                  HeterogeneousMapEntry(
+                    key: strings.ffiNativeAsset,
+                    valueConfigSpec: StringConfigSpec(),
+                    required: true,
+                  )
+                ],
+              )
+            ],
+            transform: (node) => ffiNativeExtractor(node.value),
+          ),
+          defaultValue: (node) => FfiNativeConfig(enabled: false),
+          resultOrDefault: (node) =>
+              _ffiNativeConfig = (node.value) as FfiNativeConfig,
+        ),
+      ],
+    );
   }
 
-  /// Extracts variables from Yaml according to given specs.
-  ///
-  /// Validation must be done beforehand, using [_checkConfigs].
-  void _extract(YamlMap map, Map<List<String>, Specification> specs) {
-    for (final key in specs.keys) {
-      final spec = specs[key];
-      if (checkKeyInYaml(key, map)) {
-        spec!.extractedResult(spec.extractor(getKeyValueFromYaml(key, map)));
-      } else {
-        spec!.extractedResult(spec.defaultValue?.call());
-      }
+  bool _libraryImportsPredefinedValidation(ConfigValue node) {
+    if (node.value is YamlMap) {
+      return (node.value as YamlMap).keys.where((key) {
+        if (strings.predefinedLibraryImports.containsKey(key)) {
+          _logger.severe(
+              '${node.pathString} -> $key should not collide with any predefined imports - ${strings.predefinedLibraryImports.keys}.');
+          return true;
+        }
+        return false;
+      }).isEmpty;
     }
+    return true;
   }
 
-  /// Returns map of various specifications avaialble for our tool.
-  ///
-  /// Key: Name, Value: [Specification]
-  Map<List<String>, Specification> _getSpecs() {
-    return <List<String>, Specification>{
-      [strings.llvmPath]: Specification<String>(
-        requirement: Requirement.no,
-        validator: llvmPathValidator,
-        extractor: llvmPathExtractor,
-        defaultValue: () => findDylibAtDefaultLocations(),
-        extractedResult: (dynamic result) {
-          _libclangDylib = result as String;
+  OneOfConfigSpec _commentConfigSpec() {
+    return OneOfConfigSpec(
+      childConfigSpecs: [
+        BoolConfigSpec(
+          transform: (node) =>
+              (node.value == true) ? CommentType.def() : CommentType.none(),
+        ),
+        HeterogeneousMapConfigSpec(
+          entries: [
+            HeterogeneousMapEntry(
+              key: strings.style,
+              valueConfigSpec: EnumConfigSpec(
+                allowedValues: {strings.doxygen, strings.any},
+                transform: (node) => node.value == strings.doxygen
+                    ? CommentStyle.doxygen
+                    : CommentStyle.any,
+              ),
+              defaultValue: (node) => CommentStyle.doxygen,
+            ),
+            HeterogeneousMapEntry(
+              key: strings.length,
+              valueConfigSpec: EnumConfigSpec(
+                allowedValues: {strings.brief, strings.full},
+                transform: (node) => node.value == strings.brief
+                    ? CommentLength.brief
+                    : CommentLength.full,
+              ),
+              defaultValue: (node) => CommentLength.full,
+            ),
+          ],
+          transform: (node) => CommentType(
+            (node.value)[strings.style] as CommentStyle,
+            (node.value)[strings.length] as CommentLength,
+          ),
+        ),
+      ],
+    );
+  }
+
+  MapConfigSpec _functionVarArgsConfigSpec() {
+    return MapConfigSpec(
+      keyValueConfigSpecs: [
+        (
+          keyRegexp: ".*",
+          valueConfigSpec: ListConfigSpec(
+            childConfigSpec: OneOfConfigSpec(
+              childConfigSpecs: [
+                ListConfigSpec(childConfigSpec: StringConfigSpec()),
+                HeterogeneousMapConfigSpec(
+                  entries: [
+                    HeterogeneousMapEntry(
+                      key: strings.types,
+                      valueConfigSpec: ListConfigSpec<String, List<String>>(
+                          childConfigSpec: StringConfigSpec()),
+                      required: true,
+                    ),
+                    HeterogeneousMapEntry(
+                      key: strings.postfix,
+                      valueConfigSpec: StringConfigSpec(),
+                    ),
+                  ],
+                )
+              ],
+            ),
+          )
+        )
+      ],
+      transform: (node) => varArgFunctionConfigExtractor(node.value),
+    );
+  }
+
+  HeterogeneousMapConfigSpec _outputFullConfigSpec() {
+    return HeterogeneousMapConfigSpec(
+      entries: [
+        HeterogeneousMapEntry(
+          key: strings.bindings,
+          valueConfigSpec: _filePathStringConfigSpec(),
+          required: true,
+        ),
+        HeterogeneousMapEntry(
+          key: strings.symbolFile,
+          valueConfigSpec: HeterogeneousMapConfigSpec(
+            entries: [
+              HeterogeneousMapEntry(
+                key: strings.output,
+                valueConfigSpec: _filePathStringConfigSpec(),
+                required: true,
+              ),
+              HeterogeneousMapEntry(
+                key: strings.importPath,
+                valueConfigSpec: StringConfigSpec(),
+                required: true,
+              ),
+            ],
+          ),
+        ),
+      ],
+    );
+  }
+
+  StringConfigSpec _filePathStringConfigSpec() {
+    return StringConfigSpec(
+      schemaDefName: 'filePath',
+      schemaDescription: "A file path",
+    );
+  }
+
+  StringConfigSpec _nonEmptyStringConfigSpec() {
+    return StringConfigSpec(
+      schemaDefName: 'nonEmptyString',
+      pattern: r'.+',
+    );
+  }
+
+  StringConfigSpec _dartClassNameStringConfigSpec() {
+    return StringConfigSpec(
+      schemaDefName: 'publicDartClass',
+      schemaDescription: "A public dart class name.",
+      pattern: r'^[a-zA-Z]+[_a-zA-Z0-9]*$',
+    );
+  }
+
+  List<HeterogeneousMapEntry> _includeExcludeProperties() {
+    return [
+      HeterogeneousMapEntry(
+        key: strings.include,
+        valueConfigSpec: _fullMatchOrRegexpList(),
+      ),
+      HeterogeneousMapEntry(
+        key: strings.exclude,
+        valueConfigSpec: _fullMatchOrRegexpList(),
+        defaultValue: (node) => <String>[],
+      ),
+    ];
+  }
+
+  ListConfigSpec<String, List<String>> _fullMatchOrRegexpList() {
+    return ListConfigSpec(
+      schemaDefName: "fullMatchOrRegexpList",
+      childConfigSpec: StringConfigSpec(),
+    );
+  }
+
+  List<HeterogeneousMapEntry> _renameProperties() {
+    return [
+      HeterogeneousMapEntry(
+        key: strings.rename,
+        valueConfigSpec: MapConfigSpec<String, dynamic>(
+          schemaDefName: "rename",
+          keyValueConfigSpecs: [
+            (keyRegexp: ".*", valueConfigSpec: StringConfigSpec()),
+          ],
+        ),
+      ),
+    ];
+  }
+
+  List<HeterogeneousMapEntry> _memberRenameProperties() {
+    return [
+      HeterogeneousMapEntry(
+        key: strings.memberRename,
+        valueConfigSpec: MapConfigSpec<Map<dynamic, String>,
+            Map<dynamic, Map<dynamic, String>>>(
+          schemaDefName: "memberRename",
+          keyValueConfigSpecs: [
+            (
+              keyRegexp: ".*",
+              valueConfigSpec: MapConfigSpec<String, Map<dynamic, String>>(
+                keyValueConfigSpecs: [
+                  (keyRegexp: ".*", valueConfigSpec: StringConfigSpec())
+                ],
+              ),
+            ),
+          ],
+        ),
+      ),
+    ];
+  }
+
+  HeterogeneousMapConfigSpec<List<String>, Includer> _includeExcludeObject() {
+    return HeterogeneousMapConfigSpec(
+      schemaDefName: "includeExclude",
+      entries: [
+        ..._includeExcludeProperties(),
+      ],
+      transform: (node) => extractIncluderFromYaml(node.value),
+    );
+  }
+
+  HeterogeneousMapEntry _dependencyOnlyHeterogeneousMapKey() {
+    return HeterogeneousMapEntry(
+      key: strings.dependencyOnly,
+      valueConfigSpec: EnumConfigSpec<String, CompoundDependencies>(
+        schemaDefName: "dependencyOnly",
+        allowedValues: {
+          strings.fullCompoundDependencies,
+          strings.opaqueCompoundDependencies,
         },
+        transform: (node) => node.value == strings.opaqueCompoundDependencies
+            ? CompoundDependencies.opaque
+            : CompoundDependencies.full,
       ),
-      [strings.output]: Specification<OutputConfig>(
-        requirement: Requirement.yes,
-        validator: outputValidator,
-        extractor: (dynamic value) =>
-            outputExtractor(value, filename, packageConfig),
-        extractedResult: (dynamic result) {
-          _output = (result as OutputConfig).output;
-          _symbolFile = result.symbolFile;
-        },
-      ),
-      [strings.language]: Specification<Language>(
-        requirement: Requirement.no,
-        validator: languageValidator,
-        extractor: languageExtractor,
-        defaultValue: () => Language.c,
-        extractedResult: (dynamic result) => _language = result as Language,
-      ),
-      [strings.headers]: Specification<Headers>(
-        requirement: Requirement.yes,
-        validator: headersValidator,
-        extractor: (dynamic value) => headersExtractor(value, filename),
-        extractedResult: (dynamic result) => _headers = result as Headers,
-      ),
-      [strings.compilerOpts]: Specification<List<String>>(
-        requirement: Requirement.no,
-        validator: compilerOptsValidator,
-        extractor: compilerOptsExtractor,
-        defaultValue: () => [],
-        extractedResult: (dynamic result) =>
-            _compilerOpts = result as List<String>,
-      ),
-      [strings.compilerOptsAuto]: Specification<CompilerOptsAuto>(
-          requirement: Requirement.no,
-          validator: compilerOptsAutoValidator,
-          extractor: compilerOptsAutoExtractor,
-          defaultValue: () => CompilerOptsAuto(),
-          extractedResult: (dynamic result) {
-            _compilerOpts
-                .addAll((result as CompilerOptsAuto).extractCompilerOpts());
-          }),
-      [strings.functions]: Specification<Declaration>(
-        requirement: Requirement.no,
-        validator: declarationConfigValidator,
-        extractor: declarationConfigExtractor,
-        defaultValue: () => Declaration(),
-        extractedResult: (dynamic result) {
-          _functionDecl = result as Declaration;
-        },
-      ),
-      [strings.structs]: Specification<Declaration>(
-        requirement: Requirement.no,
-        validator: declarationConfigValidator,
-        extractor: declarationConfigExtractor,
-        defaultValue: () => Declaration(),
-        extractedResult: (dynamic result) {
-          _structDecl = result as Declaration;
-        },
-      ),
-      [strings.unions]: Specification<Declaration>(
-        requirement: Requirement.no,
-        validator: declarationConfigValidator,
-        extractor: declarationConfigExtractor,
-        defaultValue: () => Declaration(),
-        extractedResult: (dynamic result) {
-          _unionDecl = result as Declaration;
-        },
-      ),
-      [strings.enums]: Specification<Declaration>(
-        requirement: Requirement.no,
-        validator: declarationConfigValidator,
-        extractor: declarationConfigExtractor,
-        defaultValue: () => Declaration(),
-        extractedResult: (dynamic result) {
-          _enumClassDecl = result as Declaration;
-        },
-      ),
-      [strings.unnamedEnums]: Specification<Declaration>(
-        requirement: Requirement.no,
-        validator: declarationConfigValidator,
-        extractor: declarationConfigExtractor,
-        defaultValue: () => Declaration(),
-        extractedResult: (dynamic result) =>
-            _unnamedEnumConstants = result as Declaration,
-      ),
-      [strings.globals]: Specification<Declaration>(
-        requirement: Requirement.no,
-        validator: declarationConfigValidator,
-        extractor: declarationConfigExtractor,
-        defaultValue: () => Declaration(),
-        extractedResult: (dynamic result) {
-          _globals = result as Declaration;
-        },
-      ),
-      [strings.macros]: Specification<Declaration>(
-        requirement: Requirement.no,
-        validator: declarationConfigValidator,
-        extractor: declarationConfigExtractor,
-        defaultValue: () => Declaration(),
-        extractedResult: (dynamic result) {
-          _macroDecl = result as Declaration;
-        },
-      ),
-      [strings.typedefs]: Specification<Declaration>(
-        requirement: Requirement.no,
-        validator: declarationConfigValidator,
-        extractor: declarationConfigExtractor,
-        defaultValue: () => Declaration(),
-        extractedResult: (dynamic result) {
-          _typedefs = result as Declaration;
-        },
-      ),
-      [strings.objcInterfaces]: Specification<Declaration>(
-        requirement: Requirement.no,
-        validator: declarationConfigValidator,
-        extractor: declarationConfigExtractor,
-        defaultValue: () => Declaration(),
-        extractedResult: (dynamic result) {
-          _objcInterfaces = result as Declaration;
-        },
-      ),
-      [strings.objcInterfaces, strings.objcModule]:
-          Specification<Map<String, String>>(
-        requirement: Requirement.no,
-        validator: stringStringMapValidator,
-        extractor: stringStringMapExtractor,
-        defaultValue: () => <String, String>{},
-        extractedResult: (dynamic result) => _objcModulePrefixer =
-            ObjCModulePrefixer(result as Map<String, String>),
-      ),
-      [strings.libraryImports]: Specification<Map<String, LibraryImport>>(
-        validator: libraryImportsValidator,
-        extractor: libraryImportsExtractor,
-        defaultValue: () => <String, LibraryImport>{},
-        extractedResult: (dynamic result) {
-          _libraryImports = result as Map<String, LibraryImport>;
-        },
-      ),
-      [strings.import, strings.symbolFilesImport]:
-          Specification<Map<String, ImportedType>>(
-        validator: symbolFileImportValidator,
-        extractor: (value) => symbolFileImportExtractor(
-            value, _libraryImports, filename, packageConfig),
-        defaultValue: () => <String, ImportedType>{},
-        extractedResult: (dynamic result) {
-          _usrTypeMappings = result as Map<String, ImportedType>;
-        },
-      ),
-      [strings.typeMap, strings.typeMapTypedefs]:
-          Specification<Map<String, List<String>>>(
-        validator: typeMapValidator,
-        extractor: typeMapExtractor,
-        defaultValue: () => <String, List<String>>{},
-        extractedResult: (dynamic result) {
-          _typedefTypeMappings = makeImportTypeMapping(
-              result as Map<String, List<String>>, _libraryImports);
-        },
-      ),
-      [strings.typeMap, strings.typeMapStructs]:
-          Specification<Map<String, List<String>>>(
-        validator: typeMapValidator,
-        extractor: typeMapExtractor,
-        defaultValue: () => <String, List<String>>{},
-        extractedResult: (dynamic result) {
-          _structTypeMappings = makeImportTypeMapping(
-              result as Map<String, List<String>>, _libraryImports);
-        },
-      ),
-      [strings.typeMap, strings.typeMapUnions]:
-          Specification<Map<String, List<String>>>(
-        validator: typeMapValidator,
-        extractor: typeMapExtractor,
-        defaultValue: () => <String, List<String>>{},
-        extractedResult: (dynamic result) {
-          _unionTypeMappings = makeImportTypeMapping(
-              result as Map<String, List<String>>, _libraryImports);
-        },
-      ),
-      [strings.typeMap, strings.typeMapNativeTypes]:
-          Specification<Map<String, List<String>>>(
-        validator: typeMapValidator,
-        extractor: typeMapExtractor,
-        defaultValue: () => <String, List<String>>{},
-        extractedResult: (dynamic result) {
-          _nativeTypeMappings = makeImportTypeMapping(
-              result as Map<String, List<String>>, _libraryImports);
-        },
-      ),
-      [strings.excludeAllByDefault]: Specification<bool>(
-        requirement: Requirement.no,
-        validator: booleanValidator,
-        extractor: booleanExtractor,
-        defaultValue: () => false,
-        extractedResult: (dynamic result) =>
-            _excludeAllByDefault = result as bool,
-      ),
-      [strings.sort]: Specification<bool>(
-        requirement: Requirement.no,
-        validator: booleanValidator,
-        extractor: booleanExtractor,
-        defaultValue: () => false,
-        extractedResult: (dynamic result) => _sort = result as bool,
-      ),
-      [strings.useSupportedTypedefs]: Specification<bool>(
-        requirement: Requirement.no,
-        validator: booleanValidator,
-        extractor: booleanExtractor,
-        defaultValue: () => true,
-        extractedResult: (dynamic result) =>
-            _useSupportedTypedefs = result as bool,
-      ),
-      [strings.comments]: Specification<CommentType>(
-        requirement: Requirement.no,
-        validator: commentValidator,
-        extractor: commentExtractor,
-        defaultValue: () => CommentType.def(),
-        extractedResult: (dynamic result) =>
-            _commentType = result as CommentType,
-      ),
-      [strings.structs, strings.dependencyOnly]:
-          Specification<CompoundDependencies>(
-        requirement: Requirement.no,
-        validator: dependencyOnlyValidator,
-        extractor: dependencyOnlyExtractor,
-        defaultValue: () => CompoundDependencies.full,
-        extractedResult: (dynamic result) =>
-            _structDependencies = result as CompoundDependencies,
-      ),
-      [strings.unions, strings.dependencyOnly]:
-          Specification<CompoundDependencies>(
-        requirement: Requirement.no,
-        validator: dependencyOnlyValidator,
-        extractor: dependencyOnlyExtractor,
-        defaultValue: () => CompoundDependencies.full,
-        extractedResult: (dynamic result) =>
-            _unionDependencies = result as CompoundDependencies,
-      ),
-      [strings.structs, strings.structPack]:
-          Specification<StructPackingOverride>(
-        requirement: Requirement.no,
-        validator: structPackingOverrideValidator,
-        extractor: structPackingOverrideExtractor,
-        defaultValue: () => StructPackingOverride(),
-        extractedResult: (dynamic result) =>
-            _structPackingOverride = result as StructPackingOverride,
-      ),
-      [strings.name]: Specification<String>(
-        requirement: Requirement.prefer,
-        validator: dartClassNameValidator,
-        extractor: stringExtractor,
-        defaultValue: () => 'NativeLibrary',
-        extractedResult: (dynamic result) => _wrapperName = result as String,
-      ),
-      [strings.description]: Specification<String?>(
-        requirement: Requirement.prefer,
-        validator: nonEmptyStringValidator,
-        extractor: stringExtractor,
-        defaultValue: () => null,
-        extractedResult: (dynamic result) =>
-            _wrapperDocComment = result as String?,
-      ),
-      [strings.preamble]: Specification<String?>(
-        requirement: Requirement.no,
-        validator: nonEmptyStringValidator,
-        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,
-      ),
-      [strings.functions, strings.exposeFunctionTypedefs]:
-          Specification<Includer>(
-        requirement: Requirement.no,
-        validator: exposeFunctionTypeValidator,
-        extractor: exposeFunctionTypeExtractor,
-        defaultValue: () => Includer.excludeByDefault(),
-        extractedResult: (dynamic result) =>
-            _exposeFunctionTypedefs = result as Includer,
-      ),
-      [strings.functions, strings.leafFunctions]: Specification<Includer>(
-        requirement: Requirement.no,
-        validator: leafFunctionValidator,
-        extractor: leafFunctionExtractor,
-        defaultValue: () => Includer.excludeByDefault(),
-        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,
-      )
-    };
+      defaultValue: (node) => CompoundDependencies.full,
+    );
+  }
+
+  MapConfigSpec _mappedTypeObject() {
+    return MapConfigSpec(
+      schemaDefName: "mappedTypes",
+      keyValueConfigSpecs: [
+        (
+          keyRegexp: ".*",
+          valueConfigSpec: HeterogeneousMapConfigSpec(entries: [
+            HeterogeneousMapEntry(
+                key: strings.lib, valueConfigSpec: StringConfigSpec()),
+            HeterogeneousMapEntry(
+                key: strings.cType, valueConfigSpec: StringConfigSpec()),
+            HeterogeneousMapEntry(
+                key: strings.dartType, valueConfigSpec: StringConfigSpec()),
+          ]),
+        )
+      ],
+      transform: (node) => typeMapExtractor(node.value),
+    );
+  }
+
+  MapConfigSpec _objcInterfaceModuleObject() {
+    return MapConfigSpec(
+      schemaDefName: "objcInterfaceModule",
+      keyValueConfigSpecs: [
+        (keyRegexp: ".*", valueConfigSpec: StringConfigSpec()),
+      ],
+      transform: (node) =>
+          ObjCModulePrefixer(node.value.cast<String, String>()),
+    );
   }
 }
diff --git a/pkgs/ffigen/lib/src/config_provider/config_spec.dart b/pkgs/ffigen/lib/src/config_provider/config_spec.dart
new file mode 100644
index 0000000..3ad3249
--- /dev/null
+++ b/pkgs/ffigen/lib/src/config_provider/config_spec.dart
@@ -0,0 +1,822 @@
+import 'package:logging/logging.dart';
+import 'package:yaml/yaml.dart';
+
+final _logger = Logger('ffigen.config_provider.config');
+
+/// Base class for all ConfigSpecs to extend.
+///
+/// [TE] - type input for [transform], [RE] - type input for [result].
+///
+/// Validation -
+///
+/// - [customValidation] is called after the ConfigSpec hierarchical validations
+/// are completed.
+///
+/// Extraction -
+///
+/// - The data is first validated, if invalid it throws a
+/// ConfigSpecExtractionError.
+/// - The extracted data from the child(s) is collected and the value is
+/// transformed via [transform] (if specified).
+/// - Finally the [result] closure is called (if specified).
+abstract class ConfigSpec<TE extends Object?, RE extends Object?> {
+  /// Used to generate and refer the reference definition generated in json
+  /// schema. Must be unique for a nested Schema.
+  String? schemaDefName;
+
+  /// Used to generate the description field in json schema.
+  String? schemaDescription;
+
+  /// Custom validation hook, called post validation if successful.
+  bool Function(ConfigValue node)? customValidation;
+
+  /// Used to transform the payload to another type before passing to parent
+  /// nodes and [result].
+  RE Function(ConfigValue<TE> node)? transform;
+
+  /// Called when final result is prepared via [_extractNode].
+  void Function(ConfigValue<RE> node)? result;
+  ConfigSpec({
+    required this.schemaDefName,
+    required this.schemaDescription,
+    required this.customValidation,
+    required this.transform,
+    required this.result,
+  });
+
+  bool _validateNode(ConfigValue o, {bool log = true});
+
+  ConfigValue<RE> _extractNode(ConfigValue o);
+
+  /// ConfigSpec objects should call [_getJsonRefOrSchemaNode] instead to get the
+  /// child json schema.
+  Map<String, dynamic> _generateJsonSchemaNode(Map<String, dynamic> defs);
+
+  Map<String, dynamic> _getJsonRefOrSchemaNode(Map<String, dynamic> defs) {
+    if (schemaDefName == null) {
+      return _generateJsonSchemaNode(defs);
+    }
+    defs.putIfAbsent(schemaDefName!, () => _generateJsonSchemaNode(defs));
+    return {r"$ref": "#/\$defs/$schemaDefName"};
+  }
+
+  Map<String, dynamic> generateJsonSchema(String schemaId) {
+    final defs = <String, dynamic>{};
+    final schemaMap = _generateJsonSchemaNode(defs);
+    return {
+      r"$id": schemaId,
+      r"$comment":
+          "This file is generated. To regenerate run: dart tool/generate_json_schema.dart in github.com/dart-lang/ffigen",
+      r"$schema": "https://json-schema.org/draft/2020-12/schema",
+      ...schemaMap,
+      r"$defs": defs,
+    };
+  }
+
+  /// Run validation on an object [value].
+  bool validate(dynamic value) {
+    return _validateNode(ConfigValue(path: [], value: value));
+  }
+
+  /// Extract ConfigSpecNode from [value]. This will call the [transform] for all
+  /// underlying ConfigSpecs if valid.
+  /// Should ideally only be called if [validate] returns True. Throws
+  /// [ConfigSpecExtractionError] if any validation fails.
+  ConfigValue extract(dynamic value) {
+    return _extractNode(ConfigValue(path: [], value: value));
+  }
+}
+
+/// An individual value in a config for a specific [path] instantiated from a [ConfigSpec].
+///
+/// A config value contains both the [value] that users of the configuration would want
+/// to use, as well as the [rawValue] that was provided as input to the configuration.
+class ConfigValue<TE> {
+  /// The path to this node.
+  ///
+  /// E.g - ["path", "to", "arr", "[1]", "item"]
+  final List<String> path;
+
+  /// Get a string representation for path.
+  ///
+  /// E.g - "path -> to -> arr -> [1] -> item"
+  String get pathString => path.join(" -> ");
+
+  /// Contains the underlying node value after all transformations and
+  /// default values have been applied.
+  final TE value;
+
+  /// Contains the raw underlying node value. Would be null for fields populated
+  /// but default values
+  final Object? rawValue;
+
+  ConfigValue({
+    required this.path,
+    required this.value,
+    Object? rawValue,
+    bool nullRawValue = false,
+  }) : rawValue = nullRawValue ? null : (rawValue ?? value);
+
+  /// Copy object with a different value.
+  ConfigValue<T> withValue<T>(T value, Object? rawValue) {
+    return ConfigValue<T>(
+      path: path,
+      value: value,
+      rawValue: rawValue,
+      nullRawValue: rawValue == null,
+    );
+  }
+
+  /// Transforms this with a nullable [transform] or return itself
+  /// and calls the [result] callback
+  ConfigValue<RE> transformOrThis<RE extends Object?>(
+    RE Function(ConfigValue<TE> value)? transform,
+    void Function(ConfigValue<RE> node)? resultCallback,
+  ) {
+    ConfigValue<RE> returnValue;
+    if (transform != null) {
+      returnValue = this.withValue(transform.call(this), rawValue);
+    } else {
+      returnValue = this.withValue(this.value as RE, rawValue);
+    }
+    resultCallback?.call(returnValue);
+    return returnValue;
+  }
+
+  /// Returns true if [value] is of Type [T].
+  bool checkType<T>({bool log = true}) {
+    if (value is! T) {
+      if (log) {
+        _logger.severe(
+            "Expected value of key '$pathString' to be of type '$T' (Got ${value.runtimeType}).");
+      }
+      return false;
+    }
+    return true;
+  }
+}
+
+class ConfigSpecExtractionError extends Error {
+  final ConfigValue? item;
+  final String message;
+  ConfigSpecExtractionError(this.item, [this.message = "Invalid ConfigSpec"]);
+
+  @override
+  String toString() {
+    if (item != null) {
+      return "$runtimeType: $message @ ${item!.pathString}";
+    }
+    return "$runtimeType: $message";
+  }
+}
+
+class HeterogeneousMapEntry {
+  final String key;
+  final ConfigSpec valueConfigSpec;
+  final Object? Function(ConfigValue<void> o)? defaultValue;
+  void Function(ConfigValue<Object?> node)? resultOrDefault;
+  final bool required;
+
+  HeterogeneousMapEntry({
+    required this.key,
+    required this.valueConfigSpec,
+    this.defaultValue,
+    this.resultOrDefault,
+    this.required = false,
+  });
+}
+
+enum AdditionalProperties { Allow, Warn, Error }
+
+/// ConfigSpec for a Map which has a fixed set of known keys.
+///
+/// [CE] typecasts result from entries->{}->valueConfigSpec.
+///
+/// [RE] typecasts result returned by this node.
+class HeterogeneousMapConfigSpec<CE extends Object?, RE extends Object?>
+    extends ConfigSpec<Map<dynamic, CE>, RE> {
+  final List<HeterogeneousMapEntry> entries;
+  final Set<String> allKeys;
+  final Set<String> requiredKeys;
+  final AdditionalProperties additionalProperties;
+
+  HeterogeneousMapConfigSpec({
+    required this.entries,
+    super.schemaDefName,
+    super.schemaDescription,
+    super.customValidation,
+    super.transform,
+    super.result,
+    this.additionalProperties = AdditionalProperties.Warn,
+  })  : requiredKeys = {
+          for (final kv in entries.where((kv) => kv.required)) kv.key
+        },
+        allKeys = {for (final kv in entries) kv.key};
+
+  @override
+  bool _validateNode(ConfigValue o, {bool log = true}) {
+    if (!o.checkType<Map>(log: log)) {
+      return false;
+    }
+
+    var result = true;
+    final inputMap = (o.value as Map);
+
+    for (final requiredKey in requiredKeys) {
+      if (!inputMap.containsKey(requiredKey)) {
+        if (log) {
+          _logger.severe(
+              "Key '${[...o.path, requiredKey].join(' -> ')}' is required.");
+        }
+        result = false;
+      }
+    }
+
+    for (final entry in entries) {
+      final path = [...o.path, entry.key.toString()];
+      if (!inputMap.containsKey(entry.key)) {
+        continue;
+      }
+      final configSpecNode =
+          ConfigValue(path: path, value: inputMap[entry.key]);
+      if (!entry.valueConfigSpec._validateNode(configSpecNode, log: log)) {
+        result = false;
+        continue;
+      }
+    }
+
+    if (additionalProperties != AdditionalProperties.Allow) {
+      for (final key in inputMap.keys) {
+        if (!allKeys.contains(key)) {
+          if (log) {
+            _logger.severe("Unknown key - '${[...o.path, key].join(' -> ')}'.");
+          }
+          if (additionalProperties == AdditionalProperties.Error) {
+            result = false;
+          }
+        }
+      }
+    }
+
+    if (!result && customValidation != null) {
+      return customValidation!.call(o);
+    }
+    return result;
+  }
+
+  dynamic _getAllDefaults(ConfigValue o) {
+    final result = <dynamic, CE>{};
+    for (final entry in entries) {
+      final path = [...o.path, entry.key];
+      if (entry.defaultValue != null) {
+        result[entry.key] = entry.defaultValue!
+            .call(ConfigValue(path: path, value: null)) as CE;
+      } else if (entry.valueConfigSpec is HeterogeneousMapConfigSpec) {
+        final defaultValue =
+            (entry.valueConfigSpec as HeterogeneousMapConfigSpec)
+                ._getAllDefaults(ConfigValue(path: path, value: null));
+        if (defaultValue != null) {
+          result[entry.key] =
+              (entry.valueConfigSpec as HeterogeneousMapConfigSpec)
+                  ._getAllDefaults(ConfigValue(path: path, value: null)) as CE;
+        }
+      }
+      if (result.containsKey(entry.key) && entry.resultOrDefault != null) {
+        // Call resultOrDefault hook for HeterogeneousMapEntry.
+        entry.resultOrDefault!.call(ConfigValue(
+            path: path, value: result[entry.key], nullRawValue: true));
+      }
+    }
+    return result.isEmpty
+        ? null
+        : o
+            .withValue(result, null)
+            .transformOrThis(transform, this.result)
+            .value;
+  }
+
+  @override
+  ConfigValue<RE> _extractNode(ConfigValue o) {
+    if (!o.checkType<Map>(log: false)) {
+      throw ConfigSpecExtractionError(o);
+    }
+
+    final inputMap = (o.value as Map);
+    final childExtracts = <dynamic, CE>{};
+
+    for (final requiredKey in requiredKeys) {
+      if (!inputMap.containsKey(requiredKey)) {
+        throw ConfigSpecExtractionError(
+            null, "Invalid config spec, missing required key - $requiredKey.");
+      }
+    }
+
+    for (final entry in entries) {
+      final path = [...o.path, entry.key.toString()];
+      if (!inputMap.containsKey(entry.key)) {
+        // No value specified, fill in with default value instead.
+        if (entry.defaultValue != null) {
+          childExtracts[entry.key] = entry.defaultValue!
+              .call(ConfigValue(path: path, value: null)) as CE;
+        } else if (entry.valueConfigSpec is HeterogeneousMapConfigSpec) {
+          final defaultValue =
+              (entry.valueConfigSpec as HeterogeneousMapConfigSpec)
+                  ._getAllDefaults(ConfigValue(path: path, value: null));
+          if (defaultValue != null) {
+            childExtracts[entry.key] = (entry.valueConfigSpec
+                    as HeterogeneousMapConfigSpec)
+                ._getAllDefaults(ConfigValue(path: path, value: null)) as CE;
+          }
+        }
+      } else {
+        // Extract value from node.
+        final configSpecNode =
+            ConfigValue(path: path, value: inputMap[entry.key]);
+        if (!entry.valueConfigSpec._validateNode(configSpecNode, log: false)) {
+          throw ConfigSpecExtractionError(configSpecNode);
+        }
+        childExtracts[entry.key] =
+            entry.valueConfigSpec._extractNode(configSpecNode).value as CE;
+      }
+
+      if (childExtracts.containsKey(entry.key) &&
+          entry.resultOrDefault != null) {
+        // Call resultOrDefault hook for HeterogeneousMapEntry.
+        entry.resultOrDefault!.call(ConfigValue(
+            path: path, value: childExtracts[entry.key], nullRawValue: true));
+      }
+    }
+
+    if (additionalProperties == AdditionalProperties.Error) {
+      for (final key in inputMap.keys) {
+        if (!allKeys.contains(key)) {
+          throw ConfigSpecExtractionError(
+              o, "Invalid ConfigSpec: additional properties not allowed.");
+        }
+      }
+    }
+
+    return o
+        .withValue(childExtracts, o.rawValue)
+        .transformOrThis(transform, result);
+  }
+
+  @override
+  Map<String, dynamic> _generateJsonSchemaNode(Map<String, dynamic> defs) {
+    return {
+      "type": "object",
+      if (additionalProperties != AdditionalProperties.Allow)
+        "additionalProperties": false,
+      if (schemaDescription != null) "description": schemaDescription!,
+      if (entries.isNotEmpty)
+        "properties": {
+          for (final kv in entries)
+            kv.key: kv.valueConfigSpec._getJsonRefOrSchemaNode(defs)
+        },
+      if (requiredKeys.isNotEmpty) "required": requiredKeys.toList(),
+    };
+  }
+}
+
+/// ConfigSpec for a Map that can have any number of keys.
+///
+/// [CE] typecasts result from keyValueConfigSpecs->{}->valueConfigSpec.
+///
+/// [RE] typecasts result returned by this node.
+class MapConfigSpec<CE extends Object?, RE extends Object?>
+    extends ConfigSpec<Map<dynamic, CE>, RE> {
+  /// Both [keyRegexp] - [valueConfigSpec] pair is used to match a set of
+  /// key-value input. Atleast one entry must match against an input for it
+  /// to be considered valid.
+  ///
+  /// Note: [keyRegexp] will be matched against key.toString()
+  final List<({String keyRegexp, ConfigSpec valueConfigSpec})>
+      keyValueConfigSpecs;
+
+  MapConfigSpec({
+    required this.keyValueConfigSpecs,
+    super.schemaDefName,
+    super.schemaDescription,
+    super.customValidation,
+    super.transform,
+    super.result,
+  });
+
+  @override
+  bool _validateNode(ConfigValue o, {bool log = true}) {
+    if (!o.checkType<Map>(log: log)) {
+      return false;
+    }
+
+    var result = true;
+    final inputMap = (o.value as Map);
+
+    for (final MapEntry(key: key, value: value) in inputMap.entries) {
+      final configSpecNode =
+          ConfigValue(path: [...o.path, key.toString()], value: value);
+      var keyValueMatch = false;
+
+      /// Running first time with no logs.
+      for (final (keyRegexp: keyRegexp, valueConfigSpec: valueConfigSpec)
+          in keyValueConfigSpecs) {
+        if (RegExp(keyRegexp, dotAll: true).hasMatch(key.toString()) &&
+            valueConfigSpec._validateNode(configSpecNode, log: false)) {
+          keyValueMatch = true;
+          break;
+        }
+      }
+      if (!keyValueMatch) {
+        result = false;
+        // No configSpec matched, running again to print logs this time.
+        if (log) {
+          _logger.severe(
+              "'${configSpecNode.pathString}' must match atleast one of the allowed key regex and configSpec.");
+          for (final (keyRegexp: keyRegexp, valueConfigSpec: valueConfigSpec)
+              in keyValueConfigSpecs) {
+            if (!RegExp(keyRegexp, dotAll: true).hasMatch(key.toString())) {
+              _logger.severe(
+                  "'${configSpecNode.pathString}' does not match regex - '$keyRegexp' (Input - $key)");
+              continue;
+            }
+            if (valueConfigSpec._validateNode(configSpecNode, log: log)) {
+              continue;
+            }
+          }
+        }
+      }
+    }
+
+    if (!result && customValidation != null) {
+      return customValidation!.call(o);
+    }
+    return result;
+  }
+
+  @override
+  ConfigValue<RE> _extractNode(ConfigValue o) {
+    if (!o.checkType<Map>(log: false)) {
+      throw ConfigSpecExtractionError(o);
+    }
+
+    final inputMap = (o.value as Map);
+    final childExtracts = <dynamic, CE>{};
+    for (final MapEntry(key: key, value: value) in inputMap.entries) {
+      final configSpecNode =
+          ConfigValue(path: [...o.path, key.toString()], value: value);
+      var keyValueMatch = false;
+      for (final (keyRegexp: keyRegexp, valueConfigSpec: valueConfigSpec)
+          in keyValueConfigSpecs) {
+        if (RegExp(keyRegexp, dotAll: true).hasMatch(key.toString()) &&
+            valueConfigSpec._validateNode(configSpecNode, log: false)) {
+          childExtracts[key] =
+              valueConfigSpec._extractNode(configSpecNode).value as CE;
+          keyValueMatch = true;
+          break;
+        }
+      }
+      if (!keyValueMatch) {
+        throw ConfigSpecExtractionError(configSpecNode);
+      }
+    }
+
+    return o
+        .withValue(childExtracts, o.rawValue)
+        .transformOrThis(transform, result);
+  }
+
+  @override
+  Map<String, dynamic> _generateJsonSchemaNode(Map<String, dynamic> defs) {
+    return {
+      "type": "object",
+      if (schemaDescription != null) "description": schemaDescription!,
+      if (keyValueConfigSpecs.isNotEmpty)
+        "patternProperties": {
+          for (final (keyRegexp: keyRegexp, valueConfigSpec: valueConfigSpec)
+              in keyValueConfigSpecs)
+            keyRegexp: valueConfigSpec._getJsonRefOrSchemaNode(defs)
+        }
+    };
+  }
+}
+
+/// ConfigSpec for a List.
+///
+/// [CE] typecasts result from [childConfigSpec].
+///
+/// [RE] typecasts result returned by this node.
+class ListConfigSpec<CE extends Object?, RE extends Object?>
+    extends ConfigSpec<List<CE>, RE> {
+  final ConfigSpec childConfigSpec;
+
+  ListConfigSpec({
+    required this.childConfigSpec,
+    super.schemaDefName,
+    super.schemaDescription,
+    super.customValidation,
+    super.transform,
+    super.result,
+  });
+
+  @override
+  bool _validateNode(ConfigValue o, {bool log = true}) {
+    if (!o.checkType<YamlList>(log: log)) {
+      return false;
+    }
+    final inputList = (o.value as YamlList).cast<dynamic>();
+    var result = true;
+    for (final (i, input) in inputList.indexed) {
+      final configSpecNode =
+          ConfigValue(path: [...o.path, "[$i]"], value: input);
+      if (!childConfigSpec._validateNode(configSpecNode, log: log)) {
+        result = false;
+        continue;
+      }
+    }
+
+    if (!result && customValidation != null) {
+      return customValidation!.call(o);
+    }
+    return result;
+  }
+
+  @override
+  ConfigValue<RE> _extractNode(ConfigValue o) {
+    if (!o.checkType<YamlList>(log: false)) {
+      throw ConfigSpecExtractionError(o);
+    }
+    final inputList = (o.value as YamlList).cast<dynamic>();
+    final childExtracts = <CE>[];
+    for (final (i, input) in inputList.indexed) {
+      final configSpecNode =
+          ConfigValue(path: [...o.path, i.toString()], value: input);
+      if (!childConfigSpec._validateNode(configSpecNode, log: false)) {
+        throw ConfigSpecExtractionError(configSpecNode);
+      }
+      childExtracts
+          .add(childConfigSpec._extractNode(configSpecNode).value as CE);
+    }
+    return o
+        .withValue(childExtracts, o.rawValue)
+        .transformOrThis(transform, result);
+  }
+
+  @override
+  Map<String, dynamic> _generateJsonSchemaNode(Map<String, dynamic> defs) {
+    return {
+      "type": "array",
+      if (schemaDescription != null) "description": schemaDescription!,
+      "items": childConfigSpec._getJsonRefOrSchemaNode(defs),
+    };
+  }
+}
+
+/// ConfigSpec for a String.
+///
+/// [RE] typecasts result returned by this node.
+class StringConfigSpec<RE extends Object?> extends ConfigSpec<String, RE> {
+  final String? pattern;
+  final RegExp? _regexp;
+
+  StringConfigSpec({
+    super.schemaDefName,
+    super.schemaDescription,
+    super.customValidation,
+    super.transform,
+    super.result,
+    this.pattern,
+  }) : _regexp = pattern == null ? null : RegExp(pattern, dotAll: true);
+
+  @override
+  bool _validateNode(ConfigValue o, {bool log = true}) {
+    if (!o.checkType<String>(log: log)) {
+      return false;
+    }
+    if (_regexp != null && !_regexp!.hasMatch(o.value as String)) {
+      if (log) {
+        _logger.severe(
+            "Expected value of key '${o.pathString}' to match pattern $pattern (Input - ${o.value}).");
+      }
+      return false;
+    }
+    if (customValidation != null) {
+      return customValidation!.call(o);
+    }
+    return true;
+  }
+
+  @override
+  ConfigValue<RE> _extractNode(ConfigValue o) {
+    if (!o.checkType<String>(log: false)) {
+      throw ConfigSpecExtractionError(o);
+    }
+    return o
+        .withValue(o.value as String, o.rawValue)
+        .transformOrThis(transform, result);
+  }
+
+  @override
+  Map<String, dynamic> _generateJsonSchemaNode(Map<String, dynamic> defs) {
+    return {
+      "type": "string",
+      if (schemaDescription != null) "description": schemaDescription!,
+      if (pattern != null) "pattern": pattern,
+    };
+  }
+}
+
+/// ConfigSpec for an Int.
+///
+/// [RE] typecasts result returned by this node.
+class IntConfigSpec<RE extends Object?> extends ConfigSpec<int, RE> {
+  IntConfigSpec({
+    super.schemaDefName,
+    super.schemaDescription,
+    super.customValidation,
+    super.transform,
+    super.result,
+  });
+
+  @override
+  bool _validateNode(ConfigValue o, {bool log = true}) {
+    if (!o.checkType<int>(log: log)) {
+      return false;
+    }
+    if (customValidation != null) {
+      return customValidation!.call(o);
+    }
+    return true;
+  }
+
+  @override
+  ConfigValue<RE> _extractNode(ConfigValue o) {
+    if (!o.checkType<int>(log: false)) {
+      throw ConfigSpecExtractionError(o);
+    }
+    return o
+        .withValue(o.value as int, o.rawValue)
+        .transformOrThis(transform, result);
+  }
+
+  @override
+  Map<String, dynamic> _generateJsonSchemaNode(Map<String, dynamic> defs) {
+    return {
+      "type": "integer",
+      if (schemaDescription != null) "description": schemaDescription!,
+    };
+  }
+}
+
+/// ConfigSpec for an object where only specific values are allowed.
+/// [CE] is the type for elements in [allowedValues].
+///
+/// [RE] typecasts result returned by this node.
+class EnumConfigSpec<CE extends Object?, RE extends Object?>
+    extends ConfigSpec<CE, RE> {
+  Set<CE> allowedValues;
+  EnumConfigSpec({
+    required this.allowedValues,
+    super.schemaDefName,
+    super.schemaDescription,
+    super.customValidation,
+    super.transform,
+    super.result,
+  });
+
+  @override
+  bool _validateNode(ConfigValue o, {bool log = true}) {
+    if (!allowedValues.contains(o.value)) {
+      if (log) {
+        _logger.severe(
+            "'${o.pathString}' must be one of the following - $allowedValues (Got ${o.value})");
+      }
+      return false;
+    }
+    if (customValidation != null) {
+      return customValidation!.call(o);
+    }
+    return true;
+  }
+
+  @override
+  ConfigValue<RE> _extractNode(ConfigValue o) {
+    if (!allowedValues.contains(o.value)) {
+      throw ConfigSpecExtractionError(o);
+    }
+    return o
+        .withValue(o.value as CE, o.rawValue)
+        .transformOrThis(transform, result);
+  }
+
+  @override
+  Map<String, dynamic> _generateJsonSchemaNode(Map<String, dynamic> defs) {
+    return {
+      "enum": allowedValues.toList(),
+      if (schemaDescription != null) "description": schemaDescription!,
+    };
+  }
+}
+
+/// ConfigSpec for a bool.
+///
+/// [RE] typecasts result returned by this node.
+class BoolConfigSpec<RE> extends ConfigSpec<bool, RE> {
+  BoolConfigSpec({
+    super.schemaDefName,
+    super.schemaDescription,
+    super.customValidation,
+    super.transform,
+    super.result,
+  });
+
+  @override
+  bool _validateNode(ConfigValue o, {bool log = true}) {
+    if (!o.checkType<bool>(log: log)) {
+      return false;
+    }
+    if (customValidation != null) {
+      return customValidation!.call(o);
+    }
+    return true;
+  }
+
+  @override
+  ConfigValue<RE> _extractNode(ConfigValue o) {
+    if (!o.checkType<bool>(log: false)) {
+      throw ConfigSpecExtractionError(o);
+    }
+    return o
+        .withValue(o.value as bool, o.rawValue)
+        .transformOrThis(transform, result);
+  }
+
+  @override
+  Map<String, dynamic> _generateJsonSchemaNode(Map<String, dynamic> defs) {
+    return {
+      "type": "boolean",
+      if (schemaDescription != null) "description": schemaDescription!,
+    };
+  }
+}
+
+/// ConfigSpec that requires atleast one underlying match.
+///
+/// [TE] typecasts the result returned by the the first valid [childConfigSpecs].
+///
+/// [RE] typecasts result returned by this node.
+class OneOfConfigSpec<TE extends Object?, RE extends Object?>
+    extends ConfigSpec<TE, RE> {
+  final List<ConfigSpec> childConfigSpecs;
+
+  OneOfConfigSpec({
+    required this.childConfigSpecs,
+    super.schemaDefName,
+    super.schemaDescription,
+    super.customValidation,
+    super.transform,
+    super.result,
+  });
+
+  @override
+  bool _validateNode(ConfigValue o, {bool log = true}) {
+    // Running first time with no logs.
+    for (final spec in childConfigSpecs) {
+      if (spec._validateNode(o, log: false)) {
+        if (customValidation != null) {
+          return customValidation!.call(o);
+        }
+        return true;
+      }
+    }
+    // No configSpec matched, running again to print logs this time.
+    if (log) {
+      _logger.severe(
+          "'${o.pathString}' must match atleast one of the allowed configSpec -");
+      for (final spec in childConfigSpecs) {
+        spec._validateNode(o, log: log);
+      }
+    }
+    return false;
+  }
+
+  @override
+  ConfigValue<RE> _extractNode(ConfigValue o) {
+    for (final spec in childConfigSpecs) {
+      if (spec._validateNode(o, log: false)) {
+        return o
+            .withValue(spec._extractNode(o).value as TE, o.rawValue)
+            .transformOrThis(transform, result);
+      }
+    }
+    throw ConfigSpecExtractionError(o);
+  }
+
+  @override
+  Map<String, dynamic> _generateJsonSchemaNode(Map<String, dynamic> defs) {
+    return {
+      if (schemaDescription != null) "description": schemaDescription!,
+      r"$oneOf": childConfigSpecs
+          .map((child) => child._getJsonRefOrSchemaNode(defs))
+          .toList(),
+    };
+  }
+}
diff --git a/pkgs/ffigen/lib/src/config_provider/config_types.dart b/pkgs/ffigen/lib/src/config_provider/config_types.dart
index 8521bdb..75d5fca 100644
--- a/pkgs/ffigen/lib/src/config_provider/config_types.dart
+++ b/pkgs/ffigen/lib/src/config_provider/config_types.dart
@@ -5,6 +5,7 @@
 /// Contains all the neccesary classes required by config.
 import 'dart:io';
 
+import 'package:ffigen/src/code_generator.dart';
 import 'package:quiver/pattern.dart' as quiver;
 
 import 'path_finder.dart';
@@ -63,28 +64,6 @@
   }
 }
 
-/// Represents a single specification in configurations.
-///
-/// [E] is the return type of the extractedResult.
-class Specification<E> {
-  final bool Function(List<String> name, dynamic value) validator;
-  final E Function(dynamic map) extractor;
-  final E Function()? defaultValue;
-
-  final Requirement requirement;
-  final void Function(dynamic result) extractedResult;
-
-  Specification({
-    required this.extractedResult,
-    required this.validator,
-    required this.extractor,
-    this.defaultValue,
-    this.requirement = Requirement.no,
-  });
-}
-
-enum Requirement { yes, prefer, no }
-
 // Holds headers and filters for header.
 class Headers {
   /// Path to headers.
@@ -423,3 +402,17 @@
 
   OutputConfig(this.output, this.symbolFile);
 }
+
+class RawVarArgFunction {
+  String? postfix;
+  final List<String> rawTypeStrings;
+
+  RawVarArgFunction(this.postfix, this.rawTypeStrings);
+}
+
+class VarArgFunction {
+  final String postfix;
+  final List<Type> types;
+
+  VarArgFunction(this.postfix, this.types);
+}
diff --git a/pkgs/ffigen/lib/src/config_provider/spec_utils.dart b/pkgs/ffigen/lib/src/config_provider/spec_utils.dart
index dfe6bbf..ff4e1b7 100644
--- a/pkgs/ffigen/lib/src/config_provider/spec_utils.dart
+++ b/pkgs/ffigen/lib/src/config_provider/spec_utils.dart
@@ -6,6 +6,7 @@
 
 import 'package:ffigen/src/code_generator.dart';
 import 'package:ffigen/src/code_generator/utils.dart';
+import 'package:ffigen/src/header_parser/type_extractor/cxtypekindmap.dart';
 import 'package:file/local.dart';
 import 'package:glob/glob.dart';
 import 'package:logging/logging.dart';
@@ -38,115 +39,18 @@
       skipNormalization ? path : p.join(p.dirname(configFilename), path));
 }
 
-/// Checks if type of value is [T], logs an error if it's not.
-///
-/// [key] is printed as `'item1 -> item2 => item3'` in log message.
-bool checkType<T>(List<String> keys, dynamic value) {
-  if (value is! T) {
-    _logger.severe(
-        "Expected value of key '${keys.join(' -> ')}' to be of type '$T'.");
-    return false;
-  }
-  return true;
-}
-
-/// Checks if there are nested [key] in [map].
-bool checkKeyInYaml(List<String> key, YamlMap map) {
-  dynamic last = map;
-  for (final k in key) {
-    if (last is YamlMap) {
-      if (!last.containsKey(k)) return false;
-      last = last[k];
-    } else {
-      return false;
-    }
-  }
-  // The entry for the last key may be null.
-  return true;
-}
-
-/// Extracts value of nested [key] from [map].
-dynamic getKeyValueFromYaml(List<String> key, YamlMap map) {
-  if (checkKeyInYaml(key, map)) {
-    dynamic last = map;
-    for (final k in key) {
-      last = last[k];
-    }
-    return last;
-  }
-
-  return null;
-}
-
-/// Recursively checks the keys in [configKeyMap] from [allowedKeyList].
-void warnUnknownKeys(List<List<String>> allowedKeyList, YamlMap configKeyMap) {
-  final allowedKeyMap = <String, dynamic>{};
-  for (final specKeys in allowedKeyList) {
-    var item = allowedKeyMap;
-    for (final specSubKey in specKeys) {
-      item.putIfAbsent(specSubKey, () => <String, dynamic>{});
-      item = item[specSubKey] as Map<String, dynamic>;
-    }
-    // Add empty key to mark that any sub-keys of this key are allowed.
-    item[''] = <String, dynamic>{};
-  }
-  _warnUnknownKeysInMap(allowedKeyMap, configKeyMap, <dynamic>[]);
-}
-
-/// Recursive function to check a key set in a configKeyMap.
-void _warnUnknownKeysInMap(Map<String, dynamic> allowedKeyMap,
-    dynamic configKeyMap, List<dynamic> prev) {
-  if (allowedKeyMap.containsKey('') || configKeyMap is! YamlMap) {
-    return;
-  }
-  for (final key in configKeyMap.keys) {
-    if (allowedKeyMap.containsKey(key)) {
-      prev.add(key);
-      _warnUnknownKeysInMap(
-          allowedKeyMap[key] as Map<String, dynamic>, configKeyMap[key], prev);
-      prev.removeLast();
-    } else {
-      prev.add(key);
-      _logger.warning('Unknown key - ${prev.join(' -> ')}.');
-      prev.removeLast();
-    }
-  }
-}
-
-bool booleanExtractor(dynamic value) => value as bool;
-
-bool booleanValidator(List<String> name, dynamic value) =>
-    checkType<bool>(name, value);
-
-Map<String, LibraryImport> libraryImportsExtractor(dynamic yamlConfig) {
+Map<String, LibraryImport> libraryImportsExtractor(
+    Map<String, String>? typeMap) {
   final resultMap = <String, LibraryImport>{};
-  final typeMap = yamlConfig as YamlMap?;
   if (typeMap != null) {
     for (final typeName in typeMap.keys) {
-      resultMap[typeName as String] =
+      resultMap[typeName] =
           LibraryImport(typeName, typeMap[typeName] as String);
     }
   }
   return resultMap;
 }
 
-bool libraryImportsValidator(List<String> name, dynamic yamlConfig) {
-  if (!checkType<YamlMap>(name, yamlConfig)) {
-    return false;
-  }
-  for (final key in (yamlConfig as YamlMap).keys) {
-    if (!checkType<String>([...name, key as String], yamlConfig[key])) {
-      return false;
-    }
-    if (strings.predefinedLibraryImports.containsKey(key)) {
-      _logger.severe(
-          'library-import -> $key should not collide with any predefined imports - ${strings.predefinedLibraryImports.keys}.');
-      return false;
-    }
-  }
-  return true;
-}
-
 void loadImportedTypes(YamlMap fileConfig,
     Map<String, ImportedType> usrTypeMappings, LibraryImport libraryImport) {
   final symbols = fileConfig['symbols'] as YamlMap;
@@ -168,18 +72,14 @@
 }
 
 Map<String, ImportedType> symbolFileImportExtractor(
-    dynamic yamlConfig,
+    List<String> yamlConfig,
     Map<String, LibraryImport> libraryImports,
     String? configFileName,
     PackageConfig? packageConfig) {
   final resultMap = <String, ImportedType>{};
-  for (final item in (yamlConfig as YamlList)) {
+  for (final item in yamlConfig) {
     String symbolFilePath;
-    if (item is String) {
-      symbolFilePath = item;
-    } else {
-      symbolFilePath = item[strings.symbolFile] as String;
-    }
+    symbolFilePath = item;
     final symbolFile =
         loadSymbolFile(symbolFilePath, configFileName, packageConfig);
     final formatVersion = symbolFile[strings.formatVersion] as String;
@@ -209,44 +109,13 @@
   return resultMap;
 }
 
-bool symbolFileImportValidator(List<String> name, dynamic yamlConfig) {
-  if (!checkType<YamlList>(name, yamlConfig)) {
-    return false;
-  }
-  var result = true;
-  (yamlConfig as YamlList).asMap().forEach((idx, value) {
-    if (value is YamlMap) {
-      if (!value.keys.contains(strings.symbolFile)) {
-        result = false;
-        _logger
-            .severe('Key $name -> $idx -> ${strings.symbolFile} is required.');
-      }
-      for (final key in value.keys) {
-        if (key == strings.symbolFile) {
-          if (!checkType<String>(
-              [...name, idx.toString(), key as String], value[key])) {
-            result = false;
-          }
-        } else {
-          result = false;
-          _logger.severe('Unknown key : $name -> $idx -> $key.');
-        }
-      }
-    } else if (value is! String) {
-      result = false;
-      _logger.severe('Expected $name -> $idx should be a String or Map.');
-    }
-  });
-  return result;
-}
-
-Map<String, List<String>> typeMapExtractor(dynamic yamlConfig) {
+Map<String, List<String>> typeMapExtractor(Map<dynamic, dynamic>? yamlConfig) {
   // Key - type_name, Value - [lib, cType, dartType].
   final resultMap = <String, List<String>>{};
-  final typeMap = yamlConfig as YamlMap?;
+  final typeMap = yamlConfig;
   if (typeMap != null) {
     for (final typeName in typeMap.keys) {
-      final typeConfigItem = typeMap[typeName] as YamlMap;
+      final typeConfigItem = typeMap[typeName] as Map;
       resultMap[typeName as String] = [
         typeConfigItem[strings.lib] as String,
         typeConfigItem[strings.cType] as String,
@@ -257,57 +126,6 @@
   return resultMap;
 }
 
-bool typeMapValidator(List<String> name, dynamic yamlConfig) {
-  if (!checkType<YamlMap>(name, yamlConfig)) {
-    return false;
-  }
-  var result = true;
-  for (final key in (yamlConfig as YamlMap).keys) {
-    if (!checkType<YamlMap>([...name, key as String], yamlConfig[key])) {
-      return false;
-    }
-    final lib = (yamlConfig[key] as YamlMap).containsKey(strings.lib);
-    if (!lib) {
-      _logger.severe("Key '${strings.lib}' in $name -> $key is required.");
-      result = false;
-    }
-    final cType = (yamlConfig[key] as YamlMap).containsKey(strings.cType);
-    if (!cType) {
-      _logger.severe("Key '${strings.cType}' in $name -> $key is required.");
-      result = false;
-    }
-    final dartType = (yamlConfig[key] as YamlMap).containsKey(strings.dartType);
-    if (!dartType) {
-      _logger.severe("Key '${strings.dartType}' in $name -> $key is required.");
-      result = false;
-    }
-  }
-  return result;
-}
-
-Map<String, String> stringStringMapExtractor(dynamic yamlConfig) {
-  final resultMap = <String, String>{};
-  final inputMap = yamlConfig as YamlMap?;
-  if (inputMap != null) {
-    for (final key in inputMap.keys) {
-      resultMap[key as String] = inputMap[key] as String;
-    }
-  }
-  return resultMap;
-}
-
-bool stringStringMapValidator(List<String> name, dynamic yamlConfig) {
-  if (!checkType<YamlMap>(name, yamlConfig)) {
-    return false;
-  }
-  for (final key in (yamlConfig as YamlMap).keys) {
-    if (!checkType<String>([...name, key as String], yamlConfig[key])) {
-      return false;
-    }
-  }
-  return true;
-}
-
 Map<String, ImportedType> makeImportTypeMapping(
     Map<String, List<String>> rawTypeMappings,
     Map<String, LibraryImport> libraryImportsMap) {
@@ -329,6 +147,99 @@
   return typeMappings;
 }
 
+Type makePointerToType(Type type, int pointerCount) {
+  for (var i = 0; i < pointerCount; i++) {
+    type = PointerType(type);
+  }
+  return type;
+}
+
+String makePostfixFromRawVarArgType(List<String> rawVarArgType) {
+  return rawVarArgType
+      .map((e) => e
+          .replaceAll('*', 'Ptr')
+          .replaceAll(RegExp(r'_t$'), '')
+          .replaceAll(' ', '')
+          .replaceAll(RegExp('[^A-Za-z0-9_]'), ''))
+      .map((e) => e.length > 1 ? '${e[0].toUpperCase()}${e.substring(1)}' : e)
+      .join('');
+}
+
+Type makeTypeFromRawVarArgType(
+    String rawVarArgType, Map<String, LibraryImport> libraryImportsMap) {
+  Type baseType;
+  var rawBaseType = rawVarArgType.trim();
+  // Split the raw type based on pointer usage. E.g -
+  // int => [int]
+  // char* => [char,*]
+  // ffi.Hello ** => [ffi.Hello,**]
+  final typeStringRegexp = RegExp(r'([a-zA-Z0-9_\s\.]+)(\**)$');
+  if (!typeStringRegexp.hasMatch(rawBaseType)) {
+    throw Exception('Cannot parse variadic argument type - $rawVarArgType.');
+  }
+  final regExpMatch = typeStringRegexp.firstMatch(rawBaseType)!;
+  final groups = regExpMatch.groups([1, 2]);
+  rawBaseType = groups[0]!;
+  // Handle basic supported types.
+  if (cxTypeKindToImportedTypes.containsKey(rawBaseType)) {
+    baseType = cxTypeKindToImportedTypes[rawBaseType]!;
+  } else if (supportedTypedefToImportedType.containsKey(rawBaseType)) {
+    baseType = supportedTypedefToImportedType[rawBaseType]!;
+  } else if (suportedTypedefToSuportedNativeType.containsKey(rawBaseType)) {
+    baseType = NativeType(suportedTypedefToSuportedNativeType[rawBaseType]!);
+  } else {
+    // Use library import if specified (E.g - ffi.UintPtr or custom.MyStruct)
+    final rawVarArgTypeSplit = rawBaseType.split('.');
+    if (rawVarArgTypeSplit.length == 1) {
+      final typeName = rawVarArgTypeSplit[0].replaceAll(' ', '');
+      baseType = SelfImportedType(typeName, typeName);
+    } else if (rawVarArgTypeSplit.length == 2) {
+      final lib = rawVarArgTypeSplit[0];
+      final libraryImport = strings.predefinedLibraryImports[lib] ??
+          libraryImportsMap[rawVarArgTypeSplit[0]];
+      if (libraryImport == null) {
+        throw Exception('Please declare $lib in library-imports.');
+      }
+      final typeName = rawVarArgTypeSplit[1].replaceAll(' ', '');
+      baseType = ImportedType(libraryImport, typeName, typeName);
+    } else {
+      throw Exception(
+          'Invalid type $rawVarArgType : Expected 0 or 1 .(dot) separators.');
+    }
+  }
+
+  // Handle pointers
+  final pointerCount = groups[1]!.length;
+  return makePointerToType(baseType, pointerCount);
+}
+
+Map<String, List<VarArgFunction>> makeVarArgFunctionsMapping(
+    Map<String, List<RawVarArgFunction>> rawVarArgMappings,
+    Map<String, LibraryImport> libraryImportsMap) {
+  final mappings = <String, List<VarArgFunction>>{};
+  for (final key in rawVarArgMappings.keys) {
+    final varArgList = <VarArgFunction>[];
+    for (final rawVarArg in rawVarArgMappings[key]!) {
+      var postfix = rawVarArg.postfix ?? '';
+      final types = <Type>[];
+      for (final rva in rawVarArg.rawTypeStrings) {
+        types.add(makeTypeFromRawVarArgType(rva, libraryImportsMap));
+      }
+      if (postfix.isEmpty) {
+        if (rawVarArgMappings[key]!.length == 1) {
+          postfix = '';
+        } else {
+          postfix = makePostfixFromRawVarArgType(rawVarArg.rawTypeStrings);
+        }
+      }
+      // Extract postfix from config and/or deduce from var names.
+      varArgList.add(VarArgFunction(postfix, types));
+    }
+    mappings[key] = varArgList;
+  }
+  return mappings;
+}
+
 final _quoteMatcher = RegExp(r'''^["'](.*)["']$''', dotAll: true);
 final _cmdlineArgMatcher = RegExp(r'''['"](\\"|[^"])*?['"]|[^ ]+''');
 List<String> compilerOptsToList(String compilerOpts) {
@@ -346,77 +257,22 @@
   return list;
 }
 
-List<String> compilerOptsExtractor(dynamic value) {
-  if (value is String) {
-    return compilerOptsToList(value);
-  }
-
+List<String> compilerOptsExtractor(List<String> value) {
   final list = <String>[];
-  for (final el in (value as YamlList)) {
-    if (el is String) {
-      list.addAll(compilerOptsToList(el));
-    }
+  for (final el in (value)) {
+    list.addAll(compilerOptsToList(el));
   }
   return list;
 }
 
-bool compilerOptsValidator(List<String> name, dynamic value) {
-  if (value is String || value is YamlList) {
-    return true;
-  } else {
-    _logger.severe('Expected $name to be a String or List of String.');
-    return false;
-  }
-}
-
-CompilerOptsAuto compilerOptsAutoExtractor(dynamic value) {
-  return CompilerOptsAuto(
-    macIncludeStdLib: getKeyValueFromYaml(
-      [strings.macos, strings.includeCStdLib],
-      value as YamlMap,
-    ) as bool?,
-  );
-}
-
-bool compilerOptsAutoValidator(List<String> name, dynamic value) {
-  var result = true;
-
-  if (!checkType<YamlMap>(name, value)) {
-    return false;
-  }
-
-  for (final oskey in (value as YamlMap).keys) {
-    if (oskey == strings.macos) {
-      if (!checkType<YamlMap>([...name, oskey as String], value[oskey])) {
-        return false;
-      }
-
-      for (final inckey in (value[oskey] as YamlMap).keys) {
-        if (inckey == strings.includeCStdLib) {
-          if (!checkType<bool>(
-              [...name, oskey, inckey as String], value[oskey][inckey])) {
-            result = false;
-          }
-        } else {
-          _logger.severe("Unknown key '$inckey' in '$name -> $oskey.");
-          result = false;
-        }
-      }
-    } else {
-      _logger.severe("Unknown key '$oskey' in '$name'.");
-      result = false;
-    }
-  }
-  return result;
-}
-
-Headers headersExtractor(dynamic yamlConfig, String? configFilename) {
+Headers headersExtractor(
+    Map<dynamic, List<String>> yamlConfig, String? configFilename) {
   final entryPoints = <String>[];
   final includeGlobs = <quiver.Glob>[];
-  for (final key in (yamlConfig as YamlMap).keys) {
+  for (final key in yamlConfig.keys) {
     if (key == strings.entryPoints) {
-      for (final h in (yamlConfig[key] as YamlList)) {
-        final headerGlob = _normalizePath(h as String, configFilename);
+      for (final h in (yamlConfig[key]!)) {
+        final headerGlob = _normalizePath(h, configFilename);
         // Add file directly to header if it's not a Glob but a File.
         if (File(headerGlob).existsSync()) {
           final osSpecificPath = headerGlob;
@@ -434,8 +290,8 @@
       }
     }
     if (key == strings.includeDirectives) {
-      for (final h in (yamlConfig[key] as YamlList)) {
-        final headerGlob = h as String;
+      for (final h in yamlConfig[key]!) {
+        final headerGlob = h;
         final fixedGlob = _normalizePath(headerGlob, configFilename);
         includeGlobs.add(quiver.Glob(fixedGlob));
       }
@@ -449,28 +305,6 @@
   );
 }
 
-bool headersValidator(List<String> name, dynamic value) {
-  if (!checkType<YamlMap>(name, value)) {
-    return false;
-  }
-  if (!(value as YamlMap).containsKey(strings.entryPoints)) {
-    _logger.severe("Required '$name -> ${strings.entryPoints}'.");
-    return false;
-  } else {
-    for (final key in value.keys) {
-      if (key == strings.entryPoints || key == strings.includeDirectives) {
-        if (!checkType<YamlList>([...name, key as String], value[key])) {
-          return false;
-        }
-      } else {
-        _logger.severe("Unknown key '$key' in '$name'.");
-        return false;
-      }
-    }
-    return true;
-  }
-}
-
 /// Returns location of dynamic library by searching default locations. Logs
 /// error and throws an Exception if not found.
 String findDylibAtDefaultLocations() {
@@ -546,10 +380,9 @@
   }
 }
 
-String llvmPathExtractor(dynamic value) {
+String llvmPathExtractor(List<String> value) {
   // Extract libclang's dylib from user specified paths.
-  for (final path in (value as YamlList)) {
-    if (path is! String) continue;
+  for (final path in value) {
     final dylibPath =
         findLibclangDylib(p.join(path, strings.dynamicLibParentName));
     if (dylibPath != null) {
@@ -578,19 +411,12 @@
   }
 }
 
-bool llvmPathValidator(List<String> name, dynamic value) {
-  if (!checkType<YamlList>(name, value)) {
-    return false;
-  }
-  return true;
-}
-
 OutputConfig outputExtractor(
     dynamic value, String? configFilename, PackageConfig? packageConfig) {
   if (value is String) {
     return OutputConfig(_normalizePath(value, configFilename), null);
   }
-  value = value as YamlMap;
+  value = value as Map;
   return OutputConfig(
     _normalizePath((value)[strings.bindings] as String, configFilename),
     value.containsKey(strings.symbolFile)
@@ -600,36 +426,9 @@
   );
 }
 
-bool outputValidator(List<String> name, dynamic value) {
-  if (value is String) {
-    return true;
-  } else if (value is YamlMap) {
-    final keys = value.keys;
-    var result = true;
-    for (final key in keys) {
-      if (key == strings.bindings) {
-        if (!checkType<String>([...name, key as String], value[key])) {
-          result = false;
-        }
-      } else if (key == strings.symbolFile) {
-        result = symbolFileOutputValidator(
-            [...name, strings.symbolFile], value[key]);
-      } else {
-        result = false;
-        _logger.severe("Unknown key '$key' in '$name'.");
-      }
-    }
-    return result;
-  } else {
-    _logger.severe(
-        "Expected value of key '${name.join(' -> ')}' to be a String or Map.");
-    return false;
-  }
-}
-
 SymbolFile symbolFileOutputExtractor(
     dynamic value, String? configFilename, PackageConfig? packageConfig) {
-  value = value as YamlMap;
+  value = value as Map;
   var output = value[strings.output] as String;
   if (Uri.parse(output).scheme != "package") {
     _logger.warning(
@@ -646,71 +445,19 @@
   return SymbolFile(importPath, output);
 }
 
-bool symbolFileOutputValidator(List<String> name, dynamic value) {
-  if (!checkType<YamlMap>(name, value)) {
-    return false;
-  }
-  if (!(value as YamlMap).containsKey(strings.output)) {
-    _logger.severe("Required '$name -> ${strings.output}'.");
-    return false;
-  }
-  if (!(value).containsKey(strings.importPath)) {
-    _logger.severe("Required '$name -> ${strings.importPath}'.");
-    return false;
-  }
-  for (final key in value.keys) {
-    if (key == strings.output || key == strings.importPath) {
-      if (!checkType<String>([...name, key as String], value[key])) {
-        return false;
-      }
-    } else {
-      _logger.severe("Unknown key '$key' in '$name'.");
-      return false;
-    }
-  }
-  return true;
-}
-
-Language languageExtractor(dynamic value) {
-  if (value == strings.langC) {
-    return Language.c;
-  } else if (value == strings.langObjC) {
-    return Language.objc;
-  }
-  return Language.c;
-}
-
-bool languageValidator(List<String> name, dynamic value) {
-  if (value is String) {
-    if (value == strings.langC) {
-      return true;
-    }
-    if (value == strings.langObjC) {
-      _logger.severe('Objective C support is EXPERIMENTAL. The API may change '
-          'in a breaking way without notice.');
-      return true;
-    }
-    _logger.severe("'$name' must be one of the following - "
-        "{${strings.langC}, ${strings.langObjC}}");
-    return false;
-  }
-  _logger.severe("Expected value of key '$name' to be a String.");
-  return false;
-}
-
 /// Returns true if [str] is not a full name.
 ///
 /// E.g `abc` is a full name, `abc.*` is not.
 bool isFullDeclarationName(String str) =>
     quiver.matchesFull(RegExp('[a-zA-Z_0-9]*'), str);
 
-Includer _extractIncluderFromYaml(dynamic yamlMap) {
+Includer extractIncluderFromYaml(Map<dynamic, dynamic> yamlMap) {
   final includeMatchers = <RegExp>[],
       includeFull = <String>{},
       excludeMatchers = <RegExp>[],
       excludeFull = <String>{};
 
-  final include = (yamlMap[strings.include] as YamlList?)?.cast<String>();
+  final include = yamlMap[strings.include] as List<String>?;
   if (include != null) {
     if (include.isEmpty) {
       return Includer.excludeByDefault();
@@ -724,7 +471,7 @@
     }
   }
 
-  final exclude = (yamlMap[strings.exclude] as YamlList?)?.cast<String>();
+  final exclude = yamlMap[strings.exclude] as List<String>?;
   if (exclude != null) {
     for (final str in exclude) {
       if (isFullDeclarationName(str)) {
@@ -743,23 +490,43 @@
   );
 }
 
-Declaration declarationConfigExtractor(dynamic yamlMap) {
+Map<String, List<RawVarArgFunction>> varArgFunctionConfigExtractor(
+    Map<dynamic, dynamic> yamlMap) {
+  final result = <String, List<RawVarArgFunction>>{};
+  final configMap = yamlMap;
+  for (final key in configMap.keys) {
+    final List<RawVarArgFunction> vafuncs = [];
+    for (final rawVaFunc in (configMap[key] as List)) {
+      if (rawVaFunc is List) {
+        vafuncs.add(RawVarArgFunction(null, rawVaFunc.cast()));
+      } else if (rawVaFunc is Map) {
+        vafuncs.add(RawVarArgFunction(rawVaFunc[strings.postfix] as String?,
+            (rawVaFunc[strings.types] as List).cast()));
+      } else {
+        throw Exception("Unexpected type in variadic-argument config.");
+      }
+    }
+    result[key as String] = vafuncs;
+  }
+
+  return result;
+}
+
+Declaration declarationConfigExtractor(Map<dynamic, dynamic> yamlMap) {
   final renamePatterns = <RegExpRenamer>[];
   final renameFull = <String, String>{};
   final memberRenamePatterns = <RegExpMemberRenamer>[];
   final memberRenamerFull = <String, Renamer>{};
 
-  final includer = _extractIncluderFromYaml(yamlMap);
+  final includer = extractIncluderFromYaml(yamlMap);
 
-  Includer? symbolIncluder;
-  if (yamlMap[strings.symbolAddress] != null) {
-    symbolIncluder = _extractIncluderFromYaml(yamlMap[strings.symbolAddress]);
-  }
+  final symbolIncluder = yamlMap[strings.symbolAddress] as Includer?;
 
-  final rename = (yamlMap[strings.rename] as YamlMap?)?.cast<String, String>();
+  final rename = yamlMap[strings.rename] as Map<dynamic, String>?;
 
   if (rename != null) {
-    for (final str in rename.keys) {
+    for (final key in rename.keys) {
+      final str = key.toString();
       if (isFullDeclarationName(str)) {
         renameFull[str] = rename[str]!;
       } else {
@@ -770,20 +537,22 @@
   }
 
   final memberRename =
-      (yamlMap[strings.memberRename] as YamlMap?)?.cast<String, YamlMap>();
+      yamlMap[strings.memberRename] as Map<dynamic, Map<dynamic, String>>?;
 
   if (memberRename != null) {
-    for (final decl in memberRename.keys) {
+    for (final key in memberRename.keys) {
+      final decl = key.toString();
       final renamePatterns = <RegExpRenamer>[];
       final renameFull = <String, String>{};
 
-      final memberRenameMap = memberRename[decl]!.cast<String, String>();
+      final memberRenameMap = memberRename[decl]!;
       for (final member in memberRenameMap.keys) {
-        if (isFullDeclarationName(member)) {
-          renameFull[member] = memberRenameMap[member]!;
+        final memberStr = member.toString();
+        if (isFullDeclarationName(memberStr)) {
+          renameFull[memberStr] = memberRenameMap[member]!;
         } else {
           renamePatterns.add(RegExpRenamer(
-              RegExp(member, dotAll: true), memberRenameMap[member]!));
+              RegExp(memberStr, dotAll: true), memberRenameMap[member]!));
         }
       }
       if (isFullDeclarationName(decl)) {
@@ -819,294 +588,20 @@
   );
 }
 
-bool declarationConfigValidator(List<String> name, dynamic value) {
-  var result = true;
-  if (value is YamlMap) {
-    for (final key in value.keys) {
-      if (key == strings.include || key == strings.exclude) {
-        if (!checkType<YamlList>([...name, key as String], value[key])) {
-          result = false;
-        }
-      } else if (key == strings.rename) {
-        if (!checkType<YamlMap>([...name, key as String], value[key])) {
-          result = false;
-        } else {
-          for (final subkey in (value[key] as YamlMap).keys) {
-            if (!checkType<String>(
-                [...name, key, subkey as String], value[key][subkey])) {
-              result = false;
-            }
-          }
-        }
-      } else if (key == strings.memberRename) {
-        if (!checkType<YamlMap>([...name, key as String], value[key])) {
-          result = false;
-        } else {
-          for (final declNameKey in (value[key] as YamlMap).keys) {
-            if (!checkType<YamlMap>([...name, key, declNameKey as String],
-                value[key][declNameKey])) {
-              result = false;
-            } else {
-              for (final memberNameKey
-                  in ((value[key] as YamlMap)[declNameKey] as YamlMap).keys) {
-                if (!checkType<String>([
-                  ...name,
-                  key,
-                  declNameKey,
-                  memberNameKey as String,
-                ], value[key][declNameKey][memberNameKey])) {
-                  result = false;
-                }
-              }
-            }
-          }
-        }
-      } else if (key == strings.symbolAddress) {
-        if (!checkType<YamlMap>([...name, key as String], value[key])) {
-          result = false;
-        } else {
-          for (final subkey in (value[key] as YamlMap).keys) {
-            if (subkey == strings.include || subkey == strings.exclude) {
-              if (!checkType<YamlList>(
-                  [...name, key, subkey as String], value[key][subkey])) {
-                result = false;
-              }
-            } else {
-              _logger.severe("Unknown key '$subkey' in '$name -> $key'.");
-              result = false;
-            }
-          }
-        }
-      }
-    }
-  } else {
-    _logger.severe("Expected value '$name' to be a Map.");
-    result = false;
-  }
-  return result;
-}
-
-Includer exposeFunctionTypeExtractor(dynamic value) =>
-    _extractIncluderFromYaml(value);
-
-bool exposeFunctionTypeValidator(List<String> name, dynamic value) {
-  var result = true;
-
-  if (!checkType<YamlMap>(name, value)) {
-    result = false;
-  } else {
-    final mp = value as YamlMap;
-    for (final key in mp.keys) {
-      if (key == strings.include || key == strings.exclude) {
-        if (!checkType<YamlList>([...name, key as String], value[key])) {
-          result = false;
-        }
-      } else {
-        _logger.severe("Unknown subkey '$key' in '$name'.");
-        result = false;
-      }
-    }
-  }
-
-  return result;
-}
-
-Includer leafFunctionExtractor(dynamic value) =>
-    _extractIncluderFromYaml(value);
-
-bool leafFunctionValidator(List<String> name, dynamic value) {
-  var result = true;
-
-  if (!checkType<YamlMap>(name, value)) {
-    result = false;
-  } else {
-    final mp = value as YamlMap;
-    for (final key in mp.keys) {
-      if (key == strings.include || key == strings.exclude) {
-        if (!checkType<YamlList>([...name, key as String], value[key])) {
-          result = false;
-        }
-      } else {
-        _logger.severe("Unknown subkey '$key' in '$name'.");
-        result = false;
-      }
-    }
-  }
-
-  return result;
-}
-
-SupportedNativeType nativeSupportedType(int value, {bool signed = true}) {
-  switch (value) {
-    case 1:
-      return signed ? SupportedNativeType.Int8 : SupportedNativeType.Uint8;
-    case 2:
-      return signed ? SupportedNativeType.Int16 : SupportedNativeType.Uint16;
-    case 4:
-      return signed ? SupportedNativeType.Int32 : SupportedNativeType.Uint32;
-    case 8:
-      return signed ? SupportedNativeType.Int64 : SupportedNativeType.Uint64;
-    default:
-      throw Exception(
-          'Unsupported value given to sizemap, Allowed values for sizes are: 1, 2, 4, 8');
-  }
-}
-
-String stringExtractor(dynamic value) => value as String;
-
-bool nonEmptyStringValidator(List<String> name, dynamic value) {
-  if (value is String && value.isNotEmpty) {
-    return true;
-  } else {
-    _logger.severe("Expected value of key '$name' to be a non-empty String.");
-    return false;
-  }
-}
-
-bool dartClassNameValidator(List<String> name, dynamic value) {
-  if (value is String &&
-      quiver.matchesFull(RegExp('[a-zA-Z]+[_a-zA-Z0-9]*'), value)) {
-    return true;
-  } else {
-    _logger.severe(
-        "Expected value of key '$name' to be a valid public class name.");
-    return false;
-  }
-}
-
-CommentType commentExtractor(dynamic value) {
-  if (value is bool) {
-    if (value) {
-      return CommentType.def();
-    } else {
-      return CommentType.none();
-    }
-  }
-  final ct = CommentType.def();
-  if (value is YamlMap) {
-    for (final key in value.keys) {
-      if (key == strings.style) {
-        if (value[key] == strings.any) {
-          ct.style = CommentStyle.any;
-        } else if (value[key] == strings.doxygen) {
-          ct.style = CommentStyle.doxygen;
-        }
-      } else if (key == strings.length) {
-        if (value[key] == strings.full) {
-          ct.length = CommentLength.full;
-        } else if (value[key] == strings.brief) {
-          ct.length = CommentLength.brief;
-        }
-      }
-    }
-  }
-  return ct;
-}
-
-bool commentValidator(List<String> name, dynamic value) {
-  if (value is bool) {
-    return true;
-  } else if (value is YamlMap) {
-    var result = true;
-    for (final key in value.keys) {
-      if (key == strings.style) {
-        if (value[key] is! String ||
-            !(value[key] == strings.doxygen || value[key] == strings.any)) {
-          _logger.severe(
-              "'$name'>'${strings.style}' must be one of the following - {${strings.doxygen}, ${strings.any}}");
-          result = false;
-        }
-      } else if (key == strings.length) {
-        if (value[key] is! String ||
-            !(value[key] == strings.brief || value[key] == strings.full)) {
-          _logger.severe(
-              "'$name'>'${strings.length}' must be one of the following - {${strings.brief}, ${strings.full}}");
-          result = false;
-        }
-      } else {
-        _logger.severe("Unknown key '$key' in '$name'.");
-        result = false;
-      }
-    }
-    return result;
-  } else {
-    _logger.severe("Expected value of key '$name' to be a bool or a Map.");
-    return false;
-  }
-}
-
-CompoundDependencies dependencyOnlyExtractor(dynamic value) {
-  var result = CompoundDependencies.full;
-  if (value == strings.opaqueCompoundDependencies) {
-    result = CompoundDependencies.opaque;
-  }
-  return result;
-}
-
-bool dependencyOnlyValidator(List<String> name, dynamic value) {
-  var result = true;
-  if (value is! String ||
-      !(value == strings.fullCompoundDependencies ||
-          value == strings.opaqueCompoundDependencies)) {
-    _logger.severe(
-        "'$name' must be one of the following - {${strings.fullCompoundDependencies}, ${strings.opaqueCompoundDependencies}}");
-    result = false;
-  }
-  return result;
-}
-
-StructPackingOverride structPackingOverrideExtractor(dynamic value) {
+StructPackingOverride structPackingOverrideExtractor(
+    Map<dynamic, dynamic> value) {
   final matcherMap = <RegExp, int?>{};
-  for (final key in (value as YamlMap).keys) {
+  for (final key in value.keys) {
     matcherMap[RegExp(key as String, dotAll: true)] =
         strings.packingValuesMap[value[key]];
   }
   return StructPackingOverride(matcherMap: matcherMap);
 }
 
-bool structPackingOverrideValidator(List<String> name, dynamic value) {
-  var result = true;
-
-  if (!checkType<YamlMap>([...name], value)) {
-    result = false;
-  } else {
-    for (final key in (value as YamlMap).keys) {
-      if (!(strings.packingValuesMap.keys.contains(value[key]))) {
-        _logger.severe(
-            "'$name -> $key' must be one of the following - ${strings.packingValuesMap.keys.toList()}");
-        result = false;
-      }
-    }
-  }
-
-  return result;
-}
-
 FfiNativeConfig ffiNativeExtractor(dynamic yamlConfig) {
-  final yamlMap = yamlConfig as YamlMap?;
+  final yamlMap = yamlConfig as Map?;
   return FfiNativeConfig(
     enabled: true,
     asset: yamlMap?[strings.ffiNativeAsset] as String?,
   );
 }
-
-bool ffiNativeValidator(List<String> name, dynamic yamlConfig) {
-  if (!checkType<YamlMap?>(name, yamlConfig)) {
-    return false;
-  }
-  if (yamlConfig == null) {
-    // Empty means no asset name.
-    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/pkgs/ffigen/lib/src/header_parser/clang_bindings/clang_bindings.dart b/pkgs/ffigen/lib/src/header_parser/clang_bindings/clang_bindings.dart
index 6b77794..4c5fba2 100644
--- a/pkgs/ffigen/lib/src/header_parser/clang_bindings/clang_bindings.dart
+++ b/pkgs/ffigen/lib/src/header_parser/clang_bindings/clang_bindings.dart
@@ -792,6 +792,21 @@
       _clang_Type_getObjCObjectBaseTypePtr
           .asFunction<CXType Function(CXType)>();
 
+  /// Return 1 if the CXType is a variadic function type, and 0 otherwise.
+  int clang_isFunctionTypeVariadic(
+    CXType T,
+  ) {
+    return _clang_isFunctionTypeVariadic(
+      T,
+    );
+  }
+
+  late final _clang_isFunctionTypeVariadicPtr =
+      _lookup<ffi.NativeFunction<ffi.UnsignedInt Function(CXType)>>(
+          'clang_isFunctionTypeVariadic');
+  late final _clang_isFunctionTypeVariadic =
+      _clang_isFunctionTypeVariadicPtr.asFunction<int Function(CXType)>();
+
   /// Retrieve the return type associated with a given cursor.
   ///
   /// This only returns a valid type if the cursor refers to a function or method.
@@ -933,6 +948,24 @@
       _clang_Cursor_isAnonymousRecordDeclPtr
           .asFunction<int Function(CXCursor)>();
 
+  /// Returns the storage class for a function or variable declaration.
+  ///
+  /// If the passed in Cursor is not a function or variable declaration,
+  /// CX_SC_Invalid is returned else the storage class.
+  int clang_Cursor_getStorageClass(
+    CXCursor arg0,
+  ) {
+    return _clang_Cursor_getStorageClass(
+      arg0,
+    );
+  }
+
+  late final _clang_Cursor_getStorageClassPtr =
+      _lookup<ffi.NativeFunction<ffi.Int32 Function(CXCursor)>>(
+          'clang_Cursor_getStorageClass');
+  late final _clang_Cursor_getStorageClass =
+      _clang_Cursor_getStorageClassPtr.asFunction<int Function(CXCursor)>();
+
   /// Visit the children of a particular cursor.
   ///
   /// This function visits all the direct children of the given cursor,
@@ -1310,21 +1343,21 @@
 /// the ownership of that string might differ from one call to the next.
 /// Use \c clang_getCString() to retrieve the string data and, once finished
 /// with the string data, call \c clang_disposeString() to free the string.
-class CXString extends ffi.Struct {
+final class CXString extends ffi.Struct {
   external ffi.Pointer<ffi.Void> data;
 
   @ffi.UnsignedInt()
   external int private_flags;
 }
 
-class CXTranslationUnitImpl extends ffi.Opaque {}
+final class CXTranslationUnitImpl extends ffi.Opaque {}
 
 /// Provides the contents of a file that has not yet been saved to disk.
 ///
 /// Each CXUnsavedFile instance provides the name of a file on the
 /// system along with the current contents of that file that have not
 /// yet been saved to disk.
-class CXUnsavedFile extends ffi.Struct {
+final class CXUnsavedFile extends ffi.Struct {
   /// The file whose contents have not yet been saved.
   ///
   /// This file must already exist in the file system.
@@ -1350,7 +1383,7 @@
 ///
 /// Use clang_getExpansionLocation() or clang_getSpellingLocation()
 /// to map a source location to a particular file, line, and column.
-class CXSourceLocation extends ffi.Struct {
+final class CXSourceLocation extends ffi.Struct {
   @ffi.Array.multi([2])
   external ffi.Array<ffi.Pointer<ffi.Void>> ptr_data;
 
@@ -1362,7 +1395,7 @@
 ///
 /// Use clang_getRangeStart() and clang_getRangeEnd() to retrieve the
 /// starting and end locations from a source range, respectively.
-class CXSourceRange extends ffi.Struct {
+final class CXSourceRange extends ffi.Struct {
   @ffi.Array.multi([2])
   external ffi.Array<ffi.Pointer<ffi.Void>> ptr_data;
 
@@ -2356,7 +2389,7 @@
 /// translation unit. clang_getCursor() maps from a physical source location
 /// to the entity that resides at that location, allowing one to map from the
 /// source code into the AST.
-class CXCursor extends ffi.Struct {
+final class CXCursor extends ffi.Struct {
   @ffi.Int32()
   external int kind;
 
@@ -2498,7 +2531,7 @@
 }
 
 /// The type of an element in the abstract syntax tree.
-class CXType extends ffi.Struct {
+final class CXType extends ffi.Struct {
   @ffi.Int32()
   external int kind;
 
@@ -2549,6 +2582,19 @@
   static const int CXTypeLayoutError_Undeduced = -6;
 }
 
+/// Represents the storage classes as declared in the source. CX_SC_Invalid
+/// was added for the case that the passed cursor in not a declaration.
+abstract class CX_StorageClass {
+  static const int CX_SC_Invalid = 0;
+  static const int CX_SC_None = 1;
+  static const int CX_SC_Extern = 2;
+  static const int CX_SC_Static = 3;
+  static const int CX_SC_PrivateExtern = 4;
+  static const int CX_SC_OpenCLWorkGroupLocal = 5;
+  static const int CX_SC_Auto = 6;
+  static const int CX_SC_Register = 7;
+}
+
 /// Describes how the traversal of the children of a particular
 /// cursor should proceed after visiting a particular child cursor.
 ///
@@ -2578,7 +2624,9 @@
 /// The visitor should return one of the \c CXChildVisitResult values
 /// to direct clang_visitCursorChildren().
 typedef CXCursorVisitor = ffi.Pointer<
-    ffi.NativeFunction<ffi.Int32 Function(CXCursor, CXCursor, CXClientData)>>;
+    ffi.NativeFunction<
+        ffi.Int32 Function(
+            CXCursor cursor, CXCursor parent, CXClientData client_data)>>;
 
 /// Opaque pointer representing client data that will be passed through
 /// to various callbacks and visitors.
diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/compounddecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/compounddecl_parser.dart
index 93d662d..8c700c9 100644
--- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/compounddecl_parser.dart
+++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/compounddecl_parser.dart
@@ -294,12 +294,15 @@
         break;
       case clang_types.CXCursorKind.CXCursor_UnionDecl:
       case clang_types.CXCursorKind.CXCursor_StructDecl:
-        final mt = cursor.toCodeGenType();
-
         // If the union/struct are anonymous, then we need to add them now,
         // otherwise they will be added in the next iteration.
         if (!cursor.isAnonymousRecordDecl()) break;
 
+        final mt = cursor.toCodeGenType();
+        if (mt.isIncompleteCompound) {
+          parsed.incompleteCompoundMember = true;
+        }
+
         // Anonymous members are always unnamed. To avoid environment-
         // dependent naming issues with the generated code, we explicitly
         // use the empty string as spelling.
diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/functiondecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/functiondecl_parser.dart
index d2d3412..f9d1121 100644
--- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/functiondecl_parser.dart
+++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/functiondecl_parser.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 'package:ffigen/src/header_parser/data.dart';
 import 'package:logging/logging.dart';
 
@@ -14,7 +15,9 @@
 
 /// Holds temporary information regarding [Func] while parsing.
 class _ParserFunc {
-  Func? func;
+  /// Multiple values are since there may be more than one instance of the
+  /// same base C function with different variadic arguments.
+  List<Func> funcs = [];
   bool incompleteStructParameter = false;
   bool unimplementedParameterType = false;
   _ParserFunc();
@@ -23,7 +26,7 @@
 final _stack = Stack<_ParserFunc>();
 
 /// Parses a function declaration.
-Func? parseFunctionDeclaration(clang_types.CXCursor cursor) {
+List<Func>? parseFunctionDeclaration(clang_types.CXCursor cursor) {
   _stack.push(_ParserFunc());
 
   final funcUsr = cursor.usr();
@@ -33,14 +36,15 @@
 
     final rt = _getFunctionReturnType(cursor);
     final parameters = _getParameters(cursor, funcName);
-
-    if (clang.clang_Cursor_isFunctionInlined(cursor) != 0) {
+    if (clang.clang_Cursor_isFunctionInlined(cursor) != 0 &&
+        clang.clang_Cursor_getStorageClass(cursor) !=
+            clang_types.CX_StorageClass.CX_SC_Extern) {
       _logger.fine('---- Removed Function, reason: inline function: '
           '${cursor.completeStringRepr()}');
       _logger.warning(
           "Skipped Function '$funcName', inline functions are not supported.");
-      // Returning null so that [addToBindings] function excludes this.
-      return _stack.pop().func;
+      // Returning empty so that [addToBindings] function excludes this.
+      return _stack.pop().funcs;
     }
 
     if (rt.isIncompleteCompound || _stack.top.incompleteStructParameter) {
@@ -51,7 +55,7 @@
           "Skipped Function '$funcName', Incomplete struct pass/return by "
           'value not supported.');
       // Returning null so that [addToBindings] function excludes this.
-      return _stack.pop().func;
+      return _stack.pop().funcs;
     }
 
     if (rt.baseType is UnimplementedType ||
@@ -62,32 +66,46 @@
           "Skipped Function '$funcName', function has unsupported return type "
           'or parameter type.');
       // Returning null so that [addToBindings] function excludes this.
-      return _stack.pop().func;
+      return _stack.pop().funcs;
     }
 
-    _stack.top.func = Func(
-      dartDoc: getCursorDocComment(
-        cursor,
-        nesting.length + commentPrefix.length,
-      ),
-      usr: funcUsr,
-      name: config.functionDecl.renameUsingConfig(funcName),
-      originalName: funcName,
-      returnType: rt,
-      parameters: parameters,
-      exposeSymbolAddress:
-          config.functionDecl.shouldIncludeSymbolAddress(funcName),
-      exposeFunctionTypedefs:
-          config.exposeFunctionTypedefs.shouldInclude(funcName),
-      isLeaf: config.leafFunctions.shouldInclude(funcName),
-      ffiNativeConfig: config.ffiNativeConfig,
-    );
-    bindingsIndex.addFuncToSeen(funcUsr, _stack.top.func!);
+    // Initialized with a single value with no prefix and empty var args.
+    var varArgFunctions = [VarArgFunction('', [])];
+    if (config.varArgFunctions.containsKey(funcName)) {
+      if (clang.clang_isFunctionTypeVariadic(cursor.type()) == 1) {
+        varArgFunctions = config.varArgFunctions[funcName]!;
+      } else {
+        _logger.warning(
+            "Skipping variadic-argument config for function $funcName since its not variadic.");
+      }
+    }
+    for (final vaFunc in varArgFunctions) {
+      _stack.top.funcs.add(Func(
+        dartDoc: getCursorDocComment(
+          cursor,
+          nesting.length + commentPrefix.length,
+        ),
+        usr: funcUsr + vaFunc.postfix,
+        name: config.functionDecl.renameUsingConfig(funcName) + vaFunc.postfix,
+        originalName: funcName,
+        returnType: rt,
+        parameters: parameters,
+        varArgParameters:
+            vaFunc.types.map((ta) => Parameter(type: ta, name: 'va')).toList(),
+        exposeSymbolAddress:
+            config.functionDecl.shouldIncludeSymbolAddress(funcName),
+        exposeFunctionTypedefs:
+            config.exposeFunctionTypedefs.shouldInclude(funcName),
+        isLeaf: config.leafFunctions.shouldInclude(funcName),
+        ffiNativeConfig: config.ffiNativeConfig,
+      ));
+    }
+    bindingsIndex.addFuncToSeen(funcUsr, _stack.top.funcs.last);
   } else if (bindingsIndex.isSeenFunc(funcUsr)) {
-    _stack.top.func = bindingsIndex.getSeenFunc(funcUsr);
+    _stack.top.funcs.add(bindingsIndex.getSeenFunc(funcUsr)!);
   }
 
-  return _stack.pop().func;
+  return _stack.pop().funcs;
 }
 
 Type _getFunctionReturnType(clang_types.CXCursor cursor) {
diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/var_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/var_parser.dart
index a8d46d3..90b3853 100644
--- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/var_parser.dart
+++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/var_parser.dart
@@ -34,8 +34,8 @@
   }
 
   if (config.ffiNativeConfig.enabled) {
-    _logger.warning(
-        "Skipped global variable '$name', not supported in FfiNatives.");
+    _logger
+        .warning("Skipped global variable '$name', not supported in Natives.");
     return null;
   }
 
diff --git a/pkgs/ffigen/lib/src/header_parser/translation_unit_parser.dart b/pkgs/ffigen/lib/src/header_parser/translation_unit_parser.dart
index ea9d594..31f50b8 100644
--- a/pkgs/ffigen/lib/src/header_parser/translation_unit_parser.dart
+++ b/pkgs/ffigen/lib/src/header_parser/translation_unit_parser.dart
@@ -56,7 +56,7 @@
       _logger.finest('rootCursorVisitor: ${cursor.completeStringRepr()}');
       switch (clang.clang_getCursorKind(cursor)) {
         case clang_types.CXCursorKind.CXCursor_FunctionDecl:
-          addToBindings(parseFunctionDeclaration(cursor));
+          addAllToBindings(parseFunctionDeclaration(cursor) as List<Binding>);
           break;
         case clang_types.CXCursorKind.CXCursor_StructDecl:
         case clang_types.CXCursorKind.CXCursor_UnionDecl:
@@ -96,6 +96,14 @@
   }
 }
 
+/// Adds all binding if not empty.
+void addAllToBindings(List<Binding> b) {
+  if (b.isNotEmpty) {
+    // This is a set, and hence will not have duplicates.
+    _bindings.addAll(b);
+  }
+}
+
 BindingType? _getCodeGenTypeFromCursor(clang_types.CXCursor cursor) {
   final t = getCodeGenType(cursor.type(), ignoreFilter: false);
   return t is BindingType ? t : null;
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 c6a0951..c3c8054 100644
--- a/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart
+++ b/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart
@@ -117,13 +117,16 @@
       // Primarily used for function types with zero arguments.
       return _extractFromFunctionProto(cxtype, cursor: originalCursor);
     case clang_types.CXTypeKind
-        .CXType_ConstantArray: // Primarily used for constant array in struct members.
-      return ConstantArray(
-        clang.clang_getNumElements(cxtype),
-        clang.clang_getArrayElementType(cxtype).toCodeGenType(),
-      );
+          .CXType_ConstantArray: // Primarily used for constant array in struct members.
+      final numElements = clang.clang_getNumElements(cxtype);
+      final elementType =
+          clang.clang_getArrayElementType(cxtype).toCodeGenType();
+      // Handle numElements being 0 as an incomplete array.
+      return numElements == 0
+          ? IncompleteArray(elementType)
+          : ConstantArray(numElements, elementType);
     case clang_types.CXTypeKind
-        .CXType_IncompleteArray: // Primarily used for incomplete array in function parameters.
+          .CXType_IncompleteArray: // Primarily used for incomplete array in function parameters.
       return IncompleteArray(
         clang.clang_getArrayElementType(cxtype).toCodeGenType(),
       );
diff --git a/pkgs/ffigen/lib/src/strings.dart b/pkgs/ffigen/lib/src/strings.dart
index e0f2761..7613289 100644
--- a/pkgs/ffigen/lib/src/strings.dart
+++ b/pkgs/ffigen/lib/src/strings.dart
@@ -85,6 +85,11 @@
 // Nested under `functions`
 const exposeFunctionTypedefs = 'expose-typedefs';
 const leafFunctions = 'leaf';
+const varArgFunctions = 'variadic-arguments';
+
+// Nested under varArg entries
+const postfix = "postfix";
+const types = "types";
 
 // Sub-fields of ObjC interfaces.
 const objcModule = 'module';
@@ -272,3 +277,7 @@
   _tmpDir ??= Directory.systemTemp.createTempSync();
   return _tmpDir!.path;
 }
+
+const ffigenJsonSchemaIndent = '  ';
+const ffigenJsonSchemaId = "https://json.schemastore.org/ffigen";
+const ffigenJsonSchemaFileName = "ffigen.schema.json";
diff --git a/pkgs/ffigen/pubspec.yaml b/pkgs/ffigen/pubspec.yaml
index bebcaf5..3a8e0c1 100644
--- a/pkgs/ffigen/pubspec.yaml
+++ b/pkgs/ffigen/pubspec.yaml
@@ -3,12 +3,17 @@
 # BSD-style license that can be found in the LICENSE file.
 
 name: ffigen
-version: 7.2.11
+version: 9.0.1
 description: Generator for FFI bindings, using LibClang to parse C header files.
 repository: https://github.com/dart-lang/ffigen
 
+topics:
+ - interop
+ - ffi
+ - codegen
+
 environment:
-  sdk: '>=2.17.0 <4.0.0'
+  sdk: ">=3.0.0 <4.0.0"
 
 dependencies:
   ffi: ^2.0.1
@@ -19,10 +24,11 @@
   logging: ^1.0.0
   cli_util: ^0.4.0
   glob: ^2.0.0
-  file: ^6.0.0
+  file: ^7.0.0
   package_config: ^2.1.0
   yaml_edit: ^2.0.3
 
 dev_dependencies:
   lints: ^2.0.1
   test: ^1.16.2
+  json_schema: ^5.1.1
diff --git a/pkgs/ffigen/test/README.md b/pkgs/ffigen/test/README.md
index 1a499a4..dc57474 100644
--- a/pkgs/ffigen/test/README.md
+++ b/pkgs/ffigen/test/README.md
@@ -1,5 +1,5 @@
 [![Build Status](https://github.com/dart-lang/ffigen/workflows/Dart%20CI/badge.svg)](https://github.com/dart-lang/ffigen/actions?query=workflow%3A"Dart+CI")
-[![Coverage Status](https://coveralls.io/repos/github/dart-lang/ffigen/badge.svg?branch=master)](https://coveralls.io/github/dart-lang/ffigen?branch=master)
+[![Coverage Status](https://coveralls.io/repos/github/dart-lang/ffigen/badge.svg?branch=main)](https://coveralls.io/github/dart-lang/ffigen?branch=main)
 
 # ffigen testing
 
@@ -23,18 +23,18 @@
 Some tests verify that the generated Dart FFI bindings match a golden file.
 
 For example, the test
-[`test/native_test/native_test.dart`](https://github.com/dart-lang/ffigen/blob/master/test/native_test/native_test.dart)
+[`test/native_test/native_test.dart`](https://github.com/dart-lang/ffigen/blob/main/test/native_test/native_test.dart)
 works by:
 
 1.  Loading the dynamic library for
-    [`test/native_test/native_test.c`](https://github.com/dart-lang/ffigen/blob/master/test/native_test/native_test.c)
+    [`test/native_test/native_test.c`](https://github.com/dart-lang/ffigen/blob/main/test/native_test/native_test.c)
     (which was generated by `dart run test/setup.dart`).
 
 2.  Generating binding files for that dynamic library in the
     `test/debug_generated` directory.
 
 3.  Comparing the golden file (i.e.
-    [`test/native_test/native_test_bindings.dart`](https://github.com/dart-lang/ffigen/blob/master/test/native_test/native_test_bindings.dart))
+    [`test/native_test/_expected_native_test_bindings.dart`](https://github.com/dart-lang/ffigen/blob/main/test/native_test/_expected_native_test_bindings.dart))
     to the generated file and generating a test failure if they do not match.
 
 4.  Using the *golden* bindings to excercise the dynamic library.
diff --git a/pkgs/ffigen/test/code_generator_tests/expected_bindings/_expected_boolean_dartbool_bindings.dart b/pkgs/ffigen/test/code_generator_tests/expected_bindings/_expected_boolean_dartbool_bindings.dart
index 0bc046c..015821e 100644
--- a/pkgs/ffigen/test/code_generator_tests/expected_bindings/_expected_boolean_dartbool_bindings.dart
+++ b/pkgs/ffigen/test/code_generator_tests/expected_bindings/_expected_boolean_dartbool_bindings.dart
@@ -35,7 +35,7 @@
       _test1Ptr.asFunction<bool Function(bool, ffi.Pointer<ffi.Bool>)>();
 }
 
-class Test2 extends ffi.Struct {
+final class Test2 extends ffi.Struct {
   @ffi.Bool()
   external bool a;
 }
diff --git a/pkgs/ffigen/test/code_generator_tests/expected_bindings/_expected_function_n_struct_bindings.dart b/pkgs/ffigen/test/code_generator_tests/expected_bindings/_expected_function_n_struct_bindings.dart
index cad1980..cf255b2 100644
--- a/pkgs/ffigen/test/code_generator_tests/expected_bindings/_expected_function_n_struct_bindings.dart
+++ b/pkgs/ffigen/test/code_generator_tests/expected_bindings/_expected_function_n_struct_bindings.dart
@@ -34,7 +34,7 @@
       ffi.Pointer<SomeStruct> Function(ffi.Pointer<ffi.Pointer<SomeStruct>>)>();
 }
 
-class SomeStruct extends ffi.Struct {
+final class SomeStruct extends ffi.Struct {
   @ffi.Int32()
   external int a;
 
diff --git a/pkgs/ffigen/test/code_generator_tests/expected_bindings/_expected_global_bindings.dart b/pkgs/ffigen/test/code_generator_tests/expected_bindings/_expected_global_bindings.dart
index f3f3f12..37081b8 100644
--- a/pkgs/ffigen/test/code_generator_tests/expected_bindings/_expected_global_bindings.dart
+++ b/pkgs/ffigen/test/code_generator_tests/expected_bindings/_expected_global_bindings.dart
@@ -44,6 +44,6 @@
   ffi.Pointer<EmptyStruct> get globalStruct => _globalStruct;
 }
 
-class Some extends ffi.Opaque {}
+final class Some extends ffi.Opaque {}
 
-class EmptyStruct extends ffi.Opaque {}
+final class EmptyStruct extends ffi.Opaque {}
diff --git a/pkgs/ffigen/test/code_generator_tests/expected_bindings/_expected_internal_conflict_resolution_bindings.dart b/pkgs/ffigen/test/code_generator_tests/expected_bindings/_expected_internal_conflict_resolution_bindings.dart
index edc3935..1a71e3b 100644
--- a/pkgs/ffigen/test/code_generator_tests/expected_bindings/_expected_internal_conflict_resolution_bindings.dart
+++ b/pkgs/ffigen/test/code_generator_tests/expected_bindings/_expected_internal_conflict_resolution_bindings.dart
@@ -62,12 +62,12 @@
   late final _Test = _TestPtr.asFunction<void Function()>();
 }
 
-class _Test extends ffi.Struct {
+final class _Test extends ffi.Struct {
   @ffi.Array.multi([2])
   external ffi.Array<ffi.Int8> array;
 }
 
-class ArrayHelperPrefixCollisionTest extends ffi.Opaque {}
+final class ArrayHelperPrefixCollisionTest extends ffi.Opaque {}
 
 abstract class _c_Test {}
 
diff --git a/pkgs/ffigen/test/code_generator_tests/expected_bindings/_expected_packed_structs_bindings.dart b/pkgs/ffigen/test/code_generator_tests/expected_bindings/_expected_packed_structs_bindings.dart
index e703e28..f882e7e 100644
--- a/pkgs/ffigen/test/code_generator_tests/expected_bindings/_expected_packed_structs_bindings.dart
+++ b/pkgs/ffigen/test/code_generator_tests/expected_bindings/_expected_packed_structs_bindings.dart
@@ -4,37 +4,37 @@
 // ignore_for_file: type=lint
 import 'dart:ffi' as ffi;
 
-class NoPacking extends ffi.Struct {
+final class NoPacking extends ffi.Struct {
   @ffi.Uint8()
   external int a;
 }
 
 @ffi.Packed(1)
-class Pack1 extends ffi.Struct {
+final class Pack1 extends ffi.Struct {
   @ffi.Uint8()
   external int a;
 }
 
 @ffi.Packed(2)
-class Pack2 extends ffi.Struct {
+final class Pack2 extends ffi.Struct {
   @ffi.Uint8()
   external int a;
 }
 
 @ffi.Packed(4)
-class Pack21 extends ffi.Struct {
+final class Pack21 extends ffi.Struct {
   @ffi.Uint8()
   external int a;
 }
 
 @ffi.Packed(8)
-class Pack22 extends ffi.Struct {
+final class Pack22 extends ffi.Struct {
   @ffi.Uint8()
   external int a;
 }
 
 @ffi.Packed(16)
-class Pack16 extends ffi.Struct {
+final class Pack16 extends ffi.Struct {
   @ffi.Uint8()
   external int a;
 }
diff --git a/pkgs/ffigen/test/code_generator_tests/expected_bindings/_expected_sort_bindings_bindings.dart b/pkgs/ffigen/test/code_generator_tests/expected_bindings/_expected_sort_bindings_bindings.dart
index 3a9aa67..eddeeb7 100644
--- a/pkgs/ffigen/test/code_generator_tests/expected_bindings/_expected_sort_bindings_bindings.dart
+++ b/pkgs/ffigen/test/code_generator_tests/expected_bindings/_expected_sort_bindings_bindings.dart
@@ -33,6 +33,6 @@
   late final _b = _bPtr.asFunction<void Function()>();
 }
 
-class C extends ffi.Opaque {}
+final class C extends ffi.Opaque {}
 
-class D extends ffi.Opaque {}
+final class D extends ffi.Opaque {}
diff --git a/pkgs/ffigen/test/code_generator_tests/expected_bindings/_expected_struct_bindings.dart b/pkgs/ffigen/test/code_generator_tests/expected_bindings/_expected_struct_bindings.dart
index f8482e7..b7a4e9d 100644
--- a/pkgs/ffigen/test/code_generator_tests/expected_bindings/_expected_struct_bindings.dart
+++ b/pkgs/ffigen/test/code_generator_tests/expected_bindings/_expected_struct_bindings.dart
@@ -6,9 +6,9 @@
 
 /// Just a test struct
 /// heres another line
-class NoMember extends ffi.Opaque {}
+final class NoMember extends ffi.Opaque {}
 
-class WithPrimitiveMember extends ffi.Struct {
+final class WithPrimitiveMember extends ffi.Struct {
   @ffi.Int32()
   external int a;
 
@@ -19,7 +19,7 @@
   external int c;
 }
 
-class WithPointerMember extends ffi.Struct {
+final class WithPointerMember extends ffi.Struct {
   external ffi.Pointer<ffi.Int32> a;
 
   external ffi.Pointer<ffi.Pointer<ffi.Double>> b;
@@ -28,7 +28,7 @@
   external int c;
 }
 
-class WithIntPtrUintPtr extends ffi.Struct {
+final class WithIntPtrUintPtr extends ffi.Struct {
   external ffi.Pointer<ffi.UintPtr> a;
 
   external ffi.Pointer<ffi.Pointer<ffi.IntPtr>> b;
diff --git a/pkgs/ffigen/test/code_generator_tests/expected_bindings/_expected_typealias_bindings.dart b/pkgs/ffigen/test/code_generator_tests/expected_bindings/_expected_typealias_bindings.dart
index 00306c6..4383756 100644
--- a/pkgs/ffigen/test/code_generator_tests/expected_bindings/_expected_typealias_bindings.dart
+++ b/pkgs/ffigen/test/code_generator_tests/expected_bindings/_expected_typealias_bindings.dart
@@ -39,19 +39,19 @@
 
 typedef RawUnused = Struct1;
 
-class Struct1 extends ffi.Opaque {}
+final class Struct1 extends ffi.Opaque {}
 
-class WithTypealiasStruct extends ffi.Struct {
+final class WithTypealiasStruct extends ffi.Struct {
   external Struct2Typealias t;
 }
 
 typedef Struct2Typealias = Struct2;
 
-class Struct2 extends ffi.Struct {
+final class Struct2 extends ffi.Struct {
   @ffi.Double()
   external double a;
 }
 
 typedef Struct3Typealias = Struct3;
 
-class Struct3 extends ffi.Opaque {}
+final class Struct3 extends ffi.Opaque {}
diff --git a/pkgs/ffigen/test/code_generator_tests/expected_bindings/_expected_unions_bindings.dart b/pkgs/ffigen/test/code_generator_tests/expected_bindings/_expected_unions_bindings.dart
index 1865919..692f0d6 100644
--- a/pkgs/ffigen/test/code_generator_tests/expected_bindings/_expected_unions_bindings.dart
+++ b/pkgs/ffigen/test/code_generator_tests/expected_bindings/_expected_unions_bindings.dart
@@ -4,19 +4,19 @@
 // ignore_for_file: type=lint
 import 'dart:ffi' as ffi;
 
-class Struct1 extends ffi.Struct {
+final class Struct1 extends ffi.Struct {
   @ffi.Char()
   external int a;
 }
 
-class Union1 extends ffi.Union {
+final class Union1 extends ffi.Union {
   @ffi.Char()
   external int a;
 }
 
-class EmptyUnion extends ffi.Opaque {}
+final class EmptyUnion extends ffi.Opaque {}
 
-class Primitives extends ffi.Union {
+final class Primitives extends ffi.Union {
   @ffi.Char()
   external int a;
 
@@ -30,7 +30,7 @@
   external double d;
 }
 
-class PrimitivesWithPointers extends ffi.Union {
+final class PrimitivesWithPointers extends ffi.Union {
   @ffi.Char()
   external int a;
 
@@ -44,7 +44,7 @@
   external ffi.Pointer<Struct1> d1;
 }
 
-class WithArray extends ffi.Union {
+final class WithArray extends ffi.Union {
   @ffi.Array.multi([10])
   external ffi.Array<ffi.Char> a;
 
diff --git a/pkgs/ffigen/test/collision_tests/expected_bindings/_expected_decl_symbol_address_collision_bindings.dart b/pkgs/ffigen/test/collision_tests/expected_bindings/_expected_decl_symbol_address_collision_bindings.dart
index 3b3e791..0d5473f 100644
--- a/pkgs/ffigen/test/collision_tests/expected_bindings/_expected_decl_symbol_address_collision_bindings.dart
+++ b/pkgs/ffigen/test/collision_tests/expected_bindings/_expected_decl_symbol_address_collision_bindings.dart
@@ -50,9 +50,9 @@
       _library1.__SymbolAddresses_1Ptr;
 }
 
-class addresses extends ffi.Opaque {}
+final class addresses extends ffi.Opaque {}
 
-class _SymbolAddresses extends ffi.Opaque {}
+final class _SymbolAddresses extends ffi.Opaque {}
 
 abstract class Bindings {}
 
diff --git a/pkgs/ffigen/test/collision_tests/expected_bindings/_expected_decl_type_name_collision_bindings.dart b/pkgs/ffigen/test/collision_tests/expected_bindings/_expected_decl_type_name_collision_bindings.dart
index f7207cf..625c69a 100644
--- a/pkgs/ffigen/test/collision_tests/expected_bindings/_expected_decl_type_name_collision_bindings.dart
+++ b/pkgs/ffigen/test/collision_tests/expected_bindings/_expected_decl_type_name_collision_bindings.dart
@@ -6,12 +6,12 @@
 // ignore_for_file: type=lint
 import 'dart:ffi' as ffi;
 
-class A extends ffi.Struct {
+final class A extends ffi.Struct {
   @ffi.Int()
   external int a;
 }
 
-class B extends ffi.Struct {
+final class B extends ffi.Struct {
   @ffi.Int()
   external int B1;
 
@@ -19,13 +19,13 @@
   external int A;
 }
 
-class C extends ffi.Struct {
+final class C extends ffi.Struct {
   external A A1;
 
   external B B1;
 }
 
-class D extends ffi.Struct {
+final class D extends ffi.Struct {
   external B A1;
 
   external A B1;
diff --git a/pkgs/ffigen/test/config_tests/json_schema_test.dart b/pkgs/ffigen/test/config_tests/json_schema_test.dart
new file mode 100644
index 0000000..44ea1e7
--- /dev/null
+++ b/pkgs/ffigen/test/config_tests/json_schema_test.dart
@@ -0,0 +1,89 @@
+// Copyright (c) 2023, 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 'dart:convert';
+import 'dart:io';
+
+import 'package:ffigen/ffigen.dart';
+import 'package:ffigen/src/strings.dart' as strings;
+import 'package:file/local.dart';
+import 'package:glob/glob.dart';
+import 'package:json_schema/json_schema.dart';
+import 'package:test/test.dart';
+import 'package:yaml/yaml.dart';
+
+late Library actual, expected;
+
+void main() {
+  group('json_schema_test', () {
+    final schema = Config.getsRootConfigSpec()
+        .generateJsonSchema(strings.ffigenJsonSchemaId);
+
+    test('Schema Changes', () {
+      final actualJsonSchema =
+          JsonEncoder.withIndent(strings.ffigenJsonSchemaIndent).convert(
+        Config.getsRootConfigSpec()
+            .generateJsonSchema(strings.ffigenJsonSchemaId),
+      );
+      final expectedJsonSchema = File(strings.ffigenJsonSchemaFileName)
+          .readAsStringSync()
+          .replaceAll('\r\n', '\n');
+      expect(actualJsonSchema, expectedJsonSchema);
+    });
+
+    final jsonSchema = JsonSchema.create(schema);
+    test('Valid json schema', () {
+      expect(jsonSchema, isNot(null));
+    });
+
+    // Find all ffigen config files in the repo.
+    final configYamlGlob = Glob("**config.yaml");
+    final configYamlFiles =
+        configYamlGlob.listFileSystemSync(const LocalFileSystem());
+    test('$configYamlGlob files not empty', () {
+      expect(configYamlFiles.isNotEmpty, true);
+    });
+
+    final sharedBindingsConfigYamlGlob =
+        Glob("example/shared_bindings/ffigen_configs/**.yaml");
+    final sharedBindingsConfigYamlFiles = sharedBindingsConfigYamlGlob
+        .listFileSystemSync(const LocalFileSystem());
+    test('$sharedBindingsConfigYamlGlob files not emty', () {
+      expect(sharedBindingsConfigYamlFiles.isNotEmpty, true);
+    });
+
+    final allConfigFiles = configYamlFiles + sharedBindingsConfigYamlFiles;
+
+    for (final fe in allConfigFiles) {
+      test('validate config file: ${fe.path}', () {
+        final yamlDoc = loadYaml(File(fe.absolute.path).readAsStringSync());
+        final validationResult = jsonSchema.validate(yamlDoc);
+        expect(validationResult.errors.isEmpty, true,
+            reason: "Schema Errors: ${validationResult.errors}");
+        expect(validationResult.warnings.isEmpty, true,
+            reason: "Schema Warnings: ${validationResult.errors}");
+      });
+    }
+
+    test('Bare minimal input', () {
+      expect(
+          jsonSchema
+              .validate({
+                "output": "abcd.dart",
+                "headers": {
+                  "entry-points": ["a.h"]
+                }
+              })
+              .errors
+              .isEmpty,
+          true);
+    });
+    test('Fail input', () {
+      expect(jsonSchema.validate(null).errors.isNotEmpty, true);
+      expect(jsonSchema.validate({"a": 1}).errors.isNotEmpty, true);
+      expect(
+          jsonSchema.validate({"output": "abcd.dart"}).errors.isNotEmpty, true);
+    });
+  });
+}
diff --git a/pkgs/ffigen/test/config_tests/unknown_keys_warn_test.dart b/pkgs/ffigen/test/config_tests/unknown_keys_warn_test.dart
index 3dd0451..6f80757 100644
--- a/pkgs/ffigen/test/config_tests/unknown_keys_warn_test.dart
+++ b/pkgs/ffigen/test/config_tests/unknown_keys_warn_test.dart
@@ -28,12 +28,6 @@
 ${strings.typeMap}:
   'warn-2': 'warn'
   'warn-3': 'warn'
-${strings.functions}:
-  'warn-4': 'skip'
-${strings.structs}:
-  'warn-5': 'skip'
-${strings.unions}:
-  'warn-6': 'skip'
         ''');
       logString = logArr.join("\n");
     });
@@ -42,10 +36,5 @@
       expect(logString.contains('warn-2'), true);
       expect(logString.contains('warn-3'), true);
     });
-    test('Do not warn for unknown keys in declarations.', () {
-      expect(logString.contains('warn-4'), false);
-      expect(logString.contains('warn-5'), false);
-      expect(logString.contains('warn-6'), false);
-    });
   });
 }
diff --git a/pkgs/ffigen/test/header_parser_tests/comment_markup.h b/pkgs/ffigen/test/header_parser_tests/comment_markup.h
index b3912da..635c2c3 100644
--- a/pkgs/ffigen/test/header_parser_tests/comment_markup.h
+++ b/pkgs/ffigen/test/header_parser_tests/comment_markup.h
@@ -23,4 +23,7 @@
 
     /* Single line field comment. */
     float b;
+
+    /* Comment on array member. */
+    int c[3];
 };
diff --git a/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_comment_markup_bindings.dart b/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_comment_markup_bindings.dart
index ea88b5b..91d9908 100644
--- a/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_comment_markup_bindings.dart
+++ b/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_comment_markup_bindings.dart
@@ -52,7 +52,7 @@
 }
 
 /// Test comment for struct.
-class Com4 extends ffi.Struct {
+final class Com4 extends ffi.Struct {
   /// Muli-line test comment for struct field
   /// With multiple line and both // and ///.
   @ffi.Int()
@@ -61,4 +61,8 @@
   /// Single line field comment.
   @ffi.Float()
   external double b;
+
+  /// Comment on array member.
+  @ffi.Array.multi([3])
+  external ffi.Array<ffi.Int> c;
 }
diff --git a/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_dart_handle_bindings.dart b/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_dart_handle_bindings.dart
index f51bf57..8455b7a 100644
--- a/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_dart_handle_bindings.dart
+++ b/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_dart_handle_bindings.dart
@@ -71,8 +71,8 @@
 typedef Typedef1
     = ffi.Pointer<ffi.NativeFunction<ffi.Void Function(ffi.Handle)>>;
 
-class Struct1 extends ffi.Opaque {}
+final class Struct1 extends ffi.Opaque {}
 
-class Struct2 extends ffi.Struct {
+final class Struct2 extends ffi.Struct {
   external ffi.Pointer<ffi.Handle> h;
 }
diff --git a/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_forward_decl_bindings.dart b/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_forward_decl_bindings.dart
index 81cb2e3..f1175cc 100644
--- a/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_forward_decl_bindings.dart
+++ b/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_forward_decl_bindings.dart
@@ -36,7 +36,7 @@
   late final _func = _funcPtr.asFunction<void Function(ffi.Pointer<A>, int)>();
 }
 
-class A extends ffi.Struct {
+final class A extends ffi.Struct {
   @ffi.Int()
   external int a;
 
diff --git a/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_functions_bindings.dart b/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_functions_bindings.dart
index c4a01b3..7475b09 100644
--- a/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_functions_bindings.dart
+++ b/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_functions_bindings.dart
@@ -103,6 +103,20 @@
       void Function(ffi.Pointer<shortHand>,
           ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>();
 
+  void externInlineFunc(
+    int a,
+  ) {
+    return _externInlineFunc(
+      a,
+    );
+  }
+
+  late final _externInlineFuncPtr =
+      _lookup<ffi.NativeFunction<ffi.Void Function(ffi.Int)>>(
+          'externInlineFunc');
+  late final _externInlineFunc =
+      _externInlineFuncPtr.asFunction<void Function(int)>();
+
   int diffChars(
     int a,
     int b,
diff --git a/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_native_func_typedef_bindings.dart b/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_native_func_typedef_bindings.dart
index 6ef30ff..f77a57b 100644
--- a/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_native_func_typedef_bindings.dart
+++ b/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_native_func_typedef_bindings.dart
@@ -64,7 +64,7 @@
       _funcWithNativeFuncPtr.asFunction<void Function(WithTypedefReturnType)>();
 }
 
-class Struct extends ffi.Struct {
+final class Struct extends ffi.Struct {
   external ffi.Pointer<
       ffi.NativeFunction<
           ffi.Void Function(
@@ -76,7 +76,7 @@
     = ffi.Pointer<ffi.NativeFunction<InsideReturnType Function()>>;
 typedef InsideReturnType = ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>;
 
-class Struct2 extends ffi.Struct {
+final class Struct2 extends ffi.Struct {
   external VoidFuncPointer constFuncPointer;
 }
 
diff --git a/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_opaque_dependencies_bindings.dart b/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_opaque_dependencies_bindings.dart
index c53f04f..6481420 100644
--- a/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_opaque_dependencies_bindings.dart
+++ b/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_opaque_dependencies_bindings.dart
@@ -51,36 +51,36 @@
 
 typedef BAlias = B;
 
-class B extends ffi.Opaque {}
+final class B extends ffi.Opaque {}
 
-class A extends ffi.Opaque {}
+final class A extends ffi.Opaque {}
 
-class E extends ffi.Struct {
+final class E extends ffi.Struct {
   external ffi.Pointer<C> c;
 
   external D d;
 }
 
-class C extends ffi.Opaque {}
+final class C extends ffi.Opaque {}
 
-class D extends ffi.Struct {
+final class D extends ffi.Struct {
   @ffi.Int()
   external int a;
 }
 
-class UB extends ffi.Opaque {}
+final class UB extends ffi.Opaque {}
 
-class UA extends ffi.Opaque {}
+final class UA extends ffi.Opaque {}
 
-class UE extends ffi.Union {
+final class UE extends ffi.Union {
   external ffi.Pointer<UC> c;
 
   external UD d;
 }
 
-class UC extends ffi.Opaque {}
+final class UC extends ffi.Opaque {}
 
-class UD extends ffi.Union {
+final class UD extends ffi.Union {
   @ffi.Int()
   external int a;
 }
diff --git a/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_packed_structs_bindings.dart b/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_packed_structs_bindings.dart
index 9dfabd2..dc2ea75 100644
--- a/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_packed_structs_bindings.dart
+++ b/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_packed_structs_bindings.dart
@@ -4,13 +4,13 @@
 // ignore_for_file: type=lint
 import 'dart:ffi' as ffi;
 
-class NormalStruct1 extends ffi.Struct {
+final class NormalStruct1 extends ffi.Struct {
   @ffi.Char()
   external int a;
 }
 
 /// Should not be packed.
-class StructWithAttr extends ffi.Struct {
+final class StructWithAttr extends ffi.Struct {
   external ffi.Pointer<ffi.Int> a;
 
   external ffi.Pointer<ffi.Int> b;
@@ -18,33 +18,33 @@
 
 /// Should be packed with 1.
 @ffi.Packed(1)
-class PackedAttr extends ffi.Struct {
+final class PackedAttr extends ffi.Struct {
   @ffi.Int()
   external int a;
 }
 
 /// Should be packed with 8.
 @ffi.Packed(8)
-class PackedAttrAlign8 extends ffi.Struct {
+final class PackedAttrAlign8 extends ffi.Struct {
   @ffi.Int()
   external int a;
 }
 
 /// Should be packed with 2.
 @ffi.Packed(2)
-class Pack2WithPragma extends ffi.Struct {
+final class Pack2WithPragma extends ffi.Struct {
   @ffi.Int()
   external int a;
 }
 
 /// Should be packed with 4.
 @ffi.Packed(4)
-class Pack4WithPragma extends ffi.Struct {
+final class Pack4WithPragma extends ffi.Struct {
   @ffi.LongLong()
   external int a;
 }
 
-class NormalStruct2 extends ffi.Struct {
+final class NormalStruct2 extends ffi.Struct {
   @ffi.Char()
   external int a;
 }
diff --git a/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_struct_fptr_fields_bindings.dart b/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_struct_fptr_fields_bindings.dart
index b0490b2..2b2fe8a 100644
--- a/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_struct_fptr_fields_bindings.dart
+++ b/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_struct_fptr_fields_bindings.dart
@@ -4,7 +4,7 @@
 // ignore_for_file: type=lint
 import 'dart:ffi' as ffi;
 
-class S extends ffi.Struct {
+final class S extends ffi.Struct {
   external ffi.Pointer<ffi.NativeFunction<ffi.Int Function()>> func1;
 
   external ffi
diff --git a/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_typedef_bindings.dart b/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_typedef_bindings.dart
index 412acb2..885f641 100644
--- a/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_typedef_bindings.dart
+++ b/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_typedef_bindings.dart
@@ -6,6 +6,7 @@
 // ignore_for_file: type=lint
 import 'dart:ffi' as ffi;
 
+/// Typedef Test
 class Bindings {
   /// Holds the symbol lookup function.
   final ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName)
@@ -84,7 +85,7 @@
       _func4Ptr.asFunction<bool Function(ffi.Pointer<ffi.Bool>)>();
 }
 
-class Struct1 extends ffi.Struct {
+final class Struct1 extends ffi.Struct {
   external NamedFunctionProto named;
 
   external ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> unnamed;
@@ -93,14 +94,14 @@
 typedef NamedFunctionProto
     = ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>;
 
-class AnonymousStructInTypedef extends ffi.Opaque {}
+final class AnonymousStructInTypedef extends ffi.Opaque {}
 
-class _NamedStructInTypedef extends ffi.Opaque {}
+final class _NamedStructInTypedef extends ffi.Opaque {}
 
 typedef NTyperef1 = ExcludedStruct;
 typedef ExcludedStruct = _ExcludedStruct;
 
-class _ExcludedStruct extends ffi.Opaque {}
+final class _ExcludedStruct extends ffi.Opaque {}
 
 abstract class AnonymousEnumInTypedef {
   static const int a = 0;
@@ -112,9 +113,9 @@
 
 typedef NestingASpecifiedType = ffi.IntPtr;
 
-class Struct2 extends ffi.Opaque {}
+final class Struct2 extends ffi.Opaque {}
 
-class WithBoolAlias extends ffi.Struct {
+final class WithBoolAlias extends ffi.Struct {
   @ffi.Bool()
   external bool b;
 }
diff --git a/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_unions_bindings.dart b/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_unions_bindings.dart
index b35cd3d..9ef50eb 100644
--- a/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_unions_bindings.dart
+++ b/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_unions_bindings.dart
@@ -49,33 +49,35 @@
       _func2Ptr.asFunction<void Function(ffi.Pointer<Union3>)>();
 }
 
-class Union1 extends ffi.Union {
+final class Union1 extends ffi.Union {
   @ffi.Int()
   external int a;
 }
 
-class Union2 extends ffi.Union {
+final class Union2 extends ffi.Union {
   external Union1 a;
 }
 
-class Union3 extends ffi.Opaque {}
+final class Union3 extends ffi.Opaque {}
 
-class Union4 extends ffi.Opaque {}
+final class Union4 extends ffi.Opaque {}
 
-class Union5 extends ffi.Opaque {}
+final class Union5 extends ffi.Opaque {}
 
-class Union6 extends ffi.Union {
+final class Union6 extends ffi.Union {
   external UnnamedUnion1 unnamed;
 
   external UnnamedUnion2 unnamed1;
 }
 
-class UnnamedUnion1 extends ffi.Union {
+final class UnnamedUnion1 extends ffi.Union {
   @ffi.Float()
   external double a;
 }
 
-class UnnamedUnion2 extends ffi.Union {
+final class UnnamedUnion2 extends ffi.Union {
   @ffi.Float()
   external double b;
 }
+
+final class Union7 extends ffi.Opaque {}
diff --git a/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_varargs_bindings.dart b/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_varargs_bindings.dart
new file mode 100644
index 0000000..5b51bcd
--- /dev/null
+++ b/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_varargs_bindings.dart
@@ -0,0 +1,196 @@
+// ignore_for_file: camel_case_types
+
+// AUTO GENERATED FILE, DO NOT EDIT.
+//
+// Generated by `package:ffigen`.
+// ignore_for_file: type=lint
+import 'dart:ffi' as ffi;
+
+/// VarArgs Test
+class NativeLibrary {
+  /// Holds the symbol lookup function.
+  final ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName)
+      _lookup;
+
+  /// The symbols are looked up in [dynamicLibrary].
+  NativeLibrary(ffi.DynamicLibrary dynamicLibrary)
+      : _lookup = dynamicLibrary.lookup;
+
+  /// The symbols are looked up with [lookup].
+  NativeLibrary.fromLookup(
+      ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName)
+          lookup)
+      : _lookup = lookup;
+
+  int myfunc(
+    int a,
+    int va,
+    ffi.Pointer<ffi.Char> va1,
+    SA va2,
+  ) {
+    return _myfunc(
+      a,
+      va,
+      va1,
+      va2,
+    );
+  }
+
+  late final _myfuncPtr = _lookup<
+      ffi.NativeFunction<
+          ffi.Int Function(
+              ffi.Int,
+              ffi.VarArgs<
+                  (
+                    ffi.Int,
+                    ffi.Pointer<ffi.Char>,
+                    SA,
+                  )>)>>('myfunc');
+  late final _myfunc = _myfuncPtr
+      .asFunction<int Function(int, int, ffi.Pointer<ffi.Char>, SA)>();
+
+  void myfunc2CharPtrLongPtrPtr(
+    int a,
+    int b,
+    ffi.Pointer<ffi.Char> va,
+    ffi.Pointer<ffi.Pointer<ffi.Long>> va1,
+  ) {
+    return _myfunc2CharPtrLongPtrPtr(
+      a,
+      b,
+      va,
+      va1,
+    );
+  }
+
+  late final _myfunc2CharPtrLongPtrPtrPtr = _lookup<
+      ffi.NativeFunction<
+          ffi.Void Function(
+              ffi.Char,
+              ffi.Char,
+              ffi.VarArgs<
+                  (
+                    ffi.Pointer<ffi.Char>,
+                    ffi.Pointer<ffi.Pointer<ffi.Long>>,
+                  )>)>>('myfunc2');
+  late final _myfunc2CharPtrLongPtrPtr =
+      _myfunc2CharPtrLongPtrPtrPtr.asFunction<
+          void Function(int, int, ffi.Pointer<ffi.Char>,
+              ffi.Pointer<ffi.Pointer<ffi.Long>>)>();
+
+  void myfunc2SAIntPtrUnsignedcharPtrPtr(
+    int a,
+    int b,
+    SA va,
+    ffi.Pointer<ffi.Int> va1,
+    ffi.Pointer<ffi.Pointer<ffi.UnsignedChar>> va2,
+  ) {
+    return _myfunc2SAIntPtrUnsignedcharPtrPtr(
+      a,
+      b,
+      va,
+      va1,
+      va2,
+    );
+  }
+
+  late final _myfunc2SAIntPtrUnsignedcharPtrPtrPtr = _lookup<
+      ffi.NativeFunction<
+          ffi.Void Function(
+              ffi.Char,
+              ffi.Char,
+              ffi.VarArgs<
+                  (
+                    SA,
+                    ffi.Pointer<ffi.Int>,
+                    ffi.Pointer<ffi.Pointer<ffi.UnsignedChar>>,
+                  )>)>>('myfunc2');
+  late final _myfunc2SAIntPtrUnsignedcharPtrPtr =
+      _myfunc2SAIntPtrUnsignedcharPtrPtrPtr.asFunction<
+          void Function(int, int, SA, ffi.Pointer<ffi.Int>,
+              ffi.Pointer<ffi.Pointer<ffi.UnsignedChar>>)>();
+
+  void myfunc2_custompostfix(
+    int a,
+    int b,
+    SA va,
+    ffi.Pointer<ffi.Int> va1,
+    ffi.Pointer<ffi.Pointer<ffi.UnsignedChar>> va2,
+  ) {
+    return _myfunc2_custompostfix(
+      a,
+      b,
+      va,
+      va1,
+      va2,
+    );
+  }
+
+  late final _myfunc2_custompostfixPtr = _lookup<
+      ffi.NativeFunction<
+          ffi.Void Function(
+              ffi.Char,
+              ffi.Char,
+              ffi.VarArgs<
+                  (
+                    SA,
+                    ffi.Pointer<ffi.Int>,
+                    ffi.Pointer<ffi.Pointer<ffi.UnsignedChar>>,
+                  )>)>>('myfunc2');
+  late final _myfunc2_custompostfix = _myfunc2_custompostfixPtr.asFunction<
+      void Function(int, int, SA, ffi.Pointer<ffi.Int>,
+          ffi.Pointer<ffi.Pointer<ffi.UnsignedChar>>)>();
+
+  void myfunc3Struct_WithLong_Name_testPtrFloatPtr(
+    int a,
+    ffi.Pointer<Struct_WithLong_Name_test> va,
+    ffi.Pointer<ffi.Float> va1,
+  ) {
+    return _myfunc3Struct_WithLong_Name_testPtrFloatPtr(
+      a,
+      va,
+      va1,
+    );
+  }
+
+  late final _myfunc3Struct_WithLong_Name_testPtrFloatPtrPtr = _lookup<
+      ffi.NativeFunction<
+          ffi.Void Function(
+              ffi.Long,
+              ffi.VarArgs<
+                  (
+                    ffi.Pointer<Struct_WithLong_Name_test>,
+                    ffi.Pointer<ffi.Float>,
+                  )>)>>('myfunc3');
+  late final _myfunc3Struct_WithLong_Name_testPtrFloatPtr =
+      _myfunc3Struct_WithLong_Name_testPtrFloatPtrPtr.asFunction<
+          void Function(int, ffi.Pointer<Struct_WithLong_Name_test>,
+              ffi.Pointer<ffi.Float>)>();
+
+  void myfunc3_custompostfix2(
+    int a,
+    Struct_WithLong_Name_test va,
+  ) {
+    return _myfunc3_custompostfix2(
+      a,
+      va,
+    );
+  }
+
+  late final _myfunc3_custompostfix2Ptr = _lookup<
+      ffi.NativeFunction<
+          ffi.Void Function(
+              ffi.Long, ffi.VarArgs<(Struct_WithLong_Name_test,)>)>>('myfunc3');
+  late final _myfunc3_custompostfix2 = _myfunc3_custompostfix2Ptr
+      .asFunction<void Function(int, Struct_WithLong_Name_test)>();
+}
+
+final class SA extends ffi.Struct {
+  @ffi.Int()
+  external int a;
+}
+
+final class Struct_WithLong_Name_test extends ffi.Struct {
+  @ffi.Int()
+  external int a;
+}
diff --git a/pkgs/ffigen/test/header_parser_tests/function_n_struct.h b/pkgs/ffigen/test/header_parser_tests/function_n_struct.h
index e428cf3..b3c43a7 100644
--- a/pkgs/ffigen/test/header_parser_tests/function_n_struct.h
+++ b/pkgs/ffigen/test/header_parser_tests/function_n_struct.h
@@ -40,6 +40,14 @@
     arr10 a[2];
 };
 
+// All members should be removed, Zero-length arrays are equivalent to
+// flexible arrays members (by an extension) are not supported.
+struct Struct7
+{
+    int a;
+    int b[0]; // Flexible array member.
+};
+
 void func1(struct Struct2 *s);
 
 // Incomplete array parameter will be treated as a pointer.
diff --git a/pkgs/ffigen/test/header_parser_tests/function_n_struct_test.dart b/pkgs/ffigen/test/header_parser_tests/function_n_struct_test.dart
index b61070c..5ddd58a 100644
--- a/pkgs/ffigen/test/header_parser_tests/function_n_struct_test.dart
+++ b/pkgs/ffigen/test/header_parser_tests/function_n_struct_test.dart
@@ -58,6 +58,9 @@
       expect(actual.getBindingAsString('Struct6'),
           expected.getBindingAsString('Struct6'));
     });
+    test('Struct7 zero length array (extension on flexible array member)', () {
+      expect((actual.getBinding('Struct7') as Struct).members.isEmpty, true);
+    });
     test('func3 constant typedef array parameter', () {
       expect(actual.getBindingAsString('func3'),
           expected.getBindingAsString('func3'));
@@ -117,6 +120,7 @@
       Struct(name: 'Struct6', members: [
         Member(name: 'a', type: ConstantArray(2, ConstantArray(10, intType)))
       ]),
+      Struct(name: 'Struct7'),
     ],
   );
 }
diff --git a/pkgs/ffigen/test/header_parser_tests/functions.h b/pkgs/ffigen/test/header_parser_tests/functions.h
index 28b8546..9cf6a3b 100644
--- a/pkgs/ffigen/test/header_parser_tests/functions.h
+++ b/pkgs/ffigen/test/header_parser_tests/functions.h
@@ -20,4 +20,7 @@
 // Should be skipped as inline functions are not supported.
 static inline void inlineFunc();
 
+// Not skipped since it is extern.
+extern inline void externInlineFunc(int a);
+
 char diffChars(unsigned char a, signed char b);
diff --git a/pkgs/ffigen/test/header_parser_tests/typedef_test.dart b/pkgs/ffigen/test/header_parser_tests/typedef_test.dart
index 51b90e6..5c373f5 100644
--- a/pkgs/ffigen/test/header_parser_tests/typedef_test.dart
+++ b/pkgs/ffigen/test/header_parser_tests/typedef_test.dart
@@ -19,6 +19,7 @@
       actual = parser.parse(
         testConfig('''
 ${strings.name}: 'Bindings'
+${strings.description}: 'Typedef Test'
 ${strings.output}: 'unused'
 
 ${strings.headers}:
diff --git a/pkgs/ffigen/test/header_parser_tests/unions.h b/pkgs/ffigen/test/header_parser_tests/unions.h
index e7d86ff..7f03834 100644
--- a/pkgs/ffigen/test/header_parser_tests/unions.h
+++ b/pkgs/ffigen/test/header_parser_tests/unions.h
@@ -46,6 +46,21 @@
     };
 };
 
+// Multiple anonymous declarations with incomplete members
+union Union7
+{
+    union
+    {
+        float a;
+    };
+
+    union
+    {
+        float b;
+        int c[];
+    };
+};
+
 void func1(union Union2 *s);
 
 // Incomplete array parameter will be treated as a pointer.
diff --git a/pkgs/ffigen/test/header_parser_tests/varargs.h b/pkgs/ffigen/test/header_parser_tests/varargs.h
new file mode 100644
index 0000000..e316b90
--- /dev/null
+++ b/pkgs/ffigen/test/header_parser_tests/varargs.h
@@ -0,0 +1,15 @@
+// Copyright (c) 2023, 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.
+
+int myfunc(int a, ...);
+void myfunc2(char a, char b, ...);
+void myfunc3(long a, ...);
+
+struct SA {
+    int a;
+};
+
+struct Struct_WithLong_Name_test {
+    int a;
+};
diff --git a/pkgs/ffigen/test/header_parser_tests/varargs_test.dart b/pkgs/ffigen/test/header_parser_tests/varargs_test.dart
new file mode 100644
index 0000000..deaa5af
--- /dev/null
+++ b/pkgs/ffigen/test/header_parser_tests/varargs_test.dart
@@ -0,0 +1,59 @@
+// Copyright (c) 2023, 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/code_generator.dart';
+import 'package:ffigen/src/config_provider.dart';
+import 'package:ffigen/src/header_parser.dart' as parser;
+import 'package:ffigen/src/strings.dart' as strings;
+import 'package:test/test.dart';
+import 'package:yaml/yaml.dart' as yaml;
+
+import '../test_utils.dart';
+
+late Library actual, expected;
+
+void main() {
+  group('varargs_test', () {
+    setUpAll(() {
+      logWarnings();
+      actual = parser.parse(
+        Config.fromYaml(yaml.loadYaml('''
+${strings.name}: 'NativeLibrary'
+${strings.description}: 'VarArgs Test'
+${strings.output}: 'unused'
+
+${strings.headers}:
+  ${strings.entryPoints}:
+    - 'test/header_parser_tests/varargs.h'
+
+${strings.functions}:
+  ${strings.varArgFunctions}:
+    myfunc:
+      - [int, char*, SA]
+    myfunc2:
+      - [char*, long**]
+      - [SA, int*, unsigned char**]
+      - types: [SA, int*, unsigned char**]
+        postfix: _custompostfix
+    myfunc3:
+      - [Struct_WithLong_Name_test*, float*]
+      - types: [Struct_WithLong_Name_test]
+        postfix: _custompostfix2
+
+${strings.preamble}: |
+  // ignore_for_file: camel_case_types
+        ''') as yaml.YamlMap),
+      );
+    });
+    test('Expected Bindings', () {
+      matchLibraryWithExpected(
+          actual, 'header_parser_varargs_test_output.dart', [
+        'test',
+        'header_parser_tests',
+        'expected_bindings',
+        '_expected_varargs_bindings.dart'
+      ]);
+    });
+  });
+}
diff --git a/pkgs/ffigen/test/large_integration_tests/_expected_cjson_bindings.dart b/pkgs/ffigen/test/large_integration_tests/_expected_cjson_bindings.dart
index fe6b008..b93c74f 100644
--- a/pkgs/ffigen/test/large_integration_tests/_expected_cjson_bindings.dart
+++ b/pkgs/ffigen/test/large_integration_tests/_expected_cjson_bindings.dart
@@ -1215,7 +1215,7 @@
       _cJSON_freePtr.asFunction<void Function(ffi.Pointer<ffi.Void>)>();
 }
 
-class cJSON extends ffi.Struct {
+final class cJSON extends ffi.Struct {
   external ffi.Pointer<cJSON> next;
 
   external ffi.Pointer<cJSON> prev;
@@ -1236,7 +1236,7 @@
   external ffi.Pointer<ffi.Char> string;
 }
 
-class cJSON_Hooks extends ffi.Struct {
+final class cJSON_Hooks extends ffi.Struct {
   external ffi.Pointer<
           ffi.NativeFunction<ffi.Pointer<ffi.Void> Function(ffi.Size sz)>>
       malloc_fn;
diff --git a/pkgs/ffigen/test/large_integration_tests/_expected_libclang_bindings.dart b/pkgs/ffigen/test/large_integration_tests/_expected_libclang_bindings.dart
index 510e0b0..22c6f266 100644
--- a/pkgs/ffigen/test/large_integration_tests/_expected_libclang_bindings.dart
+++ b/pkgs/ffigen/test/large_integration_tests/_expected_libclang_bindings.dart
@@ -6036,37 +6036,37 @@
 }
 
 /// A character string.
-class CXString extends ffi.Struct {
+final class CXString extends ffi.Struct {
   external ffi.Pointer<ffi.Void> data;
 
   @ffi.UnsignedInt()
   external int private_flags;
 }
 
-class CXStringSet extends ffi.Struct {
+final class CXStringSet extends ffi.Struct {
   external ffi.Pointer<CXString> Strings;
 
   @ffi.UnsignedInt()
   external int Count;
 }
 
-class CXVirtualFileOverlayImpl extends ffi.Opaque {}
+final class CXVirtualFileOverlayImpl extends ffi.Opaque {}
 
 /// Object encapsulating information about overlaying virtual file/directories
 /// over the real file system.
 typedef CXVirtualFileOverlay = ffi.Pointer<CXVirtualFileOverlayImpl>;
 
-class CXModuleMapDescriptorImpl extends ffi.Opaque {}
+final class CXModuleMapDescriptorImpl extends ffi.Opaque {}
 
 /// Object encapsulating information about a module.map file.
 typedef CXModuleMapDescriptor = ffi.Pointer<CXModuleMapDescriptorImpl>;
 
-class CXTargetInfoImpl extends ffi.Opaque {}
+final class CXTargetInfoImpl extends ffi.Opaque {}
 
-class CXTranslationUnitImpl extends ffi.Opaque {}
+final class CXTranslationUnitImpl extends ffi.Opaque {}
 
 /// Provides the contents of a file that has not yet been saved to disk.
-class CXUnsavedFile extends ffi.Struct {
+final class CXUnsavedFile extends ffi.Struct {
   /// The file whose contents have not yet been saved.
   external ffi.Pointer<ffi.Char> Filename;
 
@@ -6098,7 +6098,7 @@
 }
 
 /// Describes a version number of the form major.minor.subminor.
-class CXVersion extends ffi.Struct {
+final class CXVersion extends ffi.Struct {
   /// The major version number, e.g., the '10' in '10.7.3'. A negative value
   /// indicates that there is no version number at all.
   @ffi.Int()
@@ -6175,7 +6175,7 @@
 
 /// Uniquely identifies a CXFile, that refers to the same underlying file,
 /// across an indexing session.
-class CXFileUniqueID extends ffi.Struct {
+final class CXFileUniqueID extends ffi.Struct {
   @ffi.Array.multi([3])
   external ffi.Array<ffi.UnsignedLongLong> data;
 }
@@ -6184,7 +6184,7 @@
 typedef CXTranslationUnit = ffi.Pointer<CXTranslationUnitImpl>;
 
 /// Identifies a specific source location within a translation unit.
-class CXSourceLocation extends ffi.Struct {
+final class CXSourceLocation extends ffi.Struct {
   @ffi.Array.multi([2])
   external ffi.Array<ffi.Pointer<ffi.Void>> ptr_data;
 
@@ -6193,7 +6193,7 @@
 }
 
 /// Identifies a half-open character range in the source code.
-class CXSourceRange extends ffi.Struct {
+final class CXSourceRange extends ffi.Struct {
   @ffi.Array.multi([2])
   external ffi.Array<ffi.Pointer<ffi.Void>> ptr_data;
 
@@ -6205,7 +6205,7 @@
 }
 
 /// Identifies an array of ranges.
-class CXSourceRangeList extends ffi.Struct {
+final class CXSourceRangeList extends ffi.Struct {
   /// The number of ranges in the ranges array.
   @ffi.UnsignedInt()
   external int count;
@@ -6399,7 +6399,7 @@
   static const int CXTUResourceUsage_Last = 14;
 }
 
-class CXTUResourceUsageEntry extends ffi.Struct {
+final class CXTUResourceUsageEntry extends ffi.Struct {
   @ffi.Int32()
   external int kind;
 
@@ -6408,7 +6408,7 @@
 }
 
 /// The memory usage of a CXTranslationUnit, broken into categories.
-class CXTUResourceUsage extends ffi.Struct {
+final class CXTUResourceUsage extends ffi.Struct {
   external ffi.Pointer<ffi.Void> data;
 
   @ffi.UnsignedInt()
@@ -7074,7 +7074,7 @@
 
 /// A cursor representing some element in the abstract syntax tree for a
 /// translation unit.
-class CXCursor extends ffi.Struct {
+final class CXCursor extends ffi.Struct {
   @ffi.Int32()
   external int kind;
 
@@ -7123,7 +7123,7 @@
 
 /// Describes the availability of a given entity on a particular platform, e.g.,
 /// a particular class might only be available on Mac OS 10.7 or newer.
-class CXPlatformAvailability extends ffi.Struct {
+final class CXPlatformAvailability extends ffi.Struct {
   /// A string that describes the platform for which this structure provides
   /// availability information.
   external CXString Platform;
@@ -7164,7 +7164,7 @@
   static const int CXTLS_Static = 2;
 }
 
-class CXCursorSetImpl extends ffi.Opaque {}
+final class CXCursorSetImpl extends ffi.Opaque {}
 
 /// A fast container representing a set of CXCursors.
 typedef CXCursorSet = ffi.Pointer<CXCursorSetImpl>;
@@ -7321,7 +7321,7 @@
 }
 
 /// The type of an element in the abstract syntax tree.
-class CXType extends ffi.Struct {
+final class CXType extends ffi.Struct {
   @ffi.Int32()
   external int kind;
 
@@ -7538,7 +7538,7 @@
 }
 
 /// Describes a single preprocessing token.
-class CXToken extends ffi.Struct {
+final class CXToken extends ffi.Struct {
   @ffi.Array.multi([4])
   external ffi.Array<ffi.UnsignedInt> int_data;
 
@@ -7546,7 +7546,7 @@
 }
 
 /// A single result of code completion.
-class CXCompletionResult extends ffi.Struct {
+final class CXCompletionResult extends ffi.Struct {
   /// The kind of entity that this completion refers to.
   @ffi.Int32()
   external int CursorKind;
@@ -7633,7 +7633,7 @@
 }
 
 /// Contains the results of code-completion.
-class CXCodeCompleteResults extends ffi.Struct {
+final class CXCodeCompleteResults extends ffi.Struct {
   /// The code-completion results.
   external ffi.Pointer<CXCompletionResult> Results;
 
@@ -7791,7 +7791,7 @@
   static const int CXVisit_Continue = 1;
 }
 
-class CXCursorAndRangeVisitor extends ffi.Struct {
+final class CXCursorAndRangeVisitor extends ffi.Struct {
   external ffi.Pointer<ffi.Void> context;
 
   external ffi.Pointer<
@@ -7812,7 +7812,7 @@
 }
 
 /// Source location passed to index callbacks.
-class CXIdxLoc extends ffi.Struct {
+final class CXIdxLoc extends ffi.Struct {
   @ffi.Array.multi([2])
   external ffi.Array<ffi.Pointer<ffi.Void>> ptr_data;
 
@@ -7821,7 +7821,7 @@
 }
 
 /// Data for ppIncludedFile callback.
-class CXIdxIncludedFileInfo extends ffi.Struct {
+final class CXIdxIncludedFileInfo extends ffi.Struct {
   /// Location of '#' in the #include/#import directive.
   external CXIdxLoc hashLoc;
 
@@ -7843,7 +7843,7 @@
 }
 
 /// Data for IndexerCallbacks#importedASTFile.
-class CXIdxImportedASTFileInfo extends ffi.Struct {
+final class CXIdxImportedASTFileInfo extends ffi.Struct {
   /// Top level AST file containing the imported PCH, module or submodule.
   external CXFile file;
 
@@ -7915,7 +7915,7 @@
   static const int CXIdxAttr_IBOutletCollection = 3;
 }
 
-class CXIdxAttrInfo extends ffi.Struct {
+final class CXIdxAttrInfo extends ffi.Struct {
   @ffi.Int32()
   external int kind;
 
@@ -7924,7 +7924,7 @@
   external CXIdxLoc loc;
 }
 
-class CXIdxEntityInfo extends ffi.Struct {
+final class CXIdxEntityInfo extends ffi.Struct {
   @ffi.Int32()
   external int kind;
 
@@ -7946,11 +7946,11 @@
   external int numAttributes;
 }
 
-class CXIdxContainerInfo extends ffi.Struct {
+final class CXIdxContainerInfo extends ffi.Struct {
   external CXCursor cursor;
 }
 
-class CXIdxIBOutletCollectionAttrInfo extends ffi.Struct {
+final class CXIdxIBOutletCollectionAttrInfo extends ffi.Struct {
   external ffi.Pointer<CXIdxAttrInfo> attrInfo;
 
   external ffi.Pointer<CXIdxEntityInfo> objcClass;
@@ -7964,7 +7964,7 @@
   static const int CXIdxDeclFlag_Skipped = 1;
 }
 
-class CXIdxDeclInfo extends ffi.Struct {
+final class CXIdxDeclInfo extends ffi.Struct {
   external ffi.Pointer<CXIdxEntityInfo> entityInfo;
 
   external CXCursor cursor;
@@ -8008,14 +8008,14 @@
   static const int CXIdxObjCContainer_Implementation = 2;
 }
 
-class CXIdxObjCContainerDeclInfo extends ffi.Struct {
+final class CXIdxObjCContainerDeclInfo extends ffi.Struct {
   external ffi.Pointer<CXIdxDeclInfo> declInfo;
 
   @ffi.Int32()
   external int kind;
 }
 
-class CXIdxBaseClassInfo extends ffi.Struct {
+final class CXIdxBaseClassInfo extends ffi.Struct {
   external ffi.Pointer<CXIdxEntityInfo> base;
 
   external CXCursor cursor;
@@ -8023,7 +8023,7 @@
   external CXIdxLoc loc;
 }
 
-class CXIdxObjCProtocolRefInfo extends ffi.Struct {
+final class CXIdxObjCProtocolRefInfo extends ffi.Struct {
   external ffi.Pointer<CXIdxEntityInfo> protocol;
 
   external CXCursor cursor;
@@ -8031,14 +8031,14 @@
   external CXIdxLoc loc;
 }
 
-class CXIdxObjCProtocolRefListInfo extends ffi.Struct {
+final class CXIdxObjCProtocolRefListInfo extends ffi.Struct {
   external ffi.Pointer<ffi.Pointer<CXIdxObjCProtocolRefInfo>> protocols;
 
   @ffi.UnsignedInt()
   external int numProtocols;
 }
 
-class CXIdxObjCInterfaceDeclInfo extends ffi.Struct {
+final class CXIdxObjCInterfaceDeclInfo extends ffi.Struct {
   external ffi.Pointer<CXIdxObjCContainerDeclInfo> containerInfo;
 
   external ffi.Pointer<CXIdxBaseClassInfo> superInfo;
@@ -8046,7 +8046,7 @@
   external ffi.Pointer<CXIdxObjCProtocolRefListInfo> protocols;
 }
 
-class CXIdxObjCCategoryDeclInfo extends ffi.Struct {
+final class CXIdxObjCCategoryDeclInfo extends ffi.Struct {
   external ffi.Pointer<CXIdxObjCContainerDeclInfo> containerInfo;
 
   external ffi.Pointer<CXIdxEntityInfo> objcClass;
@@ -8058,7 +8058,7 @@
   external ffi.Pointer<CXIdxObjCProtocolRefListInfo> protocols;
 }
 
-class CXIdxObjCPropertyDeclInfo extends ffi.Struct {
+final class CXIdxObjCPropertyDeclInfo extends ffi.Struct {
   external ffi.Pointer<CXIdxDeclInfo> declInfo;
 
   external ffi.Pointer<CXIdxEntityInfo> getter;
@@ -8066,7 +8066,7 @@
   external ffi.Pointer<CXIdxEntityInfo> setter;
 }
 
-class CXIdxCXXClassDeclInfo extends ffi.Struct {
+final class CXIdxCXXClassDeclInfo extends ffi.Struct {
   external ffi.Pointer<CXIdxDeclInfo> declInfo;
 
   external ffi.Pointer<ffi.Pointer<CXIdxBaseClassInfo>> bases;
@@ -8100,7 +8100,7 @@
 }
 
 /// Data for IndexerCallbacks#indexEntityReference.
-class CXIdxEntityRefInfo extends ffi.Struct {
+final class CXIdxEntityRefInfo extends ffi.Struct {
   @ffi.Int32()
   external int kind;
 
@@ -8125,7 +8125,7 @@
 
 /// A group of callbacks used by #clang_indexSourceFile and
 /// #clang_indexTranslationUnit.
-class IndexerCallbacks extends ffi.Struct {
+final class IndexerCallbacks extends ffi.Struct {
   /// Called periodically to check whether indexing should be aborted. Should
   /// return 0 to continue, and non-zero to abort.
   external ffi.Pointer<
diff --git a/pkgs/ffigen/test/large_integration_tests/_expected_sqlite_bindings.dart b/pkgs/ffigen/test/large_integration_tests/_expected_sqlite_bindings.dart
index d3b48db..af6cccf 100644
--- a/pkgs/ffigen/test/large_integration_tests/_expected_sqlite_bindings.dart
+++ b/pkgs/ffigen/test/large_integration_tests/_expected_sqlite_bindings.dart
@@ -10718,20 +10718,20 @@
                       ffi.Void Function(ffi.Pointer<ffi.Void>)>>)>();
 }
 
-class sqlite3 extends ffi.Opaque {}
+final class sqlite3 extends ffi.Opaque {}
 
-class sqlite3_file extends ffi.Struct {
+final class sqlite3_file extends ffi.Struct {
   /// Methods for an open file
   external ffi.Pointer<sqlite3_io_methods> pMethods;
 }
 
-class sqlite3_io_methods extends ffi.Opaque {}
+final class sqlite3_io_methods extends ffi.Opaque {}
 
-class sqlite3_mutex extends ffi.Opaque {}
+final class sqlite3_mutex extends ffi.Opaque {}
 
-class sqlite3_api_routines extends ffi.Opaque {}
+final class sqlite3_api_routines extends ffi.Opaque {}
 
-class sqlite3_vfs extends ffi.Struct {
+final class sqlite3_vfs extends ffi.Struct {
   /// Structure version number (currently 3)
   @ffi.Int()
   external int iVersion;
@@ -10850,7 +10850,7 @@
 typedef sqlite3_syscall_ptr
     = ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>;
 
-class sqlite3_mem_methods extends ffi.Struct {
+final class sqlite3_mem_methods extends ffi.Struct {
   /// Memory allocation function
   external ffi
           .Pointer<ffi.NativeFunction<ffi.Pointer<ffi.Void> Function(ffi.Int)>>
@@ -10892,11 +10892,11 @@
 typedef sqlite3_uint64 = sqlite_uint64;
 typedef sqlite_uint64 = ffi.UnsignedLongLong;
 
-class sqlite3_stmt extends ffi.Opaque {}
+final class sqlite3_stmt extends ffi.Opaque {}
 
-class sqlite3_value extends ffi.Opaque {}
+final class sqlite3_value extends ffi.Opaque {}
 
-class sqlite3_context extends ffi.Opaque {}
+final class sqlite3_context extends ffi.Opaque {}
 
 /// CAPI3REF: Virtual Table Instance Object
 /// KEYWORDS: sqlite3_vtab
@@ -10914,7 +10914,7 @@
 /// prior to assigning a new string to zErrMsg.  ^After the error message
 /// is delivered up to the client application, the string will be automatically
 /// freed by sqlite3_free() and the zErrMsg field will be zeroed.
-class sqlite3_vtab extends ffi.Struct {
+final class sqlite3_vtab extends ffi.Struct {
   /// The module for this virtual table
   external ffi.Pointer<sqlite3_module> pModule;
 
@@ -10940,7 +10940,7 @@
 /// module or until the [database connection] closes.  The content
 /// of this structure must not change while it is registered with
 /// any database connection.
-class sqlite3_module extends ffi.Struct {
+final class sqlite3_module extends ffi.Struct {
   @ffi.Int()
   external int iVersion;
 
@@ -11181,7 +11181,7 @@
 /// It may therefore only be used if
 /// sqlite3_libversion_number() returns a value greater than or equal to
 /// 3009000.
-class sqlite3_index_info extends ffi.Struct {
+final class sqlite3_index_info extends ffi.Struct {
   /// Number of entries in aConstraint
   @ffi.Int()
   external int nConstraint;
@@ -11230,7 +11230,7 @@
   external int colUsed;
 }
 
-class sqlite3_index_constraint extends ffi.Struct {
+final class sqlite3_index_constraint extends ffi.Struct {
   /// Column constrained.  -1 for ROWID
   @ffi.Int()
   external int iColumn;
@@ -11248,7 +11248,7 @@
   external int iTermOffset;
 }
 
-class sqlite3_index_orderby extends ffi.Struct {
+final class sqlite3_index_orderby extends ffi.Struct {
   /// Column number
   @ffi.Int()
   external int iColumn;
@@ -11259,7 +11259,7 @@
 }
 
 /// Outputs
-class sqlite3_index_constraint_usage extends ffi.Struct {
+final class sqlite3_index_constraint_usage extends ffi.Struct {
   /// if >0, constraint is part of argv to xFilter
   @ffi.Int()
   external int argvIndex;
@@ -11284,14 +11284,14 @@
 ///
 /// This superclass exists in order to define fields of the cursor that
 /// are common to all implementations.
-class sqlite3_vtab_cursor extends ffi.Struct {
+final class sqlite3_vtab_cursor extends ffi.Struct {
   /// Virtual table of this cursor
   external ffi.Pointer<sqlite3_vtab> pVtab;
 }
 
-class sqlite3_blob extends ffi.Opaque {}
+final class sqlite3_blob extends ffi.Opaque {}
 
-class sqlite3_mutex_methods extends ffi.Struct {
+final class sqlite3_mutex_methods extends ffi.Struct {
   external ffi.Pointer<ffi.NativeFunction<ffi.Int Function()>> xMutexInit;
 
   external ffi.Pointer<ffi.NativeFunction<ffi.Int Function()>> xMutexEnd;
@@ -11325,11 +11325,11 @@
       xMutexNotheld;
 }
 
-class sqlite3_str extends ffi.Opaque {}
+final class sqlite3_str extends ffi.Opaque {}
 
-class sqlite3_pcache extends ffi.Opaque {}
+final class sqlite3_pcache extends ffi.Opaque {}
 
-class sqlite3_pcache_page extends ffi.Struct {
+final class sqlite3_pcache_page extends ffi.Struct {
   /// The content of the page
   external ffi.Pointer<ffi.Void> pBuf;
 
@@ -11337,7 +11337,7 @@
   external ffi.Pointer<ffi.Void> pExtra;
 }
 
-class sqlite3_pcache_methods2 extends ffi.Struct {
+final class sqlite3_pcache_methods2 extends ffi.Struct {
   @ffi.Int()
   external int iVersion;
 
@@ -11396,7 +11396,7 @@
       xShrink;
 }
 
-class sqlite3_pcache_methods extends ffi.Struct {
+final class sqlite3_pcache_methods extends ffi.Struct {
   external ffi.Pointer<ffi.Void> pArg;
 
   external ffi
@@ -11446,7 +11446,7 @@
       xDestroy;
 }
 
-class sqlite3_backup extends ffi.Opaque {}
+final class sqlite3_backup extends ffi.Opaque {}
 
 /// CAPI3REF: Database Snapshot
 /// KEYWORDS: {snapshot} {sqlite3_snapshot}
@@ -11466,14 +11466,14 @@
 /// version of the database file so that it is possible to later open a new read
 /// transaction that sees that historical version of the database rather than
 /// the most recent version.
-class sqlite3_snapshot extends ffi.Struct {
+final class sqlite3_snapshot extends ffi.Struct {
   @ffi.Array.multi([48])
   external ffi.Array<ffi.UnsignedChar> hidden;
 }
 
 /// A pointer to a structure of the following type is passed as the first
 /// argument to callbacks registered using rtree_geometry_callback().
-class sqlite3_rtree_geometry extends ffi.Struct {
+final class sqlite3_rtree_geometry extends ffi.Struct {
   /// Copy of pContext passed to s_r_g_c()
   external ffi.Pointer<ffi.Void> pContext;
 
@@ -11502,7 +11502,7 @@
 /// Note that the first 5 fields of this structure are identical to
 /// sqlite3_rtree_geometry.  This structure is a subclass of
 /// sqlite3_rtree_geometry.
-class sqlite3_rtree_query_info extends ffi.Struct {
+final class sqlite3_rtree_query_info extends ffi.Struct {
   /// pContext from when function registered
   external ffi.Pointer<ffi.Void> pContext;
 
@@ -11772,7 +11772,7 @@
 ///
 /// xPhraseNextColumn()
 /// See xPhraseFirstColumn above.
-class Fts5ExtensionApi extends ffi.Struct {
+final class Fts5ExtensionApi extends ffi.Struct {
   /// Currently always set to 3
   @ffi.Int()
   external int iVersion;
@@ -11912,17 +11912,17 @@
               ffi.Pointer<ffi.Int>)>> xPhraseNextColumn;
 }
 
-class Fts5Context extends ffi.Opaque {}
+final class Fts5Context extends ffi.Opaque {}
 
-class Fts5PhraseIter extends ffi.Struct {
+final class Fts5PhraseIter extends ffi.Struct {
   external ffi.Pointer<ffi.UnsignedChar> a;
 
   external ffi.Pointer<ffi.UnsignedChar> b;
 }
 
-class Fts5Tokenizer extends ffi.Opaque {}
+final class Fts5Tokenizer extends ffi.Opaque {}
 
-class fts5_tokenizer extends ffi.Struct {
+final class fts5_tokenizer extends ffi.Struct {
   external ffi.Pointer<
       ffi.NativeFunction<
           ffi.Int Function(
@@ -11954,7 +11954,7 @@
                           ffi.Int)>>)>> xTokenize;
 }
 
-class fts5_api extends ffi.Struct {
+final class fts5_api extends ffi.Struct {
   /// Currently always set to 2
   @ffi.Int()
   external int iVersion;
diff --git a/pkgs/ffigen/test/native_objc_test/cast_test.dart b/pkgs/ffigen/test/native_objc_test/cast_test.dart
index 55b787b..c63d940 100644
--- a/pkgs/ffigen/test/native_objc_test/cast_test.dart
+++ b/pkgs/ffigen/test/native_objc_test/cast_test.dart
@@ -40,6 +40,11 @@
       expect(fromCast, testInstance!);
     });
 
+    test('pointers are equal', () {
+      final meAsInt = testInstance!.meAsInt();
+      expect(testInstance!.pointer.address, meAsInt);
+    });
+
     test('equality equals', () {
       final meAsInt = testInstance!.meAsInt();
       final fromCast = Castaway.castFromPointer(
diff --git a/pkgs/ffigen/test/native_test/native_test_bindings.dart b/pkgs/ffigen/test/native_test/_expected_native_test_bindings.dart
similarity index 98%
rename from pkgs/ffigen/test/native_test/native_test_bindings.dart
rename to pkgs/ffigen/test/native_test/_expected_native_test_bindings.dart
index d4a25d6..58934cf 100644
--- a/pkgs/ffigen/test/native_test/native_test_bindings.dart
+++ b/pkgs/ffigen/test/native_test/_expected_native_test_bindings.dart
@@ -244,7 +244,7 @@
       _Function1StructPassByValuePtr.asFunction<int Function(Struct3)>();
 }
 
-class Struct1 extends ffi.Struct {
+final class Struct1 extends ffi.Struct {
   @ffi.Int8()
   external int a;
 
@@ -252,7 +252,7 @@
   external ffi.Array<ffi.Array<ffi.Array<ffi.Int32>>> data;
 }
 
-class Struct3 extends ffi.Struct {
+final class Struct3 extends ffi.Struct {
   @ffi.Int()
   external int a;
 
diff --git a/pkgs/ffigen/test/native_test/config.yaml b/pkgs/ffigen/test/native_test/config.yaml
index 901e559..3942f6f 100644
--- a/pkgs/ffigen/test/native_test/config.yaml
+++ b/pkgs/ffigen/test/native_test/config.yaml
@@ -8,7 +8,7 @@
 
 name: NativeLibrary
 description: 'Native tests.'
-output: 'native_test_bindings.dart'
+output: '_expected_native_test_bindings.dart'
 headers:
   entry-points:
     - 'native_test.c'
diff --git a/pkgs/ffigen/test/native_test/native_test.dart b/pkgs/ffigen/test/native_test/native_test.dart
index 7a19c2a..cc31709 100644
--- a/pkgs/ffigen/test/native_test/native_test.dart
+++ b/pkgs/ffigen/test/native_test/native_test.dart
@@ -10,7 +10,7 @@
 import 'package:path/path.dart' as path;
 import 'package:test/test.dart';
 import '../test_utils.dart';
-import 'native_test_bindings.dart';
+import '_expected_native_test_bindings.dart';
 
 void main() {
   late NativeLibrary bindings;
@@ -32,7 +32,8 @@
       final configFile =
           File(path.join('test', 'native_test', 'config.yaml')).absolute;
       final outFile = File(
-        path.join('test', 'debug_generated', 'native_test_bindings.dart'),
+        path.join(
+            'test', 'debug_generated', '_expected_native_test_bindings.dart'),
       ).absolute;
 
       late Config config;
diff --git a/pkgs/ffigen/test/regen.dart b/pkgs/ffigen/test/regen.dart
index 1ee0df3..1617eda 100644
--- a/pkgs/ffigen/test/regen.dart
+++ b/pkgs/ffigen/test/regen.dart
@@ -52,7 +52,7 @@
 
   final nativeTestConfig = File('test/native_test/config.yaml').absolute;
   final nativeTestOut =
-      File('test/native_test/native_test_bindings.dart').absolute;
+      File('test/native_test/_expected_native_test_bindings.dart').absolute;
   await withChDir(nativeTestConfig.path,
       () => _regenConfig(nativeTestConfig, nativeTestOut));
 
diff --git a/pkgs/ffigen/test/test_utils.dart b/pkgs/ffigen/test/test_utils.dart
index 50cb30e..95434ff 100644
--- a/pkgs/ffigen/test/test_utils.dart
+++ b/pkgs/ffigen/test/test_utils.dart
@@ -57,13 +57,13 @@
 /// This will not delete the actual debug file incase [expect] throws an error.
 void matchLibraryWithExpected(
     Library library, String pathForActual, List<String> pathToExpected,
-    {String Function(String)? codeNormalizer}) {
+    {String Function(String)? codeNormalizer, bool format = true}) {
   _matchFileWithExpected(
     library: library,
     pathForActual: pathForActual,
     pathToExpected: pathToExpected,
     fileWriter: ({required Library library, required File file}) =>
-        library.generateFile(file),
+        library.generateFile(file, format: format),
     codeNormalizer: codeNormalizer,
   );
 }
diff --git a/pkgs/ffigen/tool/generate_json_schema.dart b/pkgs/ffigen/tool/generate_json_schema.dart
new file mode 100644
index 0000000..7a5a030
--- /dev/null
+++ b/pkgs/ffigen/tool/generate_json_schema.dart
@@ -0,0 +1,28 @@
+// Copyright (c) 2023, 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.
+
+// ================== GENERATING JSON SCHEMA =====================
+//    cd to project's root, and run -
+//    dart generate_json_schema.dart
+// ===============================================================
+import 'dart:convert';
+import 'dart:io';
+
+import 'package:ffigen/ffigen.dart';
+import 'package:ffigen/src/strings.dart' as strings;
+
+void main() async {
+  final actualJsonSchema =
+      JsonEncoder.withIndent(strings.ffigenJsonSchemaIndent).convert(
+    Config.getsRootConfigSpec().generateJsonSchema(strings.ffigenJsonSchemaId),
+  );
+
+  final file = File(strings.ffigenJsonSchemaFileName);
+  if (!await file.exists()) {
+    throw Exception("File '${file.absolute.path}' does not exist.");
+  }
+  await file.writeAsString(actualJsonSchema);
+
+  print("Generated json schema: ${file.absolute.path}");
+}
diff --git a/pkgs/ffigen/tool/libclang_config.yaml b/pkgs/ffigen/tool/libclang_config.yaml
index 339c52c..b948e5c 100644
--- a/pkgs/ffigen/tool/libclang_config.yaml
+++ b/pkgs/ffigen/tool/libclang_config.yaml
@@ -9,6 +9,8 @@
 #    dart run ffigen --config tool/libclang_config.yaml
 # ===============================================================
 
+# yaml-language-server: $schema=../ffigen.schema.json
+
 name: Clang
 description: Holds bindings to LibClang.
 output: '../lib/src/header_parser/clang_bindings/clang_bindings.dart'
@@ -91,6 +93,8 @@
     - clang_Cursor_getArgument
     - clang_getNumArgTypes
     - clang_getArgType
+    - clang_isFunctionTypeVariadic
+    - clang_Cursor_getStorageClass
     - clang_getCursorResultType
     - clang_getEnumConstantDeclValue
     - clang_equalRanges