[ffigen] Move ObjCBuiltInFunctions to its own file and its singleton to data.dart (#325)

diff --git a/pkgs/ffigen/lib/src/code_generator.dart b/pkgs/ffigen/lib/src/code_generator.dart
index a0c5d7e..f599c7b 100644
--- a/pkgs/ffigen/lib/src/code_generator.dart
+++ b/pkgs/ffigen/lib/src/code_generator.dart
@@ -16,6 +16,7 @@
 export 'code_generator/imports.dart';
 export 'code_generator/library.dart';
 export 'code_generator/native_type.dart';
+export 'code_generator/objc_built_in_functions.dart';
 export 'code_generator/objc_interface.dart';
 export 'code_generator/pointer.dart';
 export 'code_generator/struct.dart';
diff --git a/pkgs/ffigen/lib/src/code_generator/objc_built_in_functions.dart b/pkgs/ffigen/lib/src/code_generator/objc_built_in_functions.dart
new file mode 100644
index 0000000..3c170c0
--- /dev/null
+++ b/pkgs/ffigen/lib/src/code_generator/objc_built_in_functions.dart
@@ -0,0 +1,112 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:ffigen/src/code_generator.dart';
+
+import 'writer.dart';
+
+/// Built in functions used by the Objective C bindings.
+class ObjCBuiltInFunctions {
+  late final _registerNameFunc = Func(
+    name: '_sel_registerName',
+    originalName: 'sel_registerName',
+    returnType: PointerType(objCSelType),
+    parameters: [Parameter(name: 'str', type: PointerType(charType))],
+    isInternal: true,
+  );
+  late final String registerName;
+
+  late final _getClassFunc = Func(
+    name: '_objc_getClass',
+    originalName: 'objc_getClass',
+    returnType: PointerType(objCObjectType),
+    parameters: [Parameter(name: 'str', type: PointerType(charType))],
+    isInternal: true,
+  );
+  late final String getClass;
+
+  // We need to load a separate instance of objc_msgSend for each signature.
+  final _msgSendFuncs = <String, Func>{};
+  Func getMsgSendFunc(Type returnType, List<ObjCMethodParam> params) {
+    // TODO(#279): These keys don't dedupe sufficiently.
+    var key = returnType.hashCode.toRadixString(36);
+    for (final p in params) {
+      key += ' ' + p.type.hashCode.toRadixString(36);
+    }
+    _msgSendFuncs[key] ??= Func(
+      name: '_objc_msgSend_${_msgSendFuncs.length}',
+      originalName: 'objc_msgSend',
+      returnType: returnType,
+      parameters: [
+        Parameter(name: 'obj', type: PointerType(objCObjectType)),
+        Parameter(name: 'sel', type: PointerType(objCSelType)),
+        for (final p in params) Parameter(name: p.name, type: p.type),
+      ],
+      isInternal: true,
+    );
+    return _msgSendFuncs[key]!;
+  }
+
+  bool utilsExist = false;
+  void ensureUtilsExist(Writer w, StringBuffer s) {
+    if (utilsExist) return;
+    utilsExist = true;
+
+    registerName = w.topLevelUniqueNamer.makeUnique('_registerName');
+    final selType = _registerNameFunc.functionType.returnType.getCType(w);
+    s.write('\n$selType $registerName(${w.className} _lib, String name) {\n');
+    s.write('  final cstr = name.toNativeUtf8();\n');
+    s.write('  final sel = _lib.${_registerNameFunc.name}(cstr.cast());\n');
+    s.write('  ${w.ffiPkgLibraryPrefix}.calloc.free(cstr);\n');
+    s.write('  return sel;\n');
+    s.write('}\n');
+
+    getClass = w.topLevelUniqueNamer.makeUnique('_getClass');
+    final objType = _getClassFunc.functionType.returnType.getCType(w);
+    s.write('\n$objType $getClass(${w.className} _lib, String name) {\n');
+    s.write('  final cstr = name.toNativeUtf8();\n');
+    s.write('  final clazz = _lib.${_getClassFunc.name}(cstr.cast());\n');
+    s.write('  ${w.ffiPkgLibraryPrefix}.calloc.free(cstr);\n');
+    s.write('  return clazz;\n');
+    s.write('}\n');
+
+    s.write('\nclass _ObjCWrapper {\n');
+    s.write('  final $objType _id;\n');
+    s.write('  final ${w.className} _lib;\n');
+    s.write('  _ObjCWrapper._(this._id, this._lib);\n');
+    s.write('}\n');
+  }
+
+  void addDependencies(Set<Binding> dependencies) {
+    _registerNameFunc.addDependencies(dependencies);
+    _getClassFunc.addDependencies(dependencies);
+    for (final func in _msgSendFuncs.values) {
+      func.addDependencies(dependencies);
+    }
+  }
+
+  void generateNSStringUtils(Writer w, StringBuffer s) {
+    // Generate a constructor that wraps stringWithCString.
+    s.write('  factory NSString(${w.className} _lib, String str) {\n');
+    s.write('    final cstr = str.toNativeUtf8();\n');
+    s.write('    final nsstr = stringWithCString_encoding('
+        '_lib, cstr.cast(), 4 /* UTF8 */);\n');
+    s.write('    ${w.ffiPkgLibraryPrefix}.calloc.free(cstr);\n');
+    s.write('    return nsstr;\n');
+    s.write('  }\n\n');
+
+    // Generate a toString method that wraps UTF8String.
+    s.write('  @override\n');
+    s.write('  String toString() => UTF8String().cast<'
+        '${w.ffiPkgLibraryPrefix}.Utf8>().toDartString();\n\n');
+  }
+
+  void generateStringUtils(Writer w, StringBuffer s) {
+    // Generate an extension on String to convert to NSString
+    s.write('extension StringToNSString on String {\n');
+    s.write('  NSString toNSString(${w.className} lib) => '
+        'NSString(lib, this);\n');
+    s.write('}\n\n');
+  }
+}
diff --git a/pkgs/ffigen/lib/src/code_generator/objc_interface.dart b/pkgs/ffigen/lib/src/code_generator/objc_interface.dart
index 4b59379..ed9e1c3 100644
--- a/pkgs/ffigen/lib/src/code_generator/objc_interface.dart
+++ b/pkgs/ffigen/lib/src/code_generator/objc_interface.dart
@@ -30,112 +30,6 @@
   'superclass',
 };
 
