From e81ed9624416c7a1b1c3a755728193ef44d29025 Mon Sep 17 00:00:00 2001 From: Liam Appelbe Date: Tue, 2 Dec 2025 13:23:19 +1100 Subject: [PATCH 01/19] Remove header index --- .../clang_bindings/clang_bindings.dart | 13 ++++++ .../translation_unit_parser.dart | 42 ++++++------------- pkgs/ffigen/lib/src/header_parser/utils.dart | 19 ++------- pkgs/ffigen/tool/libclang_config.yaml | 1 + 4 files changed, 31 insertions(+), 44 deletions(-) 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 e8a47b5fed..5103c92876 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 @@ -1156,6 +1156,19 @@ class Clang { late final _clang_getCursorDefinition = _clang_getCursorDefinitionPtr .asFunction(); + /// Determine whether the declaration pointed to by this cursor + /// is also a definition of that entity. + int clang_isCursorDefinition(CXCursor arg0) { + return _clang_isCursorDefinition(arg0); + } + + late final _clang_isCursorDefinitionPtr = + _lookup>( + 'clang_isCursorDefinition', + ); + late final _clang_isCursorDefinition = _clang_isCursorDefinitionPtr + .asFunction(); + /// Given a cursor that represents a property declaration, return the /// associated property attributes. The bits are formed from /// \c CXObjCPropertyAttrKind. 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 284c6b2791..6e861cdf2b 100644 --- a/pkgs/ffigen/lib/src/header_parser/translation_unit_parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/translation_unit_parser.dart @@ -20,10 +20,13 @@ Set parseTranslationUnit( ) { final bindings = {}; final logger = context.logger; + final headers = {}; translationUnitCursor.visitChildren((cursor) { - try { - if (shouldIncludeRootCursor(context, cursor.sourceFileName())) { + final file = cursor.sourceFileName(); + if (file.isEmpty) return; + if (headers[file] ??= context.config.shouldIncludeHeader(Uri.file(file))) { + try { logger.finest('rootCursorVisitor: ${cursor.completeStringRepr()}'); switch (clang.clang_getCursorKind(cursor)) { case clang_types.CXCursorKind.CXCursor_FunctionDecl: @@ -57,15 +60,15 @@ Set parseTranslationUnit( default: logger.finer('rootCursorVisitor: CursorKind not implemented'); } - } else { - logger.finest( - 'rootCursorVisitor:(not included) ${cursor.completeStringRepr()}', - ); + } catch (e, s) { + logger.severe(e); + logger.severe(s); + rethrow; } - } catch (e, s) { - logger.severe(e); - logger.severe(s); - rethrow; + } else { + logger.finest( + 'rootCursorVisitor:(not included) ${cursor.completeStringRepr()}', + ); } }); @@ -104,22 +107,3 @@ void buildUsrCursorDefinitionMap( } }); } - -/// True if a cursor should be included based on headers config, used on root -/// declarations. -bool shouldIncludeRootCursor(Context context, String sourceFile) { - // Handle empty string in case of system headers or macros. - if (sourceFile.isEmpty) { - return false; - } - - // Add header to seen if it's not. - if (!context.bindingsIndex.isSeenHeader(sourceFile)) { - context.bindingsIndex.addHeaderToSeen( - sourceFile, - context.config.shouldIncludeHeader(Uri.file(sourceFile)), - ); - } - - return context.bindingsIndex.getSeenHeaderStatus(sourceFile)!; -} diff --git a/pkgs/ffigen/lib/src/header_parser/utils.dart b/pkgs/ffigen/lib/src/header_parser/utils.dart index f2f5746e36..b69517e106 100644 --- a/pkgs/ffigen/lib/src/header_parser/utils.dart +++ b/pkgs/ffigen/lib/src/header_parser/utils.dart @@ -85,6 +85,10 @@ extension CXSourceRangePtrExt on Pointer { } extension CXCursorExt on clang_types.CXCursor { + bool get isNull => clang.clang_Cursor_isNull(this) != 0; + bool get isDefinition => clang.clang_isCursorDefinition(this) != 0; + clang_types.CXCursor get definition => clang.clang_getCursorDefinition(this); + String usr() { var res = clang.clang_getCursorUSR(this).toStringAndDispose(); if (isAnonymousRecordDecl()) { @@ -471,14 +475,6 @@ extension DynamicCStringArray on Pointer> { } } -class Stack { - final _stack = []; - - T get top => _stack.last; - T pop() => _stack.removeLast(); - void push(T item) => _stack.add(item); -} - class Macro { final String usr; final String? originalName; @@ -501,9 +497,6 @@ class BindingsIndex { /// Contains usr for typedefs which cannot be generated. final Set _unsupportedTypealiases = {}; - /// Index for headers. - final Map _headerCache = {}; - bool isSeenType(String usr) => _declaredTypes.containsKey(usr); void addTypeToSeen(String usr, Type type) => _declaredTypes[usr] = type; Type? getSeenType(String usr) => _declaredTypes[usr]; @@ -523,10 +516,6 @@ class BindingsIndex { _unsupportedTypealiases.contains(usr); void addUnsupportedTypealiasToSeen(String usr) => _unsupportedTypealiases.add(usr); - bool isSeenHeader(String source) => _headerCache.containsKey(source); - void addHeaderToSeen(String source, bool includeStatus) => - _headerCache[source] = includeStatus; - bool? getSeenHeaderStatus(String source) => _headerCache[source]; void addObjCBlockToSeen(String key, ObjCBlock t) => _objcBlocks[key] = t; ObjCBlock? getSeenObjCBlock(String key) => _objcBlocks[key]; void addObjCProtocolToSeen(String usr, ObjCProtocol t) => diff --git a/pkgs/ffigen/tool/libclang_config.yaml b/pkgs/ffigen/tool/libclang_config.yaml index e5d7ebc2f6..0221ec9817 100644 --- a/pkgs/ffigen/tool/libclang_config.yaml +++ b/pkgs/ffigen/tool/libclang_config.yaml @@ -120,6 +120,7 @@ functions: - clang_getFieldDeclBitWidth - clang_Cursor_isFunctionInlined - clang_getCursorDefinition + - clang_isCursorDefinition - clang_getCursorAvailability - clang_getCursorPlatformAvailability - clang_disposeCXPlatformAvailability From 70126044825d5e96d5c7f507a007ba23e4b8af2c Mon Sep 17 00:00:00 2001 From: Liam Appelbe Date: Tue, 2 Dec 2025 14:05:16 +1100 Subject: [PATCH 02/19] wip --- .../ffigen/lib/src/code_generator/writer.dart | 2 + pkgs/ffigen/lib/src/context.dart | 4 +- .../lib/src/header_parser/bindings_index.dart | 29 +++++ pkgs/ffigen/lib/src/header_parser/parser.dart | 7 +- .../translation_unit_parser.dart | 100 ++++++++++-------- pkgs/ffigen/lib/src/header_parser/utils.dart | 4 +- 6 files changed, 93 insertions(+), 53 deletions(-) create mode 100644 pkgs/ffigen/lib/src/header_parser/bindings_index.dart diff --git a/pkgs/ffigen/lib/src/code_generator/writer.dart b/pkgs/ffigen/lib/src/code_generator/writer.dart index 646726cadf..3d611e9aa2 100644 --- a/pkgs/ffigen/lib/src/code_generator/writer.dart +++ b/pkgs/ffigen/lib/src/code_generator/writer.dart @@ -212,6 +212,7 @@ class Writer { } // Warn for macros. + // TODO: Use runtime type, not USR. final hasMacroBindings = bindings.any( (element) => element is Constant && element.usr.contains('@macro@'), ); @@ -223,6 +224,7 @@ class Writer { } // Remove internal bindings and macros. + // TODO: Use runtime type, not USR. bindings.removeWhere((element) { return element.isInternal || (element is Constant && element.usr.contains('@macro@')); diff --git a/pkgs/ffigen/lib/src/context.dart b/pkgs/ffigen/lib/src/context.dart index 22d996f82e..10c612edc1 100644 --- a/pkgs/ffigen/lib/src/context.dart +++ b/pkgs/ffigen/lib/src/context.dart @@ -18,7 +18,6 @@ import 'header_parser/utils.dart'; class Context { final Logger logger; final Config config; - final CursorIndex cursorIndex; final bindingsIndex = BindingsIndex(); final savedMacros = {}; final unnamedEnumConstants = []; @@ -32,8 +31,7 @@ class Context { late final ExtraSymbols extraSymbols; Context(this.logger, FfiGenerator generator, {Uri? libclangDylib}) - : config = Config(generator), - cursorIndex = CursorIndex(logger) { + : config = Config(generator) { objCBuiltInFunctions = ObjCBuiltInFunctions( this, // ignore: deprecated_member_use_from_same_package diff --git a/pkgs/ffigen/lib/src/header_parser/bindings_index.dart b/pkgs/ffigen/lib/src/header_parser/bindings_index.dart new file mode 100644 index 0000000000..30391c9856 --- /dev/null +++ b/pkgs/ffigen/lib/src/header_parser/bindings_index.dart @@ -0,0 +1,29 @@ +// Copyright (c) 2025, 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. + +class BindingsIndex { + final _entries = {}; + + void addDefinition(clang_types.CXCursor cursor) { + if (!cursor.isDefinition) { + cursor = cursor.definition; + } + if (cursor.isNull) return; + final usr = cursor.usr(); + if (usr.isEmpty) return; + _entries[usr] ??= IndexEntry(cursor); + } + + IndexEntry? operator [](String usr) => _entries[usr]; + + Iterable get bindings => + _entries.values.map((e) => e.binding).nonNulls; +} + +class IndexEntry { + clang_types.CXCursor definition; + bool filled = false; + Binding? binding; + IndexEntry(this.definition); +} diff --git a/pkgs/ffigen/lib/src/header_parser/parser.dart b/pkgs/ffigen/lib/src/header_parser/parser.dart index 29c485094b..d8c52c3a7d 100644 --- a/pkgs/ffigen/lib/src/header_parser/parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/parser.dart @@ -138,9 +138,8 @@ List parseToBindings(Context context) { } // Parse definitions from translation units. - for (final rootCursor in tuCursors) { - bindings.addAll(parseTranslationUnit(context, rootCursor)); - } + parseTranslationUnits(context, tuCursors); + final bindings = context.bindingsIndex.bindings; // Dispose translation units. for (final tu in tuList) { @@ -148,9 +147,11 @@ List parseToBindings(Context context) { } // Add all saved unnamed enums. + // TODO: Store these directly in the bindingsIndex. bindings.addAll(context.unnamedEnumConstants); // Parse all saved macros. + // TODO: Store these directly in the bindingsIndex. bindings.addAll(parseSavedMacros(context)); clangCmdArgs.dispose(cmdLen); 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 6e861cdf2b..67e833fb00 100644 --- a/pkgs/ffigen/lib/src/header_parser/translation_unit_parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/translation_unit_parser.dart @@ -13,53 +13,30 @@ import 'sub_parsers/var_parser.dart'; import 'type_extractor/extractor.dart'; import 'utils.dart'; -/// Parses the translation unit and returns the generated bindings. -Set parseTranslationUnit( +/// Parses the translation units and adds all the bindings to the context's +/// bindingsIndex. +void parseTranslationUnits( Context context, - clang_types.CXCursor translationUnitCursor, + List translationUnitCursors, ) { - final bindings = {}; - final logger = context.logger; final headers = {}; + for (final translationUnitCursor in translationUnitCursors) { + _parseTranslationUnits(context, translationUnitCursor, headers); + } +} +void _parseTranslationUnit( + Context context, + List translationUnitCursors, + Map headers, +) { + final logger = context.logger; translationUnitCursor.visitChildren((cursor) { final file = cursor.sourceFileName(); if (file.isEmpty) return; if (headers[file] ??= context.config.shouldIncludeHeader(Uri.file(file))) { try { - logger.finest('rootCursorVisitor: ${cursor.completeStringRepr()}'); - switch (clang.clang_getCursorKind(cursor)) { - case clang_types.CXCursorKind.CXCursor_FunctionDecl: - bindings.addAll(parseFunctionDeclaration(context, cursor)); - break; - case clang_types.CXCursorKind.CXCursor_StructDecl: - case clang_types.CXCursorKind.CXCursor_UnionDecl: - case clang_types.CXCursorKind.CXCursor_EnumDecl: - case clang_types.CXCursorKind.CXCursor_ObjCInterfaceDecl: - case clang_types.CXCursorKind.CXCursor_TypedefDecl: - addToBindings(bindings, _getCodeGenTypeFromCursor(context, cursor)); - break; - case clang_types.CXCursorKind.CXCursor_ObjCCategoryDecl: - addToBindings( - bindings, - parseObjCCategoryDeclaration(context, cursor), - ); - break; - case clang_types.CXCursorKind.CXCursor_ObjCProtocolDecl: - addToBindings( - bindings, - parseObjCProtocolDeclaration(context, cursor), - ); - break; - case clang_types.CXCursorKind.CXCursor_MacroDefinition: - saveMacroDefinition(context, cursor); - break; - case clang_types.CXCursorKind.CXCursor_VarDecl: - addToBindings(bindings, parseVarDeclaration(context, cursor)); - break; - default: - logger.finer('rootCursorVisitor: CursorKind not implemented'); - } + _parseCursor(); } catch (e, s) { logger.severe(e); logger.severe(s); @@ -71,15 +48,48 @@ Set parseTranslationUnit( ); } }); - - return bindings; } -/// Adds to binding if unseen and not null. -void addToBindings(Set bindings, Binding? b) { - if (b != null) { - // This is a set, and hence will not have duplicates. - bindings.add(b); +void _parseCursor( + Context context, + clang_types.CXCursor cursor, +) { + final usr = cursor.usr(); + final entry = context.bindingsIndex[usr]; + if (entry == null || entry.filled) return; + + logger.finest('rootCursorVisitor: ${cursor.completeStringRepr()}'); + switch (clang.clang_getCursorKind(cursor)) { + case clang_types.CXCursorKind.CXCursor_FunctionDecl: + bindings.addAll(parseFunctionDeclaration(context, cursor)); + break; + case clang_types.CXCursorKind.CXCursor_StructDecl: + case clang_types.CXCursorKind.CXCursor_UnionDecl: + case clang_types.CXCursorKind.CXCursor_EnumDecl: + case clang_types.CXCursorKind.CXCursor_ObjCInterfaceDecl: + case clang_types.CXCursorKind.CXCursor_TypedefDecl: + addToBindings(bindings, _getCodeGenTypeFromCursor(context, cursor)); + break; + case clang_types.CXCursorKind.CXCursor_ObjCCategoryDecl: + addToBindings( + bindings, + parseObjCCategoryDeclaration(context, cursor), + ); + break; + case clang_types.CXCursorKind.CXCursor_ObjCProtocolDecl: + addToBindings( + bindings, + parseObjCProtocolDeclaration(context, cursor), + ); + break; + case clang_types.CXCursorKind.CXCursor_MacroDefinition: + saveMacroDefinition(context, cursor); + break; + case clang_types.CXCursorKind.CXCursor_VarDecl: + addToBindings(bindings, parseVarDeclaration(context, cursor)); + break; + default: + logger.finer('rootCursorVisitor: CursorKind not implemented'); } } diff --git a/pkgs/ffigen/lib/src/header_parser/utils.dart b/pkgs/ffigen/lib/src/header_parser/utils.dart index b69517e106..72e042434f 100644 --- a/pkgs/ffigen/lib/src/header_parser/utils.dart +++ b/pkgs/ffigen/lib/src/header_parser/utils.dart @@ -483,7 +483,7 @@ class Macro { } /// Tracks if a binding is 'seen' or not. -class BindingsIndex { +/*class BindingsIndex { // Tracks if bindings are already seen, Map key is USR obtained from libclang. final Map _declaredTypes = {}; final Map _functions = {}; @@ -573,4 +573,4 @@ class CursorIndex { } } } -} +}*/ From 082b1ea131fceafd71d0e854e42e0bbcad6cbdff Mon Sep 17 00:00:00 2001 From: Liam Appelbe Date: Tue, 2 Dec 2025 14:51:52 +1100 Subject: [PATCH 03/19] [ffigen] Tighten up synthetic USRs --- .../ffigen/lib/src/code_generator/objc_block.dart | 15 +++++++-------- .../sub_parsers/functiondecl_parser.dart | 11 +++++++---- pkgs/ffigen/lib/src/header_parser/utils.dart | 4 +++- pkgs/ffigen/lib/src/strings.dart | 3 +++ pkgs/ffigen/test/regen.dart | 2 +- pkgs/ffigen/test/test_utils.dart | 4 ++-- 6 files changed, 23 insertions(+), 16 deletions(-) diff --git a/pkgs/ffigen/lib/src/code_generator/objc_block.dart b/pkgs/ffigen/lib/src/code_generator/objc_block.dart index 7e2bbec70b..e517552633 100644 --- a/pkgs/ffigen/lib/src/code_generator/objc_block.dart +++ b/pkgs/ffigen/lib/src/code_generator/objc_block.dart @@ -4,6 +4,7 @@ import '../code_generator.dart'; import '../context.dart'; +import '../strings.dart'; import '../visitor/ast.dart'; import 'binding_string.dart'; @@ -108,14 +109,12 @@ class ObjCBlock extends BindingType with HasLocalScope { ) { // Create a fake USR code for the block. This code is used to dedupe blocks // with the same signature. Not intended to be human readable. - final usr = StringBuffer(); - usr.write( - 'objcBlock: ${returnType.cacheKey()} ${returnsRetained ? 'R' : ''}', - ); - for (final param in params) { - usr.write(' ${param.type.cacheKey()} ${param.objCConsumed ? 'C' : ''}'); - } - return usr.toString(); + return [ + '$synthUsrChar objcBlock:', + '${returnType.cacheKey()} ${returnsRetained ? 'R' : ''}', + for (final param in params) + '${param.type.cacheKey()} ${param.objCConsumed ? 'C' : ''}', + ].join(synthUsrChar); } bool get hasListener => returnType == voidType; 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 31ab4da428..ee3ab90cd4 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 @@ -5,6 +5,7 @@ import '../../code_generator.dart'; import '../../config_provider/config_types.dart'; import '../../context.dart'; +import '../../strings.dart'; import '../clang_bindings/clang_bindings.dart' as clang_types; import '../utils.dart'; import 'api_availability.dart'; @@ -118,7 +119,7 @@ List parseFunctionDeclaration( ); // Initialized with a single value with no prefix and empty var args. - var varArgFunctions = [VarArgFunction('', [])]; + var varArgFunctions = [null]; if (config.functions.varArgs.containsKey(funcName)) { if (clang.clang_isFunctionTypeVariadic(cursor.type()) == 1) { varArgFunctions = config.functions.varArgs[funcName]!; @@ -130,6 +131,8 @@ List parseFunctionDeclaration( } } for (final vaFunc in varArgFunctions) { + var usr = funcUsr; + if (vaFunc != null) usr += '$synthUsrChar vaFunc: ${vaFunc.postfix}'; funcs.add( Func( dartDoc: getCursorDocComment( @@ -138,13 +141,13 @@ List parseFunctionDeclaration( indent: nesting.length + commentPrefix.length, availability: apiAvailability.dartDoc, ), - usr: funcUsr + vaFunc.postfix, - name: config.functions.rename(decl) + vaFunc.postfix, + usr: usr, + name: config.functions.rename(decl) + (vaFunc?.postfix ?? ''), originalName: funcName, returnType: returnType, parameters: parameters, varArgParameters: [ - for (final ta in vaFunc.types) + for (final ta in vaFunc?.types ?? const []) Parameter(type: ta, name: 'va', objCConsumed: false), ], exposeSymbolAddress: config.functions.includeSymbolAddress(decl), diff --git a/pkgs/ffigen/lib/src/header_parser/utils.dart b/pkgs/ffigen/lib/src/header_parser/utils.dart index f2f5746e36..46ab1f0ab3 100644 --- a/pkgs/ffigen/lib/src/header_parser/utils.dart +++ b/pkgs/ffigen/lib/src/header_parser/utils.dart @@ -10,6 +10,7 @@ import 'package:logging/logging.dart'; import '../code_generator.dart'; import '../config_provider/config_types.dart'; import '../context.dart'; +import '../strings.dart'; import 'clang_bindings/clang_bindings.dart' as clang_types; import 'type_extractor/extractor.dart'; @@ -87,8 +88,9 @@ extension CXSourceRangePtrExt on Pointer { extension CXCursorExt on clang_types.CXCursor { String usr() { var res = clang.clang_getCursorUSR(this).toStringAndDispose(); + assert(!res.contains(synthUsrChar)); if (isAnonymousRecordDecl()) { - res += '@offset:${sourceFileOffset()}'; + res += '$synthUsrChar anonRec: offset:${sourceFileOffset()}'; } return res; } diff --git a/pkgs/ffigen/lib/src/strings.dart b/pkgs/ffigen/lib/src/strings.dart index b6a5fdef2f..4e5c18cf11 100644 --- a/pkgs/ffigen/lib/src/strings.dart +++ b/pkgs/ffigen/lib/src/strings.dart @@ -274,6 +274,9 @@ const doubleNaN = 'double.nan'; /// USR for struct `_Dart_Handle`. const dartHandleUsr = 'c:@S@_Dart_Handle'; +// A character that will never appear in real USRs, for making synthetic USRs. +const synthUsrChar = '~'; + const ffiNative = 'ffi-native'; const ffiNativeAsset = 'asset-id'; diff --git a/pkgs/ffigen/test/regen.dart b/pkgs/ffigen/test/regen.dart index 3ca5c0fe30..db23d0fc34 100644 --- a/pkgs/ffigen/test/regen.dart +++ b/pkgs/ffigen/test/regen.dart @@ -22,7 +22,7 @@ $ dart run test/setup.dart && dart run test/regen.dart && dart test void _regenConfig(Logger logger, String yamlConfigPath) { final path = p.join(packagePathForTests, yamlConfigPath); Directory.current = File(path).parent; - testConfigFromPath(path).generate(logger: logger); + testConfigFromPath(path, logger: logger).generate(logger: logger); } Future main(List args) async { diff --git a/pkgs/ffigen/test/test_utils.dart b/pkgs/ffigen/test/test_utils.dart index 762fd3341e..d5665b1c89 100644 --- a/pkgs/ffigen/test/test_utils.dart +++ b/pkgs/ffigen/test/test_utils.dart @@ -235,10 +235,10 @@ FfiGenerator testConfig(String yamlBody, {String? filename, Logger? logger}) { ).configAdapter(); } -FfiGenerator testConfigFromPath(String path) { +FfiGenerator testConfigFromPath(String path, {Logger? logger}) { final file = File(path); final yamlBody = file.readAsStringSync(); - return testConfig(yamlBody, filename: path); + return testConfig(yamlBody, filename: path, logger: logger); } bool isFlutterTester = Platform.resolvedExecutable.contains('flutter_tester'); From 6a45600e9c8747fcc9838ed08845cccbf1e0e561 Mon Sep 17 00:00:00 2001 From: Liam Appelbe Date: Tue, 2 Dec 2025 15:02:10 +1100 Subject: [PATCH 04/19] changelog --- pkgs/ffigen/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkgs/ffigen/CHANGELOG.md b/pkgs/ffigen/CHANGELOG.md index 83243bff0c..661143c132 100644 --- a/pkgs/ffigen/CHANGELOG.md +++ b/pkgs/ffigen/CHANGELOG.md @@ -6,6 +6,9 @@ `Categories`, `Interfaces`, and `Protocols`. - __Breaking change__: Remove deprecated `wrapperName` field from `NativeExternalBindings`. +- __Breaking change__: Certain synthetic USRs have been modified to ensure they + cannot collide with real USRs. It's very unlikely that any user facing USRs + are affected. ## 20.1.1 From 3604ed04fcd5a5a0ed6f4bcd119f24dd22cc9d15 Mon Sep 17 00:00:00 2001 From: Liam Appelbe Date: Wed, 3 Dec 2025 10:45:48 +1100 Subject: [PATCH 05/19] wip --- .../bindings_index.dart | 19 ++++++---- .../lib/src/code_generator/objc_block.dart | 26 +++++-------- pkgs/ffigen/lib/src/context.dart | 1 + .../translation_unit_parser.dart | 37 ++++++------------- 4 files changed, 34 insertions(+), 49 deletions(-) rename pkgs/ffigen/lib/src/{header_parser => code_generator}/bindings_index.dart (55%) diff --git a/pkgs/ffigen/lib/src/header_parser/bindings_index.dart b/pkgs/ffigen/lib/src/code_generator/bindings_index.dart similarity index 55% rename from pkgs/ffigen/lib/src/header_parser/bindings_index.dart rename to pkgs/ffigen/lib/src/code_generator/bindings_index.dart index 30391c9856..19c8dede31 100644 --- a/pkgs/ffigen/lib/src/header_parser/bindings_index.dart +++ b/pkgs/ffigen/lib/src/code_generator/bindings_index.dart @@ -2,6 +2,10 @@ // 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 'binding.dart'; +import '../header_parser/clang_bindings/clang_bindings.dart' as clang_types; +import '../header_parser/utils.dart'; + class BindingsIndex { final _entries = {}; @@ -12,18 +16,19 @@ class BindingsIndex { if (cursor.isNull) return; final usr = cursor.usr(); if (usr.isEmpty) return; - _entries[usr] ??= IndexEntry(cursor); + _entries[usr] ??= IndexEntry(definition: cursor); } IndexEntry? operator [](String usr) => _entries[usr]; + IndexEntry getOrInsert(String usr) => _entries[usr] ?? IndexEntry(); - Iterable get bindings => - _entries.values.map((e) => e.binding).nonNulls; + Set get bindings => { + for (final b in _entries.values.map((e) => e.bindings).nonNulls) ...b, + }; } class IndexEntry { - clang_types.CXCursor definition; - bool filled = false; - Binding? binding; - IndexEntry(this.definition); + clang_types.CXCursor? definition; + List? bindings; + IndexEntry({this.definition}); } diff --git a/pkgs/ffigen/lib/src/code_generator/objc_block.dart b/pkgs/ffigen/lib/src/code_generator/objc_block.dart index 7e2bbec70b..d6fe56bf2a 100644 --- a/pkgs/ffigen/lib/src/code_generator/objc_block.dart +++ b/pkgs/ffigen/lib/src/code_generator/objc_block.dart @@ -40,22 +40,16 @@ class ObjCBlock extends BindingType with HasLocalScope { final usr = _getBlockUsr(returnType, renamedParams, returnsRetained); - final oldBlock = context.bindingsIndex.getSeenObjCBlock(usr); - if (oldBlock != null) { - return oldBlock; - } - - final block = ObjCBlock._( - context, - usr: usr, - name: _getBlockName(returnType, renamedParams.map((a) => a.type)), - returnType: returnType, - params: renamedParams, - returnsRetained: returnsRetained, - ); - context.bindingsIndex.addObjCBlockToSeen(usr, block); - - return block; + return (context.bindingsIndex.getOrInsert(usr)?.bindings ??= [ + ObjCBlock._( + context, + usr: usr, + name: _getBlockName(returnType, renamedParams.map((a) => a.type)), + returnType: returnType, + params: renamedParams, + returnsRetained: returnsRetained, + ) + ]).single; } ObjCBlock._( diff --git a/pkgs/ffigen/lib/src/context.dart b/pkgs/ffigen/lib/src/context.dart index 10c612edc1..dd90e15334 100644 --- a/pkgs/ffigen/lib/src/context.dart +++ b/pkgs/ffigen/lib/src/context.dart @@ -7,6 +7,7 @@ import 'dart:ffi'; import 'package:logging/logging.dart'; import 'code_generator.dart'; +import 'code_generator/bindings_index.dart'; import 'code_generator/scope.dart'; import 'config_provider/config.dart'; import 'config_provider/config_types.dart'; 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 67e833fb00..0894186133 100644 --- a/pkgs/ffigen/lib/src/header_parser/translation_unit_parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/translation_unit_parser.dart @@ -36,7 +36,8 @@ void _parseTranslationUnit( if (file.isEmpty) return; if (headers[file] ??= context.config.shouldIncludeHeader(Uri.file(file))) { try { - _parseCursor(); + final entry = context.bindingsIndex.getOrInsert(cursor.usr()); + entry.bindings ??= _parseCursor(context, entry.definition ?? cursor); } catch (e, s) { logger.severe(e); logger.severe(s); @@ -50,47 +51,31 @@ void _parseTranslationUnit( }); } -void _parseCursor( - Context context, - clang_types.CXCursor cursor, -) { - final usr = cursor.usr(); - final entry = context.bindingsIndex[usr]; - if (entry == null || entry.filled) return; - +List _parseCursor(Context context, clang_types.CXCursor cursor) { logger.finest('rootCursorVisitor: ${cursor.completeStringRepr()}'); switch (clang.clang_getCursorKind(cursor)) { case clang_types.CXCursorKind.CXCursor_FunctionDecl: - bindings.addAll(parseFunctionDeclaration(context, cursor)); - break; + return parseFunctionDeclaration(context, cursor); case clang_types.CXCursorKind.CXCursor_StructDecl: case clang_types.CXCursorKind.CXCursor_UnionDecl: case clang_types.CXCursorKind.CXCursor_EnumDecl: case clang_types.CXCursorKind.CXCursor_ObjCInterfaceDecl: case clang_types.CXCursorKind.CXCursor_TypedefDecl: - addToBindings(bindings, _getCodeGenTypeFromCursor(context, cursor)); - break; + return [_getCodeGenTypeFromCursor(context, cursor)]; case clang_types.CXCursorKind.CXCursor_ObjCCategoryDecl: - addToBindings( - bindings, - parseObjCCategoryDeclaration(context, cursor), - ); - break; + return [parseObjCCategoryDeclaration(context, cursor)]; case clang_types.CXCursorKind.CXCursor_ObjCProtocolDecl: - addToBindings( - bindings, - parseObjCProtocolDeclaration(context, cursor), - ); - break; + return [parseObjCProtocolDeclaration(context, cursor)]; case clang_types.CXCursorKind.CXCursor_MacroDefinition: + // TODO: Return a binding? saveMacroDefinition(context, cursor); - break; + return []; case clang_types.CXCursorKind.CXCursor_VarDecl: - addToBindings(bindings, parseVarDeclaration(context, cursor)); - break; + return [parseVarDeclaration(context, cursor)]; default: logger.finer('rootCursorVisitor: CursorKind not implemented'); } + return []; } BindingType? _getCodeGenTypeFromCursor( From 01daf63e338b85aeb2d92b021f350fa840f2d77c Mon Sep 17 00:00:00 2001 From: Liam Appelbe Date: Wed, 3 Dec 2025 11:35:36 +1100 Subject: [PATCH 06/19] wip --- .../src/code_generator/bindings_index.dart | 16 +- .../lib/src/code_generator/objc_block.dart | 19 +- pkgs/ffigen/lib/src/header_parser/parser.dart | 3 - .../sub_parsers/compounddecl_parser.dart | 7 +- .../sub_parsers/enumdecl_parser.dart | 3 +- .../sub_parsers/functiondecl_parser.dart | 234 +++++++++--------- .../sub_parsers/macro_parser.dart | 5 - .../sub_parsers/objccategorydecl_parser.dart | 2 - .../translation_unit_parser.dart | 80 +++--- 9 files changed, 184 insertions(+), 185 deletions(-) diff --git a/pkgs/ffigen/lib/src/code_generator/bindings_index.dart b/pkgs/ffigen/lib/src/code_generator/bindings_index.dart index 19c8dede31..1bbc236b81 100644 --- a/pkgs/ffigen/lib/src/code_generator/bindings_index.dart +++ b/pkgs/ffigen/lib/src/code_generator/bindings_index.dart @@ -19,16 +19,22 @@ class BindingsIndex { _entries[usr] ??= IndexEntry(definition: cursor); } + Binding? addBinding(Context context, clang_types.CXCursor cursor) { + } + IndexEntry? operator [](String usr) => _entries[usr]; - IndexEntry getOrInsert(String usr) => _entries[usr] ?? IndexEntry(); + IndexEntry getOrInsert(String usr) { + assert(usr.isNotEmpty); + return _entries[usr] ?? IndexEntry(); + } - Set get bindings => { - for (final b in _entries.values.map((e) => e.bindings).nonNulls) ...b, - }; + Set get bindings => + _entries.values.map((e) => e.binding).nonNulls.toSet(); } class IndexEntry { clang_types.CXCursor? definition; - List? bindings; + bool filled = false; + Binding? binding; IndexEntry({this.definition}); } diff --git a/pkgs/ffigen/lib/src/code_generator/objc_block.dart b/pkgs/ffigen/lib/src/code_generator/objc_block.dart index d6fe56bf2a..745317619d 100644 --- a/pkgs/ffigen/lib/src/code_generator/objc_block.dart +++ b/pkgs/ffigen/lib/src/code_generator/objc_block.dart @@ -40,16 +40,15 @@ class ObjCBlock extends BindingType with HasLocalScope { final usr = _getBlockUsr(returnType, renamedParams, returnsRetained); - return (context.bindingsIndex.getOrInsert(usr)?.bindings ??= [ - ObjCBlock._( - context, - usr: usr, - name: _getBlockName(returnType, renamedParams.map((a) => a.type)), - returnType: returnType, - params: renamedParams, - returnsRetained: returnsRetained, - ) - ]).single; + return (context.bindingsIndex.getOrInsert(usr).binding ??= ObjCBlock._( + context, + usr: usr, + name: _getBlockName(returnType, renamedParams.map((a) => a.type)), + returnType: returnType, + params: renamedParams, + returnsRetained: returnsRetained, + )) + as ObjCBlock; } ObjCBlock._( diff --git a/pkgs/ffigen/lib/src/header_parser/parser.dart b/pkgs/ffigen/lib/src/header_parser/parser.dart index d8c52c3a7d..7e0fc55e5b 100644 --- a/pkgs/ffigen/lib/src/header_parser/parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/parser.dart @@ -68,9 +68,6 @@ List parseToBindings(Context context) { clangCmdArgs = createDynamicStringArray(compilerOpts); final cmdLen = compilerOpts.length; - // Contains all bindings. A set ensures we never have duplicates. - final bindings = {}; - // Log all headers for user. context.logger.info('Input Headers: ${config.entryPoints}'); 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 eb5e6d9c0d..ba743d09d9 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 @@ -122,7 +122,7 @@ Compound? _parseCompoundDeclaration( }) constructor, ) { - // Parse the cursor definition instead, if this is a forward declaration. + assert(cursor.isDefinition); final declUsr = cursor.usr(); final String declName; @@ -146,7 +146,6 @@ Compound? _parseCompoundDeclaration( final decl = Declaration(usr: declUsr, originalName: declName); if (declName.isEmpty) { - cursor = context.cursorIndex.getDefinition(cursor); return constructor( name: 'Unnamed$className', usr: declUsr, @@ -159,7 +158,6 @@ Compound? _parseCompoundDeclaration( nativeType: cursor.type().spelling(), ); } else { - cursor = context.cursorIndex.getDefinition(cursor); context.logger.fine( '++++ Adding $className: Name: $declName, ${cursor.completeStringRepr()}', ); @@ -190,8 +188,7 @@ void fillCompoundMembersIfNeeded( if (compound.parsedDependencies) return; final logger = context.logger; - cursor = context.cursorIndex.getDefinition(cursor); - + assert(cursor.isDefinition); final parsed = _ParsedCompound(context, compound); final className = compound is Struct ? 'Struct' : 'Union'; parsed.hasAttr = clang.clang_Cursor_hasAttrs(cursor) != 0; diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/enumdecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/enumdecl_parser.dart index 149a922e2c..e1e3f94bc7 100644 --- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/enumdecl_parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/enumdecl_parser.dart @@ -21,8 +21,7 @@ import 'unnamed_enumdecl_parser.dart'; final config = context.config; final logger = context.logger; EnumClass? enumClass; - // Parse the cursor definition instead, if this is a forward declaration. - cursor = context.cursorIndex.getDefinition(cursor); + assert(cursor.isDefinition); final enumUsr = cursor.usr(); final String enumName; 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 31ab4da428..259d657434 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 @@ -10,152 +10,144 @@ import '../utils.dart'; import 'api_availability.dart'; /// Parses a function declaration. -List parseFunctionDeclaration( +Func? parseFunctionDeclaration( Context context, clang_types.CXCursor cursor, ) { final config = context.config; final logger = context.logger; - /// Multiple values are since there may be more than one instance of the - /// same base C function with different variadic arguments. - final funcs = []; - final funcUsr = cursor.usr(); final funcName = cursor.spelling(); final apiAvailability = ApiAvailability.fromCursor(cursor, context); if (apiAvailability.availability == Availability.none) { logger.info('Omitting deprecated function $funcName'); - return funcs; + return null; } final decl = Declaration(usr: funcUsr, originalName: funcName); - final cachedFunc = context.bindingsIndex.getSeenFunc(funcUsr); - if (cachedFunc != null) { - funcs.add(cachedFunc); - } else { - logger.fine('++++ Adding Function: ${cursor.completeStringRepr()}'); - - final returnType = cursor.returnType().toCodeGenType(context); - - final parameters = []; - var incompleteStructParameter = false; - var unimplementedParameterType = false; - final totalArgs = clang.clang_Cursor_getNumArguments(cursor); - for (var i = 0; i < totalArgs; i++) { - final paramCursor = clang.clang_Cursor_getArgument(cursor, i); - - logger.finer('===== parameter: ${paramCursor.completeStringRepr()}'); - - final paramType = paramCursor.toCodeGenType(context); - if (paramType.isIncompleteCompound) { - incompleteStructParameter = true; - } else if (paramType.baseType is UnimplementedType) { - logger.finer('Unimplemented type: ${paramType.baseType}'); - unimplementedParameterType = true; - } - - final paramName = paramCursor.spelling(); - final objCConsumed = paramCursor.hasChildWithKind( - clang_types.CXCursorKind.CXCursor_NSConsumed, - ); + logger.fine('++++ Adding Function: ${cursor.completeStringRepr()}'); - parameters.add( - Parameter( - originalName: paramName, - name: config.functions.renameMember(decl, paramName), - type: paramType, - objCConsumed: objCConsumed, - ), - ); - } + final returnType = cursor.returnType().toCodeGenType(context); - 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 empty so that [addToBindings] function excludes this. - return funcs; - } + final parameters = []; + var incompleteStructParameter = false; + var unimplementedParameterType = false; + final totalArgs = clang.clang_Cursor_getNumArguments(cursor); + for (var i = 0; i < totalArgs; i++) { + final paramCursor = clang.clang_Cursor_getArgument(cursor, i); - if (returnType.isIncompleteCompound || incompleteStructParameter) { - logger.fine( - '---- Removed Function, reason: Incomplete struct pass/return by ' - 'value: ${cursor.completeStringRepr()}', - ); - logger.warning( - "Skipped Function '$funcName', Incomplete struct pass/return by " - 'value not supported.', - ); - // Returning null so that [addToBindings] function excludes this. - return funcs; - } + logger.finer('===== parameter: ${paramCursor.completeStringRepr()}'); - if (returnType.baseType is UnimplementedType || - unimplementedParameterType) { - logger.fine( - '---- Removed Function, reason: unsupported return type or ' - 'parameter type: ${cursor.completeStringRepr()}', - ); - logger.warning( - "Skipped Function '$funcName', function has unsupported return type " - 'or parameter type.', - ); - // Returning null so that [addToBindings] function excludes this. - return funcs; + final paramType = paramCursor.toCodeGenType(context); + if (paramType.isIncompleteCompound) { + incompleteStructParameter = true; + } else if (paramType.baseType is UnimplementedType) { + logger.finer('Unimplemented type: ${paramType.baseType}'); + unimplementedParameterType = true; } - // Look for any annotations on the function. - final objCReturnsRetained = cursor.hasChildWithKind( - clang_types.CXCursorKind.CXCursor_NSReturnsRetained, + final paramName = paramCursor.spelling(); + final objCConsumed = paramCursor.hasChildWithKind( + clang_types.CXCursorKind.CXCursor_NSConsumed, ); - // Initialized with a single value with no prefix and empty var args. - var varArgFunctions = [VarArgFunction('', [])]; - if (config.functions.varArgs.containsKey(funcName)) { - if (clang.clang_isFunctionTypeVariadic(cursor.type()) == 1) { - varArgFunctions = config.functions.varArgs[funcName]!; - } else { - logger.warning( - 'Skipping variadic-argument config for function ' - "'$funcName' since its not variadic.", - ); - } - } - for (final vaFunc in varArgFunctions) { - funcs.add( - Func( - dartDoc: getCursorDocComment( - context, - cursor, - indent: nesting.length + commentPrefix.length, - availability: apiAvailability.dartDoc, - ), - usr: funcUsr + vaFunc.postfix, - name: config.functions.rename(decl) + vaFunc.postfix, - originalName: funcName, - returnType: returnType, - parameters: parameters, - varArgParameters: [ - for (final ta in vaFunc.types) - Parameter(type: ta, name: 'va', objCConsumed: false), - ], - exposeSymbolAddress: config.functions.includeSymbolAddress(decl), - exposeFunctionTypedefs: config.functions.includeTypedef(decl), - isLeaf: config.functions.isLeaf(decl), - objCReturnsRetained: objCReturnsRetained, - loadFromNativeAsset: config.ffiNativeConfig.enabled, - ), + parameters.add( + Parameter( + originalName: paramName, + name: config.functions.renameMember(decl, paramName), + type: paramType, + objCConsumed: objCConsumed, + ), + ); + } + + 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 empty so that [addToBindings] function excludes this. + return null; + } + + if (returnType.isIncompleteCompound || incompleteStructParameter) { + logger.fine( + '---- Removed Function, reason: Incomplete struct pass/return by ' + 'value: ${cursor.completeStringRepr()}', + ); + logger.warning( + "Skipped Function '$funcName', Incomplete struct pass/return by " + 'value not supported.', + ); + // Returning null so that [addToBindings] function excludes this. + return null; + } + + if (returnType.baseType is UnimplementedType || unimplementedParameterType) { + logger.fine( + '---- Removed Function, reason: unsupported return type or ' + 'parameter type: ${cursor.completeStringRepr()}', + ); + logger.warning( + "Skipped Function '$funcName', function has unsupported return type " + 'or parameter type.', + ); + // Returning null so that [addToBindings] function excludes this. + return null; + } + + // Look for any annotations on the function. + final objCReturnsRetained = cursor.hasChildWithKind( + clang_types.CXCursorKind.CXCursor_NSReturnsRetained, + ); + + // Initialized with a single value with no prefix and empty var args. + var varArgFunctions = [VarArgFunction('', [])]; + if (config.functions.varArgs.containsKey(funcName)) { + if (clang.clang_isFunctionTypeVariadic(cursor.type()) == 1) { + varArgFunctions = config.functions.varArgs[funcName]!; + } else { + logger.warning( + 'Skipping variadic-argument config for function ' + "'$funcName' since its not variadic.", ); } - context.bindingsIndex.addFuncToSeen(funcUsr, funcs.last); + } + + /// Multiple values are since there may be more than one instance of the + /// same base C function with different variadic arguments. + for (final vaFunc in varArgFunctions) { + funcs.add( + Func( + dartDoc: getCursorDocComment( + context, + cursor, + indent: nesting.length + commentPrefix.length, + availability: apiAvailability.dartDoc, + ), + usr: funcUsr + vaFunc.postfix, + name: config.functions.rename(decl) + vaFunc.postfix, + originalName: funcName, + returnType: returnType, + parameters: parameters, + varArgParameters: [ + for (final ta in vaFunc.types) + Parameter(type: ta, name: 'va', objCConsumed: false), + ], + exposeSymbolAddress: config.functions.includeSymbolAddress(decl), + exposeFunctionTypedefs: config.functions.includeTypedef(decl), + isLeaf: config.functions.isLeaf(decl), + objCReturnsRetained: objCReturnsRetained, + loadFromNativeAsset: config.ffiNativeConfig.enabled, + ), + ); } return funcs; diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/macro_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/macro_parser.dart index cf9583c24e..0ff3dfdd15 100644 --- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/macro_parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/macro_parser.dart @@ -19,11 +19,7 @@ import '../utils.dart'; /// Adds a macro definition to be parsed later. void saveMacroDefinition(Context context, clang_types.CXCursor cursor) { - final bindingsIndex = context.bindingsIndex; final macroUsr = cursor.usr(); - if (bindingsIndex.isSeenMacro(macroUsr)) { - return; - } final originalMacroName = cursor.spelling(); final decl = Declaration(usr: macroUsr, originalName: originalMacroName); if (clang.clang_Cursor_isMacroBuiltin(cursor) == 0 && @@ -34,7 +30,6 @@ void saveMacroDefinition(Context context, clang_types.CXCursor cursor) { '${cursor.completeStringRepr()}', ); final prefixedName = context.config.macros.rename(decl); - bindingsIndex.addMacroToSeen(macroUsr, prefixedName); _saveMacro(prefixedName, macroUsr, originalMacroName, context); } } diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/objccategorydecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/objccategorydecl_parser.dart index a50204dcab..d53da7e213 100644 --- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/objccategorydecl_parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/objccategorydecl_parser.dart @@ -69,8 +69,6 @@ ObjCCategory? parseObjCCategoryDeclaration( context: context, ); - context.bindingsIndex.addObjCCategoryToSeen(usr, category); - cursor.visitChildren((child) { switch (child.kind) { case clang_types.CXCursorKind.CXCursor_ObjCProtocolRef: 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 0894186133..38b4ee5417 100644 --- a/pkgs/ffigen/lib/src/header_parser/translation_unit_parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/translation_unit_parser.dart @@ -17,7 +17,7 @@ import 'utils.dart'; /// bindingsIndex. void parseTranslationUnits( Context context, - List translationUnitCursors, + Iterable translationUnitCursors, ) { final headers = {}; for (final translationUnitCursor in translationUnitCursors) { @@ -27,7 +27,7 @@ void parseTranslationUnits( void _parseTranslationUnit( Context context, - List translationUnitCursors, + clang_types.CXCursor translationUnitCursor, Map headers, ) { final logger = context.logger; @@ -35,13 +35,23 @@ void _parseTranslationUnit( final file = cursor.sourceFileName(); if (file.isEmpty) return; if (headers[file] ??= context.config.shouldIncludeHeader(Uri.file(file))) { - try { - final entry = context.bindingsIndex.getOrInsert(cursor.usr()); - entry.bindings ??= _parseCursor(context, entry.definition ?? cursor); - } catch (e, s) { - logger.severe(e); - logger.severe(s); - rethrow; + final usr = cursor.usr(); + if (usr.isEmpty) return; + final entry = context.bindingsIndex.getOrInsert(usr); + if (!entry.filled) { + final bindings = _parseCursor(context, entry.definition ?? cursor); + if (bindings.isEmpty) { + entry.filled = true; + } else { + for (final b in bindings) { + final e = context.bindingsIndex.getOrInsert(b.usr); + assert(!e.filled); + e.filled = true; + e.binding = b; + } + // One of the the bindings must have the same USR as the cursor. + assert(entry.filled); + } } } else { logger.finest( @@ -51,31 +61,37 @@ void _parseTranslationUnit( }); } -List _parseCursor(Context context, clang_types.CXCursor cursor) { +Binding? parseCursor(Context context, clang_types.CXCursor cursor) { + final logger = context.logger; logger.finest('rootCursorVisitor: ${cursor.completeStringRepr()}'); - switch (clang.clang_getCursorKind(cursor)) { - case clang_types.CXCursorKind.CXCursor_FunctionDecl: - return parseFunctionDeclaration(context, cursor); - case clang_types.CXCursorKind.CXCursor_StructDecl: - case clang_types.CXCursorKind.CXCursor_UnionDecl: - case clang_types.CXCursorKind.CXCursor_EnumDecl: - case clang_types.CXCursorKind.CXCursor_ObjCInterfaceDecl: - case clang_types.CXCursorKind.CXCursor_TypedefDecl: - return [_getCodeGenTypeFromCursor(context, cursor)]; - case clang_types.CXCursorKind.CXCursor_ObjCCategoryDecl: - return [parseObjCCategoryDeclaration(context, cursor)]; - case clang_types.CXCursorKind.CXCursor_ObjCProtocolDecl: - return [parseObjCProtocolDeclaration(context, cursor)]; - case clang_types.CXCursorKind.CXCursor_MacroDefinition: - // TODO: Return a binding? - saveMacroDefinition(context, cursor); - return []; - case clang_types.CXCursorKind.CXCursor_VarDecl: - return [parseVarDeclaration(context, cursor)]; - default: - logger.finer('rootCursorVisitor: CursorKind not implemented'); + try { + switch (clang.clang_getCursorKind(cursor)) { + case clang_types.CXCursorKind.CXCursor_FunctionDecl: + return parseFunctionDeclaration(context, cursor); + case clang_types.CXCursorKind.CXCursor_StructDecl: + case clang_types.CXCursorKind.CXCursor_UnionDecl: + case clang_types.CXCursorKind.CXCursor_EnumDecl: + case clang_types.CXCursorKind.CXCursor_ObjCInterfaceDecl: + case clang_types.CXCursorKind.CXCursor_TypedefDecl: + return _getCodeGenTypeFromCursor(context, cursor); + case clang_types.CXCursorKind.CXCursor_ObjCCategoryDecl: + return parseObjCCategoryDeclaration(context, cursor); + case clang_types.CXCursorKind.CXCursor_ObjCProtocolDecl: + return parseObjCProtocolDeclaration(context, cursor); + case clang_types.CXCursorKind.CXCursor_MacroDefinition: + saveMacroDefinition(context, cursor); + return null; + case clang_types.CXCursorKind.CXCursor_VarDecl: + return parseVarDeclaration(context, cursor); + default: + logger.finer('rootCursorVisitor: CursorKind not implemented'); + } + return null; + } catch (e, s) { + logger.severe(e); + logger.severe(s); + rethrow; } - return []; } BindingType? _getCodeGenTypeFromCursor( From 9d7f546c8f1139f69dd0ae9f73d37bff210b3bae Mon Sep 17 00:00:00 2001 From: Liam Appelbe Date: Wed, 3 Dec 2025 13:26:49 +1100 Subject: [PATCH 07/19] wip --- .../lib/src/header_parser/translation_unit_parser.dart | 7 +++++-- .../lib/src/header_parser/type_extractor/extractor.dart | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) 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 54b9be2243..11de544be2 100644 --- a/pkgs/ffigen/lib/src/header_parser/translation_unit_parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/translation_unit_parser.dart @@ -35,7 +35,7 @@ void _parseTranslationUnit( final file = cursor.sourceFileName(); if (file.isEmpty) return; if (headers[file] ??= context.config.shouldIncludeHeader(Uri.file(file))) { - context.bindingsIndex.cache(cursor, (def) => parseCursor(context, def)); + parseCursor(context, def); } else { logger.finest( 'rootCursorVisitor:(not included) ${cursor.completeStringRepr()}', @@ -44,7 +44,10 @@ void _parseTranslationUnit( }); } -Binding? parseCursor(Context context, clang_types.CXCursor cursor) { +Binding? parseCursor(Context context, clang_types.CXCursor cursor) => + context.bindingsIndex.cache(cursor, (def) => _parseCursor(context, def)); + +Binding? _parseCursor(Context context, clang_types.CXCursor cursor) { final logger = context.logger; logger.finest('rootCursorVisitor: ${cursor.completeStringRepr()}'); try { 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 2c76840ace..bf25ef770d 100644 --- a/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart +++ b/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart @@ -65,8 +65,8 @@ Type getCodeGenType( final protocols = []; for (var i = 0; i < numProtocols; ++i) { final pdecl = clang.clang_Type_getObjCProtocolDecl(pt, i); - final p = parseObjCProtocolDeclaration(context, pdecl); - if (p != null) protocols.add(p); + final p = parseCursor(context, pdecl); + if (p != null) protocols.add(p as ObjCProtocol); } if (protocols.isNotEmpty) { return ObjCObjectPointerWithProtocols(protocols); From 855610394af67dc0dd594808bfd9df1b87be00f9 Mon Sep 17 00:00:00 2001 From: Liam Appelbe Date: Tue, 16 Dec 2025 13:29:00 +1100 Subject: [PATCH 08/19] wip --- pkgs/ffigen/lib/src/code_generator.dart | 1 + .../src/code_generator/bindings_index.dart | 33 +++++--- .../sub_parsers/compounddecl_parser.dart | 3 +- .../sub_parsers/enumdecl_parser.dart | 4 - .../sub_parsers/functiondecl_parser.dart | 2 +- .../sub_parsers/objccategorydecl_parser.dart | 75 +++++++++---------- .../sub_parsers/objcinterfacedecl_parser.dart | 4 - .../sub_parsers/objcprotocoldecl_parser.dart | 75 +++++++++---------- .../sub_parsers/typedefdecl_parser.dart | 14 +--- .../sub_parsers/unnamed_enumdecl_parser.dart | 5 -- .../header_parser/sub_parsers/var_parser.dart | 10 --- .../translation_unit_parser.dart | 12 +-- .../type_extractor/extractor.dart | 6 +- pkgs/ffigen/lib/src/header_parser/utils.dart | 3 - 14 files changed, 105 insertions(+), 142 deletions(-) diff --git a/pkgs/ffigen/lib/src/code_generator.dart b/pkgs/ffigen/lib/src/code_generator.dart index 6a1a3552cd..97c3202887 100644 --- a/pkgs/ffigen/lib/src/code_generator.dart +++ b/pkgs/ffigen/lib/src/code_generator.dart @@ -6,6 +6,7 @@ library; export 'code_generator/binding.dart'; +export 'code_generator/bindings_index.dart'; export 'code_generator/compound.dart'; export 'code_generator/constant.dart'; export 'code_generator/enum_class.dart'; diff --git a/pkgs/ffigen/lib/src/code_generator/bindings_index.dart b/pkgs/ffigen/lib/src/code_generator/bindings_index.dart index b38c5a6e46..7c9b7b860e 100644 --- a/pkgs/ffigen/lib/src/code_generator/bindings_index.dart +++ b/pkgs/ffigen/lib/src/code_generator/bindings_index.dart @@ -19,28 +19,31 @@ class BindingsIndex { _entries[usr] ??= IndexEntry(definition: cursor); } - void fillBinding(Binding binding) { - final entry = getOrInsert(binding.usr); - assert(!entry.filled); - entry.binding = binding; - entry.filled = true; - } - Binding? cache( clang_types.CXCursor cursor, - Binding? Function(clang_types.CXCursor cursor) builder, + CachableBinding? Function(clang_types.CXCursor cursor) builder, ) { final usr = cursor.usr(); if (usr.isEmpty) return null; final entry = getOrInsert(usr); if (!entry.filled) { - final binding = builder(entry.definition ?? cursor); - if (binding != null) entry.binding = binding; + final cachable = builder(entry.definition ?? cursor); entry.filled = true; + if (cachable != null) { + entry.binding = cachable.binding; + cachable.filler(); + } } return entry.binding; } + void fillBinding(Binding binding) { + final entry = getOrInsert(binding.usr); + assert(!entry.filled); + entry.binding = binding; + entry.filled = true; + } + IndexEntry? operator [](String usr) => _entries[usr]; IndexEntry getOrInsert(String usr) { assert(usr.isNotEmpty); @@ -57,3 +60,13 @@ class IndexEntry { Binding? binding; IndexEntry({this.definition}); } + +// Some bindings need to split intial creation from filling, to avoid cycles. +// In that case they can provide a filler function that will be called after the +// cache entry is created. +class CachableBinding { + Binding binding; + void Function() filler; + CachableBinding(this.binding, [this.filler = _defaultFiller]); + static void _defaultFiller() {} +} 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 38d6728bba..92413ca714 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 @@ -123,7 +123,7 @@ Compound? _parseCompoundDeclaration( constructor, ) { assert(cursor.isDefinition); - final declUsr = cursor.usr(); + final usr = cursor.usr(); final String declName; // Only set name using USR if the type is not Anonymous (A struct is anonymous @@ -145,7 +145,6 @@ Compound? _parseCompoundDeclaration( } final decl = Declaration(usr: usr, originalName: declName); - final Compound compound; if (declName.isEmpty) { return constructor( name: 'Unnamed$className', diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/enumdecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/enumdecl_parser.dart index 52d4454505..dca61bd594 100644 --- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/enumdecl_parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/enumdecl_parser.dart @@ -25,9 +25,6 @@ import 'unnamed_enumdecl_parser.dart'; final usr = cursor.usr(); - final cachedEnum = context.bindingsIndex.getSeenEnum(usr); - if (cachedEnum != null) return (cachedEnum, cachedEnum.nativeType); - final String enumName; // Only set name using USR if the type is not Anonymous (i.e not inside // any typedef and declared inplace inside another type). @@ -114,7 +111,6 @@ import 'unnamed_enumdecl_parser.dart'; }); final suggestedStyle = isNSOptions ? EnumStyle.intConstants : null; enumClass.style = config.enums.style(decl, suggestedStyle); - context.bindingsIndex.addEnumToSeen(usr, enumClass); } if (hasNegativeEnumConstants) { 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 8d2743bc33..66ea95a9c0 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 @@ -141,7 +141,7 @@ void parseFunctionDeclaration(Context context, clang_types.CXCursor cursor) { exposeFunctionTypedefs: config.functions.includeTypedef(decl), isLeaf: config.functions.isLeaf(decl), objCReturnsRetained: objCReturnsRetained, - loadFromNativeAsset: config.ffiNativeConfig.enabled, + loadFromNativeAsset: config.output.style is NativeExternalBindings, ), ); } diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/objccategorydecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/objccategorydecl_parser.dart index 9b1a10f64d..f5781674f0 100644 --- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/objccategorydecl_parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/objccategorydecl_parser.dart @@ -6,12 +6,13 @@ import '../../code_generator.dart'; import '../../config_provider/config_types.dart'; import '../../context.dart'; import '../clang_bindings/clang_bindings.dart' as clang_types; +import '../translation_unit_parser.dart'; import '../utils.dart'; import 'api_availability.dart'; import 'objcinterfacedecl_parser.dart'; import 'objcprotocoldecl_parser.dart'; -ObjCCategory? parseObjCCategoryDeclaration( +CachableBinding? parseObjCCategoryDeclaration( Context context, clang_types.CXCursor cursor, ) { @@ -26,11 +27,6 @@ ObjCCategory? parseObjCCategoryDeclaration( final decl = Declaration(usr: usr, originalName: name); - final cachedCategory = context.bindingsIndex.getSeenObjCCategory(usr); - if (cachedCategory != null) { - return cachedCategory; - } - final apiAvailability = ApiAvailability.fromCursor(cursor, context); if (apiAvailability.availability == Availability.none) { logger.info('Omitting deprecated category $name'); @@ -72,39 +68,40 @@ ObjCCategory? parseObjCCategoryDeclaration( ), context: context, ); + parentInterface.categories.add(category); - cursor.visitChildren((child) { - switch (child.kind) { - case clang_types.CXCursorKind.CXCursor_ObjCProtocolRef: - final protoCursor = clang.clang_getCursorDefinition(child); - category.addProtocol( - parseObjCProtocolDeclaration(context, protoCursor), - ); - break; - case clang_types.CXCursorKind.CXCursor_ObjCPropertyDecl: - final (getter, setter) = parseObjCProperty( - context, - child, - decl, - objcCategories, - ); - category.addMethod(getter); - category.addMethod(setter); - break; - case clang_types.CXCursorKind.CXCursor_ObjCInstanceMethodDecl: - case clang_types.CXCursorKind.CXCursor_ObjCClassMethodDecl: - category.addMethod( - parseObjCMethod(context, child, decl, objcCategories), - ); - break; - } - }); + return CachableBinding(category, () { + cursor.visitChildren((child) { + switch (child.kind) { + case clang_types.CXCursorKind.CXCursor_ObjCProtocolRef: + final protoCursor = clang.clang_getCursorDefinition(child); + final protocol = parseCursor(context, protoCursor); + if (protocol != null) { + category.addProtocol(protocol as ObjCProtocol); + } + break; + case clang_types.CXCursorKind.CXCursor_ObjCPropertyDecl: + final (getter, setter) = parseObjCProperty( + context, + child, + decl, + objcCategories, + ); + category.addMethod(getter); + category.addMethod(setter); + break; + case clang_types.CXCursorKind.CXCursor_ObjCInstanceMethodDecl: + case clang_types.CXCursorKind.CXCursor_ObjCClassMethodDecl: + category.addMethod( + parseObjCMethod(context, child, decl, objcCategories), + ); + break; + } + }); - logger.fine( - '++++ Finished ObjC category: ' - 'Name: $name, ${cursor.completeStringRepr()}', - ); - - parentInterface.categories.add(category); - return category; + logger.fine( + '++++ Finished ObjC category: ' + 'Name: $name, ${cursor.completeStringRepr()}', + ); + }); } diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/objcinterfacedecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/objcinterfacedecl_parser.dart index cd3de4bc07..2f372176d7 100644 --- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/objcinterfacedecl_parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/objcinterfacedecl_parser.dart @@ -20,9 +20,6 @@ Type? parseObjCInterfaceDeclaration( ) { final usr = cursor.usr(); - final cachedItf = context.bindingsIndex.getSeenObjCInterface(usr); - if (cachedItf != null) return cachedItf; - final name = cursor.spelling(); final decl = Declaration(usr: usr, originalName: name); final apiAvailability = ApiAvailability.fromCursor(cursor, context); @@ -52,7 +49,6 @@ Type? parseObjCInterfaceDeclaration( ), apiAvailability: apiAvailability, ); - context.bindingsIndex.addObjCInterfaceToSeen(usr, itf); return itf; } diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/objcprotocoldecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/objcprotocoldecl_parser.dart index 469ffc7f42..20aec26e29 100644 --- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/objcprotocoldecl_parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/objcprotocoldecl_parser.dart @@ -10,7 +10,7 @@ import '../utils.dart'; import 'api_availability.dart'; import 'objcinterfacedecl_parser.dart'; -ObjCProtocol? parseObjCProtocolDeclaration( +CachableBinding? parseObjCProtocolDeclaration( Context context, clang_types.CXCursor cursor, ) { @@ -31,11 +31,6 @@ ObjCProtocol? parseObjCProtocolDeclaration( final decl = Declaration(usr: usr, originalName: name); - final cachedProtocol = bindingsIndex.getSeenObjCProtocol(usr); - if (cachedProtocol != null) { - return cachedProtocol; - } - // There's a strange shape in the AST for protocols seen in certain contexts, // where instead of the AST looking like (decl -> methods/etc), it looks like // (stubDecl --superProto-> decl -> methods/etc). If we try and parse the stub @@ -74,39 +69,39 @@ ObjCProtocol? parseObjCProtocolDeclaration( apiAvailability: apiAvailability, ); - // Make sure to add the protocol to the index before parsing the AST, to break - // cycles. - bindingsIndex.addObjCProtocolToSeen(usr, protocol); - - cursor.visitChildren((child) { - switch (child.kind) { - case clang_types.CXCursorKind.CXCursor_ObjCProtocolRef: - final declCursor = clang.clang_getCursorDefinition(child); - logger.fine( - ' > Super protocol: ${declCursor.completeStringRepr()}', - ); - final superProtocol = parseObjCProtocolDeclaration(context, declCursor); - if (superProtocol != null) { - protocol.superProtocols.add(superProtocol); - } - break; - case clang_types.CXCursorKind.CXCursor_ObjCPropertyDecl: - final (getter, setter) = parseObjCProperty( - context, - child, - decl, - objcProtocols, - ); - protocol.addMethod(getter); - protocol.addMethod(setter); - break; - case clang_types.CXCursorKind.CXCursor_ObjCInstanceMethodDecl: - case clang_types.CXCursorKind.CXCursor_ObjCClassMethodDecl: - protocol.addMethod( - parseObjCMethod(context, child, decl, objcProtocols), - ); - break; - } + return CachableBinding(protocol, () { + cursor.visitChildren((child) { + switch (child.kind) { + case clang_types.CXCursorKind.CXCursor_ObjCProtocolRef: + final declCursor = clang.clang_getCursorDefinition(child); + logger.fine( + ' > Super protocol: ${declCursor.completeStringRepr()}', + ); + final superProtocol = parseObjCProtocolDeclaration( + context, + declCursor, + ); + if (superProtocol != null) { + protocol.superProtocols.add(superProtocol); + } + break; + case clang_types.CXCursorKind.CXCursor_ObjCPropertyDecl: + final (getter, setter) = parseObjCProperty( + context, + child, + decl, + objcProtocols, + ); + protocol.addMethod(getter); + protocol.addMethod(setter); + break; + case clang_types.CXCursorKind.CXCursor_ObjCInstanceMethodDecl: + case clang_types.CXCursorKind.CXCursor_ObjCClassMethodDecl: + protocol.addMethod( + parseObjCMethod(context, child, decl, objcProtocols), + ); + break; + } + }); }); - return protocol; } diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/typedefdecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/typedefdecl_parser.dart index 59c2ada597..58930d548b 100644 --- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/typedefdecl_parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/typedefdecl_parser.dart @@ -39,9 +39,6 @@ Typealias? parseTypedefDeclaration( final name = cursor.spelling(); final usr = cursor.usr(); - final cachedType = bindingsIndex.getSeenTypealias(usr); - if (cachedType != null) return cachedType; - final decl = Declaration(usr: usr, originalName: name); final ct = clang.clang_getTypedefDeclUnderlyingType(cursor); final s = getCodeGenType( @@ -51,37 +48,29 @@ Typealias? parseTypedefDeclaration( originalCursor: cursor, ); - if (bindingsIndex.isSeenUnsupportedTypealias(usr)) { - // Do not process unsupported typealiases again. - } else if (s is UnimplementedType) { + if (s is UnimplementedType) { logger.fine( "Skipped Typedef '$name': " 'Unimplemented type referred.', ); - bindingsIndex.addUnsupportedTypealiasToSeen(usr); } else if (s is Compound && s.originalName == name) { // Ignore typedef if it refers to a compound with the same original name. - bindingsIndex.addUnsupportedTypealiasToSeen(usr); logger.fine( "Skipped Typedef '$name': " 'Name matches with referred struct/union.', ); } else if (s is EnumClass) { // Ignore typedefs to Enum. - bindingsIndex.addUnsupportedTypealiasToSeen(usr); logger.fine("Skipped Typedef '$name': typedef to enum."); } else if (s is HandleType) { // Ignore typedefs to Handle. logger.fine("Skipped Typedef '$name': typedef to Dart Handle."); - bindingsIndex.addUnsupportedTypealiasToSeen(usr); } else if (s is ConstantArray || s is IncompleteArray) { // Ignore typedefs to Constant Array. logger.fine("Skipped Typedef '$name': typedef to array."); - bindingsIndex.addUnsupportedTypealiasToSeen(usr); } else if (s is BooleanType) { // Ignore typedefs to Boolean. logger.fine("Skipped Typedef '$name': typedef to bool."); - bindingsIndex.addUnsupportedTypealiasToSeen(usr); } else { // Create typealias. final type = Typealias( @@ -91,7 +80,6 @@ Typealias? parseTypedefDeclaration( type: s, dartDoc: getCursorDocComment(context, cursor), ); - bindingsIndex.addTypealiasToSeen(usr, type); return type; } return null; diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/unnamed_enumdecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/unnamed_enumdecl_parser.dart index d608b01294..ce7a1d7a1d 100644 --- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/unnamed_enumdecl_parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/unnamed_enumdecl_parser.dart @@ -50,10 +50,6 @@ Constant? _addUnNamedEnumConstant( final bindingsIndex = context.bindingsIndex; final usr = cursor.usr(); - final oldConstant = bindingsIndex.getSeenUnnamedEnumConstant(usr); - if (oldConstant != null) { - return oldConstant; - } final unnamedEnumConstants = context.unnamedEnumConstants; final apiAvailability = ApiAvailability.fromCursor(cursor, context); @@ -75,7 +71,6 @@ Constant? _addUnNamedEnumConstant( rawType: 'int', rawValue: clang.clang_getEnumConstantDeclValue(cursor).toString(), ); - bindingsIndex.addUnnamedEnumConstantToSeen(cursor.usr(), constant); unnamedEnumConstants.add(constant); return constant; } 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 6bd83d10ba..d3daad71ee 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 @@ -14,17 +14,9 @@ Binding? parseVarDeclaration(Context context, clang_types.CXCursor cursor) { final logger = context.logger; final config = context.config; final nativeOutputStyle = config.output.style is NativeExternalBindings; - final bindingsIndex = context.bindingsIndex; final name = cursor.spelling(); final usr = cursor.usr(); - if (bindingsIndex.isSeenGlobalVar(usr)) { - return bindingsIndex.getSeenGlobalVar(usr); - } - if (bindingsIndex.isSeenVariableConstant(usr)) { - return bindingsIndex.getSeenVariableConstant(usr); - } - final decl = Declaration(usr: usr, originalName: name); final cType = cursor.type(); @@ -77,7 +69,6 @@ Binding? parseVarDeclaration(Context context, clang_types.CXCursor cursor) { logger.fine( '++++ Adding Constant from Global: ${cursor.completeStringRepr()}', ); - bindingsIndex.addVariableConstantToSeen(usr, constant); return constant; } } @@ -109,7 +100,6 @@ Binding? parseVarDeclaration(Context context, clang_types.CXCursor cursor) { constant: cType.isConstQualified, loadFromNativeAsset: nativeOutputStyle, ); - bindingsIndex.addGlobalVarToSeen(usr, global); return global; } 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 11de544be2..b114eb8f27 100644 --- a/pkgs/ffigen/lib/src/header_parser/translation_unit_parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/translation_unit_parser.dart @@ -21,7 +21,7 @@ void parseTranslationUnits( ) { final headers = {}; for (final translationUnitCursor in translationUnitCursors) { - _parseTranslationUnits(context, translationUnitCursor, headers); + _parseTranslationUnit(context, translationUnitCursor, headers); } } @@ -34,8 +34,8 @@ void _parseTranslationUnit( translationUnitCursor.visitChildren((cursor) { final file = cursor.sourceFileName(); if (file.isEmpty) return; - if (headers[file] ??= context.config.shouldIncludeHeader(Uri.file(file))) { - parseCursor(context, def); + if (headers[file] ??= context.config.headers.include(Uri.file(file))) { + parseCursor(context, cursor); } else { logger.finest( 'rootCursorVisitor:(not included) ${cursor.completeStringRepr()}', @@ -47,7 +47,7 @@ void _parseTranslationUnit( Binding? parseCursor(Context context, clang_types.CXCursor cursor) => context.bindingsIndex.cache(cursor, (def) => _parseCursor(context, def)); -Binding? _parseCursor(Context context, clang_types.CXCursor cursor) { +CachableBinding? _parseCursor(Context context, clang_types.CXCursor cursor) { final logger = context.logger; logger.finest('rootCursorVisitor: ${cursor.completeStringRepr()}'); try { @@ -72,7 +72,7 @@ Binding? _parseCursor(Context context, clang_types.CXCursor cursor) { saveMacroDefinition(context, cursor); return null; case clang_types.CXCursorKind.CXCursor_VarDecl: - return parseVarDeclaration(context, cursor); + return CachableBinding(parseVarDeclaration(context, cursor)); default: logger.finer('rootCursorVisitor: CursorKind not implemented'); } @@ -100,7 +100,7 @@ void buildUsrCursorDefinitionMap( final logger = context.logger; translationUnitCursor.visitChildren((cursor) { try { - context.cursorIndex.saveDefinition(cursor); + context.bindingsIndex.addDefinition(cursor); } catch (e, s) { logger.severe(e); logger.severe(s); 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 26f47bb30c..5ccfebb944 100644 --- a/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart +++ b/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart @@ -93,11 +93,7 @@ Type getCodeGenType( cursor, pointerReference, ); - if (type == null) { - return UnimplementedType('${cxtype.kindSpelling()} not implemented'); - } - _fillFromCursorIfNeeded(context, type, cursor, pointerReference); - return type; + return type ?? UnimplementedType('${cxtype.kindSpelling()} not implemented'); } // If the type doesn't have a declaration cursor, then it's a basic type such diff --git a/pkgs/ffigen/lib/src/header_parser/utils.dart b/pkgs/ffigen/lib/src/header_parser/utils.dart index 319ce149dc..690c9443c0 100644 --- a/pkgs/ffigen/lib/src/header_parser/utils.dart +++ b/pkgs/ffigen/lib/src/header_parser/utils.dart @@ -12,10 +12,7 @@ import '../code_generator.dart'; import '../config_provider/config_types.dart'; import '../context.dart'; import '../strings.dart'; -<<<<<<< HEAD -======= import '../strings.dart' as strings; ->>>>>>> main import 'clang_bindings/clang_bindings.dart' as clang_types; import 'type_extractor/extractor.dart'; From 16c13496762a63dd1f77ead9d29f368f4fc326f4 Mon Sep 17 00:00:00 2001 From: Liam Appelbe Date: Tue, 6 Jan 2026 10:02:03 +1100 Subject: [PATCH 09/19] wip --- pkgs/ffigen/lib/src/code_generator/bindings_index.dart | 1 + .../src/header_parser/type_extractor/extractor.dart | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/pkgs/ffigen/lib/src/code_generator/bindings_index.dart b/pkgs/ffigen/lib/src/code_generator/bindings_index.dart index 7c9b7b860e..6fd40fec69 100644 --- a/pkgs/ffigen/lib/src/code_generator/bindings_index.dart +++ b/pkgs/ffigen/lib/src/code_generator/bindings_index.dart @@ -31,6 +31,7 @@ class BindingsIndex { entry.filled = true; if (cachable != null) { entry.binding = cachable.binding; + // Note: Filler may re-enter this cache method. cachable.filler(); } } 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 5ccfebb944..04bc658b3e 100644 --- a/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart +++ b/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart @@ -93,7 +93,8 @@ Type getCodeGenType( cursor, pointerReference, ); - return type ?? UnimplementedType('${cxtype.kindSpelling()} not implemented'); + return type ?? + UnimplementedType('${cxtype.kindSpelling()} not implemented'); } // If the type doesn't have a declaration cursor, then it's a basic type such @@ -175,7 +176,7 @@ Type getCodeGenType( } } } - +/* Type? _createTypeFromCursor( Context context, clang_types.CXType cxtype, @@ -244,7 +245,8 @@ Type? _createTypeFromCursor( return UnimplementedType('Unknown type: ${cxtype.completeStringRepr()}'); } } - +*/ +/* void _fillFromCursorIfNeeded( Context context, Type? type, @@ -263,7 +265,7 @@ void _fillFromCursorIfNeeded( fillObjCInterfaceMethodsIfNeeded(context, type, cursor); } } - +*/ Type? _extractfromRecord( Context context, clang_types.CXType cxtype, From d3e845d4baeb205dabfaa2c855526be6a49e8dca Mon Sep 17 00:00:00 2001 From: Liam Appelbe Date: Tue, 6 Jan 2026 10:11:27 +1100 Subject: [PATCH 10/19] [ffigen] Remove pointerReference plumbing --- .../sub_parsers/compounddecl_parser.dart | 41 ++++--------- .../sub_parsers/typedefdecl_parser.dart | 12 +--- .../type_extractor/extractor.dart | 59 ++++--------------- 3 files changed, 26 insertions(+), 86 deletions(-) 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 426dcd7a03..b9b79b3ce5 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 @@ -13,35 +13,23 @@ import 'api_availability.dart'; Compound? parseStructDeclaration( clang_types.CXCursor cursor, - Context context, { - - /// To track if the declaration was used by reference(i.e T*). (Used to only - /// generate these as opaque if `dependency-only` was set to opaque). - bool pointerReference = false, -}) => _parseCompoundDeclaration( + Context context, +) => _parseCompoundDeclaration( cursor, context, - pointerReference, 'Struct', context.config.structs, Struct.new, ); -Compound? parseUnionDeclaration( - clang_types.CXCursor cursor, - Context context, { - - /// To track if the declaration was used by reference(i.e T*). (Used to only - /// generate these as opaque if `dependency-only` was set to opaque). - bool pointerReference = false, -}) => _parseCompoundDeclaration( - cursor, - context, - pointerReference, - 'Union', - context.config.unions, - Union.new, -); +Compound? parseUnionDeclaration(clang_types.CXCursor cursor, Context context) => + _parseCompoundDeclaration( + cursor, + context, + 'Union', + context.config.unions, + Union.new, + ); /// Holds temporary information regarding [compound] while parsing. class _ParsedCompound { @@ -109,7 +97,6 @@ class _ParsedCompound { Compound? _parseCompoundDeclaration( clang_types.CXCursor cursor, Context context, - bool pointerReference, String className, Declarations configDecl, Compound Function({ @@ -188,12 +175,8 @@ Compound? _parseCompoundDeclaration( void fillCompoundMembersIfNeeded( Compound compound, clang_types.CXCursor cursor, - Context context, { - - /// To track if the declaration was used by reference(i.e T*). (Used to only - /// generate these as opaque if `dependency-only` was set to opaque). - bool pointerReference = false, -}) { + Context context, +) { if (compound.parsedDependencies) return; final logger = context.logger; diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/typedefdecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/typedefdecl_parser.dart index 59c2ada597..8d8f8ae341 100644 --- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/typedefdecl_parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/typedefdecl_parser.dart @@ -30,9 +30,8 @@ import '../utils.dart'; /// by the config. Typealias? parseTypedefDeclaration( Context context, - clang_types.CXCursor cursor, { - bool pointerReference = false, -}) { + clang_types.CXCursor cursor, +) { final logger = context.logger; final config = context.config; final bindingsIndex = context.bindingsIndex; @@ -44,12 +43,7 @@ Typealias? parseTypedefDeclaration( final decl = Declaration(usr: usr, originalName: name); final ct = clang.clang_getTypedefDeclUnderlyingType(cursor); - final s = getCodeGenType( - context, - ct, - pointerReference: pointerReference, - originalCursor: cursor, - ); + final s = getCodeGenType(context, ct, originalCursor: cursor); if (bindingsIndex.isSeenUnsupportedTypealias(usr)) { // Do not process unsupported typealiases again. 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 182baa43b7..30f52453c7 100644 --- a/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart +++ b/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart @@ -28,9 +28,6 @@ Type getCodeGenType( Context context, clang_types.CXType cxtype, { - /// Passed on if a value was marked as a pointer before this one. - bool pointerReference = false, - /// Cursor of the declaration, currently this is useful only to extract /// parameter names in function types. clang_types.CXCursor? originalCursor, @@ -42,11 +39,7 @@ Type getCodeGenType( // Special case: Elaborated types just refer to another type. if (cxtype.kind == clang_types.CXTypeKind.CXType_Elaborated) { - return getCodeGenType( - context, - clang.clang_Type_getNamedType(cxtype), - pointerReference: pointerReference, - ); + return getCodeGenType(context, clang.clang_Type_getNamedType(cxtype)); } // These basic Objective C types skip the cache, and are conditional on the @@ -55,7 +48,7 @@ Type getCodeGenType( switch (cxtype.kind) { case clang_types.CXTypeKind.CXType_ObjCObjectPointer: final pt = clang.clang_getPointeeType(cxtype); - final s = getCodeGenType(context, pt, pointerReference: true); + final s = getCodeGenType(context, pt); if (s is ObjCInterface) { return s; } @@ -87,16 +80,11 @@ Type getCodeGenType( // any potential cycles, and dedupe the Type. final cursor = clang.clang_getTypeDeclaration(cxtype); if (cursor.kind != clang_types.CXCursorKind.CXCursor_NoDeclFound) { - final type = _createTypeFromCursor( - context, - cxtype, - cursor, - pointerReference, - ); + final type = _createTypeFromCursor(context, cxtype, cursor); if (type == null) { return UnimplementedType('${cxtype.kindSpelling()} not implemented'); } - _fillFromCursorIfNeeded(context, type, cursor, pointerReference); + _fillFromCursorIfNeeded(context, type, cursor); return type; } @@ -106,12 +94,7 @@ Type getCodeGenType( switch (cxtype.kind) { case clang_types.CXTypeKind.CXType_Pointer: final pt = clang.clang_getPointeeType(cxtype); - final s = getCodeGenType( - context, - pt, - pointerReference: true, - originalCursor: originalCursor, - ); + final s = getCodeGenType(context, pt, originalCursor: originalCursor); // Replace Pointer<_Dart_Handle> with Handle. if (s is Struct && s.usr == strings.dartHandleUsr) { @@ -184,7 +167,6 @@ Type? _createTypeFromCursor( Context context, clang_types.CXType cxtype, clang_types.CXCursor cursor, - bool pointerReference, ) { final logger = context.logger; final config = context.config; @@ -217,11 +199,7 @@ Type? _createTypeFromCursor( } } - final typealias = parseTypedefDeclaration( - context, - cursor, - pointerReference: pointerReference, - ); + final typealias = parseTypedefDeclaration(context, cursor); if (typealias != null) { return typealias; @@ -229,10 +207,10 @@ Type? _createTypeFromCursor( // Use underlying type if typealias couldn't be created or if the user // excluded this typedef. final ct = clang.clang_getTypedefDeclUnderlyingType(cursor); - return getCodeGenType(context, ct, pointerReference: pointerReference); + return getCodeGenType(context, ct); } case clang_types.CXTypeKind.CXType_Record: - return _extractfromRecord(context, cxtype, cursor, pointerReference); + return _extractfromRecord(context, cxtype, cursor); case clang_types.CXTypeKind.CXType_Enum: final (enumClass, nativeType) = parseEnumDeclaration(cursor, context); if (enumClass == null) { @@ -253,16 +231,10 @@ void _fillFromCursorIfNeeded( Context context, Type? type, clang_types.CXCursor cursor, - bool pointerReference, ) { if (type == null) return; if (type is Compound) { - fillCompoundMembersIfNeeded( - type, - cursor, - context, - pointerReference: pointerReference, - ); + fillCompoundMembersIfNeeded(type, cursor, context); } else if (type is ObjCInterface) { fillObjCInterfaceMethodsIfNeeded(context, type, cursor); } @@ -272,7 +244,6 @@ Type? _extractfromRecord( Context context, clang_types.CXType cxtype, clang_types.CXCursor cursor, - bool pointerReference, ) { final logger = context.logger; final config = context.config; @@ -285,21 +256,13 @@ Type? _extractfromRecord( logger.fine(' Type Mapped from type-map'); return config.structTypeMappings[declSpelling]!; } - return parseStructDeclaration( - cursor, - context, - pointerReference: pointerReference, - ); + return parseStructDeclaration(cursor, context); } else if (cursorKind == clang_types.CXCursorKind.CXCursor_UnionDecl) { if (config.unionTypeMappings.containsKey(declSpelling)) { logger.fine(' Type Mapped from type-map'); return config.unionTypeMappings[declSpelling]!; } - return parseUnionDeclaration( - cursor, - context, - pointerReference: pointerReference, - ); + return parseUnionDeclaration(cursor, context); } logger.fine( From b6f37fe96a0fc81d6b4a66b5c3a2d2e3a63033ab Mon Sep 17 00:00:00 2001 From: Liam Appelbe Date: Tue, 6 Jan 2026 11:14:41 +1100 Subject: [PATCH 11/19] wip --- .../lib/src/code_generator/enum_class.dart | 3 + .../sub_parsers/compounddecl_parser.dart | 20 ++++- .../sub_parsers/enumdecl_parser.dart | 23 +++-- .../sub_parsers/objcinterfacedecl_parser.dart | 5 +- .../header_parser/sub_parsers/var_parser.dart | 6 +- .../translation_unit_parser.dart | 6 +- .../type_extractor/extractor.dart | 83 ++++--------------- .../lib/src/visitor/apply_config_filters.dart | 5 +- .../lib/src/visitor/find_transitive_deps.dart | 6 ++ 9 files changed, 72 insertions(+), 85 deletions(-) diff --git a/pkgs/ffigen/lib/src/code_generator/enum_class.dart b/pkgs/ffigen/lib/src/code_generator/enum_class.dart index ede11c4a04..eb5e640a47 100644 --- a/pkgs/ffigen/lib/src/code_generator/enum_class.dart +++ b/pkgs/ffigen/lib/src/code_generator/enum_class.dart @@ -51,6 +51,8 @@ class EnumClass extends BindingType with HasLocalScope { /// Whether this enum should be generated as a collection of integers. EnumStyle style; + bool isAnonymous; + EnumClass({ super.usr, super.originalName, @@ -60,6 +62,7 @@ class EnumClass extends BindingType with HasLocalScope { List? enumConstants, required this.context, this.style = EnumStyle.dartEnum, + this.isAnonymous = false, }) : nativeType = nativeType ?? intType, enumConstants = enumConstants ?? []; 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 c2a6bc97f7..e69b053b13 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 @@ -11,7 +11,7 @@ import '../clang_bindings/clang_bindings.dart' as clang_types; import '../utils.dart'; import 'api_availability.dart'; -Compound? parseStructDeclaration( +CachableBinding? parseStructDeclaration( clang_types.CXCursor cursor, Context context, ) => _parseCompoundDeclaration( @@ -19,15 +19,17 @@ Compound? parseStructDeclaration( context, 'Struct', context.config.structs, + context.config.structTypeMappings, Struct.new, ); -Compound? parseUnionDeclaration(clang_types.CXCursor cursor, Context context) => +CachableBinding? parseUnionDeclaration(clang_types.CXCursor cursor, Context context) => _parseCompoundDeclaration( cursor, context, 'Union', context.config.unions, + context.config.unionTypeMappings, Union.new, ); @@ -99,6 +101,7 @@ Compound? _parseCompoundDeclaration( Context context, String className, Declarations configDecl, + Map configTypeMappings, Compound Function({ String? usr, String? originalName, @@ -110,6 +113,13 @@ Compound? _parseCompoundDeclaration( constructor, ) { assert(cursor.isDefinition); + + final mappedType = configTypeMappings[cursor.spelling()]; + if (mappedType != null) { + logger.fine(' Type Mapped from type-map: ${cursor.spelling()}'); + return CachableBinding(mappedType); + } + final usr = cursor.usr(); final String declName; @@ -132,8 +142,9 @@ Compound? _parseCompoundDeclaration( } final decl = Declaration(usr: usr, originalName: declName); + Compound compound; if (declName.isEmpty) { - return constructor( + compound = constructor( name: 'Unnamed$className', usr: usr, dartDoc: getCursorDocComment( @@ -148,7 +159,7 @@ Compound? _parseCompoundDeclaration( context.logger.fine( '++++ Adding $className: Name: $declName, ${cursor.completeStringRepr()}', ); - return constructor( + compound = constructor( usr: usr, originalName: declName, name: configDecl.rename(decl), @@ -161,6 +172,7 @@ Compound? _parseCompoundDeclaration( nativeType: cursor.type().spelling(), ); } + return CachableBinding(compound, fillCompoundMembersIfNeeded(compound, cursor, context)); } void fillCompoundMembersIfNeeded( diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/enumdecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/enumdecl_parser.dart index dca61bd594..b2c67a4171 100644 --- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/enumdecl_parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/enumdecl_parser.dart @@ -12,9 +12,9 @@ import '../utils.dart'; import 'api_availability.dart'; import 'unnamed_enumdecl_parser.dart'; -/// Parses an enum declaration. Returns (enumClass, nativeType). enumClass -/// is null for anonymous enums. -(EnumClass? enumClass, Type nativeType) parseEnumDeclaration( +/// Parses an enum declaration. +(EnumClass? enumClass, Type nativeType) +CachableBinding? parseEnumDeclaration( clang_types.CXCursor cursor, Context context, ) { @@ -48,17 +48,14 @@ import 'unnamed_enumdecl_parser.dart'; final apiAvailability = ApiAvailability.fromCursor(cursor, context); if (apiAvailability.availability == Availability.none) { logger.info('Omitting deprecated enum $enumName'); - return (null, nativeType); - } - - final decl = Declaration(usr: usr, originalName: enumName); - if (enumName.isEmpty) { + } else if (enumName.isEmpty) { logger.fine('Saving anonymous enum.'); final addedConstants = saveUnNamedEnum(context, cursor); hasNegativeEnumConstants = addedConstants .where((c) => c.rawValue.startsWith('-')) .isNotEmpty; } else { + final decl = Declaration(usr: usr, originalName: enumName); logger.fine('++++ Adding Enum: ${cursor.completeStringRepr()}'); enumClass = EnumClass( usr: usr, @@ -123,5 +120,13 @@ import 'unnamed_enumdecl_parser.dart'; enumClass?.nativeType = nativeType; } - return (enumClass, nativeType); + return CachableBinding( + enumClass ?? EnumClass( + usr: usr, + name: enumName, + nativeType: nativeType, + context: context, + isAnonymous: true, + ) + ); } diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/objcinterfacedecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/objcinterfacedecl_parser.dart index 2f372176d7..244ae3ccdd 100644 --- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/objcinterfacedecl_parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/objcinterfacedecl_parser.dart @@ -14,7 +14,7 @@ import 'objcprotocoldecl_parser.dart'; String applyModulePrefix(String name, String? module) => module == null ? name : '$module.$name'; -Type? parseObjCInterfaceDeclaration( +CachableBinding? parseObjCInterfaceDeclaration( Context context, clang_types.CXCursor cursor, ) { @@ -49,7 +49,8 @@ Type? parseObjCInterfaceDeclaration( ), apiAvailability: apiAvailability, ); - return itf; + return CachableBinding( + itf, () => fillObjCInterfaceMethodsIfNeeded(context, itf, cursor)); } void fillObjCInterfaceMethodsIfNeeded( 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 d3daad71ee..8a5495e278 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 @@ -10,7 +10,7 @@ import '../clang_bindings/clang_bindings.dart' as clang_types; import '../utils.dart'; /// Parses a global variable -Binding? parseVarDeclaration(Context context, clang_types.CXCursor cursor) { +CachableBinding? parseVarDeclaration(Context context, clang_types.CXCursor cursor) { final logger = context.logger; final config = context.config; final nativeOutputStyle = config.output.style is NativeExternalBindings; @@ -69,7 +69,7 @@ Binding? parseVarDeclaration(Context context, clang_types.CXCursor cursor) { logger.fine( '++++ Adding Constant from Global: ${cursor.completeStringRepr()}', ); - return constant; + return CachableBinding(constant); } } @@ -101,5 +101,5 @@ Binding? parseVarDeclaration(Context context, clang_types.CXCursor cursor) { loadFromNativeAsset: nativeOutputStyle, ); - return global; + return CachableBinding(global); } 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 b114eb8f27..44ea0c4d6b 100644 --- a/pkgs/ffigen/lib/src/header_parser/translation_unit_parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/translation_unit_parser.dart @@ -59,9 +59,13 @@ CachableBinding? _parseCursor(Context context, clang_types.CXCursor cursor) { parseFunctionDeclaration(context, cursor); return null; case clang_types.CXCursorKind.CXCursor_StructDecl: + return parseStructDeclaration(cursor, context); case clang_types.CXCursorKind.CXCursor_UnionDecl: + return parseUnionDeclaration(cursor, context); case clang_types.CXCursorKind.CXCursor_EnumDecl: + return parseEnumDeclaration(cursor, context); case clang_types.CXCursorKind.CXCursor_ObjCInterfaceDecl: + return parseObjCInterfaceDeclaration(context, cursor); case clang_types.CXCursorKind.CXCursor_TypedefDecl: return _getCodeGenTypeFromCursor(context, cursor); case clang_types.CXCursorKind.CXCursor_ObjCCategoryDecl: @@ -72,7 +76,7 @@ CachableBinding? _parseCursor(Context context, clang_types.CXCursor cursor) { saveMacroDefinition(context, cursor); return null; case clang_types.CXCursorKind.CXCursor_VarDecl: - return CachableBinding(parseVarDeclaration(context, cursor)); + return parseVarDeclaration(context, cursor); default: logger.finer('rootCursorVisitor: CursorKind not implemented'); } 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 4c3f2a2c58..636e3010e0 100644 --- a/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart +++ b/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart @@ -80,13 +80,7 @@ Type getCodeGenType( // any potential cycles, and dedupe the Type. final cursor = clang.clang_getTypeDeclaration(cxtype); if (cursor.kind != clang_types.CXCursorKind.CXCursor_NoDeclFound) { - final type = _createTypeFromCursor( - context, - cxtype, - cursor, - ); - return type ?? - UnimplementedType('${cxtype.kindSpelling()} not implemented'); + return _createTypeFromCursor(context, cursor); } // If the type doesn't have a declaration cursor, then it's a basic type such @@ -163,19 +157,24 @@ Type getCodeGenType( } } } -/* -Type? _createTypeFromCursor( - Context context, - clang_types.CXType cxtype, - clang_types.CXCursor cursor, -) { - final logger = context.logger; - final config = context.config; + +Type _createTypeFromCursor(Context context, clang_types.CXCursor cursor) { final usr = cursor.usr(); - if (config.importedTypesByUsr.containsKey(usr)) { - logger.fine(' Type $usr mapped from usr'); - return config.importedTypesByUsr[usr]!; + final importedType = context.config.importedTypesByUsr[usr]; + if (importedType != null) { + context.logger.fine(' Type $usr mapped from usr'); + return importedType; } + + final binding = parseCursor(context, cursor) as BindingType?; + if (binding == null) { + return UnimplementedType('Unknown type: ${cursor.completeStringRepr()}'); + } + if (binding is EnumClass && binding.isOmitted) { + return binding.nativeType; + } + return binding; + switch (cxtype.kind) { case clang_types.CXTypeKind.CXType_Typedef: final spelling = clang.clang_getTypedefName(cxtype).toStringAndDispose(); @@ -210,24 +209,9 @@ Type? _createTypeFromCursor( final ct = clang.clang_getTypedefDeclUnderlyingType(cursor); return getCodeGenType(context, ct); } - case clang_types.CXTypeKind.CXType_Record: - return _extractfromRecord(context, cxtype, cursor); - case clang_types.CXTypeKind.CXType_Enum: - final (enumClass, nativeType) = parseEnumDeclaration(cursor, context); - if (enumClass == null) { - // Handle anonymous enum declarations within another declaration. - return nativeType; - } else { - return enumClass; - } - case clang_types.CXTypeKind.CXType_ObjCInterface: - case clang_types.CXTypeKind.CXType_ObjCObject: - return parseObjCInterfaceDeclaration(context, cursor); - default: - return UnimplementedType('Unknown type: ${cxtype.completeStringRepr()}'); } } -*/ + /* void _fillFromCursorIfNeeded( Context context, @@ -242,37 +226,6 @@ void _fillFromCursorIfNeeded( } } */ -Type? _extractfromRecord( - Context context, - clang_types.CXType cxtype, - clang_types.CXCursor cursor, -) { - final logger = context.logger; - final config = context.config; - logger.fine('${_padding}_extractfromRecord: ${cursor.completeStringRepr()}'); - - final declSpelling = cursor.spelling(); - final cursorKind = clang.clang_getCursorKind(cursor); - if (cursorKind == clang_types.CXCursorKind.CXCursor_StructDecl) { - if (config.structTypeMappings.containsKey(declSpelling)) { - logger.fine(' Type Mapped from type-map'); - return config.structTypeMappings[declSpelling]!; - } - return parseStructDeclaration(cursor, context); - } else if (cursorKind == clang_types.CXCursorKind.CXCursor_UnionDecl) { - if (config.unionTypeMappings.containsKey(declSpelling)) { - logger.fine(' Type Mapped from type-map'); - return config.unionTypeMappings[declSpelling]!; - } - return parseUnionDeclaration(cursor, context); - } - - logger.fine( - 'typedeclarationCursorVisitor: _extractfromRecord: ' - 'Not Implemented, ${cursor.completeStringRepr()}', - ); - return UnimplementedType('${cxtype.kindSpelling()} not implemented'); -} // Used for function pointer arguments. Type _extractFromFunctionProto( diff --git a/pkgs/ffigen/lib/src/visitor/apply_config_filters.dart b/pkgs/ffigen/lib/src/visitor/apply_config_filters.dart index 9801f1f9c0..b2c8924bd0 100644 --- a/pkgs/ffigen/lib/src/visitor/apply_config_filters.dart +++ b/pkgs/ffigen/lib/src/visitor/apply_config_filters.dart @@ -27,7 +27,10 @@ class ApplyConfigFiltersVisitation extends Visitation { void visitUnion(Union node) => _visitImpl(node, config.unions); @override - void visitEnumClass(EnumClass node) => _visitImpl(node, config.enums); + void visitEnumClass(EnumClass node) { + if (node.isAnonymous) return; + _visitImpl(node, config.enums); + } @override void visitFunc(Func node) => _visitImpl(node, config.functions); diff --git a/pkgs/ffigen/lib/src/visitor/find_transitive_deps.dart b/pkgs/ffigen/lib/src/visitor/find_transitive_deps.dart index 9be48f5527..4d071ce6a2 100644 --- a/pkgs/ffigen/lib/src/visitor/find_transitive_deps.dart +++ b/pkgs/ffigen/lib/src/visitor/find_transitive_deps.dart @@ -16,6 +16,12 @@ class FindTransitiveDepsVisitation extends Visitation { node.visitChildren(visitor); transitives.add(node); } + + @override + void visitEnumClass(EnumClass node) { + if (node.isAnonymous) return; + visitBinding(node); + } } class FindDirectTransitiveDepsVisitation extends Visitation { From 8df1380d68304a4e7c1135a8df8c730f178de70a Mon Sep 17 00:00:00 2001 From: Liam Appelbe Date: Tue, 6 Jan 2026 13:00:54 +1100 Subject: [PATCH 12/19] wip --- .../lib/src/code_generator/typealias.dart | 8 +++++ .../sub_parsers/enumdecl_parser.dart | 1 - .../sub_parsers/typedefdecl_parser.dart | 27 ++++++++++++--- .../type_extractor/extractor.dart | 34 ++++--------------- 4 files changed, 38 insertions(+), 32 deletions(-) diff --git a/pkgs/ffigen/lib/src/code_generator/typealias.dart b/pkgs/ffigen/lib/src/code_generator/typealias.dart index 999a257493..b0a4c6a3db 100644 --- a/pkgs/ffigen/lib/src/code_generator/typealias.dart +++ b/pkgs/ffigen/lib/src/code_generator/typealias.dart @@ -21,6 +21,7 @@ class Typealias extends BindingType { final Type type; Symbol? _ffiDartAliasName; Symbol? dartAliasName; + bool isAnonymous; /// Creates a Typealias. /// @@ -71,6 +72,12 @@ class Typealias extends BindingType { ); } + Typealias.anonymous({ + required String usr, + required String name, + required Type type, + }) : this._(usr: usr, name: name, type: type, isAnonymous: true); + Typealias._({ super.usr, super.originalName, @@ -79,6 +86,7 @@ class Typealias extends BindingType { required this.type, bool genFfiDartType = false, super.isInternal, + bool isAnonymous = false, }) : _ffiDartAliasName = genFfiDartType ? Symbol('Dart$name', SymbolKind.klass) : null, diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/enumdecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/enumdecl_parser.dart index b2c67a4171..c3f8f94a99 100644 --- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/enumdecl_parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/enumdecl_parser.dart @@ -13,7 +13,6 @@ import 'api_availability.dart'; import 'unnamed_enumdecl_parser.dart'; /// Parses an enum declaration. -(EnumClass? enumClass, Type nativeType) CachableBinding? parseEnumDeclaration( clang_types.CXCursor cursor, Context context, diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/typedefdecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/typedefdecl_parser.dart index 78952375b4..7c156d53a7 100644 --- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/typedefdecl_parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/typedefdecl_parser.dart @@ -34,10 +34,30 @@ Typealias? parseTypedefDeclaration( ) { final logger = context.logger; final config = context.config; - final bindingsIndex = context.bindingsIndex; final name = cursor.spelling(); final usr = cursor.usr(); + if (config.objectiveC != null && name == strings.objcBOOL) { + // Objective C's BOOL type can be either bool or signed char, depending + // on the platform. We want to present a consistent API to the user, and + // those two types are ABI compatible, so just return bool regardless. + return BooleanType(); + } + + if (config.typedefTypeMappings.containsKey(name)) { + logger.fine(' Type $name mapped from type-map'); + return config.typedefTypeMappings[name]!; + } + + if (config.typedefs.useSupportedTypedefs) { + final supportedTypedef = suportedTypedefToSuportedNativeType[name] ?? + supportedTypedefToImportedType[name]; + if (supportedTypedef != null) { + logger.fine(' Type Mapped from supported typedef'); + return NativeType(supportedTypedef); + } + } + final decl = Declaration(usr: usr, originalName: name); final ct = clang.clang_getTypedefDeclUnderlyingType(cursor); final s = getCodeGenType(context, ct, originalCursor: cursor); @@ -67,14 +87,13 @@ Typealias? parseTypedefDeclaration( logger.fine("Skipped Typedef '$name': typedef to bool."); } else { // Create typealias. - final type = Typealias( + return Typealias( usr: usr, originalName: name, name: config.typedefs.rename(decl), type: s, dartDoc: getCursorDocComment(context, cursor), ); - return type; } - return null; + return Typealias.anonymous(usr: usr, name: name, type: s); } 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 636e3010e0..b31ab2433d 100644 --- a/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart +++ b/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart @@ -166,38 +166,18 @@ Type _createTypeFromCursor(Context context, clang_types.CXCursor cursor) { return importedType; } - final binding = parseCursor(context, cursor) as BindingType?; - if (binding == null) { - return UnimplementedType('Unknown type: ${cursor.completeStringRepr()}'); + final binding = parseCursor(context, cursor); + if (binding is Type) { + if (binding is EnumClass && binding.isAnonymous) { + return binding.nativeType; + } else if + return binding; } - if (binding is EnumClass && binding.isOmitted) { - return binding.nativeType; - } - return binding; + return UnimplementedType('Unknown type: ${cursor.completeStringRepr()}'); switch (cxtype.kind) { case clang_types.CXTypeKind.CXType_Typedef: final spelling = clang.clang_getTypedefName(cxtype).toStringAndDispose(); - if (config.objectiveC != null && spelling == strings.objcBOOL) { - // Objective C's BOOL type can be either bool or signed char, depending - // on the platform. We want to present a consistent API to the user, and - // those two types are ABI compatible, so just return bool regardless. - return BooleanType(); - } - if (config.typedefTypeMappings.containsKey(spelling)) { - logger.fine(' Type $spelling mapped from type-map'); - return config.typedefTypeMappings[spelling]!; - } - // Get name from supported typedef name if config allows. - if (config.typedefs.useSupportedTypedefs) { - if (suportedTypedefToSuportedNativeType.containsKey(spelling)) { - logger.fine(' Type Mapped from supported typedef'); - return NativeType(suportedTypedefToSuportedNativeType[spelling]!); - } else if (supportedTypedefToImportedType.containsKey(spelling)) { - logger.fine(' Type Mapped from supported typedef'); - return supportedTypedefToImportedType[spelling]!; - } - } final typealias = parseTypedefDeclaration(context, cursor); From a776cad31a2d36e035f45c0597cb0db0c730bdd0 Mon Sep 17 00:00:00 2001 From: Liam Appelbe Date: Tue, 6 Jan 2026 13:11:09 +1100 Subject: [PATCH 13/19] [ffigen] Simplify parseTypedefDeclaration signature --- pkgs/ffigen/lib/src/code_generator/typealias.dart | 11 +++++++++++ .../header_parser/sub_parsers/typedefdecl_parser.dart | 7 ++----- .../src/header_parser/type_extractor/extractor.dart | 6 +++--- pkgs/ffigen/lib/src/visitor/apply_config_filters.dart | 5 ++++- pkgs/ffigen/lib/src/visitor/find_transitive_deps.dart | 6 ++++++ 5 files changed, 26 insertions(+), 9 deletions(-) diff --git a/pkgs/ffigen/lib/src/code_generator/typealias.dart b/pkgs/ffigen/lib/src/code_generator/typealias.dart index 999a257493..3bf9182fd4 100644 --- a/pkgs/ffigen/lib/src/code_generator/typealias.dart +++ b/pkgs/ffigen/lib/src/code_generator/typealias.dart @@ -22,6 +22,9 @@ class Typealias extends BindingType { Symbol? _ffiDartAliasName; Symbol? dartAliasName; + // Don't code gen this alias at all, just use the [type] directly. + bool isAnonymous; + /// Creates a Typealias. /// /// If [genFfiDartType] is true, a binding is generated for the Ffi Dart type @@ -71,6 +74,12 @@ class Typealias extends BindingType { ); } + Typealias.anonymous({ + required String usr, + required String name, + required Type type, + }) : this._(usr: usr, name: name, type: type, isAnonymous: true); + Typealias._({ super.usr, super.originalName, @@ -79,6 +88,7 @@ class Typealias extends BindingType { required this.type, bool genFfiDartType = false, super.isInternal, + this.isAnonymous = false, }) : _ffiDartAliasName = genFfiDartType ? Symbol('Dart$name', SymbolKind.klass) : null, @@ -97,6 +107,7 @@ class Typealias extends BindingType { @override BindingString toBindingString(Writer w) { + assert(!isAnonymous); final context = w.context; final sb = StringBuffer(); sb.write(makeDartDoc(dartDoc)); diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/typedefdecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/typedefdecl_parser.dart index 59c2ada597..ac0bf5e8d9 100644 --- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/typedefdecl_parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/typedefdecl_parser.dart @@ -25,10 +25,7 @@ import '../utils.dart'; /// /// typedef A D; // Typeref. /// ``` -/// -/// Returns `null` if the typedef could not be generated or has been excluded -/// by the config. -Typealias? parseTypedefDeclaration( +Typealias parseTypedefDeclaration( Context context, clang_types.CXCursor cursor, { bool pointerReference = false, @@ -94,5 +91,5 @@ Typealias? parseTypedefDeclaration( bindingsIndex.addTypealiasToSeen(usr, type); return type; } - return null; + return Typealias.anonymous(usr: usr, name: name, type: s); } 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 182baa43b7..7808ff2aa5 100644 --- a/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart +++ b/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart @@ -223,13 +223,13 @@ Type? _createTypeFromCursor( pointerReference: pointerReference, ); - if (typealias != null) { - return typealias; - } else { + if (typealias.isAnonymous) { // Use underlying type if typealias couldn't be created or if the user // excluded this typedef. final ct = clang.clang_getTypedefDeclUnderlyingType(cursor); return getCodeGenType(context, ct, pointerReference: pointerReference); + } else { + return typealias; } case clang_types.CXTypeKind.CXType_Record: return _extractfromRecord(context, cxtype, cursor, pointerReference); diff --git a/pkgs/ffigen/lib/src/visitor/apply_config_filters.dart b/pkgs/ffigen/lib/src/visitor/apply_config_filters.dart index 9801f1f9c0..442dcb1882 100644 --- a/pkgs/ffigen/lib/src/visitor/apply_config_filters.dart +++ b/pkgs/ffigen/lib/src/visitor/apply_config_filters.dart @@ -99,5 +99,8 @@ class ApplyConfigFiltersVisitation extends Visitation { } @override - void visitTypealias(Typealias node) => _visitImpl(node, config.typedefs); + void visitTypealias(Typealias node) { + if (node.isAnonymous) return; + _visitImpl(node, config.typedefs); + } } diff --git a/pkgs/ffigen/lib/src/visitor/find_transitive_deps.dart b/pkgs/ffigen/lib/src/visitor/find_transitive_deps.dart index 9be48f5527..b335e4e877 100644 --- a/pkgs/ffigen/lib/src/visitor/find_transitive_deps.dart +++ b/pkgs/ffigen/lib/src/visitor/find_transitive_deps.dart @@ -16,6 +16,12 @@ class FindTransitiveDepsVisitation extends Visitation { node.visitChildren(visitor); transitives.add(node); } + + @override + void visitTypealias(Typealias node) { + if (node.isAnonymous) return; + visitBinding(node); + } } class FindDirectTransitiveDepsVisitation extends Visitation { From 0b5b6c10e382a8de8e38c66a18462d78657d4c1a Mon Sep 17 00:00:00 2001 From: Liam Appelbe Date: Tue, 6 Jan 2026 15:01:58 +1100 Subject: [PATCH 14/19] wip --- .../lib/src/code_generator/typealias.dart | 1 - .../sub_parsers/compounddecl_parser.dart | 29 +++++++----- .../sub_parsers/enumdecl_parser.dart | 15 ++++--- .../sub_parsers/objcinterfacedecl_parser.dart | 11 +++-- .../sub_parsers/typedefdecl_parser.dart | 3 +- .../header_parser/sub_parsers/var_parser.dart | 5 ++- .../type_extractor/extractor.dart | 45 ++++--------------- 7 files changed, 47 insertions(+), 62 deletions(-) diff --git a/pkgs/ffigen/lib/src/code_generator/typealias.dart b/pkgs/ffigen/lib/src/code_generator/typealias.dart index 416327bd85..3bf9182fd4 100644 --- a/pkgs/ffigen/lib/src/code_generator/typealias.dart +++ b/pkgs/ffigen/lib/src/code_generator/typealias.dart @@ -21,7 +21,6 @@ class Typealias extends BindingType { final Type type; Symbol? _ffiDartAliasName; Symbol? dartAliasName; - bool isAnonymous; // Don't code gen this alias at all, just use the [type] directly. bool isAnonymous; 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 e69b053b13..7e4ff5c6a3 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 @@ -23,15 +23,17 @@ CachableBinding? parseStructDeclaration( Struct.new, ); -CachableBinding? parseUnionDeclaration(clang_types.CXCursor cursor, Context context) => - _parseCompoundDeclaration( - cursor, - context, - 'Union', - context.config.unions, - context.config.unionTypeMappings, - Union.new, - ); +CachableBinding? parseUnionDeclaration( + clang_types.CXCursor cursor, + Context context, +) => _parseCompoundDeclaration( + cursor, + context, + 'Union', + context.config.unions, + context.config.unionTypeMappings, + Union.new, +); /// Holds temporary information regarding [compound] while parsing. class _ParsedCompound { @@ -96,7 +98,7 @@ class _ParsedCompound { } /// Parses a compound declaration. -Compound? _parseCompoundDeclaration( +CachableBinding? _parseCompoundDeclaration( clang_types.CXCursor cursor, Context context, String className, @@ -116,7 +118,7 @@ Compound? _parseCompoundDeclaration( final mappedType = configTypeMappings[cursor.spelling()]; if (mappedType != null) { - logger.fine(' Type Mapped from type-map: ${cursor.spelling()}'); + context.logger.fine(' Type Mapped from type-map: ${cursor.spelling()}'); return CachableBinding(mappedType); } @@ -172,7 +174,10 @@ Compound? _parseCompoundDeclaration( nativeType: cursor.type().spelling(), ); } - return CachableBinding(compound, fillCompoundMembersIfNeeded(compound, cursor, context)); + return CachableBinding( + compound, + () => fillCompoundMembersIfNeeded(compound, cursor, context), + ); } void fillCompoundMembersIfNeeded( diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/enumdecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/enumdecl_parser.dart index c3f8f94a99..fb959ea442 100644 --- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/enumdecl_parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/enumdecl_parser.dart @@ -120,12 +120,13 @@ CachableBinding? parseEnumDeclaration( } return CachableBinding( - enumClass ?? EnumClass( - usr: usr, - name: enumName, - nativeType: nativeType, - context: context, - isAnonymous: true, - ) + enumClass ?? + EnumClass( + usr: usr, + name: enumName, + nativeType: nativeType, + context: context, + isAnonymous: true, + ), ); } diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/objcinterfacedecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/objcinterfacedecl_parser.dart index 244ae3ccdd..dd54525cf1 100644 --- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/objcinterfacedecl_parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/objcinterfacedecl_parser.dart @@ -7,6 +7,7 @@ import '../../config_provider/config.dart'; import '../../config_provider/config_types.dart'; import '../../context.dart'; import '../clang_bindings/clang_bindings.dart' as clang_types; +import '../translation_unit_parser.dart'; import '../utils.dart'; import 'api_availability.dart'; import 'objcprotocoldecl_parser.dart'; @@ -50,7 +51,9 @@ CachableBinding? parseObjCInterfaceDeclaration( apiAvailability: apiAvailability, ); return CachableBinding( - itf, () => fillObjCInterfaceMethodsIfNeeded(context, itf, cursor)); + itf, + () => fillObjCInterfaceMethodsIfNeeded(context, itf, cursor), + ); } void fillObjCInterfaceMethodsIfNeeded( @@ -81,8 +84,10 @@ void fillObjCInterfaceMethodsIfNeeded( _parseSuperType(context, child, itf); break; case clang_types.CXCursorKind.CXCursor_ObjCProtocolRef: - final protoCursor = clang.clang_getCursorDefinition(child); - itf.addProtocol(parseObjCProtocolDeclaration(context, protoCursor)); + final p = parseCursor(context, clang.clang_getCursorDefinition(child)); + if (p is ObjCProtocol) { + itf.addProtocol(p); + } break; case clang_types.CXCursorKind.CXCursor_ObjCPropertyDecl: final (getter, setter) = parseObjCProperty( diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/typedefdecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/typedefdecl_parser.dart index 9443229aa0..0c09bdefbb 100644 --- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/typedefdecl_parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/typedefdecl_parser.dart @@ -47,7 +47,8 @@ Typealias parseTypedefDeclaration( } if (config.typedefs.useSupportedTypedefs) { - final supportedTypedef = suportedTypedefToSuportedNativeType[name] ?? + final supportedTypedef = + suportedTypedefToSuportedNativeType[name] ?? supportedTypedefToImportedType[name]; if (supportedTypedef != null) { logger.fine(' Type Mapped from supported typedef'); 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 8a5495e278..1edb8aa1f5 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 @@ -10,7 +10,10 @@ import '../clang_bindings/clang_bindings.dart' as clang_types; import '../utils.dart'; /// Parses a global variable -CachableBinding? parseVarDeclaration(Context context, clang_types.CXCursor cursor) { +CachableBinding? parseVarDeclaration( + Context context, + clang_types.CXCursor cursor, +) { final logger = context.logger; final config = context.config; final nativeOutputStyle = config.output.style is NativeExternalBindings; 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 2680507f22..ef5f3a2256 100644 --- a/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart +++ b/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart @@ -166,46 +166,17 @@ Type _createTypeFromCursor(Context context, clang_types.CXCursor cursor) { return importedType; } - final binding = parseCursor(context, cursor); - if (binding is Type) { - if (binding is EnumClass && binding.isAnonymous) { - return binding.nativeType; - } else if - return binding; + final type = parseCursor(context, cursor); + if (type is Type) { + if (type is EnumClass && type.isAnonymous) { + return type.nativeType; + } else if (type is Typealias && type.isAnonymous) { + return type.type; + } + return type; } return UnimplementedType('Unknown type: ${cursor.completeStringRepr()}'); - - switch (cxtype.kind) { - case clang_types.CXTypeKind.CXType_Typedef: - final spelling = clang.clang_getTypedefName(cxtype).toStringAndDispose(); - - final typealias = parseTypedefDeclaration(context, cursor); - - if (typealias.isAnonymous) { - // Use underlying type if typealias couldn't be created or if the user - // excluded this typedef. - final ct = clang.clang_getTypedefDeclUnderlyingType(cursor); - return getCodeGenType(context, ct); - } else { - return typealias; - } - } -} - -/* -void _fillFromCursorIfNeeded( - Context context, - Type? type, - clang_types.CXCursor cursor, -) { - if (type == null) return; - if (type is Compound) { - fillCompoundMembersIfNeeded(type, cursor, context); - } else if (type is ObjCInterface) { - fillObjCInterfaceMethodsIfNeeded(context, type, cursor); - } } -*/ // Used for function pointer arguments. Type _extractFromFunctionProto( From 93618b4559cbee07a85b620c99944fde9430ae4b Mon Sep 17 00:00:00 2001 From: Liam Appelbe Date: Wed, 7 Jan 2026 10:18:01 +1100 Subject: [PATCH 15/19] wip --- .../src/code_generator/bindings_index.dart | 17 +++++++------- .../lib/src/code_generator/objc_block.dart | 2 +- .../lib/src/config_provider/spec_utils.dart | 2 +- .../sub_parsers/objcprotocoldecl_parser.dart | 13 ++++------- .../sub_parsers/typedefdecl_parser.dart | 9 ++++---- .../type_extractor/cxtypekindmap.dart | 23 ++++++++++--------- 6 files changed, 31 insertions(+), 35 deletions(-) diff --git a/pkgs/ffigen/lib/src/code_generator/bindings_index.dart b/pkgs/ffigen/lib/src/code_generator/bindings_index.dart index 6fd40fec69..65a63e9337 100644 --- a/pkgs/ffigen/lib/src/code_generator/bindings_index.dart +++ b/pkgs/ffigen/lib/src/code_generator/bindings_index.dart @@ -5,6 +5,7 @@ import 'binding.dart'; import '../header_parser/clang_bindings/clang_bindings.dart' as clang_types; import '../header_parser/utils.dart'; +import '../visitor/ast.dart'; class BindingsIndex { final _entries = {}; @@ -19,7 +20,7 @@ class BindingsIndex { _entries[usr] ??= IndexEntry(definition: cursor); } - Binding? cache( + AstNode? cache( clang_types.CXCursor cursor, CachableBinding? Function(clang_types.CXCursor cursor) builder, ) { @@ -30,18 +31,18 @@ class BindingsIndex { final cachable = builder(entry.definition ?? cursor); entry.filled = true; if (cachable != null) { - entry.binding = cachable.binding; + entry.node = cachable.node; // Note: Filler may re-enter this cache method. cachable.filler(); } } - return entry.binding; + return entry.node; } void fillBinding(Binding binding) { final entry = getOrInsert(binding.usr); assert(!entry.filled); - entry.binding = binding; + entry.node = binding; entry.filled = true; } @@ -52,13 +53,13 @@ class BindingsIndex { } Set get bindings => - _entries.values.map((e) => e.binding).nonNulls.toSet(); + _entries.values.map((e) => e.node).whereType().toSet(); } class IndexEntry { clang_types.CXCursor? definition; bool filled = false; - Binding? binding; + AstNode? node; IndexEntry({this.definition}); } @@ -66,8 +67,8 @@ class IndexEntry { // In that case they can provide a filler function that will be called after the // cache entry is created. class CachableBinding { - Binding binding; + AstNode node; void Function() filler; - CachableBinding(this.binding, [this.filler = _defaultFiller]); + CachableBinding(this.node, [this.filler = _defaultFiller]); static void _defaultFiller() {} } diff --git a/pkgs/ffigen/lib/src/code_generator/objc_block.dart b/pkgs/ffigen/lib/src/code_generator/objc_block.dart index ee53ab4e2d..94a2944c52 100644 --- a/pkgs/ffigen/lib/src/code_generator/objc_block.dart +++ b/pkgs/ffigen/lib/src/code_generator/objc_block.dart @@ -41,7 +41,7 @@ class ObjCBlock extends BindingType with HasLocalScope { final usr = _getBlockUsr(returnType, renamedParams, returnsRetained); - return (context.bindingsIndex.getOrInsert(usr).binding ??= ObjCBlock._( + return (context.bindingsIndex.getOrInsert(usr).node ??= ObjCBlock._( context, usr: usr, name: _getBlockName(returnType, renamedParams.map((a) => a.type)), diff --git a/pkgs/ffigen/lib/src/config_provider/spec_utils.dart b/pkgs/ffigen/lib/src/config_provider/spec_utils.dart index 10963e3006..7e69e07146 100644 --- a/pkgs/ffigen/lib/src/config_provider/spec_utils.dart +++ b/pkgs/ffigen/lib/src/config_provider/spec_utils.dart @@ -205,7 +205,7 @@ Type makeTypeFromRawVarArgType( } else if (supportedTypedefToImportedType.containsKey(rawBaseType)) { baseType = supportedTypedefToImportedType[rawBaseType]!; } else if (suportedTypedefToSuportedNativeType.containsKey(rawBaseType)) { - baseType = NativeType(suportedTypedefToSuportedNativeType[rawBaseType]!); + baseType = suportedTypedefToSuportedNativeType[rawBaseType]!; } else { // Use library import if specified (E.g - ffi.UintPtr or custom.MyStruct) final rawVarArgTypeSplit = rawBaseType.split('.'); diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/objcprotocoldecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/objcprotocoldecl_parser.dart index 20aec26e29..9ae430f8fe 100644 --- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/objcprotocoldecl_parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/objcprotocoldecl_parser.dart @@ -6,6 +6,7 @@ import '../../code_generator.dart'; import '../../config_provider/config_types.dart'; import '../../context.dart'; import '../clang_bindings/clang_bindings.dart' as clang_types; +import '../translation_unit_parser.dart'; import '../utils.dart'; import 'api_availability.dart'; import 'objcinterfacedecl_parser.dart'; @@ -73,15 +74,9 @@ CachableBinding? parseObjCProtocolDeclaration( cursor.visitChildren((child) { switch (child.kind) { case clang_types.CXCursorKind.CXCursor_ObjCProtocolRef: - final declCursor = clang.clang_getCursorDefinition(child); - logger.fine( - ' > Super protocol: ${declCursor.completeStringRepr()}', - ); - final superProtocol = parseObjCProtocolDeclaration( - context, - declCursor, - ); - if (superProtocol != null) { + logger.fine(' > Super protocol: ${child.completeStringRepr()}'); + final superProtocol = parseCursor(context, child); + if (superProtocol is ObjCProtocol) { protocol.superProtocols.add(superProtocol); } break; diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/typedefdecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/typedefdecl_parser.dart index 0c09bdefbb..6cb32e2163 100644 --- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/typedefdecl_parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/typedefdecl_parser.dart @@ -5,7 +5,9 @@ import '../../code_generator.dart'; import '../../config_provider/config_types.dart'; import '../../context.dart'; +import '../../strings.dart' as strings; import '../clang_bindings/clang_bindings.dart' as clang_types; +import '../type_extractor/cxtypekindmap.dart'; import '../type_extractor/extractor.dart'; import '../utils.dart'; @@ -25,10 +27,7 @@ import '../utils.dart'; /// /// typedef A D; // Typeref. /// ``` -Typealias parseTypedefDeclaration( - Context context, - clang_types.CXCursor cursor, -) { +Type parseTypedefDeclaration(Context context, clang_types.CXCursor cursor) { final logger = context.logger; final config = context.config; final name = cursor.spelling(); @@ -52,7 +51,7 @@ Typealias parseTypedefDeclaration( supportedTypedefToImportedType[name]; if (supportedTypedef != null) { logger.fine(' Type Mapped from supported typedef'); - return NativeType(supportedTypedef); + return supportedTypedef; } } diff --git a/pkgs/ffigen/lib/src/header_parser/type_extractor/cxtypekindmap.dart b/pkgs/ffigen/lib/src/header_parser/type_extractor/cxtypekindmap.dart index b29755de22..126b057576 100644 --- a/pkgs/ffigen/lib/src/header_parser/type_extractor/cxtypekindmap.dart +++ b/pkgs/ffigen/lib/src/header_parser/type_extractor/cxtypekindmap.dart @@ -5,6 +5,7 @@ import 'package:collection/collection.dart'; import '../../code_generator.dart' show SupportedNativeType, Type; import '../../code_generator/imports.dart'; +import '../../code_generator/native_type.dart'; Map cxTypeKindToImportedTypes = { 'void': voidType, @@ -45,17 +46,17 @@ Map signedToUnsignedNativeIntType = Map.fromEntries( ), ); -Map suportedTypedefToSuportedNativeType = { - 'uint8_t': SupportedNativeType.uint8, - 'uint16_t': SupportedNativeType.uint16, - 'uint32_t': SupportedNativeType.uint32, - 'uint64_t': SupportedNativeType.uint64, - 'int8_t': SupportedNativeType.int8, - 'int16_t': SupportedNativeType.int16, - 'int32_t': SupportedNativeType.int32, - 'int64_t': SupportedNativeType.int64, - 'intptr_t': SupportedNativeType.intPtr, - 'uintptr_t': SupportedNativeType.uintPtr, +Map suportedTypedefToSuportedNativeType = { + 'uint8_t': NativeType(SupportedNativeType.uint8), + 'uint16_t': NativeType(SupportedNativeType.uint16), + 'uint32_t': NativeType(SupportedNativeType.uint32), + 'uint64_t': NativeType(SupportedNativeType.uint64), + 'int8_t': NativeType(SupportedNativeType.int8), + 'int16_t': NativeType(SupportedNativeType.int16), + 'int32_t': NativeType(SupportedNativeType.int32), + 'int64_t': NativeType(SupportedNativeType.int64), + 'intptr_t': NativeType(SupportedNativeType.intPtr), + 'uintptr_t': NativeType(SupportedNativeType.uintPtr), }; Map supportedTypedefToImportedType = { From d29211d5e499c92357a9f565559b35a4b42c633b Mon Sep 17 00:00:00 2001 From: Liam Appelbe Date: Wed, 7 Jan 2026 11:58:46 +1100 Subject: [PATCH 16/19] fix analysis --- .../src/code_generator/bindings_index.dart | 2 +- pkgs/ffigen/lib/src/context.dart | 1 - .../sub_parsers/objccategorydecl_parser.dart | 1 - .../sub_parsers/objcinterfacedecl_parser.dart | 1 - .../sub_parsers/objcprotocoldecl_parser.dart | 1 - .../sub_parsers/typedefdecl_parser.dart | 27 +++++++++++-------- .../sub_parsers/unnamed_enumdecl_parser.dart | 2 -- .../translation_unit_parser.dart | 18 +++++-------- .../type_extractor/extractor.dart | 6 +---- 9 files changed, 25 insertions(+), 34 deletions(-) diff --git a/pkgs/ffigen/lib/src/code_generator/bindings_index.dart b/pkgs/ffigen/lib/src/code_generator/bindings_index.dart index 65a63e9337..1e216347a0 100644 --- a/pkgs/ffigen/lib/src/code_generator/bindings_index.dart +++ b/pkgs/ffigen/lib/src/code_generator/bindings_index.dart @@ -2,10 +2,10 @@ // 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 'binding.dart'; import '../header_parser/clang_bindings/clang_bindings.dart' as clang_types; import '../header_parser/utils.dart'; import '../visitor/ast.dart'; +import 'binding.dart'; class BindingsIndex { final _entries = {}; diff --git a/pkgs/ffigen/lib/src/context.dart b/pkgs/ffigen/lib/src/context.dart index 5f77944412..3ef462c5ae 100644 --- a/pkgs/ffigen/lib/src/context.dart +++ b/pkgs/ffigen/lib/src/context.dart @@ -7,7 +7,6 @@ import 'dart:ffi'; import 'package:logging/logging.dart'; import 'code_generator.dart'; -import 'code_generator/bindings_index.dart'; import 'code_generator/scope.dart'; import 'config_provider/config.dart'; import 'config_provider/config_types.dart'; diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/objccategorydecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/objccategorydecl_parser.dart index f5781674f0..8cc93b1051 100644 --- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/objccategorydecl_parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/objccategorydecl_parser.dart @@ -10,7 +10,6 @@ import '../translation_unit_parser.dart'; import '../utils.dart'; import 'api_availability.dart'; import 'objcinterfacedecl_parser.dart'; -import 'objcprotocoldecl_parser.dart'; CachableBinding? parseObjCCategoryDeclaration( Context context, diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/objcinterfacedecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/objcinterfacedecl_parser.dart index dd54525cf1..cd494155de 100644 --- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/objcinterfacedecl_parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/objcinterfacedecl_parser.dart @@ -10,7 +10,6 @@ import '../clang_bindings/clang_bindings.dart' as clang_types; import '../translation_unit_parser.dart'; import '../utils.dart'; import 'api_availability.dart'; -import 'objcprotocoldecl_parser.dart'; String applyModulePrefix(String name, String? module) => module == null ? name : '$module.$name'; diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/objcprotocoldecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/objcprotocoldecl_parser.dart index 9ae430f8fe..74f445a7d6 100644 --- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/objcprotocoldecl_parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/objcprotocoldecl_parser.dart @@ -17,7 +17,6 @@ CachableBinding? parseObjCProtocolDeclaration( ) { final logger = context.logger; final config = context.config; - final bindingsIndex = context.bindingsIndex; if (cursor.kind != clang_types.CXCursorKind.CXCursor_ObjCProtocolDecl) { return null; } diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/typedefdecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/typedefdecl_parser.dart index 6cb32e2163..8a4866996f 100644 --- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/typedefdecl_parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/typedefdecl_parser.dart @@ -27,7 +27,10 @@ import '../utils.dart'; /// /// typedef A D; // Typeref. /// ``` -Type parseTypedefDeclaration(Context context, clang_types.CXCursor cursor) { +CachableBinding parseTypedefDeclaration( + Context context, + clang_types.CXCursor cursor, +) { final logger = context.logger; final config = context.config; final name = cursor.spelling(); @@ -37,12 +40,12 @@ Type parseTypedefDeclaration(Context context, clang_types.CXCursor cursor) { // Objective C's BOOL type can be either bool or signed char, depending // on the platform. We want to present a consistent API to the user, and // those two types are ABI compatible, so just return bool regardless. - return BooleanType(); + return CachableBinding(BooleanType()); } if (config.typedefTypeMappings.containsKey(name)) { logger.fine(' Type $name mapped from type-map'); - return config.typedefTypeMappings[name]!; + return CachableBinding(config.typedefTypeMappings[name]!); } if (config.typedefs.useSupportedTypedefs) { @@ -51,7 +54,7 @@ Type parseTypedefDeclaration(Context context, clang_types.CXCursor cursor) { supportedTypedefToImportedType[name]; if (supportedTypedef != null) { logger.fine(' Type Mapped from supported typedef'); - return supportedTypedef; + return CachableBinding(supportedTypedef); } } @@ -84,13 +87,15 @@ Type parseTypedefDeclaration(Context context, clang_types.CXCursor cursor) { logger.fine("Skipped Typedef '$name': typedef to bool."); } else { // Create typealias. - return Typealias( - usr: usr, - originalName: name, - name: config.typedefs.rename(decl), - type: s, - dartDoc: getCursorDocComment(context, cursor), + return CachableBinding( + Typealias( + usr: usr, + originalName: name, + name: config.typedefs.rename(decl), + type: s, + dartDoc: getCursorDocComment(context, cursor), + ), ); } - return Typealias.anonymous(usr: usr, name: name, type: s); + return CachableBinding(Typealias.anonymous(usr: usr, name: name, type: s)); } diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/unnamed_enumdecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/unnamed_enumdecl_parser.dart index ce7a1d7a1d..9521662116 100644 --- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/unnamed_enumdecl_parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/unnamed_enumdecl_parser.dart @@ -47,8 +47,6 @@ Constant? _addUnNamedEnumConstant( ) { final logger = context.logger; final config = context.config; - final bindingsIndex = context.bindingsIndex; - final usr = cursor.usr(); final unnamedEnumConstants = context.unnamedEnumConstants; 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 44ea0c4d6b..e7380910e9 100644 --- a/pkgs/ffigen/lib/src/header_parser/translation_unit_parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/translation_unit_parser.dart @@ -4,13 +4,17 @@ import '../code_generator.dart'; import '../context.dart'; +import '../visitor/ast.dart'; import 'clang_bindings/clang_bindings.dart' as clang_types; +import 'sub_parsers/compounddecl_parser.dart'; +import 'sub_parsers/enumdecl_parser.dart'; import 'sub_parsers/functiondecl_parser.dart'; import 'sub_parsers/macro_parser.dart'; import 'sub_parsers/objccategorydecl_parser.dart'; +import 'sub_parsers/objcinterfacedecl_parser.dart'; import 'sub_parsers/objcprotocoldecl_parser.dart'; +import 'sub_parsers/typedefdecl_parser.dart'; import 'sub_parsers/var_parser.dart'; -import 'type_extractor/extractor.dart'; import 'utils.dart'; /// Parses the translation units and adds all the bindings to the context's @@ -44,7 +48,7 @@ void _parseTranslationUnit( }); } -Binding? parseCursor(Context context, clang_types.CXCursor cursor) => +AstNode? parseCursor(Context context, clang_types.CXCursor cursor) => context.bindingsIndex.cache(cursor, (def) => _parseCursor(context, def)); CachableBinding? _parseCursor(Context context, clang_types.CXCursor cursor) { @@ -67,7 +71,7 @@ CachableBinding? _parseCursor(Context context, clang_types.CXCursor cursor) { case clang_types.CXCursorKind.CXCursor_ObjCInterfaceDecl: return parseObjCInterfaceDeclaration(context, cursor); case clang_types.CXCursorKind.CXCursor_TypedefDecl: - return _getCodeGenTypeFromCursor(context, cursor); + return parseTypedefDeclaration(context, cursor); case clang_types.CXCursorKind.CXCursor_ObjCCategoryDecl: return parseObjCCategoryDeclaration(context, cursor); case clang_types.CXCursorKind.CXCursor_ObjCProtocolDecl: @@ -88,14 +92,6 @@ CachableBinding? _parseCursor(Context context, clang_types.CXCursor cursor) { } } -BindingType? _getCodeGenTypeFromCursor( - Context context, - clang_types.CXCursor cursor, -) { - final t = getCodeGenType(context, cursor.type()); - return t is BindingType ? t : null; -} - /// Visits all cursors and builds a map of usr and [clang_types.CXCursor]. void buildUsrCursorDefinitionMap( Context context, 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 ef5f3a2256..28758f6712 100644 --- a/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart +++ b/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart @@ -9,13 +9,9 @@ import '../../code_generator.dart'; import '../../context.dart'; import '../../strings.dart' as strings; import '../clang_bindings/clang_bindings.dart' as clang_types; -import '../sub_parsers/compounddecl_parser.dart'; -import '../sub_parsers/enumdecl_parser.dart'; import '../sub_parsers/function_type_param_parser.dart'; import '../sub_parsers/objc_block_parser.dart'; -import '../sub_parsers/objcinterfacedecl_parser.dart'; -import '../sub_parsers/objcprotocoldecl_parser.dart'; -import '../sub_parsers/typedefdecl_parser.dart'; +import '../translation_unit_parser.dart'; import '../type_extractor/cxtypekindmap.dart'; import '../utils.dart'; From 61f8a66e10c1020526831f74c02c878f90e29fbf Mon Sep 17 00:00:00 2001 From: Liam Appelbe Date: Wed, 7 Jan 2026 13:41:16 +1100 Subject: [PATCH 17/19] fix assert fail --- .../src/code_generator/bindings_index.dart | 5 +- .../clang_bindings/clang_bindings.dart | 17 +-- .../sub_parsers/compounddecl_parser.dart | 3 - .../sub_parsers/enumdecl_parser.dart | 1 - pkgs/ffigen/lib/src/header_parser/utils.dart | 116 ------------------ pkgs/ffigen/tool/libclang_config.yaml | 1 - 6 files changed, 6 insertions(+), 137 deletions(-) diff --git a/pkgs/ffigen/lib/src/code_generator/bindings_index.dart b/pkgs/ffigen/lib/src/code_generator/bindings_index.dart index 1e216347a0..548864027f 100644 --- a/pkgs/ffigen/lib/src/code_generator/bindings_index.dart +++ b/pkgs/ffigen/lib/src/code_generator/bindings_index.dart @@ -11,10 +11,9 @@ class BindingsIndex { final _entries = {}; void addDefinition(clang_types.CXCursor cursor) { - if (!cursor.isDefinition) { - cursor = cursor.definition; - } if (cursor.isNull) return; + final definition = cursor.definition; + if (!definition.isNull) cursor = definition; final usr = cursor.usr(); if (usr.isEmpty) return; _entries[usr] ??= IndexEntry(definition: cursor); 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 5103c92876..23e96a7f7f 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 @@ -1156,19 +1156,6 @@ class Clang { late final _clang_getCursorDefinition = _clang_getCursorDefinitionPtr .asFunction(); - /// Determine whether the declaration pointed to by this cursor - /// is also a definition of that entity. - int clang_isCursorDefinition(CXCursor arg0) { - return _clang_isCursorDefinition(arg0); - } - - late final _clang_isCursorDefinitionPtr = - _lookup>( - 'clang_isCursorDefinition', - ); - late final _clang_isCursorDefinition = _clang_isCursorDefinitionPtr - .asFunction(); - /// Given a cursor that represents a property declaration, return the /// associated property attributes. The bits are formed from /// \c CXObjCPropertyAttrKind. @@ -1394,6 +1381,10 @@ class Clang { .asFunction(); } +typedef __time_t = ffi.Long; +typedef Dart__time_t = int; +typedef time_t = __time_t; + /// A character string. /// /// The \c CXString type is used to return strings from the interface when 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 7e4ff5c6a3..5c0815b0e7 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 @@ -114,8 +114,6 @@ CachableBinding? _parseCompoundDeclaration( }) constructor, ) { - assert(cursor.isDefinition); - final mappedType = configTypeMappings[cursor.spelling()]; if (mappedType != null) { context.logger.fine(' Type Mapped from type-map: ${cursor.spelling()}'); @@ -188,7 +186,6 @@ void fillCompoundMembersIfNeeded( if (compound.parsedDependencies) return; final logger = context.logger; - assert(cursor.isDefinition); final parsed = _ParsedCompound(context, compound); final className = compound is Struct ? 'Struct' : 'Union'; parsed.hasAttr = clang.clang_Cursor_hasAttrs(cursor) != 0; diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/enumdecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/enumdecl_parser.dart index fb959ea442..febe428170 100644 --- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/enumdecl_parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/enumdecl_parser.dart @@ -20,7 +20,6 @@ CachableBinding? parseEnumDeclaration( final config = context.config; final logger = context.logger; EnumClass? enumClass; - assert(cursor.isDefinition); final usr = cursor.usr(); diff --git a/pkgs/ffigen/lib/src/header_parser/utils.dart b/pkgs/ffigen/lib/src/header_parser/utils.dart index 690c9443c0..4f6a18a804 100644 --- a/pkgs/ffigen/lib/src/header_parser/utils.dart +++ b/pkgs/ffigen/lib/src/header_parser/utils.dart @@ -89,7 +89,6 @@ extension CXSourceRangePtrExt on Pointer { extension CXCursorExt on clang_types.CXCursor { bool get isNull => clang.clang_Cursor_isNull(this) != 0; - bool get isDefinition => clang.clang_isCursorDefinition(this) != 0; clang_types.CXCursor get definition => clang.clang_getCursorDefinition(this); String usr() { @@ -486,121 +485,6 @@ class Macro { Macro(this.usr, this.originalName); } -/// Tracks if a binding is 'seen' or not. -/*class BindingsIndex { - // Tracks if bindings are already seen, Map key is USR obtained from libclang. - final Map _functions = {}; - final Map _unnamedEnumConstants = {}; - final Map _macros = {}; - final Map _globals = {}; - final Map _variableConstants = {}; - final Map _typealiases = {}; - final Map _enums = {}; - final Map _compounds = {}; - final Map _objcBlocks = {}; - final Map _objcInterfaces = {}; - final Map _objcProtocols = {}; - final Map _objcCategories = {}; - - /// Contains usr for typedefs which cannot be generated. - final Set _unsupportedTypealiases = {}; - - bool isSeenType(String usr) => _declaredTypes.containsKey(usr); - void addTypeToSeen(String usr, Type type) => _declaredTypes[usr] = type; - Type? getSeenType(String usr) => _declaredTypes[usr]; - bool isSeenFunc(String usr) => _functions.containsKey(usr); - void addFuncToSeen(String usr, Func func) => _functions[usr] = func; - Func? getSeenFunc(String usr) => _functions[usr]; - void addUnnamedEnumConstantToSeen(String usr, Constant enumConstant) => - _unnamedEnumConstants[usr] = enumConstant; - Constant? getSeenUnnamedEnumConstant(String usr) => - _unnamedEnumConstants[usr]; - bool isSeenGlobalVar(String usr) => _globals.containsKey(usr); - void addGlobalVarToSeen(String usr, Global global) => _globals[usr] = global; - Global? getSeenGlobalVar(String usr) => _globals[usr]; - bool isSeenVariableConstant(String usr) => - _variableConstants.containsKey(usr); - void addVariableConstantToSeen(String usr, Constant constant) => - _variableConstants[usr] = constant; - Constant? getSeenVariableConstant(String usr) => _variableConstants[usr]; - bool isSeenTypealias(String usr) => _typealiases.containsKey(usr); - void addTypealiasToSeen(String usr, Typealias t) => _typealiases[usr] = t; - Typealias? getSeenTypealias(String usr) => _typealiases[usr]; - bool isSeenEnum(String usr) => _enums.containsKey(usr); - void addEnumToSeen(String usr, EnumClass t) => _enums[usr] = t; - EnumClass? getSeenEnum(String usr) => _enums[usr]; - bool isSeenCompound(String usr) => _compounds.containsKey(usr); - void addCompoundToSeen(String usr, Compound t) => _compounds[usr] = t; - Compound? getSeenCompound(String usr) => _compounds[usr]; - bool isSeenMacro(String usr) => _macros.containsKey(usr); - void addMacroToSeen(String usr, String macro) => _macros[usr] = macro; - bool isSeenUnsupportedTypealias(String usr) => - _unsupportedTypealiases.contains(usr); - void addUnsupportedTypealiasToSeen(String usr) => - _unsupportedTypealiases.add(usr); - void addObjCBlockToSeen(String key, ObjCBlock t) => _objcBlocks[key] = t; - ObjCBlock? getSeenObjCBlock(String key) => _objcBlocks[key]; - void addObjCInterfaceToSeen(String usr, ObjCInterface t) => - _objcInterfaces[usr] = t; - ObjCInterface? getSeenObjCInterface(String usr) => _objcInterfaces[usr]; - bool isSeenObjCInterface(String usr) => _objcInterfaces.containsKey(usr); - void addObjCProtocolToSeen(String usr, ObjCProtocol t) => - _objcProtocols[usr] = t; - ObjCProtocol? getSeenObjCProtocol(String usr) => _objcProtocols[usr]; - bool isSeenObjCProtocol(String usr) => _objcProtocols.containsKey(usr); - void addObjCCategoryToSeen(String usr, ObjCCategory t) => - _objcCategories[usr] = t; - ObjCCategory? getSeenObjCCategory(String usr) => _objcCategories[usr]; - bool isSeenObjCCategory(String usr) => _objcCategories.containsKey(usr); -} - -class CursorIndex { - final Logger _logger; - final _usrCursorDefinition = {}; - - CursorIndex(this._logger); - - /// Returns the Cursor definition (if found) or itself. - clang_types.CXCursor getDefinition(clang_types.CXCursor cursor) { - final cursorDefinition = clang.clang_getCursorDefinition(cursor); - if (clang.clang_Cursor_isNull(cursorDefinition) == 0) { - return cursorDefinition; - } else { - final usr = cursor.usr(); - if (_usrCursorDefinition.containsKey(usr)) { - return _usrCursorDefinition[cursor.usr()]!; - } else { - _logger.warning( - 'No definition found for declaration -' - '${cursor.completeStringRepr()}', - ); - return cursor; - } - } - } - - /// Saves cursor definition based on its kind. - void saveDefinition(clang_types.CXCursor cursor) { - switch (cursor.kind) { - case clang_types.CXCursorKind.CXCursor_StructDecl: - case clang_types.CXCursorKind.CXCursor_UnionDecl: - case clang_types.CXCursorKind.CXCursor_EnumDecl: - final usr = cursor.usr(); - if (!_usrCursorDefinition.containsKey(usr)) { - final cursorDefinition = clang.clang_getCursorDefinition(cursor); - if (clang.clang_Cursor_isNull(cursorDefinition) == 0) { - _usrCursorDefinition[usr] = cursorDefinition; - } else { - _logger.finest( - 'Missing cursor definition in current translation unit: ' - '${cursor.completeStringRepr()}', - ); - } - } - } - } -}*/ - /// Converts a double to a string, handling cases like Infinity and NaN. String writeDoubleAsString(double d) { if (d.isFinite) { diff --git a/pkgs/ffigen/tool/libclang_config.yaml b/pkgs/ffigen/tool/libclang_config.yaml index 0221ec9817..e5d7ebc2f6 100644 --- a/pkgs/ffigen/tool/libclang_config.yaml +++ b/pkgs/ffigen/tool/libclang_config.yaml @@ -120,7 +120,6 @@ functions: - clang_getFieldDeclBitWidth - clang_Cursor_isFunctionInlined - clang_getCursorDefinition - - clang_isCursorDefinition - clang_getCursorAvailability - clang_getCursorPlatformAvailability - clang_disposeCXPlatformAvailability From 727c6d305e234b8783434262d8da91dc635b6772 Mon Sep 17 00:00:00 2001 From: Liam Appelbe Date: Wed, 7 Jan 2026 14:08:23 +1100 Subject: [PATCH 18/19] fix --- pkgs/ffigen/lib/src/code_generator/bindings_index.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkgs/ffigen/lib/src/code_generator/bindings_index.dart b/pkgs/ffigen/lib/src/code_generator/bindings_index.dart index 548864027f..d64b31b99d 100644 --- a/pkgs/ffigen/lib/src/code_generator/bindings_index.dart +++ b/pkgs/ffigen/lib/src/code_generator/bindings_index.dart @@ -48,7 +48,7 @@ class BindingsIndex { IndexEntry? operator [](String usr) => _entries[usr]; IndexEntry getOrInsert(String usr) { assert(usr.isNotEmpty); - return _entries[usr] ?? IndexEntry(); + return _entries[usr] ??= IndexEntry(); } Set get bindings => @@ -60,6 +60,7 @@ class IndexEntry { bool filled = false; AstNode? node; IndexEntry({this.definition}); + String toString() => '$node'; } // Some bindings need to split intial creation from filling, to avoid cycles. From e0b3d08e0f36111278606c0ab6be8b39bd0f36b5 Mon Sep 17 00:00:00 2001 From: Liam Appelbe Date: Wed, 7 Jan 2026 15:19:24 +1100 Subject: [PATCH 19/19] fix --- .../lib/src/code_generator/bindings_index.dart | 9 ++++++++- .../clang_bindings/clang_bindings.dart | 17 +++++++++++++---- pkgs/ffigen/lib/src/header_parser/utils.dart | 1 + pkgs/ffigen/tool/libclang_config.yaml | 1 + 4 files changed, 23 insertions(+), 5 deletions(-) diff --git a/pkgs/ffigen/lib/src/code_generator/bindings_index.dart b/pkgs/ffigen/lib/src/code_generator/bindings_index.dart index d64b31b99d..e41b8bd5c5 100644 --- a/pkgs/ffigen/lib/src/code_generator/bindings_index.dart +++ b/pkgs/ffigen/lib/src/code_generator/bindings_index.dart @@ -16,7 +16,12 @@ class BindingsIndex { if (!definition.isNull) cursor = definition; final usr = cursor.usr(); if (usr.isEmpty) return; - _entries[usr] ??= IndexEntry(definition: cursor); + final existingEntry = _entries[usr]; + if (existingEntry == null) { + _entries[usr] = IndexEntry(definition: cursor); + } else if (!(existingEntry.definition?.isDefinition ?? false)) { + existingEntry.definition = cursor; + } } AstNode? cache( @@ -60,6 +65,8 @@ class IndexEntry { bool filled = false; AstNode? node; IndexEntry({this.definition}); + + @override String toString() => '$node'; } 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 23e96a7f7f..5103c92876 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 @@ -1156,6 +1156,19 @@ class Clang { late final _clang_getCursorDefinition = _clang_getCursorDefinitionPtr .asFunction(); + /// Determine whether the declaration pointed to by this cursor + /// is also a definition of that entity. + int clang_isCursorDefinition(CXCursor arg0) { + return _clang_isCursorDefinition(arg0); + } + + late final _clang_isCursorDefinitionPtr = + _lookup>( + 'clang_isCursorDefinition', + ); + late final _clang_isCursorDefinition = _clang_isCursorDefinitionPtr + .asFunction(); + /// Given a cursor that represents a property declaration, return the /// associated property attributes. The bits are formed from /// \c CXObjCPropertyAttrKind. @@ -1381,10 +1394,6 @@ class Clang { .asFunction(); } -typedef __time_t = ffi.Long; -typedef Dart__time_t = int; -typedef time_t = __time_t; - /// A character string. /// /// The \c CXString type is used to return strings from the interface when diff --git a/pkgs/ffigen/lib/src/header_parser/utils.dart b/pkgs/ffigen/lib/src/header_parser/utils.dart index 4f6a18a804..b313f1fbc1 100644 --- a/pkgs/ffigen/lib/src/header_parser/utils.dart +++ b/pkgs/ffigen/lib/src/header_parser/utils.dart @@ -89,6 +89,7 @@ extension CXSourceRangePtrExt on Pointer { extension CXCursorExt on clang_types.CXCursor { bool get isNull => clang.clang_Cursor_isNull(this) != 0; + bool get isDefinition => clang.clang_isCursorDefinition(this) != 0; clang_types.CXCursor get definition => clang.clang_getCursorDefinition(this); String usr() { diff --git a/pkgs/ffigen/tool/libclang_config.yaml b/pkgs/ffigen/tool/libclang_config.yaml index e5d7ebc2f6..0221ec9817 100644 --- a/pkgs/ffigen/tool/libclang_config.yaml +++ b/pkgs/ffigen/tool/libclang_config.yaml @@ -120,6 +120,7 @@ functions: - clang_getFieldDeclBitWidth - clang_Cursor_isFunctionInlined - clang_getCursorDefinition + - clang_isCursorDefinition - clang_getCursorAvailability - clang_getCursorPlatformAvailability - clang_disposeCXPlatformAvailability