[ffigen] Added support for marking functions as Leaf. (#252)

* Add support for specifying leaf functions.
* Remove folder debug_generated to prevent warning when publishing.
* Add an FAQ for explaining generated logs.
diff --git a/pkgs/ffigen/CHANGELOG.md b/pkgs/ffigen/CHANGELOG.md
index 02b880e..9cdf028 100644
--- a/pkgs/ffigen/CHANGELOG.md
+++ b/pkgs/ffigen/CHANGELOG.md
@@ -1,3 +1,6 @@
+# 4.1.0
+- Add config key `functions -> leaf` for specifying `isLeaf:true` for functions.
+
 # 4.0.0
 - Release for Dart SDK `>=2.14`.
 
diff --git a/pkgs/ffigen/README.md b/pkgs/ffigen/README.md
index e89ceb2..0cf4247 100644
--- a/pkgs/ffigen/README.md
+++ b/pkgs/ffigen/README.md
@@ -292,6 +292,28 @@
   </td>
   </tr>
   <tr>
+    <td>functions -> leaf</td>
+    <td>Set isLeaf:true for functions.<br>
+    <b>Default: all functions are excluded.</b>
+    </td>
+    <td>
+
+```yaml
+functions:
+  leaf:
+    include:
+      # Match function name.
+      - 'myFunc'
+       # Do this to set isLeaf:true for all functions.
+      - '.*'
+    exclude:
+      # If you only use exclude, then everything
+      # not excluded is generated.
+      - 'dispose'
+```
+  </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>
@@ -593,6 +615,25 @@
 ### Why are some typedefs not generated?
 
 The following typedefs are not generated -
-- They are not referred to anywhere in the included declarations.
-- They refer to a struct/union having the same name as itself.
-- They refer to a boolean, enum, inline array, Handle or any unsupported type.
+  - They are not referred to anywhere in the included declarations.
+  - They refer to a struct/union having the same name as itself.
+  - They refer to a boolean, enum, inline array, Handle or any unsupported type.
+
+### What are these logs generated by ffigen and how to fix them?
+
+Ffigen can sometimes generate a lot of logs, especially when it's parsing a lot of code.
+  - `SEVERE` logs are something you *definitely need to address*. They can be
+    caused due to syntax errors, or more generally missing header files
+    (which need to be specified using `compiler-opts` in config)
+  - `WARNING` logs are something *you can ignore*, but should probably look into.
+    These are mostly indications of declarations ffigen couldn't generate due
+    to limitations of dart:ffi, private declarations (which can be resolved
+    by renaming them via ffigen config) or other minor issues in the config
+    file itself.
+  - Everything else can be safely ignored. It's purpose is to simply
+    let you know what ffigen is doing.
+  - The verbosity of the logs can be changed by adding a flag with
+    the log level. E.g - `dart run ffigen --verbose <level>`.
+    Level options are - `[all, fine, info (default), warning, severe]`.
+    The `all` and `fine` will print a ton of logs are meant for debugging
+    purposes only.
diff --git a/pkgs/ffigen/lib/src/code_generator/func.dart b/pkgs/ffigen/lib/src/code_generator/func.dart
index e779c77..425ee47 100644
--- a/pkgs/ffigen/lib/src/code_generator/func.dart
+++ b/pkgs/ffigen/lib/src/code_generator/func.dart
@@ -32,6 +32,7 @@
   final FunctionType functionType;
   final bool exposeSymbolAddress;
   final bool exposeFunctionTypedefs;
+  final bool isLeaf;
 
   /// Contains typealias for function type if [exposeFunctionTypedefs] is true.
   Typealias? _exposedCFunctionTypealias, _exposedDartFunctionTypealias;
@@ -47,6 +48,7 @@
     List<Parameter>? parameters,
     this.exposeSymbolAddress = false,
     this.exposeFunctionTypedefs = false,
+    this.isLeaf = false,
   })  : functionType = FunctionType(
           returnType: returnType,
           parameters: parameters ?? const [],
@@ -152,8 +154,9 @@
     // Write function pointer.
     s.write(
         "late final $funcPointerName = ${w.lookupFuncIdentifier}<${w.ffiLibraryPrefix}.NativeFunction<$cType>>('$originalName');\n");
+    final isLeafString = isLeaf ? 'isLeaf:true' : '';
     s.write(
-        'late final $funcVarName = $funcPointerName.asFunction<$dartType>();\n\n');
+        'late final $funcVarName = $funcPointerName.asFunction<$dartType>($isLeafString);\n\n');
 
     return BindingString(type: BindingStringType.func, string: s.toString());
   }
diff --git a/pkgs/ffigen/lib/src/config_provider/config.dart b/pkgs/ffigen/lib/src/config_provider/config.dart
index 7d8c261..88d5d8d 100644
--- a/pkgs/ffigen/lib/src/config_provider/config.dart
+++ b/pkgs/ffigen/lib/src/config_provider/config.dart
@@ -119,6 +119,9 @@
   Includer get exposeFunctionTypedefs => _exposeFunctionTypedefs;
   late Includer _exposeFunctionTypedefs;
 
+  Includer get leafFunctions => _leafFunctions;
+  late Includer _leafFunctions;
+
   Config._();
 
   /// Create config from Yaml map.
@@ -416,6 +419,14 @@
         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,
+      ),
     };
   }
 }
