diff --git a/pkgs/ffigen/lib/ffigen.dart b/pkgs/ffigen/lib/ffigen.dart index da396ee043..2422b6fd81 100644 --- a/pkgs/ffigen/lib/ffigen.dart +++ b/pkgs/ffigen/lib/ffigen.dart @@ -55,3 +55,5 @@ export 'src/config_provider.dart' macSdkUri, xcodePath, xcodeUri; +export 'src/public_ast.dart'; +export 'src/public_visitor.dart'; diff --git a/pkgs/ffigen/lib/src/code_generator/binding.dart b/pkgs/ffigen/lib/src/code_generator/binding.dart index 5b52c350b1..8669e44266 100644 --- a/pkgs/ffigen/lib/src/code_generator/binding.dart +++ b/pkgs/ffigen/lib/src/code_generator/binding.dart @@ -3,6 +3,7 @@ // BSD-style license that can be found in the LICENSE file. import '../config_provider/config_types.dart' show Declaration; +import '../public_ast.dart' as public_ast; import '../visitor/ast.dart'; import 'binding_string.dart'; import 'scope.dart'; @@ -61,6 +62,9 @@ abstract class Binding extends AstNode implements Declaration { /// Returns the Cpp glue code for this binding, if any. String? toCppBindingString(Writer w) => null; + /// Returns the public AST node wrapper for this binding, or null. + public_ast.AstNode? toPublicAstNode() => null; + @override void visit(Visitation visitation) => visitation.visitBinding(this); diff --git a/pkgs/ffigen/lib/src/code_generator/compound.dart b/pkgs/ffigen/lib/src/code_generator/compound.dart index bc13331cac..451c322156 100644 --- a/pkgs/ffigen/lib/src/code_generator/compound.dart +++ b/pkgs/ffigen/lib/src/code_generator/compound.dart @@ -252,8 +252,8 @@ class CompoundMember extends AstNode { final String originalName; final Type type; - final Symbol _symbol; - String get name => _symbol.name; + final Symbol symbol; + String get name => symbol.name; CompoundMember({ String? originalName, @@ -261,12 +261,12 @@ class CompoundMember extends AstNode { required this.type, this.dartDoc, }) : originalName = originalName ?? name, - _symbol = Symbol(name, SymbolKind.field); + symbol = Symbol(name, SymbolKind.field); @override void visitChildren(Visitor visitor) { super.visitChildren(visitor); - visitor.visit(_symbol); + visitor.visit(symbol); visitor.visit(type); } } diff --git a/pkgs/ffigen/lib/src/code_generator/constant.dart b/pkgs/ffigen/lib/src/code_generator/constant.dart index 2bae25cbc0..8d0eb67651 100644 --- a/pkgs/ffigen/lib/src/code_generator/constant.dart +++ b/pkgs/ffigen/lib/src/code_generator/constant.dart @@ -3,6 +3,7 @@ // BSD-style license that can be found in the LICENSE file. import '../header_parser/sub_parsers/api_availability.dart'; +import '../public_ast.dart' as public_ast; import '../visitor/ast.dart'; import 'binding.dart'; import 'binding_string.dart'; @@ -76,6 +77,9 @@ class UnnamedEnumConstant extends Constant { super.apiAvailability, }); + @override + public_ast.AstNode? toPublicAstNode() => public_ast.UnnamedEnumConstant(this); + @override void visit(Visitation visitation) => visitation.visitUnnamedEnumConstant(this); @@ -93,6 +97,9 @@ class MacroConstant extends Constant { super.apiAvailability, }); + @override + public_ast.AstNode? toPublicAstNode() => public_ast.MacroConstant(this); + @override void visit(Visitation visitation) => visitation.visitMacroConstant(this); } diff --git a/pkgs/ffigen/lib/src/code_generator/cpp_class.dart b/pkgs/ffigen/lib/src/code_generator/cpp_class.dart index a2e6cb13d8..4ddbf91a39 100644 --- a/pkgs/ffigen/lib/src/code_generator/cpp_class.dart +++ b/pkgs/ffigen/lib/src/code_generator/cpp_class.dart @@ -4,6 +4,7 @@ import '../code_generator.dart'; import '../context.dart'; +import '../public_ast.dart' as public_ast; import '../visitor/ast.dart'; import 'binding_string.dart'; @@ -80,6 +81,9 @@ class CppClass extends BindingType with HasLocalScope { required this.fields, }); + @override + public_ast.AstNode? toPublicAstNode() => public_ast.CppClass(this); + @override void visit(Visitation visitation) => visitation.visitCppClass(this); diff --git a/pkgs/ffigen/lib/src/code_generator/enum_class.dart b/pkgs/ffigen/lib/src/code_generator/enum_class.dart index ab87bafb05..20a6304537 100644 --- a/pkgs/ffigen/lib/src/code_generator/enum_class.dart +++ b/pkgs/ffigen/lib/src/code_generator/enum_class.dart @@ -7,6 +7,7 @@ import 'package:collection/collection.dart'; import '../config_provider.dart'; import '../context.dart'; import '../header_parser/sub_parsers/api_availability.dart'; +import '../public_ast.dart' as public_ast; import '../visitor/ast.dart'; import 'binding_string.dart'; import 'imports.dart'; @@ -72,6 +73,10 @@ class EnumClass extends BindingType with HasLocalScope { }) : nativeType = nativeType ?? intType, enumConstants = enumConstants ?? []; + @override + public_ast.AstNode? toPublicAstNode() => + isAnonymous ? null : public_ast.EnumClass(this); + /// Returns a string to declare the enum member and any documentation it may /// have had. String _formatValue(EnumConstant ec, {bool asInt = false}) { @@ -300,8 +305,8 @@ class EnumConstant extends AstNode { final String? dartDoc; final int value; - final Symbol _symbol; - String get name => _symbol.name; + final Symbol symbol; + String get name => symbol.name; EnumConstant({ String? originalName, @@ -309,11 +314,11 @@ class EnumConstant extends AstNode { required this.value, this.dartDoc, }) : originalName = originalName ?? name, - _symbol = Symbol(name, SymbolKind.field); + symbol = Symbol(name, SymbolKind.field); @override void visitChildren(Visitor visitor) { super.visitChildren(visitor); - visitor.visit(_symbol); + visitor.visit(symbol); } } diff --git a/pkgs/ffigen/lib/src/code_generator/func.dart b/pkgs/ffigen/lib/src/code_generator/func.dart index 81104a2bf0..92410cc04a 100644 --- a/pkgs/ffigen/lib/src/code_generator/func.dart +++ b/pkgs/ffigen/lib/src/code_generator/func.dart @@ -5,6 +5,7 @@ import '../code_generator.dart'; import '../context.dart'; import '../header_parser/sub_parsers/api_availability.dart'; +import '../public_ast.dart' as public_ast; import '../visitor/ast.dart'; import 'binding_string.dart'; import 'local_variables.dart'; @@ -107,6 +108,9 @@ class Func extends LookUpBinding with HasLocalScope { } } + @override + public_ast.AstNode? toPublicAstNode() => public_ast.Func(this); + @override BindingString toBindingString(Writer w) { final s = StringBuffer(); diff --git a/pkgs/ffigen/lib/src/code_generator/global.dart b/pkgs/ffigen/lib/src/code_generator/global.dart index 594077eaeb..5e47b0e36d 100644 --- a/pkgs/ffigen/lib/src/code_generator/global.dart +++ b/pkgs/ffigen/lib/src/code_generator/global.dart @@ -2,6 +2,7 @@ // 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 '../public_ast.dart' as public_ast; import '../visitor/ast.dart'; import 'binding.dart'; import 'binding_string.dart'; @@ -43,6 +44,9 @@ class Global extends LookUpBinding with HasLocalScope { this.loadFromNativeAsset = false, }) : super(symbol: Symbol(name, SymbolKind.field)); + @override + public_ast.AstNode? toPublicAstNode() => public_ast.Global(this); + @override BindingString toBindingString(Writer w) { final s = StringBuffer(); diff --git a/pkgs/ffigen/lib/src/code_generator/objc_category.dart b/pkgs/ffigen/lib/src/code_generator/objc_category.dart index 9dc5235389..453494b1b8 100644 --- a/pkgs/ffigen/lib/src/code_generator/objc_category.dart +++ b/pkgs/ffigen/lib/src/code_generator/objc_category.dart @@ -5,6 +5,7 @@ import '../code_generator.dart'; import '../context.dart'; import '../header_parser/sub_parsers/api_availability.dart'; +import '../public_ast.dart' as public_ast; import '../visitor/ast.dart'; import 'binding_string.dart'; import 'scope.dart'; @@ -45,6 +46,9 @@ class ObjCCategory extends NoLookUpBinding with ObjCMethods, HasLocalScope { bool get isObjCImport => context.objCBuiltInFunctions.isBuiltInCategory(originalName); + @override + public_ast.AstNode? toPublicAstNode() => public_ast.ObjCCategory(this); + @override BindingString toBindingString(Writer w) { final s = StringBuffer(); diff --git a/pkgs/ffigen/lib/src/code_generator/objc_interface.dart b/pkgs/ffigen/lib/src/code_generator/objc_interface.dart index 7aca79873c..4b7b76429a 100644 --- a/pkgs/ffigen/lib/src/code_generator/objc_interface.dart +++ b/pkgs/ffigen/lib/src/code_generator/objc_interface.dart @@ -5,6 +5,7 @@ import '../code_generator.dart'; import '../context.dart'; import '../header_parser/sub_parsers/api_availability.dart'; +import '../public_ast.dart' as public_ast; import '../visitor/ast.dart'; import 'binding_string.dart'; import 'local_variables.dart'; @@ -19,7 +20,7 @@ class ObjCInterface extends BindingType with ObjCMethods, HasLocalScope { bool filled = false; final String? module; - late final NoLookUpBinding classObject; + late final ObjCClassGlobal classObject; late final ObjCInternalGlobal _isKindOfClass; late final ObjCMsgSendFunc _isKindOfClassMsgSend; final protocols = []; @@ -108,6 +109,9 @@ class ObjCInterface extends BindingType with ObjCMethods, HasLocalScope { bool get unavailable => apiAvailability.availability == Availability.none; + @override + public_ast.AstNode? toPublicAstNode() => public_ast.ObjCInterface(this); + @override BindingString toBindingString(Writer w) { final context = w.context; diff --git a/pkgs/ffigen/lib/src/code_generator/objc_methods.dart b/pkgs/ffigen/lib/src/code_generator/objc_methods.dart index 04ee538fb6..554f021aa5 100644 --- a/pkgs/ffigen/lib/src/code_generator/objc_methods.dart +++ b/pkgs/ffigen/lib/src/code_generator/objc_methods.dart @@ -324,6 +324,8 @@ class ObjCMethod extends AstNode with HasLocalScope { bool get isProperty => kind == ObjCMethodKind.propertyGetter || kind == ObjCMethodKind.propertySetter; + bool get isPropertyGetter => kind == ObjCMethodKind.propertyGetter; + bool get isPropertySetter => kind == ObjCMethodKind.propertySetter; bool get isRequired => !isOptional; bool get isInstanceMethod => !isClassMethod; bool get unavailable => apiAvailability.availability == Availability.none; diff --git a/pkgs/ffigen/lib/src/code_generator/objc_protocol.dart b/pkgs/ffigen/lib/src/code_generator/objc_protocol.dart index 456922be6b..1618dc03ce 100644 --- a/pkgs/ffigen/lib/src/code_generator/objc_protocol.dart +++ b/pkgs/ffigen/lib/src/code_generator/objc_protocol.dart @@ -5,6 +5,7 @@ import '../code_generator.dart'; import '../context.dart'; import '../header_parser/sub_parsers/api_availability.dart'; +import '../public_ast.dart' as public_ast; import '../visitor/ast.dart'; import 'binding_string.dart'; import 'local_variables.dart'; @@ -71,6 +72,9 @@ class ObjCProtocol extends BindingType with ObjCMethods, HasLocalScope { bool get unavailable => apiAvailability.availability == Availability.none; + @override + public_ast.AstNode? toPublicAstNode() => public_ast.ObjCProtocol(this); + @override BindingString toBindingString(Writer w) { final protocolClass = ObjCBuiltInFunctions.protocolClass.gen(context); diff --git a/pkgs/ffigen/lib/src/code_generator/struct.dart b/pkgs/ffigen/lib/src/code_generator/struct.dart index bf3c3059b6..25b43b82d6 100644 --- a/pkgs/ffigen/lib/src/code_generator/struct.dart +++ b/pkgs/ffigen/lib/src/code_generator/struct.dart @@ -2,6 +2,7 @@ // 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 '../public_ast.dart' as public_ast; import '../visitor/ast.dart'; import 'compound.dart'; @@ -48,6 +49,9 @@ class Struct extends Compound { @override int? pack; + @override + public_ast.AstNode? toPublicAstNode() => public_ast.Struct(this); + @override void visit(Visitation visitation) => visitation.visitStruct(this); } diff --git a/pkgs/ffigen/lib/src/code_generator/typealias.dart b/pkgs/ffigen/lib/src/code_generator/typealias.dart index dbd3687156..da8113df3b 100644 --- a/pkgs/ffigen/lib/src/code_generator/typealias.dart +++ b/pkgs/ffigen/lib/src/code_generator/typealias.dart @@ -4,6 +4,7 @@ import '../code_generator.dart'; import '../context.dart'; +import '../public_ast.dart' as public_ast; import '../strings.dart' as strings; import '../visitor/ast.dart'; import 'binding_string.dart'; @@ -106,6 +107,9 @@ class Typealias extends BindingType { return pointee.type; } + @override + public_ast.AstNode? toPublicAstNode() => public_ast.Typealias(this); + @override BindingString toBindingString(Writer w) { assert(!isAnonymous); diff --git a/pkgs/ffigen/lib/src/code_generator/union.dart b/pkgs/ffigen/lib/src/code_generator/union.dart index 478132431f..00d2f1f92f 100644 --- a/pkgs/ffigen/lib/src/code_generator/union.dart +++ b/pkgs/ffigen/lib/src/code_generator/union.dart @@ -2,6 +2,7 @@ // 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 '../public_ast.dart' as public_ast; import '../visitor/ast.dart'; import 'compound.dart'; @@ -45,6 +46,9 @@ class Union extends Compound { @override int? get pack => null; + @override + public_ast.AstNode? toPublicAstNode() => public_ast.Union(this); + @override void visit(Visitation visitation) => visitation.visitUnion(this); } diff --git a/pkgs/ffigen/lib/src/config_provider/config.dart b/pkgs/ffigen/lib/src/config_provider/config.dart index 7559ca9ae3..7a04541b68 100644 --- a/pkgs/ffigen/lib/src/config_provider/config.dart +++ b/pkgs/ffigen/lib/src/config_provider/config.dart @@ -9,6 +9,7 @@ import 'package:meta/meta.dart'; import '../code_generator.dart'; import '../ffigen.dart'; +import '../public_ast.dart'; import 'config_types.dart'; /// The generator that generates bindings for `dart:ffi` from C and Objective-C @@ -58,6 +59,38 @@ final class FfiGenerator { /// The configuration for outputting bindings. final Output output; + /// AST visitors to run on the generated bindings to perform transformations + /// before Dart code generation occurs. + /// + /// Visitors are executed sequentially in the order they appear in this list. + /// Each visitor can inspect or mutate AST node names and properties (such as + /// renaming functions, parameters, struct fields, enum constants, etc.). + /// + /// ### Examples + /// + /// Filtering declarations: + /// ```dart + /// Visitor( + /// visitFunc: (node) { + /// if (node.name.startsWith('_')) { + /// node.isIncluded = false; + /// } + /// }, + /// ) + /// ``` + /// + /// Renaming declarations: + /// ```dart + /// Visitor( + /// visitStruct: (node) { + /// if (node.name == 'custom_type') { + /// node.name = 'CustomType'; + /// } + /// }, + /// ) + /// ``` + final List visitors; + /// Returns an [ImportedType] if the given [Declaration] should be imported /// from another Dart library, or `null` otherwise. final ImportedType? Function(Declaration declaration) importType; @@ -83,6 +116,7 @@ final class FfiGenerator { this.unnamedEnums = UnnamedEnums.excludeAll, this.objectiveC, required this.output, + this.visitors = const [], this.importType = _defaultImportType, @Deprecated('Only visible for YamlConfig plumbing.') this.libclangDylib, }); @@ -181,69 +215,10 @@ final class Declarations { /// The address is exposed as an FFI pointer. final bool Function(Declaration declaration) includeSymbolAddress; - /// Returns a new name for the declaration, to replace its `originalName`. - /// - /// ```dart - /// // This renames `Foo` to `Bar`, and nothing else: - /// rename: (Declaration decl) => - /// decl.originalName == 'Foo' ? 'Bar' : decl.originalName - /// ``` - final String Function(Declaration declaration) rename; - - /// A function to pass to [rename] that doesn't rename the declaration. - static String useOriginalName(Declaration declaration) => - declaration.originalName; - - /// A function to pass to [rename] that applies a rename map. - /// - /// The key of the map is the declaration's `originalName`, and the value is - /// the new name to use. If the declaration is not in the map, it is not - /// renamed. - static String Function(Declaration) renameWithMap( - Map renames, - ) => - (Declaration declaration) => - renames[declaration.originalName] ?? declaration.originalName; - - /// Returns a new name for the member of the declaration, to replace its - /// `originalName`. - /// - /// Used for struct/union fields, enum elements, function params, and - /// Objective-C interface/protocol/category methods/properties. - /// - /// ```dart - /// // This renames `Foo.bar` to `Foo.baz`, and nothing else: - /// rename: (Declaration decl, String member) { - /// if (decl.originalName == 'Foo' && member == 'baz') { - /// return 'baz'; - /// } - /// return member; - /// } - /// ``` - final String Function(Declaration declaration, String member) renameMember; - - /// A function to pass to [renameMember] that doesn't rename the member. - static String useMemberOriginalName(Declaration declaration, String member) => - member; - - /// A function to pass to [renameMember] that applies a rename map. - /// - /// The key of the map is the declaration's `originalName`, and the value is - /// a map from member name to renamed member name. If the declaration is not - /// in the map, or the member isn't in the declaration's map, the member is - /// not renamed. - static String Function(Declaration, String) renameMemberWithMap( - Map> renames, - ) => - (Declaration declaration, String member) => - renames[declaration.originalName]?[member] ?? member; - const Declarations({ this.include = excludeAll, this.includeMember = includeAllMembers, this.includeSymbolAddress = excludeAll, - this.rename = useOriginalName, - this.renameMember = useMemberOriginalName, }); } @@ -277,8 +252,6 @@ final class Enums extends Declarations { const Enums({ super.include, - super.rename, - super.renameMember, this.style = _styleDefault, this.silenceWarning = false, }); @@ -337,8 +310,6 @@ final class Functions extends Declarations { const Functions({ super.include, super.includeSymbolAddress, - super.rename, - super.renameMember, this.includeTypedef = _includeTypedefDefault, this.isLeaf = _isLeafDefault, this.recordUse = _recordUseDefault, @@ -355,7 +326,7 @@ final class Functions extends Declarations { /// Configuration for globals. final class Globals extends Declarations { - const Globals({super.rename, super.include, super.includeSymbolAddress}); + const Globals({super.include, super.includeSymbolAddress}); static const excludeAll = Globals(include: Declarations.excludeAll); @@ -367,7 +338,7 @@ final class Globals extends Declarations { /// Configuration for macros. final class Macros extends Declarations { - const Macros({super.rename, super.include}); + const Macros({super.include}); static const excludeAll = Macros(include: Declarations.excludeAll); @@ -389,8 +360,6 @@ final class Structs extends Declarations { const Structs({ super.include, - super.rename, - super.renameMember, this.dependencies = CompoundDependencies.opaque, this.packingOverride = _packingOverrideDefault, }); @@ -413,7 +382,6 @@ final class Typedefs extends Declarations { final bool useSupportedTypedefs; const Typedefs({ - super.rename, super.include, this.useSupportedTypedefs = true, this.includeUnused = false, @@ -429,7 +397,7 @@ final class Typedefs extends Declarations { /// Configuration for C++ class declarations. final class CppClasses extends Declarations { - const CppClasses({super.include, super.rename, super.renameMember}); + const CppClasses({super.include}); static const excludeAll = CppClasses(include: Declarations.excludeAll); static const includeAll = CppClasses(include: Declarations.includeAll); @@ -453,8 +421,6 @@ final class Unions extends Declarations { const Unions({ super.include, - super.rename, - super.renameMember, this.dependencies = CompoundDependencies.opaque, }); @@ -468,7 +434,7 @@ final class Unions extends Declarations { /// Configuration for unnamed enum constants. final class UnnamedEnums extends Declarations { - const UnnamedEnums({super.include, super.rename, super.renameMember}); + const UnnamedEnums({super.include}); static const excludeAll = UnnamedEnums(include: Declarations.excludeAll); @@ -522,8 +488,6 @@ final class Categories extends Declarations { const Categories({ super.include, super.includeMember, - super.rename, - super.renameMember, this.includeTransitive = true, }); @@ -549,8 +513,6 @@ final class Interfaces extends Declarations { const Interfaces({ super.include, super.includeMember, - super.rename, - super.renameMember, this.includeTransitive = false, this.module = noModule, }); @@ -579,8 +541,6 @@ final class Protocols extends Declarations { const Protocols({ super.include, super.includeMember, - super.rename, - super.renameMember, this.includeTransitive = false, this.module = noModule, }); diff --git a/pkgs/ffigen/lib/src/config_provider/config_types.dart b/pkgs/ffigen/lib/src/config_provider/config_types.dart index 0d356ac7a0..f66c79c544 100644 --- a/pkgs/ffigen/lib/src/config_provider/config_types.dart +++ b/pkgs/ffigen/lib/src/config_provider/config_types.dart @@ -127,11 +127,11 @@ final class YamlDeclarationFilters { _memberIncluder = memberIncluder ?? YamlMemberIncluder(); /// Applies renaming and returns the result. - String rename(Declaration declaration) => + String? rename(Declaration declaration) => _renamer.rename(declaration.originalName); /// Applies member renaming and returns the result. - String renameMember(Declaration declaration, String member) => + String? renameMember(Declaration declaration, String member) => _memberRenamer.rename(declaration.originalName, member); /// Checks if a name is allowed by a filter. @@ -151,8 +151,6 @@ final class YamlDeclarationFilters { include: shouldInclude, includeSymbolAddress: shouldIncludeSymbolAddress, includeMember: shouldIncludeMember, - rename: rename, - renameMember: renameMember, ); } } @@ -172,8 +170,8 @@ class RegExpRenamer { /// Renames [str] according to [replacementPattern]. /// - /// Returns [str] if [regExp] doesn't have a full match. - String rename(String str) { + /// Returns `null` if [regExp] doesn't have a full match. + String? rename(String str) { if (matches(str)) { // Get match. final regExpMatch = regExp.firstMatch(str)!; @@ -195,7 +193,7 @@ class RegExpRenamer { }); return result; } else { - return str; + return null; } } @@ -267,7 +265,7 @@ class YamlRenamer { YamlRenamer.noRename() : _renameMatchers = [], _renameFull = {}; - String rename(String name) { + String? rename(String name) { // Apply full rename (if any). if (_renameFull.containsKey(name)) { return _renameFull[name]!; @@ -275,13 +273,13 @@ class YamlRenamer { // Apply rename regexp (if matches). for (final renamer in _renameMatchers) { - if (renamer.matches(name)) { - return renamer.rename(name); + if (renamer.rename(name) case final rename?) { + return rename; } } - // No renaming is provided for this declaration, return unchanged. - return name; + // No renaming is provided for this declaration, return null. + return null; } } @@ -316,7 +314,7 @@ class YamlMemberRenamer { }) : _memberRenameFull = memberRenameFull ?? {}, _memberRenameMatchers = memberRenamePattern ?? []; - String rename(String declaration, String member) { + String? rename(String declaration, String member) { if (_cache.containsKey(declaration)) { return _cache[declaration]!.rename(member); } @@ -337,8 +335,8 @@ class YamlMemberRenamer { } } - // No renaming is provided for this declaration, return unchanged. - return member; + // No renaming is provided for this declaration, return null. + return null; } } diff --git a/pkgs/ffigen/lib/src/config_provider/yaml_config.dart b/pkgs/ffigen/lib/src/config_provider/yaml_config.dart index 5a63d0edcb..c8c70d10f3 100644 --- a/pkgs/ffigen/lib/src/config_provider/yaml_config.dart +++ b/pkgs/ffigen/lib/src/config_provider/yaml_config.dart @@ -12,6 +12,7 @@ import 'package:package_config/package_config_types.dart'; import 'package:yaml/yaml.dart'; import '../code_generator.dart'; +import '../public_ast.dart' as public_ast; import '../strings.dart' as strings; import 'config.dart'; import 'config_spec.dart'; @@ -1240,6 +1241,7 @@ final class YamlConfig { } return FfiGenerator( + visitors: [YamlConfigAstVisitor(this)], input: Input( compilerOptions: compilerOpts, entryPoints: entryPoints, @@ -1263,24 +1265,18 @@ final class YamlConfig { functions: Functions( include: functionDecl.shouldInclude, includeSymbolAddress: functionDecl.shouldIncludeSymbolAddress, - rename: functionDecl.rename, - renameMember: functionDecl.renameMember, varArgs: varArgFunctions, includeTypedef: shouldExposeFunctionTypedef, isLeaf: isLeafFunction, ), structs: Structs( include: _structDecl.shouldInclude, - rename: _structDecl.rename, - renameMember: _structDecl.renameMember, dependencies: _structDependencies, packingOverride: (decl) => _structPackingOverride.getOverridenPackValue(decl.originalName), ), enums: Enums( include: _enumClassDecl.shouldInclude, - rename: _enumClassDecl.rename, - renameMember: _enumClassDecl.renameMember, silenceWarning: silenceEnumWarning, style: (e, suggestedStyle) { if (suggestedStyle != null) return suggestedStyle; @@ -1292,26 +1288,16 @@ final class YamlConfig { ), unions: Unions( include: _unionDecl.shouldInclude, - rename: _unionDecl.rename, - renameMember: _unionDecl.renameMember, dependencies: _unionDependencies, ), - unnamedEnums: UnnamedEnums( - include: _unnamedEnumConstants.shouldInclude, - rename: _unnamedEnumConstants.rename, - ), + unnamedEnums: UnnamedEnums(include: _unnamedEnumConstants.shouldInclude), globals: Globals( include: globals.shouldInclude, includeSymbolAddress: globals.shouldIncludeSymbolAddress, - rename: globals.rename, - ), - macros: Macros( - include: macroDecl.shouldInclude, - rename: macroDecl.rename, ), + macros: Macros(include: macroDecl.shouldInclude), typedefs: Typedefs( include: typedefs.shouldInclude, - rename: typedefs.rename, useSupportedTypedefs: useSupportedTypedefs, includeUnused: includeUnusedTypedefs, ), @@ -1321,24 +1307,18 @@ final class YamlConfig { interfaces: Interfaces( include: objcInterfaces.shouldInclude, includeMember: objcInterfaces.shouldIncludeMember, - rename: objcInterfaces.rename, - renameMember: objcInterfaces.renameMember, includeTransitive: includeTransitiveObjCInterfaces, module: interfaceModule, ), protocols: Protocols( include: objcProtocols.shouldInclude, includeMember: objcProtocols.shouldIncludeMember, - rename: objcProtocols.rename, - renameMember: objcProtocols.renameMember, includeTransitive: includeTransitiveObjCProtocols, module: protocolModule, ), categories: Categories( include: objcCategories.shouldInclude, includeMember: objcCategories.shouldIncludeMember, - rename: objcCategories.rename, - renameMember: objcCategories.renameMember, includeTransitive: includeTransitiveObjCCategories, ), externalVersions: externalVersions, @@ -1351,3 +1331,152 @@ final class YamlConfig { ); } } + +/// AST Visitor that applies renames configured in [YamlConfig]. +final class YamlConfigAstVisitor extends public_ast.Visitor { + final YamlConfig config; + + const YamlConfigAstVisitor(this.config) : super.base(); + + Declaration _decl(public_ast.DeclNode node) => + Declaration(usr: node.usr, originalName: node.originalName); + + @override + void visitFunc(public_ast.Func node) { + if (config.functionDecl.rename(_decl(node)) case final rename?) { + node.name = rename; + } + } + + @override + void visitUnnamedEnumConstant(public_ast.UnnamedEnumConstant node) { + if (config.unnamedEnumConstants.rename(_decl(node)) case final rename?) { + node.name = rename; + } + } + + @override + void visitStruct(public_ast.Struct node) { + if (config.structDecl.rename(_decl(node)) case final rename?) { + node.name = rename; + } + } + + @override + void visitUnion(public_ast.Union node) { + if (config.unionDecl.rename(_decl(node)) case final rename?) { + node.name = rename; + } + } + + @override + void visitEnum(public_ast.EnumClass node) { + if (config.enumClassDecl.rename(_decl(node)) case final rename?) { + node.name = rename; + } + } + + @override + void visitGlobal(public_ast.Global node) { + if (config.globals.rename(_decl(node)) case final rename?) { + node.name = rename; + } + } + + @override + void visitMacro(public_ast.MacroConstant node) { + if (config.macroDecl.rename(_decl(node)) case final rename?) { + node.name = rename; + } + } + + @override + void visitTypealias(public_ast.Typealias node) { + if (config.typedefs.rename(_decl(node)) case final rename?) { + node.name = rename; + } + } + + @override + void visitObjCInterface(public_ast.ObjCInterface node) { + if (config.objcInterfaces.rename(_decl(node)) case final rename?) { + node.name = rename; + } + } + + @override + void visitObjCProtocol(public_ast.ObjCProtocol node) { + if (config.objcProtocols.rename(_decl(node)) case final rename?) { + node.name = rename; + } + } + + @override + void visitObjCCategory(public_ast.ObjCCategory node) { + if (config.objcCategories.rename(_decl(node)) case final rename?) { + node.name = rename; + } + } + + YamlDeclarationFilters? _getObjCDecl(public_ast.DeclNode node) { + if (node is public_ast.ObjCInterface) { + return config.objcInterfaces; + } else if (node is public_ast.ObjCProtocol) { + return config.objcProtocols; + } else if (node is public_ast.ObjCCategory) { + return config.objcCategories; + } + return null; + } + + @override + void visitObjCMethod(public_ast.ObjCMethod node) { + if (node.isPropertySetter) return; + final decl = _getObjCDecl(node.parent); + if (decl != null) { + if (decl.renameMember(_decl(node.parent), node.originalName) + case final rename?) { + node.name = rename; + } + } + } + + YamlDeclarationFilters? _getCompoundDecl(public_ast.DeclNode node) { + if (node is public_ast.Struct) { + return config.structDecl; + } else if (node is public_ast.Union) { + return config.unionDecl; + } + return null; + } + + @override + void visitField(public_ast.Field node) { + final decl = _getCompoundDecl(node.parent); + if (decl != null) { + if (decl.renameMember(_decl(node.parent), node.originalName) + case final rename?) { + node.name = rename; + } + } + } + + @override + void visitParam(public_ast.Param node) { + final parent = node.parent; + if (parent is public_ast.Func) { + if (config.functionDecl.renameMember(_decl(parent), node.originalName) + case final rename?) { + node.name = rename; + } + } + } + + @override + void visitEnumConstant(public_ast.EnumConstant node) { + if (config.enumClassDecl.renameMember(_decl(node.parent), node.originalName) + case final rename?) { + node.name = rename; + } + } +} diff --git a/pkgs/ffigen/lib/src/header_parser/parser.dart b/pkgs/ffigen/lib/src/header_parser/parser.dart index b4c5b60aac..5f480dda20 100644 --- a/pkgs/ffigen/lib/src/header_parser/parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/parser.dart @@ -167,6 +167,11 @@ List _findObjectiveCSysroot() => [ List transformBindings(List rawBindings, Context context) { final config = context.config; + final nodes = rawBindings.map((b) => b.toPublicAstNode()).nonNulls.toList(); + for (final visitor in config.visitors) { + visitor.visitAll(nodes); + } + final allBindings = visit( context, FindTransitiveDepsVisitation(), @@ -218,6 +223,7 @@ List transformBindings(List rawBindings, Context context) { visit(context, MarkBindingsVisitation(finalBindings), allBindings); visit(context, MarkImportsVisitation(context), finalBindings); + visit(context, DefaultParameterNamesVisitation(), finalBindings); _nameAllSymbols(context, finalBindings); /// Sort bindings. diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/classdecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/classdecl_parser.dart index 64d9f92041..bbf538ee49 100644 --- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/classdecl_parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/classdecl_parser.dart @@ -72,7 +72,7 @@ CppClass? parseClassDeclaration(Context context, clang_types.CXCursor cursor) { availability: apiAvailability.dartDoc, ), originalName: className, - name: cppClasses.rename(decl), + name: className, context: context, methods: methods, fields: [], @@ -107,7 +107,7 @@ void _parseAnyMethod( return; } - final className = context.config.cpp!.classes.rename(classDecl); + final className = classDecl.originalName; final symbol = switch (kind) { CppMethodKind.constructor => '${className}_new', CppMethodKind.method => '${className}_$methodName', @@ -135,12 +135,7 @@ List? _parseParameters( Declaration classDecl, ) { final logger = context.logger; - var i = 0; - final parsed = parseParameters( - context, - cursor, - renameFn: (paramName) => paramName.isEmpty ? 'arg${i++}' : paramName, - ); + final parsed = parseParameters(context, cursor); if (parsed.hasIncompleteStruct || parsed.hasUnimplementedType) { logger.fine(' Unsupported parameter type'); return null; 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 23e01f33b1..3bd22b2c04 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 @@ -4,7 +4,6 @@ import '../../code_generator.dart'; import '../../config_provider/config.dart'; -import '../../config_provider/config_types.dart'; import '../../context.dart'; import '../../strings.dart' as strings; import '../clang_bindings/clang_bindings.dart' as clang_types; @@ -71,9 +70,6 @@ class _ParsedCompound { return maxChildAlignment > alignment; } - Declarations get compoundConfig => - compound is Struct ? context.config.structs : context.config.unions; - /// Returns pack value of a struct depending on config, returns null for no /// packing. int? get packValue { @@ -136,7 +132,6 @@ Compound? _parseCompoundDeclaration( return null; } - final decl = Declaration(usr: usr, originalName: declName); final Compound compound; if (declName.isEmpty) { cursor = context.cursorIndex.getDefinition(cursor); @@ -160,7 +155,7 @@ Compound? _parseCompoundDeclaration( compound = constructor( usr: usr, originalName: declName, - name: configDecl.rename(decl), + name: declName, dartDoc: getCursorDocComment( context, cursor, @@ -271,11 +266,6 @@ void _compoundMembersVisitor( _ParsedCompound parsed, ) { final context = parsed.context; - final compoundConf = parsed.compoundConfig; - final decl = Declaration( - usr: parsed.compound.usr, - originalName: parsed.compound.originalName, - ); try { switch (cursor.kind) { case clang_types.CXCursorKind.CXCursor_FieldDecl: @@ -315,7 +305,7 @@ void _compoundMembersVisitor( indent: nesting.length + commentPrefix.length, ), originalName: cursor.spelling(), - name: compoundConf.renameMember(decl, cursor.spelling()), + name: cursor.spelling(), type: mt, ), ); @@ -350,7 +340,7 @@ void _compoundMembersVisitor( indent: nesting.length + commentPrefix.length, ), originalName: spelling, - name: compoundConf.renameMember(decl, spelling), + name: spelling, type: mt, ), ); 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 ffd58fa9c5..a8494cb5e6 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 @@ -65,7 +65,7 @@ EnumClass parseEnumDeclaration(clang_types.CXCursor cursor, Context context) { availability: apiAvailability.dartDoc, ), originalName: enumName, - name: config.enums.rename(decl), + name: enumName, nativeType: nativeType, context: context, apiAvailability: apiAvailability, @@ -84,7 +84,7 @@ EnumClass parseEnumDeclaration(clang_types.CXCursor cursor, Context context) { indent: nesting.length + commentPrefix.length, ), originalName: child.spelling(), - name: config.enums.renameMember(decl, child.spelling()), + name: child.spelling(), value: enumIntValue, ), ); 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 e5b91200ed..25393c413a 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 @@ -41,15 +41,8 @@ List parseFunctionDeclaration( final returnType = cursor.returnType().toCodeGenType(context); - final ( - :parameters, - :hasIncompleteStruct, - :hasUnimplementedType, - ) = parseParameters( - context, - cursor, - renameFn: (paramName) => config.functions.renameMember(decl, paramName), - ); + final (:parameters, :hasIncompleteStruct, :hasUnimplementedType) = + parseParameters(context, cursor); if (clang.clang_Cursor_isFunctionInlined(cursor) != 0 && clang.clang_Cursor_getStorageClass(cursor) != @@ -120,7 +113,7 @@ List parseFunctionDeclaration( availability: apiAvailability.dartDoc, ), usr: usr, - name: config.functions.rename(decl) + (vaFunc?.postfix ?? ''), + name: funcName + (vaFunc?.postfix ?? ''), originalName: funcName, returnType: returnType, parameters: parameters.map((p) => p.clone()).toList(), @@ -170,14 +163,14 @@ parseParameters( context.logger.finer('Unimplemented type: ${paramType.baseType}'); unimplementedParameterType = true; } - final paramName = paramCursor.spelling(); - final name = renameFn != null ? renameFn(paramName) : paramName; + final spelling = paramCursor.spelling(); + final name = spelling.isEmpty ? 'arg$i' : spelling; final objCConsumed = paramCursor.hasChildWithKind( clang_types.CXCursorKind.CXCursor_NSConsumed, ); parameters.add( Parameter( - originalName: paramName, + originalName: spelling, name: name, type: paramType, objCConsumed: objCConsumed, 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 e700f7330a..d9930d377b 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 @@ -10,7 +10,6 @@ import 'package:logging/logging.dart'; import 'package:path/path.dart' as p; import '../../code_generator.dart'; -import '../../config_provider/config_types.dart'; import '../../context.dart'; import '../clang_bindings/clang_bindings.dart' as clang_types; import '../utils.dart'; @@ -23,7 +22,6 @@ void saveMacroDefinition(Context context, clang_types.CXCursor cursor) { return; } final originalMacroName = cursor.spelling(); - final decl = Declaration(usr: macroUsr, originalName: originalMacroName); if (clang.clang_Cursor_isMacroBuiltin(cursor) == 0 && clang.clang_Cursor_isMacroFunctionLike(cursor) == 0) { // Parse macro only if it's not builtin or function-like. @@ -31,7 +29,7 @@ void saveMacroDefinition(Context context, clang_types.CXCursor cursor) { "++++ Saved Macro '$originalMacroName' for later : " '${cursor.completeStringRepr()}', ); - final prefixedName = context.config.macros.rename(decl); + final prefixedName = originalMacroName; 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 2c44e46d83..0a33b64454 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 @@ -62,7 +62,7 @@ ObjCCategory? parseObjCCategoryDeclaration( final category = ObjCCategory( usr: usr, originalName: name, - name: objcCategories.rename(decl), + name: name, parent: parentInterface, dartDoc: getCursorDocComment( context, @@ -85,20 +85,13 @@ ObjCCategory? parseObjCCategoryDeclaration( ); break; case clang_types.CXCursorKind.CXCursor_ObjCPropertyDecl: - final (getter, setter) = parseObjCProperty( - context, - child, - decl, - objcCategories, - ); + final (getter, setter) = parseObjCProperty(context, child, decl); 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), - ); + category.addMethod(parseObjCMethod(context, child, decl)); break; } }); 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 a6b668add7..603102a4dd 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 @@ -3,7 +3,6 @@ // BSD-style license that can be found in the LICENSE file. import '../../code_generator.dart'; -import '../../config_provider/config.dart'; import '../../config_provider/config_types.dart'; import '../../context.dart'; import '../clang_bindings/clang_bindings.dart' as clang_types; @@ -39,7 +38,7 @@ Type? parseObjCInterfaceDeclaration( context: context, usr: usr, originalName: name, - name: objcInterfaces.rename(decl), + name: name, module: objcInterfaces.module(decl), dartDoc: getCursorDocComment( context, @@ -67,8 +66,6 @@ void fillObjCInterfaceMethodsIfNeeded( if (itf.filled) return; itf.filled = true; // Break cycles. - final objcInterfaces = context.config.objectiveC!.interfaces; - context.logger.fine( '++++ Filling ObjC interface: ' 'Name: ${itf.originalName}, ${cursor.completeStringRepr()}', @@ -85,18 +82,13 @@ void fillObjCInterfaceMethodsIfNeeded( itf.addProtocol(parseObjCProtocolDeclaration(context, protoCursor)); break; case clang_types.CXCursorKind.CXCursor_ObjCPropertyDecl: - final (getter, setter) = parseObjCProperty( - context, - child, - itfDecl, - objcInterfaces, - ); + final (getter, setter) = parseObjCProperty(context, child, itfDecl); itf.addMethod(getter); itf.addMethod(setter); break; case clang_types.CXCursorKind.CXCursor_ObjCInstanceMethodDecl: case clang_types.CXCursorKind.CXCursor_ObjCClassMethodDecl: - itf.addMethod(parseObjCMethod(context, child, itfDecl, objcInterfaces)); + itf.addMethod(parseObjCMethod(context, child, itfDecl)); break; } }); @@ -144,7 +136,6 @@ void _parseSuperType( Context context, clang_types.CXCursor cursor, Declaration decl, - Declarations filters, ) { final fieldName = cursor.spelling(); final fieldType = cursor.type().toCodeGenType(context); @@ -190,7 +181,7 @@ void _parseSuperType( final getter = ObjCMethod( context: context, originalName: getterName, - name: filters.renameMember(decl, getterName), + name: getterName, dartDoc: dartDoc ?? getterName, kind: ObjCMethodKind.propertyGetter, isClassMethod: isClassMethod, @@ -233,7 +224,6 @@ ObjCMethod? parseObjCMethod( Context context, clang_types.CXCursor cursor, Declaration itfDecl, - Declarations filters, ) { final logger = context.logger; final methodName = cursor.spelling(); @@ -299,7 +289,7 @@ ObjCMethod? parseObjCMethod( return ObjCMethod( context: context, originalName: methodName, - name: filters.renameMember(itfDecl, methodName), + name: methodName, dartDoc: getCursorDocComment( context, cursor, 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 b5c1f22c33..a618a30f27 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 @@ -63,7 +63,7 @@ ObjCProtocol? parseObjCProtocolDeclaration( context: context, usr: usr, originalName: name, - name: objcProtocols.rename(decl), + name: name, module: objcProtocols.module(decl), dartDoc: getCursorDocComment( context, @@ -91,20 +91,13 @@ ObjCProtocol? parseObjCProtocolDeclaration( } break; case clang_types.CXCursorKind.CXCursor_ObjCPropertyDecl: - final (getter, setter) = parseObjCProperty( - context, - child, - decl, - objcProtocols, - ); + final (getter, setter) = parseObjCProperty(context, child, decl); 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), - ); + protocol.addMethod(parseObjCMethod(context, child, decl)); 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 5b6c40693b..52c8591f31 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 @@ -3,7 +3,6 @@ // BSD-style license that can be found in the LICENSE file. import '../../code_generator.dart'; -import '../../config_provider/config_types.dart'; import '../../context.dart'; import '../clang_bindings/clang_bindings.dart' as clang_types; import '../type_extractor/extractor.dart'; @@ -30,7 +29,6 @@ Typealias parseTypedefDeclaration( clang_types.CXCursor cursor, ) { final logger = context.logger; - final config = context.config; final bindingsIndex = context.bindingsIndex; final name = cursor.spelling(); final usr = cursor.usr(); @@ -38,7 +36,6 @@ Typealias parseTypedefDeclaration( 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(context, ct, originalCursor: cursor); @@ -78,7 +75,7 @@ Typealias parseTypedefDeclaration( final type = Typealias( usr: usr, originalName: name, - name: config.typedefs.rename(decl), + name: name, type: s, dartDoc: getCursorDocComment(context, cursor), ); 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 87c6269edb..2dec4a9965 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 @@ -3,7 +3,6 @@ // BSD-style license that can be found in the LICENSE file. import '../../code_generator.dart'; -import '../../config_provider/config_types.dart'; import '../../context.dart'; import '../clang_bindings/clang_bindings.dart' as clang_types; import '../utils.dart'; @@ -46,7 +45,6 @@ Constant? _addUnNamedEnumConstant( clang_types.CXCursor cursor, ) { final logger = context.logger; - final config = context.config; final bindingsIndex = context.bindingsIndex; final usr = cursor.usr(); @@ -68,9 +66,7 @@ Constant? _addUnNamedEnumConstant( final constant = UnnamedEnumConstant( usr: usr, originalName: cursor.spelling(), - name: config.unnamedEnums.rename( - Declaration(usr: cursor.usr(), originalName: cursor.spelling()), - ), + name: cursor.spelling(), dartDoc: apiAvailability.dartDoc, rawType: 'int', rawValue: clang.clang_getEnumConstantDeclValue(cursor).toString(), 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..2e6da6402f 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 @@ -41,7 +41,7 @@ Binding? parseVarDeclaration(Context context, clang_types.CXCursor cursor) { constant = Constant( usr: usr, originalName: name, - name: config.globals.rename(decl), + name: name, dartDoc: getCursorDocComment(context, cursor), rawType: 'int', rawValue: value.toString(), @@ -52,7 +52,7 @@ Binding? parseVarDeclaration(Context context, clang_types.CXCursor cursor) { constant = Constant( usr: usr, originalName: name, - name: config.globals.rename(decl), + name: name, dartDoc: getCursorDocComment(context, cursor), rawType: 'double', rawValue: writeDoubleAsString(value), @@ -64,7 +64,7 @@ Binding? parseVarDeclaration(Context context, clang_types.CXCursor cursor) { constant = Constant( usr: usr, originalName: name, - name: config.globals.rename(decl), + name: name, dartDoc: getCursorDocComment(context, cursor), rawType: 'String', rawValue: "'$rawValue'", @@ -101,7 +101,7 @@ Binding? parseVarDeclaration(Context context, clang_types.CXCursor cursor) { final global = Global( originalName: name, - name: config.globals.rename(decl), + name: name, usr: usr, type: type, dartDoc: getCursorDocComment(context, cursor), diff --git a/pkgs/ffigen/lib/src/public_ast.dart b/pkgs/ffigen/lib/src/public_ast.dart new file mode 100644 index 0000000000..802edaa374 --- /dev/null +++ b/pkgs/ffigen/lib/src/public_ast.dart @@ -0,0 +1,512 @@ +// Copyright (c) 2026, 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 'code_generator.dart' as internal; +import 'public_visitor.dart'; + +export 'public_visitor.dart'; + +/// Abstract base class for all public AST nodes. +abstract class AstNode { + const AstNode(); + + void accept(Visitor visitor); +} + +/// Base class for AST nodes with a name. +abstract class NamedNode extends AstNode { + const NamedNode(); + + /// Original C/C++/Objective-C name of this AST node. + String get originalName; + + /// The generated Dart name for this AST node. + String get name; + set name(String value); +} + +/// Base class for declaration AST nodes. +abstract class DeclNode extends NamedNode { + const DeclNode(); + + /// USR identifier for this declaration. + String get usr; +} + +/// A C function declaration. +class Func extends DeclNode { + final internal.Func _func; + + /// The parameters of this function. + final List params; + + Func(this._func) : params = [] { + params.addAll(_func.functionType.parameters.map((p) => Param(this, p))); + } + + @override + void accept(Visitor visitor) { + visitor.visitFunc(this); + visitor.visitAll(params); + } + + @override + String get usr => _func.usr; + + @override + String get originalName => _func.originalName; + + @override + String get name => _func.symbol.oldName; + + @override + set name(String value) { + _func.symbol.oldName = value; + _func.funcVarSymbol.oldName = '_$value'; + } +} + +/// A C struct declaration. +class Struct extends DeclNode { + final internal.Struct _struct; + + /// The fields belonging to this struct. + final List members; + + Struct(this._struct) : members = [] { + members.addAll(_struct.members.map((m) => Field(this, m))); + } + + @override + void accept(Visitor visitor) { + visitor.visitStruct(this); + visitor.visitAll(members); + } + + @override + String get usr => _struct.usr; + + @override + String get originalName => _struct.originalName; + + @override + String get name => _struct.symbol.oldName; + + @override + set name(String value) => _struct.symbol.oldName = value; +} + +/// A C union declaration. +class Union extends DeclNode { + final internal.Union _union; + + /// The fields belonging to this union. + final List members; + + Union(this._union) : members = [] { + members.addAll(_union.members.map((m) => Field(this, m))); + } + + @override + void accept(Visitor visitor) { + visitor.visitUnion(this); + visitor.visitAll(members); + } + + @override + String get usr => _union.usr; + + @override + String get originalName => _union.originalName; + + @override + String get name => _union.symbol.oldName; + + @override + set name(String value) => _union.symbol.oldName = value; +} + +/// An enum declaration. +class EnumClass extends DeclNode { + final internal.EnumClass _enumClass; + + /// The constants belonging to this enum. + final List constants; + + EnumClass(this._enumClass) : constants = [] { + constants.addAll( + _enumClass.enumConstants.map((c) => EnumConstant(this, c)), + ); + } + + @override + void accept(Visitor visitor) { + visitor.visitEnum(this); + visitor.visitAll(constants); + } + + @override + String get usr => _enumClass.usr; + + @override + String get originalName => _enumClass.originalName; + + @override + String get name => _enumClass.symbol.oldName; + + @override + set name(String value) => _enumClass.symbol.oldName = value; +} + +/// A C global variable declaration. +class Global extends DeclNode { + final internal.Global _global; + + Global(this._global); + + @override + void accept(Visitor visitor) => visitor.visitGlobal(this); + + @override + String get usr => _global.usr; + + @override + String get originalName => _global.originalName; + + @override + String get name => _global.symbol.oldName; + + @override + set name(String value) => _global.symbol.oldName = value; +} + +/// A C macro constant declaration. +class MacroConstant extends DeclNode { + final internal.MacroConstant _macro; + + MacroConstant(this._macro); + + @override + void accept(Visitor visitor) => visitor.visitMacro(this); + + @override + String get usr => _macro.usr; + + @override + String get originalName => _macro.originalName; + + @override + String get name => _macro.symbol.oldName; + + @override + set name(String value) => _macro.symbol.oldName = value; +} + +/// A C typedef (type alias) declaration. +class Typealias extends DeclNode { + final internal.Typealias _typealias; + + Typealias(this._typealias); + + @override + void accept(Visitor visitor) => visitor.visitTypealias(this); + + @override + String get usr => _typealias.usr; + + @override + String get originalName => _typealias.originalName; + + @override + String get name => _typealias.symbol.oldName; + + @override + set name(String value) => _typealias.symbol.oldName = value; +} + +/// An Objective-C interface (class) declaration. +class ObjCInterface extends DeclNode { + final internal.ObjCInterface _interface; + + /// The methods belonging to this Objective-C interface. + final List methods; + + ObjCInterface(this._interface) : methods = [] { + methods.addAll(_interface.methods.map((m) => ObjCMethod(this, m))); + } + + @override + void accept(Visitor visitor) { + visitor.visitObjCInterface(this); + visitor.visitAll(methods); + } + + @override + String get usr => _interface.usr; + + @override + String get originalName => _interface.originalName; + + @override + String get name => _interface.symbol.oldName; + + @override + set name(String value) { + _interface.symbol.oldName = value; + _interface.classObject.symbol.oldName = '_class_$value'; + _interface.classObject.rawSymbol.oldName = '_class_${value}_raw'; + } +} + +/// An Objective-C protocol declaration. +class ObjCProtocol extends DeclNode { + final internal.ObjCProtocol _protocol; + + /// The methods belonging to this Objective-C protocol. + final List methods; + + ObjCProtocol(this._protocol) : methods = [] { + methods.addAll(_protocol.methods.map((m) => ObjCMethod(this, m))); + } + + @override + void accept(Visitor visitor) { + visitor.visitObjCProtocol(this); + visitor.visitAll(methods); + } + + @override + String get usr => _protocol.usr; + + @override + String get originalName => _protocol.originalName; + + @override + String get name => _protocol.symbol.oldName; + + @override + set name(String value) => _protocol.symbol.oldName = value; +} + +/// An Objective-C category declaration. +class ObjCCategory extends DeclNode { + final internal.ObjCCategory _category; + + /// The methods belonging to this Objective-C category. + final List methods; + + ObjCCategory(this._category) : methods = [] { + methods.addAll(_category.methods.map((m) => ObjCMethod(this, m))); + } + + /// The [ObjCInterface] that this category extends. + ObjCInterface get interface => ObjCInterface(_category.parent); + + @override + void accept(Visitor visitor) { + visitor.visitObjCCategory(this); + visitor.visitAll(methods); + } + + @override + String get usr => _category.usr; + + @override + String get originalName => _category.originalName; + + @override + String get name => _category.symbol.oldName; + + @override + set name(String value) => _category.symbol.oldName = value; +} + +/// A C++ class declaration. +class CppClass extends DeclNode { + final internal.CppClass _cppClass; + + /// The methods belonging to this C++ class. + final List methods; + + CppClass(this._cppClass) : methods = [] { + methods.addAll(_cppClass.methods.map((m) => CppMethod(this, m))); + } + + @override + void accept(Visitor visitor) { + visitor.visitCppClass(this); + visitor.visitAll(methods); + } + + @override + String get usr => _cppClass.usr; + + @override + String get originalName => _cppClass.originalName; + + @override + String get name => _cppClass.symbol.oldName; + + @override + set name(String value) => _cppClass.symbol.oldName = value; +} + +/// A field in a struct or union. +class Field extends NamedNode { + final internal.CompoundMember _member; + + /// The parent AST node containing this field (a [Struct] or [Union]). + final DeclNode parent; + + Field(this.parent, this._member); + + @override + void accept(Visitor visitor) => visitor.visitField(this); + + @override + String get originalName => _member.originalName; + + @override + String get name => _member.symbol.oldName; + + @override + set name(String value) => _member.symbol.oldName = value; +} + +/// A constant inside a named enum. +class EnumConstant extends NamedNode { + final internal.EnumConstant _constant; + + /// The parent [EnumClass] containing this constant. + final EnumClass parent; + + EnumConstant(this.parent, this._constant); + + @override + void accept(Visitor visitor) => visitor.visitEnumConstant(this); + + @override + String get originalName => _constant.originalName ?? _constant.name; + + @override + String get name => _constant.symbol.oldName; + + @override + set name(String value) => _constant.symbol.oldName = value; +} + +/// A function or method parameter. +class Param extends NamedNode { + final internal.Parameter _parameter; + + /// The parent AST node containing this parameter (a [Func], [ObjCMethod], or + /// [CppMethod]). + final NamedNode parent; + + Param(this.parent, this._parameter); + + @override + void accept(Visitor visitor) => visitor.visitParam(this); + + @override + String get originalName => _parameter.originalName; + + @override + String get name => _parameter.symbol.oldName; + + @override + set name(String value) => _parameter.symbol.oldName = value; +} + +/// A C++ method declaration. +class CppMethod extends NamedNode { + final internal.CppMethod _method; + + /// The parameters of this C++ method. + final List params; + + /// The parent [CppClass] containing this C++ method. + final CppClass parent; + + CppMethod(this.parent, this._method) : params = [] { + params.addAll(_method.parameters.map((p) => Param(this, p))); + } + + @override + void accept(Visitor visitor) { + visitor.visitCppMethod(this); + visitor.visitAll(params); + } + + @override + String get originalName => _method.originalName; + + @override + String get name => _method.name.oldName; + + @override + set name(String value) => _method.name.oldName = value; +} + +/// An Objective-C method declaration. +class ObjCMethod extends NamedNode { + final internal.ObjCMethod _method; + + /// The parameters of this Objective-C method. + final List params; + + /// The parent AST node containing this Objective-C method (an + /// [ObjCInterface], [ObjCProtocol], or [ObjCCategory]). + final DeclNode parent; + + ObjCMethod(this.parent, this._method) : params = [] { + params.addAll(_method.params.map((p) => Param(this, p))); + } + + @override + void accept(Visitor visitor) { + visitor.visitObjCMethod(this); + visitor.visitAll(params); + } + + /// The Objective-C method selector string. + String get selector => _method.originalName; + + @override + String get originalName => _method.originalName; + + @override + String get name => _method.symbol.oldName; + + @override + set name(String value) => _method.symbol.oldName = value; + + /// Whether this method is a property getter. + bool get isPropertyGetter => _method.isPropertyGetter; + + /// Whether this method is a property setter. + bool get isPropertySetter => _method.isPropertySetter; +} + +/// An unnamed enum constant. +class UnnamedEnumConstant extends DeclNode { + final internal.UnnamedEnumConstant _constant; + + UnnamedEnumConstant(this._constant); + + @override + void accept(Visitor visitor) => visitor.visitUnnamedEnumConstant(this); + + @override + String get usr => _constant.usr; + + @override + String get originalName => _constant.originalName; + + @override + String get name => _constant.symbol.oldName; + + @override + set name(String value) => _constant.symbol.oldName = value; +} diff --git a/pkgs/ffigen/lib/src/public_visitor.dart b/pkgs/ffigen/lib/src/public_visitor.dart new file mode 100644 index 0000000000..b11e144c22 --- /dev/null +++ b/pkgs/ffigen/lib/src/public_visitor.dart @@ -0,0 +1,170 @@ +// Copyright (c) 2026, 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 'public_ast.dart'; + +/// Base class for AST visitors that inspect and transform AST nodes. +/// +/// Implementations can extend [Visitor] (must call the [Visitor.base] +/// constructor) or use the [Visitor] factory constructor to provide inline +/// callbacks for specific nodes. +abstract base class Visitor { + const Visitor.base(); + + /// Creates a [Visitor] that delegates visiting to the provided callbacks. + factory Visitor({ + void Function(Func) visitFunc, + void Function(Struct) visitStruct, + void Function(Union) visitUnion, + void Function(EnumClass) visitEnum, + void Function(Global) visitGlobal, + void Function(MacroConstant) visitMacro, + void Function(Typealias) visitTypealias, + void Function(ObjCInterface) visitObjCInterface, + void Function(ObjCProtocol) visitObjCProtocol, + void Function(ObjCCategory) visitObjCCategory, + void Function(CppClass) visitCppClass, + void Function(Field) visitField, + void Function(EnumConstant) visitEnumConstant, + void Function(UnnamedEnumConstant) visitUnnamedEnumConstant, + void Function(Param) visitParam, + void Function(ObjCMethod) visitObjCMethod, + void Function(CppMethod) visitCppMethod, + }) = _CallbackVisitor; + + void visitAll(Iterable nodes) { + for (final node in nodes) { + node.accept(this); + } + } + + void visitFunc(Func node) {} + void visitStruct(Struct node) {} + void visitUnion(Union node) {} + void visitEnum(EnumClass node) {} + void visitGlobal(Global node) {} + void visitMacro(MacroConstant node) {} + void visitTypealias(Typealias node) {} + void visitObjCInterface(ObjCInterface node) {} + void visitObjCProtocol(ObjCProtocol node) {} + void visitObjCCategory(ObjCCategory node) {} + void visitCppClass(CppClass node) {} + void visitField(Field node) {} + void visitEnumConstant(EnumConstant node) {} + void visitUnnamedEnumConstant(UnnamedEnumConstant node) {} + void visitParam(Param node) {} + void visitObjCMethod(ObjCMethod node) {} + void visitCppMethod(CppMethod node) {} +} + +final class _CallbackVisitor extends Visitor { + final void Function(Func) _visitFunc; + final void Function(Struct) _visitStruct; + final void Function(Union) _visitUnion; + final void Function(EnumClass) _visitEnum; + final void Function(Global) _visitGlobal; + final void Function(MacroConstant) _visitMacro; + final void Function(Typealias) _visitTypealias; + final void Function(ObjCInterface) _visitObjCInterface; + final void Function(ObjCProtocol) _visitObjCProtocol; + final void Function(ObjCCategory) _visitObjCCategory; + final void Function(CppClass) _visitCppClass; + final void Function(Field) _visitField; + final void Function(EnumConstant) _visitEnumConstant; + final void Function(UnnamedEnumConstant) _visitUnnamedEnumConstant; + final void Function(Param) _visitParam; + final void Function(ObjCMethod) _visitObjCMethod; + final void Function(CppMethod) _visitCppMethod; + + const _CallbackVisitor({ + void Function(Func) visitFunc = _defaultVisit, + void Function(Struct) visitStruct = _defaultVisit, + void Function(Union) visitUnion = _defaultVisit, + void Function(EnumClass) visitEnum = _defaultVisit, + void Function(Global) visitGlobal = _defaultVisit, + void Function(MacroConstant) visitMacro = _defaultVisit, + void Function(Typealias) visitTypealias = _defaultVisit, + void Function(ObjCInterface) visitObjCInterface = _defaultVisit, + void Function(ObjCProtocol) visitObjCProtocol = _defaultVisit, + void Function(ObjCCategory) visitObjCCategory = _defaultVisit, + void Function(CppClass) visitCppClass = _defaultVisit, + void Function(Field) visitField = _defaultVisit, + void Function(EnumConstant) visitEnumConstant = _defaultVisit, + void Function(UnnamedEnumConstant) visitUnnamedEnumConstant = _defaultVisit, + void Function(Param) visitParam = _defaultVisit, + void Function(ObjCMethod) visitObjCMethod = _defaultVisit, + void Function(CppMethod) visitCppMethod = _defaultVisit, + }) : _visitFunc = visitFunc, + _visitStruct = visitStruct, + _visitUnion = visitUnion, + _visitEnum = visitEnum, + _visitGlobal = visitGlobal, + _visitMacro = visitMacro, + _visitTypealias = visitTypealias, + _visitObjCInterface = visitObjCInterface, + _visitObjCProtocol = visitObjCProtocol, + _visitObjCCategory = visitObjCCategory, + _visitCppClass = visitCppClass, + _visitField = visitField, + _visitEnumConstant = visitEnumConstant, + _visitUnnamedEnumConstant = visitUnnamedEnumConstant, + _visitParam = visitParam, + _visitObjCMethod = visitObjCMethod, + _visitCppMethod = visitCppMethod, + super.base(); + + static void _defaultVisit(Object _) {} + + @override + void visitFunc(Func node) => _visitFunc(node); + + @override + void visitStruct(Struct node) => _visitStruct(node); + + @override + void visitUnion(Union node) => _visitUnion(node); + + @override + void visitEnum(EnumClass node) => _visitEnum(node); + + @override + void visitGlobal(Global node) => _visitGlobal(node); + + @override + void visitMacro(MacroConstant node) => _visitMacro(node); + + @override + void visitTypealias(Typealias node) => _visitTypealias(node); + + @override + void visitObjCInterface(ObjCInterface node) => _visitObjCInterface(node); + + @override + void visitObjCProtocol(ObjCProtocol node) => _visitObjCProtocol(node); + + @override + void visitObjCCategory(ObjCCategory node) => _visitObjCCategory(node); + + @override + void visitCppClass(CppClass node) => _visitCppClass(node); + + @override + void visitField(Field node) => _visitField(node); + + @override + void visitEnumConstant(EnumConstant node) => _visitEnumConstant(node); + + @override + void visitUnnamedEnumConstant(UnnamedEnumConstant node) => + _visitUnnamedEnumConstant(node); + + @override + void visitParam(Param node) => _visitParam(node); + + @override + void visitObjCMethod(ObjCMethod node) => _visitObjCMethod(node); + + @override + void visitCppMethod(CppMethod node) => _visitCppMethod(node); +} diff --git a/pkgs/ffigen/lib/src/visitor/default_param_names.dart b/pkgs/ffigen/lib/src/visitor/default_param_names.dart new file mode 100644 index 0000000000..19ce56925e --- /dev/null +++ b/pkgs/ffigen/lib/src/visitor/default_param_names.dart @@ -0,0 +1,64 @@ +// Copyright (c) 2026, 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 '../code_generator/cpp_class.dart'; +import '../code_generator/func.dart'; +import '../code_generator/func_type.dart'; +import '../code_generator/objc_block.dart'; +import '../code_generator/objc_built_in_functions.dart'; +import '../code_generator/objc_methods.dart'; +import '../code_generator/scope.dart'; +import 'ast.dart'; + +/// Visitation to set default names for unnamed parameters across all AST nodes. +class DefaultParameterNamesVisitation extends Visitation { + void _defaultParamNames(Iterable params, {int startIndex = 0}) { + var i = startIndex; + for (final param in params) { + if (param.symbol.oldName.isEmpty) { + param.symbol = Symbol('arg$i', SymbolKind.field); + } + i++; + } + } + + @override + void visitFunctionType(FunctionType node) { + _defaultParamNames(node.parameters); + _defaultParamNames( + node.varArgParameters, + startIndex: node.parameters.length, + ); + visitor.visit(node.returnType); + visitor.visitAll(node.parameters); + visitor.visitAll(node.varArgParameters); + } + + @override + void visitObjCMethod(ObjCMethod node) { + _defaultParamNames(node.params); + visitor.visit(node.returnType); + visitor.visitAll(node.params); + } + + @override + void visitCppMethod(CppMethod node) { + _defaultParamNames(node.parameters); + visitor.visit(node.returnType); + visitor.visitAll(node.parameters); + } + + @override + void visitObjCBlock(ObjCBlock node) { + _defaultParamNames(node.params); + visitor.visit(node.returnType); + visitor.visitAll(node.params); + } + + @override + void visitObjCMsgSendFunc(ObjCMsgSendFunc node) {} + + @override + void visitObjCMsgSendVariantFunc(ObjCMsgSendVariantFunc node) {} +} diff --git a/pkgs/ffigen/lib/src/visitor/visitor.dart b/pkgs/ffigen/lib/src/visitor/visitor.dart index 12fc52c076..66cc257c23 100644 --- a/pkgs/ffigen/lib/src/visitor/visitor.dart +++ b/pkgs/ffigen/lib/src/visitor/visitor.dart @@ -7,6 +7,8 @@ import '../code_generator/scope.dart'; import '../context.dart'; import 'ast.dart'; +export 'default_param_names.dart'; + /// Wrapper around [Visitation] to be used by callers. final class Visitor { Visitor(this.context, this._visitation, {bool debug = false}) diff --git a/pkgs/ffigen/test/header_parser_tests/record_use_test.dart b/pkgs/ffigen/test/header_parser_tests/record_use_test.dart index bf0bc1ee13..fb83820a04 100644 --- a/pkgs/ffigen/test/header_parser_tests/record_use_test.dart +++ b/pkgs/ffigen/test/header_parser_tests/record_use_test.dart @@ -2,13 +2,25 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -import 'package:ffigen/src/config_provider.dart'; +import 'package:ffigen/ffigen.dart'; import 'package:ffigen/src/header_parser.dart' show parse; +import 'package:ffigen/src/public_ast.dart' as public_ast; import 'package:path/path.dart' as p; import 'package:test/test.dart'; import '../test_utils.dart'; +final class _RenamingVisitor extends public_ast.Visitor { + const _RenamingVisitor() : super.base(); + + @override + void visitFunc(public_ast.Func node) { + if (node.name == 'sum') { + node.name = 'add'; + } + } +} + void main() { group('record_use_test', () { test('Expected Bindings', () { @@ -20,9 +32,8 @@ void main() { functions: Functions( include: (decl) => true, recordUse: (decl) => true, - rename: (decl) => - decl.originalName == 'sum' ? 'add' : decl.originalName, ), + visitors: const [_RenamingVisitor()], output: Output( dartFile: Uri.file('unused.dart'), style: const NativeExternalBindings(), diff --git a/pkgs/ffigen/test/native_objc_test/rename_test.dart b/pkgs/ffigen/test/native_objc_test/rename_test.dart index 08910a6a8e..259fff6b6d 100644 --- a/pkgs/ffigen/test/native_objc_test/rename_test.dart +++ b/pkgs/ffigen/test/native_objc_test/rename_test.dart @@ -40,7 +40,7 @@ void main() { test('Renamed method', () { final renamed = Renamed(); - expect(renamed.fooBarBaz(123, y: 456), 579); + expect(renamed.fooBarBaz(123, otherArg: 456), 579); }); test('Renamed property', () { diff --git a/pkgs/ffigen/test/native_objc_test/rename_test_bindings.dart b/pkgs/ffigen/test/native_objc_test/rename_test_bindings.dart index ea954ae1ef..ea453eea06 100644 --- a/pkgs/ffigen/test/native_objc_test/rename_test_bindings.dart +++ b/pkgs/ffigen/test/native_objc_test/rename_test_bindings.dart @@ -94,13 +94,13 @@ extension Renamed$Methods on Renamed { } /// renamedMethod:otherArg: - int fooBarBaz(int x, {required int y}) { + int fooBarBaz(int x, {required int otherArg}) { final _$$ref = object$.ref; return _objc_msgSend_1q0lyci( _$$ref.pointer, _sel_renamedMethod_otherArg_, x, - y, + otherArg, ); } diff --git a/pkgs/ffigen/test/unit_tests/config_util_test.dart b/pkgs/ffigen/test/unit_tests/config_util_test.dart index b928ed54a4..e7b74fe8b4 100644 --- a/pkgs/ffigen/test/unit_tests/config_util_test.dart +++ b/pkgs/ffigen/test/unit_tests/config_util_test.dart @@ -25,24 +25,5 @@ void main() { expect(includer(decl('goo'), 'bar'), isTrue); expect(includer(decl('goo'), 'baz'), isTrue); }); - - test('renameWithMap', () { - final renamer = Declarations.renameWithMap({'foo': 'bar'}); - expect(renamer(decl('foo')), 'bar'); - expect(renamer(decl('bar')), 'bar'); - expect(renamer(decl('baz')), 'baz'); - }); - - test('renameMemberWithMap', () { - final renamer = Declarations.renameMemberWithMap({ - 'foo': {'bar': 'baz'}, - }); - expect(renamer(decl('foo'), 'bar'), 'baz'); - expect(renamer(decl('foo'), 'baz'), 'baz'); - expect(renamer(decl('foo'), 'bop'), 'bop'); - expect(renamer(decl('goo'), 'bar'), 'bar'); - expect(renamer(decl('goo'), 'baz'), 'baz'); - expect(renamer(decl('goo'), 'bop'), 'bop'); - }); }); } diff --git a/pkgs/ffigen/test/unit_tests/renaming_visitor_test.dart b/pkgs/ffigen/test/unit_tests/renaming_visitor_test.dart new file mode 100644 index 0000000000..c1e871554a --- /dev/null +++ b/pkgs/ffigen/test/unit_tests/renaming_visitor_test.dart @@ -0,0 +1,646 @@ +// Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:ffigen/ffigen.dart' show FfiGenerator, Output, YamlConfig; +import 'package:ffigen/src/code_generator.dart'; +import 'package:ffigen/src/code_generator/scope.dart'; +import 'package:ffigen/src/header_parser/sub_parsers/api_availability.dart'; +import 'package:ffigen/src/public_ast.dart' as public_ast; +import 'package:test/test.dart'; +import 'package:yaml/yaml.dart'; + +import '../test_utils.dart'; + +final class CustomRenamerVisitor extends public_ast.Visitor { + CustomRenamerVisitor() : super.base(); + @override + void visitFunc(public_ast.Func node) { + if (node.name == 'c_foo') { + node.name = 'dartFoo'; + } + super.visitFunc(node); + } + + @override + void visitStruct(public_ast.Struct node) { + if (node.name == 'c_struct') { + node.name = 'DartStruct'; + } + super.visitStruct(node); + } + + @override + void visitField(public_ast.Field node) { + if (node.name == 'field_a') { + node.name = 'renamedFieldA'; + } + } + + @override + void visitEnum(public_ast.EnumClass node) { + if (node.name == 'c_enum') { + node.name = 'DartEnum'; + } + super.visitEnum(node); + } + + @override + void visitEnumConstant(public_ast.EnumConstant node) { + if (node.name == 'K_VALUE_A') { + node.name = 'valueA'; + } + } + + @override + void visitObjCInterface(public_ast.ObjCInterface node) { + if (node.name == 'MyClass') { + node.name = 'RenamedMyClass'; + } + super.visitObjCInterface(node); + } + + @override + void visitObjCMethod(public_ast.ObjCMethod node) { + if (node.selector == 'compare:options:range:') { + node.name = 'customCompare'; + node.params[1].name = 'customOptions'; + node.params[2].name = 'customRange'; + } + } + + @override + void visitParam(public_ast.Param node) { + if (node.name == 'arg_0') { + node.name = 'renamedArg0'; + } + } +} + +void main() { + group('RenamingVisitor Tests', () { + test('Top-level and member renames via Custom Visitor', () { + final context = testContext( + FfiGenerator(output: Output(dartFile: Uri.file('out.dart'))), + ); + + final func = Func( + name: 'c_foo', + originalName: 'c_foo', + returnType: voidType, + parameters: [Parameter(name: 'arg_0', type: intType)], + ); + + final struct = Struct( + name: 'c_struct', + originalName: 'c_struct', + context: context, + members: [ + CompoundMember( + name: 'field_a', + originalName: 'field_a', + type: intType, + ), + ], + ); + + final enumClass = EnumClass( + name: 'c_enum', + originalName: 'c_enum', + context: context, + enumConstants: [ + EnumConstant(name: 'K_VALUE_A', originalName: 'K_VALUE_A', value: 0), + ], + ); + + final objcMethod = ObjCMethod( + context: context, + originalName: 'compare:options:range:', + name: 'compare:options:range:', + kind: ObjCMethodKind.method, + isClassMethod: false, + isOptional: false, + returnType: intType, + family: null, + apiAvailability: ApiAvailability.all, + params: [ + Parameter(name: 'str', type: intType), + Parameter(name: 'opts', type: intType), + Parameter(name: 'rng', type: intType), + ], + ownershipAttribute: null, + consumesSelfAttribute: false, + ); + + final objcInterface = ObjCInterface( + context: context, + originalName: 'MyClass', + name: 'MyClass', + apiAvailability: ApiAvailability.all, + )..addMethod(objcMethod); + + final rawBindings = [func, struct, enumClass, objcInterface]; + final nodes = rawBindings + .map((b) => b.toPublicAstNode()) + .nonNulls + .toList(); + + expect(func.symbol.oldName, 'c_foo'); + expect(struct.symbol.oldName, 'c_struct'); + expect(struct.members[0].symbol.oldName, 'field_a'); + expect(enumClass.symbol.oldName, 'c_enum'); + expect(enumClass.enumConstants[0].symbol.oldName, 'K_VALUE_A'); + expect(objcInterface.symbol.oldName, 'MyClass'); + expect(objcMethod.symbol.oldName, 'compare'); + expect(objcMethod.params.elementAt(1).symbol.oldName, 'options'); + expect(objcMethod.params.elementAt(2).symbol.oldName, 'range'); + + CustomRenamerVisitor().visitAll(nodes); + + expect(func.symbol.oldName, 'dartFoo'); + expect(struct.symbol.oldName, 'DartStruct'); + expect(struct.members[0].symbol.oldName, 'renamedFieldA'); + expect(enumClass.symbol.oldName, 'DartEnum'); + expect(enumClass.enumConstants[0].symbol.oldName, 'valueA'); + expect(objcInterface.symbol.oldName, 'RenamedMyClass'); + expect(objcMethod.symbol.oldName, 'customCompare'); + expect(objcMethod.params.elementAt(1).symbol.oldName, 'customOptions'); + expect(objcMethod.params.elementAt(2).symbol.oldName, 'customRange'); + }); + + test( + 'ObjC method selector splitting in constructor and visitor overrides', + () { + final context = testContext( + FfiGenerator(output: Output(dartFile: Uri.file('out.dart'))), + ); + + final method = ObjCMethod( + context: context, + originalName: 'doSomething:withArg:andOther:', + name: 'doSomething:withArg:andOther:', + kind: ObjCMethodKind.method, + isClassMethod: false, + isOptional: false, + returnType: voidType, + family: null, + apiAvailability: ApiAvailability.all, + params: [ + Parameter(name: 'a', type: intType), + Parameter(name: 'b', type: intType), + Parameter(name: 'c', type: intType), + ], + ownershipAttribute: null, + consumesSelfAttribute: false, + ); + + expect(method.originalName, 'doSomething:withArg:andOther:'); + expect(method.symbol.oldName, 'doSomething'); + expect(method.params.elementAt(0).symbol.oldName, 'a'); + expect(method.params.elementAt(1).symbol.oldName, 'withArg'); + expect(method.params.elementAt(2).symbol.oldName, 'andOther'); + + final nodes = [ + ObjCInterface( + context: context, + originalName: 'TestItf', + name: 'TestItf', + apiAvailability: ApiAvailability.all, + )..addMethod(method), + ].map((b) => b.toPublicAstNode()).nonNulls.toList(); + + CustomRenamerVisitor().visitAll(nodes); + + expect(method.symbol.oldName, 'doSomething'); + expect(method.params.elementAt(1).symbol.oldName, 'withArg'); + }, + ); + + test('YamlConfigAstVisitor exact, regex, and member renames', () { + final yamlConfig = YamlConfig.fromYaml( + loadYaml(r''' +output: 'unused.dart' +headers: + entry-points: + - 'unused.h' +functions: + rename: + 'c_(.*)': 'dart_$1' + member-rename: + 'c_func': + 'param1': 'renamedParam1' +structs: + rename: + 'my_struct': 'MyStruct' + member-rename: + 'my_struct': + 'old_field': 'newField' +objc-interfaces: + rename: + 'OldClass': 'NewClass' + member-rename: + 'OldClass': + 'foo:bar:': 'customFoo:customBar:' +''') + as YamlMap, + createTestLogger(), + ); + + final generator = yamlConfig.configAdapter(); + expect(generator.visitors.length, 1); + + final context = testContext(generator); + + final func = Func( + name: 'c_func', + originalName: 'c_func', + returnType: voidType, + parameters: [Parameter(name: 'param1', type: intType)], + ); + + final struct = Struct( + name: 'my_struct', + originalName: 'my_struct', + context: context, + members: [ + CompoundMember( + name: 'old_field', + originalName: 'old_field', + type: intType, + ), + ], + ); + + final objcMethod = ObjCMethod( + context: context, + originalName: 'foo:bar:', + name: 'foo:bar:', + kind: ObjCMethodKind.method, + isClassMethod: false, + isOptional: false, + returnType: voidType, + family: null, + apiAvailability: ApiAvailability.all, + params: [ + Parameter(name: 'a', type: intType), + Parameter(name: 'b', type: intType), + ], + ownershipAttribute: null, + consumesSelfAttribute: false, + ); + + final objcInterface = ObjCInterface( + context: context, + originalName: 'OldClass', + name: 'OldClass', + apiAvailability: ApiAvailability.all, + )..addMethod(objcMethod); + + final nodes = [ + func, + struct, + objcInterface, + ].map((b) => b.toPublicAstNode()).nonNulls.toList(); + generator.visitors.first.visitAll(nodes); + + expect(func.symbol.oldName, 'dart_func'); + expect(func.functionType.parameters[0].symbol.oldName, 'renamedParam1'); + + expect(struct.symbol.oldName, 'MyStruct'); + expect(struct.members[0].symbol.oldName, 'newField'); + + expect(objcInterface.symbol.oldName, 'NewClass'); + expect(objcMethod.symbol.oldName, 'customFoo:customBar:'); + expect(objcMethod.params.elementAt(1).symbol.oldName, 'bar'); + }); + + test('Public AST nodes expose usr getter', () { + final context = testContext( + FfiGenerator(output: Output(dartFile: Uri.file('out.dart'))), + ); + + final func = Func( + usr: 'c_foo_usr', + name: 'c_foo', + originalName: 'c_foo', + returnType: voidType, + ); + final struct = Struct( + usr: 'c_struct_usr', + name: 'c_struct', + originalName: 'c_struct', + context: context, + ); + final union = Union( + usr: 'c_union_usr', + name: 'c_union', + originalName: 'c_union', + context: context, + ); + final enumClass = EnumClass( + usr: 'c_enum_usr', + name: 'c_enum', + originalName: 'c_enum', + context: context, + ); + final global = Global( + usr: 'c_global_usr', + name: 'c_global', + originalName: 'c_global', + type: intType, + ); + final macro = MacroConstant( + usr: 'c_macro_usr', + name: 'c_macro', + originalName: 'c_macro', + rawType: 'int', + rawValue: '42', + ); + final typealias = Typealias( + usr: 'c_typealias_usr', + name: 'c_typealias', + type: intType, + ); + final objcInterface = ObjCInterface( + usr: 'c_interface_usr', + context: context, + originalName: 'MyClass', + name: 'MyClass', + apiAvailability: ApiAvailability.all, + ); + final objcProtocol = ObjCProtocol( + usr: 'c_protocol_usr', + context: context, + originalName: 'MyProto', + name: 'MyProto', + apiAvailability: ApiAvailability.all, + ); + final objcCategory = ObjCCategory( + usr: 'c_category_usr', + context: context, + originalName: 'MyCat', + name: 'MyCat', + parent: objcInterface, + apiAvailability: ApiAvailability.all, + ); + final cppClass = CppClass( + usr: 'c_cppclass_usr', + name: 'CppClass', + originalName: 'CppClass', + context: context, + methods: [], + fields: [], + ); + final unnamedEnumConst = UnnamedEnumConstant( + usr: 'c_unnamed_usr', + name: 'c_unnamed', + originalName: 'c_unnamed', + rawType: 'int', + rawValue: '0', + ); + + final rawBindings = [ + func, + struct, + union, + enumClass, + global, + macro, + typealias, + objcInterface, + objcProtocol, + objcCategory, + cppClass, + unnamedEnumConst, + ]; + + final nodes = rawBindings + .map((b) => b.toPublicAstNode()) + .nonNulls + .toList(); + + expect((nodes[0] as public_ast.Func).usr, 'c_foo_usr'); + expect((nodes[1] as public_ast.Struct).usr, 'c_struct_usr'); + expect((nodes[2] as public_ast.Union).usr, 'c_union_usr'); + expect((nodes[3] as public_ast.EnumClass).usr, 'c_enum_usr'); + expect((nodes[4] as public_ast.Global).usr, 'c_global_usr'); + expect((nodes[5] as public_ast.MacroConstant).usr, 'c_macro_usr'); + expect((nodes[6] as public_ast.Typealias).usr, 'c_typealias_usr'); + expect((nodes[7] as public_ast.ObjCInterface).usr, 'c_interface_usr'); + expect((nodes[8] as public_ast.ObjCProtocol).usr, 'c_protocol_usr'); + expect((nodes[9] as public_ast.ObjCCategory).usr, 'c_category_usr'); + expect((nodes[10] as public_ast.CppClass).usr, 'c_cppclass_usr'); + expect( + (nodes[11] as public_ast.UnnamedEnumConstant).usr, + 'c_unnamed_usr', + ); + }); + + test('Visitor callback-based factory constructor', () { + final context = testContext( + FfiGenerator(output: Output(dartFile: Uri.file('out.dart'))), + ); + + final func = Func( + name: 'c_foo', + originalName: 'c_foo', + returnType: voidType, + parameters: [Parameter(name: 'arg_0', type: intType)], + ); + + final struct = Struct( + name: 'c_struct', + originalName: 'c_struct', + context: context, + members: [ + CompoundMember( + name: 'field_a', + originalName: 'field_a', + type: intType, + ), + ], + ); + + final visitedFuncs = []; + final visitedStructs = []; + final visitedParams = []; + + final visitor = public_ast.Visitor( + visitFunc: (node) { + visitedFuncs.add(node.name); + if (node.name == 'c_foo') { + node.name = 'dartFoo'; + } + }, + visitStruct: (node) { + visitedStructs.add(node.name); + if (node.name == 'c_struct') { + node.name = 'DartStruct'; + } + }, + visitParam: (node) { + visitedParams.add(node.name); + if (node.name == 'arg_0') { + node.name = 'renamedArg0'; + } + }, + ); + + final nodes = [ + func, + struct, + ].map((b) => b.toPublicAstNode()).nonNulls.toList(); + visitor.visitAll(nodes); + + expect(visitedFuncs, ['c_foo']); + expect(visitedStructs, ['c_struct']); + expect(visitedParams, ['arg_0']); + + expect(func.symbol.oldName, 'dartFoo'); + expect(struct.symbol.oldName, 'DartStruct'); + expect(func.functionType.parameters[0].symbol.oldName, 'renamedArg0'); + }); + + test('Parent and child pointers in public AST nodes', () { + final context = testContext( + FfiGenerator(output: Output(dartFile: Uri.file('out.dart'))), + ); + + final cgFunc = Func( + name: 'my_func', + originalName: 'my_func', + returnType: voidType, + parameters: [Parameter(name: 'p1', type: intType)], + ); + final publicFunc = public_ast.Func(cgFunc); + expect(publicFunc.params[0].parent, same(publicFunc)); + + final cgStruct = Struct( + name: 'my_struct', + originalName: 'my_struct', + context: context, + members: [ + CompoundMember(name: 'f1', originalName: 'f1', type: intType), + ], + ); + final publicStruct = public_ast.Struct(cgStruct); + expect(publicStruct.members[0].parent, same(publicStruct)); + + final cgUnion = Union( + name: 'my_union', + originalName: 'my_union', + context: context, + members: [ + CompoundMember(name: 'u1', originalName: 'u1', type: intType), + ], + ); + final publicUnion = public_ast.Union(cgUnion); + expect(publicUnion.members[0].parent, same(publicUnion)); + + final cgEnum = EnumClass( + name: 'my_enum', + originalName: 'my_enum', + context: context, + enumConstants: [EnumConstant(name: 'C1', originalName: 'C1', value: 0)], + ); + final publicEnum = public_ast.EnumClass(cgEnum); + expect(publicEnum.constants[0].parent, same(publicEnum)); + + final cgObjCMethod = ObjCMethod( + context: context, + originalName: 'doIt:', + name: 'doIt:', + kind: ObjCMethodKind.method, + isClassMethod: false, + isOptional: false, + returnType: voidType, + family: null, + apiAvailability: ApiAvailability.all, + params: [Parameter(name: 'arg1', type: intType)], + ownershipAttribute: null, + consumesSelfAttribute: false, + ); + final cgObjCInterface = ObjCInterface( + context: context, + originalName: 'MyItf', + name: 'MyItf', + apiAvailability: ApiAvailability.all, + )..addMethod(cgObjCMethod); + final publicObjCInterface = public_ast.ObjCInterface(cgObjCInterface); + expect(publicObjCInterface.methods[0].parent, same(publicObjCInterface)); + expect( + publicObjCInterface.methods[0].params[0].parent, + same(publicObjCInterface.methods[0]), + ); + + final cgObjCProtoMethod = ObjCMethod( + context: context, + originalName: 'protoMethod:', + name: 'protoMethod:', + kind: ObjCMethodKind.method, + isClassMethod: false, + isOptional: false, + returnType: voidType, + family: null, + apiAvailability: ApiAvailability.all, + params: [Parameter(name: 'pArg', type: intType)], + ownershipAttribute: null, + consumesSelfAttribute: false, + ); + final cgObjCProtocol = ObjCProtocol( + context: context, + originalName: 'MyProto', + name: 'MyProto', + apiAvailability: ApiAvailability.all, + )..addMethod(cgObjCProtoMethod); + final publicObjCProtocol = public_ast.ObjCProtocol(cgObjCProtocol); + expect(publicObjCProtocol.methods[0].parent, same(publicObjCProtocol)); + + final cgObjCCatMethod = ObjCMethod( + context: context, + originalName: 'catMethod:', + name: 'catMethod:', + kind: ObjCMethodKind.method, + isClassMethod: false, + isOptional: false, + returnType: voidType, + family: null, + apiAvailability: ApiAvailability.all, + params: [Parameter(name: 'cArg', type: intType)], + ownershipAttribute: null, + consumesSelfAttribute: false, + ); + final cgObjCCategory = ObjCCategory( + context: context, + originalName: 'MyCat', + name: 'MyCat', + parent: cgObjCInterface, + apiAvailability: ApiAvailability.all, + )..addMethod(cgObjCCatMethod); + final publicObjCCategory = public_ast.ObjCCategory(cgObjCCategory); + expect(publicObjCCategory.methods[0].parent, same(publicObjCCategory)); + expect(publicObjCCategory.interface.name, 'MyItf'); + + final cgCppMethod = CppMethod( + name: Symbol('cppFunc', SymbolKind.method), + originalName: 'cppFunc', + returnType: voidType, + parameters: [Parameter(name: 'cppArg', type: intType)], + isConstant: false, + ); + final cgCppClass = CppClass( + context: context, + originalName: 'CppClass', + name: 'CppClass', + methods: [cgCppMethod], + fields: [], + ); + final publicCppClass = public_ast.CppClass(cgCppClass); + expect(publicCppClass.methods[0].parent, same(publicCppClass)); + expect( + publicCppClass.methods[0].params[0].parent, + same(publicCppClass.methods[0]), + ); + }); + }); +} diff --git a/pkgs/swiftgen/lib/src/config.dart b/pkgs/swiftgen/lib/src/config.dart index 62dfec8f01..c1b82acc16 100644 --- a/pkgs/swiftgen/lib/src/config.dart +++ b/pkgs/swiftgen/lib/src/config.dart @@ -243,6 +243,9 @@ class FfiGeneratorOptions { /// [ffigen.FfiGenerator.objectiveC] final ffigen.ObjectiveC objectiveC; + /// [ffigen.FfiGenerator.visitors] + final List visitors; + const FfiGeneratorOptions({ this.functions = ffigen.Functions.excludeAll, this.structs = ffigen.Structs.excludeAll, @@ -253,5 +256,6 @@ class FfiGeneratorOptions { this.macros = ffigen.Macros.excludeAll, this.typedefs = ffigen.Typedefs.excludeAll, this.objectiveC = const ffigen.ObjectiveC(), + this.visitors = const [], }); } diff --git a/pkgs/swiftgen/lib/src/generator.dart b/pkgs/swiftgen/lib/src/generator.dart index b46119c2a3..750a27ca0a 100644 --- a/pkgs/swiftgen/lib/src/generator.dart +++ b/pkgs/swiftgen/lib/src/generator.dart @@ -99,8 +99,6 @@ extension SwiftGenGenerator on SwiftGenerator { interfaces: fg.Interfaces( include: interfaces.include, includeMember: interfaces.includeMember, - rename: interfaces.rename, - renameMember: interfaces.renameMember, includeTransitive: interfaces.includeTransitive, module: interfaces.module != fg.Interfaces.noModule ? interfaces.module @@ -109,8 +107,6 @@ extension SwiftGenGenerator on SwiftGenerator { protocols: fg.Protocols( include: protocols.include, includeMember: protocols.includeMember, - rename: protocols.rename, - renameMember: protocols.renameMember, includeTransitive: protocols.includeTransitive, module: protocols.module != fg.Protocols.noModule ? protocols.module @@ -119,6 +115,7 @@ extension SwiftGenGenerator on SwiftGenerator { categories: ffigen.objectiveC.categories, externalVersions: ffigen.objectiveC.externalVersions, ), + visitors: ffigen.visitors, input: fg.Input( entryPoints: [Uri.file(objcHeader)], compilerOptions: [