-class _ObjCBuiltInFunctions {
-  late final _registerNameFunc = Func(
-    name: '_sel_registerName',
-    originalName: 'sel_registerName',
-    returnType: PointerType(objCSelType),
-    parameters: [Parameter(name: 'str', type: PointerType(charType))],
-    isInternal: true,
-  );
-  late final String registerName;
-
-  late final _getClassFunc = Func(
-    name: '_objc_getClass',
-    originalName: 'objc_getClass',
-    returnType: PointerType(objCObjectType),
-    parameters: [Parameter(name: 'str', type: PointerType(charType))],
-    isInternal: true,
-  );
-  late final String getClass;
-
-  // We need to load a separate instance of objc_msgSend for each signature.
-  final _msgSendFuncs = <String, Func>{};
-  Func getMsgSendFunc(Type returnType, List<ObjCMethodParam> params) {
-    // TODO(#279): These keys don't dedupe sufficiently.
-    var key = returnType.hashCode.toRadixString(36);
-    for (final p in params) {
-      key += ' ' + p.type.hashCode.toRadixString(36);
-    }
-    _msgSendFuncs[key] ??= Func(
-      name: '_objc_msgSend_${_msgSendFuncs.length}',
-      originalName: 'objc_msgSend',
-      returnType: returnType,
-      parameters: [
-        Parameter(name: 'obj', type: PointerType(objCObjectType)),
-        Parameter(name: 'sel', type: PointerType(objCSelType)),
-        for (final p in params) Parameter(name: p.name, type: p.type),
-      ],
-      isInternal: true,
-    );
-    return _msgSendFuncs[key]!;
-  }
-
-  bool utilsExist = false;
-  void ensureUtilsExist(Writer w, StringBuffer s) {
-    if (utilsExist) return;
-    utilsExist = true;
-
-    registerName = w.topLevelUniqueNamer.makeUnique('_registerName');
-    final selType = _registerNameFunc.functionType.returnType.getCType(w);
-    s.write('\n$selType $registerName(${w.className} _lib, String name) {\n');
-    s.write('  final cstr = name.toNativeUtf8();\n');
-    s.write('  final sel = _lib.${_registerNameFunc.name}(cstr.cast());\n');
-    s.write('  ${w.ffiPkgLibraryPrefix}.calloc.free(cstr);\n');
-    s.write('  return sel;\n');
-    s.write('}\n');
-
-    getClass = w.topLevelUniqueNamer.makeUnique('_getClass');
-    final objType = _getClassFunc.functionType.returnType.getCType(w);
-    s.write('\n$objType $getClass(${w.className} _lib, String name) {\n');
-    s.write('  final cstr = name.toNativeUtf8();\n');
-    s.write('  final clazz = _lib.${_getClassFunc.name}(cstr.cast());\n');
-    s.write('  ${w.ffiPkgLibraryPrefix}.calloc.free(cstr);\n');
-    s.write('  return clazz;\n');
-    s.write('}\n');
-
-    s.write('\nclass _ObjCWrapper {\n');
-    s.write('  final $objType _id;\n');
-    s.write('  final ${w.className} _lib;\n');
-    s.write('  _ObjCWrapper._(this._id, this._lib);\n');
-    s.write('}\n');
-  }
-
-  void addDependencies(Set<Binding> dependencies) {
-    _registerNameFunc.addDependencies(dependencies);
-    _getClassFunc.addDependencies(dependencies);
-    for (final func in _msgSendFuncs.values) {
-      func.addDependencies(dependencies);
-    }
-  }
-
-  void generateNSStringUtils(Writer w, StringBuffer s) {
-    // Generate a constructor that wraps stringWithCString.
-    s.write('  factory NSString(${w.className} _lib, String str) {\n');
-    s.write('    final cstr = str.toNativeUtf8();\n');
-    s.write('    final nsstr = stringWithCString_encoding('
-        '_lib, cstr.cast(), 4 /* UTF8 */);\n');
-    s.write('    ${w.ffiPkgLibraryPrefix}.calloc.free(cstr);\n');
-    s.write('    return nsstr;\n');
-    s.write('  }\n\n');
-
-    // Generate a toString method that wraps UTF8String.
-    s.write('  @override\n');
-    s.write('  String toString() => UTF8String().cast<'
-        '${w.ffiPkgLibraryPrefix}.Utf8>().toDartString();\n\n');
-  }
-
-  void generateStringUtils(Writer w, StringBuffer s) {
-    // Generate an extension on String to convert to NSString
-    s.write('extension StringToNSString on String {\n');
-    s.write('  NSString toNSString(${w.className} lib) => '
-        'NSString(lib, this);\n');
-    s.write('}\n\n');
-  }
-}
-
-final _builtInFunctions = _ObjCBuiltInFunctions();
-
 class ObjCInterface extends BindingType {
   ObjCInterface? superType;
   final methods = <ObjCMethod>[];
@@ -148,11 +42,14 @@
   // methods list.
   final classMethods = <String, ObjCMethod>{};
 
+  final ObjCBuiltInFunctions builtInFunctions;
+
   ObjCInterface({
     String? usr,
     required String originalName,
     required String name,
     String? dartDoc,
+    required this.builtInFunctions,
   }) : super(
           usr: usr,
           originalName: originalName,
@@ -184,7 +81,7 @@
     final uniqueNamer = UniqueNamer({name});
     final natLib = w.className;
 
-    _builtInFunctions.ensureUtilsExist(w, s);
+    builtInFunctions.ensureUtilsExist(w, s);
     final objType = PointerType(objCObjectType).getCType(w);
     final selType = PointerType(objCSelType).getCType(w);
 
@@ -204,7 +101,7 @@
     s.write('  }\n\n');
 
     if (isNSString) {
-      _builtInFunctions.generateNSStringUtils(w, s);
+      builtInFunctions.generateNSStringUtils(w, s);
     }
 
     // Methods.
@@ -273,10 +170,10 @@
       // Implementation.
       if (isStatic) {
         s.write('    $classObject ??= '
-            '${_builtInFunctions.getClass}(_lib, "$originalName");\n');
+            '${builtInFunctions.getClass}(_lib, "$originalName");\n');
       }
       s.write('    $selName ??= '
-          '${_builtInFunctions.registerName}(_lib, "${m.originalName}");\n');
+          '${builtInFunctions.registerName}(_lib, "${m.originalName}");\n');
       final convertReturn = m.kind != ObjCMethodKind.propertySetter &&
           _needsConverting(returnType);
 
