[ffigen] Add a registry for ObjC built in interfaces (#358)

* ObjC interface registry

* Don't remove trailing underscores from method names

* Move isInSystemHeader to cursor utils

* Fix analysis

* Merge
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
index ebb48aa..23cbb7a 100644
--- a/pkgs/ffigen/lib/src/code_generator/objc_built_in_functions.dart
+++ b/pkgs/ffigen/lib/src/code_generator/objc_built_in_functions.dart
@@ -172,6 +172,11 @@
     }
   }
 
+  final _interfaceRegistry = <String, ObjCInterface>{};
+  void registerInterface(ObjCInterface interface) {
+    _interfaceRegistry[interface.originalName] = interface;
+  }
+
   void generateNSStringUtils(Writer w, StringBuffer s) {
     // Generate a constructor that wraps stringWithCString.
     s.write('  factory NSString(${w.className} _lib, String str) {\n');
diff --git a/pkgs/ffigen/lib/src/code_generator/objc_interface.dart b/pkgs/ffigen/lib/src/code_generator/objc_interface.dart
index 0879e94..e92b4e3 100644
--- a/pkgs/ffigen/lib/src/code_generator/objc_interface.dart
+++ b/pkgs/ffigen/lib/src/code_generator/objc_interface.dart
@@ -42,6 +42,7 @@
   bool filled = false;
 
   final ObjCBuiltInFunctions builtInFunctions;
+  final bool isBuiltIn;
   late final ObjCInternalGlobal _classObject;
 
   ObjCInterface({
@@ -50,6 +51,7 @@
     required String name,
     String? dartDoc,
     required this.builtInFunctions,
+    required this.isBuiltIn,
   }) : super(
           usr: usr,
           originalName: originalName,
@@ -57,7 +59,7 @@
           dartDoc: dartDoc,
         );
 
-  bool get isNSString => name == "NSString";
+  bool get isNSString => isBuiltIn && originalName == "NSString";
 
   @override
   BindingString toBindingString(Writer w) {
@@ -207,6 +209,10 @@
     dependencies.add(this);
     builtInFunctions.addDependencies(dependencies);
 
+    if (isBuiltIn) {
+      builtInFunctions.registerInterface(this);
+    }
+
     _classObject = ObjCInternalGlobal(
         PointerType(objCObjectType),
         '_class_$originalName',
diff --git a/pkgs/ffigen/lib/src/header_parser/clang_bindings/clang_bindings.dart b/pkgs/ffigen/lib/src/header_parser/clang_bindings/clang_bindings.dart
index 9191f67..9077b89 100644
--- a/pkgs/ffigen/lib/src/header_parser/clang_bindings/clang_bindings.dart
+++ b/pkgs/ffigen/lib/src/header_parser/clang_bindings/clang_bindings.dart
@@ -143,6 +143,22 @@
   late final _clang_getFileName =
       _clang_getFileNamePtr.asFunction<CXString Function(CXFile)>();
 
+  /// Returns non-zero if the given source location is in a system header.
+  int clang_Location_isInSystemHeader(
+    CXSourceLocation location,
+  ) {
+    return _clang_Location_isInSystemHeader(
+      location,
+    );
+  }
+
+  late final _clang_Location_isInSystemHeaderPtr =
+      _lookup<ffi.NativeFunction<pkg_ffi.Int Function(CXSourceLocation)>>(
+          'clang_Location_isInSystemHeader');
+  late final _clang_Location_isInSystemHeader =
+      _clang_Location_isInSystemHeaderPtr
+          .asFunction<int Function(CXSourceLocation)>();
+
   /// Determine whether two ranges are equivalent.
   ///
   /// \returns non-zero if the ranges are the same, zero if they differ.
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 03d7325..be660df 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
@@ -53,6 +53,7 @@
     name: config.objcInterfaces.renameUsingConfig(name),
     dartDoc: getCursorDocComment(cursor),
     builtInFunctions: objCBuiltInFunctions,
+    isBuiltIn: cursor.isInSystemHeader(),
   );
 }
 
diff --git a/pkgs/ffigen/lib/src/header_parser/utils.dart b/pkgs/ffigen/lib/src/header_parser/utils.dart
index e8c60cf..7352642 100644
--- a/pkgs/ffigen/lib/src/header_parser/utils.dart
+++ b/pkgs/ffigen/lib/src/header_parser/utils.dart
@@ -101,24 +101,25 @@
     return clang.clang_getResultType(type());
   }
 
+  /// Returns the file name of the file that the cursor is inside.
   String sourceFileName() {
     final cxsource = clang.clang_getCursorLocation(this);
     final cxfilePtr = calloc<Pointer<Void>>();
-    final line = calloc<UnsignedInt>();
-    final column = calloc<UnsignedInt>();
-    final offset = calloc<UnsignedInt>();
 
     // Puts the values in these pointers.
-    clang.clang_getFileLocation(cxsource, cxfilePtr, line, column, offset);
+    clang.clang_getFileLocation(cxsource, cxfilePtr, nullptr, nullptr, nullptr);
     final s = clang.clang_getFileName(cxfilePtr.value).toStringAndDispose();
 
     calloc.free(cxfilePtr);
-    calloc.free(line);
-    calloc.free(column);
-    calloc.free(offset);
     return s;
   }
 
+  /// Returns whether the file that the cursor is inside is a system header.
+  bool isInSystemHeader() {
+    final location = clang.clang_getCursorLocation(this);
+    return clang.clang_Location_isInSystemHeader(location) != 0;
+  }
+
   /// Recursively print the AST, for debugging.
   void printAst([int maxDepth = 3]) {
     _printAstVisitorMaxDepth = maxDepth;