[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 @@ [](https://pub.dev/packages/ffigen) [](https://github.com/dart-lang/ffigen/actions?query=workflow%3A"Dart+CI") -[](https://coveralls.io/github/dart-lang/ffigen?branch=master) +[](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 @@ [](https://github.com/dart-lang/ffigen/actions?query=workflow%3A"Dart+CI") -[](https://coveralls.io/github/dart-lang/ffigen?branch=master) +[](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