diff --git a/pkgs/ffigen/CHANGELOG.md b/pkgs/ffigen/CHANGELOG.md index 83243bff0..661143c13 100644 --- a/pkgs/ffigen/CHANGELOG.md +++ b/pkgs/ffigen/CHANGELOG.md @@ -6,6 +6,9 @@ `Categories`, `Interfaces`, and `Protocols`. - __Breaking change__: Remove deprecated `wrapperName` field from `NativeExternalBindings`. +- __Breaking change__: Certain synthetic USRs have been modified to ensure they + cannot collide with real USRs. It's very unlikely that any user facing USRs + are affected. ## 20.1.1 diff --git a/pkgs/ffigen/lib/src/code_generator/objc_block.dart b/pkgs/ffigen/lib/src/code_generator/objc_block.dart index 7e2bbec70..53697c301 100644 --- a/pkgs/ffigen/lib/src/code_generator/objc_block.dart +++ b/pkgs/ffigen/lib/src/code_generator/objc_block.dart @@ -4,6 +4,7 @@ import '../code_generator.dart'; import '../context.dart'; +import '../strings.dart' as strings; import '../visitor/ast.dart'; import 'binding_string.dart'; @@ -108,14 +109,12 @@ class ObjCBlock extends BindingType with HasLocalScope { ) { // Create a fake USR code for the block. This code is used to dedupe blocks // with the same signature. Not intended to be human readable. - final usr = StringBuffer(); - usr.write( - 'objcBlock: ${returnType.cacheKey()} ${returnsRetained ? 'R' : ''}', - ); - for (final param in params) { - usr.write(' ${param.type.cacheKey()} ${param.objCConsumed ? 'C' : ''}'); - } - return usr.toString(); + return [ + '${strings.synthUsrChar} objcBlock:', + '${returnType.cacheKey()} ${returnsRetained ? 'R' : ''}', + for (final param in params) + '${param.type.cacheKey()} ${param.objCConsumed ? 'C' : ''}', + ].join(' '); } bool get hasListener => returnType == voidType; diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/functiondecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/functiondecl_parser.dart index 71f0a23b3..895eb1f44 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 @@ -6,6 +6,7 @@ import '../../code_generator.dart'; import '../../config_provider/config.dart'; import '../../config_provider/config_types.dart'; import '../../context.dart'; +import '../../strings.dart'; import '../clang_bindings/clang_bindings.dart' as clang_types; import '../utils.dart'; import 'api_availability.dart'; @@ -119,7 +120,7 @@ List parseFunctionDeclaration( ); // Initialized with a single value with no prefix and empty var args. - var varArgFunctions = [VarArgFunction('', [])]; + var varArgFunctions = [null]; if (config.functions.varArgs.containsKey(funcName)) { if (clang.clang_isFunctionTypeVariadic(cursor.type()) == 1) { varArgFunctions = config.functions.varArgs[funcName]!; @@ -131,6 +132,8 @@ List parseFunctionDeclaration( } } for (final vaFunc in varArgFunctions) { + var usr = funcUsr; + if (vaFunc != null) usr += '$synthUsrChar vaFunc: ${vaFunc.postfix}'; funcs.add( Func( dartDoc: getCursorDocComment( @@ -139,13 +142,13 @@ List parseFunctionDeclaration( indent: nesting.length + commentPrefix.length, availability: apiAvailability.dartDoc, ), - usr: funcUsr + vaFunc.postfix, - name: config.functions.rename(decl) + vaFunc.postfix, + usr: usr, + name: config.functions.rename(decl) + (vaFunc?.postfix ?? ''), originalName: funcName, returnType: returnType, parameters: parameters, varArgParameters: [ - for (final ta in vaFunc.types) + for (final ta in vaFunc?.types ?? const []) Parameter(type: ta, name: 'va', objCConsumed: false), ], exposeSymbolAddress: config.functions.includeSymbolAddress(decl), diff --git a/pkgs/ffigen/lib/src/header_parser/utils.dart b/pkgs/ffigen/lib/src/header_parser/utils.dart index cfbb20130..95a022bde 100644 --- a/pkgs/ffigen/lib/src/header_parser/utils.dart +++ b/pkgs/ffigen/lib/src/header_parser/utils.dart @@ -10,6 +10,7 @@ import 'package:logging/logging.dart'; import '../code_generator.dart'; import '../config_provider/config_types.dart'; import '../context.dart'; +import '../strings.dart'; import 'clang_bindings/clang_bindings.dart' as clang_types; import 'type_extractor/extractor.dart'; @@ -91,8 +92,9 @@ extension CXCursorExt on clang_types.CXCursor { String usr() { var res = clang.clang_getCursorUSR(this).toStringAndDispose(); + assert(!res.contains(synthUsrChar)); if (isAnonymousRecordDecl()) { - res += '@offset:${sourceFileOffset()}'; + res += '$synthUsrChar anonRec: offset:${sourceFileOffset()}'; } return res; } diff --git a/pkgs/ffigen/lib/src/strings.dart b/pkgs/ffigen/lib/src/strings.dart index b6a5fdef2..4e5c18cf1 100644 --- a/pkgs/ffigen/lib/src/strings.dart +++ b/pkgs/ffigen/lib/src/strings.dart @@ -274,6 +274,9 @@ const doubleNaN = 'double.nan'; /// USR for struct `_Dart_Handle`. const dartHandleUsr = 'c:@S@_Dart_Handle'; +// A character that will never appear in real USRs, for making synthetic USRs. +const synthUsrChar = '~'; + const ffiNative = 'ffi-native'; const ffiNativeAsset = 'asset-id'; diff --git a/pkgs/ffigen/test/regen.dart b/pkgs/ffigen/test/regen.dart index 3ca5c0fe3..db23d0fc3 100644 --- a/pkgs/ffigen/test/regen.dart +++ b/pkgs/ffigen/test/regen.dart @@ -22,7 +22,7 @@ $ dart run test/setup.dart && dart run test/regen.dart && dart test void _regenConfig(Logger logger, String yamlConfigPath) { final path = p.join(packagePathForTests, yamlConfigPath); Directory.current = File(path).parent; - testConfigFromPath(path).generate(logger: logger); + testConfigFromPath(path, logger: logger).generate(logger: logger); } Future main(List args) async { diff --git a/pkgs/ffigen/test/test_utils.dart b/pkgs/ffigen/test/test_utils.dart index 762fd3341..d5665b1c8 100644 --- a/pkgs/ffigen/test/test_utils.dart +++ b/pkgs/ffigen/test/test_utils.dart @@ -235,10 +235,10 @@ FfiGenerator testConfig(String yamlBody, {String? filename, Logger? logger}) { ).configAdapter(); } -FfiGenerator testConfigFromPath(String path) { +FfiGenerator testConfigFromPath(String path, {Logger? logger}) { final file = File(path); final yamlBody = file.readAsStringSync(); - return testConfig(yamlBody, filename: path); + return testConfig(yamlBody, filename: path, logger: logger); } bool isFlutterTester = Platform.resolvedExecutable.contains('flutter_tester');