diff --git a/pkgs/ffigen/CHANGELOG.md b/pkgs/ffigen/CHANGELOG.md index 1dd7776cb0..3a6adc7f6f 100644 --- a/pkgs/ffigen/CHANGELOG.md +++ b/pkgs/ffigen/CHANGELOG.md @@ -13,10 +13,9 @@ - __Breaking change__: Dart const values will be generated for global variables marked const in C (e.g. static const int) instead of symbol lookups. This supports integers, doubles, and string literals. Including the variable name - in the globals -> symbol-address configuration will still generate symbol + in the globals -> symbol-address configuration will still generate symbol lookups. - ## 20.1.1 - Update tests and examples now that package:objective_c is using native assets. 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 new file mode 100644 index 0000000000..e41b8bd5c5 --- /dev/null +++ b/pkgs/ffigen/lib/src/code_generator/bindings_index.dart @@ -0,0 +1,81 @@ +// 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. + +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 = {}; + + void addDefinition(clang_types.CXCursor cursor) { + if (cursor.isNull) return; + final definition = cursor.definition; + if (!definition.isNull) cursor = definition; + final usr = cursor.usr(); + if (usr.isEmpty) return; + final existingEntry = _entries[usr]; + if (existingEntry == null) { + _entries[usr] = IndexEntry(definition: cursor); + } else if (!(existingEntry.definition?.isDefinition ?? false)) { + existingEntry.definition = cursor; + } + } + + AstNode? cache( + clang_types.CXCursor cursor, + CachableBinding? Function(clang_types.CXCursor cursor) builder, + ) { + final usr = cursor.usr(); + if (usr.isEmpty) return null; + final entry = getOrInsert(usr); + if (!entry.filled) { + final cachable = builder(entry.definition ?? cursor); + entry.filled = true; + if (cachable != null) { + entry.node = cachable.node; + // Note: Filler may re-enter this cache method. + cachable.filler(); + } + } + return entry.node; + } + + void fillBinding(Binding binding) { + final entry = getOrInsert(binding.usr); + assert(!entry.filled); + entry.node = binding; + entry.filled = true; + } + + IndexEntry? operator [](String usr) => _entries[usr]; + IndexEntry getOrInsert(String usr) { + assert(usr.isNotEmpty); + return _entries[usr] ??= IndexEntry(); + } + + Set get bindings => + _entries.values.map((e) => e.node).whereType().toSet(); +} + +class IndexEntry { + clang_types.CXCursor? definition; + bool filled = false; + AstNode? node; + IndexEntry({this.definition}); + + @override + String toString() => '$node'; +} + +// 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 { + AstNode node; + void Function() filler; + 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 53697c3013..94a2944c52 100644 --- a/pkgs/ffigen/lib/src/code_generator/objc_block.dart +++ b/pkgs/ffigen/lib/src/code_generator/objc_block.dart @@ -41,22 +41,15 @@ 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).node ??= 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/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/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/context.dart b/pkgs/ffigen/lib/src/context.dart index 4b2eae002a..3ef462c5ae 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 = []; @@ -33,8 +32,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/parser.dart b/pkgs/ffigen/lib/src/header_parser/parser.dart index f04c3d01ae..44a2e8d66a 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.headers.entryPoints}'); @@ -138,9 +135,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 +144,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/sub_parsers/compounddecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/compounddecl_parser.dart index b9b79b3ce5..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 @@ -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,17 +19,21 @@ Compound? parseStructDeclaration( context, 'Struct', context.config.structs, + context.config.structTypeMappings, Struct.new, ); -Compound? parseUnionDeclaration(clang_types.CXCursor cursor, Context context) => - _parseCompoundDeclaration( - cursor, - context, - 'Union', - context.config.unions, - 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 { @@ -94,11 +98,12 @@ class _ParsedCompound { } /// Parses a compound declaration. -Compound? _parseCompoundDeclaration( +CachableBinding? _parseCompoundDeclaration( clang_types.CXCursor cursor, Context context, String className, Declarations configDecl, + Map configTypeMappings, Compound Function({ String? usr, String? originalName, @@ -109,12 +114,13 @@ Compound? _parseCompoundDeclaration( }) constructor, ) { - // Parse the cursor definition instead, if this is a forward declaration. - final usr = cursor.usr(); - - final cachedCompound = context.bindingsIndex.getSeenCompound(usr); - if (cachedCompound != null) return cachedCompound; + final mappedType = configTypeMappings[cursor.spelling()]; + if (mappedType != null) { + context.logger.fine(' Type Mapped from type-map: ${cursor.spelling()}'); + return CachableBinding(mappedType); + } + final usr = cursor.usr(); final String declName; // Only set name using USR if the type is not Anonymous (A struct is anonymous @@ -136,9 +142,8 @@ Compound? _parseCompoundDeclaration( } final decl = Declaration(usr: usr, originalName: declName); - final Compound compound; + Compound compound; if (declName.isEmpty) { - cursor = context.cursorIndex.getDefinition(cursor); compound = constructor( name: 'Unnamed$className', usr: usr, @@ -151,7 +156,6 @@ Compound? _parseCompoundDeclaration( nativeType: cursor.type().spelling(), ); } else { - cursor = context.cursorIndex.getDefinition(cursor); context.logger.fine( '++++ Adding $className: Name: $declName, ${cursor.completeStringRepr()}', ); @@ -168,8 +172,10 @@ Compound? _parseCompoundDeclaration( nativeType: cursor.type().spelling(), ); } - context.bindingsIndex.addCompoundToSeen(usr, compound); - return compound; + return CachableBinding( + compound, + () => fillCompoundMembersIfNeeded(compound, cursor, context), + ); } void fillCompoundMembersIfNeeded( @@ -180,8 +186,6 @@ void fillCompoundMembersIfNeeded( if (compound.parsedDependencies) return; final logger = context.logger; - cursor = context.cursorIndex.getDefinition(cursor); - 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 68ee9bd690..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 @@ -13,18 +13,16 @@ import 'api_availability.dart'; import 'unnamed_enumdecl_parser.dart'; /// Parses an enum declaration. -EnumClass parseEnumDeclaration(clang_types.CXCursor cursor, Context context) { +CachableBinding? parseEnumDeclaration( + clang_types.CXCursor cursor, + Context context, +) { 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); final usr = cursor.usr(); - final cachedEnum = context.bindingsIndex.getSeenEnum(usr); - if (cachedEnum != null) return cachedEnum; - 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). @@ -108,7 +106,6 @@ EnumClass parseEnumDeclaration(clang_types.CXCursor cursor, Context context) { }); final suggestedStyle = isNSOptions ? EnumStyle.intConstants : null; enumClass.style = config.enums.style(decl, suggestedStyle); - context.bindingsIndex.addEnumToSeen(usr, enumClass); } if (hasNegativeEnumConstants) { @@ -121,12 +118,14 @@ EnumClass parseEnumDeclaration(clang_types.CXCursor cursor, Context context) { enumClass?.nativeType = nativeType; } - return enumClass ?? - EnumClass( - usr: usr, - name: enumName, - nativeType: nativeType, - context: context, - isAnonymous: true, - ); + 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/functiondecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/functiondecl_parser.dart index 895eb1f44d..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 @@ -12,155 +12,137 @@ import '../utils.dart'; import 'api_availability.dart'; /// Parses a function declaration. -List parseFunctionDeclaration( - Context context, - clang_types.CXCursor cursor, -) { +void 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; } 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 = [null]; - 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) { - var usr = funcUsr; - if (vaFunc != null) usr += '$synthUsrChar vaFunc: ${vaFunc.postfix}'; - funcs.add( - Func( - dartDoc: getCursorDocComment( - context, - cursor, - indent: nesting.length + commentPrefix.length, - availability: apiAvailability.dartDoc, - ), - usr: usr, - name: config.functions.rename(decl) + (vaFunc?.postfix ?? ''), - originalName: funcName, - returnType: returnType, - parameters: parameters, - varArgParameters: [ - for (final ta in vaFunc?.types ?? const []) - 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.output.style is NativeExternalBindings, - ), + 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.", + ); + return; + } + + 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.', + ); + return; + } + + 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.', + ); + return; + } + + // 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 = [null]; + 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); } - return funcs; + for (final vaFunc in varArgFunctions) { + var usr = funcUsr; + if (vaFunc != null) usr += '$synthUsrChar vaFunc: ${vaFunc.postfix}'; + context.bindingsIndex.fillBinding( + Func( + dartDoc: getCursorDocComment( + context, + cursor, + indent: nesting.length + commentPrefix.length, + availability: apiAvailability.dartDoc, + ), + usr: usr, + name: config.functions.rename(decl) + (vaFunc?.postfix ?? ''), + originalName: funcName, + returnType: returnType, + parameters: parameters, + varArgParameters: [ + for (final ta in vaFunc?.types ?? const []) + 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.output.style is NativeExternalBindings, + ), + ); + } } 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 5a681f5236..60d4f89dc1 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 @@ -18,11 +18,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 && @@ -33,7 +29,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 73bfec155b..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 @@ -6,12 +6,12 @@ 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 +26,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,41 +67,40 @@ ObjCCategory? parseObjCCategoryDeclaration( ), context: context, ); + parentInterface.categories.add(category); - context.bindingsIndex.addObjCCategoryToSeen(usr, category); + 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; + } + }); - 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; - } + logger.fine( + '++++ Finished ObjC category: ' + 'Name: $name, ${cursor.completeStringRepr()}', + ); }); - - logger.fine( - '++++ Finished ObjC category: ' - 'Name: $name, ${cursor.completeStringRepr()}', - ); - - parentInterface.categories.add(category); - return category; } 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..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 @@ -7,22 +7,19 @@ 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'; String applyModulePrefix(String name, String? module) => module == null ? name : '$module.$name'; -Type? parseObjCInterfaceDeclaration( +CachableBinding? parseObjCInterfaceDeclaration( Context context, clang_types.CXCursor cursor, ) { 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,8 +49,10 @@ Type? parseObjCInterfaceDeclaration( ), apiAvailability: apiAvailability, ); - context.bindingsIndex.addObjCInterfaceToSeen(usr, itf); - return itf; + return CachableBinding( + itf, + () => fillObjCInterfaceMethodsIfNeeded(context, itf, cursor), + ); } void fillObjCInterfaceMethodsIfNeeded( @@ -84,8 +83,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/objcprotocoldecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/objcprotocoldecl_parser.dart index 469ffc7f42..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 @@ -6,17 +6,17 @@ 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'; -ObjCProtocol? parseObjCProtocolDeclaration( +CachableBinding? parseObjCProtocolDeclaration( Context context, clang_types.CXCursor cursor, ) { final logger = context.logger; final config = context.config; - final bindingsIndex = context.bindingsIndex; if (cursor.kind != clang_types.CXCursorKind.CXCursor_ObjCProtocolDecl) { return null; } @@ -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,33 @@ 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: + logger.fine(' > Super protocol: ${child.completeStringRepr()}'); + final superProtocol = parseCursor(context, child); + if (superProtocol is ObjCProtocol) { + 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 5b6c40693b..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 @@ -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,65 +27,75 @@ import '../utils.dart'; /// /// typedef A D; // Typeref. /// ``` -Typealias parseTypedefDeclaration( +CachableBinding parseTypedefDeclaration( Context context, clang_types.CXCursor cursor, ) { final logger = context.logger; final config = context.config; - final bindingsIndex = context.bindingsIndex; final name = cursor.spelling(); final usr = cursor.usr(); - final cachedType = bindingsIndex.getSeenTypealias(usr); - if (cachedType != null) return cachedType; + 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 CachableBinding(BooleanType()); + } + + if (config.typedefTypeMappings.containsKey(name)) { + logger.fine(' Type $name mapped from type-map'); + return CachableBinding(config.typedefTypeMappings[name]!); + } + + if (config.typedefs.useSupportedTypedefs) { + final supportedTypedef = + suportedTypedefToSuportedNativeType[name] ?? + supportedTypedefToImportedType[name]; + if (supportedTypedef != null) { + logger.fine(' Type Mapped from supported typedef'); + return CachableBinding(supportedTypedef); + } + } final decl = Declaration(usr: usr, originalName: name); final ct = clang.clang_getTypedefDeclUnderlyingType(cursor); final s = getCodeGenType(context, ct, 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( - 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), + ), ); - bindingsIndex.addTypealiasToSeen(usr, type); - return type; } - 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 d608b01294..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,13 +47,7 @@ Constant? _addUnNamedEnumConstant( ) { final logger = context.logger; final config = context.config; - 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 +69,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..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,21 +10,16 @@ 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; - 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,8 +72,7 @@ Binding? parseVarDeclaration(Context context, clang_types.CXCursor cursor) { logger.fine( '++++ Adding Constant from Global: ${cursor.completeStringRepr()}', ); - bindingsIndex.addVariableConstantToSeen(usr, constant); - return constant; + return CachableBinding(constant); } } @@ -109,7 +103,6 @@ Binding? parseVarDeclaration(Context context, clang_types.CXCursor cursor) { constant: cType.isConstQualified, loadFromNativeAsset: nativeOutputStyle, ); - bindingsIndex.addGlobalVarToSeen(usr, global); - 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 ce3eeac655..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,91 +4,92 @@ 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 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, + Iterable translationUnitCursors, ) { - final bindings = {}; - final logger = context.logger; final headers = {}; + for (final translationUnitCursor in translationUnitCursors) { + _parseTranslationUnit(context, translationUnitCursor, headers); + } +} +void _parseTranslationUnit( + Context context, + clang_types.CXCursor translationUnitCursor, + Map headers, +) { + final logger = context.logger; translationUnitCursor.visitChildren((cursor) { final file = cursor.sourceFileName(); if (file.isEmpty) return; if (headers[file] ??= context.config.headers.include(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'); - } - } catch (e, s) { - logger.severe(e); - logger.severe(s); - rethrow; - } + parseCursor(context, cursor); } else { logger.finest( 'rootCursorVisitor:(not included) ${cursor.completeStringRepr()}', ); } }); - - 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); - } -} +AstNode? parseCursor(Context context, clang_types.CXCursor cursor) => + context.bindingsIndex.cache(cursor, (def) => _parseCursor(context, def)); -BindingType? _getCodeGenTypeFromCursor( - Context context, - clang_types.CXCursor cursor, -) { - final t = getCodeGenType(context, cursor.type()); - return t is BindingType ? t : null; +CachableBinding? _parseCursor(Context context, clang_types.CXCursor cursor) { + final logger = context.logger; + logger.finest('rootCursorVisitor: ${cursor.completeStringRepr()}'); + try { + switch (clang.clang_getCursorKind(cursor)) { + case clang_types.CXCursorKind.CXCursor_FunctionDecl: + // Due to variadic functions, we may get multiple bindings from a single + // cursor, each with different USRs. So parseFunctionDeclaration is + // responsible for filling its own index entries. + 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 parseTypedefDeclaration(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; + } } /// Visits all cursors and builds a map of usr and [clang_types.CXCursor]. @@ -99,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/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 = { 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 484b38eca0..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'; @@ -57,8 +53,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); @@ -80,12 +76,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); - if (type == null) { - return UnimplementedType('${cxtype.kindSpelling()} not implemented'); - } - _fillFromCursorIfNeeded(context, type, cursor); - return type; + return _createTypeFromCursor(context, cursor); } // If the type doesn't have a declaration cursor, then it's a basic type such @@ -163,112 +154,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]!; - } - 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); - - 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; - } - case clang_types.CXTypeKind.CXType_Record: - return _extractfromRecord(context, cxtype, cursor); - case clang_types.CXTypeKind.CXType_Enum: - final enumClass = parseEnumDeclaration(cursor, context); - if (enumClass.isAnonymous) { - return enumClass.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, - 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); + final importedType = context.config.importedTypesByUsr[usr]; + if (importedType != null) { + context.logger.fine(' Type $usr mapped from usr'); + return importedType; } -} -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]!; + 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 parseUnionDeclaration(cursor, context); + return type; } - - logger.fine( - 'typedeclarationCursorVisitor: _extractfromRecord: ' - 'Not Implemented, ${cursor.completeStringRepr()}', - ); - return UnimplementedType('${cxtype.kindSpelling()} not implemented'); + return UnimplementedType('Unknown type: ${cursor.completeStringRepr()}'); } // Used for function pointer arguments. diff --git a/pkgs/ffigen/lib/src/header_parser/utils.dart b/pkgs/ffigen/lib/src/header_parser/utils.dart index 781b912030..b313f1fbc1 100644 --- a/pkgs/ffigen/lib/src/header_parser/utils.dart +++ b/pkgs/ffigen/lib/src/header_parser/utils.dart @@ -486,118 +486,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 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) {