diff --git a/pkgs/ffigen/lib/src/config_provider/spec_utils.dart b/pkgs/ffigen/lib/src/config_provider/spec_utils.dart
index 5acd29e..ad76cc9 100644
--- a/pkgs/ffigen/lib/src/config_provider/spec_utils.dart
+++ b/pkgs/ffigen/lib/src/config_provider/spec_utils.dart
@@ -592,6 +592,31 @@
   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:
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 7a0bb20..41d877c 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
@@ -80,6 +80,7 @@
           config.functionDecl.shouldIncludeSymbolAddress(funcName),
       exposeFunctionTypedefs:
           config.exposeFunctionTypedefs.shouldInclude(funcName),
+      isLeaf: config.leafFunctions.shouldInclude(funcName),
     );
     bindingsIndex.addFuncToSeen(funcUsr, _stack.top.func!);
   } else if (bindingsIndex.isSeenFunc(funcUsr)) {
diff --git a/pkgs/ffigen/lib/src/strings.dart b/pkgs/ffigen/lib/src/strings.dart
index 84e767c..5ae3e35 100644
--- a/pkgs/ffigen/lib/src/strings.dart
+++ b/pkgs/ffigen/lib/src/strings.dart
@@ -62,6 +62,7 @@
 
 // Nested under `functions`
 const exposeFunctionTypedefs = 'expose-typedefs';
+const leafFunctions = 'leaf';
 
 const dependencyOnly = 'dependency-only';
 // Values for `compoundDependencies`.
diff --git a/pkgs/ffigen/pubspec.yaml b/pkgs/ffigen/pubspec.yaml
index ae3782f..f4e7135 100644
--- a/pkgs/ffigen/pubspec.yaml
+++ b/pkgs/ffigen/pubspec.yaml
@@ -3,7 +3,7 @@
 # BSD-style license that can be found in the LICENSE file.
 
 name: ffigen
-version: 4.0.0
+version: 4.1.0
 homepage: https://github.com/dart-lang/ffigen
 description: Generator for FFI bindings, using LibClang to parse C header files.
 
diff --git a/pkgs/ffigen/test/code_generator_tests/code_generator_test.dart b/pkgs/ffigen/test/code_generator_tests/code_generator_test.dart
index 5d7f4c9..34905a4 100644
--- a/pkgs/ffigen/test/code_generator_tests/code_generator_test.dart
+++ b/pkgs/ffigen/test/code_generator_tests/code_generator_test.dart
@@ -67,6 +67,22 @@
               ),
             ),
           ),
+          Func(
+            isLeaf: true,
+            name: 'leafFunc',
+            dartDoc: 'A function with isLeaf: true',
+            parameters: [
+              Parameter(
+                name: 'a',
+                type: Type.nativeType(
+                  SupportedNativeType.Int32,
+                ),
+              ),
+            ],
+            returnType: Type.nativeType(
+              SupportedNativeType.Int32,
+            ),
+          ),
         ],
       );
 
diff --git a/pkgs/ffigen/test/code_generator_tests/expected_bindings/_expected_function_bindings.dart b/pkgs/ffigen/test/code_generator_tests/expected_bindings/_expected_function_bindings.dart
index 2a7dba5..ada968c 100644
--- a/pkgs/ffigen/test/code_generator_tests/expected_bindings/_expected_function_bindings.dart
+++ b/pkgs/ffigen/test/code_generator_tests/expected_bindings/_expected_function_bindings.dart
@@ -60,4 +60,18 @@
   late final _withPointerParam = _withPointerParamPtr.asFunction<
       ffi.Pointer<ffi.Double> Function(
           ffi.Pointer<ffi.Int32>, ffi.Pointer<ffi.Pointer<ffi.Uint8>>)>();
+
+  /// A function with isLeaf: true
+  int leafFunc(
+    int a,
+  ) {
+    return _leafFunc(
+      a,
+    );
+  }
+
+  late final _leafFuncPtr =
+      _lookup<ffi.NativeFunction<ffi.Int32 Function(ffi.Int32)>>('leafFunc');
+  late final _leafFunc =
+      _leafFuncPtr.asFunction<int Function(int)>(isLeaf: true);
 }
diff --git a/pkgs/ffigen/test/debug_generated/README.md b/pkgs/ffigen/test/debug_generated/README.md
deleted file mode 100644
index f8fe72f..0000000
--- a/pkgs/ffigen/test/debug_generated/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-This folder is used in tests.
-These files are deleted if tests are successful, but
-will contain debug files from failed tests.
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 a5e3276..d42ec32 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
@@ -27,7 +27,7 @@
 
   late final _func1Ptr =
       _lookup<ffi.NativeFunction<ffi.Void Function()>>('func1');
-  late final _func1 = _func1Ptr.asFunction<void Function()>();
+  late final _func1 = _func1Ptr.asFunction<void Function()>(isLeaf: true);
 
   int func2(
     int arg0,
diff --git a/pkgs/ffigen/test/header_parser_tests/functions_test.dart b/pkgs/ffigen/test/header_parser_tests/functions_test.dart
index 87a8642..7b07562 100644
--- a/pkgs/ffigen/test/header_parser_tests/functions_test.dart
+++ b/pkgs/ffigen/test/header_parser_tests/functions_test.dart
@@ -34,6 +34,9 @@
     ${strings.include}:
       - func3
       - func4
+  ${strings.leafFunctions}:
+    ${strings.include}:
+      - func1
 
 ${strings.preamble}: |
   // ignore_for_file: camel_case_types