@@ -301,7 +198,7 @@
     s.write('}\n\n');
 
     if (isNSString) {
-      _builtInFunctions.generateStringUtils(w, s);
+      builtInFunctions.generateStringUtils(w, s);
     }
 
     return BindingString(
@@ -325,10 +222,10 @@
     }
 
     for (final m in methods) {
-      m.addDependencies(dependencies);
+      m.addDependencies(dependencies, builtInFunctions);
     }
 
-    _builtInFunctions.addDependencies(dependencies);
+    builtInFunctions.addDependencies(dependencies);
   }
 
   void _filterPropertyMethods() {
@@ -469,13 +366,14 @@
       kind == ObjCMethodKind.propertyGetter ||
       kind == ObjCMethodKind.propertySetter;
 
-  void addDependencies(Set<Binding> dependencies) {
+  void addDependencies(
+      Set<Binding> dependencies, ObjCBuiltInFunctions builtInFunctions) {
     returnType ??= NativeType(SupportedNativeType.Void);
     returnType!.addDependencies(dependencies);
     for (final p in params) {
       p.type.addDependencies(dependencies);
     }
-    msgSend = _builtInFunctions.getMsgSendFunc(returnType!, params);
+    msgSend = builtInFunctions.getMsgSendFunc(returnType!, params);
   }
 
   String _getDartMethodName(UniqueNamer uniqueNamer) {
diff --git a/pkgs/ffigen/lib/src/header_parser/data.dart b/pkgs/ffigen/lib/src/header_parser/data.dart
index ac16572..1381fd3 100644
--- a/pkgs/ffigen/lib/src/header_parser/data.dart
+++ b/pkgs/ffigen/lib/src/header_parser/data.dart
@@ -4,7 +4,8 @@
 
 import 'dart:ffi';
 
-import 'package:ffigen/src/code_generator.dart' show Constant;
+import 'package:ffigen/src/code_generator.dart'
+    show Constant, ObjCBuiltInFunctions;
 import 'package:ffigen/src/config_provider.dart' show Config;
 import 'clang_bindings/clang_bindings.dart' show Clang;
 
@@ -36,6 +37,10 @@
 List<Constant> get unnamedEnumConstants => _unnamedEnumConstants;
 List<Constant> _unnamedEnumConstants = [];
 
+/// Built in functions used by the Objective C bindings.
+ObjCBuiltInFunctions get objCBuiltInFunctions => _objCBuiltInFunctions;
+late ObjCBuiltInFunctions _objCBuiltInFunctions;
+
 void initializeGlobals({required Config config}) {
   _config = config;
   _clang = Clang(DynamicLibrary.open(config.libclangDylib));
@@ -43,4 +48,5 @@
   _savedMacros = {};
   _unnamedEnumConstants = [];
   _bindingsIndex = BindingsIndex();
+  _objCBuiltInFunctions = ObjCBuiltInFunctions();
 }
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 26dd1b3..d90f56c 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
@@ -45,6 +45,7 @@
     usr: itfUsr, originalName: name,
     name: name, // TODO(#279): config.interfaceDecl.renameUsingConfig(name),
     dartDoc: getCursorDocComment(cursor),
+    builtInFunctions: objCBuiltInFunctions,
   );
 }