[jnigen] Refactor into Visitor pattern (https://github.com/dart-lang/jnigen/issues/200)

Introducing a top-level `Classes` class which includes all the classes we're trying to generate as yet another element, makes each step of the code generation pipeline, feel the same. For example exclusion of classes and methods are now both done via actually removing the objects instead of setting an `isIncluded` flag. 

This `Classes` object will go through a pipeline of:

1. `Excluder` – excludes the unwanted classes, the private ones and the ones specified by the user.
2. `Linker` – links up the objects together to create a graph.
3. `Renamer` – renames the classes, methods, fields, ... not to clash with other reserved names.
4. `DartGenerator` – generates and writes to files.
diff --git a/pkgs/jnigen/analysis_options.yaml b/pkgs/jnigen/analysis_options.yaml
index 00d47a9..063bacc 100644
--- a/pkgs/jnigen/analysis_options.yaml
+++ b/pkgs/jnigen/analysis_options.yaml
@@ -15,4 +15,6 @@
     - prefer_final_locals
     - prefer_const_declarations
     - unawaited_futures
+    - prefer_const_constructors
+    - prefer_relative_imports
 
diff --git a/pkgs/jnigen/example/in_app_java/lib/android_utils.dart b/pkgs/jnigen/example/in_app_java/lib/android_utils.dart
index 082b105..75abb32 100644
--- a/pkgs/jnigen/example/in_app_java/lib/android_utils.dart
+++ b/pkgs/jnigen/example/in_app_java/lib/android_utils.dart
@@ -10,7 +10,9 @@
 // ignore_for_file: overridden_fields
 // ignore_for_file: unnecessary_cast
 // ignore_for_file: unused_element
+// ignore_for_file: unused_field
 // ignore_for_file: unused_import
+// ignore_for_file: unused_shown_name
 
 import "dart:isolate" show ReceivePort;
 import "dart:ffi" as ffi;
@@ -34,7 +36,6 @@
 
   /// The type which includes information such as the signature of this class.
   static const type = $AndroidUtilsType();
-
   static final _showToast = jniLookup<
           ffi.NativeFunction<
               jni.JniResult Function(ffi.Pointer<ffi.Void>,
@@ -82,7 +83,6 @@
 
   /// The type which includes information such as the signature of this class.
   static const type = $BuildType();
-
   static final _get_BOARD =
       jniLookup<ffi.NativeFunction<jni.JniResult Function()>>(
               "get_Build__BOARD")
@@ -339,7 +339,7 @@
       const jni.JStringType().fromRef(_get_TYPE().object);
 
   /// from: static public final java.lang.String UNKNOWN
-  static const UNKNOWN = "unknown";
+  static const UNKNOWN = r"""unknown""";
 
   static final _get_USER =
       jniLookup<ffi.NativeFunction<jni.JniResult Function()>>("get_Build__USER")
@@ -355,6 +355,7 @@
           .asFunction<jni.JniResult Function()>();
 
   /// from: public void <init>()
+  /// The returned object must be deleted after use, by calling the `delete` method.
   Build() : super.fromRef(_ctor().object);
 
   static final _getSerial =
@@ -414,10 +415,7 @@
     extends jni.JObject {
   late final jni.JObjType? _$type;
   @override
-  jni.JObjType get $type => _$type ??= type(
-        $K,
-        $V,
-      );
+  jni.JObjType get $type => _$type ??= type($K, $V);
 
   final jni.JObjType<K> $K;
   final jni.JObjType<V> $V;
@@ -445,6 +443,7 @@
       .asFunction<jni.JniResult Function(int, double)>();
 
   /// from: public void <init>(int i, float f)
+  /// The returned object must be deleted after use, by calling the `delete` method.
   HashMap(this.$K, this.$V, int i, double f)
       : super.fromRef(_ctor(i, f).object);
 
@@ -454,6 +453,7 @@
           .asFunction<jni.JniResult Function(int)>();
 
   /// from: public void <init>(int i)
+  /// The returned object must be deleted after use, by calling the `delete` method.
   HashMap.ctor1(this.$K, this.$V, int i) : super.fromRef(_ctor1(i).object);
 
   static final _ctor2 =
@@ -461,6 +461,7 @@
           .asFunction<jni.JniResult Function()>();
 
   /// from: public void <init>()
+  /// The returned object must be deleted after use, by calling the `delete` method.
   HashMap.ctor2(this.$K, this.$V) : super.fromRef(_ctor2().object);
 
   static final _ctor3 = jniLookup<
@@ -469,6 +470,7 @@
       .asFunction<jni.JniResult Function(ffi.Pointer<ffi.Void>)>();
 
   /// from: public void <init>(java.util.Map map)
+  /// The returned object must be deleted after use, by calling the `delete` method.
   HashMap.ctor3(this.$K, this.$V, jni.JObject map)
       : super.fromRef(_ctor3(map.reference).object);
 
diff --git a/pkgs/jnigen/example/kotlin_plugin/lib/kotlin_bindings.dart b/pkgs/jnigen/example/kotlin_plugin/lib/kotlin_bindings.dart
index e5c8331..f508dac 100644
--- a/pkgs/jnigen/example/kotlin_plugin/lib/kotlin_bindings.dart
+++ b/pkgs/jnigen/example/kotlin_plugin/lib/kotlin_bindings.dart
@@ -10,7 +10,9 @@
 // ignore_for_file: overridden_fields
 // ignore_for_file: unnecessary_cast
 // ignore_for_file: unused_element
+// ignore_for_file: unused_field
 // ignore_for_file: unused_import
+// ignore_for_file: unused_shown_name
 
 import "dart:isolate" show ReceivePort;
 import "dart:ffi" as ffi;
@@ -34,12 +36,12 @@
 
   /// The type which includes information such as the signature of this class.
   static const type = $ExampleType();
-
   static final _ctor =
       jniLookup<ffi.NativeFunction<jni.JniResult Function()>>("Example__ctor")
           .asFunction<jni.JniResult Function()>();
 
   /// from: public void <init>()
+  /// The returned object must be deleted after use, by calling the `delete` method.
   Example() : super.fromRef(_ctor().object);
 
   static final _thinkBeforeAnswering = jniLookup<
@@ -54,8 +56,8 @@
   /// The returned object must be deleted after use, by calling the `delete` method.
   Future<jni.JString> thinkBeforeAnswering() async {
     final $p = ReceivePort();
-    final $c = jni.Jni.newPortContinuation($p);
-    _thinkBeforeAnswering(reference, $c).object;
+    final $c = jni.JObject.fromRef(jni.Jni.newPortContinuation($p));
+    _thinkBeforeAnswering(reference, $c.reference).object;
     final $o = jni.JObjectPtr.fromAddress(await $p.first);
     final $k = const jni.JStringType().getClass().reference;
     if (jni.Jni.env.IsInstanceOf($o, $k) == 0) {
diff --git a/pkgs/jnigen/example/notification_plugin/lib/notifications.dart b/pkgs/jnigen/example/notification_plugin/lib/notifications.dart
index 853f2a9..028904a 100644
--- a/pkgs/jnigen/example/notification_plugin/lib/notifications.dart
+++ b/pkgs/jnigen/example/notification_plugin/lib/notifications.dart
@@ -14,7 +14,9 @@
 // ignore_for_file: overridden_fields
 // ignore_for_file: unnecessary_cast
 // ignore_for_file: unused_element
+// ignore_for_file: unused_field
 // ignore_for_file: unused_import
+// ignore_for_file: unused_shown_name
 
 import "dart:isolate" show ReceivePort;
 import "dart:ffi" as ffi;
@@ -38,12 +40,12 @@
 
   /// The type which includes information such as the signature of this class.
   static const type = $NotificationsType();
-
   static final _ctor = jniLookup<ffi.NativeFunction<jni.JniResult Function()>>(
           "Notifications__ctor")
       .asFunction<jni.JniResult Function()>();
 
   /// from: public void <init>()
+  /// The returned object must be deleted after use, by calling the `delete` method.
   Notifications() : super.fromRef(_ctor().object);
 
   static final _showNotification = jniLookup<
diff --git a/pkgs/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/pdmodel/PDDocument.dart b/pkgs/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/pdmodel/PDDocument.dart
index 3ff6a53..15e060e 100644
--- a/pkgs/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/pdmodel/PDDocument.dart
+++ b/pkgs/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/pdmodel/PDDocument.dart
@@ -28,7 +28,9 @@
 // ignore_for_file: overridden_fields
 // ignore_for_file: unnecessary_cast
 // ignore_for_file: unused_element
+// ignore_for_file: unused_field
 // ignore_for_file: unused_import
+// ignore_for_file: unused_shown_name
 
 import "dart:isolate" show ReceivePort;
 import "dart:ffi" as ffi;
@@ -36,7 +38,7 @@
 import "package:jni/jni.dart" as jni;
 
 import "PDDocumentInformation.dart" as pddocumentinformation_;
-import "../../../../_init.dart" show jniLookup;
+import "../../../../_init.dart";
 
 /// from: org.apache.pdfbox.pdmodel.PDDocument
 ///
@@ -54,12 +56,12 @@
 
   /// The type which includes information such as the signature of this class.
   static const type = $PDDocumentType();
-
   static final _ctor = jniLookup<ffi.NativeFunction<jni.JniResult Function()>>(
           "PDDocument__ctor")
       .asFunction<jni.JniResult Function()>();
 
   /// from: public void <init>()
+  /// The returned object must be deleted after use, by calling the `delete` method.
   ///
   /// Creates an empty PDF document.
   /// You need to add at least one page for the document to be valid.
@@ -72,6 +74,7 @@
       .asFunction<jni.JniResult Function(ffi.Pointer<ffi.Void>)>();
 
   /// from: public void <init>(org.apache.pdfbox.io.MemoryUsageSetting memUsageSetting)
+  /// The returned object must be deleted after use, by calling the `delete` method.
   ///
   /// Creates an empty PDF document.
   /// You need to add at least one page for the document to be valid.
@@ -86,6 +89,7 @@
       .asFunction<jni.JniResult Function(ffi.Pointer<ffi.Void>)>();
 
   /// from: public void <init>(org.apache.pdfbox.cos.COSDocument doc)
+  /// The returned object must be deleted after use, by calling the `delete` method.
   ///
   /// Constructor that uses an existing document. The COSDocument that is passed in must be valid.
   ///@param doc The COSDocument that this document wraps.
@@ -101,6 +105,7 @@
               ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>)>();
 
   /// from: public void <init>(org.apache.pdfbox.cos.COSDocument doc, org.apache.pdfbox.io.RandomAccessRead source)
+  /// The returned object must be deleted after use, by calling the `delete` method.
   ///
   /// Constructor that uses an existing document. The COSDocument that is passed in must be valid.
   ///@param doc The COSDocument that this document wraps.
@@ -119,6 +124,7 @@
               ffi.Pointer<ffi.Void>)>();
 
   /// from: public void <init>(org.apache.pdfbox.cos.COSDocument doc, org.apache.pdfbox.io.RandomAccessRead source, org.apache.pdfbox.pdmodel.encryption.AccessPermission permission)
+  /// The returned object must be deleted after use, by calling the `delete` method.
   ///
   /// Constructor that uses an existing document. The COSDocument that is passed in must be valid.
   ///@param doc The COSDocument that this document wraps.
diff --git a/pkgs/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/pdmodel/PDDocumentInformation.dart b/pkgs/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/pdmodel/PDDocumentInformation.dart
index 0ea6ad6..30e4076 100644
--- a/pkgs/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/pdmodel/PDDocumentInformation.dart
+++ b/pkgs/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/pdmodel/PDDocumentInformation.dart
@@ -28,14 +28,16 @@
 // ignore_for_file: overridden_fields
 // ignore_for_file: unnecessary_cast
 // ignore_for_file: unused_element
+// ignore_for_file: unused_field
 // ignore_for_file: unused_import
+// ignore_for_file: unused_shown_name
 
 import "dart:isolate" show ReceivePort;
 import "dart:ffi" as ffi;
 import "package:jni/internal_helpers_for_jnigen.dart";
 import "package:jni/jni.dart" as jni;
 
-import "../../../../_init.dart" show jniLookup;
+import "../../../../_init.dart";
 
 /// from: org.apache.pdfbox.pdmodel.PDDocumentInformation
 ///
@@ -55,12 +57,12 @@
 
   /// The type which includes information such as the signature of this class.
   static const type = $PDDocumentInformationType();
-
   static final _ctor = jniLookup<ffi.NativeFunction<jni.JniResult Function()>>(
           "PDDocumentInformation__ctor")
       .asFunction<jni.JniResult Function()>();
 
   /// from: public void <init>()
+  /// The returned object must be deleted after use, by calling the `delete` method.
   ///
   /// Default Constructor.
   PDDocumentInformation() : super.fromRef(_ctor().object);
@@ -72,6 +74,7 @@
       .asFunction<jni.JniResult Function(ffi.Pointer<ffi.Void>)>();
 
   /// from: public void <init>(org.apache.pdfbox.cos.COSDictionary dic)
+  /// The returned object must be deleted after use, by calling the `delete` method.
   ///
   /// Constructor that is used for a preexisting dictionary.
   ///@param dic The underlying dictionary.
diff --git a/pkgs/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/text/PDFTextStripper.dart b/pkgs/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/text/PDFTextStripper.dart
index 2094a0b..aff6055 100644
--- a/pkgs/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/text/PDFTextStripper.dart
+++ b/pkgs/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/text/PDFTextStripper.dart
@@ -28,7 +28,9 @@
 // ignore_for_file: overridden_fields
 // ignore_for_file: unnecessary_cast
 // ignore_for_file: unused_element
+// ignore_for_file: unused_field
 // ignore_for_file: unused_import
+// ignore_for_file: unused_shown_name
 
 import "dart:isolate" show ReceivePort;
 import "dart:ffi" as ffi;
@@ -36,7 +38,7 @@
 import "package:jni/jni.dart" as jni;
 
 import "../pdmodel/PDDocument.dart" as pddocument_;
-import "../../../../_init.dart" show jniLookup;
+import "../../../../_init.dart";
 
 /// from: org.apache.pdfbox.text.PDFTextStripper
 ///
@@ -58,7 +60,6 @@
 
   /// The type which includes information such as the signature of this class.
   static const type = $PDFTextStripperType();
-
   static final _get_LINE_SEPARATOR = jniLookup<
           ffi.NativeFunction<
               jni.JniResult Function(
@@ -86,6 +87,14 @@
     jni.JObjectPtr,
   )>();
 
+  static final _set_charactersByArticle = jniLookup<
+              ffi.NativeFunction<
+                  jni.JThrowablePtr Function(
+                      jni.JObjectPtr, ffi.Pointer<ffi.Void>)>>(
+          "set_PDFTextStripper__charactersByArticle")
+      .asFunction<
+          jni.JThrowablePtr Function(jni.JObjectPtr, ffi.Pointer<ffi.Void>)>();
+
   /// from: protected java.util.ArrayList<java.util.List<org.apache.pdfbox.text.TextPosition>> charactersByArticle
   /// The returned object must be deleted after use, by calling the `delete` method.
   ///
@@ -103,13 +112,6 @@
   /// Most PDFs won't have any beads, so charactersByArticle will contain a single entry.
   jni.JObject get charactersByArticle => const jni.JObjectType()
       .fromRef(_get_charactersByArticle(reference).object);
-  static final _set_charactersByArticle = jniLookup<
-              ffi.NativeFunction<
-                  jni.JThrowablePtr Function(
-                      jni.JObjectPtr, ffi.Pointer<ffi.Void>)>>(
-          "set_PDFTextStripper__charactersByArticle")
-      .asFunction<
-          jni.JThrowablePtr Function(jni.JObjectPtr, ffi.Pointer<ffi.Void>)>();
 
   /// from: protected java.util.ArrayList<java.util.List<org.apache.pdfbox.text.TextPosition>> charactersByArticle
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -139,10 +141,6 @@
     jni.JObjectPtr,
   )>();
 
-  /// from: protected org.apache.pdfbox.pdmodel.PDDocument document
-  /// The returned object must be deleted after use, by calling the `delete` method.
-  pddocument_.PDDocument get document => const pddocument_.$PDDocumentType()
-      .fromRef(_get_document(reference).object);
   static final _set_document = jniLookup<
           ffi.NativeFunction<
               jni.JThrowablePtr Function(jni.JObjectPtr,
@@ -152,6 +150,11 @@
 
   /// from: protected org.apache.pdfbox.pdmodel.PDDocument document
   /// The returned object must be deleted after use, by calling the `delete` method.
+  pddocument_.PDDocument get document => const pddocument_.$PDDocumentType()
+      .fromRef(_get_document(reference).object);
+
+  /// from: protected org.apache.pdfbox.pdmodel.PDDocument document
+  /// The returned object must be deleted after use, by calling the `delete` method.
   set document(pddocument_.PDDocument value) =>
       _set_document(reference, value.reference);
 
@@ -165,10 +168,6 @@
     jni.JObjectPtr,
   )>();
 
-  /// from: protected java.io.Writer output
-  /// The returned object must be deleted after use, by calling the `delete` method.
-  jni.JObject get output =>
-      const jni.JObjectType().fromRef(_get_output(reference).object);
   static final _set_output = jniLookup<
           ffi.NativeFunction<
               jni.JThrowablePtr Function(jni.JObjectPtr,
@@ -178,6 +177,11 @@
 
   /// from: protected java.io.Writer output
   /// The returned object must be deleted after use, by calling the `delete` method.
+  jni.JObject get output =>
+      const jni.JObjectType().fromRef(_get_output(reference).object);
+
+  /// from: protected java.io.Writer output
+  /// The returned object must be deleted after use, by calling the `delete` method.
   set output(jni.JObject value) => _set_output(reference, value.reference);
 
   static final _ctor = jniLookup<ffi.NativeFunction<jni.JniResult Function()>>(
@@ -185,6 +189,7 @@
       .asFunction<jni.JniResult Function()>();
 
   /// from: public void <init>()
+  /// The returned object must be deleted after use, by calling the `delete` method.
   ///
   /// Instantiate a new PDFTextStripper object.
   ///@throws IOException If there is an error loading the properties.
diff --git a/pkgs/jnigen/lib/src/bindings/bindings.dart b/pkgs/jnigen/lib/src/bindings/bindings.dart
deleted file mode 100644
index 27bd35e..0000000
--- a/pkgs/jnigen/lib/src/bindings/bindings.dart
+++ /dev/null
@@ -1,9 +0,0 @@
-// 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.
-
-export 'preprocessor.dart';
-export 'c_bindings.dart';
-export 'dart_bindings.dart';
-export 'pure_dart_bindings.dart';
-export 'symbol_resolver.dart';
diff --git a/pkgs/jnigen/lib/src/bindings/c_bindings.dart b/pkgs/jnigen/lib/src/bindings/c_bindings.dart
index 488e3d8..f57e79c 100644
--- a/pkgs/jnigen/lib/src/bindings/c_bindings.dart
+++ b/pkgs/jnigen/lib/src/bindings/c_bindings.dart
@@ -2,10 +2,9 @@
 // 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:jnigen/src/elements/elements.dart';
-import 'package:jnigen/src/config/config.dart';
-
-import 'common.dart';
+import '../config/config.dart';
+import '../elements/elements.dart';
+import 'c_generator.dart';
 
 class CBindingGenerator {
   static const classVarPrefix = '_c';
@@ -35,28 +34,19 @@
   String generateBinding(ClassDecl c) => _class(c);
 
   String _class(ClassDecl c) {
-    if (!c.isIncluded) {
-      return '';
-    }
     final s = StringBuffer();
-    final classNameInC = getUniqueClassName(c);
+    final classNameInC = c.uniqueName;
     // global variable in C that holds the reference to class
     final classVar = '${classVarPrefix}_$classNameInC';
     s.write('// ${c.binaryName}\n'
         'jclass $classVar = NULL;\n\n');
 
     for (var m in c.methods) {
-      if (!m.isIncluded) {
-        continue;
-      }
       s.write(_method(c, m));
       s.writeln();
     }
 
     for (var f in c.fields) {
-      if (!f.isIncluded) {
-        continue;
-      }
       final fieldBinding = _field(c, f);
       s.write(fieldBinding);
       // Fields are skipped if they're static final. In that case
@@ -66,19 +56,42 @@
     return s.toString();
   }
 
+  String getCType(String binaryName) {
+    switch (binaryName) {
+      case "void":
+        return "void";
+      case "byte":
+        return "int8_t";
+      case "char":
+        return "char";
+      case "double":
+        return "double";
+      case "float":
+        return "float";
+      case "int":
+        return "int32_t";
+      case "long":
+        return "int64_t";
+      case "short":
+        return "int16_t";
+      case "boolean":
+        return "uint8_t";
+      default:
+        return "jobject";
+    }
+  }
+
   String _method(ClassDecl c, Method m) {
-    final classNameInC = getUniqueClassName(c);
-    final isACtor = isCtor(m);
-    final isStatic = isStaticMethod(m);
+    final classNameInC = c.uniqueName;
+    final isACtor = m.isCtor;
+    final isStatic = m.isStatic;
 
     final s = StringBuffer();
-    final name = m.finalName;
-    final functionName = getMemberNameInC(c, name);
+    final cMethodName = m.accept(const CMethodName());
     final classRef = '${classVarPrefix}_$classNameInC';
-    final methodID = '${methodVarPrefix}_$functionName';
-    final cMethodName = getMemberNameInC(c, name);
+    final methodID = '${methodVarPrefix}_$cMethodName';
     final cMethodParams = _formalArgs(m);
-    final jniSignature = getJniSignatureForMethod(m);
+    final jniSignature = m.accept(const MethodSignature());
     final ifStaticMethodID = isStatic ? 'static_' : '';
 
     var javaReturnType = m.returnType.name;
@@ -102,7 +115,7 @@
 FFI_PLUGIN_EXPORT
 $jniResultType $cMethodName($cMethodParams) {
     $_loadEnvCall
-    ${_loadClassCall(classRef, getInternalName(c.binaryName))}
+    ${_loadClassCall(classRef, c.internalName)}
     load_${ifStaticMethodID}method($classRef,
       &$methodID, "${m.name}", "$jniSignature");
     if ($methodID == NULL) return $ifError;
@@ -113,16 +126,16 @@
   }
 
   String _field(ClassDecl c, Field f) {
-    final cClassName = getUniqueClassName(c);
-    final isStatic = isStaticField(f);
+    final cClassName = c.uniqueName;
+    final isStatic = f.isStatic;
 
     final fieldName = f.finalName;
-    final fieldNameInC = getMemberNameInC(c, fieldName);
+    final fieldNameInC = f.accept(const CFieldName());
     final fieldVar = "${fieldVarPrefix}_$fieldNameInC";
 
     // If the field is final and default is assigned, then no need to wrap
     // this field. It should then be a constant in dart code.
-    if (isStatic && isFinalField(f) && f.defaultValue != null) {
+    if (isStatic && f.isFinal && f.defaultValue != null) {
       return "";
     }
 
@@ -152,7 +165,7 @@
       } else {
         var getterExpr = '(*jniEnv)->Get$ifStaticCall${callType}Field(jniEnv, '
             '$objectArgument, $fieldVar)';
-        if (!isPrimitive(f.type)) {
+        if (f.type.kind != Kind.primitive) {
           getterExpr = 'to_global_ref($getterExpr)';
         }
         final cResultType = getCType(f.type.name);
@@ -166,15 +179,15 @@
 FFI_PLUGIN_EXPORT
 $cReturnType ${cMethodPrefix}_$fieldNameInC($formalArgs) {
     $_loadEnvCall
-    ${_loadClassCall(classVar, getInternalName(c.binaryName))}
+    ${_loadClassCall(classVar, c.internalName)}
     load_${ifStaticField}field($classVar, &$fieldVar, "$fieldName",
-      "${getDescriptor(f.type)}");
+      "${f.type.accept(const Descriptor())}");
 $accessorStatements
 }\n\n''');
     }
 
     writeAccessor(isSetter: false);
-    if (isFinalField(f)) {
+    if (f.isFinal) {
       return s.toString();
     }
     writeAccessor(isSetter: true);
@@ -190,7 +203,7 @@
 
   String _formalArgs(Method m) {
     final args = <String>[];
-    if (hasSelfParam(m)) {
+    if (!m.isCtor && !m.isStatic) {
       // The underscore-suffixed name prevents accidental collision with
       // parameter named self, if any.
       args.add('jobject self_');
@@ -216,7 +229,7 @@
       'double': 'd',
       'void': 'j', // in case of void return, just write 0 to largest field.
     };
-    if (isPrimitive(type)) {
+    if (type.kind == Kind.primitive) {
       return primitives[type.name]!;
     }
     return 'l';
@@ -225,7 +238,7 @@
   // Returns arguments at call site, concatenated by `,`.
   String _callArgs(Method m, String classVar, String methodVar) {
     final args = ['jniEnv'];
-    if (hasSelfParam(m)) {
+    if (!m.isCtor && !m.isStatic) {
       args.add('self_');
     } else {
       args.add(classVar);
@@ -242,7 +255,7 @@
     final cReturnType = getCType(m.returnType.name);
     String valuePart;
     String unionField;
-    if (cReturnType == 'jobject' || isCtor(m)) {
+    if (cReturnType == 'jobject' || m.isCtor) {
       unionField = 'l';
       valuePart = 'to_global_ref(_result)';
     } else if (cReturnType == 'void') {
@@ -261,7 +274,7 @@
   /// Returns capitalized java type name to be used as in call${type}Method
   /// or get${type}Field etc..
   String _typeNameAtCallSite(TypeUsage type) {
-    if (isPrimitive(type)) {
+    if (type.kind == Kind.primitive) {
       return type.name.substring(0, 1).toUpperCase() + type.name.substring(1);
     }
     return "Object";
diff --git a/pkgs/jnigen/lib/src/bindings/c_generator.dart b/pkgs/jnigen/lib/src/bindings/c_generator.dart
new file mode 100644
index 0000000..97b2604
--- /dev/null
+++ b/pkgs/jnigen/lib/src/bindings/c_generator.dart
@@ -0,0 +1,94 @@
+// Copyright (c) 2023, 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 '../elements/elements.dart';
+import 'visitor.dart';
+
+/// JVM representation of type signatures.
+///
+/// https://docs.oracle.com/en/java/javase/18/docs/specs/jni/types.html#type-signatures
+class Descriptor extends TypeVisitor<String> {
+  const Descriptor();
+
+  @override
+  String visitArrayType(ArrayType node) {
+    final inner = node.type.accept(this);
+    return '[$inner';
+  }
+
+  @override
+  String visitDeclaredType(DeclaredType node) {
+    final internalName = node.binaryName.replaceAll('.', '/');
+    return 'L$internalName;';
+  }
+
+  @override
+  String visitPrimitiveType(PrimitiveType node) {
+    return node.signature;
+  }
+
+  @override
+  String visitTypeVar(TypeVar node) {
+    // It should be possible to compute the erasure of a type
+    // in parser itself.
+    // TODO(#23): Use erasure of the type variable here.
+    // This is just a (wrong) placeholder
+    return super.visitTypeVar(node);
+  }
+
+  @override
+  String visitWildcard(Wildcard node) {
+    final extendsBound = node.extendsBound?.accept(this);
+    return extendsBound ?? 'Ljava/lang/Object;';
+  }
+
+  @override
+  String visitNonPrimitiveType(ReferredType node) {
+    return "Ljava/lang/Object;";
+  }
+}
+
+/// Generates JNI Method signatures.
+///
+/// https://docs.oracle.com/en/java/javase/18/docs/specs/jni/types.html#type-signatures
+/// Also see: [Descriptor]
+class MethodSignature extends Visitor<Method, String> {
+  const MethodSignature();
+
+  @override
+  String visit(Method node) {
+    final s = StringBuffer();
+    s.write('(');
+    s.write(node.params
+        .map((param) => param.type)
+        .accept(const Descriptor())
+        .join());
+    s.write(')');
+    final returnType = node.returnType.accept(const Descriptor());
+    s.write(returnType);
+    return s.toString();
+  }
+}
+
+class CFieldName extends Visitor<Field, String> {
+  const CFieldName();
+
+  @override
+  String visit(Field node) {
+    final className = node.classDecl.uniqueName;
+    final fieldName = node.finalName;
+    return '${className}__$fieldName';
+  }
+}
+
+class CMethodName extends Visitor<Method, String> {
+  const CMethodName();
+
+  @override
+  String visit(Method node) {
+    final className = node.classDecl.uniqueName;
+    final methodName = node.finalName;
+    return '${className}__$methodName';
+  }
+}
diff --git a/pkgs/jnigen/lib/src/bindings/common.dart b/pkgs/jnigen/lib/src/bindings/common.dart
deleted file mode 100644
index 480a038..0000000
--- a/pkgs/jnigen/lib/src/bindings/common.dart
+++ /dev/null
@@ -1,801 +0,0 @@
-// 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:jnigen/src/elements/elements.dart';
-import 'package:jnigen/src/util/rename_conflict.dart';
-
-import 'symbol_resolver.dart';
-
-/// Base class of both C based and Dart-only binding generators. Implements
-/// Many methods used commonly by both of them.
-///
-/// These methods are in a superclass since they usually require access
-/// to resolver, or for consistency with similar methods which require
-/// the resolver.
-abstract class BindingsGenerator {
-  // Name for reference in base class.
-  static const selfPointer = 'reference';
-  static final indent = ' ' * 2;
-
-  // import prefixes
-  static const ffi = 'ffi.';
-  static const jni = 'jni.';
-
-  static const String voidPointer = '${ffi}Pointer<${ffi}Void>';
-
-  static const String ffiVoidType = '${ffi}Void';
-
-  static const String jobjectType = '${jni}JObjectPtr';
-
-  static const String jthrowableType = '${jni}JThrowablePtr';
-
-  static const String jniStringType = '${jni}JString';
-  static const String jniStringTypeClass = '${jni}JString$typeClassSuffix';
-
-  static const String jniObjectType = '${jni}JObject';
-  static const String jniObjectTypeClass = '${jni}JObject$typeClassSuffix';
-
-  static const String jniArrayType = '${jni}JArray';
-  static const String jniArrayTypeClass = '${jni}JArray$typeClassSuffix';
-
-  static const String jniCallType = '${jni}JniCallType';
-
-  static const String jniTypeType = '${jni}JObjType';
-  static const String typeClassSuffix = 'Type';
-  // TODO(#143): this is a temporary fix for the name collision.
-  static const String typeClassPrefix = '\$';
-
-  static const String instanceTypeGetter = '\$type';
-
-  static const String typeParamPrefix = '\$';
-
-  static const String jniResultType = '${jni}JniResult';
-
-  /// Generate bindings string for given class declaration.
-  String generateBindings(ClassDecl decl, SymbolResolver resolver);
-
-  /// Get common initialization code for bindings.
-  ///
-  /// if this method returns an empty String, no init file is created.
-  String getInitFileContents();
-
-  /// Get boilerplate to be pasted on a file before any imports.
-  ///
-  /// [initFilePath], if provided, shall point to the _init.dart file where
-  /// initialization code is stored. If this is null, the method should provide
-  /// standalone boilerplate which doesn't need an init file.
-  ///
-  /// This method should return an empty string if no such boilerplate is
-  /// required.
-  String getPreImportBoilerplate([String? initFilePath]);
-
-  /// Get boilerplate to be pasted on a file before any imports.
-  ///
-  /// See [getPreImportBoilerplate] for an explanation of [initFilePath].
-  ///
-  /// This method should return an empty String if no such boilerplate is
-  /// required.
-  String getPostImportBoilerplate([String? initFilePath]);
-
-  /// Returns the formal parameters list of the generated function.
-  ///
-  /// This is the signature seen by the user.
-  String getFormalArgs(ClassDecl c, Method m, SymbolResolver resolver) {
-    final List<String> args = [];
-    // Prepending the parameters with type parameters
-    if (isCtor(m)) {
-      for (final typeParam in c.allTypeParams) {
-        args.add('this.$typeParamPrefix${typeParam.name}');
-      }
-    }
-    for (final typeParam in m.typeParams) {
-      args.add(
-          '$jniTypeType<${typeParam.name}> $typeParamPrefix${typeParam.name}');
-    }
-    for (final param in m.params) {
-      args.add(
-          '${getDartOuterType(param.type, resolver)} ${kwRename(param.name)}');
-    }
-    if (m.asyncReturnType != null) {
-      // Remove the continuation object in async methods.
-      args.removeLast();
-    }
-    return args.join(', ');
-  }
-
-  /// Actual arguments passed to native call.
-  String actualArgs(Method m, {bool addSelf = true}) {
-    final List<String> args = [if (hasSelfParam(m) && addSelf) selfPointer];
-    for (var param in m.params) {
-      final paramName = kwRename(param.name);
-      args.add(toNativeArg(paramName, param.type));
-    }
-    if (m.asyncReturnType != null) {
-      // Change the continuation object in async methods.
-      args.last = '\$c';
-    }
-    return args.join(', ');
-  }
-
-  String _dartTypeClassName(String className) {
-    return '$typeClassPrefix$className$typeClassSuffix';
-  }
-
-  String dartTypeParams(List<TypeParam> typeParams,
-      {required bool includeExtends}) {
-    if (typeParams.isEmpty) return '';
-    // TODO(#144): resolve the actual type being extended, if any.
-    final ifExtendsIncluded = includeExtends ? ' extends $jniObjectType' : '';
-    final args =
-        typeParams.map((e) => '${e.name}$ifExtendsIncluded').join(' ,');
-    return '<$args>';
-  }
-
-  String dartClassDefinition(ClassDecl decl, SymbolResolver resolver) {
-    final name = decl.finalName;
-    var superName = jniObjectType;
-    if (decl.superclass != null) {
-      superName = _dartType(decl.superclass!, resolver: resolver);
-    }
-    final typeParamsWithExtend =
-        dartTypeParams(decl.allTypeParams, includeExtends: true);
-    final ifSomeArgs =
-        decl.allTypeParams.isNotEmpty ? '(${_typeParamArgs(decl)})' : '';
-    return 'class $name$typeParamsWithExtend extends $superName {\n'
-        'late final $jniTypeType? _$instanceTypeGetter;\n'
-        '@override\n'
-        '$jniTypeType get $instanceTypeGetter => '
-        '_$instanceTypeGetter ??= type$ifSomeArgs;\n\n'
-        '${_typeParamDefs(decl)}\n'
-        '$indent$name.fromRef(\n'
-        '${_typeParamCtorArgs(decl)}'
-        '$jobjectType ref,'
-        '): super.fromRef(${dartSuperArgs(decl, resolver)}ref);\n\n';
-  }
-
-  String dartSigForField(Field f,
-      {bool isSetter = false, required bool isFfiSig}) {
-    final ref = f.modifiers.contains('static') ? '' : '$jobjectType, ';
-    final conv = isFfiSig ? getDartFfiType : getDartInnerType;
-    if (isSetter) {
-      return '$jthrowableType Function($ref${conv(f.type)})';
-    }
-    return '$jniResultType Function($ref)';
-  }
-
-  String dartSuperArgs(ClassDecl decl, SymbolResolver resolver) {
-    if (decl.superclass == null ||
-        resolver.resolve((decl.superclass!.type as DeclaredType).binaryName) ==
-            null) {
-      return '';
-    }
-    return (decl.superclass!.type as DeclaredType)
-        .params
-        .map((param) => '${getDartTypeClass(param, resolver)},')
-        .join();
-  }
-
-  String dartArrayExtension(ClassDecl decl) {
-    final name = decl.finalName;
-    final typeParamsWithExtend =
-        dartTypeParams(decl.allTypeParams, includeExtends: true);
-    final typeParams =
-        dartTypeParams(decl.allTypeParams, includeExtends: false);
-    final typeClassName = _dartTypeClassName(name);
-    return '\nextension \$${name}Array$typeParamsWithExtend on $jniArrayType<$name$typeParams> {\n'
-        '$indent$name$typeParams operator [](int index) {\n'
-        '${indent * 2}return (elementType as $typeClassName$typeParams)'
-        '.fromRef(elementAt(index, ${jni}JniCallType.objectType).object);\n'
-        '$indent}\n\n'
-        '${indent}void operator []=(int index, $name$typeParams value) {\n'
-        '${indent * 2}(this as $jniArrayType<$jniObjectType>)[index] = value;\n'
-        '$indent}\n'
-        '}\n';
-  }
-
-  String _typeParamDefs(ClassDecl decl) {
-    return decl.allTypeParams
-        .map((e) =>
-            '${indent}final $jniTypeType<${e.name}> $typeParamPrefix${e.name};\n')
-        .join();
-  }
-
-  String _typeParamCtorArgs(ClassDecl decl) {
-    return decl.allTypeParams
-        .map((e) => '${indent * 2}this.$typeParamPrefix${e.name},\n')
-        .join();
-  }
-
-  String _typeParamArgs(ClassDecl decl) {
-    return decl.allTypeParams.map((e) => '$typeParamPrefix${e.name}, ').join();
-  }
-
-  String dartTypeClass(ClassDecl decl) {
-    final name = decl.finalName;
-    final signature = getSignature(decl.binaryName);
-    final typeClassName = _dartTypeClassName(name);
-    final typeParamsWithExtend =
-        dartTypeParams(decl.allTypeParams, includeExtends: true);
-    final typeParams =
-        dartTypeParams(decl.allTypeParams, includeExtends: false);
-
-    return '\nclass $typeClassName$typeParamsWithExtend extends $jniTypeType<$name$typeParams> {\n'
-        '${_typeParamDefs(decl)}\n'
-        '${indent}const $typeClassName(\n'
-        '${_typeParamCtorArgs(decl)}'
-        '$indent);\n\n'
-        '$indent@override\n'
-        '${indent}String get signature => r"$signature";\n\n'
-        '$indent@override\n'
-        '$indent$name$typeParams fromRef($jobjectType ref) => $name.fromRef(${_typeParamArgs(decl)}ref);\n'
-        '}\n';
-  }
-
-  String dartInitType(ClassDecl decl) {
-    final typeClassName = _dartTypeClassName(decl.finalName);
-    final args =
-        decl.allTypeParams.map((e) => '$typeParamPrefix${e.name},').join();
-    return '$instanceTypeGetter = $typeClassName($args)';
-  }
-
-  String dartStaticTypeGetter(ClassDecl decl) {
-    final typeClassName = _dartTypeClassName(decl.finalName);
-    const docs =
-        '/// The type which includes information such as the signature of this class.';
-    if (decl.allTypeParams.isEmpty) {
-      return '$indent$docs\n'
-          '${indent}static const type = $typeClassName();\n\n';
-    }
-    final typeParamsWithExtend =
-        dartTypeParams(decl.allTypeParams, includeExtends: true);
-    final typeParams =
-        dartTypeParams(decl.allTypeParams, includeExtends: false);
-    final methodArgs = decl.allTypeParams
-        .map((e) =>
-            '${indent * 2}$jniTypeType<${e.name}> $typeParamPrefix${e.name},\n')
-        .join();
-    final ctorArgs = decl.allTypeParams
-        .map((e) => '${indent * 3}$typeParamPrefix${e.name},\n')
-        .join();
-    return '$indent$docs\n'
-        '${indent}static $typeClassName$typeParams type$typeParamsWithExtend(\n'
-        '$methodArgs'
-        '$indent) {\n'
-        '${indent * 2}return $typeClassName(\n'
-        '$ctorArgs'
-        '${indent * 2});\n'
-        '$indent}\n\n';
-  }
-
-  String dartSigForMethod(Method m, {required bool isFfiSig}) {
-    final conv = isFfiSig ? getDartFfiType : getDartInnerType;
-    final argTypes = [if (hasSelfParam(m)) voidPointer];
-    for (var param in m.params) {
-      argTypes.add(conv(param.type));
-    }
-    return '$jniResultType Function (${argTypes.join(", ")})';
-  }
-
-  String _dartType(
-    TypeUsage t, {
-    SymbolResolver? resolver,
-  }) {
-    // if resolver == null, looking for inner fn type, type of fn reference
-    // else looking for outer fn type, that's what user of the library sees.
-    const primitives = {
-      'byte': 'int',
-      'short': 'int',
-      'char': 'int',
-      'int': 'int',
-      'long': 'int',
-      'float': 'double',
-      'double': 'double',
-      'void': 'void',
-      'boolean': 'bool',
-    };
-    const jniTypes = {
-      'byte': 'JByte',
-      'short': 'JShort',
-      'char': 'JChar',
-      'int': 'JInt',
-      'long': 'JLong',
-      'float': 'JFloat',
-      'double': 'JDouble',
-      'boolean': 'JBoolean',
-    };
-    switch (t.kind) {
-      case Kind.primitive:
-        if (t.name == 'boolean' && resolver == null) return 'int';
-        return primitives[(t.type as PrimitiveType).name]!;
-      case Kind.typeVariable:
-        if (resolver != null) {
-          return t.name;
-        }
-        return voidPointer;
-      case Kind.wildcard:
-        throw SkipException('Wildcards are not yet supported');
-      case Kind.array:
-        if (resolver != null) {
-          final innerType = (t.type as ArrayType).type;
-          final dartType = innerType.kind == Kind.primitive
-              ? jni + jniTypes[(innerType.type as PrimitiveType).name]!
-              : _dartType(innerType, resolver: resolver);
-          return '$jniArrayType<$dartType>';
-        }
-        return voidPointer;
-      case Kind.declared:
-        if (resolver != null) {
-          final type = t.type as DeclaredType;
-          final resolved = resolver.resolve(type.binaryName);
-          final resolvedClass = resolver.resolveClass(type.binaryName);
-          if (resolved == null ||
-              resolvedClass == null ||
-              !resolvedClass.isIncluded) {
-            return jniObjectType;
-          }
-
-          // All type parameters of this type
-          final allTypeParams =
-              resolvedClass.allTypeParams.map((param) => param.name).toList();
-
-          // The ones that are declared.
-          final paramTypeClasses =
-              type.params.map((param) => _dartType(param, resolver: resolver));
-
-          // Replacing the declared ones. They come at the end.
-          // The rest will be JObject.
-          if (allTypeParams.length >= type.params.length) {
-            allTypeParams.replaceRange(
-              0,
-              allTypeParams.length - type.params.length,
-              List.filled(
-                allTypeParams.length - type.params.length,
-                jniObjectType,
-              ),
-            );
-            allTypeParams.replaceRange(
-              allTypeParams.length - type.params.length,
-              allTypeParams.length,
-              paramTypeClasses,
-            );
-          }
-
-          final args = allTypeParams.join(',');
-          final ifArgs = args.isNotEmpty ? '<$args>' : '';
-          return '$resolved$ifArgs';
-        }
-        return voidPointer;
-    }
-  }
-
-  String getDartTypeClass(
-    TypeUsage t,
-    SymbolResolver resolver,
-  ) {
-    return _getDartTypeClass(t, resolver, addConst: true).name;
-  }
-
-  _TypeClass _getDartTypeClass(
-    TypeUsage t,
-    SymbolResolver resolver, {
-    required bool addConst,
-  }) {
-    const primitives = {
-      'byte': 'JByteType',
-      'short': 'JShortType',
-      'char': 'JCharType',
-      'int': 'JIntType',
-      'long': 'JLongType',
-      'float': 'JFloatType',
-      'double': 'JDoubleType',
-      'boolean': 'JBooleanType',
-      'void': 'JVoidType', // This will never be in the generated code.
-    };
-    switch (t.kind) {
-      case Kind.primitive:
-        final ifConst = addConst ? 'const ' : '';
-        return _TypeClass(
-          '$ifConst$jni${primitives[(t.type as PrimitiveType).name]}()',
-          true,
-        );
-      case Kind.typeVariable:
-        return _TypeClass(
-          '$typeParamPrefix${(t.type as TypeVar).name}',
-          false,
-        );
-      case Kind.wildcard:
-        throw SkipException('Wildcards are not yet supported');
-      case Kind.array:
-        final innerType = (t.type as ArrayType).type;
-        final innerTypeClass = _getDartTypeClass(
-          innerType,
-          resolver,
-          addConst: false,
-        );
-        final ifConst = addConst && innerTypeClass.canBeConst ? 'const ' : '';
-        return _TypeClass(
-          '$ifConst$jniArrayTypeClass(${innerTypeClass.name})',
-          innerTypeClass.canBeConst,
-        );
-      case Kind.declared:
-        final type = (t.type as DeclaredType);
-        final resolved = resolver.resolve(type.binaryName);
-        final resolvedClass = resolver.resolveClass(type.binaryName);
-
-        if (resolved == null ||
-            resolvedClass == null ||
-            !resolvedClass.isIncluded) {
-          return _TypeClass(
-            '${addConst ? 'const ' : ''}$jniObjectTypeClass()',
-            true,
-          );
-        }
-
-        // All type params of this type
-        final allTypeParams = resolvedClass.allTypeParams
-            .map((param) => '$typeParamPrefix${param.name}')
-            .toList();
-
-        // The ones that are declared.
-        final paramTypeClasses = type.params.map(
-            (param) => _getDartTypeClass(param, resolver, addConst: false));
-
-        // Replacing the declared ones. They come at the end.
-        // The rest will be JObject.
-        if (allTypeParams.length >= type.params.length) {
-          allTypeParams.replaceRange(
-            0,
-            allTypeParams.length - type.params.length,
-            List.filled(
-              allTypeParams.length - type.params.length,
-              'const $jniObjectTypeClass()',
-            ),
-          );
-          allTypeParams.replaceRange(
-            allTypeParams.length - type.params.length,
-            allTypeParams.length,
-            paramTypeClasses.map((param) => param.name),
-          );
-        }
-
-        final args = allTypeParams.join(',');
-
-        final canBeConst = (allTypeParams.length == paramTypeClasses.length &&
-                paramTypeClasses.every((e) => e.canBeConst)) ||
-            allTypeParams.isEmpty;
-        final ifConst = addConst && canBeConst ? 'const ' : '';
-
-        if (resolved == jniObjectType) {
-          return _TypeClass('$ifConst$jniObjectTypeClass()', true);
-        } else if (resolved == jniStringType) {
-          return _TypeClass('$ifConst$jniStringTypeClass()', true);
-        } else if (resolved.contains('.')) {
-          // It is in form of jni.SomeClass which should be converted to jni.$SomeClassType
-          final dotIndex = resolved.indexOf('.');
-          final module = resolved.substring(0, dotIndex);
-          final clazz = resolved.substring(dotIndex + 1);
-          return _TypeClass(
-            '$ifConst$module.${_dartTypeClassName(clazz)}($args)',
-            canBeConst,
-          );
-        }
-        return _TypeClass(
-          '$ifConst${_dartTypeClassName(resolved)}($args)',
-          canBeConst,
-        );
-    }
-  }
-
-  /// Get corresponding Dart FFI type of Java type.
-  String getDartFfiType(TypeUsage t) {
-    const primitives = {
-      'byte': 'Int8',
-      'short': 'Int16',
-      'char': 'Uint16',
-      'int': 'Int32',
-      'long': 'Int64',
-      'float': 'Float',
-      'double': 'Double',
-      'void': 'Void',
-      'boolean': 'Uint8',
-    };
-    switch (t.kind) {
-      case Kind.primitive:
-        return ffi + primitives[(t.type as PrimitiveType).name]!;
-      case Kind.wildcard:
-        throw SkipException('Wildcards are not yet supported');
-      case Kind.typeVariable:
-      case Kind.array:
-      case Kind.declared:
-        return voidPointer;
-    }
-  }
-
-  String getDartInnerType(TypeUsage t) => _dartType(t);
-  String getDartOuterType(TypeUsage t, SymbolResolver resolver) =>
-      _dartType(t, resolver: resolver);
-
-  String getDartLiteral(dynamic value) {
-    if (value is String) {
-      // TODO(#31): escape string literal.
-      return '"$value"'.replaceAll('\\', '\\\\');
-    }
-    if (value is int || value is double || value is bool) {
-      return value.toString();
-    }
-    throw SkipException('Not a constant of a known type.');
-  }
-
-  String getJValueAccessor(TypeUsage type) {
-    const primitives = {
-      'boolean': 'boolean',
-      'byte': 'byte',
-      'short': 'short',
-      'char': 'char',
-      'int': 'integer',
-      'long': 'long',
-      'float': 'float',
-      'double': 'doubleFloat',
-      'void': 'check()',
-    };
-    if (isPrimitive(type)) {
-      return primitives[type.name]!;
-    }
-    return 'object';
-  }
-
-  String getOriginalFieldDecl(Field f) {
-    final declStmt = '${f.type.shorthand} ${f.name}';
-    return [...f.modifiers, declStmt].join(' ');
-  }
-
-  String getOriginalMethodHeader(Method m) {
-    final args = <String>[];
-    for (var p in m.params) {
-      args.add('${p.type.shorthand} ${p.name}');
-    }
-    final declStmt = '${m.returnType.shorthand} ${m.name}'
-        '(${args.join(', ')})';
-    return [...m.modifiers, declStmt].join(' ');
-  }
-
-  String toNativeArg(String name, TypeUsage type,
-      {bool convertBooleanToInt = true}) {
-    if (isPrimitive(type)) {
-      return (type.name == 'boolean' && convertBooleanToInt)
-          ? '$name ? 1 : 0'
-          : name;
-    }
-    return '$name.$selfPointer';
-  }
-
-  String toDartResult(String expr, TypeUsage type, String dartTypeClass) {
-    if (isPrimitive(type)) {
-      return expr;
-    }
-    return '$dartTypeClass.fromRef($expr)';
-  }
-
-  static final deleteInstruction =
-      '$indent/// The returned object must be deleted after use, '
-      'by calling the `delete` method.\n';
-  String getCallType(TypeUsage returnType) {
-    if (isPrimitive(returnType)) {
-      return "$jniCallType.${returnType.name}Type";
-    }
-    return "$jniCallType.objectType";
-  }
-}
-
-String breakDocComment(JavaDocComment? javadoc, {String depth = '    '}) {
-  final link = RegExp('{@link ([^{}]+)}');
-  if (javadoc == null) return '';
-  final comment = javadoc.comment
-      .replaceAllMapped(link, (match) => match.group(1) ?? '')
-      .replaceAll('#', '\\#')
-      .replaceAll('<p>', '')
-      .replaceAll('</p>', '\n')
-      .replaceAll('<b>', '__')
-      .replaceAll('</b>', '__')
-      .replaceAll('<em>', '_')
-      .replaceAll('</em>', '_');
-  return '$depth///\n'
-      '$depth/// ${comment.replaceAll('\n', '\n$depth///')}\n';
-}
-
-/// class name canonicalized for C bindings, by replacing "." with "_" and
-/// "$" with "__".
-String getUniqueClassName(ClassDecl decl) {
-  if (!decl.isPreprocessed) {
-    throw StateError("class not preprocessed: ${decl.binaryName}");
-  }
-  return decl.uniqueName;
-}
-
-/// Returns the name of the class member as referred to by C bindings
-String getMemberNameInC(ClassDecl decl, String name) =>
-    "${getUniqueClassName(decl)}__$name";
-
-String getCType(String binaryName) {
-  switch (binaryName) {
-    case "void":
-      return "void";
-    case "byte":
-      return "int8_t";
-    case "char":
-      return "char";
-    case "double":
-      return "double";
-    case "float":
-      return "float";
-    case "int":
-      return "int32_t";
-    case "long":
-      return "int64_t";
-    case "short":
-      return "int16_t";
-    case "boolean":
-      return "uint8_t";
-    default:
-      return "jobject";
-  }
-}
-
-String getInternalName(String binaryName) {
-  switch (binaryName) {
-    case "void":
-      return "V";
-    case "byte":
-      return "B";
-    case "char":
-      return "C";
-    case "double":
-      return "D";
-    case "float":
-      return "F";
-    case "int":
-      return "I";
-    case "long":
-      return "J";
-    case "short":
-      return "S";
-    case "boolean":
-      return "Z";
-    default:
-      return binaryName.replaceAll(".", "/");
-  }
-}
-
-String getSignature(String binaryName) {
-  switch (binaryName) {
-    case "void":
-      return "V";
-    case "byte":
-      return "B";
-    case "char":
-      return "C";
-    case "double":
-      return "D";
-    case "float":
-      return "F";
-    case "int":
-      return "I";
-    case "long":
-      return "J";
-    case "short":
-      return "S";
-    case "boolean":
-      return "Z";
-    default:
-      return 'L${binaryName.replaceAll(".", "/")};';
-  }
-}
-
-String getDescriptor(TypeUsage usage, {bool escapeDollarSign = false}) {
-  switch (usage.kind) {
-    case Kind.declared:
-      return getSignature((usage.type as DeclaredType).binaryName);
-    case Kind.primitive:
-      return getSignature((usage.type as PrimitiveType).name);
-    case Kind.typeVariable:
-      // It should be possible to compute the erasure of a type
-      // in parser itself.
-      // TODO(#23): Use erasure of the type variable here.
-      // This is just a (wrong) placeholder
-      return "Ljava/lang/Object;";
-    case Kind.array:
-      final inner = getDescriptor((usage.type as ArrayType).type);
-      return "[$inner";
-    case Kind.wildcard:
-      final extendsBound = (usage.type as Wildcard).extendsBound;
-      if (extendsBound != null) {
-        return getDescriptor(extendsBound);
-      }
-      return 'Ljava/lang/Object;';
-  }
-}
-
-String toFuture(String type) {
-  return 'Future<$type>';
-}
-
-bool isPrimitive(TypeUsage t) => t.kind == Kind.primitive;
-bool isVoid(TypeUsage t) => isPrimitive(t) && t.name == 'void';
-
-bool isStaticField(Field f) => f.modifiers.contains('static');
-bool isStaticMethod(Method m) => m.modifiers.contains('static');
-
-bool isFinalField(Field f) => f.modifiers.contains('final');
-bool isFinalMethod(Method m) => m.modifiers.contains('final');
-
-bool isCtor(Method m) => m.name == '<init>';
-
-// static methods & constructors do not have self param.
-bool hasSelfParam(Method m) => !isStaticMethod(m) && !isCtor(m);
-
-bool isObjectField(Field f) => !isPrimitive(f.type);
-bool isObjectMethod(Method m) => !isPrimitive(m.returnType);
-
-/// Returns class name as useful in dart.
-///
-/// Eg -> a.b.X.Y -> X_Y
-String getSimplifiedClassName(String binaryName) =>
-    binaryName.split('.').last.replaceAll('\$', '_');
-
-// Marker exception when a method or class cannot be translated
-// The inner functions may not know how much context has to be skipped in case
-// of an error or unknown element. They throw SkipException.
-class SkipException implements Exception {
-  SkipException(this.message, [this.element]);
-  String message;
-  dynamic element;
-
-  @override
-  String toString() {
-    return '$message;';
-  }
-}
-
-String getTypeNameAtCallSite(TypeUsage t) {
-  if (isPrimitive(t)) {
-    return t.name.substring(0, 1).toUpperCase() + t.name.substring(1);
-  }
-  return "Object";
-}
-
-String getResultGetterName(TypeUsage returnType) {
-  final primitives = {
-    'boolean': 'boolean',
-    'byte': 'byte',
-    'short': 'short',
-    'char': 'char',
-    'int': 'integer',
-    'long': 'long',
-    'float': 'float',
-    'double': 'doubleFloat',
-    'void': 'check()',
-  };
-  return primitives[returnType.name] ?? 'object';
-}
-
-/// Returns the JNI signature of the method.
-String getJniSignatureForMethod(Method m) {
-  final s = StringBuffer();
-  s.write('(');
-  for (var param in m.params) {
-    final type = getDescriptor(param.type);
-    s.write(type);
-  }
-  s.write(')');
-  final returnType = getDescriptor(m.returnType);
-  s.write(returnType);
-  return s.toString();
-}
-
-class _TypeClass {
-  final String name;
-  final bool canBeConst;
-
-  _TypeClass(this.name, this.canBeConst);
-}
diff --git a/pkgs/jnigen/lib/src/bindings/dart_bindings.dart b/pkgs/jnigen/lib/src/bindings/dart_bindings.dart
deleted file mode 100644
index d1ad576..0000000
--- a/pkgs/jnigen/lib/src/bindings/dart_bindings.dart
+++ /dev/null
@@ -1,273 +0,0 @@
-// 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:jnigen/src/elements/elements.dart';
-import 'package:jnigen/src/config/config.dart';
-import 'package:jnigen/src/logging/logging.dart';
-
-import 'symbol_resolver.dart';
-import 'common.dart';
-
-class CBasedDartBindingsGenerator extends BindingsGenerator {
-  static const selfPointer = BindingsGenerator.selfPointer;
-
-  /// Symbol lookup function for generated code.
-  static const lookup = 'jniLookup';
-
-  // import prefixes
-  static const ffi = BindingsGenerator.ffi;
-  static const jni = BindingsGenerator.jni;
-
-  static final indent = ' ' * 2;
-
-  static const voidPointer = BindingsGenerator.jobjectType;
-
-  static const ffiVoidType = BindingsGenerator.ffiVoidType;
-
-  static const jniObjectType = BindingsGenerator.jniObjectType;
-
-  CBasedDartBindingsGenerator(this.config);
-
-  final Config config;
-
-  @override
-  String generateBindings(ClassDecl decl, SymbolResolver resolver) {
-    if (!decl.isPreprocessed) {
-      throw StateError('Java class declaration must be preprocessed before'
-          'being passed to bindings generator');
-    }
-    if (!decl.isIncluded) {
-      return '';
-    }
-    final bindings = _class(decl, resolver);
-    log.finest('generated bindings for class ${decl.binaryName}');
-    return bindings;
-  }
-
-  String _class(ClassDecl decl, SymbolResolver resolver) {
-    final s = StringBuffer();
-
-    s.write('/// from: ${decl.binaryName}\n');
-    s.write(breakDocComment(decl.javadoc, depth: ''));
-
-    s.write(dartClassDefinition(decl, resolver));
-    s.write(dartStaticTypeGetter(decl));
-    for (var field in decl.fields) {
-      if (!field.isIncluded) {
-        continue;
-      }
-      try {
-        s.write(_field(decl, field, resolver));
-        s.writeln();
-      } on SkipException catch (e) {
-        log.fine('skip field ${decl.binaryName}#${field.name}: '
-            '${e.message}');
-      }
-    }
-
-    for (var method in decl.methods) {
-      if (!method.isIncluded) {
-        continue;
-      }
-      try {
-        s.write(_method(decl, method, resolver));
-        s.writeln();
-      } on SkipException catch (e) {
-        log.fine('skip field ${decl.binaryName}#${method.name}: '
-            '${e.message}');
-      }
-    }
-    s.write("}\n");
-    s.write(dartTypeClass(decl));
-    s.write(dartArrayExtension(decl));
-    return s.toString();
-  }
-
-  String _method(ClassDecl c, Method m, SymbolResolver resolver) {
-    final name = m.finalName;
-    final cName = getMemberNameInC(c, name);
-    final s = StringBuffer();
-    final sym = '_$name';
-    final ffiSig = dartSigForMethod(m, isFfiSig: true);
-    final dartSig = dartSigForMethod(m, isFfiSig: false);
-    var returnType = getDartOuterType(
-      m.asyncReturnType ?? m.returnType,
-      resolver,
-    );
-    if (m.asyncReturnType != null) {
-      returnType = toFuture(returnType);
-    }
-    final returnTypeClass =
-        getDartTypeClass(m.asyncReturnType ?? m.returnType, resolver);
-    final ifStaticMethod = isStaticMethod(m) ? 'static' : '';
-
-    // Load corresponding C method.
-    s.write('${indent}static final $sym = $lookup'
-        '<${ffi}NativeFunction<$ffiSig>>("$cName")\n'
-        '.asFunction<$dartSig>();\n');
-    // Different logic for constructor and method;
-    // For constructor, we want return type to be new object.
-    s.write('$indent/// from: ${getOriginalMethodHeader(m)}\n');
-    if (!isPrimitive(m.returnType)) {
-      s.write(BindingsGenerator.deleteInstruction);
-    }
-    s.write(breakDocComment(m.javadoc));
-    s.write(indent);
-
-    if (isCtor(m)) {
-      final className = c.finalName;
-      final ctorFnName = name == 'ctor' ? className : '$className.$name';
-      final wrapperExpr = '$sym(${actualArgs(m)})';
-      s.write(
-        '$ctorFnName(${getFormalArgs(c, m, resolver)}) : '
-        'super.fromRef(${dartSuperArgs(c, resolver)}$wrapperExpr.object);\n',
-      );
-      return s.toString();
-    }
-
-    final resultGetter = getJValueAccessor(m.returnType);
-    var wrapperExpr = '$sym(${actualArgs(m)}).$resultGetter';
-    final typeParamsWithExtend =
-        dartTypeParams(m.typeParams, includeExtends: true);
-    final params = getFormalArgs(c, m, resolver);
-    s.write(
-      '$indent$ifStaticMethod $returnType $name$typeParamsWithExtend($params) ',
-    );
-    if (m.asyncReturnType == null) {
-      wrapperExpr = toDartResult(wrapperExpr, m.returnType, returnTypeClass);
-      s.write('=> $wrapperExpr;\n');
-    } else {
-      s.write(
-        'async {\n'
-        '${indent * 2}final \$p = ReceivePort();\n'
-        '${indent * 2}final \$c = ${jni}Jni.newPortContinuation(\$p);\n'
-        '${indent * 2}$wrapperExpr;\n'
-        '${indent * 2}final \$o = $voidPointer.fromAddress(await \$p.first);\n'
-        '${indent * 2}final \$k = $returnTypeClass.getClass().reference;\n'
-        '${indent * 2}if (${jni}Jni.env.IsInstanceOf(\$o, \$k) == 0) {\n'
-        '${indent * 3}throw "Failed";\n'
-        '${indent * 2}}\n'
-        '${indent * 2}return ${toDartResult(
-          '\$o',
-          m.returnType,
-          returnTypeClass,
-        )};\n'
-        '}\n',
-      );
-    }
-    return s.toString();
-  }
-
-  String _field(ClassDecl c, Field f, SymbolResolver resolver) {
-    final name = f.finalName;
-    final s = StringBuffer();
-
-    void writeDocs({bool writeDeleteInstruction = true}) {
-      s.write('$indent/// from: ${getOriginalFieldDecl(f)}\n');
-      if (!isPrimitive(f.type) && writeDeleteInstruction) {
-        s.write(BindingsGenerator.deleteInstruction);
-      }
-      s.write(breakDocComment(f.javadoc));
-    }
-
-    if (isStaticField(f) && isFinalField(f) && f.defaultValue != null) {
-      writeDocs(writeDeleteInstruction: false);
-      s.write('${indent}static const $name = '
-          '${getDartLiteral(f.defaultValue)};\n');
-      return s.toString();
-    }
-
-    final cName = getMemberNameInC(c, name);
-
-    void writeAccessor({bool isSetter = false}) {
-      final symPrefix = isSetter ? 'set' : 'get';
-      final sym = '_${symPrefix}_$name';
-      final ffiSig = dartSigForField(f, isSetter: isSetter, isFfiSig: true);
-      final dartSig = dartSigForField(f, isSetter: isSetter, isFfiSig: false);
-      s.write('${indent}static final $sym = $lookup'
-          '<${ffi}NativeFunction<$ffiSig>>("${symPrefix}_$cName")\n'
-          '.asFunction<$dartSig>();\n');
-      writeDocs();
-      final ifStatic = isStaticField(f) ? 'static' : '';
-      if (isSetter) {
-        final args = [
-          if (!isStaticField(f)) selfPointer,
-          toNativeArg('value', f.type),
-        ].join(', ');
-        final valueOuterType = getDartOuterType(f.type, resolver);
-        s.write('$indent$ifStatic set $name($valueOuterType value) => '
-            '$sym($args);\n');
-      } else {
-        // getter
-        final self = isStaticField(f) ? '' : selfPointer;
-        final outerType = getDartOuterType(f.type, resolver);
-        final outerTypeClass = getDartTypeClass(f.type, resolver);
-        final resultGetter = getJValueAccessor(f.type);
-        final callExpr = '$sym($self).$resultGetter';
-        final resultExpr = toDartResult(callExpr, f.type, outerTypeClass);
-        s.write('$indent$ifStatic $outerType get $name => $resultExpr;\n');
-      }
-    }
-
-    writeAccessor(isSetter: false);
-    if (!isFinalField(f)) writeAccessor(isSetter: true);
-    s.writeln();
-    return s.toString();
-  }
-
-  static const _importsForInitCode = 'import "dart:ffi" as ffi;\n'
-      'import "package:jni/internal_helpers_for_jnigen.dart";\n';
-
-  /// Initialization code for C based bindings.
-  ///
-  /// Should be called once in a package. In package-structured bindings
-  /// this is placed in _init.dart in package root.
-  String _initCode() => '// Auto-generated initialization code.\n'
-      '\n'
-      'final ffi.Pointer<T> Function<T extends ffi.NativeType>(String sym)\n'
-      'jniLookup = ProtectedJniExtensions.initGeneratedLibrary'
-      '("${config.outputConfig.cConfig!.libraryName}");'
-      '\n\n';
-  static const autoGeneratedNotice = '// Autogenerated by jnigen. '
-      'DO NOT EDIT!\n\n';
-  static const defaultImports = 'import "dart:isolate" show ReceivePort;\n'
-      'import "dart:ffi" as ffi;\n'
-      'import "package:jni/internal_helpers_for_jnigen.dart";\n'
-      'import "package:jni/jni.dart" as jni;\n\n';
-  static const defaultLintSuppressions =
-      '// ignore_for_file: annotate_overrides\n'
-      '// ignore_for_file: camel_case_extensions\n'
-      '// ignore_for_file: camel_case_types\n'
-      '// ignore_for_file: constant_identifier_names\n'
-      '// ignore_for_file: file_names\n'
-      '// ignore_for_file: no_leading_underscores_for_local_identifiers\n'
-      '// ignore_for_file: non_constant_identifier_names\n'
-      '// ignore_for_file: overridden_fields\n'
-      '// ignore_for_file: unnecessary_cast\n'
-      '// ignore_for_file: unused_element\n'
-      '// ignore_for_file: unused_import\n'
-      '\n';
-  static const preImportBoilerplate =
-      autoGeneratedNotice + defaultLintSuppressions + defaultImports;
-
-  @override
-  String getPostImportBoilerplate([String? initFilePath]) {
-    if (config.outputConfig.dartConfig.structure ==
-        OutputStructure.singleFile) {
-      return _initCode();
-    } else {
-      return 'import "${initFilePath!}" show jniLookup;\n\n';
-    }
-  }
-
-  @override
-  String getPreImportBoilerplate([String? initFilePath]) {
-    return preImportBoilerplate;
-  }
-
-  @override
-  String getInitFileContents() {
-    return '$_importsForInitCode\n${_initCode()}';
-  }
-}
diff --git a/pkgs/jnigen/lib/src/bindings/dart_generator.dart b/pkgs/jnigen/lib/src/bindings/dart_generator.dart
new file mode 100644
index 0000000..6ae3396
--- /dev/null
+++ b/pkgs/jnigen/lib/src/bindings/dart_generator.dart
@@ -0,0 +1,1115 @@
+// Copyright (c) 2023, 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 'dart:io';
+
+import '../config/config.dart';
+import '../elements/elements.dart';
+import '../logging/logging.dart';
+import '../writers/bindings_writer.dart';
+import 'c_generator.dart';
+import 'resolver.dart';
+import 'visitor.dart';
+
+// Import prefixes
+const _jni = 'jni';
+const _ffi = 'ffi';
+
+// package:jni types
+const _jType = '$_jni.JObjType';
+const _jPointer = '$_jni.JObjectPtr';
+const _jArray = '$_jni.JArray';
+const _jObject = '$_jni.JObject';
+const _jThrowable = '$_jni.JThrowablePtr';
+const _jResult = '$_jni.JniResult';
+const _jCallType = '$_jni.JniCallType';
+
+// package:ffi types
+const _voidPointer = '$_ffi.Pointer<$_ffi.Void>';
+
+// Prefixes and suffixes
+const _typeParamPrefix = '\$';
+// TODO(#143): this is a temporary fix for the name collision.
+const _typeClassPrefix = '\$';
+const _typeClassSuffix = 'Type';
+
+// Misc.
+const _classRef = '_classRef';
+const _env = 'jniEnv';
+const _accessors = 'jniAccessors';
+const _lookup = 'jniLookup';
+const _selfPointer = 'reference';
+
+// Docs
+const _deleteInstruction =
+    '  /// The returned object must be deleted after use, '
+    'by calling the `delete` method.';
+
+extension on String {
+  String capitalize() {
+    return '${this[0].toUpperCase()}${substring(1)}';
+  }
+}
+
+/// Encloses [inside] in the middle of [open] and [close]
+/// if [inside] is not empty.
+String _encloseIfNotEmpty(String open, String inside, String close) {
+  if (inside == '') return '';
+  return '$open$inside$close';
+}
+
+String _newLine({int depth = 0}) {
+  return '\n${'  ' * depth}';
+}
+
+/// **Naming Convention**
+///
+/// Let's take the following code as an example:
+///
+/// ```dart
+/// Method definition
+/// void f<T extends num, U>(JType<T> $T, JType<U> $U, T t, U u) {
+///   // ...
+/// }
+/// f<int, String>($T, $U, t, u); // Calling the Method
+/// ```
+///
+/// Here `f` will be replaced according to the place of usage.
+///
+/// * `fArgsDef` refers to `T t, U u` – the arguments in the method
+///   definition.
+/// * `fArgsCall` refer to `t, u` – the arguments passed to call the method.
+/// * `fTypeParamsDef` refers to `<T extends num, U>` – the type parameters
+///   of the method at the point of definition.
+/// * `fTypeParamsCall` refers to `<int, String>` – the type parameters when
+///   calling, or whenever we don't want to have the `extends` keyword.
+/// * `fTypeClassesDef` refers to `JType<T> $T, JType<U> $U`.
+/// * `fTypeClassesCall` refers to `$T, $U` when calling the method.
+class DartGenerator extends Visitor<Classes, Future<void>> {
+  DartGenerator(this.config);
+
+  final Config config;
+
+  static const cInitImport = 'import "dart:ffi" as ffi;\n'
+      'import "package:jni/internal_helpers_for_jnigen.dart";\n';
+
+  /// Initialization code for C based bindings.
+  ///
+  /// Should be called once in a package. In package-structured bindings
+  /// this is placed in _init.dart in package root.
+  String get cInitCode => '''
+// Auto-generated initialization code.
+
+final $_ffi.Pointer<T> Function<T extends $_ffi.NativeType>(String sym) $_lookup =
+    ProtectedJniExtensions.initGeneratedLibrary("${config.outputConfig.cConfig!.libraryName}");
+
+
+''';
+
+  static const dartOnlyInitImport = 'import "package:jni/jni.dart" as jni;\n';
+
+  static const dartOnlyInitCode = '''
+// Auto-generated initialization code.
+
+final $_env = $_jni.Jni.env;
+final $_accessors = $_jni.Jni.accessors;
+
+
+''';
+
+  static const autoGeneratedNotice = '// Autogenerated by jnigen. '
+      'DO NOT EDIT!\n\n';
+  static const defaultImports = '''
+import "dart:isolate" show ReceivePort;
+import "dart:ffi" as ffi;
+import "package:jni/internal_helpers_for_jnigen.dart";
+import "package:jni/jni.dart" as jni;
+
+''';
+  static const defaultLintSuppressions = '''
+// ignore_for_file: annotate_overrides
+// ignore_for_file: camel_case_extensions
+// ignore_for_file: camel_case_types
+// ignore_for_file: constant_identifier_names
+// ignore_for_file: file_names
+// ignore_for_file: no_leading_underscores_for_local_identifiers
+// ignore_for_file: non_constant_identifier_names
+// ignore_for_file: overridden_fields
+// ignore_for_file: unnecessary_cast
+// ignore_for_file: unused_element
+// ignore_for_file: unused_field
+// ignore_for_file: unused_import
+// ignore_for_file: unused_shown_name
+
+''';
+  static const preImportBoilerplate =
+      autoGeneratedNotice + defaultLintSuppressions + defaultImports;
+
+  @override
+  Future<void> visit(Classes node) async {
+    final cBased = config.outputConfig.bindingsType == BindingsType.cBased;
+    final root = config.outputConfig.dartConfig.path;
+    final preamble = config.preamble ?? '';
+    if (config.outputConfig.dartConfig.structure ==
+        OutputStructure.singleFile) {
+      final file = File.fromUri(root);
+      await file.create(recursive: true);
+      log.info("Generating ${cBased ? "C + Dart" : "Pure Dart"} Bindings");
+      final s = file.openWrite();
+      s.writeln(preamble);
+      s.writeln(autoGeneratedNotice);
+      s.writeln(defaultLintSuppressions);
+      s.writeln(defaultImports);
+      if (cBased) {
+        s.writeln(cInitCode);
+      } else {
+        s.writeln(dartOnlyInitCode);
+      }
+      final classGenerator = _ClassGenerator(config, s);
+      node.decls.values.accept(classGenerator).toList();
+      await s.close();
+      await runDartFormat(file.path);
+      return;
+    }
+    final files = <String, List<ClassDecl>>{};
+    final packages = <String, Set<String>>{};
+    for (final classDecl in node.decls.values) {
+      final fileClass = Resolver.getFileClassName(classDecl.binaryName);
+
+      files.putIfAbsent(fileClass, () => <ClassDecl>[]);
+      files[fileClass]!.add(classDecl);
+
+      packages.putIfAbsent(classDecl.packageName, () => {});
+      packages[classDecl.packageName]!.add(fileClass.split('.').last);
+    }
+
+    log.info("Using dart root = $root");
+    const initFileName = '_init.dart';
+    final initFileUri = root.resolve(initFileName);
+    final initFile = File.fromUri(initFileUri);
+    await initFile.create(recursive: true);
+    final initStream = initFile.openWrite();
+    initStream.writeln(preamble);
+    initStream.writeln(cBased ? cInitImport : dartOnlyInitImport);
+    initStream.writeln(cBased ? cInitCode : dartOnlyInitCode);
+    await initStream.close();
+    for (var fileClassName in files.keys) {
+      final relativeFileName = '${fileClassName.replaceAll('.', '/')}.dart';
+      final dartFileUri = root.resolve(relativeFileName);
+      final dartFile = await File.fromUri(dartFileUri).create(recursive: true);
+      log.fine('$fileClassName -> ${dartFile.path}');
+
+      final classesInFile = files[fileClassName]!;
+      final dartFileStream = dartFile.openWrite();
+      dartFileStream.writeln(preamble);
+      dartFileStream.writeln(autoGeneratedNotice);
+      dartFileStream.writeln(defaultLintSuppressions);
+      dartFileStream.writeln(defaultImports);
+      final s = StringBuffer();
+      final initFilePath = ('../' *
+              relativeFileName.codeUnits
+                  .where((cu) => cu == '/'.codeUnitAt(0))
+                  .length) +
+          initFileName;
+      s.write('import "$initFilePath";');
+      final resolver = Resolver(
+        importMap: config.importMap ?? {},
+        currentClass: fileClassName,
+        inputClassNames: node.decls.keys.toSet(),
+      );
+      classesInFile
+          .accept(_ClassGenerator(config, s, resolver: resolver))
+          .toList();
+      dartFileStream.writeAll(resolver.getImportStrings(), '\n');
+      dartFileStream.writeln(s.toString());
+      await dartFileStream.close();
+    }
+
+    // write _package.dart export files
+    for (var package in packages.keys) {
+      final dirUri = root.resolve('${package.replaceAll('.', '/')}/');
+      final exportFileUri = dirUri.resolve("_package.dart");
+      final exportFile = File.fromUri(exportFileUri);
+      exportFile.createSync(recursive: true);
+      final exports =
+          packages[package]!.map((cls) => 'export "$cls.dart";').join('\n');
+      exportFile.writeAsStringSync(exports);
+    }
+    await runDartFormat(root.toFilePath());
+    log.info('Completed.');
+  }
+}
+
+/// Generates the Dart class definition, type class, and the array extension
+class _ClassGenerator extends Visitor<ClassDecl, void> {
+  _ClassGenerator(
+    this.config,
+    this.s, {
+    this.resolver,
+  });
+
+  final Config config;
+  final StringSink s;
+  final Resolver? resolver;
+
+  static const staticTypeGetter = 'type';
+  static const instanceTypeGetter = '\$$staticTypeGetter';
+
+  static const arrayExtensionPrefix = '\$';
+  static const arrayExtensionSuffix = 'Array';
+
+  @override
+  void visit(ClassDecl node) {
+    final isDartOnly =
+        config.outputConfig.bindingsType == BindingsType.dartOnly;
+
+    // Docs
+    s.write('/// from: ${node.binaryName}\n');
+    node.javadoc?.accept(_DocGenerator(s, depth: 0));
+
+    // Class definition
+    final name = node.finalName;
+    final superName = node.superclass!.accept(_TypeGenerator(resolver));
+    final typeParamsDef = _encloseIfNotEmpty(
+      '<',
+      node.allTypeParams
+          .accept(const _TypeParamGenerator(withExtends: true))
+          .join(', '),
+      '>',
+    );
+    final typeParams = node.allTypeParams
+        .accept(const _TypeParamGenerator(withExtends: false));
+    final typeParamsCall = _encloseIfNotEmpty('<', typeParams.join(', '), '>');
+    final staticTypeGetterCallArgs = _encloseIfNotEmpty(
+      '(',
+      typeParams.map((typeParams) => '$_typeParamPrefix$typeParams').join(', '),
+      ')',
+    );
+    final typeClassDefinitions = typeParams
+        .map((typeParam) =>
+            'final $_jType<$typeParam> $_typeParamPrefix$typeParam;')
+        .join(_newLine(depth: 1));
+    final ctorTypeClassesDef = typeParams
+        .map((typeParam) => 'this.$_typeParamPrefix$typeParam,')
+        .join(_newLine(depth: 2));
+    final superClass = (node.classDecl.superclass!.type as DeclaredType);
+    final superTypeClassesCall = superClass.classDecl == ClassDecl.object
+        ? ''
+        : superClass.params
+            .accept(_TypeClassGenerator(resolver))
+            .map((typeClass) => '${typeClass.name},')
+            .join(_newLine(depth: 2));
+    s.write('''
+class $name$typeParamsDef extends $superName {
+  late final $_jType? _$instanceTypeGetter;
+  @override
+  $_jType get $instanceTypeGetter => _$instanceTypeGetter ??= $staticTypeGetter$staticTypeGetterCallArgs;
+
+  $typeClassDefinitions
+
+  $name.fromRef(
+    $ctorTypeClassesDef
+    $_jPointer ref,
+  ): super.fromRef(
+    $superTypeClassesCall
+    ref
+  );
+
+''');
+
+    if (isDartOnly) {
+      final internalName = node.internalName;
+      s.write('''
+  static final $_classRef = $_accessors.getClassOf(r"$internalName");
+
+''');
+    }
+
+    // Static TypeClass getter
+    s.writeln(
+        '  /// The type which includes information such as the signature of this class.');
+    final typeClassName = '$_typeClassPrefix$name$_typeClassSuffix';
+    if (typeParams.isEmpty) {
+      s.writeln('static const $staticTypeGetter = $typeClassName();');
+    } else {
+      final staticTypeGetterTypeClassesDef = typeParams
+          .map(
+              (typeParam) => '$_jType<$typeParam> $_typeParamPrefix$typeParam,')
+          .join(_newLine(depth: 2));
+      final typeClassesCall = typeParams
+          .map((typeParam) => '$_typeParamPrefix$typeParam,')
+          .join(_newLine(depth: 3));
+      s.write('''
+  static $typeClassName$typeParamsCall $staticTypeGetter$typeParamsDef(
+    $staticTypeGetterTypeClassesDef
+  ) {
+    return $typeClassName(
+      $typeClassesCall
+    );
+  }
+
+''');
+    }
+
+    // Fields and Methods
+    final fieldGenerator = _FieldGenerator(config, resolver, s);
+    for (final field in node.fields) {
+      field.accept(fieldGenerator);
+    }
+    final methodGenerator = _MethodGenerator(config, resolver, s);
+    for (final method in node.methods) {
+      method.accept(methodGenerator);
+    }
+
+    // End of Class definition
+    s.writeln('}');
+
+    // TypeClass definition
+    final typeClassesCall = typeParams
+        .map((typeParam) => '$_typeParamPrefix$typeParam,')
+        .join(_newLine(depth: 2));
+    final signature = node.signature;
+    s.write('''
+class $typeClassName$typeParamsDef extends $_jType<$name$typeParamsCall> {
+  $typeClassDefinitions
+
+  const $typeClassName(
+    $ctorTypeClassesDef
+  );
+
+  @override
+  String get signature => r"$signature";
+
+  @override
+  $name$typeParamsCall fromRef($_jPointer ref) => $name.fromRef(
+    $typeClassesCall
+    ref
+  );
+}
+
+''');
+
+    // Array extension
+    s.write('''
+extension $arrayExtensionPrefix$name$arrayExtensionSuffix$typeParamsDef on $_jArray<$name$typeParamsCall> {
+  $name$typeParamsCall operator [](int index) {
+    return (elementType as $typeClassName$typeParamsCall)
+        .fromRef(elementAt(index, $_jni.JniCallType.objectType).object);
+  }
+
+  void operator []=(int index, $name$typeParamsCall value) {
+    (this as $_jArray<$_jObject>)[index] = value;
+  }
+}
+''');
+    log.finest('Generated bindings for class ${node.binaryName}');
+  }
+}
+
+/// Generates the JavaDoc comments.
+class _DocGenerator extends Visitor<JavaDocComment, void> {
+  const _DocGenerator(this.s, {required this.depth});
+
+  final StringSink s;
+  final int depth;
+
+  @override
+  void visit(JavaDocComment node) {
+    final link = RegExp('{@link ([^{}]+)}');
+    final indent = '  ' * depth;
+    final comments = node.comment
+        .replaceAllMapped(link, (match) => match.group(1) ?? '')
+        .replaceAll('#', '\\#')
+        .replaceAll('<p>', '')
+        .replaceAll('</p>', '\n')
+        .replaceAll('<b>', '__')
+        .replaceAll('</b>', '__')
+        .replaceAll('<em>', '_')
+        .replaceAll('</em>', '_')
+        .split('\n')
+        .join('\n$indent///');
+    s.write('''
+$indent///
+$indent/// $comments
+''');
+  }
+}
+
+/// Generates the user-facing Dart type.
+class _TypeGenerator extends TypeVisitor<String> {
+  const _TypeGenerator(this.resolver);
+
+  final Resolver? resolver;
+
+  @override
+  String visitArrayType(ArrayType node) {
+    final innerType = node.type;
+    if (innerType.kind == Kind.primitive) {
+      return '$_jArray<$_jni.${(innerType.type as PrimitiveType).jniType}>';
+    }
+    return '$_jArray<${innerType.accept(this)}>';
+  }
+
+  @override
+  String visitDeclaredType(DeclaredType node) {
+    if (node.classDecl.binaryName == 'java.lang.Object' ||
+        node.classDecl.binaryName == 'java.lang.String') {
+      return '$_jni.${node.classDecl.finalName}';
+    }
+
+    // All type parameters of this type
+    final allTypeParams = node.classDecl.allTypeParams
+        .accept(const _TypeParamGenerator(withExtends: false))
+        .toList();
+    // The ones that are declared.
+    final definedTypeParams = node.params.accept(this).toList();
+
+    // Replacing the declared ones. They come at the end.
+    // The rest will be JObject.
+    if (allTypeParams.length >= node.params.length) {
+      allTypeParams.replaceRange(
+        0,
+        allTypeParams.length - node.params.length,
+        List.filled(
+          allTypeParams.length - node.params.length,
+          _jObject,
+        ),
+      );
+      allTypeParams.replaceRange(
+        allTypeParams.length - node.params.length,
+        allTypeParams.length,
+        definedTypeParams,
+      );
+    }
+
+    final typeParams = _encloseIfNotEmpty('<', allTypeParams.join(', '), '>');
+    final prefix = resolver?.resolvePrefix(node.classDecl.binaryName) ?? '';
+    return '$prefix${node.classDecl.finalName}$typeParams';
+  }
+
+  @override
+  String visitPrimitiveType(PrimitiveType node) {
+    return node.dartType;
+  }
+
+  @override
+  String visitTypeVar(TypeVar node) {
+    return node.name;
+  }
+
+  @override
+  String visitWildcard(Wildcard node) {
+    // TODO(#141): Support wildcards
+    return super.visitWildcard(node);
+  }
+
+  @override
+  String visitNonPrimitiveType(ReferredType node) {
+    return _jObject;
+  }
+}
+
+class _TypeClass {
+  const _TypeClass(this.name, this.canBeConst);
+
+  final String name;
+  final bool canBeConst;
+}
+
+/// Generates the type class.
+class _TypeClassGenerator extends TypeVisitor<_TypeClass> {
+  final bool isConst;
+  final Resolver? resolver;
+
+  _TypeClassGenerator(this.resolver, {this.isConst = true});
+
+  @override
+  _TypeClass visitArrayType(ArrayType node) {
+    final innerTypeClass =
+        node.type.accept(_TypeClassGenerator(resolver, isConst: false));
+    final ifConst = innerTypeClass.canBeConst && isConst ? 'const ' : '';
+    return _TypeClass(
+      '$ifConst$_jArray$_typeClassSuffix(${innerTypeClass.name})',
+      innerTypeClass.canBeConst,
+    );
+  }
+
+  @override
+  _TypeClass visitDeclaredType(DeclaredType node) {
+    if (node.classDecl.binaryName == 'java.lang.Object' ||
+        node.classDecl.binaryName == 'java.lang.String') {
+      final ifConst = isConst ? 'const ' : '';
+      return _TypeClass(
+          '$ifConst$_jni.${node.classDecl.finalName}$_typeClassSuffix()', true);
+    }
+    final allTypeParams = node.classDecl.allTypeParams
+        .accept(const _TypeParamGenerator(withExtends: false))
+        .map((typeParam) => '$_typeParamPrefix$typeParam')
+        .toList();
+
+    // The ones that are declared.
+    final definedTypeClasses =
+        node.params.accept(_TypeClassGenerator(resolver, isConst: false));
+
+    // Can be const if all the type parameters are defined and each of them are
+    // also const.
+    final canBeConst = definedTypeClasses.every((e) => e.canBeConst);
+
+    // Adding const to `JObjectType`s if the entire expression is not const.
+    final constJObject = canBeConst ? '' : 'const ';
+
+    // Replacing the declared ones. They come at the end.
+    // The rest will be `JObjectType`.
+    if (allTypeParams.length >= node.params.length) {
+      allTypeParams.replaceRange(
+        0,
+        allTypeParams.length - node.params.length,
+        List.filled(
+          allTypeParams.length - node.params.length,
+          '$constJObject$_jObject$_typeClassSuffix()',
+        ),
+      );
+      allTypeParams.replaceRange(
+        allTypeParams.length - node.params.length,
+        allTypeParams.length,
+        definedTypeClasses.map((param) => param.name),
+      );
+    }
+
+    final args = allTypeParams.join(', ');
+    final ifConst = isConst && canBeConst ? 'const ' : '';
+    final prefix = resolver?.resolvePrefix(node.classDecl.binaryName) ?? '';
+    return _TypeClass(
+      '$ifConst$prefix$_typeClassPrefix${node.classDecl.finalName}$_typeClassSuffix($args)',
+      canBeConst,
+    );
+  }
+
+  @override
+  _TypeClass visitPrimitiveType(PrimitiveType node) {
+    final ifConst = isConst ? 'const ' : '';
+    return _TypeClass('$ifConst$_jni.${node.jniType}$_typeClassSuffix()', true);
+  }
+
+  @override
+  _TypeClass visitTypeVar(TypeVar node) {
+    return _TypeClass('$_typeParamPrefix${node.name}', false);
+  }
+
+  @override
+  _TypeClass visitWildcard(Wildcard node) {
+    // TODO(#141): Support wildcards
+    return super.visitWildcard(node);
+  }
+
+  @override
+  _TypeClass visitNonPrimitiveType(ReferredType node) {
+    final ifConst = isConst ? 'const ' : '';
+    return _TypeClass('$ifConst$_jObject$_typeClassSuffix()', true);
+  }
+}
+
+class _TypeParamGenerator extends Visitor<TypeParam, String> {
+  const _TypeParamGenerator({required this.withExtends});
+
+  final bool withExtends;
+
+  @override
+  String visit(TypeParam node) {
+    if (!withExtends) {
+      return node.name;
+    }
+    // TODO(#144): resolve the actual type being extended, if any.
+    return '${node.name} extends $_jObject';
+  }
+}
+
+class _JniResultGetter extends TypeVisitor<String> {
+  const _JniResultGetter();
+
+  @override
+  String visitPrimitiveType(PrimitiveType node) {
+    if (node.name == 'void') return 'check()';
+    if (node.name == 'double') return 'doubleFloat';
+    if (node.name == 'int') return 'integer';
+    return node.name;
+  }
+
+  @override
+  String visitNonPrimitiveType(ReferredType node) {
+    return 'object';
+  }
+}
+
+/// Type signature for C-based bindings.
+///
+/// When `isFFi` is `true`, it generates the ffi type signature and when it's
+/// false, it generates the dart type signature.
+///
+/// For example `ffi.Int32` is an ffi type signature while `int` is a Dart one:
+/// ```dart
+/// jniLookup<ffi.NativeFunction<jni.JniResult Function(ffi.Int32, ffi.Int32)>>(
+///       "sum")
+///   .asFunction<jni.JniResult Function(int, int)>();
+/// ```
+class _TypeSig extends TypeVisitor<String> {
+  const _TypeSig({required this.isFfi});
+
+  final bool isFfi;
+
+  @override
+  String visitPrimitiveType(PrimitiveType node) {
+    if (isFfi) return '$_ffi.${node.ffiType}';
+    if (node.name == 'boolean') return 'int';
+    return node.dartType;
+  }
+
+  @override
+  String visitNonPrimitiveType(ReferredType node) {
+    return _voidPointer;
+  }
+}
+
+class _TypeName extends TypeVisitor<String> {
+  const _TypeName();
+
+  @override
+  String visitPrimitiveType(PrimitiveType node) {
+    return node.name;
+  }
+
+  @override
+  String visitNonPrimitiveType(ReferredType node) {
+    return 'object';
+  }
+}
+
+class _CallType extends TypeVisitor<String> {
+  const _CallType();
+
+  @override
+  String visitPrimitiveType(PrimitiveType node) {
+    return '$_jCallType.${node.name}Type';
+  }
+
+  @override
+  String visitNonPrimitiveType(ReferredType node) {
+    return '$_jCallType.objectType';
+  }
+}
+
+class _ToNativeSuffix extends TypeVisitor<String> {
+  const _ToNativeSuffix();
+
+  @override
+  String visitPrimitiveType(PrimitiveType node) {
+    if (node.name == 'boolean') {
+      return ' ? 1 : 0';
+    }
+    return '';
+  }
+
+  @override
+  String visitNonPrimitiveType(ReferredType node) {
+    return '.$_selfPointer';
+  }
+}
+
+class _FromNative extends TypeVisitor<String> {
+  const _FromNative(this.resolver, this.value);
+
+  final Resolver? resolver;
+  final String value;
+
+  @override
+  String visitPrimitiveType(PrimitiveType node) {
+    return value;
+  }
+
+  @override
+  String visitNonPrimitiveType(ReferredType node) {
+    final typeClass = node.accept(_TypeClassGenerator(resolver)).name;
+    return '$typeClass.fromRef($value)';
+  }
+}
+
+class _FieldGenerator extends Visitor<Field, void> {
+  const _FieldGenerator(this.config, this.resolver, this.s);
+
+  final Config config;
+  final Resolver? resolver;
+  final StringSink s;
+
+  void writeCAccessor(Field node) {
+    final name = node.finalName;
+    final cName = node.accept(const CFieldName());
+    final ifRef = node.isStatic ? '' : '$_jPointer, ';
+
+    s.write('''
+  static final _get_$name =
+      $_lookup<$_ffi.NativeFunction<$_jResult Function($ifRef)>>(
+              "get_$cName")
+          .asFunction<$_jResult Function($ifRef)>();
+
+''');
+
+    if (!node.isFinal) {
+      final ffiSig = node.type.accept(const _TypeSig(isFfi: true));
+      final dartSig = node.type.accept(const _TypeSig(isFfi: false));
+      s.write('''
+  static final _set_$name =
+      $_lookup<$_ffi.NativeFunction<$_jThrowable Function($ifRef$ffiSig)>>(
+              "set_$cName")
+          .asFunction<$_jThrowable Function($ifRef$dartSig)>();
+
+''');
+    }
+  }
+
+  String cGetter(Field node) {
+    final name = node.finalName;
+    final getter = node.type.accept(const _JniResultGetter());
+    final self = node.isStatic ? '' : _selfPointer;
+    return '_get_$name($self).$getter';
+  }
+
+  String cSetter(Field node) {
+    final name = node.finalName;
+    final self = node.isStatic ? '' : '$_selfPointer, ';
+    final toNativeSuffix = node.type.accept(const _ToNativeSuffix());
+    return '_set_$name(${self}value$toNativeSuffix)';
+  }
+
+  void writeDartOnlyAccessor(Field node) {
+    final name = node.finalName;
+    final ifStatic = node.isStatic ? 'Static' : '';
+    final descriptor = node.type.accept(const Descriptor());
+    s.write('''
+  static final _id_$name = 
+      $_accessors.get${ifStatic}FieldIDOf(
+        $_classRef, 
+        r"${node.name}",
+        r"$descriptor",
+      );
+''');
+  }
+
+  String dartOnlyGetter(Field node) {
+    final name = node.finalName;
+    final self = node.isStatic ? _classRef : _selfPointer;
+    final ifStatic = node.isStatic ? 'Static' : '';
+    final callType = node.type.accept(const _CallType());
+    final resultGetter = node.type.accept(const _JniResultGetter());
+    return '$_accessors.get${ifStatic}Field($self, _id_$name, $callType)'
+        '.$resultGetter';
+  }
+
+  String dartOnlySetter(Field node) {
+    final name = node.finalName;
+    final self = node.isStatic ? _classRef : _selfPointer;
+    final ifStatic = node.isStatic ? 'Static' : '';
+    final fieldType = node.type.accept(const _TypeName()).capitalize();
+    final toNativeSuffix = node.type.accept(const _ToNativeSuffix());
+    return '$_env.Set$ifStatic${fieldType}Field($self, _id_$name, value$toNativeSuffix)';
+  }
+
+  void writeDocs(Field node, {required bool writeDeleteInstructions}) {
+    final originalDecl = '${node.type.shorthand} ${node.name}';
+    s.writeln('  /// from: ${node.modifiers.join(' ')} $originalDecl');
+    if (node.type.kind != Kind.primitive && writeDeleteInstructions) {
+      s.writeln(_deleteInstruction);
+    }
+    node.javadoc?.accept(_DocGenerator(s, depth: 1));
+  }
+
+  @override
+  void visit(Field node) {
+    final isCBased = config.outputConfig.bindingsType == BindingsType.cBased;
+
+    // Check if it should be a `static const` getter.
+    if (node.isFinal && node.isStatic && node.defaultValue != null) {
+      final name = node.finalName;
+      final value = node.defaultValue!;
+      // TODO(#31): Should we leave String as a normal getter instead?
+      if (value is String || value is num || value is bool) {
+        writeDocs(node, writeDeleteInstructions: false);
+        s.write('  static const $name = ');
+        if (value is String) {
+          s.write('r"""$value"""');
+        } else {
+          s.write(value);
+        }
+        s.writeln(';\n');
+        return;
+      }
+    }
+
+    // Accessors
+    (isCBased ? writeCAccessor : writeDartOnlyAccessor)(node);
+
+    // Getter docs
+    writeDocs(node, writeDeleteInstructions: true);
+
+    final name = node.finalName;
+    final ifStatic = node.isStatic ? 'static ' : '';
+    final type = node.type.accept(_TypeGenerator(resolver));
+    s.write('$ifStatic$type get $name => ');
+    s.write(node.type.accept(
+      _FromNative(resolver, (isCBased ? cGetter : dartOnlyGetter)(node)),
+    ));
+    s.writeln(';\n');
+    if (!node.isFinal) {
+      // Setter docs
+      writeDocs(node, writeDeleteInstructions: true);
+
+      s.write('${ifStatic}set $name($type value) => ');
+      s.write((isCBased ? cSetter : dartOnlySetter)(node));
+      s.writeln(';\n');
+    }
+  }
+}
+
+class _MethodTypeSig extends Visitor<Method, String> {
+  const _MethodTypeSig({required this.isFfi});
+
+  final bool isFfi;
+
+  @override
+  String visit(Method node) {
+    final args = [
+      if (!node.isCtor && !node.isStatic) _voidPointer,
+      ...node.params.map((param) => param.type).accept(
+            _TypeSig(isFfi: isFfi),
+          )
+    ].join(', ');
+    return '$_jResult Function($args)';
+  }
+}
+
+/// Generates Dart bindings for Java methods.
+class _MethodGenerator extends Visitor<Method, void> {
+  const _MethodGenerator(this.config, this.resolver, this.s);
+
+  final Config config;
+  final Resolver? resolver;
+  final StringSink s;
+
+  void writeCAccessor(Method node) {
+    final name = node.finalName;
+    final cName = node.accept(const CMethodName());
+    final ffiSig = node.accept(const _MethodTypeSig(isFfi: true));
+    final dartSig = node.accept(const _MethodTypeSig(isFfi: false));
+    s.write('''
+  static final _$name =
+          $_lookup<$_ffi.NativeFunction<$ffiSig>>("$cName")
+      .asFunction<$dartSig>();
+
+''');
+  }
+
+  void writeDartOnlyAccessor(Method node) {
+    final name = node.finalName;
+    final ifStatic = node.isStatic ? 'Static' : '';
+    final signature = node.accept(const MethodSignature());
+    s.write('''
+  static final _id_$name = $_accessors.get${ifStatic}MethodIDOf(
+          $_classRef, r"${node.name}", r"$signature");
+
+''');
+  }
+
+  bool isSuspendFun(Method node) {
+    return node.asyncReturnType != null;
+  }
+
+  String cCtor(Method node) {
+    final name = node.finalName;
+    final params = node.params.accept(const _ParamCall()).join(', ');
+    return '_$name($params)';
+  }
+
+  String dartOnlyCtor(Method node) {
+    final name = node.finalName;
+    final params = node.params.accept(const _ParamCall()).join(', ');
+    return '$_accessors.newObjectWithArgs($_classRef, _id_$name, [$params])';
+  }
+
+  String cMethodCall(Method node) {
+    final name = node.finalName;
+    final params = [
+      if (!node.isStatic) _selfPointer,
+      ...node.params.accept(const _ParamCall()),
+    ].join(', ');
+    final resultGetter = node.returnType.accept(const _JniResultGetter());
+    return '_$name($params).$resultGetter';
+  }
+
+  String dartOnlyMethodCall(Method node) {
+    final name = node.finalName;
+    final ifStatic = node.isStatic ? 'Static' : '';
+    final self = node.isStatic ? _classRef : _selfPointer;
+    final callType = node.returnType.accept(const _CallType());
+    final params = node.params.accept(const _ParamCall()).join(', ');
+    final resultGetter = node.returnType.accept(const _JniResultGetter());
+    return '$_accessors.call${ifStatic}MethodWithArgs($self, _id_$name, $callType, [$params]).$resultGetter';
+  }
+
+  @override
+  void visit(Method node) {
+    final isCBased = config.outputConfig.bindingsType == BindingsType.cBased;
+
+    // Accessors
+    (isCBased ? writeCAccessor : writeDartOnlyAccessor)(node);
+
+    // Docs
+    s.write('  /// from: ');
+    s.writeAll(node.modifiers.map((m) => '$m '));
+    s.write('${node.returnType.shorthand} ${node.name}(');
+    s.writeAll(node.params.map((p) => '${p.type.shorthand} ${p.name}'), ', ');
+    s.writeln(')');
+    if (node.returnType.kind != Kind.primitive || node.isCtor) {
+      s.writeln(_deleteInstruction);
+    }
+    node.javadoc?.accept(_DocGenerator(s, depth: 1));
+
+    if (node.isCtor) {
+      final className = node.classDecl.finalName;
+      final name = node.finalName;
+      final ctorName = name == 'ctor' ? className : '$className.$name';
+      final paramsDef = [
+        ...node.classDecl.allTypeParams.accept(const _ClassTypeParamDef()),
+        ...node.params.accept(_ParamDef(resolver)),
+      ].join(', ');
+      final superClass = (node.classDecl.superclass!.type as DeclaredType);
+      final superTypeClassesCall = superClass.classDecl == ClassDecl.object
+          ? ''
+          : superClass.params
+              .accept(_TypeClassGenerator(resolver))
+              .map((typeClass) => '${typeClass.name},')
+              .join(_newLine(depth: 2));
+      final ctorExpr = (isCBased ? cCtor : dartOnlyCtor)(node);
+      s.write('''
+  $ctorName($paramsDef) : super.fromRef(
+    $superTypeClassesCall
+    $ctorExpr.object
+  );
+
+''');
+      return;
+    }
+
+    final name = node.finalName;
+    final returnType = isSuspendFun(node)
+        ? 'Future<${node.asyncReturnType!.accept(_TypeGenerator(resolver))}>'
+        : node.returnType.accept(_TypeGenerator(resolver));
+    final returnTypeClass = (node.asyncReturnType ?? node.returnType)
+        .accept(_TypeClassGenerator(resolver))
+        .name;
+    final ifStatic = node.isStatic ? 'static ' : '';
+    final defArgs = [
+      ...node.typeParams.accept(const _MethodTypeParamDef()),
+      ...node.params.accept(_ParamDef(resolver))
+    ];
+    final typeParamsDef = _encloseIfNotEmpty(
+      '<',
+      node.typeParams
+          .accept(const _TypeParamGenerator(withExtends: true))
+          .join(', '),
+      '>',
+    );
+    if (isSuspendFun(node)) {
+      defArgs.removeLast();
+    }
+    final params = defArgs.join(', ');
+    s.write('  $ifStatic$returnType $name$typeParamsDef($params) ');
+    final callExpr = (isCBased ? cMethodCall : dartOnlyMethodCall)(node);
+    if (isSuspendFun(node)) {
+      final returning =
+          node.asyncReturnType!.accept(_FromNative(resolver, '\$o'));
+      s.write('''async {
+    final \$p = ReceivePort();
+    final \$c = $_jObject.fromRef($_jni.Jni.newPortContinuation(\$p));
+    $callExpr;
+    final \$o = $_jPointer.fromAddress(await \$p.first);
+    final \$k = $returnTypeClass.getClass().reference;
+    if ($_jni.Jni.env.IsInstanceOf(\$o, \$k) == 0) {
+      throw "Failed";
+    }
+    return $returning;
+  }
+
+''');
+    } else {
+      final returning = node.returnType.accept(_FromNative(resolver, callExpr));
+      s.writeln('=> $returning;\n');
+    }
+  }
+}
+
+/// Generates the method type param definition.
+///
+/// For example `JObjType<T> $T` in:
+/// ```dart
+/// void bar(JObjType<T> $T, ...) => ...
+/// ```
+class _MethodTypeParamDef extends Visitor<TypeParam, String> {
+  const _MethodTypeParamDef();
+
+  @override
+  String visit(TypeParam node) {
+    return '$_jType<${node.name}> $_typeParamPrefix${node.name}';
+  }
+}
+
+/// Generates the class type param definition. Used only in constructors.
+///
+/// For example `this.$T` in:
+/// ```dart
+/// class Foo {
+///   final JObjType<T> $T;
+///   Foo(this.$T, ...) => ...
+/// }
+/// ```
+class _ClassTypeParamDef extends Visitor<TypeParam, String> {
+  const _ClassTypeParamDef();
+
+  @override
+  String visit(TypeParam node) {
+    return 'this.$_typeParamPrefix${node.name}';
+  }
+}
+
+/// Method parameter's definition.
+///
+/// For example `Foo foo` in:
+/// ```dart
+/// void bar(Foo foo) => ...
+/// ```
+class _ParamDef extends Visitor<Param, String> {
+  const _ParamDef(this.resolver);
+
+  final Resolver? resolver;
+
+  @override
+  String visit(Param node) {
+    final type = node.type.accept(_TypeGenerator(resolver));
+    return '$type ${node.finalName}';
+  }
+}
+
+/// Method parameter used in calling the native method.
+///
+/// For example `foo.reference` in:
+/// ```dart
+/// void bar(Foo foo) => _bar(foo.reference);
+/// ```
+class _ParamCall extends Visitor<Param, String> {
+  const _ParamCall();
+
+  @override
+  String visit(Param node) {
+    final nativeSuffix = node.type.accept(const _ToNativeSuffix());
+    return '${node.finalName}$nativeSuffix';
+  }
+}
diff --git a/pkgs/jnigen/lib/src/bindings/excluder.dart b/pkgs/jnigen/lib/src/bindings/excluder.dart
new file mode 100644
index 0000000..88beefd
--- /dev/null
+++ b/pkgs/jnigen/lib/src/bindings/excluder.dart
@@ -0,0 +1,61 @@
+// Copyright (c) 2023, 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 '../config/config.dart';
+import '../elements/elements.dart';
+import '../logging/logging.dart';
+import 'visitor.dart';
+
+bool _isPrivate(ClassMember classMember) =>
+    !classMember.isPublic && !classMember.isProtected;
+
+class Excluder extends Visitor<Classes, void> {
+  const Excluder(this.config);
+
+  final Config config;
+
+  @override
+  void visit(Classes node) {
+    node.decls.removeWhere((_, classDecl) {
+      final excluded = _isPrivate(classDecl) ||
+          !(config.exclude?.classes?.included(classDecl) ?? true);
+      if (excluded) {
+        log.fine('Excluded class ${classDecl.binaryName}');
+      }
+      return excluded;
+    });
+    final classExcluder = _ClassExcluder(config);
+    for (final classDecl in node.decls.values) {
+      classDecl.accept(classExcluder);
+    }
+  }
+}
+
+class _ClassExcluder extends Visitor<ClassDecl, void> {
+  _ClassExcluder(this.config);
+
+  final Config config;
+
+  @override
+  void visit(ClassDecl node) {
+    node.methods = node.methods.where((method) {
+      final included = !_isPrivate(method) &&
+          !method.name.startsWith('_') &&
+          (config.exclude?.methods?.included(node, method) ?? true);
+      if (!included) {
+        log.fine('Excluded method ${node.binaryName}#${method.name}');
+      }
+      return included;
+    }).toList();
+    node.fields = node.fields.where((field) {
+      final included = !_isPrivate(field) &&
+          !field.name.startsWith('_') &&
+          (config.exclude?.fields?.included(node, field) ?? true);
+      if (!included) {
+        log.fine('Excluded field ${node.binaryName}#${field.name}');
+      }
+      return included;
+    }).toList();
+  }
+}
diff --git a/pkgs/jnigen/lib/src/bindings/linker.dart b/pkgs/jnigen/lib/src/bindings/linker.dart
new file mode 100644
index 0000000..f4fe617
--- /dev/null
+++ b/pkgs/jnigen/lib/src/bindings/linker.dart
@@ -0,0 +1,175 @@
+// Copyright (c) 2023, 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 'visitor.dart';
+import '../config/config.dart';
+import '../elements/elements.dart';
+
+typedef _Resolver = ClassDecl Function(String? binaryName);
+
+/// Adds references from child elements back to their parent elements.
+/// Resolves Kotlin specific `asyncReturnType` for methods.
+class Linker extends Visitor<Classes, void> {
+  Linker(this.config);
+
+  final Config config;
+
+  @override
+  void visit(Classes node) {
+    final classLinker = _ClassLinker(config, (binaryName) {
+      return ClassDecl.predefined[binaryName] ??
+          node.decls[binaryName] ??
+          ClassDecl.object;
+    });
+    for (final classDecl in node.decls.values) {
+      classDecl.accept(classLinker);
+    }
+  }
+}
+
+class _ClassLinker extends Visitor<ClassDecl, void> {
+  _ClassLinker(this.config, this.resolve);
+
+  final Config config;
+  final _Resolver resolve;
+  final Set<ClassDecl> _linked = {...ClassDecl.predefined.values};
+
+  @override
+  void visit(ClassDecl node) {
+    if (_linked.contains(node)) return;
+    _linked.add(node);
+
+    node.parent = resolve(node.parentName);
+    node.parent!.accept(this);
+    // Adding type params of outer classes to the nested classes
+    final allTypeParams = <TypeParam>[];
+    if (!node.modifiers.contains('static')) {
+      for (final typeParam in node.parent!.allTypeParams) {
+        if (!node.allTypeParams.contains(typeParam)) {
+          // Add only if it's not shadowing another type param.
+          allTypeParams.add(typeParam);
+        }
+      }
+    }
+    allTypeParams.addAll(node.typeParams);
+    node.allTypeParams = allTypeParams;
+
+    final typeLinker = _TypeLinker(resolve);
+    node.superclass ??= TypeUsage.object;
+    node.superclass!.type.accept(typeLinker);
+    final superclass = (node.superclass!.type as DeclaredType).classDecl;
+    superclass.accept(this);
+
+    final fieldLinker = _FieldLinker(typeLinker);
+    for (final field in node.fields) {
+      field.classDecl = node;
+      field.accept(fieldLinker);
+    }
+    final methodLinker = _MethodLinker(config, typeLinker);
+    for (final method in node.methods) {
+      method.classDecl = node;
+      method.accept(methodLinker);
+    }
+    node.interfaces.accept(typeLinker).toList();
+    node.typeParams.accept(_TypeParamLinker(typeLinker)).toList();
+  }
+}
+
+class _MethodLinker extends Visitor<Method, void> {
+  _MethodLinker(this.config, this.typeVisitor);
+
+  final Config config;
+  final TypeVisitor<void> typeVisitor;
+
+  @override
+  void visit(Method node) {
+    node.returnType.accept(typeVisitor);
+    final typeParamLinker = _TypeParamLinker(typeVisitor);
+    final paramLinker = _ParamLinker(typeVisitor);
+    node.typeParams.accept(typeParamLinker).toList();
+    node.params.accept(paramLinker).toList();
+    // Kotlin specific
+    const kotlinContinutationType = 'kotlin.coroutines.Continuation';
+    if (config.suspendFunToAsync &&
+        node.params.isNotEmpty &&
+        node.params.last.type.kind == Kind.declared &&
+        node.params.last.type.shorthand == kotlinContinutationType) {
+      final continuationType = node.params.last.type.type as DeclaredType;
+      node.asyncReturnType = continuationType.params.isEmpty
+          ? TypeUsage.object
+          : continuationType.params.first;
+    } else {
+      node.asyncReturnType = null;
+    }
+    node.asyncReturnType?.accept(typeVisitor);
+  }
+}
+
+class _TypeLinker extends TypeVisitor<void> {
+  const _TypeLinker(this.resolve);
+
+  final _Resolver resolve;
+
+  @override
+  void visitDeclaredType(DeclaredType node) {
+    node.params.accept(this).toList();
+    node.classDecl = resolve(node.binaryName);
+  }
+
+  @override
+  void visitWildcard(Wildcard node) {
+    node.superBound?.type.accept(this);
+    node.extendsBound?.type.accept(this);
+  }
+
+  @override
+  void visitArrayType(ArrayType node) {
+    node.type.accept(this);
+  }
+
+  @override
+  void visitPrimitiveType(PrimitiveType node) {
+    // Do nothing
+  }
+
+  @override
+  void visitNonPrimitiveType(ReferredType node) {
+    // Do nothing
+  }
+}
+
+class _FieldLinker extends Visitor<Field, void> {
+  _FieldLinker(this.typeVisitor);
+
+  final TypeVisitor<void> typeVisitor;
+
+  @override
+  void visit(Field node) {
+    node.type.accept(typeVisitor);
+  }
+}
+
+class _TypeParamLinker extends Visitor<TypeParam, void> {
+  _TypeParamLinker(this.typeVisitor);
+
+  final TypeVisitor<void> typeVisitor;
+
+  @override
+  void visit(TypeParam node) {
+    for (final bound in node.bounds) {
+      bound.accept(typeVisitor);
+    }
+  }
+}
+
+class _ParamLinker extends Visitor<Param, void> {
+  _ParamLinker(this.typeVisitor);
+
+  final TypeVisitor<void> typeVisitor;
+
+  @override
+  void visit(Param node) {
+    node.type.accept(typeVisitor);
+  }
+}
diff --git a/pkgs/jnigen/lib/src/bindings/preprocessor.dart b/pkgs/jnigen/lib/src/bindings/preprocessor.dart
deleted file mode 100644
index 0003f22..0000000
--- a/pkgs/jnigen/lib/src/bindings/preprocessor.dart
+++ /dev/null
@@ -1,166 +0,0 @@
-// 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:jnigen/src/elements/elements.dart';
-import 'package:jnigen/src/config/config.dart';
-import 'package:jnigen/src/logging/logging.dart';
-import 'package:jnigen/src/util/rename_conflict.dart';
-
-import 'common.dart';
-
-/// Preprocessor which fills information needed by both Dart and C generators.
-abstract class ApiPreprocessor {
-  static const kotlinContinutationType = 'kotlin.coroutines.Continuation';
-
-  static void preprocessAll(Map<String, ClassDecl> classes, Config config,
-      {bool renameClasses = false}) {
-    final classNameCounts = <String, int>{};
-    for (final c in classes.values) {
-      final className = getSimplifiedClassName(c.binaryName);
-      c.uniqueName = renameConflict(classNameCounts, className);
-      if (renameClasses) {
-        c.finalName = c.uniqueName;
-      } else {
-        c.finalName = className;
-      }
-      _preprocess(c, classes, config);
-    }
-    // Adding type params of outer classes to the nested classes
-    final visited = <ClassDecl>{};
-    for (final c in classes.values) {
-      _traverseAddTypeParams(visited, c);
-    }
-  }
-
-  static void _preprocess(
-      ClassDecl decl, Map<String, ClassDecl> classes, Config config) {
-    if (decl.isPreprocessed) return;
-
-    if (!_isClassIncluded(decl, config)) {
-      decl.isIncluded = false;
-      decl.isPreprocessed = true;
-      log.fine('exclude class ${decl.binaryName}');
-      // Excluding all the class's methods and fields
-      for (final method in decl.methods) {
-        method.isIncluded = false;
-      }
-      for (final field in decl.fields) {
-        field.isIncluded = false;
-      }
-      return;
-    }
-    if (decl.parentName != null && classes.containsKey(decl.parentName!)) {
-      decl.parent = classes[decl.parentName];
-    }
-    ClassDecl? superclass;
-    if (decl.superclass != null && classes.containsKey(decl.superclass?.name)) {
-      superclass = classes[decl.superclass!.name]!;
-      _preprocess(superclass, classes, config);
-      // again, un-consider superclass if it was excluded through config
-      if (!superclass.isIncluded) {
-        superclass = null;
-      } else {
-        decl.nameCounts.addAll(superclass.nameCounts);
-      }
-    }
-    log.finest('Superclass of ${decl.binaryName} resolved to '
-        '${superclass?.binaryName}');
-    for (var field in decl.fields) {
-      if (!_isFieldIncluded(decl, field, config)) {
-        field.isIncluded = false;
-        log.fine('exclude ${decl.binaryName}#${field.name}');
-        continue;
-      }
-      field.finalName = renameConflict(decl.nameCounts, field.name);
-    }
-
-    for (var method in decl.methods) {
-      if (!_isMethodIncluded(decl, method, config)) {
-        method.isIncluded = false;
-        log.fine('exclude method ${decl.binaryName}#${method.name}');
-        continue;
-      }
-      if (config.suspendFunToAsync &&
-          method.params.isNotEmpty &&
-          method.params.last.type.kind == Kind.declared &&
-          method.params.last.type.shorthand == kotlinContinutationType) {
-        final continuationType = method.params.last.type.type as DeclaredType;
-        method.asyncReturnType = continuationType.params.isEmpty
-            ? TypeUsage.object
-            : continuationType.params.first;
-      } else {
-        method.asyncReturnType = null;
-      }
-
-      var realName = method.name;
-      if (isCtor(method)) {
-        realName = 'ctor';
-      }
-      final sig = method.javaSig;
-      // if method already in super class, assign its number, overriding it.
-      final superNum = superclass?.methodNumsAfterRenaming[sig];
-      if (superNum != null) {
-        // TODO(#29): this logic would better live in a dedicated renamer class.
-        // don't rename if superNum == 0
-        // unless the method name is a keyword.
-        final superNumText = superNum == 0 ? '' : '$superNum';
-        final methodName = superNum == 0 ? kwRename(realName) : realName;
-        method.finalName = '$methodName$superNumText';
-        decl.methodNumsAfterRenaming[sig] = superNum;
-      } else {
-        method.finalName = renameConflict(decl.nameCounts, realName);
-        // TODO(#29): This is too much coupled with renameConflict impl.
-        // see the above todo.
-        decl.methodNumsAfterRenaming[sig] = decl.nameCounts[realName]! - 1;
-      }
-    }
-    decl.isPreprocessed = true;
-    log.fine('preprocessed ${decl.binaryName}');
-  }
-
-  /// Gathers all the type params from ancestors of a [ClassDecl] and store
-  /// them in [allTypeParams].
-  ///
-  /// Class A is a parent of Class B when B is nested inside A.
-  static void _traverseAddTypeParams(Set<ClassDecl> visited, ClassDecl decl) {
-    if (visited.contains(decl)) {
-      // The type params of its ancestors have already been added.
-      return;
-    }
-    final allTypeParams = <TypeParam>[];
-    if (decl.parent != null) {
-      // Adding all type params of parent's ancestors to the parent.
-      if (!visited.contains(decl.parent) && decl.parent != decl) {
-        _traverseAddTypeParams(visited, decl.parent!);
-      }
-      // Adding the type params of ancestors if the class is not static.
-      if (!decl.modifiers.contains('static')) {
-        for (final typeParam in decl.parent!.allTypeParams) {
-          if (!decl.allTypeParams.contains(typeParam)) {
-            // Add only if it's not shadowing another type param.
-            allTypeParams.add(typeParam);
-          }
-        }
-      }
-    }
-    allTypeParams.addAll(decl.typeParams);
-    decl.allTypeParams = allTypeParams;
-    visited.add(decl);
-  }
-
-  static bool _isPublicOrProtected(Set<String> modifiers) =>
-      modifiers.contains("public") || modifiers.contains("protected");
-
-  static bool _isFieldIncluded(ClassDecl decl, Field field, Config config) =>
-      _isPublicOrProtected(field.modifiers) &&
-      !field.name.startsWith('_') &&
-      config.exclude?.fields?.included(decl, field) != false;
-  static bool _isMethodIncluded(ClassDecl decl, Method method, Config config) =>
-      _isPublicOrProtected(method.modifiers) &&
-      !method.name.startsWith('_') &&
-      config.exclude?.methods?.included(decl, method) != false;
-  static bool _isClassIncluded(ClassDecl decl, Config config) =>
-      _isPublicOrProtected(decl.modifiers) &&
-      config.exclude?.classes?.included(decl) != false;
-}
diff --git a/pkgs/jnigen/lib/src/bindings/pure_dart_bindings.dart b/pkgs/jnigen/lib/src/bindings/pure_dart_bindings.dart
deleted file mode 100644
index 67ceeed..0000000
--- a/pkgs/jnigen/lib/src/bindings/pure_dart_bindings.dart
+++ /dev/null
@@ -1,286 +0,0 @@
-// 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:jnigen/src/elements/elements.dart';
-import 'package:jnigen/src/config/config.dart';
-import 'package:jnigen/src/logging/logging.dart';
-
-import 'symbol_resolver.dart';
-import 'common.dart';
-
-class PureDartBindingsGenerator extends BindingsGenerator {
-  static final indent = BindingsGenerator.indent;
-  static const accessors = 'jniAccessors';
-  static const env = 'jniEnv';
-
-  /// Name for reference in base class.
-  static const selfPointer = BindingsGenerator.selfPointer;
-
-  /// Name of field holding class reference.
-  static const classRef = '_classRef';
-
-  static const ffi = BindingsGenerator.ffi;
-  static const jni = BindingsGenerator.jni;
-
-  static const voidPointer = BindingsGenerator.jobjectType;
-  static const ffiVoidType = BindingsGenerator.ffiVoidType;
-  static const jniObjectType = BindingsGenerator.jniObjectType;
-
-  static final _deleteInstruction = BindingsGenerator.deleteInstruction;
-
-  String escapeDollarSign(String s) => s.replaceAll('\$', '\\\$');
-
-  PureDartBindingsGenerator(this.config);
-  final Config config;
-
-  @override
-  String generateBindings(ClassDecl decl, SymbolResolver resolver) {
-    if (!decl.isPreprocessed) {
-      throw StateError(
-          'Java class declaration ${decl.binaryName} must be preprocessed before'
-          'being passed to bindings generator');
-    }
-    if (!decl.isIncluded) {
-      return '';
-    }
-    final bindings = _class(decl, resolver);
-    log.fine('generated bindings for class ${decl.binaryName}');
-    return bindings;
-  }
-
-  String _class(ClassDecl decl, SymbolResolver resolver) {
-    final s = StringBuffer();
-
-    s.write('/// from: ${decl.binaryName}\n');
-    s.write(breakDocComment(decl.javadoc, depth: ''));
-
-    final internalName = escapeDollarSign(getInternalName(decl.binaryName));
-
-    s.write(dartClassDefinition(decl, resolver));
-    s.write(
-        '${indent}static final $classRef = $accessors.getClassOf("$internalName");\n');
-
-    s.write(dartStaticTypeGetter(decl));
-    for (var field in decl.fields) {
-      if (!field.isIncluded) continue;
-      try {
-        s.write(_field(decl, field, resolver));
-        s.writeln();
-      } on SkipException catch (e) {
-        log.fine('skip field ${decl.binaryName}#${field.name}: '
-            '${e.message}');
-      }
-    }
-
-    for (var method in decl.methods) {
-      if (!method.isIncluded) {
-        continue;
-      }
-      try {
-        s.write(_method(decl, method, resolver));
-        s.writeln();
-      } on SkipException catch (e) {
-        log.fine('skip field ${decl.binaryName}#${method.name}: '
-            '${e.message}');
-      }
-    }
-    s.write("}\n");
-    s.write(dartTypeClass(decl));
-    s.write(dartArrayExtension(decl));
-    return s.toString();
-  }
-
-  @override
-  String toNativeArg(String name, TypeUsage type,
-      {bool convertBooleanToInt = false}) {
-    return super
-        .toNativeArg(name, type, convertBooleanToInt: convertBooleanToInt);
-  }
-
-  @override
-  String actualArgs(Method m, {bool addSelf = false}) {
-    return super.actualArgs(m, addSelf: addSelf);
-  }
-
-  String _method(ClassDecl c, Method m, SymbolResolver resolver) {
-    final name = m.finalName;
-    final isStatic = isStaticMethod(m);
-    final ifStatic = isStatic ? 'Static' : '';
-    final s = StringBuffer();
-    final mID = '_id_$name';
-    final jniSignature = escapeDollarSign(getJniSignatureForMethod(m));
-
-    s.write('static final $mID = $accessors.get${ifStatic}MethodIDOf('
-        '$classRef, "${m.name}", "$jniSignature");\n');
-
-    // Different logic for constructor and method;
-    // For constructor, we want return type to be new object.
-    var returnType = getDartOuterType(
-      m.asyncReturnType ?? m.returnType,
-      resolver,
-    );
-    if (m.asyncReturnType != null) {
-      returnType = toFuture(returnType);
-    }
-    final returnTypeClass =
-        getDartTypeClass(m.asyncReturnType ?? m.returnType, resolver);
-    s.write('$indent/// from: ${getOriginalMethodHeader(m)}\n');
-    if (!isPrimitive(m.returnType) || isCtor(m)) {
-      s.write(_deleteInstruction);
-    }
-    s.write(breakDocComment(m.javadoc));
-    if (isCtor(m)) {
-      final wrapperExpr =
-          '$accessors.newObjectWithArgs($classRef, $mID, [${actualArgs(m)}]).object';
-      final className = c.finalName;
-      final ctorFnName = name == 'ctor' ? className : '$className.$name';
-      s.write(
-        '$ctorFnName(${getFormalArgs(c, m, resolver)}) : '
-        'super.fromRef(${dartSuperArgs(c, resolver)}$wrapperExpr);\n',
-      );
-      return s.toString();
-    }
-
-    s.write(indent);
-    if (isStatic) {
-      s.write('static ');
-    }
-    final selfArgument = isStatic ? classRef : selfPointer;
-    final callType = getCallType(m.returnType);
-    final resultGetter = getResultGetterName(m.returnType);
-    final typeParamsWithExtend =
-        dartTypeParams(m.typeParams, includeExtends: true);
-    var wrapperExpr = '$accessors.call${ifStatic}MethodWithArgs'
-        '($selfArgument, $mID, $callType, [${actualArgs(m)}])'
-        '.$resultGetter';
-    s.write(
-      '$returnType $name$typeParamsWithExtend(${getFormalArgs(c, m, resolver)}) ',
-    );
-    if (m.asyncReturnType == null) {
-      wrapperExpr = toDartResult(wrapperExpr, m.returnType, returnTypeClass);
-      s.write('=> $wrapperExpr;\n');
-    } else {
-      s.write(
-        'async {\n'
-        '${indent * 2}final \$p = ReceivePort();\n'
-        '${indent * 2}final \$c = ${jni}Jni.newPortContinuation(\$p);\n'
-        '${indent * 2}$wrapperExpr;\n'
-        '${indent * 2}final \$o = $voidPointer.fromAddress(await \$p.first);\n'
-        '${indent * 2}final \$k = $returnTypeClass.getClass().reference;\n'
-        '${indent * 2}if (${jni}Jni.env.IsInstanceOf(\$o, \$k) == 0) {\n'
-        '${indent * 3}throw "Failed";\n'
-        '${indent * 2}}\n'
-        '${indent * 2}return ${toDartResult(
-          '\$o',
-          m.returnType,
-          returnTypeClass,
-        )};\n'
-        '}\n',
-      );
-    }
-    return s.toString();
-  }
-
-  String _field(ClassDecl c, Field f, SymbolResolver resolver) {
-    final name = f.finalName;
-    final isStatic = isStaticField(f);
-    final ifStatic = isStatic ? 'Static' : '';
-    final isFinal = isFinalField(f);
-    final s = StringBuffer();
-    final selfArgument = isStatic ? classRef : selfPointer;
-    final callType = getCallType(f.type);
-    final resultGetter = getResultGetterName(f.type);
-    final outerType = getDartOuterType(f.type, resolver);
-
-    void writeDocs({bool writeDeleteInstruction = true}) {
-      s.write('$indent/// from: ${getOriginalFieldDecl(f)}\n');
-      if (!isPrimitive(f.type) && writeDeleteInstruction) {
-        s.write(_deleteInstruction);
-      }
-      s.write(breakDocComment(f.javadoc));
-    }
-
-    if (isStatic && isFinal && f.defaultValue != null) {
-      writeDocs(writeDeleteInstruction: false);
-      s.write(
-          '${indent}static const $name = ${getDartLiteral(f.defaultValue)};\n');
-      return s.toString();
-    }
-    final jniSignature = escapeDollarSign(getDescriptor(f.type));
-    final fID = '_id_$name';
-    s.write('static final $fID = '
-        '$accessors.get${ifStatic}FieldIDOf(_classRef, '
-        '"${f.name}","$jniSignature");\n');
-    void writeAccessor({bool setter = false}) {
-      writeDocs();
-      s.write(indent);
-      if (isStatic) s.write('static ');
-      if (setter) {
-        final fieldType = getTypeNameAtCallSite(f.type);
-        s.write('set $name($outerType value) => $env.Set$ifStatic'
-            '${fieldType}Field($selfArgument, $fID, '
-            '${toNativeArg("value", f.type, convertBooleanToInt: true)});\n');
-      } else {
-        final outer = getDartOuterType(f.type, resolver);
-        final typeClass = getDartTypeClass(f.type, resolver);
-        final callExpr =
-            '$accessors.get${ifStatic}Field($selfArgument, $fID, $callType)'
-            '.$resultGetter';
-        final resultExpr = toDartResult(callExpr, f.type, typeClass);
-        s.write('$outer get $name => $resultExpr;\n');
-      }
-    }
-
-    writeAccessor(setter: false);
-    if (!isFinalField(f)) writeAccessor(setter: true);
-    return s.toString();
-  }
-
-  static const autoGeneratedNotice = '// Autogenerated by jnigen. '
-      'DO NOT EDIT!\n\n';
-  static const defaultLintSuppressions =
-      '// ignore_for_file: annotate_overrides\n'
-      '// ignore_for_file: camel_case_extensions\n'
-      '// ignore_for_file: camel_case_types\n'
-      '// ignore_for_file: constant_identifier_names\n'
-      '// ignore_for_file: file_names\n'
-      '// ignore_for_file: no_leading_underscores_for_local_identifiers\n'
-      '// ignore_for_file: non_constant_identifier_names\n'
-      '// ignore_for_file: overridden_fields\n'
-      '// ignore_for_file: unnecessary_cast\n'
-      '// ignore_for_file: unused_element\n'
-      '// ignore_for_file: unused_field\n'
-      '// ignore_for_file: unused_import\n'
-      '// ignore_for_file: unused_shown_name\n'
-      '\n';
-
-  static const dartImports = 'import "dart:isolate" show ReceivePort;\n\n';
-  static const jniImport = 'import "package:jni/jni.dart" as jni;\n\n';
-  static const internalHelpersImport =
-      'import "package:jni/internal_helpers_for_jnigen.dart";\n\n';
-  static const defaultImports = dartImports + jniImport + internalHelpersImport;
-
-  static const initialization = 'final jniEnv = ${jni}Jni.env;\n'
-      'final jniAccessors = ${jni}Jni.accessors;\n\n';
-  static String initImport(String initFilePath) =>
-      'import "$initFilePath" show jniEnv, jniAccessors;\n\n';
-
-  @override
-  String getPostImportBoilerplate([String? initFilePath]) {
-    return (config.outputConfig.dartConfig.structure ==
-            OutputStructure.singleFile)
-        ? initialization
-        : initImport(initFilePath!);
-  }
-
-  @override
-  String getPreImportBoilerplate([String? initFilePath]) {
-    return autoGeneratedNotice + defaultLintSuppressions + defaultImports;
-  }
-
-  @override
-  String getInitFileContents() {
-    return jniImport + initialization;
-  }
-}
diff --git a/pkgs/jnigen/lib/src/bindings/renamer.dart b/pkgs/jnigen/lib/src/bindings/renamer.dart
new file mode 100644
index 0000000..e9fbda9
--- /dev/null
+++ b/pkgs/jnigen/lib/src/bindings/renamer.dart
@@ -0,0 +1,262 @@
+// Copyright (c) 2023, 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 '../config/config.dart';
+import '../elements/elements.dart';
+import '../logging/logging.dart';
+import 'visitor.dart';
+
+const Set<String> _keywords = {
+  'abstract',
+  'as',
+  'assert',
+  'async',
+  'await',
+  'break',
+  'case',
+  'catch',
+  'class',
+  'const',
+  'continue',
+  'covariant',
+  'default',
+  'deferred',
+  'do',
+  'dynamic',
+  'else',
+  'enum',
+  'export',
+  'extends',
+  'extension',
+  'external',
+  'factory',
+  'false',
+  'final',
+  'finally',
+  'for',
+  'Function',
+  'get',
+  'hide',
+  'if',
+  'implements',
+  'import',
+  'in',
+  'interface',
+  'is',
+  'late',
+  'library',
+  'mixin',
+  'new',
+  'null',
+  'on',
+  'operator',
+  'part',
+  'required',
+  'rethrow',
+  'return',
+  'set',
+  'show',
+  'static',
+  'super',
+  'switch',
+  'sync',
+  'this',
+  'throw',
+  'true',
+  'try',
+  'typedef',
+  'var',
+  'void',
+  'while',
+  'with',
+  'yield',
+};
+
+/// Methods & properties already defined by dart JObject base class.
+const Map<String, int> _definedSyms = {
+  'equals': 1,
+  'toString': 1,
+  'hashCode': 1,
+  'runtimeType': 1,
+  'noSuchMethod': 1,
+  'reference': 1,
+  'isDeleted': 1,
+  'isNull': 1,
+  'use': 1,
+  'delete': 1,
+  'getFieldID': 1,
+  'getStaticFieldID': 1,
+  'getMethodID': 1,
+  'getStaticMethodID': 1,
+  'getField': 1,
+  'getFieldByName': 1,
+  'getStaticField': 1,
+  'getStaticFieldByName': 1,
+  'callMethod': 1,
+  'callMethodByName': 1,
+  'callStaticMethod': 1,
+  'callStaticMethodByName': 1,
+};
+
+/// Appends 0 to [name] if [name] is a keyword.
+///
+/// Examples:
+/// * `int` -> `int0`
+/// * `i` -> `i`
+String _keywordRename(String name) =>
+    _keywords.contains(name) ? '${name}0' : name;
+
+String _renameConflict(Map<String, int> counts, String name) {
+  if (counts.containsKey(name)) {
+    final count = counts[name]!;
+    final renamed = '$name$count';
+    counts[name] = count + 1;
+    return renamed;
+  }
+  counts[name] = 1;
+  return _keywordRename(name);
+}
+
+class Renamer implements Visitor<Classes, void> {
+  Renamer(this.config);
+  final Config config;
+
+  @override
+  void visit(Classes node) {
+    final classRenamer = _ClassRenamer(config);
+
+    for (final classDecl in node.decls.values) {
+      classDecl.accept(classRenamer);
+    }
+  }
+}
+
+class _ClassRenamer implements Visitor<ClassDecl, void> {
+  _ClassRenamer(
+    this.config,
+  );
+
+  final Config config;
+  final Map<String, int> classNameCounts = {};
+  final Set<ClassDecl> renamed = {...ClassDecl.predefined.values};
+  final Map<ClassDecl, Map<String, int>> nameCounts = {
+    for (final predefined in ClassDecl.predefined.values) ...{
+      predefined: {..._definedSyms},
+    }
+  };
+  final Map<ClassDecl, Map<String, int>> methodNumsAfterRenaming = {};
+
+  /// Returns class name as useful in dart.
+  ///
+  /// Eg -> a.b.X.Y -> X_Y
+  static String _getSimplifiedClassName(String binaryName) =>
+      binaryName.split('.').last.replaceAll('\$', '_');
+
+  @override
+  void visit(ClassDecl node) {
+    if (renamed.contains(node)) return;
+    renamed.add(node);
+
+    nameCounts[node] = {..._definedSyms};
+    methodNumsAfterRenaming[node] = {};
+
+    final className = _getSimplifiedClassName(node.binaryName);
+    node.uniqueName = _renameConflict(classNameCounts, className);
+
+    // When generating all the classes in a single file
+    // the names need to be unique.
+    final uniquifyName =
+        config.outputConfig.dartConfig.structure == OutputStructure.singleFile;
+    node.finalName = uniquifyName ? node.uniqueName : className;
+    log.fine('Class ${node.binaryName} is named ${node.finalName}');
+
+    final superClass = (node.superclass!.type as DeclaredType).classDecl;
+    superClass.accept(this);
+    nameCounts[node]!.addAll(nameCounts[superClass]!);
+    final methodRenamer = _MethodRenamer(
+      config,
+      nameCounts[node]!,
+      methodNumsAfterRenaming,
+    );
+    for (final method in node.methods) {
+      method.accept(methodRenamer);
+    }
+
+    final fieldRenamer = _FieldRenamer(config, nameCounts[node]!);
+    for (final field in node.fields) {
+      field.accept(fieldRenamer);
+    }
+  }
+}
+
+class _MethodRenamer implements Visitor<Method, void> {
+  _MethodRenamer(this.config, this.nameCounts, this.methodNumsAfterRenaming);
+
+  final Config config;
+  final Map<String, int> nameCounts;
+  final Map<ClassDecl, Map<String, int>> methodNumsAfterRenaming;
+
+  @override
+  void visit(Method node) {
+    final name = node.name == '<init>' ? 'ctor' : node.name;
+    final sig = node.javaSig;
+    // If node is in super class, assign its number, overriding it.
+    final superClass =
+        (node.classDecl.superclass!.type as DeclaredType).classDecl;
+    final superNum = methodNumsAfterRenaming[superClass]?[sig];
+    if (superNum != null) {
+      // Don't rename if superNum == 0
+      // Unless the node name is a keyword.
+      final superNumText = superNum == 0 ? '' : '$superNum';
+      final methodName = superNum == 0 ? _keywordRename(name) : name;
+      node.finalName = '$methodName$superNumText';
+      methodNumsAfterRenaming[node.classDecl]?[sig] = superNum;
+    } else {
+      node.finalName = _renameConflict(nameCounts, name);
+      methodNumsAfterRenaming[node.classDecl]?[sig] = nameCounts[name]! - 1;
+    }
+    log.fine(
+        'Method ${node.classDecl.binaryName}#${node.name} is named ${node.finalName}');
+
+    final paramRenamer = _ParamRenamer(config);
+    for (final param in node.params) {
+      param.accept(paramRenamer);
+    }
+
+    // Kotlin specific
+    if (node.asyncReturnType != null) {
+      // It's a suspend fun so the continuation parameter
+      // should be named $c instead
+      node.params.last.finalName = '\$c';
+    }
+  }
+}
+
+class _FieldRenamer implements Visitor<Field, void> {
+  _FieldRenamer(this.config, this.nameCounts);
+
+  final Config config;
+  final Map<String, int> nameCounts;
+
+  @override
+  void visit(Field node) {
+    node.finalName = _renameConflict(
+      nameCounts,
+      node.name,
+    );
+    log.fine(
+        'Field ${node.classDecl.binaryName}#${node.name} is named ${node.finalName}');
+  }
+}
+
+class _ParamRenamer implements Visitor<Param, void> {
+  _ParamRenamer(this.config);
+
+  final Config config;
+
+  @override
+  void visit(Param node) {
+    node.finalName = _keywordRename(node.name);
+  }
+}
diff --git a/pkgs/jnigen/lib/src/bindings/resolver.dart b/pkgs/jnigen/lib/src/bindings/resolver.dart
new file mode 100644
index 0000000..cc7a45e
--- /dev/null
+++ b/pkgs/jnigen/lib/src/bindings/resolver.dart
@@ -0,0 +1,155 @@
+// Copyright (c) 2023, 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 'dart:math';
+
+import '../logging/logging.dart';
+
+class Resolver {
+  Resolver({
+    required this.importMap,
+    required this.currentClass,
+    this.inputClassNames = const {},
+  });
+
+  static const Map<String, String> predefined = {
+    'java.lang.String': 'jni.',
+  };
+
+  /// Class corresponding to currently writing file.
+  final String currentClass;
+
+  /// Explicit import mappings.
+  final Map<String, String> importMap;
+
+  /// Names of all classes in input.
+  final Set<String> inputClassNames;
+
+  final List<String> importStrings = [];
+
+  final Set<String> _relativeImportedClasses = {};
+  final Map<String, String> _importedNameToClass = {};
+  final Map<String, String> _classToImportedName = {};
+
+  static String getFileClassName(String binaryName) {
+    final dollarSign = binaryName.indexOf('\$');
+    if (dollarSign != -1) {
+      return binaryName.substring(0, dollarSign);
+    }
+    return binaryName;
+  }
+
+  /// splits [str] into 2 from last occurence of [sep]
+  static List<String> cutFromLast(String str, String sep) {
+    final li = str.lastIndexOf(sep);
+    if (li == -1) {
+      return ['', str];
+    }
+    return [str.substring(0, li), str.substring(li + 1)];
+  }
+
+  /// Get the prefix for the class
+  String resolvePrefix(String binaryName) {
+    if (predefined.containsKey(binaryName)) {
+      return predefined[binaryName]!;
+    }
+    final target = getFileClassName(binaryName);
+
+    if (target == currentClass && inputClassNames.contains(binaryName)) {
+      return '';
+    }
+
+    if (_classToImportedName.containsKey(target)) {
+      // This package was already resolved
+      // but we still need to check if it was a relative import, in which case
+      // the class not in inputClassNames cannot be mapped here.
+      if (!_relativeImportedClasses.contains(target) ||
+          inputClassNames.contains(binaryName)) {
+        final importedName = _classToImportedName[target];
+        return '$importedName.';
+      }
+    }
+
+    final classImport = getImport(target, binaryName);
+    log.finest('$target resolved to $classImport for $binaryName');
+    if (classImport == null) {
+      return '';
+    }
+
+    final pkgName = cutFromLast(target, '.')[1].toLowerCase();
+    if (pkgName.isEmpty) {
+      throw UnsupportedError('No package could be deduced from '
+          'qualified binaryName');
+    }
+
+    // We always name imports with an underscore suffix, so that they can be
+    // never shadowed by a parameter or local variable.
+    var importedName = '${pkgName}_';
+    int suffix = 0;
+    while (_importedNameToClass.containsKey(importedName)) {
+      suffix++;
+      importedName = '$pkgName${suffix}_';
+    }
+
+    _importedNameToClass[importedName] = target;
+    _classToImportedName[target] = importedName;
+    importStrings.add('import "$classImport" as $importedName;\n');
+    return '$importedName.';
+  }
+
+  /// Returns import string for [classToResolve], or `null` if the class is not
+  /// found.
+  ///
+  /// [binaryName] is the class name trying to be resolved. This parameter is
+  /// requested so that classes included in current bindings can be resolved
+  /// using relative path.
+  String? getImport(String classToResolve, String binaryName) {
+    var prefix = classToResolve;
+
+    // short circuit if the requested class is specified directly in import map.
+    if (importMap.containsKey(binaryName)) {
+      return importMap[binaryName]!;
+    }
+
+    if (prefix.isEmpty) {
+      throw UnsupportedError('unexpected: empty package name.');
+    }
+
+    final dest = classToResolve.split('.');
+    final src = currentClass.split('.');
+    // Use relative import when the required class is included in current set
+    // of bindings.
+    if (inputClassNames.contains(binaryName)) {
+      int common = 0;
+      // find the common prefix path directory of current package, and directory
+      // of target package
+      // src.length - 1 simply corresponds to directory of the package.
+      for (int i = 0; i < src.length - 1 && i < dest.length - 1; i++) {
+        if (src[i] == dest[i]) {
+          common++;
+        } else {
+          break;
+        }
+      }
+      final pathToCommon = '../' * ((src.length - 1) - common);
+      final pathToClass = dest.sublist(max(common, 0)).join('/');
+      _relativeImportedClasses.add(classToResolve);
+      return '$pathToCommon$pathToClass.dart';
+    }
+
+    while (prefix.isNotEmpty) {
+      final split = cutFromLast(prefix, '.');
+      final left = split[0];
+      if (importMap.containsKey(prefix)) {
+        return importMap[prefix]!;
+      }
+      prefix = left;
+    }
+    return null;
+  }
+
+  List<String> getImportStrings() {
+    return importStrings;
+  }
+}
diff --git a/pkgs/jnigen/lib/src/bindings/symbol_resolver.dart b/pkgs/jnigen/lib/src/bindings/symbol_resolver.dart
deleted file mode 100644
index 4b158b9..0000000
--- a/pkgs/jnigen/lib/src/bindings/symbol_resolver.dart
+++ /dev/null
@@ -1,17 +0,0 @@
-// 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 '../elements/elements.dart';
-
-/// Resolves types referred to in method signatures etc.. and provides
-/// appropriate imports for them.
-abstract class SymbolResolver {
-  /// Resolve the binary name to a String which can be used in dart code.
-  String? resolve(String binaryName);
-
-  ClassDecl? resolveClass(String binaryName);
-
-  /// Get all imports for types so far resolved through this resolver.
-  List<String> getImportStrings();
-}
diff --git a/pkgs/jnigen/lib/src/bindings/visitor.dart b/pkgs/jnigen/lib/src/bindings/visitor.dart
new file mode 100644
index 0000000..8454cac
--- /dev/null
+++ b/pkgs/jnigen/lib/src/bindings/visitor.dart
@@ -0,0 +1,40 @@
+// Copyright (c) 2023, 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 '../elements/elements.dart';
+
+abstract class Visitor<T extends Element<T>, R> {
+  const Visitor();
+
+  R visit(T node);
+}
+
+abstract class TypeVisitor<R> {
+  const TypeVisitor();
+
+  R visitNonPrimitiveType(ReferredType node);
+  R visitPrimitiveType(PrimitiveType node);
+  R visitArrayType(ArrayType node) => visitNonPrimitiveType(node);
+  R visitDeclaredType(DeclaredType node) => visitNonPrimitiveType(node);
+  R visitTypeVar(TypeVar node) => visitNonPrimitiveType(node);
+  R visitWildcard(Wildcard node) => visitNonPrimitiveType(node);
+}
+
+extension MultiVisitor<T extends Element<T>> on Iterable<Element<T>> {
+  Iterable<R> accept<R>(Visitor<T, R> v) {
+    return map((e) => e.accept(v));
+  }
+}
+
+extension MultiTypeVisitor<T extends ReferredType> on Iterable<T> {
+  Iterable<R> accept<R>(TypeVisitor<R> v) {
+    return map((e) => e.accept(v));
+  }
+}
+
+extension MultiTypeUsageVisitor on Iterable<TypeUsage> {
+  Iterable<R> accept<R>(TypeVisitor<R> v) {
+    return map((e) => e.type.accept(v));
+  }
+}
diff --git a/pkgs/jnigen/lib/src/config/config_types.dart b/pkgs/jnigen/lib/src/config/config_types.dart
index 9918b06..48d6ee0 100644
--- a/pkgs/jnigen/lib/src/config/config_types.dart
+++ b/pkgs/jnigen/lib/src/config/config_types.dart
@@ -4,14 +4,13 @@
 
 import 'dart:io';
 
-import 'package:jnigen/src/elements/elements.dart';
-
-import 'yaml_reader.dart';
-import 'filters.dart';
-import 'config_exception.dart';
-
 import 'package:logging/logging.dart';
 
+import '../elements/elements.dart';
+import 'config_exception.dart';
+import 'filters.dart';
+import 'yaml_reader.dart';
+
 /// Configuration for dependencies to be downloaded using maven.
 ///
 /// Dependency names should be listed in groupId:artifactId:version format.
diff --git a/pkgs/jnigen/lib/src/config/filters.dart b/pkgs/jnigen/lib/src/config/filters.dart
index 962a135..1d0e1a5 100644
--- a/pkgs/jnigen/lib/src/config/filters.dart
+++ b/pkgs/jnigen/lib/src/config/filters.dart
@@ -2,7 +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 'package:jnigen/src/elements/elements.dart';
+import '../elements/elements.dart';
 
 bool _matchesCompletely(String string, Pattern pattern) {
   final match = pattern.matchAsPrefix(string);
diff --git a/pkgs/jnigen/lib/src/elements/elements.dart b/pkgs/jnigen/lib/src/elements/elements.dart
index 1edefc4..7347665 100644
--- a/pkgs/jnigen/lib/src/elements/elements.dart
+++ b/pkgs/jnigen/lib/src/elements/elements.dart
@@ -2,12 +2,20 @@
 // 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.
 
-// Types to describe java API elements
-
 import 'package:json_annotation/json_annotation.dart';
 
+// Types to describe java API elements
+
+import '../bindings/visitor.dart';
+
 part 'elements.g.dart';
 
+abstract class Element<T extends Element<T>> {
+  const Element();
+
+  R accept<R>(Visitor<T, R> v);
+}
+
 @JsonEnum()
 
 /// A kind describes the type of a declaration.
@@ -20,38 +28,32 @@
   enumKind,
 }
 
+class Classes implements Element<Classes> {
+  const Classes._(this.decls);
+
+  final Map<String, ClassDecl> decls;
+
+  factory Classes.fromJson(List<dynamic> json) {
+    final decls = <String, ClassDecl>{};
+    for (final declJson in json) {
+      final classDecl = ClassDecl.fromJson(declJson);
+      decls[classDecl.binaryName] = classDecl;
+    }
+    return Classes._(decls);
+  }
+
+  @override
+  R accept<R>(Visitor<Classes, R> v) {
+    return v.visit(this);
+  }
+}
+
 // Note: We give default values in constructor, if the field is nullable in
 // JSON. this allows us to reduce JSON size by providing Include.NON_NULL
 // option in java.
 
 @JsonSerializable(createToJson: false)
-class ClassDecl {
-  /// Methods & properties already defined by dart JObject base class.
-  static const Map<String, int> _definedSyms = {
-    'equals': 1,
-    'toString': 1,
-    'hashCode': 1,
-    'runtimeType': 1,
-    'noSuchMethod': 1,
-    'reference': 1,
-    'isDeleted': 1,
-    'isNull': 1,
-    'use': 1,
-    'delete': 1,
-    'getFieldID': 1,
-    'getStaticFieldID': 1,
-    'getMethodID': 1,
-    'getStaticMethodID': 1,
-    'getField': 1,
-    'getFieldByName': 1,
-    'getStaticField': 1,
-    'getStaticFieldByName': 1,
-    'callMethod': 1,
-    'callMethodByName': 1,
-    'callStaticMethod': 1,
-    'callStaticMethodByName': 1,
-  };
-
+class ClassDecl extends ClassMember implements Element<ClassDecl> {
   ClassDecl({
     this.annotations = const [],
     this.javadoc,
@@ -70,68 +72,96 @@
     this.values,
   });
 
-  List<Annotation> annotations;
-  JavaDocComment? javadoc;
+  @override
+  final Set<String> modifiers;
 
-  Set<String> modifiers;
-  String simpleName, binaryName;
-  String? parentName;
-  String packageName;
+  final List<Annotation> annotations;
+  final JavaDocComment? javadoc;
+  final String simpleName;
+  final String binaryName;
+  final String? parentName;
+  final String packageName;
   List<TypeParam> typeParams;
   List<Method> methods;
   List<Field> fields;
+  final List<TypeUsage> interfaces;
+  final bool hasStaticInit;
+  final bool hasInstanceInit;
+
+  /// Will default to java.lang.Object if null by [Linker].
   TypeUsage? superclass;
-  List<TypeUsage> interfaces;
-  bool hasStaticInit, hasInstanceInit;
 
-  // Contains enum constant names if class is an enum,
-  // as obtained by `.values()` method in Java.
-  List<String>? values;
-
-  factory ClassDecl.fromJson(Map<String, dynamic> json) =>
-      _$ClassDeclFromJson(json);
+  /// Contains enum constant names if class is an enum,
+  /// as obtained by `.values()` method in Java.
+  final List<String>? values;
 
   String get internalName => binaryName.replaceAll(".", "/");
 
-  // synthesized attributes
-
-  /// Final name of this class
+  /// Parent's [ClassDecl] obtained from [parentName].
+  ///
+  /// Will be populated by [Linker].
   @JsonKey(includeFromJson: false)
-  late String finalName;
+  late final ClassDecl? parent;
 
-  /// Parent's [ClassDecl] obtained from [parentName]
+  /// Final name of this class.
+  ///
+  /// Will be populated by [Renamer].
   @JsonKey(includeFromJson: false)
-  ClassDecl? parent;
-
-  /// Type parameters including the ones from its ancestors
-  @JsonKey(includeFromJson: false)
-  List<TypeParam> allTypeParams = const [];
+  late final String finalName;
 
   /// Unique name obtained by renaming conflicting names with a number.
   ///
   /// This is used by C bindings instead of fully qualified name to reduce
-  /// the verbosity of generated bindings
+  /// the verbosity of generated bindings.
+  ///
+  /// Will be populated by [Renamer].
   @JsonKey(includeFromJson: false)
-  late String uniqueName;
+  late final String uniqueName;
 
+  /// Type parameters including the ones from its ancestors
+  ///
+  /// Will be populated by [Linker].
   @JsonKey(includeFromJson: false)
-  bool isPreprocessed = false;
-  @JsonKey(includeFromJson: false)
-  bool isIncluded = true;
-
-  /// Contains number with which certain overload of a method is renamed to,
-  /// so the overriding method in subclass can be renamed to same final name.
-  @JsonKey(includeFromJson: false)
-  Map<String, int> methodNumsAfterRenaming = {};
-
-  /// Name counts map, it's a field so that it can be later used by subclasses.
-  @JsonKey(includeFromJson: false)
-  Map<String, int> nameCounts = {..._definedSyms};
+  List<TypeParam> allTypeParams = const [];
 
   @override
   String toString() {
     return 'Java class declaration for $binaryName';
   }
+
+  String get signature => 'L$internalName;';
+
+  static final object = ClassDecl(
+    binaryName: 'java.lang.Object',
+    packageName: 'java.lang',
+    simpleName: 'Object',
+  )..finalName = 'JObject';
+
+  static final string = ClassDecl(
+    superclass: TypeUsage.object,
+    binaryName: 'java.lang.String',
+    packageName: 'java.lang',
+    simpleName: 'String',
+  )..finalName = 'JString';
+
+  static final predefined = {
+    'java.lang.Object': object,
+    'java.lang.String': string,
+  };
+
+  factory ClassDecl.fromJson(Map<String, dynamic> json) =>
+      _$ClassDeclFromJson(json);
+
+  @override
+  R accept<R>(Visitor<ClassDecl, R> v) {
+    return v.visit(this);
+  }
+
+  @override
+  ClassDecl get classDecl => this;
+
+  @override
+  String get name => finalName;
 }
 
 @JsonEnum()
@@ -156,18 +186,25 @@
     required this.typeJson,
   });
 
-  static TypeUsage object = TypeUsage.fromJson({
-    "shorthand": "java.lang.Object",
-    "kind": "DECLARED",
-    "type": {"binaryName": "java.lang.Object", "simpleName": "Object"}
-  });
+  static TypeUsage object = () {
+    final typeUsage = TypeUsage.fromJson({
+      "shorthand": "java.lang.Object",
+      "kind": "DECLARED",
+      "type": {"binaryName": "java.lang.Object", "simpleName": "Object"}
+    });
+    (typeUsage.type as DeclaredType).classDecl = ClassDecl.object;
+    return typeUsage;
+  }();
 
-  String shorthand;
-  Kind kind;
-  @JsonKey(includeFromJson: false)
-  late ReferredType type;
+  final String shorthand;
+  final Kind kind;
+
   @JsonKey(name: "type")
-  Map<String, dynamic> typeJson;
+  final Map<String, dynamic> typeJson;
+
+  /// Will be populated in [TypeUsage.fromJson].
+  @JsonKey(includeFromJson: false)
+  late final ReferredType type;
 
   String get name => type.name;
 
@@ -195,52 +232,168 @@
     }
     return t;
   }
+
+  R accept<R>(TypeVisitor<R> v) {
+    return type.accept(v);
+  }
 }
 
 abstract class ReferredType {
+  const ReferredType();
   String get name;
+
+  R accept<R>(TypeVisitor<R> v);
 }
 
-@JsonSerializable(createToJson: false)
-class PrimitiveType implements ReferredType {
-  PrimitiveType({required this.name});
+class PrimitiveType extends ReferredType {
+  static const _primitives = {
+    'byte': PrimitiveType._(
+      name: 'byte',
+      signature: 'B',
+      dartType: 'int',
+      jniType: 'JByte',
+      cType: 'int8_t',
+      ffiType: 'Int8',
+    ),
+    'short': PrimitiveType._(
+      name: 'short',
+      signature: 'S',
+      dartType: 'int',
+      jniType: 'JShort',
+      cType: 'int16_t',
+      ffiType: 'Int16',
+    ),
+    'char': PrimitiveType._(
+      name: 'char',
+      signature: 'C',
+      dartType: 'int',
+      jniType: 'JChar',
+      cType: 'char',
+      ffiType: 'Uint16',
+    ),
+    'int': PrimitiveType._(
+      name: 'int',
+      signature: 'I',
+      dartType: 'int',
+      jniType: 'JInt',
+      cType: 'int32_t',
+      ffiType: 'Int32',
+    ),
+    'long': PrimitiveType._(
+      name: 'long',
+      signature: 'J',
+      dartType: 'int',
+      jniType: 'JLong',
+      cType: 'int64_t',
+      ffiType: 'Int64',
+    ),
+    'float': PrimitiveType._(
+      name: 'float',
+      signature: 'F',
+      dartType: 'double',
+      jniType: 'JFloat',
+      cType: 'float',
+      ffiType: 'Float',
+    ),
+    'double': PrimitiveType._(
+      name: 'double',
+      signature: 'D',
+      dartType: 'double',
+      jniType: 'JDouble',
+      cType: 'double',
+      ffiType: 'Double',
+    ),
+    'boolean': PrimitiveType._(
+      name: 'boolean',
+      signature: 'Z',
+      dartType: 'bool',
+      jniType: 'JBoolean',
+      cType: 'uint8_t',
+      ffiType: 'Uint8',
+    ),
+    'void': PrimitiveType._(
+      name: 'void',
+      signature: 'V',
+      dartType: 'void',
+      jniType: 'JVoid', // Never used
+      cType: 'void',
+      ffiType: 'Void',
+    ),
+  };
+
+  const PrimitiveType._({
+    required this.name,
+    required this.signature,
+    required this.dartType,
+    required this.jniType,
+    required this.cType,
+    required this.ffiType,
+  });
 
   @override
-  String name;
+  final String name;
 
-  factory PrimitiveType.fromJson(Map<String, dynamic> json) =>
-      _$PrimitiveTypeFromJson(json);
+  final String signature;
+  final String dartType;
+  final String jniType;
+  final String cType;
+  final String ffiType;
+
+  factory PrimitiveType.fromJson(Map<String, dynamic> json) {
+    return _primitives[json['name']]!;
+  }
+
+  @override
+  R accept<R>(TypeVisitor<R> v) {
+    return v.visitPrimitiveType(this);
+  }
 }
 
 @JsonSerializable(createToJson: false)
-class DeclaredType implements ReferredType {
+class DeclaredType extends ReferredType {
   DeclaredType({
     required this.binaryName,
     required this.simpleName,
     this.params = const [],
   });
-  String binaryName, simpleName;
-  List<TypeUsage> params;
+
+  final String binaryName;
+  final String simpleName;
+  final List<TypeUsage> params;
+
+  @JsonKey(includeFromJson: false)
+  late ClassDecl classDecl;
 
   @override
   String get name => binaryName;
 
   factory DeclaredType.fromJson(Map<String, dynamic> json) =>
       _$DeclaredTypeFromJson(json);
+
+  @override
+  R accept<R>(TypeVisitor<R> v) {
+    return v.visitDeclaredType(this);
+  }
 }
 
 @JsonSerializable(createToJson: false)
-class TypeVar implements ReferredType {
+class TypeVar extends ReferredType {
   TypeVar({required this.name});
+
   @override
   String name;
 
   factory TypeVar.fromJson(Map<String, dynamic> json) =>
       _$TypeVarFromJson(json);
+
+  @override
+  R accept<R>(TypeVisitor<R> v) {
+    return v.visitTypeVar(this);
+  }
 }
 
 @JsonSerializable(createToJson: false)
-class Wildcard implements ReferredType {
+class Wildcard extends ReferredType {
   Wildcard({this.extendsBound, this.superBound});
   TypeUsage? extendsBound, superBound;
 
@@ -249,10 +402,15 @@
 
   factory Wildcard.fromJson(Map<String, dynamic> json) =>
       _$WildcardFromJson(json);
+
+  @override
+  R accept<R>(TypeVisitor<R> v) {
+    return v.visitWildcard(this);
+  }
 }
 
 @JsonSerializable(createToJson: false)
-class ArrayType implements ReferredType {
+class ArrayType extends ReferredType {
   ArrayType({required this.type});
   TypeUsage type;
 
@@ -261,35 +419,58 @@
 
   factory ArrayType.fromJson(Map<String, dynamic> json) =>
       _$ArrayTypeFromJson(json);
+
+  @override
+  R accept<R>(TypeVisitor<R> v) {
+    return v.visitArrayType(this);
+  }
 }
 
 abstract class ClassMember {
   String get name;
+  ClassDecl get classDecl;
+  Set<String> get modifiers;
+
+  bool get isStatic => modifiers.contains('static');
+  bool get isFinal => modifiers.contains('final');
+  bool get isPublic => modifiers.contains('public');
+  bool get isProtected => modifiers.contains('protected');
 }
 
 @JsonSerializable(createToJson: false)
-class Method implements ClassMember {
-  Method(
-      {this.annotations = const [],
-      this.javadoc,
-      this.modifiers = const {},
-      required this.name,
-      this.typeParams = const [],
-      this.params = const [],
-      required this.returnType});
-  List<Annotation> annotations;
-  JavaDocComment? javadoc;
-  Set<String> modifiers;
+class Method extends ClassMember implements Element<Method> {
+  Method({
+    this.annotations = const [],
+    this.javadoc,
+    this.modifiers = const {},
+    required this.name,
+    this.typeParams = const [],
+    this.params = const [],
+    required this.returnType,
+  });
 
   @override
-  String name;
+  final String name;
+  @override
+  final Set<String> modifiers;
 
-  List<TypeParam> typeParams;
-  List<Param> params;
-  TypeUsage returnType;
+  final List<Annotation> annotations;
+  final JavaDocComment? javadoc;
+  final List<TypeParam> typeParams;
+  final List<Param> params;
+  final TypeUsage returnType;
 
+  /// The [ClassDecl] where this method is defined.
+  ///
+  /// Will be populated by [Linker].
+  @JsonKey(includeFromJson: false)
+  @override
+  late ClassDecl classDecl;
+
+  /// Will be populated by [Renamer].
   @JsonKey(includeFromJson: false)
   late String finalName;
+
   @JsonKey(includeFromJson: false)
   late bool isOverridden;
 
@@ -299,8 +480,6 @@
   /// and the method has a `kotlin.coroutines.Continuation` final argument.
   @JsonKey(includeFromJson: false)
   late TypeUsage? asyncReturnType;
-  @JsonKey(includeFromJson: false)
-  bool isIncluded = true;
 
   @JsonKey(includeFromJson: false)
   late String javaSig = _javaSig();
@@ -310,89 +489,136 @@
     return '${returnType.name} $name($paramNames)';
   }
 
+  bool get isCtor => name == '<init>';
+
   factory Method.fromJson(Map<String, dynamic> json) => _$MethodFromJson(json);
-}
-
-@JsonSerializable(createToJson: false)
-class Param {
-  Param(
-      {this.annotations = const [],
-      this.javadoc,
-      required this.name,
-      required this.type});
-  List<Annotation> annotations;
-  JavaDocComment? javadoc;
-
-  String name;
-  TypeUsage type;
-
-  factory Param.fromJson(Map<String, dynamic> json) => _$ParamFromJson(json);
-}
-
-@JsonSerializable(createToJson: false)
-class Field implements ClassMember {
-  Field(
-      {this.annotations = const [],
-      this.javadoc,
-      this.modifiers = const {},
-      required this.name,
-      required this.type,
-      this.defaultValue});
-
-  List<Annotation> annotations;
-  JavaDocComment? javadoc;
-
-  Set<String> modifiers;
 
   @override
-  String name;
-
-  TypeUsage type;
-  Object? defaultValue;
-
-  @JsonKey(includeFromJson: false)
-  late String finalName;
-  @JsonKey(includeFromJson: false)
-  bool isIncluded = true;
-
-  factory Field.fromJson(Map<String, dynamic> json) => _$FieldFromJson(json);
+  R accept<R>(Visitor<Method, R> v) {
+    return v.visit(this);
+  }
 }
 
 @JsonSerializable(createToJson: false)
-class TypeParam {
+class Param implements Element<Param> {
+  Param({
+    this.annotations = const [],
+    this.javadoc,
+    required this.name,
+    required this.type,
+  });
+
+  final List<Annotation> annotations;
+  final JavaDocComment? javadoc;
+  final String name;
+  final TypeUsage type;
+
+  /// Will be populated by [Renamer].
+  @JsonKey(includeFromJson: false)
+  late String finalName;
+
+  factory Param.fromJson(Map<String, dynamic> json) => _$ParamFromJson(json);
+
+  @override
+  R accept<R>(Visitor<Param, R> v) {
+    return v.visit(this);
+  }
+}
+
+@JsonSerializable(createToJson: false)
+class Field extends ClassMember implements Element<Field> {
+  Field({
+    this.annotations = const [],
+    this.javadoc,
+    this.modifiers = const {},
+    required this.name,
+    required this.type,
+    this.defaultValue,
+  });
+
+  @override
+  final String name;
+  @override
+  final Set<String> modifiers;
+
+  final List<Annotation> annotations;
+  final JavaDocComment? javadoc;
+  final TypeUsage type;
+  final Object? defaultValue;
+
+  /// The [ClassDecl] where this field is defined.
+  ///
+  /// Will be populated by [Linker].
+  @JsonKey(includeFromJson: false)
+  @override
+  late final ClassDecl classDecl;
+
+  /// Will be populated by [Renamer].
+  @JsonKey(includeFromJson: false)
+  late final String finalName;
+
+  factory Field.fromJson(Map<String, dynamic> json) => _$FieldFromJson(json);
+
+  @override
+  R accept<R>(Visitor<Field, R> v) {
+    return v.visit(this);
+  }
+}
+
+@JsonSerializable(createToJson: false)
+class TypeParam implements Element<TypeParam> {
   TypeParam({required this.name, this.bounds = const []});
-  String name;
-  List<TypeUsage> bounds;
+
+  final String name;
+  final List<TypeUsage> bounds;
 
   @JsonKey(includeFromJson: false)
-  late String erasure;
+  late final String erasure;
 
   factory TypeParam.fromJson(Map<String, dynamic> json) =>
       _$TypeParamFromJson(json);
+
+  @override
+  R accept<R>(Visitor<TypeParam, R> v) {
+    return v.visit(this);
+  }
 }
 
 @JsonSerializable(createToJson: false)
-class JavaDocComment {
-  JavaDocComment({String? comment}) : comment = comment ?? '';
-  String comment;
+class JavaDocComment implements Element<JavaDocComment> {
+  JavaDocComment({this.comment = ''});
+
+  final String comment;
 
   @JsonKey(includeFromJson: false)
-  late String dartDoc;
+  late final String dartDoc;
 
   factory JavaDocComment.fromJson(Map<String, dynamic> json) =>
       _$JavaDocCommentFromJson(json);
+
+  @override
+  R accept<R>(Visitor<JavaDocComment, R> v) {
+    return v.visit(this);
+  }
 }
 
 @JsonSerializable(createToJson: false)
-class Annotation {
-  Annotation(
-      {required this.simpleName,
-      required this.binaryName,
-      this.properties = const {}});
-  String simpleName;
-  String binaryName;
-  Map<String, Object> properties;
+class Annotation implements Element<Annotation> {
+  Annotation({
+    required this.simpleName,
+    required this.binaryName,
+    this.properties = const {},
+  });
+
+  final String simpleName;
+  final String binaryName;
+  final Map<String, Object> properties;
 
   factory Annotation.fromJson(Map<String, dynamic> json) =>
       _$AnnotationFromJson(json);
+
+  @override
+  R accept<R>(Visitor<Annotation, R> v) {
+    return v.visit(this);
+  }
 }
diff --git a/pkgs/jnigen/lib/src/elements/elements.g.dart b/pkgs/jnigen/lib/src/elements/elements.g.dart
index 64e60f7..2f09913 100644
--- a/pkgs/jnigen/lib/src/elements/elements.g.dart
+++ b/pkgs/jnigen/lib/src/elements/elements.g.dart
@@ -20,7 +20,7 @@
           const {},
       simpleName: json['simpleName'] as String,
       binaryName: json['binaryName'] as String,
-      packageName: (json['packageName'] as String?) ?? '',
+      packageName: json['packageName'] as String? ?? '',
       parentName: json['parentName'] as String?,
       typeParams: (json['typeParams'] as List<dynamic>?)
               ?.map((e) => TypeParam.fromJson(e as Map<String, dynamic>))
@@ -61,11 +61,6 @@
   Kind.array: 'ARRAY',
 };
 
-PrimitiveType _$PrimitiveTypeFromJson(Map<String, dynamic> json) =>
-    PrimitiveType(
-      name: json['name'] as String,
-    );
-
 DeclaredType _$DeclaredTypeFromJson(Map<String, dynamic> json) => DeclaredType(
       binaryName: json['binaryName'] as String,
       simpleName: json['simpleName'] as String,
@@ -156,7 +151,7 @@
 
 JavaDocComment _$JavaDocCommentFromJson(Map<String, dynamic> json) =>
     JavaDocComment(
-      comment: json['comment'] as String?,
+      comment: json['comment'] as String? ?? '',
     );
 
 Annotation _$AnnotationFromJson(Map<String, dynamic> json) => Annotation(
diff --git a/pkgs/jnigen/lib/src/generate_bindings.dart b/pkgs/jnigen/lib/src/generate_bindings.dart
index b70cf8c..9c1202d 100644
--- a/pkgs/jnigen/lib/src/generate_bindings.dart
+++ b/pkgs/jnigen/lib/src/generate_bindings.dart
@@ -5,15 +5,19 @@
 import 'dart:io';
 import 'dart:convert';
 
-import 'package:jnigen/src/util/command_output.dart';
-
+import 'bindings/dart_generator.dart';
+import 'bindings/excluder.dart';
+import 'bindings/linker.dart';
+import 'bindings/renamer.dart';
 import 'elements/elements.dart';
 import 'summary/summary.dart';
 import 'config/config.dart';
 import 'tools/tools.dart';
-import 'writers/writers.dart';
+import 'writers/bindings_writer.dart';
 import 'logging/logging.dart';
 
+void collectOutputStream(Stream<List<int>> stream, StringBuffer buffer) =>
+    stream.transform(const Utf8Decoder()).forEach(buffer.write);
 Future<void> generateJniBindings(Config config) async {
   setLoggingLevel(config.logLevel);
 
@@ -81,7 +85,7 @@
   }
   final errorLog = StringBuffer();
   collectOutputStream(process.stderr, errorLog);
-  final stream = JsonDecoder().bind(Utf8Decoder().bind(input));
+  final stream = const JsonDecoder().bind(const Utf8Decoder().bind(input));
   dynamic json;
   try {
     json = await stream.single;
@@ -95,16 +99,20 @@
     return;
   }
   final list = json as List;
-  final outputStructure = config.outputConfig.dartConfig.structure;
-  BindingsWriter outputWriter;
-  if (outputStructure == OutputStructure.packageStructure) {
-    outputWriter = FilesWriter(config);
-  } else {
-    outputWriter = SingleFileWriter(config);
+  final classes = Classes.fromJson(list);
+  final cBased = config.outputConfig.bindingsType == BindingsType.cBased;
+  classes
+    ..accept(Excluder(config))
+    ..accept(Linker(config))
+    ..accept(Renamer(config));
+
+  if (cBased) {
+    await writeCBindings(config, classes.decls.values.toList());
   }
+
   try {
-    await outputWriter
-        .writeBindings(list.map((c) => ClassDecl.fromJson(c)).toList());
+    await classes.accept(DartGenerator(config));
+    log.info('Completed');
   } on Exception catch (e, trace) {
     stderr.writeln(trace);
     log.fatal('Error while writing bindings: $e');
diff --git a/pkgs/jnigen/lib/src/summary/summary.dart b/pkgs/jnigen/lib/src/summary/summary.dart
index 900f7f3..4580dbe 100644
--- a/pkgs/jnigen/lib/src/summary/summary.dart
+++ b/pkgs/jnigen/lib/src/summary/summary.dart
@@ -4,7 +4,8 @@
 
 import 'dart:async';
 import 'dart:io';
-import 'package:jnigen/src/logging/logging.dart';
+
+import '../logging/logging.dart';
 
 /// A command based summary source which calls the ApiSummarizer command.
 /// [sourcePaths] and [classPaths] can be provided for the summarizer to find
diff --git a/pkgs/jnigen/lib/src/tools/android_sdk_tools.dart b/pkgs/jnigen/lib/src/tools/android_sdk_tools.dart
index 703db13..48fe3b4 100644
--- a/pkgs/jnigen/lib/src/tools/android_sdk_tools.dart
+++ b/pkgs/jnigen/lib/src/tools/android_sdk_tools.dart
@@ -3,9 +3,10 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'dart:io';
+
 import 'package:path/path.dart';
 
-import 'package:jnigen/src/logging/logging.dart';
+import '../logging/logging.dart';
 
 class _AndroidToolsException implements Exception {
   _AndroidToolsException(this.message);
diff --git a/pkgs/jnigen/lib/src/tools/build_summarizer.dart b/pkgs/jnigen/lib/src/tools/build_summarizer.dart
index 38e395e..2647bc8 100644
--- a/pkgs/jnigen/lib/src/tools/build_summarizer.dart
+++ b/pkgs/jnigen/lib/src/tools/build_summarizer.dart
@@ -13,8 +13,8 @@
 
 import 'package:path/path.dart';
 
-import 'package:jnigen/src/util/find_package.dart';
-import 'package:jnigen/src/logging/logging.dart';
+import '../logging/logging.dart';
+import '../util/find_package.dart';
 
 final toolPath = join('.', '.dart_tool', 'jnigen');
 final mvnTargetDir = join(toolPath, 'target');
diff --git a/pkgs/jnigen/lib/src/tools/maven_tools.dart b/pkgs/jnigen/lib/src/tools/maven_tools.dart
index 07ce764..82ebdc7 100644
--- a/pkgs/jnigen/lib/src/tools/maven_tools.dart
+++ b/pkgs/jnigen/lib/src/tools/maven_tools.dart
@@ -4,9 +4,10 @@
 
 import 'dart:io';
 
-import 'package:jnigen/src/logging/logging.dart';
 import 'package:path/path.dart';
 
+import '../logging/logging.dart';
+
 /// This class provides some utility methods to download a sources / jars
 /// using maven along with transitive dependencies.
 class MavenTools {
diff --git a/pkgs/jnigen/lib/src/util/command_output.dart b/pkgs/jnigen/lib/src/util/command_output.dart
deleted file mode 100644
index 1e7a9e0..0000000
--- a/pkgs/jnigen/lib/src/util/command_output.dart
+++ /dev/null
@@ -1,8 +0,0 @@
-// 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 'dart:convert';
-
-void collectOutputStream(Stream<List<int>> stream, StringBuffer buffer) =>
-    stream.transform(Utf8Decoder()).forEach(buffer.write);
diff --git a/pkgs/jnigen/lib/src/util/name_utils.dart b/pkgs/jnigen/lib/src/util/name_utils.dart
deleted file mode 100644
index 96d0c83..0000000
--- a/pkgs/jnigen/lib/src/util/name_utils.dart
+++ /dev/null
@@ -1,12 +0,0 @@
-// 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.
-
-/// splits [str] into 2 from last occurence of [sep]
-List<String> cutFromLast(String str, String sep) {
-  final li = str.lastIndexOf(sep);
-  if (li == -1) {
-    return ['', str];
-  }
-  return [str.substring(0, li), str.substring(li + 1)];
-}
diff --git a/pkgs/jnigen/lib/src/util/rename_conflict.dart b/pkgs/jnigen/lib/src/util/rename_conflict.dart
deleted file mode 100644
index 7971d50..0000000
--- a/pkgs/jnigen/lib/src/util/rename_conflict.dart
+++ /dev/null
@@ -1,87 +0,0 @@
-// 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.
-
-String renameConflict(Map<String, int> counts, String name) {
-  if (counts.containsKey(name)) {
-    final count = counts[name]!;
-    final renamed = '$name$count';
-    counts[name] = count + 1;
-    return renamed;
-  }
-  counts[name] = 1;
-  return kwRename(name);
-}
-
-/// Appends 0 to [name] if [name] is a keyword.
-///
-/// Examples:
-/// * `int` -> `int0`
-/// * `i` -> `i`
-String kwRename(String name) => _keywords.contains(name) ? '${name}0' : name;
-
-const Set<String> _keywords = {
-  'abstract',
-  'as',
-  'assert',
-  'async',
-  'await',
-  'break',
-  'case',
-  'catch',
-  'class',
-  'const',
-  'continue',
-  'covariant',
-  'default',
-  'deferred',
-  'do',
-  'dynamic',
-  'else',
-  'enum',
-  'export',
-  'extends',
-  'extension',
-  'external',
-  'factory',
-  'false',
-  'final',
-  'finally',
-  'for',
-  'Function',
-  'get',
-  'hide',
-  'if',
-  'implements',
-  'import',
-  'in',
-  'interface',
-  'is',
-  'late',
-  'library',
-  'mixin',
-  'new',
-  'null',
-  'on',
-  'operator',
-  'part',
-  'required',
-  'rethrow',
-  'return',
-  'set',
-  'show',
-  'static',
-  'super',
-  'switch',
-  'sync',
-  'this',
-  'throw',
-  'true',
-  'try',
-  'typedef',
-  'var',
-  'void',
-  'while',
-  'with',
-  'yield',
-};
diff --git a/pkgs/jnigen/lib/src/writers/bindings_writer.dart b/pkgs/jnigen/lib/src/writers/bindings_writer.dart
index 1cfd58e..a31b3d7 100644
--- a/pkgs/jnigen/lib/src/writers/bindings_writer.dart
+++ b/pkgs/jnigen/lib/src/writers/bindings_writer.dart
@@ -4,14 +4,11 @@
 
 import 'dart:io';
 
-import 'package:jnigen/jnigen.dart';
-import 'package:jnigen/src/logging/logging.dart';
-import 'package:jnigen/src/util/find_package.dart';
-import 'package:jnigen/src/bindings/c_bindings.dart';
-
-abstract class BindingsWriter {
-  Future<void> writeBindings(List<ClassDecl> classes);
-}
+import '../bindings/c_bindings.dart';
+import '../config/config.dart';
+import '../elements/elements.dart';
+import '../logging/logging.dart';
+import '../util/find_package.dart';
 
 /// Run dart format command on [path].
 Future<void> runDartFormat(String path) async {
diff --git a/pkgs/jnigen/lib/src/writers/files_writer.dart b/pkgs/jnigen/lib/src/writers/files_writer.dart
deleted file mode 100644
index 79904f0..0000000
--- a/pkgs/jnigen/lib/src/writers/files_writer.dart
+++ /dev/null
@@ -1,305 +0,0 @@
-// 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 'dart:io';
-import 'dart:math';
-
-import 'package:jnigen/src/bindings/bindings.dart';
-import 'package:jnigen/src/logging/logging.dart';
-import 'package:jnigen/src/elements/elements.dart';
-import 'package:jnigen/src/config/config.dart';
-import 'package:jnigen/src/util/name_utils.dart';
-import 'package:jnigen/src/writers/bindings_writer.dart';
-
-String getFileClassName(String binaryName) {
-  final dollarSign = binaryName.indexOf('\$');
-  if (dollarSign != -1) {
-    return binaryName.substring(0, dollarSign);
-  }
-  return binaryName;
-}
-
-/// Resolver for file-per-package mapping, in which the java package hierarchy
-/// is mirrored.
-class FilePathResolver implements SymbolResolver {
-  FilePathResolver({
-    this.classes = const {},
-    this.importMap = const {},
-    required this.currentClass,
-    this.inputClassNames = const {},
-  });
-
-  static const Map<String, String> predefined = {
-    'java.lang.String': 'jni.JString',
-  };
-
-  static final predefinedClasses = {
-    'java.lang.String': ClassDecl(
-      binaryName: 'java.lang.String',
-      packageName: 'java.lang',
-      simpleName: 'String',
-    )
-      ..isIncluded = true
-      ..isPreprocessed = true,
-  };
-
-  /// A map of all classes by their names
-  final Map<String, ClassDecl> classes;
-
-  /// Class corresponding to currently writing file.
-  final String currentClass;
-
-  /// Explicit import mappings.
-  final Map<String, String> importMap;
-
-  /// Names of all classes in input.
-  final Set<String> inputClassNames;
-
-  final List<String> importStrings = [];
-
-  final Set<String> _relativeImportedClasses = {};
-
-  final Map<String, String> _importedNameToClass = {};
-  final Map<String, String> _classToImportedName = {};
-
-  /// Returns the dart name of the [binaryName] in current translation context,
-  /// or `null` if the name cannot be resolved.
-  @override
-  String? resolve(String binaryName) {
-    if (predefined.containsKey(binaryName)) {
-      return predefined[binaryName];
-    }
-    final target = getFileClassName(binaryName);
-    final parts = cutFromLast(binaryName, '.');
-    final typename = parts[1];
-    final simpleTypeName = typename.replaceAll('\$', '_');
-
-    if (target == currentClass && inputClassNames.contains(binaryName)) {
-      return simpleTypeName;
-    }
-
-    if (_classToImportedName.containsKey(target)) {
-      // This package was already resolved
-      // but we still need to check if it was a relative import, in which case
-      // the class not in inputClassNames cannot be mapped here.
-      if (!_relativeImportedClasses.contains(target) ||
-          inputClassNames.contains(binaryName)) {
-        final importedName = _classToImportedName[target];
-        return '$importedName.$simpleTypeName';
-      }
-    }
-
-    final classImport = getImport(target, binaryName);
-    log.finest('$target resolved to $classImport for $binaryName');
-    if (classImport == null) {
-      return null;
-    }
-
-    final pkgName = cutFromLast(target, '.')[1].toLowerCase();
-    if (pkgName.isEmpty) {
-      throw UnsupportedError('No package could be deduced from '
-          'qualified binaryName');
-    }
-
-    // We always name imports with an underscore suffix, so that they can be
-    // never shadowed by a parameter or local variable.
-    var importedName = '${pkgName}_';
-    int suffix = 0;
-    while (_importedNameToClass.containsKey(importedName)) {
-      suffix++;
-      importedName = '$pkgName${suffix}_';
-    }
-
-    _importedNameToClass[importedName] = target;
-    _classToImportedName[target] = importedName;
-    importStrings.add('import "$classImport" as $importedName;\n');
-    return '$importedName.$simpleTypeName';
-  }
-
-  /// Returns import string for [classToResolve], or `null` if the class is not
-  /// found.
-  ///
-  /// [binaryName] is the class name trying to be resolved. This parameter is
-  /// requested so that classes included in current bindings can be resolved
-  /// using relative path.
-  String? getImport(String classToResolve, String binaryName) {
-    var prefix = classToResolve;
-
-    // short circuit if the requested class is specified directly in import map.
-    if (importMap.containsKey(binaryName)) {
-      return importMap[binaryName]!;
-    }
-
-    if (prefix.isEmpty) {
-      throw UnsupportedError('unexpected: empty package name.');
-    }
-
-    final dest = classToResolve.split('.');
-    final src = currentClass.split('.');
-    // Use relative import when the required class is included in current set
-    // of bindings.
-    if (inputClassNames.contains(binaryName)) {
-      int common = 0;
-      // find the common prefix path directory of current package, and directory
-      // of target package
-      // src.length - 1 simply corresponds to directory of the package.
-      for (int i = 0; i < src.length - 1 && i < dest.length - 1; i++) {
-        if (src[i] == dest[i]) {
-          common++;
-        } else {
-          break;
-        }
-      }
-      final pathToCommon = '../' * ((src.length - 1) - common);
-      final pathToClass = dest.sublist(max(common, 0)).join('/');
-      _relativeImportedClasses.add(classToResolve);
-      return '$pathToCommon$pathToClass.dart';
-    }
-
-    while (prefix.isNotEmpty) {
-      final split = cutFromLast(prefix, '.');
-      final left = split[0];
-      if (importMap.containsKey(prefix)) {
-        return importMap[prefix]!;
-      }
-      prefix = left;
-    }
-    return null;
-  }
-
-  @override
-  List<String> getImportStrings() {
-    return importStrings;
-  }
-
-  @override
-  ClassDecl? resolveClass(String binaryName) {
-    if (predefinedClasses.containsKey(binaryName)) {
-      return predefinedClasses[binaryName];
-    }
-    return classes[binaryName];
-  }
-}
-
-/// Writer which executes custom callback on passed class elements.
-///
-/// This class is provided for debugging purposes.
-class CallbackWriter implements BindingsWriter {
-  CallbackWriter(this.callback);
-  Future<void> Function(Iterable<ClassDecl>) callback;
-  @override
-  Future<void> writeBindings(Iterable<ClassDecl> classes) async {
-    await callback(classes);
-  }
-}
-
-/// Writer which takes writes C and Dart bindings to specified directories.
-///
-/// The structure of dart files is determined by package structure of java.
-/// One dart file corresponds to one java package, and it's path is decided by
-/// fully qualified name of the package.
-///
-/// Example:
-/// `android.os` -> `$dartWrappersRoot`/`android/os.dart`
-class FilesWriter extends BindingsWriter {
-  static const _initFileName = '_init.dart';
-
-  FilesWriter(this.config);
-  Config config;
-
-  @override
-  Future<void> writeBindings(List<ClassDecl> classes) async {
-    final cBased = config.outputConfig.bindingsType == BindingsType.cBased;
-    final preamble = config.preamble;
-    final Map<String, List<ClassDecl>> files = {};
-    final Map<String, ClassDecl> classesByName = {};
-    final Map<String, Set<String>> packages = {};
-    for (var c in classes) {
-      classesByName.putIfAbsent(c.binaryName, () => c);
-      final fileClass = getFileClassName(c.binaryName);
-
-      files.putIfAbsent(fileClass, () => <ClassDecl>[]);
-      files[fileClass]!.add(c);
-
-      packages.putIfAbsent(c.packageName, () => {});
-      packages[c.packageName]!.add(fileClass.split('.').last);
-    }
-
-    final classNames = classesByName.keys.toSet();
-    ApiPreprocessor.preprocessAll(classesByName, config);
-
-    final dartRoot = config.outputConfig.dartConfig.path;
-    log.info("Using dart root = $dartRoot");
-    final generator = cBased
-        ? CBasedDartBindingsGenerator(config)
-        : PureDartBindingsGenerator(config);
-
-    if (cBased) {
-      await writeCBindings(config, classesByName.values.toList());
-    }
-
-    // Write init file
-    final initFileUri = dartRoot.resolve("_init.dart");
-    var initCode = generator.getInitFileContents();
-    if (initCode.isNotEmpty) {
-      if (preamble != null) {
-        initCode = '$preamble\n$initCode';
-      }
-      final initFile = File.fromUri(initFileUri);
-      await initFile.create(recursive: true);
-      await initFile.writeAsString(initCode);
-    }
-
-    for (var fileClassName in files.keys) {
-      final relativeFileName = '${fileClassName.replaceAll('.', '/')}.dart';
-      final dartFileUri = dartRoot.resolve(relativeFileName);
-      final dartFile = await File.fromUri(dartFileUri).create(recursive: true);
-      log.fine('$fileClassName -> ${dartFile.path}');
-      final resolver = FilePathResolver(
-        classes: classesByName,
-        importMap: config.importMap ?? const {},
-        currentClass: fileClassName,
-        inputClassNames: classNames,
-      );
-
-      final classesInFile = files[fileClassName]!;
-      final dartBindings = classesInFile
-          .map((decl) => generator.generateBindings(decl, resolver))
-          .toList();
-      final dartFileStream = dartFile.openWrite();
-      if (preamble != null) {
-        dartFileStream.writeln(preamble);
-      }
-
-      final initFilePath = ('../' *
-              relativeFileName.codeUnits
-                  .where((cu) => '/'.codeUnitAt(0) == cu)
-                  .length) +
-          _initFileName;
-
-      dartFileStream
-        ..write(generator.getPreImportBoilerplate(initFilePath))
-        ..write(resolver.getImportStrings().join('\n'))
-        ..write(generator.getPostImportBoilerplate(initFilePath));
-
-      // write dart bindings only after all imports are figured out
-      dartBindings.forEach(dartFileStream.write);
-      await dartFileStream.close();
-    }
-
-    // write _package.dart export files
-    for (var package in packages.keys) {
-      final dirUri = dartRoot.resolve('${package.replaceAll('.', '/')}/');
-      final exportFileUri = dirUri.resolve("_package.dart");
-      final exportFile = File.fromUri(exportFileUri);
-      exportFile.createSync(recursive: true);
-      final exports =
-          packages[package]!.map((cls) => 'export "$cls.dart";').join('\n');
-      exportFile.writeAsStringSync(exports);
-    }
-
-    await runDartFormat(dartRoot.toFilePath());
-    log.info('Completed.');
-  }
-}
diff --git a/pkgs/jnigen/lib/src/writers/single_file_writer.dart b/pkgs/jnigen/lib/src/writers/single_file_writer.dart
deleted file mode 100644
index 438f867..0000000
--- a/pkgs/jnigen/lib/src/writers/single_file_writer.dart
+++ /dev/null
@@ -1,97 +0,0 @@
-// 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 'dart:io';
-
-import 'package:jnigen/jnigen.dart';
-import 'package:jnigen/src/bindings/bindings.dart';
-import 'package:jnigen/src/logging/logging.dart';
-import 'package:jnigen/src/writers/bindings_writer.dart';
-
-/// Resolver for single-file mapping of input classes.
-class SingleFileResolver implements SymbolResolver {
-  static const predefined = {
-    'java.lang.String': 'jni.JString',
-  };
-  static final predefinedClasses = {
-    'java.lang.String': ClassDecl(
-      binaryName: 'java.lang.String',
-      packageName: 'java.lang',
-      simpleName: 'String',
-    )
-      ..isIncluded = true
-      ..isPreprocessed = true,
-  };
-  Map<String, ClassDecl> inputClasses;
-  SingleFileResolver(this.inputClasses);
-  @override
-  List<String> getImportStrings() {
-    return [];
-  }
-
-  @override
-  String? resolve(String binaryName) {
-    if (predefined.containsKey(binaryName)) return predefined[binaryName];
-    return inputClasses[binaryName]?.finalName;
-  }
-
-  @override
-  ClassDecl? resolveClass(String binaryName) {
-    if (predefinedClasses.containsKey(binaryName)) {
-      return predefinedClasses[binaryName];
-    }
-    return inputClasses[binaryName];
-  }
-}
-
-class SingleFileWriter extends BindingsWriter {
-  SingleFileWriter(this.config);
-  Config config;
-  @override
-  Future<void> writeBindings(List<ClassDecl> classes) async {
-    final cBased = config.outputConfig.bindingsType == BindingsType.cBased;
-    final preamble = config.preamble;
-    final Map<String, List<ClassDecl>> packages = {};
-    final Map<String, ClassDecl> classesByName = {};
-
-    for (var c in classes) {
-      classesByName.putIfAbsent(c.binaryName, () => c);
-      packages.putIfAbsent(c.packageName, () => <ClassDecl>[]);
-      packages[c.packageName]!.add(c);
-    }
-
-    ApiPreprocessor.preprocessAll(classesByName, config, renameClasses: true);
-
-    if (cBased) {
-      await writeCBindings(config, classesByName.values.toList());
-    }
-    log.info("Generating ${cBased ? "C + Dart" : "Pure Dart"} Bindings");
-    final generator = cBased
-        ? CBasedDartBindingsGenerator(config)
-        : PureDartBindingsGenerator(config);
-    final file = File.fromUri(config.outputConfig.dartConfig.path);
-    await file.create(recursive: true);
-    final fileStream = file.openWrite();
-    final resolver = SingleFileResolver(classesByName);
-
-    // Have to generate bindings beforehand so that imports are all figured
-    // out.
-    final bindings = classesByName.values
-        .map((decl) => generator.generateBindings(decl, resolver))
-        .join("\n");
-
-    log.info("Writing Dart bindings to file: ${file.path}");
-    fileStream
-      ..writeln(preamble ?? '')
-      ..writeln(generator.getPreImportBoilerplate())
-      ..writeln(resolver.getImportStrings().join('\n'))
-      ..writeln(generator.getPostImportBoilerplate())
-      ..writeln(bindings);
-
-    await fileStream.close();
-    await runDartFormat(file.path);
-    log.info('Completed');
-    return;
-  }
-}
diff --git a/pkgs/jnigen/lib/src/writers/writers.dart b/pkgs/jnigen/lib/src/writers/writers.dart
deleted file mode 100644
index 0d2ca44..0000000
--- a/pkgs/jnigen/lib/src/writers/writers.dart
+++ /dev/null
@@ -1,3 +0,0 @@
-export 'bindings_writer.dart';
-export 'single_file_writer.dart';
-export 'files_writer.dart';
diff --git a/pkgs/jnigen/test/jackson_core_test/generated_files_test.dart b/pkgs/jnigen/test/jackson_core_test/generated_files_test.dart
index d464b6c..75dde7d 100644
--- a/pkgs/jnigen/test/jackson_core_test/generated_files_test.dart
+++ b/pkgs/jnigen/test/jackson_core_test/generated_files_test.dart
@@ -15,21 +15,21 @@
     final lib = join(thirdPartyDir, 'lib');
     final src = join(thirdPartyDir, 'src');
     await generateAndCompareBindings(getConfig(), lib, src);
-  }, timeout: Timeout.factor(2));
+  }, timeout: const Timeout.factor(2));
 
   test(
       'generate and analyze bindings for complete library, '
       'not just required classes', () async {
     final config = getConfig(generateFullVersion: true);
     await generateAndAnalyzeBindings(config);
-  }, timeout: Timeout(Duration(minutes: 2)));
+  }, timeout: const Timeout(Duration(minutes: 2)));
   test('generate and analyze bindings using ASM', () async {
     final config = getConfig(generateFullVersion: true, useAsm: true);
     await generateAndAnalyzeBindings(config);
-  }, timeout: Timeout(Duration(minutes: 2)));
+  }, timeout: const Timeout(Duration(minutes: 2)));
   test('Generate and analyze pure dart bindings', () async {
     final config = getConfig(generateFullVersion: true);
     config.outputConfig.bindingsType = BindingsType.dartOnly;
     await generateAndAnalyzeBindings(config);
-  }, timeout: Timeout.factor(2));
+  }, timeout: const Timeout.factor(2));
 }
diff --git a/pkgs/jnigen/test/jackson_core_test/third_party/lib/_init.dart b/pkgs/jnigen/test/jackson_core_test/third_party/lib/_init.dart
index 7f85005..eec3a8c 100644
--- a/pkgs/jnigen/test/jackson_core_test/third_party/lib/_init.dart
+++ b/pkgs/jnigen/test/jackson_core_test/third_party/lib/_init.dart
@@ -17,5 +17,7 @@
 
 import "package:jni/jni.dart" as jni;
 
+// Auto-generated initialization code.
+
 final jniEnv = jni.Jni.env;
 final jniAccessors = jni.Jni.accessors;
diff --git a/pkgs/jnigen/test/jackson_core_test/third_party/lib/com/fasterxml/jackson/core/JsonFactory.dart b/pkgs/jnigen/test/jackson_core_test/third_party/lib/com/fasterxml/jackson/core/JsonFactory.dart
index 2cb23c0..9c8decb 100644
--- a/pkgs/jnigen/test/jackson_core_test/third_party/lib/com/fasterxml/jackson/core/JsonFactory.dart
+++ b/pkgs/jnigen/test/jackson_core_test/third_party/lib/com/fasterxml/jackson/core/JsonFactory.dart
@@ -32,13 +32,12 @@
 // ignore_for_file: unused_shown_name
 
 import "dart:isolate" show ReceivePort;
-
+import "dart:ffi" as ffi;
+import "package:jni/internal_helpers_for_jnigen.dart";
 import "package:jni/jni.dart" as jni;
 
-import "package:jni/internal_helpers_for_jnigen.dart";
-
 import "JsonParser.dart" as jsonparser_;
-import "../../../../_init.dart" show jniEnv, jniAccessors;
+import "../../../../_init.dart";
 
 /// from: com.fasterxml.jackson.core.JsonFactory
 ///
@@ -69,7 +68,7 @@
   ) : super.fromRef(ref);
 
   static final _classRef =
-      jniAccessors.getClassOf("com/fasterxml/jackson/core/JsonFactory");
+      jniAccessors.getClassOf(r"com/fasterxml/jackson/core/JsonFactory");
 
   /// The type which includes information such as the signature of this class.
   static const type = $JsonFactoryType();
@@ -78,10 +77,14 @@
   ///
   /// Name used to identify JSON format
   /// (and returned by \#getFormatName()
-  static const FORMAT_NAME_JSON = "JSON";
+  static const FORMAT_NAME_JSON = r"""JSON""";
 
-  static final _id_DEFAULT_FACTORY_FEATURE_FLAGS = jniAccessors
-      .getStaticFieldIDOf(_classRef, "DEFAULT_FACTORY_FEATURE_FLAGS", "I");
+  static final _id_DEFAULT_FACTORY_FEATURE_FLAGS =
+      jniAccessors.getStaticFieldIDOf(
+    _classRef,
+    r"DEFAULT_FACTORY_FEATURE_FLAGS",
+    r"I",
+  );
 
   /// from: static protected final int DEFAULT_FACTORY_FEATURE_FLAGS
   ///
@@ -91,8 +94,12 @@
           _classRef, _id_DEFAULT_FACTORY_FEATURE_FLAGS, jni.JniCallType.intType)
       .integer;
 
-  static final _id_DEFAULT_PARSER_FEATURE_FLAGS = jniAccessors
-      .getStaticFieldIDOf(_classRef, "DEFAULT_PARSER_FEATURE_FLAGS", "I");
+  static final _id_DEFAULT_PARSER_FEATURE_FLAGS =
+      jniAccessors.getStaticFieldIDOf(
+    _classRef,
+    r"DEFAULT_PARSER_FEATURE_FLAGS",
+    r"I",
+  );
 
   /// from: static protected final int DEFAULT_PARSER_FEATURE_FLAGS
   ///
@@ -103,8 +110,12 @@
           _classRef, _id_DEFAULT_PARSER_FEATURE_FLAGS, jni.JniCallType.intType)
       .integer;
 
-  static final _id_DEFAULT_GENERATOR_FEATURE_FLAGS = jniAccessors
-      .getStaticFieldIDOf(_classRef, "DEFAULT_GENERATOR_FEATURE_FLAGS", "I");
+  static final _id_DEFAULT_GENERATOR_FEATURE_FLAGS =
+      jniAccessors.getStaticFieldIDOf(
+    _classRef,
+    r"DEFAULT_GENERATOR_FEATURE_FLAGS",
+    r"I",
+  );
 
   /// from: static protected final int DEFAULT_GENERATOR_FEATURE_FLAGS
   ///
@@ -116,8 +127,11 @@
       .integer;
 
   static final _id_DEFAULT_ROOT_VALUE_SEPARATOR =
-      jniAccessors.getStaticFieldIDOf(_classRef, "DEFAULT_ROOT_VALUE_SEPARATOR",
-          "Lcom/fasterxml/jackson/core/SerializableString;");
+      jniAccessors.getStaticFieldIDOf(
+    _classRef,
+    r"DEFAULT_ROOT_VALUE_SEPARATOR",
+    r"Lcom/fasterxml/jackson/core/SerializableString;",
+  );
 
   /// from: static public final com.fasterxml.jackson.core.SerializableString DEFAULT_ROOT_VALUE_SEPARATOR
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -128,7 +142,7 @@
           .object);
 
   static final _id_ctor =
-      jniAccessors.getMethodIDOf(_classRef, "<init>", "()V");
+      jniAccessors.getMethodIDOf(_classRef, r"<init>", r"()V");
 
   /// from: public void <init>()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -146,7 +160,7 @@
             jniAccessors.newObjectWithArgs(_classRef, _id_ctor, []).object);
 
   static final _id_ctor1 = jniAccessors.getMethodIDOf(
-      _classRef, "<init>", "(Lcom/fasterxml/jackson/core/ObjectCodec;)V");
+      _classRef, r"<init>", r"(Lcom/fasterxml/jackson/core/ObjectCodec;)V");
 
   /// from: public void <init>(com.fasterxml.jackson.core.ObjectCodec oc)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -154,8 +168,8 @@
       : super.fromRef(jniAccessors
             .newObjectWithArgs(_classRef, _id_ctor1, [oc.reference]).object);
 
-  static final _id_ctor2 = jniAccessors.getMethodIDOf(_classRef, "<init>",
-      "(Lcom/fasterxml/jackson/core/JsonFactory;Lcom/fasterxml/jackson/core/ObjectCodec;)V");
+  static final _id_ctor2 = jniAccessors.getMethodIDOf(_classRef, r"<init>",
+      r"(Lcom/fasterxml/jackson/core/JsonFactory;Lcom/fasterxml/jackson/core/ObjectCodec;)V");
 
   /// from: protected void <init>(com.fasterxml.jackson.core.JsonFactory src, com.fasterxml.jackson.core.ObjectCodec codec)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -168,8 +182,8 @@
       : super.fromRef(jniAccessors.newObjectWithArgs(
             _classRef, _id_ctor2, [src.reference, codec.reference]).object);
 
-  static final _id_ctor3 = jniAccessors.getMethodIDOf(_classRef, "<init>",
-      "(Lcom/fasterxml/jackson/core/JsonFactoryBuilder;)V");
+  static final _id_ctor3 = jniAccessors.getMethodIDOf(_classRef, r"<init>",
+      r"(Lcom/fasterxml/jackson/core/JsonFactoryBuilder;)V");
 
   /// from: public void <init>(com.fasterxml.jackson.core.JsonFactoryBuilder b)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -182,7 +196,7 @@
             .newObjectWithArgs(_classRef, _id_ctor3, [b.reference]).object);
 
   static final _id_ctor4 = jniAccessors.getMethodIDOf(
-      _classRef, "<init>", "(Lcom/fasterxml/jackson/core/TSFBuilder;Z)V");
+      _classRef, r"<init>", r"(Lcom/fasterxml/jackson/core/TSFBuilder;Z)V");
 
   /// from: protected void <init>(com.fasterxml.jackson.core.TSFBuilder<?,?> b, boolean bogus)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -194,10 +208,10 @@
   ///@param bogus Argument only needed to separate constructor signature; ignored
   JsonFactory.ctor4(jni.JObject b, bool bogus)
       : super.fromRef(jniAccessors.newObjectWithArgs(
-            _classRef, _id_ctor4, [b.reference, bogus]).object);
+            _classRef, _id_ctor4, [b.reference, bogus ? 1 : 0]).object);
 
   static final _id_rebuild = jniAccessors.getMethodIDOf(
-      _classRef, "rebuild", "()Lcom/fasterxml/jackson/core/TSFBuilder;");
+      _classRef, r"rebuild", r"()Lcom/fasterxml/jackson/core/TSFBuilder;");
 
   /// from: public com.fasterxml.jackson.core.TSFBuilder<?,?> rebuild()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -211,7 +225,7 @@
           reference, _id_rebuild, jni.JniCallType.objectType, []).object);
 
   static final _id_builder = jniAccessors.getStaticMethodIDOf(
-      _classRef, "builder", "()Lcom/fasterxml/jackson/core/TSFBuilder;");
+      _classRef, r"builder", r"()Lcom/fasterxml/jackson/core/TSFBuilder;");
 
   /// from: static public com.fasterxml.jackson.core.TSFBuilder<?,?> builder()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -229,7 +243,7 @@
           _classRef, _id_builder, jni.JniCallType.objectType, []).object);
 
   static final _id_copy = jniAccessors.getMethodIDOf(
-      _classRef, "copy", "()Lcom/fasterxml/jackson/core/JsonFactory;");
+      _classRef, r"copy", r"()Lcom/fasterxml/jackson/core/JsonFactory;");
 
   /// from: public com.fasterxml.jackson.core.JsonFactory copy()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -251,7 +265,7 @@
           reference, _id_copy, jni.JniCallType.objectType, []).object);
 
   static final _id_readResolve = jniAccessors.getMethodIDOf(
-      _classRef, "readResolve", "()Ljava/lang/Object;");
+      _classRef, r"readResolve", r"()Ljava/lang/Object;");
 
   /// from: protected java.lang.Object readResolve()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -266,8 +280,8 @@
       const jni.JObjectType().fromRef(jniAccessors.callMethodWithArgs(
           reference, _id_readResolve, jni.JniCallType.objectType, []).object);
 
-  static final _id_requiresPropertyOrdering =
-      jniAccessors.getMethodIDOf(_classRef, "requiresPropertyOrdering", "()Z");
+  static final _id_requiresPropertyOrdering = jniAccessors.getMethodIDOf(
+      _classRef, r"requiresPropertyOrdering", r"()Z");
 
   /// from: public boolean requiresPropertyOrdering()
   ///
@@ -289,7 +303,7 @@
       _id_requiresPropertyOrdering, jni.JniCallType.booleanType, []).boolean;
 
   static final _id_canHandleBinaryNatively =
-      jniAccessors.getMethodIDOf(_classRef, "canHandleBinaryNatively", "()Z");
+      jniAccessors.getMethodIDOf(_classRef, r"canHandleBinaryNatively", r"()Z");
 
   /// from: public boolean canHandleBinaryNatively()
   ///
@@ -308,7 +322,7 @@
       _id_canHandleBinaryNatively, jni.JniCallType.booleanType, []).boolean;
 
   static final _id_canUseCharArrays =
-      jniAccessors.getMethodIDOf(_classRef, "canUseCharArrays", "()Z");
+      jniAccessors.getMethodIDOf(_classRef, r"canUseCharArrays", r"()Z");
 
   /// from: public boolean canUseCharArrays()
   ///
@@ -327,7 +341,7 @@
       reference, _id_canUseCharArrays, jni.JniCallType.booleanType, []).boolean;
 
   static final _id_canParseAsync =
-      jniAccessors.getMethodIDOf(_classRef, "canParseAsync", "()Z");
+      jniAccessors.getMethodIDOf(_classRef, r"canParseAsync", r"()Z");
 
   /// from: public boolean canParseAsync()
   ///
@@ -342,7 +356,7 @@
       reference, _id_canParseAsync, jni.JniCallType.booleanType, []).boolean;
 
   static final _id_getFormatReadFeatureType = jniAccessors.getMethodIDOf(
-      _classRef, "getFormatReadFeatureType", "()Ljava/lang/Class;");
+      _classRef, r"getFormatReadFeatureType", r"()Ljava/lang/Class;");
 
   /// from: public java.lang.Class<? extends com.fasterxml.jackson.core.FormatFeature> getFormatReadFeatureType()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -351,7 +365,7 @@
           _id_getFormatReadFeatureType, jni.JniCallType.objectType, []).object);
 
   static final _id_getFormatWriteFeatureType = jniAccessors.getMethodIDOf(
-      _classRef, "getFormatWriteFeatureType", "()Ljava/lang/Class;");
+      _classRef, r"getFormatWriteFeatureType", r"()Ljava/lang/Class;");
 
   /// from: public java.lang.Class<? extends com.fasterxml.jackson.core.FormatFeature> getFormatWriteFeatureType()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -362,7 +376,7 @@
           jni.JniCallType.objectType, []).object);
 
   static final _id_canUseSchema = jniAccessors.getMethodIDOf(_classRef,
-      "canUseSchema", "(Lcom/fasterxml/jackson/core/FormatSchema;)Z");
+      r"canUseSchema", r"(Lcom/fasterxml/jackson/core/FormatSchema;)Z");
 
   /// from: public boolean canUseSchema(com.fasterxml.jackson.core.FormatSchema schema)
   ///
@@ -382,7 +396,7 @@
       [schema.reference]).boolean;
 
   static final _id_getFormatName = jniAccessors.getMethodIDOf(
-      _classRef, "getFormatName", "()Ljava/lang/String;");
+      _classRef, r"getFormatName", r"()Ljava/lang/String;");
 
   /// from: public java.lang.String getFormatName()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -399,8 +413,8 @@
 
   static final _id_hasFormat = jniAccessors.getMethodIDOf(
       _classRef,
-      "hasFormat",
-      "(Lcom/fasterxml/jackson/core/format/InputAccessor;)Lcom/fasterxml/jackson/core/format/MatchStrength;");
+      r"hasFormat",
+      r"(Lcom/fasterxml/jackson/core/format/InputAccessor;)Lcom/fasterxml/jackson/core/format/MatchStrength;");
 
   /// from: public com.fasterxml.jackson.core.format.MatchStrength hasFormat(com.fasterxml.jackson.core.format.InputAccessor acc)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -409,7 +423,7 @@
           _id_hasFormat, jni.JniCallType.objectType, [acc.reference]).object);
 
   static final _id_requiresCustomCodec =
-      jniAccessors.getMethodIDOf(_classRef, "requiresCustomCodec", "()Z");
+      jniAccessors.getMethodIDOf(_classRef, r"requiresCustomCodec", r"()Z");
 
   /// from: public boolean requiresCustomCodec()
   ///
@@ -427,8 +441,8 @@
 
   static final _id_hasJSONFormat = jniAccessors.getMethodIDOf(
       _classRef,
-      "hasJSONFormat",
-      "(Lcom/fasterxml/jackson/core/format/InputAccessor;)Lcom/fasterxml/jackson/core/format/MatchStrength;");
+      r"hasJSONFormat",
+      r"(Lcom/fasterxml/jackson/core/format/InputAccessor;)Lcom/fasterxml/jackson/core/format/MatchStrength;");
 
   /// from: protected com.fasterxml.jackson.core.format.MatchStrength hasJSONFormat(com.fasterxml.jackson.core.format.InputAccessor acc)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -440,7 +454,7 @@
           [acc.reference]).object);
 
   static final _id_version = jniAccessors.getMethodIDOf(
-      _classRef, "version", "()Lcom/fasterxml/jackson/core/Version;");
+      _classRef, r"version", r"()Lcom/fasterxml/jackson/core/Version;");
 
   /// from: public com.fasterxml.jackson.core.Version version()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -450,8 +464,8 @@
 
   static final _id_configure = jniAccessors.getMethodIDOf(
       _classRef,
-      "configure",
-      "(Lcom/fasterxml/jackson/core/JsonFactory\$Feature;Z)Lcom/fasterxml/jackson/core/JsonFactory;");
+      r"configure",
+      r"(Lcom/fasterxml/jackson/core/JsonFactory$Feature;Z)Lcom/fasterxml/jackson/core/JsonFactory;");
 
   /// from: public final com.fasterxml.jackson.core.JsonFactory configure(com.fasterxml.jackson.core.JsonFactory.Feature f, boolean state)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -467,10 +481,10 @@
           reference,
           _id_configure,
           jni.JniCallType.objectType,
-          [f.reference, state]).object);
+          [f.reference, state ? 1 : 0]).object);
 
-  static final _id_enable = jniAccessors.getMethodIDOf(_classRef, "enable",
-      "(Lcom/fasterxml/jackson/core/JsonFactory\$Feature;)Lcom/fasterxml/jackson/core/JsonFactory;");
+  static final _id_enable = jniAccessors.getMethodIDOf(_classRef, r"enable",
+      r"(Lcom/fasterxml/jackson/core/JsonFactory$Feature;)Lcom/fasterxml/jackson/core/JsonFactory;");
 
   /// from: public com.fasterxml.jackson.core.JsonFactory enable(com.fasterxml.jackson.core.JsonFactory.Feature f)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -487,8 +501,8 @@
           jni.JniCallType.objectType,
           [f.reference]).object);
 
-  static final _id_disable = jniAccessors.getMethodIDOf(_classRef, "disable",
-      "(Lcom/fasterxml/jackson/core/JsonFactory\$Feature;)Lcom/fasterxml/jackson/core/JsonFactory;");
+  static final _id_disable = jniAccessors.getMethodIDOf(_classRef, r"disable",
+      r"(Lcom/fasterxml/jackson/core/JsonFactory$Feature;)Lcom/fasterxml/jackson/core/JsonFactory;");
 
   /// from: public com.fasterxml.jackson.core.JsonFactory disable(com.fasterxml.jackson.core.JsonFactory.Feature f)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -506,7 +520,7 @@
           [f.reference]).object);
 
   static final _id_isEnabled = jniAccessors.getMethodIDOf(_classRef,
-      "isEnabled", "(Lcom/fasterxml/jackson/core/JsonFactory\$Feature;)Z");
+      r"isEnabled", r"(Lcom/fasterxml/jackson/core/JsonFactory$Feature;)Z");
 
   /// from: public final boolean isEnabled(com.fasterxml.jackson.core.JsonFactory.Feature f)
   ///
@@ -520,28 +534,28 @@
       [f.reference]).boolean;
 
   static final _id_getParserFeatures =
-      jniAccessors.getMethodIDOf(_classRef, "getParserFeatures", "()I");
+      jniAccessors.getMethodIDOf(_classRef, r"getParserFeatures", r"()I");
 
   /// from: public final int getParserFeatures()
   int getParserFeatures() => jniAccessors.callMethodWithArgs(
       reference, _id_getParserFeatures, jni.JniCallType.intType, []).integer;
 
   static final _id_getGeneratorFeatures =
-      jniAccessors.getMethodIDOf(_classRef, "getGeneratorFeatures", "()I");
+      jniAccessors.getMethodIDOf(_classRef, r"getGeneratorFeatures", r"()I");
 
   /// from: public final int getGeneratorFeatures()
   int getGeneratorFeatures() => jniAccessors.callMethodWithArgs(
       reference, _id_getGeneratorFeatures, jni.JniCallType.intType, []).integer;
 
   static final _id_getFormatParserFeatures =
-      jniAccessors.getMethodIDOf(_classRef, "getFormatParserFeatures", "()I");
+      jniAccessors.getMethodIDOf(_classRef, r"getFormatParserFeatures", r"()I");
 
   /// from: public int getFormatParserFeatures()
   int getFormatParserFeatures() => jniAccessors.callMethodWithArgs(reference,
       _id_getFormatParserFeatures, jni.JniCallType.intType, []).integer;
 
   static final _id_getFormatGeneratorFeatures = jniAccessors.getMethodIDOf(
-      _classRef, "getFormatGeneratorFeatures", "()I");
+      _classRef, r"getFormatGeneratorFeatures", r"()I");
 
   /// from: public int getFormatGeneratorFeatures()
   int getFormatGeneratorFeatures() => jniAccessors.callMethodWithArgs(reference,
@@ -549,8 +563,8 @@
 
   static final _id_configure1 = jniAccessors.getMethodIDOf(
       _classRef,
-      "configure",
-      "(Lcom/fasterxml/jackson/core/JsonParser\$Feature;Z)Lcom/fasterxml/jackson/core/JsonFactory;");
+      r"configure",
+      r"(Lcom/fasterxml/jackson/core/JsonParser$Feature;Z)Lcom/fasterxml/jackson/core/JsonFactory;");
 
   /// from: public final com.fasterxml.jackson.core.JsonFactory configure(com.fasterxml.jackson.core.JsonParser.Feature f, boolean state)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -565,10 +579,10 @@
           reference,
           _id_configure1,
           jni.JniCallType.objectType,
-          [f.reference, state]).object);
+          [f.reference, state ? 1 : 0]).object);
 
-  static final _id_enable1 = jniAccessors.getMethodIDOf(_classRef, "enable",
-      "(Lcom/fasterxml/jackson/core/JsonParser\$Feature;)Lcom/fasterxml/jackson/core/JsonFactory;");
+  static final _id_enable1 = jniAccessors.getMethodIDOf(_classRef, r"enable",
+      r"(Lcom/fasterxml/jackson/core/JsonParser$Feature;)Lcom/fasterxml/jackson/core/JsonFactory;");
 
   /// from: public com.fasterxml.jackson.core.JsonFactory enable(com.fasterxml.jackson.core.JsonParser.Feature f)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -584,8 +598,8 @@
           jni.JniCallType.objectType,
           [f.reference]).object);
 
-  static final _id_disable1 = jniAccessors.getMethodIDOf(_classRef, "disable",
-      "(Lcom/fasterxml/jackson/core/JsonParser\$Feature;)Lcom/fasterxml/jackson/core/JsonFactory;");
+  static final _id_disable1 = jniAccessors.getMethodIDOf(_classRef, r"disable",
+      r"(Lcom/fasterxml/jackson/core/JsonParser$Feature;)Lcom/fasterxml/jackson/core/JsonFactory;");
 
   /// from: public com.fasterxml.jackson.core.JsonFactory disable(com.fasterxml.jackson.core.JsonParser.Feature f)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -602,7 +616,7 @@
           [f.reference]).object);
 
   static final _id_isEnabled1 = jniAccessors.getMethodIDOf(_classRef,
-      "isEnabled", "(Lcom/fasterxml/jackson/core/JsonParser\$Feature;)Z");
+      r"isEnabled", r"(Lcom/fasterxml/jackson/core/JsonParser$Feature;)Z");
 
   /// from: public final boolean isEnabled(com.fasterxml.jackson.core.JsonParser.Feature f)
   ///
@@ -614,7 +628,7 @@
           jni.JniCallType.booleanType, [f.reference]).boolean;
 
   static final _id_isEnabled2 = jniAccessors.getMethodIDOf(_classRef,
-      "isEnabled", "(Lcom/fasterxml/jackson/core/StreamReadFeature;)Z");
+      r"isEnabled", r"(Lcom/fasterxml/jackson/core/StreamReadFeature;)Z");
 
   /// from: public final boolean isEnabled(com.fasterxml.jackson.core.StreamReadFeature f)
   ///
@@ -625,8 +639,10 @@
   bool isEnabled2(jni.JObject f) => jniAccessors.callMethodWithArgs(reference,
       _id_isEnabled2, jni.JniCallType.booleanType, [f.reference]).boolean;
 
-  static final _id_getInputDecorator = jniAccessors.getMethodIDOf(_classRef,
-      "getInputDecorator", "()Lcom/fasterxml/jackson/core/io/InputDecorator;");
+  static final _id_getInputDecorator = jniAccessors.getMethodIDOf(
+      _classRef,
+      r"getInputDecorator",
+      r"()Lcom/fasterxml/jackson/core/io/InputDecorator;");
 
   /// from: public com.fasterxml.jackson.core.io.InputDecorator getInputDecorator()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -640,8 +656,8 @@
 
   static final _id_setInputDecorator = jniAccessors.getMethodIDOf(
       _classRef,
-      "setInputDecorator",
-      "(Lcom/fasterxml/jackson/core/io/InputDecorator;)Lcom/fasterxml/jackson/core/JsonFactory;");
+      r"setInputDecorator",
+      r"(Lcom/fasterxml/jackson/core/io/InputDecorator;)Lcom/fasterxml/jackson/core/JsonFactory;");
 
   /// from: public com.fasterxml.jackson.core.JsonFactory setInputDecorator(com.fasterxml.jackson.core.io.InputDecorator d)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -659,8 +675,8 @@
 
   static final _id_configure2 = jniAccessors.getMethodIDOf(
       _classRef,
-      "configure",
-      "(Lcom/fasterxml/jackson/core/JsonGenerator\$Feature;Z)Lcom/fasterxml/jackson/core/JsonFactory;");
+      r"configure",
+      r"(Lcom/fasterxml/jackson/core/JsonGenerator$Feature;Z)Lcom/fasterxml/jackson/core/JsonFactory;");
 
   /// from: public final com.fasterxml.jackson.core.JsonFactory configure(com.fasterxml.jackson.core.JsonGenerator.Feature f, boolean state)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -675,10 +691,10 @@
           reference,
           _id_configure2,
           jni.JniCallType.objectType,
-          [f.reference, state]).object);
+          [f.reference, state ? 1 : 0]).object);
 
-  static final _id_enable2 = jniAccessors.getMethodIDOf(_classRef, "enable",
-      "(Lcom/fasterxml/jackson/core/JsonGenerator\$Feature;)Lcom/fasterxml/jackson/core/JsonFactory;");
+  static final _id_enable2 = jniAccessors.getMethodIDOf(_classRef, r"enable",
+      r"(Lcom/fasterxml/jackson/core/JsonGenerator$Feature;)Lcom/fasterxml/jackson/core/JsonFactory;");
 
   /// from: public com.fasterxml.jackson.core.JsonFactory enable(com.fasterxml.jackson.core.JsonGenerator.Feature f)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -694,8 +710,8 @@
           jni.JniCallType.objectType,
           [f.reference]).object);
 
-  static final _id_disable2 = jniAccessors.getMethodIDOf(_classRef, "disable",
-      "(Lcom/fasterxml/jackson/core/JsonGenerator\$Feature;)Lcom/fasterxml/jackson/core/JsonFactory;");
+  static final _id_disable2 = jniAccessors.getMethodIDOf(_classRef, r"disable",
+      r"(Lcom/fasterxml/jackson/core/JsonGenerator$Feature;)Lcom/fasterxml/jackson/core/JsonFactory;");
 
   /// from: public com.fasterxml.jackson.core.JsonFactory disable(com.fasterxml.jackson.core.JsonGenerator.Feature f)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -712,7 +728,7 @@
           [f.reference]).object);
 
   static final _id_isEnabled3 = jniAccessors.getMethodIDOf(_classRef,
-      "isEnabled", "(Lcom/fasterxml/jackson/core/JsonGenerator\$Feature;)Z");
+      r"isEnabled", r"(Lcom/fasterxml/jackson/core/JsonGenerator$Feature;)Z");
 
   /// from: public final boolean isEnabled(com.fasterxml.jackson.core.JsonGenerator.Feature f)
   ///
@@ -723,7 +739,7 @@
       _id_isEnabled3, jni.JniCallType.booleanType, [f.reference]).boolean;
 
   static final _id_isEnabled4 = jniAccessors.getMethodIDOf(_classRef,
-      "isEnabled", "(Lcom/fasterxml/jackson/core/StreamWriteFeature;)Z");
+      r"isEnabled", r"(Lcom/fasterxml/jackson/core/StreamWriteFeature;)Z");
 
   /// from: public final boolean isEnabled(com.fasterxml.jackson.core.StreamWriteFeature f)
   ///
@@ -736,8 +752,8 @@
 
   static final _id_getCharacterEscapes = jniAccessors.getMethodIDOf(
       _classRef,
-      "getCharacterEscapes",
-      "()Lcom/fasterxml/jackson/core/io/CharacterEscapes;");
+      r"getCharacterEscapes",
+      r"()Lcom/fasterxml/jackson/core/io/CharacterEscapes;");
 
   /// from: public com.fasterxml.jackson.core.io.CharacterEscapes getCharacterEscapes()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -751,8 +767,8 @@
 
   static final _id_setCharacterEscapes = jniAccessors.getMethodIDOf(
       _classRef,
-      "setCharacterEscapes",
-      "(Lcom/fasterxml/jackson/core/io/CharacterEscapes;)Lcom/fasterxml/jackson/core/JsonFactory;");
+      r"setCharacterEscapes",
+      r"(Lcom/fasterxml/jackson/core/io/CharacterEscapes;)Lcom/fasterxml/jackson/core/JsonFactory;");
 
   /// from: public com.fasterxml.jackson.core.JsonFactory setCharacterEscapes(com.fasterxml.jackson.core.io.CharacterEscapes esc)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -770,8 +786,8 @@
 
   static final _id_getOutputDecorator = jniAccessors.getMethodIDOf(
       _classRef,
-      "getOutputDecorator",
-      "()Lcom/fasterxml/jackson/core/io/OutputDecorator;");
+      r"getOutputDecorator",
+      r"()Lcom/fasterxml/jackson/core/io/OutputDecorator;");
 
   /// from: public com.fasterxml.jackson.core.io.OutputDecorator getOutputDecorator()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -786,8 +802,8 @@
 
   static final _id_setOutputDecorator = jniAccessors.getMethodIDOf(
       _classRef,
-      "setOutputDecorator",
-      "(Lcom/fasterxml/jackson/core/io/OutputDecorator;)Lcom/fasterxml/jackson/core/JsonFactory;");
+      r"setOutputDecorator",
+      r"(Lcom/fasterxml/jackson/core/io/OutputDecorator;)Lcom/fasterxml/jackson/core/JsonFactory;");
 
   /// from: public com.fasterxml.jackson.core.JsonFactory setOutputDecorator(com.fasterxml.jackson.core.io.OutputDecorator d)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -805,8 +821,8 @@
 
   static final _id_setRootValueSeparator = jniAccessors.getMethodIDOf(
       _classRef,
-      "setRootValueSeparator",
-      "(Ljava/lang/String;)Lcom/fasterxml/jackson/core/JsonFactory;");
+      r"setRootValueSeparator",
+      r"(Ljava/lang/String;)Lcom/fasterxml/jackson/core/JsonFactory;");
 
   /// from: public com.fasterxml.jackson.core.JsonFactory setRootValueSeparator(java.lang.String sep)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -824,7 +840,7 @@
           [sep.reference]).object);
 
   static final _id_getRootValueSeparator = jniAccessors.getMethodIDOf(
-      _classRef, "getRootValueSeparator", "()Ljava/lang/String;");
+      _classRef, r"getRootValueSeparator", r"()Ljava/lang/String;");
 
   /// from: public java.lang.String getRootValueSeparator()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -834,8 +850,8 @@
       const jni.JStringType().fromRef(jniAccessors.callMethodWithArgs(reference,
           _id_getRootValueSeparator, jni.JniCallType.objectType, []).object);
 
-  static final _id_setCodec = jniAccessors.getMethodIDOf(_classRef, "setCodec",
-      "(Lcom/fasterxml/jackson/core/ObjectCodec;)Lcom/fasterxml/jackson/core/JsonFactory;");
+  static final _id_setCodec = jniAccessors.getMethodIDOf(_classRef, r"setCodec",
+      r"(Lcom/fasterxml/jackson/core/ObjectCodec;)Lcom/fasterxml/jackson/core/JsonFactory;");
 
   /// from: public com.fasterxml.jackson.core.JsonFactory setCodec(com.fasterxml.jackson.core.ObjectCodec oc)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -855,7 +871,7 @@
           [oc.reference]).object);
 
   static final _id_getCodec = jniAccessors.getMethodIDOf(
-      _classRef, "getCodec", "()Lcom/fasterxml/jackson/core/ObjectCodec;");
+      _classRef, r"getCodec", r"()Lcom/fasterxml/jackson/core/ObjectCodec;");
 
   /// from: public com.fasterxml.jackson.core.ObjectCodec getCodec()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -865,8 +881,8 @@
 
   static final _id_createParser = jniAccessors.getMethodIDOf(
       _classRef,
-      "createParser",
-      "(Ljava/io/File;)Lcom/fasterxml/jackson/core/JsonParser;");
+      r"createParser",
+      r"(Ljava/io/File;)Lcom/fasterxml/jackson/core/JsonParser;");
 
   /// from: public com.fasterxml.jackson.core.JsonParser createParser(java.io.File f)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -894,8 +910,8 @@
 
   static final _id_createParser1 = jniAccessors.getMethodIDOf(
       _classRef,
-      "createParser",
-      "(Ljava/net/URL;)Lcom/fasterxml/jackson/core/JsonParser;");
+      r"createParser",
+      r"(Ljava/net/URL;)Lcom/fasterxml/jackson/core/JsonParser;");
 
   /// from: public com.fasterxml.jackson.core.JsonParser createParser(java.net.URL url)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -921,8 +937,8 @@
 
   static final _id_createParser2 = jniAccessors.getMethodIDOf(
       _classRef,
-      "createParser",
-      "(Ljava/io/InputStream;)Lcom/fasterxml/jackson/core/JsonParser;");
+      r"createParser",
+      r"(Ljava/io/InputStream;)Lcom/fasterxml/jackson/core/JsonParser;");
 
   /// from: public com.fasterxml.jackson.core.JsonParser createParser(java.io.InputStream in)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -951,8 +967,8 @@
 
   static final _id_createParser3 = jniAccessors.getMethodIDOf(
       _classRef,
-      "createParser",
-      "(Ljava/io/Reader;)Lcom/fasterxml/jackson/core/JsonParser;");
+      r"createParser",
+      r"(Ljava/io/Reader;)Lcom/fasterxml/jackson/core/JsonParser;");
 
   /// from: public com.fasterxml.jackson.core.JsonParser createParser(java.io.Reader r)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -972,8 +988,8 @@
           .callMethodWithArgs(reference, _id_createParser3,
               jni.JniCallType.objectType, [r.reference]).object);
 
-  static final _id_createParser4 = jniAccessors.getMethodIDOf(
-      _classRef, "createParser", "([B)Lcom/fasterxml/jackson/core/JsonParser;");
+  static final _id_createParser4 = jniAccessors.getMethodIDOf(_classRef,
+      r"createParser", r"([B)Lcom/fasterxml/jackson/core/JsonParser;");
 
   /// from: public com.fasterxml.jackson.core.JsonParser createParser(byte[] data)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -987,7 +1003,7 @@
               jni.JniCallType.objectType, [data.reference]).object);
 
   static final _id_createParser5 = jniAccessors.getMethodIDOf(_classRef,
-      "createParser", "([BII)Lcom/fasterxml/jackson/core/JsonParser;");
+      r"createParser", r"([BII)Lcom/fasterxml/jackson/core/JsonParser;");
 
   /// from: public com.fasterxml.jackson.core.JsonParser createParser(byte[] data, int offset, int len)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -1009,8 +1025,8 @@
 
   static final _id_createParser6 = jniAccessors.getMethodIDOf(
       _classRef,
-      "createParser",
-      "(Ljava/lang/String;)Lcom/fasterxml/jackson/core/JsonParser;");
+      r"createParser",
+      r"(Ljava/lang/String;)Lcom/fasterxml/jackson/core/JsonParser;");
 
   /// from: public com.fasterxml.jackson.core.JsonParser createParser(java.lang.String content)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -1023,8 +1039,8 @@
           .callMethodWithArgs(reference, _id_createParser6,
               jni.JniCallType.objectType, [content.reference]).object);
 
-  static final _id_createParser7 = jniAccessors.getMethodIDOf(
-      _classRef, "createParser", "([C)Lcom/fasterxml/jackson/core/JsonParser;");
+  static final _id_createParser7 = jniAccessors.getMethodIDOf(_classRef,
+      r"createParser", r"([C)Lcom/fasterxml/jackson/core/JsonParser;");
 
   /// from: public com.fasterxml.jackson.core.JsonParser createParser(char[] content)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -1038,7 +1054,7 @@
               jni.JniCallType.objectType, [content.reference]).object);
 
   static final _id_createParser8 = jniAccessors.getMethodIDOf(_classRef,
-      "createParser", "([CII)Lcom/fasterxml/jackson/core/JsonParser;");
+      r"createParser", r"([CII)Lcom/fasterxml/jackson/core/JsonParser;");
 
   /// from: public com.fasterxml.jackson.core.JsonParser createParser(char[] content, int offset, int len)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -1056,8 +1072,8 @@
 
   static final _id_createParser9 = jniAccessors.getMethodIDOf(
       _classRef,
-      "createParser",
-      "(Ljava/io/DataInput;)Lcom/fasterxml/jackson/core/JsonParser;");
+      r"createParser",
+      r"(Ljava/io/DataInput;)Lcom/fasterxml/jackson/core/JsonParser;");
 
   /// from: public com.fasterxml.jackson.core.JsonParser createParser(java.io.DataInput in)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -1074,8 +1090,8 @@
               jni.JniCallType.objectType, [in0.reference]).object);
 
   static final _id_createNonBlockingByteArrayParser =
-      jniAccessors.getMethodIDOf(_classRef, "createNonBlockingByteArrayParser",
-          "()Lcom/fasterxml/jackson/core/JsonParser;");
+      jniAccessors.getMethodIDOf(_classRef, r"createNonBlockingByteArrayParser",
+          r"()Lcom/fasterxml/jackson/core/JsonParser;");
 
   /// from: public com.fasterxml.jackson.core.JsonParser createNonBlockingByteArrayParser()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -1100,8 +1116,8 @@
 
   static final _id_createGenerator = jniAccessors.getMethodIDOf(
       _classRef,
-      "createGenerator",
-      "(Ljava/io/OutputStream;Lcom/fasterxml/jackson/core/JsonEncoding;)Lcom/fasterxml/jackson/core/JsonGenerator;");
+      r"createGenerator",
+      r"(Ljava/io/OutputStream;Lcom/fasterxml/jackson/core/JsonEncoding;)Lcom/fasterxml/jackson/core/JsonGenerator;");
 
   /// from: public com.fasterxml.jackson.core.JsonGenerator createGenerator(java.io.OutputStream out, com.fasterxml.jackson.core.JsonEncoding enc)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -1133,8 +1149,8 @@
 
   static final _id_createGenerator1 = jniAccessors.getMethodIDOf(
       _classRef,
-      "createGenerator",
-      "(Ljava/io/OutputStream;)Lcom/fasterxml/jackson/core/JsonGenerator;");
+      r"createGenerator",
+      r"(Ljava/io/OutputStream;)Lcom/fasterxml/jackson/core/JsonGenerator;");
 
   /// from: public com.fasterxml.jackson.core.JsonGenerator createGenerator(java.io.OutputStream out)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -1153,8 +1169,8 @@
 
   static final _id_createGenerator2 = jniAccessors.getMethodIDOf(
       _classRef,
-      "createGenerator",
-      "(Ljava/io/Writer;)Lcom/fasterxml/jackson/core/JsonGenerator;");
+      r"createGenerator",
+      r"(Ljava/io/Writer;)Lcom/fasterxml/jackson/core/JsonGenerator;");
 
   /// from: public com.fasterxml.jackson.core.JsonGenerator createGenerator(java.io.Writer w)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -1179,8 +1195,8 @@
 
   static final _id_createGenerator3 = jniAccessors.getMethodIDOf(
       _classRef,
-      "createGenerator",
-      "(Ljava/io/File;Lcom/fasterxml/jackson/core/JsonEncoding;)Lcom/fasterxml/jackson/core/JsonGenerator;");
+      r"createGenerator",
+      r"(Ljava/io/File;Lcom/fasterxml/jackson/core/JsonEncoding;)Lcom/fasterxml/jackson/core/JsonGenerator;");
 
   /// from: public com.fasterxml.jackson.core.JsonGenerator createGenerator(java.io.File f, com.fasterxml.jackson.core.JsonEncoding enc)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -1206,8 +1222,8 @@
 
   static final _id_createGenerator4 = jniAccessors.getMethodIDOf(
       _classRef,
-      "createGenerator",
-      "(Ljava/io/DataOutput;Lcom/fasterxml/jackson/core/JsonEncoding;)Lcom/fasterxml/jackson/core/JsonGenerator;");
+      r"createGenerator",
+      r"(Ljava/io/DataOutput;Lcom/fasterxml/jackson/core/JsonEncoding;)Lcom/fasterxml/jackson/core/JsonGenerator;");
 
   /// from: public com.fasterxml.jackson.core.JsonGenerator createGenerator(java.io.DataOutput out, com.fasterxml.jackson.core.JsonEncoding enc)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -1224,8 +1240,8 @@
 
   static final _id_createGenerator5 = jniAccessors.getMethodIDOf(
       _classRef,
-      "createGenerator",
-      "(Ljava/io/DataOutput;)Lcom/fasterxml/jackson/core/JsonGenerator;");
+      r"createGenerator",
+      r"(Ljava/io/DataOutput;)Lcom/fasterxml/jackson/core/JsonGenerator;");
 
   /// from: public com.fasterxml.jackson.core.JsonGenerator createGenerator(java.io.DataOutput out)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -1244,8 +1260,8 @@
 
   static final _id_createJsonParser = jniAccessors.getMethodIDOf(
       _classRef,
-      "createJsonParser",
-      "(Ljava/io/File;)Lcom/fasterxml/jackson/core/JsonParser;");
+      r"createJsonParser",
+      r"(Ljava/io/File;)Lcom/fasterxml/jackson/core/JsonParser;");
 
   /// from: public com.fasterxml.jackson.core.JsonParser createJsonParser(java.io.File f)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -1275,8 +1291,8 @@
 
   static final _id_createJsonParser1 = jniAccessors.getMethodIDOf(
       _classRef,
-      "createJsonParser",
-      "(Ljava/net/URL;)Lcom/fasterxml/jackson/core/JsonParser;");
+      r"createJsonParser",
+      r"(Ljava/net/URL;)Lcom/fasterxml/jackson/core/JsonParser;");
 
   /// from: public com.fasterxml.jackson.core.JsonParser createJsonParser(java.net.URL url)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -1305,8 +1321,8 @@
 
   static final _id_createJsonParser2 = jniAccessors.getMethodIDOf(
       _classRef,
-      "createJsonParser",
-      "(Ljava/io/InputStream;)Lcom/fasterxml/jackson/core/JsonParser;");
+      r"createJsonParser",
+      r"(Ljava/io/InputStream;)Lcom/fasterxml/jackson/core/JsonParser;");
 
   /// from: public com.fasterxml.jackson.core.JsonParser createJsonParser(java.io.InputStream in)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -1338,8 +1354,8 @@
 
   static final _id_createJsonParser3 = jniAccessors.getMethodIDOf(
       _classRef,
-      "createJsonParser",
-      "(Ljava/io/Reader;)Lcom/fasterxml/jackson/core/JsonParser;");
+      r"createJsonParser",
+      r"(Ljava/io/Reader;)Lcom/fasterxml/jackson/core/JsonParser;");
 
   /// from: public com.fasterxml.jackson.core.JsonParser createJsonParser(java.io.Reader r)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -1363,7 +1379,7 @@
               jni.JniCallType.objectType, [r.reference]).object);
 
   static final _id_createJsonParser4 = jniAccessors.getMethodIDOf(_classRef,
-      "createJsonParser", "([B)Lcom/fasterxml/jackson/core/JsonParser;");
+      r"createJsonParser", r"([B)Lcom/fasterxml/jackson/core/JsonParser;");
 
   /// from: public com.fasterxml.jackson.core.JsonParser createJsonParser(byte[] data)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -1380,7 +1396,7 @@
               jni.JniCallType.objectType, [data.reference]).object);
 
   static final _id_createJsonParser5 = jniAccessors.getMethodIDOf(_classRef,
-      "createJsonParser", "([BII)Lcom/fasterxml/jackson/core/JsonParser;");
+      r"createJsonParser", r"([BII)Lcom/fasterxml/jackson/core/JsonParser;");
 
   /// from: public com.fasterxml.jackson.core.JsonParser createJsonParser(byte[] data, int offset, int len)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -1405,8 +1421,8 @@
 
   static final _id_createJsonParser6 = jniAccessors.getMethodIDOf(
       _classRef,
-      "createJsonParser",
-      "(Ljava/lang/String;)Lcom/fasterxml/jackson/core/JsonParser;");
+      r"createJsonParser",
+      r"(Ljava/lang/String;)Lcom/fasterxml/jackson/core/JsonParser;");
 
   /// from: public com.fasterxml.jackson.core.JsonParser createJsonParser(java.lang.String content)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -1425,8 +1441,8 @@
 
   static final _id_createJsonGenerator = jniAccessors.getMethodIDOf(
       _classRef,
-      "createJsonGenerator",
-      "(Ljava/io/OutputStream;Lcom/fasterxml/jackson/core/JsonEncoding;)Lcom/fasterxml/jackson/core/JsonGenerator;");
+      r"createJsonGenerator",
+      r"(Ljava/io/OutputStream;Lcom/fasterxml/jackson/core/JsonEncoding;)Lcom/fasterxml/jackson/core/JsonGenerator;");
 
   /// from: public com.fasterxml.jackson.core.JsonGenerator createJsonGenerator(java.io.OutputStream out, com.fasterxml.jackson.core.JsonEncoding enc)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -1460,8 +1476,8 @@
 
   static final _id_createJsonGenerator1 = jniAccessors.getMethodIDOf(
       _classRef,
-      "createJsonGenerator",
-      "(Ljava/io/Writer;)Lcom/fasterxml/jackson/core/JsonGenerator;");
+      r"createJsonGenerator",
+      r"(Ljava/io/Writer;)Lcom/fasterxml/jackson/core/JsonGenerator;");
 
   /// from: public com.fasterxml.jackson.core.JsonGenerator createJsonGenerator(java.io.Writer out)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -1488,8 +1504,8 @@
 
   static final _id_createJsonGenerator2 = jniAccessors.getMethodIDOf(
       _classRef,
-      "createJsonGenerator",
-      "(Ljava/io/OutputStream;)Lcom/fasterxml/jackson/core/JsonGenerator;");
+      r"createJsonGenerator",
+      r"(Ljava/io/OutputStream;)Lcom/fasterxml/jackson/core/JsonGenerator;");
 
   /// from: public com.fasterxml.jackson.core.JsonGenerator createJsonGenerator(java.io.OutputStream out)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -1545,13 +1561,12 @@
   ) : super.fromRef(ref);
 
   static final _classRef = jniAccessors
-      .getClassOf("com/fasterxml/jackson/core/JsonFactory\$Feature");
+      .getClassOf(r"com/fasterxml/jackson/core/JsonFactory$Feature");
 
   /// The type which includes information such as the signature of this class.
   static const type = $JsonFactory_FeatureType();
-
   static final _id_values = jniAccessors.getStaticMethodIDOf(_classRef,
-      "values", "()[Lcom/fasterxml/jackson/core/JsonFactory\$Feature;");
+      r"values", r"()[Lcom/fasterxml/jackson/core/JsonFactory$Feature;");
 
   /// from: static public com.fasterxml.jackson.core.JsonFactory.Feature[] values()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -1562,8 +1577,8 @@
 
   static final _id_valueOf = jniAccessors.getStaticMethodIDOf(
       _classRef,
-      "valueOf",
-      "(Ljava/lang/String;)Lcom/fasterxml/jackson/core/JsonFactory\$Feature;");
+      r"valueOf",
+      r"(Ljava/lang/String;)Lcom/fasterxml/jackson/core/JsonFactory$Feature;");
 
   /// from: static public com.fasterxml.jackson.core.JsonFactory.Feature valueOf(java.lang.String name)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -1573,7 +1588,7 @@
               jni.JniCallType.objectType, [name.reference]).object);
 
   static final _id_collectDefaults =
-      jniAccessors.getStaticMethodIDOf(_classRef, "collectDefaults", "()I");
+      jniAccessors.getStaticMethodIDOf(_classRef, r"collectDefaults", r"()I");
 
   /// from: static public int collectDefaults()
   ///
@@ -1584,21 +1599,21 @@
       _classRef, _id_collectDefaults, jni.JniCallType.intType, []).integer;
 
   static final _id_enabledByDefault =
-      jniAccessors.getMethodIDOf(_classRef, "enabledByDefault", "()Z");
+      jniAccessors.getMethodIDOf(_classRef, r"enabledByDefault", r"()Z");
 
   /// from: public boolean enabledByDefault()
   bool enabledByDefault() => jniAccessors.callMethodWithArgs(
       reference, _id_enabledByDefault, jni.JniCallType.booleanType, []).boolean;
 
   static final _id_enabledIn =
-      jniAccessors.getMethodIDOf(_classRef, "enabledIn", "(I)Z");
+      jniAccessors.getMethodIDOf(_classRef, r"enabledIn", r"(I)Z");
 
   /// from: public boolean enabledIn(int flags)
   bool enabledIn(int flags) => jniAccessors.callMethodWithArgs(
       reference, _id_enabledIn, jni.JniCallType.booleanType, [flags]).boolean;
 
   static final _id_getMask =
-      jniAccessors.getMethodIDOf(_classRef, "getMask", "()I");
+      jniAccessors.getMethodIDOf(_classRef, r"getMask", r"()I");
 
   /// from: public int getMask()
   int getMask() => jniAccessors.callMethodWithArgs(
diff --git a/pkgs/jnigen/test/jackson_core_test/third_party/lib/com/fasterxml/jackson/core/JsonParser.dart b/pkgs/jnigen/test/jackson_core_test/third_party/lib/com/fasterxml/jackson/core/JsonParser.dart
index 4f45337..c371560 100644
--- a/pkgs/jnigen/test/jackson_core_test/third_party/lib/com/fasterxml/jackson/core/JsonParser.dart
+++ b/pkgs/jnigen/test/jackson_core_test/third_party/lib/com/fasterxml/jackson/core/JsonParser.dart
@@ -32,13 +32,12 @@
 // ignore_for_file: unused_shown_name
 
 import "dart:isolate" show ReceivePort;
-
+import "dart:ffi" as ffi;
+import "package:jni/internal_helpers_for_jnigen.dart";
 import "package:jni/jni.dart" as jni;
 
-import "package:jni/internal_helpers_for_jnigen.dart";
-
 import "JsonToken.dart" as jsontoken_;
-import "../../../../_init.dart" show jniEnv, jniAccessors;
+import "../../../../_init.dart";
 
 /// from: com.fasterxml.jackson.core.JsonParser
 ///
@@ -56,15 +55,15 @@
   ) : super.fromRef(ref);
 
   static final _classRef =
-      jniAccessors.getClassOf("com/fasterxml/jackson/core/JsonParser");
+      jniAccessors.getClassOf(r"com/fasterxml/jackson/core/JsonParser");
 
   /// The type which includes information such as the signature of this class.
   static const type = $JsonParserType();
-
   static final _id_DEFAULT_READ_CAPABILITIES = jniAccessors.getStaticFieldIDOf(
-      _classRef,
-      "DEFAULT_READ_CAPABILITIES",
-      "Lcom/fasterxml/jackson/core/util/JacksonFeatureSet;");
+    _classRef,
+    r"DEFAULT_READ_CAPABILITIES",
+    r"Lcom/fasterxml/jackson/core/util/JacksonFeatureSet;",
+  );
 
   /// from: static protected final com.fasterxml.jackson.core.util.JacksonFeatureSet<com.fasterxml.jackson.core.StreamReadCapability> DEFAULT_READ_CAPABILITIES
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -80,7 +79,7 @@
           .object);
 
   static final _id_ctor =
-      jniAccessors.getMethodIDOf(_classRef, "<init>", "()V");
+      jniAccessors.getMethodIDOf(_classRef, r"<init>", r"()V");
 
   /// from: protected void <init>()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -89,7 +88,7 @@
             jniAccessors.newObjectWithArgs(_classRef, _id_ctor, []).object);
 
   static final _id_ctor1 =
-      jniAccessors.getMethodIDOf(_classRef, "<init>", "(I)V");
+      jniAccessors.getMethodIDOf(_classRef, r"<init>", r"(I)V");
 
   /// from: protected void <init>(int features)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -98,7 +97,7 @@
             .newObjectWithArgs(_classRef, _id_ctor1, [features]).object);
 
   static final _id_getCodec = jniAccessors.getMethodIDOf(
-      _classRef, "getCodec", "()Lcom/fasterxml/jackson/core/ObjectCodec;");
+      _classRef, r"getCodec", r"()Lcom/fasterxml/jackson/core/ObjectCodec;");
 
   /// from: public abstract com.fasterxml.jackson.core.ObjectCodec getCodec()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -112,7 +111,7 @@
           reference, _id_getCodec, jni.JniCallType.objectType, []).object);
 
   static final _id_setCodec = jniAccessors.getMethodIDOf(
-      _classRef, "setCodec", "(Lcom/fasterxml/jackson/core/ObjectCodec;)V");
+      _classRef, r"setCodec", r"(Lcom/fasterxml/jackson/core/ObjectCodec;)V");
 
   /// from: public abstract void setCodec(com.fasterxml.jackson.core.ObjectCodec oc)
   ///
@@ -124,7 +123,7 @@
       _id_setCodec, jni.JniCallType.voidType, [oc.reference]).check();
 
   static final _id_getInputSource = jniAccessors.getMethodIDOf(
-      _classRef, "getInputSource", "()Ljava/lang/Object;");
+      _classRef, r"getInputSource", r"()Ljava/lang/Object;");
 
   /// from: public java.lang.Object getInputSource()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -149,8 +148,8 @@
 
   static final _id_setRequestPayloadOnError = jniAccessors.getMethodIDOf(
       _classRef,
-      "setRequestPayloadOnError",
-      "(Lcom/fasterxml/jackson/core/util/RequestPayload;)V");
+      r"setRequestPayloadOnError",
+      r"(Lcom/fasterxml/jackson/core/util/RequestPayload;)V");
 
   /// from: public void setRequestPayloadOnError(com.fasterxml.jackson.core.util.RequestPayload payload)
   ///
@@ -162,7 +161,7 @@
           jni.JniCallType.voidType, [payload.reference]).check();
 
   static final _id_setRequestPayloadOnError1 = jniAccessors.getMethodIDOf(
-      _classRef, "setRequestPayloadOnError", "([BLjava/lang/String;)V");
+      _classRef, r"setRequestPayloadOnError", r"([BLjava/lang/String;)V");
 
   /// from: public void setRequestPayloadOnError(byte[] payload, java.lang.String charset)
   ///
@@ -179,7 +178,7 @@
           [payload.reference, charset.reference]).check();
 
   static final _id_setRequestPayloadOnError2 = jniAccessors.getMethodIDOf(
-      _classRef, "setRequestPayloadOnError", "(Ljava/lang/String;)V");
+      _classRef, r"setRequestPayloadOnError", r"(Ljava/lang/String;)V");
 
   /// from: public void setRequestPayloadOnError(java.lang.String payload)
   ///
@@ -191,7 +190,7 @@
           jni.JniCallType.voidType, [payload.reference]).check();
 
   static final _id_setSchema = jniAccessors.getMethodIDOf(
-      _classRef, "setSchema", "(Lcom/fasterxml/jackson/core/FormatSchema;)V");
+      _classRef, r"setSchema", r"(Lcom/fasterxml/jackson/core/FormatSchema;)V");
 
   /// from: public void setSchema(com.fasterxml.jackson.core.FormatSchema schema)
   ///
@@ -212,7 +211,7 @@
       [schema.reference]).check();
 
   static final _id_getSchema = jniAccessors.getMethodIDOf(
-      _classRef, "getSchema", "()Lcom/fasterxml/jackson/core/FormatSchema;");
+      _classRef, r"getSchema", r"()Lcom/fasterxml/jackson/core/FormatSchema;");
 
   /// from: public com.fasterxml.jackson.core.FormatSchema getSchema()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -226,7 +225,7 @@
           reference, _id_getSchema, jni.JniCallType.objectType, []).object);
 
   static final _id_canUseSchema = jniAccessors.getMethodIDOf(_classRef,
-      "canUseSchema", "(Lcom/fasterxml/jackson/core/FormatSchema;)Z");
+      r"canUseSchema", r"(Lcom/fasterxml/jackson/core/FormatSchema;)Z");
 
   /// from: public boolean canUseSchema(com.fasterxml.jackson.core.FormatSchema schema)
   ///
@@ -241,7 +240,7 @@
       [schema.reference]).boolean;
 
   static final _id_requiresCustomCodec =
-      jniAccessors.getMethodIDOf(_classRef, "requiresCustomCodec", "()Z");
+      jniAccessors.getMethodIDOf(_classRef, r"requiresCustomCodec", r"()Z");
 
   /// from: public boolean requiresCustomCodec()
   ///
@@ -257,7 +256,7 @@
       _id_requiresCustomCodec, jni.JniCallType.booleanType, []).boolean;
 
   static final _id_canParseAsync =
-      jniAccessors.getMethodIDOf(_classRef, "canParseAsync", "()Z");
+      jniAccessors.getMethodIDOf(_classRef, r"canParseAsync", r"()Z");
 
   /// from: public boolean canParseAsync()
   ///
@@ -277,8 +276,8 @@
 
   static final _id_getNonBlockingInputFeeder = jniAccessors.getMethodIDOf(
       _classRef,
-      "getNonBlockingInputFeeder",
-      "()Lcom/fasterxml/jackson/core/async/NonBlockingInputFeeder;");
+      r"getNonBlockingInputFeeder",
+      r"()Lcom/fasterxml/jackson/core/async/NonBlockingInputFeeder;");
 
   /// from: public com.fasterxml.jackson.core.async.NonBlockingInputFeeder getNonBlockingInputFeeder()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -296,8 +295,8 @@
 
   static final _id_getReadCapabilities = jniAccessors.getMethodIDOf(
       _classRef,
-      "getReadCapabilities",
-      "()Lcom/fasterxml/jackson/core/util/JacksonFeatureSet;");
+      r"getReadCapabilities",
+      r"()Lcom/fasterxml/jackson/core/util/JacksonFeatureSet;");
 
   /// from: public com.fasterxml.jackson.core.util.JacksonFeatureSet<com.fasterxml.jackson.core.StreamReadCapability> getReadCapabilities()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -311,7 +310,7 @@
           _id_getReadCapabilities, jni.JniCallType.objectType, []).object);
 
   static final _id_version = jniAccessors.getMethodIDOf(
-      _classRef, "version", "()Lcom/fasterxml/jackson/core/Version;");
+      _classRef, r"version", r"()Lcom/fasterxml/jackson/core/Version;");
 
   /// from: public abstract com.fasterxml.jackson.core.Version version()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -325,7 +324,7 @@
           reference, _id_version, jni.JniCallType.objectType, []).object);
 
   static final _id_close =
-      jniAccessors.getMethodIDOf(_classRef, "close", "()V");
+      jniAccessors.getMethodIDOf(_classRef, r"close", r"()V");
 
   /// from: public abstract void close()
   ///
@@ -347,7 +346,7 @@
       reference, _id_close, jni.JniCallType.voidType, []).check();
 
   static final _id_isClosed =
-      jniAccessors.getMethodIDOf(_classRef, "isClosed", "()Z");
+      jniAccessors.getMethodIDOf(_classRef, r"isClosed", r"()Z");
 
   /// from: public abstract boolean isClosed()
   ///
@@ -361,8 +360,10 @@
   bool isClosed() => jniAccessors.callMethodWithArgs(
       reference, _id_isClosed, jni.JniCallType.booleanType, []).boolean;
 
-  static final _id_getParsingContext = jniAccessors.getMethodIDOf(_classRef,
-      "getParsingContext", "()Lcom/fasterxml/jackson/core/JsonStreamContext;");
+  static final _id_getParsingContext = jniAccessors.getMethodIDOf(
+      _classRef,
+      r"getParsingContext",
+      r"()Lcom/fasterxml/jackson/core/JsonStreamContext;");
 
   /// from: public abstract com.fasterxml.jackson.core.JsonStreamContext getParsingContext()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -381,7 +382,7 @@
           _id_getParsingContext, jni.JniCallType.objectType, []).object);
 
   static final _id_currentLocation = jniAccessors.getMethodIDOf(_classRef,
-      "currentLocation", "()Lcom/fasterxml/jackson/core/JsonLocation;");
+      r"currentLocation", r"()Lcom/fasterxml/jackson/core/JsonLocation;");
 
   /// from: public com.fasterxml.jackson.core.JsonLocation currentLocation()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -403,7 +404,7 @@
           _id_currentLocation, jni.JniCallType.objectType, []).object);
 
   static final _id_currentTokenLocation = jniAccessors.getMethodIDOf(_classRef,
-      "currentTokenLocation", "()Lcom/fasterxml/jackson/core/JsonLocation;");
+      r"currentTokenLocation", r"()Lcom/fasterxml/jackson/core/JsonLocation;");
 
   /// from: public com.fasterxml.jackson.core.JsonLocation currentTokenLocation()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -425,7 +426,7 @@
           _id_currentTokenLocation, jni.JniCallType.objectType, []).object);
 
   static final _id_getCurrentLocation = jniAccessors.getMethodIDOf(_classRef,
-      "getCurrentLocation", "()Lcom/fasterxml/jackson/core/JsonLocation;");
+      r"getCurrentLocation", r"()Lcom/fasterxml/jackson/core/JsonLocation;");
 
   /// from: public abstract com.fasterxml.jackson.core.JsonLocation getCurrentLocation()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -438,7 +439,7 @@
           _id_getCurrentLocation, jni.JniCallType.objectType, []).object);
 
   static final _id_getTokenLocation = jniAccessors.getMethodIDOf(_classRef,
-      "getTokenLocation", "()Lcom/fasterxml/jackson/core/JsonLocation;");
+      r"getTokenLocation", r"()Lcom/fasterxml/jackson/core/JsonLocation;");
 
   /// from: public abstract com.fasterxml.jackson.core.JsonLocation getTokenLocation()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -451,7 +452,7 @@
           _id_getTokenLocation, jni.JniCallType.objectType, []).object);
 
   static final _id_currentValue = jniAccessors.getMethodIDOf(
-      _classRef, "currentValue", "()Ljava/lang/Object;");
+      _classRef, r"currentValue", r"()Ljava/lang/Object;");
 
   /// from: public java.lang.Object currentValue()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -472,7 +473,7 @@
           reference, _id_currentValue, jni.JniCallType.objectType, []).object);
 
   static final _id_assignCurrentValue = jniAccessors.getMethodIDOf(
-      _classRef, "assignCurrentValue", "(Ljava/lang/Object;)V");
+      _classRef, r"assignCurrentValue", r"(Ljava/lang/Object;)V");
 
   /// from: public void assignCurrentValue(java.lang.Object v)
   ///
@@ -489,7 +490,7 @@
       [v.reference]).check();
 
   static final _id_getCurrentValue = jniAccessors.getMethodIDOf(
-      _classRef, "getCurrentValue", "()Ljava/lang/Object;");
+      _classRef, r"getCurrentValue", r"()Ljava/lang/Object;");
 
   /// from: public java.lang.Object getCurrentValue()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -502,7 +503,7 @@
           _id_getCurrentValue, jni.JniCallType.objectType, []).object);
 
   static final _id_setCurrentValue = jniAccessors.getMethodIDOf(
-      _classRef, "setCurrentValue", "(Ljava/lang/Object;)V");
+      _classRef, r"setCurrentValue", r"(Ljava/lang/Object;)V");
 
   /// from: public void setCurrentValue(java.lang.Object v)
   ///
@@ -516,7 +517,7 @@
       [v.reference]).check();
 
   static final _id_releaseBuffered = jniAccessors.getMethodIDOf(
-      _classRef, "releaseBuffered", "(Ljava/io/OutputStream;)I");
+      _classRef, r"releaseBuffered", r"(Ljava/io/OutputStream;)I");
 
   /// from: public int releaseBuffered(java.io.OutputStream out)
   ///
@@ -538,7 +539,7 @@
       [out.reference]).integer;
 
   static final _id_releaseBuffered1 = jniAccessors.getMethodIDOf(
-      _classRef, "releaseBuffered", "(Ljava/io/Writer;)I");
+      _classRef, r"releaseBuffered", r"(Ljava/io/Writer;)I");
 
   /// from: public int releaseBuffered(java.io.Writer w)
   ///
@@ -560,8 +561,8 @@
       jni.JniCallType.intType,
       [w.reference]).integer;
 
-  static final _id_enable = jniAccessors.getMethodIDOf(_classRef, "enable",
-      "(Lcom/fasterxml/jackson/core/JsonParser\$Feature;)Lcom/fasterxml/jackson/core/JsonParser;");
+  static final _id_enable = jniAccessors.getMethodIDOf(_classRef, r"enable",
+      r"(Lcom/fasterxml/jackson/core/JsonParser$Feature;)Lcom/fasterxml/jackson/core/JsonParser;");
 
   /// from: public com.fasterxml.jackson.core.JsonParser enable(com.fasterxml.jackson.core.JsonParser.Feature f)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -574,8 +575,8 @@
       const $JsonParserType().fromRef(jniAccessors.callMethodWithArgs(reference,
           _id_enable, jni.JniCallType.objectType, [f.reference]).object);
 
-  static final _id_disable = jniAccessors.getMethodIDOf(_classRef, "disable",
-      "(Lcom/fasterxml/jackson/core/JsonParser\$Feature;)Lcom/fasterxml/jackson/core/JsonParser;");
+  static final _id_disable = jniAccessors.getMethodIDOf(_classRef, r"disable",
+      r"(Lcom/fasterxml/jackson/core/JsonParser$Feature;)Lcom/fasterxml/jackson/core/JsonParser;");
 
   /// from: public com.fasterxml.jackson.core.JsonParser disable(com.fasterxml.jackson.core.JsonParser.Feature f)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -590,8 +591,8 @@
 
   static final _id_configure = jniAccessors.getMethodIDOf(
       _classRef,
-      "configure",
-      "(Lcom/fasterxml/jackson/core/JsonParser\$Feature;Z)Lcom/fasterxml/jackson/core/JsonParser;");
+      r"configure",
+      r"(Lcom/fasterxml/jackson/core/JsonParser$Feature;Z)Lcom/fasterxml/jackson/core/JsonParser;");
 
   /// from: public com.fasterxml.jackson.core.JsonParser configure(com.fasterxml.jackson.core.JsonParser.Feature f, boolean state)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -606,10 +607,10 @@
           reference,
           _id_configure,
           jni.JniCallType.objectType,
-          [f.reference, state]).object);
+          [f.reference, state ? 1 : 0]).object);
 
   static final _id_isEnabled = jniAccessors.getMethodIDOf(_classRef,
-      "isEnabled", "(Lcom/fasterxml/jackson/core/JsonParser\$Feature;)Z");
+      r"isEnabled", r"(Lcom/fasterxml/jackson/core/JsonParser$Feature;)Z");
 
   /// from: public boolean isEnabled(com.fasterxml.jackson.core.JsonParser.Feature f)
   ///
@@ -623,7 +624,7 @@
       [f.reference]).boolean;
 
   static final _id_isEnabled1 = jniAccessors.getMethodIDOf(_classRef,
-      "isEnabled", "(Lcom/fasterxml/jackson/core/StreamReadFeature;)Z");
+      r"isEnabled", r"(Lcom/fasterxml/jackson/core/StreamReadFeature;)Z");
 
   /// from: public boolean isEnabled(com.fasterxml.jackson.core.StreamReadFeature f)
   ///
@@ -635,7 +636,7 @@
       _id_isEnabled1, jni.JniCallType.booleanType, [f.reference]).boolean;
 
   static final _id_getFeatureMask =
-      jniAccessors.getMethodIDOf(_classRef, "getFeatureMask", "()I");
+      jniAccessors.getMethodIDOf(_classRef, r"getFeatureMask", r"()I");
 
   /// from: public int getFeatureMask()
   ///
@@ -646,7 +647,7 @@
       reference, _id_getFeatureMask, jni.JniCallType.intType, []).integer;
 
   static final _id_setFeatureMask = jniAccessors.getMethodIDOf(_classRef,
-      "setFeatureMask", "(I)Lcom/fasterxml/jackson/core/JsonParser;");
+      r"setFeatureMask", r"(I)Lcom/fasterxml/jackson/core/JsonParser;");
 
   /// from: public com.fasterxml.jackson.core.JsonParser setFeatureMask(int mask)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -661,7 +662,7 @@
           _id_setFeatureMask, jni.JniCallType.objectType, [mask]).object);
 
   static final _id_overrideStdFeatures = jniAccessors.getMethodIDOf(_classRef,
-      "overrideStdFeatures", "(II)Lcom/fasterxml/jackson/core/JsonParser;");
+      r"overrideStdFeatures", r"(II)Lcom/fasterxml/jackson/core/JsonParser;");
 
   /// from: public com.fasterxml.jackson.core.JsonParser overrideStdFeatures(int values, int mask)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -686,7 +687,7 @@
           [values, mask]).object);
 
   static final _id_getFormatFeatures =
-      jniAccessors.getMethodIDOf(_classRef, "getFormatFeatures", "()I");
+      jniAccessors.getMethodIDOf(_classRef, r"getFormatFeatures", r"()I");
 
   /// from: public int getFormatFeatures()
   ///
@@ -699,8 +700,8 @@
 
   static final _id_overrideFormatFeatures = jniAccessors.getMethodIDOf(
       _classRef,
-      "overrideFormatFeatures",
-      "(II)Lcom/fasterxml/jackson/core/JsonParser;");
+      r"overrideFormatFeatures",
+      r"(II)Lcom/fasterxml/jackson/core/JsonParser;");
 
   /// from: public com.fasterxml.jackson.core.JsonParser overrideFormatFeatures(int values, int mask)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -723,7 +724,7 @@
           [values, mask]).object);
 
   static final _id_nextToken = jniAccessors.getMethodIDOf(
-      _classRef, "nextToken", "()Lcom/fasterxml/jackson/core/JsonToken;");
+      _classRef, r"nextToken", r"()Lcom/fasterxml/jackson/core/JsonToken;");
 
   /// from: public abstract com.fasterxml.jackson.core.JsonToken nextToken()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -741,7 +742,7 @@
           reference, _id_nextToken, jni.JniCallType.objectType, []).object);
 
   static final _id_nextValue = jniAccessors.getMethodIDOf(
-      _classRef, "nextValue", "()Lcom/fasterxml/jackson/core/JsonToken;");
+      _classRef, r"nextValue", r"()Lcom/fasterxml/jackson/core/JsonToken;");
 
   /// from: public abstract com.fasterxml.jackson.core.JsonToken nextValue()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -767,7 +768,7 @@
           reference, _id_nextValue, jni.JniCallType.objectType, []).object);
 
   static final _id_nextFieldName = jniAccessors.getMethodIDOf(_classRef,
-      "nextFieldName", "(Lcom/fasterxml/jackson/core/SerializableString;)Z");
+      r"nextFieldName", r"(Lcom/fasterxml/jackson/core/SerializableString;)Z");
 
   /// from: public boolean nextFieldName(com.fasterxml.jackson.core.SerializableString str)
   ///
@@ -793,7 +794,7 @@
       [str.reference]).boolean;
 
   static final _id_nextFieldName1 = jniAccessors.getMethodIDOf(
-      _classRef, "nextFieldName", "()Ljava/lang/String;");
+      _classRef, r"nextFieldName", r"()Ljava/lang/String;");
 
   /// from: public java.lang.String nextFieldName()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -811,7 +812,7 @@
           _id_nextFieldName1, jni.JniCallType.objectType, []).object);
 
   static final _id_nextTextValue = jniAccessors.getMethodIDOf(
-      _classRef, "nextTextValue", "()Ljava/lang/String;");
+      _classRef, r"nextTextValue", r"()Ljava/lang/String;");
 
   /// from: public java.lang.String nextTextValue()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -834,7 +835,7 @@
           reference, _id_nextTextValue, jni.JniCallType.objectType, []).object);
 
   static final _id_nextIntValue =
-      jniAccessors.getMethodIDOf(_classRef, "nextIntValue", "(I)I");
+      jniAccessors.getMethodIDOf(_classRef, r"nextIntValue", r"(I)I");
 
   /// from: public int nextIntValue(int defaultValue)
   ///
@@ -862,7 +863,7 @@
       [defaultValue]).integer;
 
   static final _id_nextLongValue =
-      jniAccessors.getMethodIDOf(_classRef, "nextLongValue", "(J)J");
+      jniAccessors.getMethodIDOf(_classRef, r"nextLongValue", r"(J)J");
 
   /// from: public long nextLongValue(long defaultValue)
   ///
@@ -890,7 +891,7 @@
       [defaultValue]).long;
 
   static final _id_nextBooleanValue = jniAccessors.getMethodIDOf(
-      _classRef, "nextBooleanValue", "()Ljava/lang/Boolean;");
+      _classRef, r"nextBooleanValue", r"()Ljava/lang/Boolean;");
 
   /// from: public java.lang.Boolean nextBooleanValue()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -916,7 +917,7 @@
           _id_nextBooleanValue, jni.JniCallType.objectType, []).object);
 
   static final _id_skipChildren = jniAccessors.getMethodIDOf(
-      _classRef, "skipChildren", "()Lcom/fasterxml/jackson/core/JsonParser;");
+      _classRef, r"skipChildren", r"()Lcom/fasterxml/jackson/core/JsonParser;");
 
   /// from: public abstract com.fasterxml.jackson.core.JsonParser skipChildren()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -941,7 +942,7 @@
           reference, _id_skipChildren, jni.JniCallType.objectType, []).object);
 
   static final _id_finishToken =
-      jniAccessors.getMethodIDOf(_classRef, "finishToken", "()V");
+      jniAccessors.getMethodIDOf(_classRef, r"finishToken", r"()V");
 
   /// from: public void finishToken()
   ///
@@ -962,7 +963,7 @@
       reference, _id_finishToken, jni.JniCallType.voidType, []).check();
 
   static final _id_currentToken = jniAccessors.getMethodIDOf(
-      _classRef, "currentToken", "()Lcom/fasterxml/jackson/core/JsonToken;");
+      _classRef, r"currentToken", r"()Lcom/fasterxml/jackson/core/JsonToken;");
 
   /// from: public com.fasterxml.jackson.core.JsonToken currentToken()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -981,7 +982,7 @@
           reference, _id_currentToken, jni.JniCallType.objectType, []).object);
 
   static final _id_currentTokenId =
-      jniAccessors.getMethodIDOf(_classRef, "currentTokenId", "()I");
+      jniAccessors.getMethodIDOf(_classRef, r"currentTokenId", r"()I");
 
   /// from: public int currentTokenId()
   ///
@@ -997,8 +998,8 @@
   int currentTokenId() => jniAccessors.callMethodWithArgs(
       reference, _id_currentTokenId, jni.JniCallType.intType, []).integer;
 
-  static final _id_getCurrentToken = jniAccessors.getMethodIDOf(
-      _classRef, "getCurrentToken", "()Lcom/fasterxml/jackson/core/JsonToken;");
+  static final _id_getCurrentToken = jniAccessors.getMethodIDOf(_classRef,
+      r"getCurrentToken", r"()Lcom/fasterxml/jackson/core/JsonToken;");
 
   /// from: public abstract com.fasterxml.jackson.core.JsonToken getCurrentToken()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -1014,7 +1015,7 @@
           jni.JniCallType.objectType, []).object);
 
   static final _id_getCurrentTokenId =
-      jniAccessors.getMethodIDOf(_classRef, "getCurrentTokenId", "()I");
+      jniAccessors.getMethodIDOf(_classRef, r"getCurrentTokenId", r"()I");
 
   /// from: public abstract int getCurrentTokenId()
   ///
@@ -1025,7 +1026,7 @@
       reference, _id_getCurrentTokenId, jni.JniCallType.intType, []).integer;
 
   static final _id_hasCurrentToken =
-      jniAccessors.getMethodIDOf(_classRef, "hasCurrentToken", "()Z");
+      jniAccessors.getMethodIDOf(_classRef, r"hasCurrentToken", r"()Z");
 
   /// from: public abstract boolean hasCurrentToken()
   ///
@@ -1041,7 +1042,7 @@
       reference, _id_hasCurrentToken, jni.JniCallType.booleanType, []).boolean;
 
   static final _id_hasTokenId =
-      jniAccessors.getMethodIDOf(_classRef, "hasTokenId", "(I)Z");
+      jniAccessors.getMethodIDOf(_classRef, r"hasTokenId", r"(I)Z");
 
   /// from: public abstract boolean hasTokenId(int id)
   ///
@@ -1061,7 +1062,7 @@
       reference, _id_hasTokenId, jni.JniCallType.booleanType, [id]).boolean;
 
   static final _id_hasToken = jniAccessors.getMethodIDOf(
-      _classRef, "hasToken", "(Lcom/fasterxml/jackson/core/JsonToken;)Z");
+      _classRef, r"hasToken", r"(Lcom/fasterxml/jackson/core/JsonToken;)Z");
 
   /// from: public abstract boolean hasToken(com.fasterxml.jackson.core.JsonToken t)
   ///
@@ -1083,8 +1084,8 @@
       jni.JniCallType.booleanType,
       [t.reference]).boolean;
 
-  static final _id_isExpectedStartArrayToken =
-      jniAccessors.getMethodIDOf(_classRef, "isExpectedStartArrayToken", "()Z");
+  static final _id_isExpectedStartArrayToken = jniAccessors.getMethodIDOf(
+      _classRef, r"isExpectedStartArrayToken", r"()Z");
 
   /// from: public boolean isExpectedStartArrayToken()
   ///
@@ -1108,7 +1109,7 @@
       _id_isExpectedStartArrayToken, jni.JniCallType.booleanType, []).boolean;
 
   static final _id_isExpectedStartObjectToken = jniAccessors.getMethodIDOf(
-      _classRef, "isExpectedStartObjectToken", "()Z");
+      _classRef, r"isExpectedStartObjectToken", r"()Z");
 
   /// from: public boolean isExpectedStartObjectToken()
   ///
@@ -1123,8 +1124,8 @@
       _id_isExpectedStartObjectToken,
       jni.JniCallType.booleanType, []).boolean;
 
-  static final _id_isExpectedNumberIntToken =
-      jniAccessors.getMethodIDOf(_classRef, "isExpectedNumberIntToken", "()Z");
+  static final _id_isExpectedNumberIntToken = jniAccessors.getMethodIDOf(
+      _classRef, r"isExpectedNumberIntToken", r"()Z");
 
   /// from: public boolean isExpectedNumberIntToken()
   ///
@@ -1141,7 +1142,7 @@
       _id_isExpectedNumberIntToken, jni.JniCallType.booleanType, []).boolean;
 
   static final _id_isNaN =
-      jniAccessors.getMethodIDOf(_classRef, "isNaN", "()Z");
+      jniAccessors.getMethodIDOf(_classRef, r"isNaN", r"()Z");
 
   /// from: public boolean isNaN()
   ///
@@ -1161,7 +1162,7 @@
       reference, _id_isNaN, jni.JniCallType.booleanType, []).boolean;
 
   static final _id_clearCurrentToken =
-      jniAccessors.getMethodIDOf(_classRef, "clearCurrentToken", "()V");
+      jniAccessors.getMethodIDOf(_classRef, r"clearCurrentToken", r"()V");
 
   /// from: public abstract void clearCurrentToken()
   ///
@@ -1179,7 +1180,7 @@
       reference, _id_clearCurrentToken, jni.JniCallType.voidType, []).check();
 
   static final _id_getLastClearedToken = jniAccessors.getMethodIDOf(_classRef,
-      "getLastClearedToken", "()Lcom/fasterxml/jackson/core/JsonToken;");
+      r"getLastClearedToken", r"()Lcom/fasterxml/jackson/core/JsonToken;");
 
   /// from: public abstract com.fasterxml.jackson.core.JsonToken getLastClearedToken()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -1197,7 +1198,7 @@
           jni.JniCallType.objectType, []).object);
 
   static final _id_overrideCurrentName = jniAccessors.getMethodIDOf(
-      _classRef, "overrideCurrentName", "(Ljava/lang/String;)V");
+      _classRef, r"overrideCurrentName", r"(Ljava/lang/String;)V");
 
   /// from: public abstract void overrideCurrentName(java.lang.String name)
   ///
@@ -1216,7 +1217,7 @@
       [name.reference]).check();
 
   static final _id_getCurrentName = jniAccessors.getMethodIDOf(
-      _classRef, "getCurrentName", "()Ljava/lang/String;");
+      _classRef, r"getCurrentName", r"()Ljava/lang/String;");
 
   /// from: public abstract java.lang.String getCurrentName()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -1230,7 +1231,7 @@
           _id_getCurrentName, jni.JniCallType.objectType, []).object);
 
   static final _id_currentName = jniAccessors.getMethodIDOf(
-      _classRef, "currentName", "()Ljava/lang/String;");
+      _classRef, r"currentName", r"()Ljava/lang/String;");
 
   /// from: public java.lang.String currentName()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -1248,8 +1249,8 @@
       const jni.JStringType().fromRef(jniAccessors.callMethodWithArgs(
           reference, _id_currentName, jni.JniCallType.objectType, []).object);
 
-  static final _id_getText =
-      jniAccessors.getMethodIDOf(_classRef, "getText", "()Ljava/lang/String;");
+  static final _id_getText = jniAccessors.getMethodIDOf(
+      _classRef, r"getText", r"()Ljava/lang/String;");
 
   /// from: public abstract java.lang.String getText()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -1267,7 +1268,7 @@
           reference, _id_getText, jni.JniCallType.objectType, []).object);
 
   static final _id_getText1 =
-      jniAccessors.getMethodIDOf(_classRef, "getText", "(Ljava/io/Writer;)I");
+      jniAccessors.getMethodIDOf(_classRef, r"getText", r"(Ljava/io/Writer;)I");
 
   /// from: public int getText(java.io.Writer writer)
   ///
@@ -1290,7 +1291,7 @@
       _id_getText1, jni.JniCallType.intType, [writer.reference]).integer;
 
   static final _id_getTextCharacters =
-      jniAccessors.getMethodIDOf(_classRef, "getTextCharacters", "()[C");
+      jniAccessors.getMethodIDOf(_classRef, r"getTextCharacters", r"()[C");
 
   /// from: public abstract char[] getTextCharacters()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -1328,7 +1329,7 @@
               jni.JniCallType.objectType, []).object);
 
   static final _id_getTextLength =
-      jniAccessors.getMethodIDOf(_classRef, "getTextLength", "()I");
+      jniAccessors.getMethodIDOf(_classRef, r"getTextLength", r"()I");
 
   /// from: public abstract int getTextLength()
   ///
@@ -1343,7 +1344,7 @@
       reference, _id_getTextLength, jni.JniCallType.intType, []).integer;
 
   static final _id_getTextOffset =
-      jniAccessors.getMethodIDOf(_classRef, "getTextOffset", "()I");
+      jniAccessors.getMethodIDOf(_classRef, r"getTextOffset", r"()I");
 
   /// from: public abstract int getTextOffset()
   ///
@@ -1358,7 +1359,7 @@
       reference, _id_getTextOffset, jni.JniCallType.intType, []).integer;
 
   static final _id_hasTextCharacters =
-      jniAccessors.getMethodIDOf(_classRef, "hasTextCharacters", "()Z");
+      jniAccessors.getMethodIDOf(_classRef, r"hasTextCharacters", r"()Z");
 
   /// from: public abstract boolean hasTextCharacters()
   ///
@@ -1380,7 +1381,7 @@
       _id_hasTextCharacters, jni.JniCallType.booleanType, []).boolean;
 
   static final _id_getNumberValue = jniAccessors.getMethodIDOf(
-      _classRef, "getNumberValue", "()Ljava/lang/Number;");
+      _classRef, r"getNumberValue", r"()Ljava/lang/Number;");
 
   /// from: public abstract java.lang.Number getNumberValue()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -1400,7 +1401,7 @@
           _id_getNumberValue, jni.JniCallType.objectType, []).object);
 
   static final _id_getNumberValueExact = jniAccessors.getMethodIDOf(
-      _classRef, "getNumberValueExact", "()Ljava/lang/Number;");
+      _classRef, r"getNumberValueExact", r"()Ljava/lang/Number;");
 
   /// from: public java.lang.Number getNumberValueExact()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -1423,8 +1424,10 @@
       const jni.JObjectType().fromRef(jniAccessors.callMethodWithArgs(reference,
           _id_getNumberValueExact, jni.JniCallType.objectType, []).object);
 
-  static final _id_getNumberType = jniAccessors.getMethodIDOf(_classRef,
-      "getNumberType", "()Lcom/fasterxml/jackson/core/JsonParser\$NumberType;");
+  static final _id_getNumberType = jniAccessors.getMethodIDOf(
+      _classRef,
+      r"getNumberType",
+      r"()Lcom/fasterxml/jackson/core/JsonParser$NumberType;");
 
   /// from: public abstract com.fasterxml.jackson.core.JsonParser.NumberType getNumberType()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -1441,7 +1444,7 @@
           reference, _id_getNumberType, jni.JniCallType.objectType, []).object);
 
   static final _id_getByteValue =
-      jniAccessors.getMethodIDOf(_classRef, "getByteValue", "()B");
+      jniAccessors.getMethodIDOf(_classRef, r"getByteValue", r"()B");
 
   /// from: public byte getByteValue()
   ///
@@ -1470,7 +1473,7 @@
       reference, _id_getByteValue, jni.JniCallType.byteType, []).byte;
 
   static final _id_getShortValue =
-      jniAccessors.getMethodIDOf(_classRef, "getShortValue", "()S");
+      jniAccessors.getMethodIDOf(_classRef, r"getShortValue", r"()S");
 
   /// from: public short getShortValue()
   ///
@@ -1493,7 +1496,7 @@
       reference, _id_getShortValue, jni.JniCallType.shortType, []).short;
 
   static final _id_getIntValue =
-      jniAccessors.getMethodIDOf(_classRef, "getIntValue", "()I");
+      jniAccessors.getMethodIDOf(_classRef, r"getIntValue", r"()I");
 
   /// from: public abstract int getIntValue()
   ///
@@ -1516,7 +1519,7 @@
       reference, _id_getIntValue, jni.JniCallType.intType, []).integer;
 
   static final _id_getLongValue =
-      jniAccessors.getMethodIDOf(_classRef, "getLongValue", "()J");
+      jniAccessors.getMethodIDOf(_classRef, r"getLongValue", r"()J");
 
   /// from: public abstract long getLongValue()
   ///
@@ -1539,7 +1542,7 @@
       reference, _id_getLongValue, jni.JniCallType.longType, []).long;
 
   static final _id_getBigIntegerValue = jniAccessors.getMethodIDOf(
-      _classRef, "getBigIntegerValue", "()Ljava/math/BigInteger;");
+      _classRef, r"getBigIntegerValue", r"()Ljava/math/BigInteger;");
 
   /// from: public abstract java.math.BigInteger getBigIntegerValue()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -1560,7 +1563,7 @@
           _id_getBigIntegerValue, jni.JniCallType.objectType, []).object);
 
   static final _id_getFloatValue =
-      jniAccessors.getMethodIDOf(_classRef, "getFloatValue", "()F");
+      jniAccessors.getMethodIDOf(_classRef, r"getFloatValue", r"()F");
 
   /// from: public abstract float getFloatValue()
   ///
@@ -1583,7 +1586,7 @@
       reference, _id_getFloatValue, jni.JniCallType.floatType, []).float;
 
   static final _id_getDoubleValue =
-      jniAccessors.getMethodIDOf(_classRef, "getDoubleValue", "()D");
+      jniAccessors.getMethodIDOf(_classRef, r"getDoubleValue", r"()D");
 
   /// from: public abstract double getDoubleValue()
   ///
@@ -1606,7 +1609,7 @@
       _id_getDoubleValue, jni.JniCallType.doubleType, []).doubleFloat;
 
   static final _id_getDecimalValue = jniAccessors.getMethodIDOf(
-      _classRef, "getDecimalValue", "()Ljava/math/BigDecimal;");
+      _classRef, r"getDecimalValue", r"()Ljava/math/BigDecimal;");
 
   /// from: public abstract java.math.BigDecimal getDecimalValue()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -1624,7 +1627,7 @@
           _id_getDecimalValue, jni.JniCallType.objectType, []).object);
 
   static final _id_getBooleanValue =
-      jniAccessors.getMethodIDOf(_classRef, "getBooleanValue", "()Z");
+      jniAccessors.getMethodIDOf(_classRef, r"getBooleanValue", r"()Z");
 
   /// from: public boolean getBooleanValue()
   ///
@@ -1643,7 +1646,7 @@
       reference, _id_getBooleanValue, jni.JniCallType.booleanType, []).boolean;
 
   static final _id_getEmbeddedObject = jniAccessors.getMethodIDOf(
-      _classRef, "getEmbeddedObject", "()Ljava/lang/Object;");
+      _classRef, r"getEmbeddedObject", r"()Ljava/lang/Object;");
 
   /// from: public java.lang.Object getEmbeddedObject()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -1667,7 +1670,7 @@
           _id_getEmbeddedObject, jni.JniCallType.objectType, []).object);
 
   static final _id_getBinaryValue = jniAccessors.getMethodIDOf(_classRef,
-      "getBinaryValue", "(Lcom/fasterxml/jackson/core/Base64Variant;)[B");
+      r"getBinaryValue", r"(Lcom/fasterxml/jackson/core/Base64Variant;)[B");
 
   /// from: public abstract byte[] getBinaryValue(com.fasterxml.jackson.core.Base64Variant bv)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -1698,7 +1701,7 @@
               jni.JniCallType.objectType, [bv.reference]).object);
 
   static final _id_getBinaryValue1 =
-      jniAccessors.getMethodIDOf(_classRef, "getBinaryValue", "()[B");
+      jniAccessors.getMethodIDOf(_classRef, r"getBinaryValue", r"()[B");
 
   /// from: public byte[] getBinaryValue()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -1715,7 +1718,7 @@
               jni.JniCallType.objectType, []).object);
 
   static final _id_readBinaryValue = jniAccessors.getMethodIDOf(
-      _classRef, "readBinaryValue", "(Ljava/io/OutputStream;)I");
+      _classRef, r"readBinaryValue", r"(Ljava/io/OutputStream;)I");
 
   /// from: public int readBinaryValue(java.io.OutputStream out)
   ///
@@ -1738,8 +1741,8 @@
 
   static final _id_readBinaryValue1 = jniAccessors.getMethodIDOf(
       _classRef,
-      "readBinaryValue",
-      "(Lcom/fasterxml/jackson/core/Base64Variant;Ljava/io/OutputStream;)I");
+      r"readBinaryValue",
+      r"(Lcom/fasterxml/jackson/core/Base64Variant;Ljava/io/OutputStream;)I");
 
   /// from: public int readBinaryValue(com.fasterxml.jackson.core.Base64Variant bv, java.io.OutputStream out)
   ///
@@ -1756,7 +1759,7 @@
           jni.JniCallType.intType, [bv.reference, out.reference]).integer;
 
   static final _id_getValueAsInt =
-      jniAccessors.getMethodIDOf(_classRef, "getValueAsInt", "()I");
+      jniAccessors.getMethodIDOf(_classRef, r"getValueAsInt", r"()I");
 
   /// from: public int getValueAsInt()
   ///
@@ -1777,7 +1780,7 @@
       reference, _id_getValueAsInt, jni.JniCallType.intType, []).integer;
 
   static final _id_getValueAsInt1 =
-      jniAccessors.getMethodIDOf(_classRef, "getValueAsInt", "(I)I");
+      jniAccessors.getMethodIDOf(_classRef, r"getValueAsInt", r"(I)I");
 
   /// from: public int getValueAsInt(int def)
   ///
@@ -1798,7 +1801,7 @@
       reference, _id_getValueAsInt1, jni.JniCallType.intType, [def]).integer;
 
   static final _id_getValueAsLong =
-      jniAccessors.getMethodIDOf(_classRef, "getValueAsLong", "()J");
+      jniAccessors.getMethodIDOf(_classRef, r"getValueAsLong", r"()J");
 
   /// from: public long getValueAsLong()
   ///
@@ -1819,7 +1822,7 @@
       reference, _id_getValueAsLong, jni.JniCallType.longType, []).long;
 
   static final _id_getValueAsLong1 =
-      jniAccessors.getMethodIDOf(_classRef, "getValueAsLong", "(J)J");
+      jniAccessors.getMethodIDOf(_classRef, r"getValueAsLong", r"(J)J");
 
   /// from: public long getValueAsLong(long def)
   ///
@@ -1840,7 +1843,7 @@
       reference, _id_getValueAsLong1, jni.JniCallType.longType, [def]).long;
 
   static final _id_getValueAsDouble =
-      jniAccessors.getMethodIDOf(_classRef, "getValueAsDouble", "()D");
+      jniAccessors.getMethodIDOf(_classRef, r"getValueAsDouble", r"()D");
 
   /// from: public double getValueAsDouble()
   ///
@@ -1861,7 +1864,7 @@
       _id_getValueAsDouble, jni.JniCallType.doubleType, []).doubleFloat;
 
   static final _id_getValueAsDouble1 =
-      jniAccessors.getMethodIDOf(_classRef, "getValueAsDouble", "(D)D");
+      jniAccessors.getMethodIDOf(_classRef, r"getValueAsDouble", r"(D)D");
 
   /// from: public double getValueAsDouble(double def)
   ///
@@ -1885,7 +1888,7 @@
       [def]).doubleFloat;
 
   static final _id_getValueAsBoolean =
-      jniAccessors.getMethodIDOf(_classRef, "getValueAsBoolean", "()Z");
+      jniAccessors.getMethodIDOf(_classRef, r"getValueAsBoolean", r"()Z");
 
   /// from: public boolean getValueAsBoolean()
   ///
@@ -1906,7 +1909,7 @@
       _id_getValueAsBoolean, jni.JniCallType.booleanType, []).boolean;
 
   static final _id_getValueAsBoolean1 =
-      jniAccessors.getMethodIDOf(_classRef, "getValueAsBoolean", "(Z)Z");
+      jniAccessors.getMethodIDOf(_classRef, r"getValueAsBoolean", r"(Z)Z");
 
   /// from: public boolean getValueAsBoolean(boolean def)
   ///
@@ -1927,10 +1930,10 @@
       reference,
       _id_getValueAsBoolean1,
       jni.JniCallType.booleanType,
-      [def]).boolean;
+      [def ? 1 : 0]).boolean;
 
   static final _id_getValueAsString = jniAccessors.getMethodIDOf(
-      _classRef, "getValueAsString", "()Ljava/lang/String;");
+      _classRef, r"getValueAsString", r"()Ljava/lang/String;");
 
   /// from: public java.lang.String getValueAsString()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -1950,8 +1953,8 @@
       const jni.JStringType().fromRef(jniAccessors.callMethodWithArgs(reference,
           _id_getValueAsString, jni.JniCallType.objectType, []).object);
 
-  static final _id_getValueAsString1 = jniAccessors.getMethodIDOf(
-      _classRef, "getValueAsString", "(Ljava/lang/String;)Ljava/lang/String;");
+  static final _id_getValueAsString1 = jniAccessors.getMethodIDOf(_classRef,
+      r"getValueAsString", r"(Ljava/lang/String;)Ljava/lang/String;");
 
   /// from: public abstract java.lang.String getValueAsString(java.lang.String def)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -1976,7 +1979,7 @@
           [def.reference]).object);
 
   static final _id_canReadObjectId =
-      jniAccessors.getMethodIDOf(_classRef, "canReadObjectId", "()Z");
+      jniAccessors.getMethodIDOf(_classRef, r"canReadObjectId", r"()Z");
 
   /// from: public boolean canReadObjectId()
   ///
@@ -1995,7 +1998,7 @@
       reference, _id_canReadObjectId, jni.JniCallType.booleanType, []).boolean;
 
   static final _id_canReadTypeId =
-      jniAccessors.getMethodIDOf(_classRef, "canReadTypeId", "()Z");
+      jniAccessors.getMethodIDOf(_classRef, r"canReadTypeId", r"()Z");
 
   /// from: public boolean canReadTypeId()
   ///
@@ -2014,7 +2017,7 @@
       reference, _id_canReadTypeId, jni.JniCallType.booleanType, []).boolean;
 
   static final _id_getObjectId = jniAccessors.getMethodIDOf(
-      _classRef, "getObjectId", "()Ljava/lang/Object;");
+      _classRef, r"getObjectId", r"()Ljava/lang/Object;");
 
   /// from: public java.lang.Object getObjectId()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -2037,7 +2040,7 @@
           reference, _id_getObjectId, jni.JniCallType.objectType, []).object);
 
   static final _id_getTypeId = jniAccessors.getMethodIDOf(
-      _classRef, "getTypeId", "()Ljava/lang/Object;");
+      _classRef, r"getTypeId", r"()Ljava/lang/Object;");
 
   /// from: public java.lang.Object getTypeId()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -2060,7 +2063,7 @@
           reference, _id_getTypeId, jni.JniCallType.objectType, []).object);
 
   static final _id_readValueAs = jniAccessors.getMethodIDOf(
-      _classRef, "readValueAs", "(Ljava/lang/Class;)Ljava/lang/Object;");
+      _classRef, r"readValueAs", r"(Ljava/lang/Class;)Ljava/lang/Object;");
 
   /// from: public T readValueAs(java.lang.Class<T> valueType)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -2097,8 +2100,8 @@
 
   static final _id_readValueAs1 = jniAccessors.getMethodIDOf(
       _classRef,
-      "readValueAs",
-      "(Lcom/fasterxml/jackson/core/type/TypeReference;)Ljava/lang/Object;");
+      r"readValueAs",
+      r"(Lcom/fasterxml/jackson/core/type/TypeReference;)Ljava/lang/Object;");
 
   /// from: public T readValueAs(com.fasterxml.jackson.core.type.TypeReference<?> valueTypeRef)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -2131,7 +2134,7 @@
           jni.JniCallType.objectType, [valueTypeRef.reference]).object);
 
   static final _id_readValuesAs = jniAccessors.getMethodIDOf(
-      _classRef, "readValuesAs", "(Ljava/lang/Class;)Ljava/util/Iterator;");
+      _classRef, r"readValuesAs", r"(Ljava/lang/Class;)Ljava/util/Iterator;");
 
   /// from: public java.util.Iterator<T> readValuesAs(java.lang.Class<T> valueType)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -2154,8 +2157,8 @@
 
   static final _id_readValuesAs1 = jniAccessors.getMethodIDOf(
       _classRef,
-      "readValuesAs",
-      "(Lcom/fasterxml/jackson/core/type/TypeReference;)Ljava/util/Iterator;");
+      r"readValuesAs",
+      r"(Lcom/fasterxml/jackson/core/type/TypeReference;)Ljava/util/Iterator;");
 
   /// from: public java.util.Iterator<T> readValuesAs(com.fasterxml.jackson.core.type.TypeReference<T> valueTypeRef)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -2177,7 +2180,7 @@
           [valueTypeRef.reference]).object);
 
   static final _id_readValueAsTree = jniAccessors.getMethodIDOf(
-      _classRef, "readValueAsTree", "()Ljava/lang/Object;");
+      _classRef, r"readValueAsTree", r"()Ljava/lang/Object;");
 
   /// from: public T readValueAsTree()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -2230,13 +2233,12 @@
   ) : super.fromRef(ref);
 
   static final _classRef =
-      jniAccessors.getClassOf("com/fasterxml/jackson/core/JsonParser\$Feature");
+      jniAccessors.getClassOf(r"com/fasterxml/jackson/core/JsonParser$Feature");
 
   /// The type which includes information such as the signature of this class.
   static const type = $JsonParser_FeatureType();
-
   static final _id_values = jniAccessors.getStaticMethodIDOf(_classRef,
-      "values", "()[Lcom/fasterxml/jackson/core/JsonParser\$Feature;");
+      r"values", r"()[Lcom/fasterxml/jackson/core/JsonParser$Feature;");
 
   /// from: static public com.fasterxml.jackson.core.JsonParser.Feature[] values()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -2247,8 +2249,8 @@
 
   static final _id_valueOf = jniAccessors.getStaticMethodIDOf(
       _classRef,
-      "valueOf",
-      "(Ljava/lang/String;)Lcom/fasterxml/jackson/core/JsonParser\$Feature;");
+      r"valueOf",
+      r"(Ljava/lang/String;)Lcom/fasterxml/jackson/core/JsonParser$Feature;");
 
   /// from: static public com.fasterxml.jackson.core.JsonParser.Feature valueOf(java.lang.String name)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -2258,7 +2260,7 @@
               jni.JniCallType.objectType, [name.reference]).object);
 
   static final _id_collectDefaults =
-      jniAccessors.getStaticMethodIDOf(_classRef, "collectDefaults", "()I");
+      jniAccessors.getStaticMethodIDOf(_classRef, r"collectDefaults", r"()I");
 
   /// from: static public int collectDefaults()
   ///
@@ -2269,21 +2271,21 @@
       _classRef, _id_collectDefaults, jni.JniCallType.intType, []).integer;
 
   static final _id_enabledByDefault =
-      jniAccessors.getMethodIDOf(_classRef, "enabledByDefault", "()Z");
+      jniAccessors.getMethodIDOf(_classRef, r"enabledByDefault", r"()Z");
 
   /// from: public boolean enabledByDefault()
   bool enabledByDefault() => jniAccessors.callMethodWithArgs(
       reference, _id_enabledByDefault, jni.JniCallType.booleanType, []).boolean;
 
   static final _id_enabledIn =
-      jniAccessors.getMethodIDOf(_classRef, "enabledIn", "(I)Z");
+      jniAccessors.getMethodIDOf(_classRef, r"enabledIn", r"(I)Z");
 
   /// from: public boolean enabledIn(int flags)
   bool enabledIn(int flags) => jniAccessors.callMethodWithArgs(
       reference, _id_enabledIn, jni.JniCallType.booleanType, [flags]).boolean;
 
   static final _id_getMask =
-      jniAccessors.getMethodIDOf(_classRef, "getMask", "()I");
+      jniAccessors.getMethodIDOf(_classRef, r"getMask", r"()I");
 
   /// from: public int getMask()
   int getMask() => jniAccessors.callMethodWithArgs(
@@ -2326,13 +2328,12 @@
   ) : super.fromRef(ref);
 
   static final _classRef = jniAccessors
-      .getClassOf("com/fasterxml/jackson/core/JsonParser\$NumberType");
+      .getClassOf(r"com/fasterxml/jackson/core/JsonParser$NumberType");
 
   /// The type which includes information such as the signature of this class.
   static const type = $JsonParser_NumberTypeType();
-
   static final _id_values = jniAccessors.getStaticMethodIDOf(_classRef,
-      "values", "()[Lcom/fasterxml/jackson/core/JsonParser\$NumberType;");
+      r"values", r"()[Lcom/fasterxml/jackson/core/JsonParser$NumberType;");
 
   /// from: static public com.fasterxml.jackson.core.JsonParser.NumberType[] values()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -2343,8 +2344,8 @@
 
   static final _id_valueOf = jniAccessors.getStaticMethodIDOf(
       _classRef,
-      "valueOf",
-      "(Ljava/lang/String;)Lcom/fasterxml/jackson/core/JsonParser\$NumberType;");
+      r"valueOf",
+      r"(Ljava/lang/String;)Lcom/fasterxml/jackson/core/JsonParser$NumberType;");
 
   /// from: static public com.fasterxml.jackson.core.JsonParser.NumberType valueOf(java.lang.String name)
   /// The returned object must be deleted after use, by calling the `delete` method.
diff --git a/pkgs/jnigen/test/jackson_core_test/third_party/lib/com/fasterxml/jackson/core/JsonToken.dart b/pkgs/jnigen/test/jackson_core_test/third_party/lib/com/fasterxml/jackson/core/JsonToken.dart
index 8c3bc75..84d23d7 100644
--- a/pkgs/jnigen/test/jackson_core_test/third_party/lib/com/fasterxml/jackson/core/JsonToken.dart
+++ b/pkgs/jnigen/test/jackson_core_test/third_party/lib/com/fasterxml/jackson/core/JsonToken.dart
@@ -32,12 +32,11 @@
 // ignore_for_file: unused_shown_name
 
 import "dart:isolate" show ReceivePort;
-
+import "dart:ffi" as ffi;
+import "package:jni/internal_helpers_for_jnigen.dart";
 import "package:jni/jni.dart" as jni;
 
-import "package:jni/internal_helpers_for_jnigen.dart";
-
-import "../../../../_init.dart" show jniEnv, jniAccessors;
+import "../../../../_init.dart";
 
 /// from: com.fasterxml.jackson.core.JsonToken
 ///
@@ -53,13 +52,12 @@
   ) : super.fromRef(ref);
 
   static final _classRef =
-      jniAccessors.getClassOf("com/fasterxml/jackson/core/JsonToken");
+      jniAccessors.getClassOf(r"com/fasterxml/jackson/core/JsonToken");
 
   /// The type which includes information such as the signature of this class.
   static const type = $JsonTokenType();
-
   static final _id_values = jniAccessors.getStaticMethodIDOf(
-      _classRef, "values", "()[Lcom/fasterxml/jackson/core/JsonToken;");
+      _classRef, r"values", r"()[Lcom/fasterxml/jackson/core/JsonToken;");
 
   /// from: static public com.fasterxml.jackson.core.JsonToken[] values()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -68,8 +66,10 @@
           .callStaticMethodWithArgs(
               _classRef, _id_values, jni.JniCallType.objectType, []).object);
 
-  static final _id_valueOf = jniAccessors.getStaticMethodIDOf(_classRef,
-      "valueOf", "(Ljava/lang/String;)Lcom/fasterxml/jackson/core/JsonToken;");
+  static final _id_valueOf = jniAccessors.getStaticMethodIDOf(
+      _classRef,
+      r"valueOf",
+      r"(Ljava/lang/String;)Lcom/fasterxml/jackson/core/JsonToken;");
 
   /// from: static public com.fasterxml.jackson.core.JsonToken valueOf(java.lang.String name)
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -80,14 +80,14 @@
           jni.JniCallType.objectType,
           [name.reference]).object);
 
-  static final _id_id = jniAccessors.getMethodIDOf(_classRef, "id", "()I");
+  static final _id_id = jniAccessors.getMethodIDOf(_classRef, r"id", r"()I");
 
   /// from: public final int id()
   int id() => jniAccessors.callMethodWithArgs(
       reference, _id_id, jni.JniCallType.intType, []).integer;
 
-  static final _id_asString =
-      jniAccessors.getMethodIDOf(_classRef, "asString", "()Ljava/lang/String;");
+  static final _id_asString = jniAccessors.getMethodIDOf(
+      _classRef, r"asString", r"()Ljava/lang/String;");
 
   /// from: public final java.lang.String asString()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -96,7 +96,7 @@
           reference, _id_asString, jni.JniCallType.objectType, []).object);
 
   static final _id_asCharArray =
-      jniAccessors.getMethodIDOf(_classRef, "asCharArray", "()[C");
+      jniAccessors.getMethodIDOf(_classRef, r"asCharArray", r"()[C");
 
   /// from: public final char[] asCharArray()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -105,7 +105,7 @@
           reference, _id_asCharArray, jni.JniCallType.objectType, []).object);
 
   static final _id_asByteArray =
-      jniAccessors.getMethodIDOf(_classRef, "asByteArray", "()[B");
+      jniAccessors.getMethodIDOf(_classRef, r"asByteArray", r"()[B");
 
   /// from: public final byte[] asByteArray()
   /// The returned object must be deleted after use, by calling the `delete` method.
@@ -114,7 +114,7 @@
           reference, _id_asByteArray, jni.JniCallType.objectType, []).object);
 
   static final _id_isNumeric =
-      jniAccessors.getMethodIDOf(_classRef, "isNumeric", "()Z");
+      jniAccessors.getMethodIDOf(_classRef, r"isNumeric", r"()Z");
 
   /// from: public final boolean isNumeric()
   ///
@@ -124,7 +124,7 @@
       reference, _id_isNumeric, jni.JniCallType.booleanType, []).boolean;
 
   static final _id_isStructStart =
-      jniAccessors.getMethodIDOf(_classRef, "isStructStart", "()Z");
+      jniAccessors.getMethodIDOf(_classRef, r"isStructStart", r"()Z");
 
   /// from: public final boolean isStructStart()
   ///
@@ -139,7 +139,7 @@
       reference, _id_isStructStart, jni.JniCallType.booleanType, []).boolean;
 
   static final _id_isStructEnd =
-      jniAccessors.getMethodIDOf(_classRef, "isStructEnd", "()Z");
+      jniAccessors.getMethodIDOf(_classRef, r"isStructEnd", r"()Z");
 
   /// from: public final boolean isStructEnd()
   ///
@@ -154,7 +154,7 @@
       reference, _id_isStructEnd, jni.JniCallType.booleanType, []).boolean;
 
   static final _id_isScalarValue =
-      jniAccessors.getMethodIDOf(_classRef, "isScalarValue", "()Z");
+      jniAccessors.getMethodIDOf(_classRef, r"isScalarValue", r"()Z");
 
   /// from: public final boolean isScalarValue()
   ///
@@ -168,7 +168,7 @@
       reference, _id_isScalarValue, jni.JniCallType.booleanType, []).boolean;
 
   static final _id_isBoolean =
-      jniAccessors.getMethodIDOf(_classRef, "isBoolean", "()Z");
+      jniAccessors.getMethodIDOf(_classRef, r"isBoolean", r"()Z");
 
   /// from: public final boolean isBoolean()
   ///
diff --git a/pkgs/jnigen/test/kotlin_test/lib/kotlin.dart b/pkgs/jnigen/test/kotlin_test/lib/kotlin.dart
index aaf5b7e..7546ded 100644
--- a/pkgs/jnigen/test/kotlin_test/lib/kotlin.dart
+++ b/pkgs/jnigen/test/kotlin_test/lib/kotlin.dart
@@ -14,7 +14,9 @@
 // ignore_for_file: overridden_fields
 // ignore_for_file: unnecessary_cast
 // ignore_for_file: unused_element
+// ignore_for_file: unused_field
 // ignore_for_file: unused_import
+// ignore_for_file: unused_shown_name
 
 import "dart:isolate" show ReceivePort;
 import "dart:ffi" as ffi;
@@ -38,12 +40,12 @@
 
   /// The type which includes information such as the signature of this class.
   static const type = $SuspendFunType();
-
   static final _ctor = jniLookup<ffi.NativeFunction<jni.JniResult Function()>>(
           "SuspendFun__ctor")
       .asFunction<jni.JniResult Function()>();
 
   /// from: public void <init>()
+  /// The returned object must be deleted after use, by calling the `delete` method.
   SuspendFun() : super.fromRef(_ctor().object);
 
   static final _sayHello = jniLookup<
@@ -58,8 +60,8 @@
   /// The returned object must be deleted after use, by calling the `delete` method.
   Future<jni.JString> sayHello() async {
     final $p = ReceivePort();
-    final $c = jni.Jni.newPortContinuation($p);
-    _sayHello(reference, $c).object;
+    final $c = jni.JObject.fromRef(jni.Jni.newPortContinuation($p));
+    _sayHello(reference, $c.reference).object;
     final $o = jni.JObjectPtr.fromAddress(await $p.first);
     final $k = const jni.JStringType().getClass().reference;
     if (jni.Jni.env.IsInstanceOf($o, $k) == 0) {
@@ -82,8 +84,8 @@
   /// The returned object must be deleted after use, by calling the `delete` method.
   Future<jni.JString> sayHello1(jni.JString string) async {
     final $p = ReceivePort();
-    final $c = jni.Jni.newPortContinuation($p);
-    _sayHello1(reference, string.reference, $c).object;
+    final $c = jni.JObject.fromRef(jni.Jni.newPortContinuation($p));
+    _sayHello1(reference, string.reference, $c.reference).object;
     final $o = jni.JObjectPtr.fromAddress(await $p.first);
     final $k = const jni.JStringType().getClass().reference;
     if (jni.Jni.env.IsInstanceOf($o, $k) == 0) {
diff --git a/pkgs/jnigen/test/package_resolver_test.dart b/pkgs/jnigen/test/package_resolver_test.dart
index 7645e12..39b8998 100644
--- a/pkgs/jnigen/test/package_resolver_test.dart
+++ b/pkgs/jnigen/test/package_resolver_test.dart
@@ -2,7 +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 'package:jnigen/src/writers/files_writer.dart';
+import 'package:jnigen/src/bindings/resolver.dart';
 import 'package:test/test.dart';
 
 class ResolverTest {
@@ -13,7 +13,7 @@
 }
 
 void main() {
-  final resolver = FilePathResolver(
+  final resolver = Resolver(
       importMap: {
         'org.apache.pdfbox': 'package:pdfbox/pdfbox.dart',
         'android.os.Process': 'package:android/os.dart',
@@ -32,29 +32,28 @@
 
   final tests = [
     // Absolute imports resolved using import map
-    ResolverTest(
-        'android.os.Process', 'package:android/os.dart', 'process_.Process'),
+    ResolverTest('android.os.Process', 'package:android/os.dart', 'process_.'),
     ResolverTest('org.apache.pdfbox.pdmodel.PDDocument',
-        'package:pdfbox/pdfbox.dart', 'pddocument_.PDDocument'),
+        'package:pdfbox/pdfbox.dart', 'pddocument_.'),
     // Relative imports
     // inner package
-    ResolverTest('a.b.c.D', 'c/D.dart', 'd_.D'),
+    ResolverTest('a.b.c.D', 'c/D.dart', 'd_.'),
     // inner package, deeper
-    ResolverTest('a.b.c.d.E', 'c/d/E.dart', 'e_.E'),
+    ResolverTest('a.b.c.d.E', 'c/d/E.dart', 'e_.'),
     // parent package
-    ResolverTest('a.X', '../X.dart', 'x_.X'),
+    ResolverTest('a.X', '../X.dart', 'x_.'),
     // unrelated package in same translation unit
-    ResolverTest('e.f.G', '../../e/f/G.dart', 'g_.G'),
-    ResolverTest('e.F', '../../e/F.dart', 'f_.F'),
+    ResolverTest('e.f.G', '../../e/f/G.dart', 'g_.'),
+    ResolverTest('e.F', '../../e/F.dart', 'f_.'),
     // neighbour package
-    ResolverTest('a.g.Y', '../g/Y.dart', 'y_.Y'),
+    ResolverTest('a.g.Y', '../g/Y.dart', 'y_.'),
     // inner package of a neighbour package
-    ResolverTest('a.m.n.P', '../m/n/P.dart', 'p_.P'),
+    ResolverTest('a.m.n.P', '../m/n/P.dart', 'p_.'),
   ];
 
   for (var testCase in tests) {
     final binaryName = testCase.binaryName;
-    final packageName = getFileClassName(binaryName);
+    final packageName = Resolver.getFileClassName(binaryName);
     test(
         'getImport $binaryName',
         () => expect(resolver.getImport(packageName, binaryName),
@@ -62,6 +61,6 @@
     test(
         'resolve $binaryName',
         () => expect(
-            resolver.resolve(binaryName), equals(testCase.expectedName)));
+            resolver.resolvePrefix(binaryName), equals(testCase.expectedName)));
   }
 }
diff --git a/pkgs/jnigen/test/regenerate_examples_test.dart b/pkgs/jnigen/test/regenerate_examples_test.dart
index b49da06..0e2ac62 100644
--- a/pkgs/jnigen/test/regenerate_examples_test.dart
+++ b/pkgs/jnigen/test/regenerate_examples_test.dart
@@ -25,7 +25,7 @@
 /// [dartOutput] and [cOutput] are relative paths from example project dir.
 void testExample(String exampleName, String dartOutput, String? cOutput) {
   test('Generate and compare bindings for $exampleName',
-      timeout: Timeout.factor(2), () async {
+      timeout: const Timeout.factor(2), () async {
     final examplePath = join('example', exampleName);
     final configPath = join(examplePath, 'jnigen.yaml');
 
diff --git a/pkgs/jnigen/test/simple_package_test/lib/simple_package.dart b/pkgs/jnigen/test/simple_package_test/lib/simple_package.dart
index 0f6841c..c38553f 100644
--- a/pkgs/jnigen/test/simple_package_test/lib/simple_package.dart
+++ b/pkgs/jnigen/test/simple_package_test/lib/simple_package.dart
@@ -14,7 +14,9 @@
 // ignore_for_file: overridden_fields
 // ignore_for_file: unnecessary_cast
 // ignore_for_file: unused_element
+// ignore_for_file: unused_field
 // ignore_for_file: unused_import
+// ignore_for_file: unused_shown_name
 
 import "dart:isolate" show ReceivePort;
 import "dart:ffi" as ffi;
@@ -50,10 +52,6 @@
               "get_Example__aux")
           .asFunction<jni.JniResult Function()>();
 
-  /// from: static public com.github.dart_lang.jnigen.simple_package.Example.Aux aux
-  /// The returned object must be deleted after use, by calling the `delete` method.
-  static Example_Aux get aux =>
-      const $Example_AuxType().fromRef(_get_aux().object);
   static final _set_aux = jniLookup<
           ffi.NativeFunction<
               jni.JThrowablePtr Function(
@@ -62,6 +60,11 @@
 
   /// from: static public com.github.dart_lang.jnigen.simple_package.Example.Aux aux
   /// The returned object must be deleted after use, by calling the `delete` method.
+  static Example_Aux get aux =>
+      const $Example_AuxType().fromRef(_get_aux().object);
+
+  /// from: static public com.github.dart_lang.jnigen.simple_package.Example.Aux aux
+  /// The returned object must be deleted after use, by calling the `delete` method.
   static set aux(Example_Aux value) => _set_aux(value.reference);
 
   static final _get_num =
@@ -69,14 +72,15 @@
               "get_Example__num")
           .asFunction<jni.JniResult Function()>();
 
-  /// from: static public int num
-  static int get num => _get_num().integer;
   static final _set_num =
       jniLookup<ffi.NativeFunction<jni.JThrowablePtr Function(ffi.Int32)>>(
               "set_Example__num")
           .asFunction<jni.JThrowablePtr Function(int)>();
 
   /// from: static public int num
+  static int get num => _get_num().integer;
+
+  /// from: static public int num
   static set num(int value) => _set_num(value);
 
   static final _ctor =
@@ -84,6 +88,7 @@
           .asFunction<jni.JniResult Function()>();
 
   /// from: public void <init>()
+  /// The returned object must be deleted after use, by calling the `delete` method.
   Example() : super.fromRef(_ctor().object);
 
   static final _ctor1 =
@@ -92,6 +97,7 @@
           .asFunction<jni.JniResult Function(int)>();
 
   /// from: public void <init>(int internal)
+  /// The returned object must be deleted after use, by calling the `delete` method.
   Example.ctor1(int internal) : super.fromRef(_ctor1(internal).object);
 
   static final _whichExample = jniLookup<
@@ -225,7 +231,6 @@
 
   /// The type which includes information such as the signature of this class.
   static const type = $Example_AuxType();
-
   static final _get_value = jniLookup<
           ffi.NativeFunction<
               jni.JniResult Function(
@@ -236,8 +241,6 @@
     jni.JObjectPtr,
   )>();
 
-  /// from: public boolean value
-  bool get value => _get_value(reference).boolean;
   static final _set_value = jniLookup<
           ffi.NativeFunction<
               jni.JThrowablePtr Function(
@@ -245,6 +248,9 @@
       .asFunction<jni.JThrowablePtr Function(jni.JObjectPtr, int)>();
 
   /// from: public boolean value
+  bool get value => _get_value(reference).boolean;
+
+  /// from: public boolean value
   set value(bool value) => _set_value(reference, value ? 1 : 0);
 
   static final _ctor =
@@ -253,6 +259,7 @@
           .asFunction<jni.JniResult Function(int)>();
 
   /// from: public void <init>(boolean value)
+  /// The returned object must be deleted after use, by calling the `delete` method.
   Example_Aux(bool value) : super.fromRef(_ctor(value ? 1 : 0).object);
 
   static final _getValue = jniLookup<
@@ -308,20 +315,20 @@
 
   /// The type which includes information such as the signature of this class.
   static const type = $C2Type();
-
   static final _get_CONSTANT =
       jniLookup<ffi.NativeFunction<jni.JniResult Function()>>(
               "get_C2__CONSTANT")
           .asFunction<jni.JniResult Function()>();
 
-  /// from: static public int CONSTANT
-  static int get CONSTANT => _get_CONSTANT().integer;
   static final _set_CONSTANT =
       jniLookup<ffi.NativeFunction<jni.JThrowablePtr Function(ffi.Int32)>>(
               "set_C2__CONSTANT")
           .asFunction<jni.JThrowablePtr Function(int)>();
 
   /// from: static public int CONSTANT
+  static int get CONSTANT => _get_CONSTANT().integer;
+
+  /// from: static public int CONSTANT
   static set CONSTANT(int value) => _set_CONSTANT(value);
 
   static final _ctor =
@@ -329,6 +336,7 @@
           .asFunction<jni.JniResult Function()>();
 
   /// from: public void <init>()
+  /// The returned object must be deleted after use, by calling the `delete` method.
   C2() : super.fromRef(_ctor().object);
 }
 
@@ -365,12 +373,12 @@
 
   /// The type which includes information such as the signature of this class.
   static const type = $Example1Type();
-
   static final _ctor =
       jniLookup<ffi.NativeFunction<jni.JniResult Function()>>("Example1__ctor")
           .asFunction<jni.JniResult Function()>();
 
   /// from: public void <init>()
+  /// The returned object must be deleted after use, by calling the `delete` method.
   Example1() : super.fromRef(_ctor().object);
 
   static final _whichExample = jniLookup<
@@ -408,9 +416,7 @@
 class GrandParent<T extends jni.JObject> extends jni.JObject {
   late final jni.JObjType? _$type;
   @override
-  jni.JObjType get $type => _$type ??= type(
-        $T,
-      );
+  jni.JObjType get $type => _$type ??= type($T);
 
   final jni.JObjType<T> $T;
 
@@ -438,9 +444,6 @@
     jni.JObjectPtr,
   )>();
 
-  /// from: public T value
-  /// The returned object must be deleted after use, by calling the `delete` method.
-  T get value => $T.fromRef(_get_value(reference).object);
   static final _set_value = jniLookup<
           ffi.NativeFunction<
               jni.JThrowablePtr Function(jni.JObjectPtr,
@@ -450,6 +453,10 @@
 
   /// from: public T value
   /// The returned object must be deleted after use, by calling the `delete` method.
+  T get value => $T.fromRef(_get_value(reference).object);
+
+  /// from: public T value
+  /// The returned object must be deleted after use, by calling the `delete` method.
   set value(T value) => _set_value(reference, value.reference);
 
   static final _ctor = jniLookup<
@@ -459,6 +466,7 @@
       .asFunction<jni.JniResult Function(ffi.Pointer<ffi.Void>)>();
 
   /// from: public void <init>(T value)
+  /// The returned object must be deleted after use, by calling the `delete` method.
   GrandParent(this.$T, T value) : super.fromRef(_ctor(value.reference).object);
 
   static final _stringParent = jniLookup<
@@ -470,7 +478,7 @@
   /// from: public com.github.dart_lang.jnigen.generics.GrandParent<T>.Parent<java.lang.String> stringParent()
   /// The returned object must be deleted after use, by calling the `delete` method.
   GrandParent_Parent<jni.JObject, jni.JString> stringParent() =>
-      $GrandParent_ParentType(const jni.JObjectType(), jni.JStringType())
+      const $GrandParent_ParentType(jni.JObjectType(), jni.JStringType())
           .fromRef(_stringParent(reference).object);
 
   static final _varParent = jniLookup<
@@ -557,10 +565,7 @@
     extends jni.JObject {
   late final jni.JObjType? _$type;
   @override
-  jni.JObjType get $type => _$type ??= type(
-        $T,
-        $S,
-      );
+  jni.JObjType get $type => _$type ??= type($T, $S);
 
   final jni.JObjType<T> $T;
   final jni.JObjType<S> $S;
@@ -593,9 +598,6 @@
     jni.JObjectPtr,
   )>();
 
-  /// from: public T parentValue
-  /// The returned object must be deleted after use, by calling the `delete` method.
-  T get parentValue => $T.fromRef(_get_parentValue(reference).object);
   static final _set_parentValue = jniLookup<
               ffi.NativeFunction<
                   jni.JThrowablePtr Function(
@@ -606,6 +608,10 @@
 
   /// from: public T parentValue
   /// The returned object must be deleted after use, by calling the `delete` method.
+  T get parentValue => $T.fromRef(_get_parentValue(reference).object);
+
+  /// from: public T parentValue
+  /// The returned object must be deleted after use, by calling the `delete` method.
   set parentValue(T value) => _set_parentValue(reference, value.reference);
 
   static final _get_value = jniLookup<
@@ -618,9 +624,6 @@
     jni.JObjectPtr,
   )>();
 
-  /// from: public S value
-  /// The returned object must be deleted after use, by calling the `delete` method.
-  S get value => $S.fromRef(_get_value(reference).object);
   static final _set_value = jniLookup<
           ffi.NativeFunction<
               jni.JThrowablePtr Function(jni.JObjectPtr,
@@ -630,6 +633,10 @@
 
   /// from: public S value
   /// The returned object must be deleted after use, by calling the `delete` method.
+  S get value => $S.fromRef(_get_value(reference).object);
+
+  /// from: public S value
+  /// The returned object must be deleted after use, by calling the `delete` method.
   set value(S value) => _set_value(reference, value.reference);
 
   static final _ctor = jniLookup<
@@ -641,6 +648,7 @@
               ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>)>();
 
   /// from: public void <init>(T parentValue, S value)
+  /// The returned object must be deleted after use, by calling the `delete` method.
   GrandParent_Parent(this.$T, this.$S, T parentValue, S value)
       : super.fromRef(_ctor(parentValue.reference, value.reference).object);
 }
@@ -681,11 +689,7 @@
     U extends jni.JObject> extends jni.JObject {
   late final jni.JObjType? _$type;
   @override
-  jni.JObjType get $type => _$type ??= type(
-        $T,
-        $S,
-        $U,
-      );
+  jni.JObjType get $type => _$type ??= type($T, $S, $U);
 
   final jni.JObjType<T> $T;
   final jni.JObjType<S> $S;
@@ -722,9 +726,6 @@
     jni.JObjectPtr,
   )>();
 
-  /// from: public T grandParentValue
-  /// The returned object must be deleted after use, by calling the `delete` method.
-  T get grandParentValue => $T.fromRef(_get_grandParentValue(reference).object);
   static final _set_grandParentValue = jniLookup<
               ffi.NativeFunction<
                   jni.JThrowablePtr Function(
@@ -735,6 +736,10 @@
 
   /// from: public T grandParentValue
   /// The returned object must be deleted after use, by calling the `delete` method.
+  T get grandParentValue => $T.fromRef(_get_grandParentValue(reference).object);
+
+  /// from: public T grandParentValue
+  /// The returned object must be deleted after use, by calling the `delete` method.
   set grandParentValue(T value) =>
       _set_grandParentValue(reference, value.reference);
 
@@ -748,9 +753,6 @@
     jni.JObjectPtr,
   )>();
 
-  /// from: public S parentValue
-  /// The returned object must be deleted after use, by calling the `delete` method.
-  S get parentValue => $S.fromRef(_get_parentValue(reference).object);
   static final _set_parentValue = jniLookup<
               ffi.NativeFunction<
                   jni.JThrowablePtr Function(
@@ -761,6 +763,10 @@
 
   /// from: public S parentValue
   /// The returned object must be deleted after use, by calling the `delete` method.
+  S get parentValue => $S.fromRef(_get_parentValue(reference).object);
+
+  /// from: public S parentValue
+  /// The returned object must be deleted after use, by calling the `delete` method.
   set parentValue(S value) => _set_parentValue(reference, value.reference);
 
   static final _get_value = jniLookup<
@@ -773,9 +779,6 @@
     jni.JObjectPtr,
   )>();
 
-  /// from: public U value
-  /// The returned object must be deleted after use, by calling the `delete` method.
-  U get value => $U.fromRef(_get_value(reference).object);
   static final _set_value = jniLookup<
               ffi.NativeFunction<
                   jni.JThrowablePtr Function(
@@ -786,6 +789,10 @@
 
   /// from: public U value
   /// The returned object must be deleted after use, by calling the `delete` method.
+  U get value => $U.fromRef(_get_value(reference).object);
+
+  /// from: public U value
+  /// The returned object must be deleted after use, by calling the `delete` method.
   set value(U value) => _set_value(reference, value.reference);
 
   static final _ctor = jniLookup<
@@ -799,6 +806,7 @@
               ffi.Pointer<ffi.Void>)>();
 
   /// from: public void <init>(T grandParentValue, S parentValue, U value)
+  /// The returned object must be deleted after use, by calling the `delete` method.
   GrandParent_Parent_Child(
       this.$T, this.$S, this.$U, T grandParentValue, S parentValue, U value)
       : super.fromRef(_ctor(grandParentValue.reference, parentValue.reference,
@@ -846,9 +854,7 @@
 class GrandParent_StaticParent<S extends jni.JObject> extends jni.JObject {
   late final jni.JObjType? _$type;
   @override
-  jni.JObjType get $type => _$type ??= type(
-        $S,
-      );
+  jni.JObjType get $type => _$type ??= type($S);
 
   final jni.JObjType<S> $S;
 
@@ -876,9 +882,6 @@
     jni.JObjectPtr,
   )>();
 
-  /// from: public S value
-  /// The returned object must be deleted after use, by calling the `delete` method.
-  S get value => $S.fromRef(_get_value(reference).object);
   static final _set_value = jniLookup<
               ffi.NativeFunction<
                   jni.JThrowablePtr Function(
@@ -889,6 +892,10 @@
 
   /// from: public S value
   /// The returned object must be deleted after use, by calling the `delete` method.
+  S get value => $S.fromRef(_get_value(reference).object);
+
+  /// from: public S value
+  /// The returned object must be deleted after use, by calling the `delete` method.
   set value(S value) => _set_value(reference, value.reference);
 
   static final _ctor = jniLookup<
@@ -898,6 +905,7 @@
       .asFunction<jni.JniResult Function(ffi.Pointer<ffi.Void>)>();
 
   /// from: public void <init>(S value)
+  /// The returned object must be deleted after use, by calling the `delete` method.
   GrandParent_StaticParent(this.$S, S value)
       : super.fromRef(_ctor(value.reference).object);
 }
@@ -936,10 +944,7 @@
     U extends jni.JObject> extends jni.JObject {
   late final jni.JObjType? _$type;
   @override
-  jni.JObjType get $type => _$type ??= type(
-        $S,
-        $U,
-      );
+  jni.JObjType get $type => _$type ??= type($S, $U);
 
   final jni.JObjType<S> $S;
   final jni.JObjType<U> $U;
@@ -972,9 +977,6 @@
     jni.JObjectPtr,
   )>();
 
-  /// from: public S parentValue
-  /// The returned object must be deleted after use, by calling the `delete` method.
-  S get parentValue => $S.fromRef(_get_parentValue(reference).object);
   static final _set_parentValue = jniLookup<
               ffi.NativeFunction<
                   jni.JThrowablePtr Function(
@@ -985,6 +987,10 @@
 
   /// from: public S parentValue
   /// The returned object must be deleted after use, by calling the `delete` method.
+  S get parentValue => $S.fromRef(_get_parentValue(reference).object);
+
+  /// from: public S parentValue
+  /// The returned object must be deleted after use, by calling the `delete` method.
   set parentValue(S value) => _set_parentValue(reference, value.reference);
 
   static final _get_value = jniLookup<
@@ -997,9 +1003,6 @@
     jni.JObjectPtr,
   )>();
 
-  /// from: public U value
-  /// The returned object must be deleted after use, by calling the `delete` method.
-  U get value => $U.fromRef(_get_value(reference).object);
   static final _set_value = jniLookup<
               ffi.NativeFunction<
                   jni.JThrowablePtr Function(
@@ -1010,6 +1013,10 @@
 
   /// from: public U value
   /// The returned object must be deleted after use, by calling the `delete` method.
+  U get value => $U.fromRef(_get_value(reference).object);
+
+  /// from: public U value
+  /// The returned object must be deleted after use, by calling the `delete` method.
   set value(U value) => _set_value(reference, value.reference);
 
   static final _ctor = jniLookup<
@@ -1022,6 +1029,7 @@
               ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>)>();
 
   /// from: public void <init>(S parentValue, U value)
+  /// The returned object must be deleted after use, by calling the `delete` method.
   GrandParent_StaticParent_Child(this.$S, this.$U, S parentValue, U value)
       : super.fromRef(_ctor(parentValue.reference, value.reference).object);
 }
@@ -1062,10 +1070,7 @@
 class MyMap<K extends jni.JObject, V extends jni.JObject> extends jni.JObject {
   late final jni.JObjType? _$type;
   @override
-  jni.JObjType get $type => _$type ??= type(
-        $K,
-        $V,
-      );
+  jni.JObjType get $type => _$type ??= type($K, $V);
 
   final jni.JObjType<K> $K;
   final jni.JObjType<V> $V;
@@ -1092,6 +1097,7 @@
           .asFunction<jni.JniResult Function()>();
 
   /// from: public void <init>()
+  /// The returned object must be deleted after use, by calling the `delete` method.
   MyMap(this.$K, this.$V) : super.fromRef(_ctor().object);
 
   static final _get0 = jniLookup<
@@ -1127,9 +1133,10 @@
 
   /// from: public com.github.dart_lang.jnigen.generics.MyStack<com.github.dart_lang.jnigen.generics.MyMap<K,V>.MyEntry> entryStack()
   /// The returned object must be deleted after use, by calling the `delete` method.
-  MyStack<MyMap_MyEntry<jni.JObject, jni.JObject>> entryStack() => $MyStackType(
-          $MyMap_MyEntryType(const jni.JObjectType(), const jni.JObjectType()))
-      .fromRef(_entryStack(reference).object);
+  MyStack<MyMap_MyEntry<jni.JObject, jni.JObject>> entryStack() =>
+      const $MyStackType(
+              $MyMap_MyEntryType(jni.JObjectType(), jni.JObjectType()))
+          .fromRef(_entryStack(reference).object);
 }
 
 class $MyMapType<K extends jni.JObject, V extends jni.JObject>
@@ -1166,10 +1173,7 @@
     extends jni.JObject {
   late final jni.JObjType? _$type;
   @override
-  jni.JObjType get $type => _$type ??= type(
-        $K,
-        $V,
-      );
+  jni.JObjType get $type => _$type ??= type($K, $V);
 
   final jni.JObjType<K> $K;
   final jni.JObjType<V> $V;
@@ -1202,9 +1206,6 @@
     jni.JObjectPtr,
   )>();
 
-  /// from: public K key
-  /// The returned object must be deleted after use, by calling the `delete` method.
-  K get key => $K.fromRef(_get_key(reference).object);
   static final _set_key = jniLookup<
           ffi.NativeFunction<
               jni.JThrowablePtr Function(jni.JObjectPtr,
@@ -1214,6 +1215,10 @@
 
   /// from: public K key
   /// The returned object must be deleted after use, by calling the `delete` method.
+  K get key => $K.fromRef(_get_key(reference).object);
+
+  /// from: public K key
+  /// The returned object must be deleted after use, by calling the `delete` method.
   set key(K value) => _set_key(reference, value.reference);
 
   static final _get_value = jniLookup<
@@ -1226,9 +1231,6 @@
     jni.JObjectPtr,
   )>();
 
-  /// from: public V value
-  /// The returned object must be deleted after use, by calling the `delete` method.
-  V get value => $V.fromRef(_get_value(reference).object);
   static final _set_value = jniLookup<
           ffi.NativeFunction<
               jni.JThrowablePtr Function(jni.JObjectPtr,
@@ -1238,6 +1240,10 @@
 
   /// from: public V value
   /// The returned object must be deleted after use, by calling the `delete` method.
+  V get value => $V.fromRef(_get_value(reference).object);
+
+  /// from: public V value
+  /// The returned object must be deleted after use, by calling the `delete` method.
   set value(V value) => _set_value(reference, value.reference);
 
   static final _ctor = jniLookup<
@@ -1249,6 +1255,7 @@
               ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>)>();
 
   /// from: public void <init>(K key, V value)
+  /// The returned object must be deleted after use, by calling the `delete` method.
   MyMap_MyEntry(this.$K, this.$V, K key, V value)
       : super.fromRef(_ctor(key.reference, value.reference).object);
 }
@@ -1288,9 +1295,7 @@
 class MyStack<T extends jni.JObject> extends jni.JObject {
   late final jni.JObjType? _$type;
   @override
-  jni.JObjType get $type => _$type ??= type(
-        $T,
-      );
+  jni.JObjType get $type => _$type ??= type($T);
 
   final jni.JObjType<T> $T;
 
@@ -1313,6 +1318,7 @@
           .asFunction<jni.JniResult Function()>();
 
   /// from: public void <init>()
+  /// The returned object must be deleted after use, by calling the `delete` method.
   MyStack(this.$T) : super.fromRef(_ctor().object);
 
   static final _push = jniLookup<
@@ -1365,9 +1371,7 @@
 class StringKeyedMap<V extends jni.JObject> extends MyMap<jni.JString, V> {
   late final jni.JObjType? _$type;
   @override
-  jni.JObjType get $type => _$type ??= type(
-        $V,
-      );
+  jni.JObjType get $type => _$type ??= type($V);
 
   final jni.JObjType<V> $V;
 
@@ -1390,6 +1394,7 @@
       .asFunction<jni.JniResult Function()>();
 
   /// from: public void <init>()
+  /// The returned object must be deleted after use, by calling the `delete` method.
   StringKeyedMap(this.$V)
       : super.fromRef(const jni.JStringType(), $V, _ctor().object);
 }
@@ -1435,12 +1440,12 @@
 
   /// The type which includes information such as the signature of this class.
   static const type = $StringStackType();
-
   static final _ctor = jniLookup<ffi.NativeFunction<jni.JniResult Function()>>(
           "StringStack__ctor")
       .asFunction<jni.JniResult Function()>();
 
   /// from: public void <init>()
+  /// The returned object must be deleted after use, by calling the `delete` method.
   StringStack() : super.fromRef(const jni.JStringType(), _ctor().object);
 }
 
@@ -1469,9 +1474,7 @@
 class StringValuedMap<K extends jni.JObject> extends MyMap<K, jni.JString> {
   late final jni.JObjType? _$type;
   @override
-  jni.JObjType get $type => _$type ??= type(
-        $K,
-      );
+  jni.JObjType get $type => _$type ??= type($K);
 
   final jni.JObjType<K> $K;
 
@@ -1494,6 +1497,7 @@
       .asFunction<jni.JniResult Function()>();
 
   /// from: public void <init>()
+  /// The returned object must be deleted after use, by calling the `delete` method.
   StringValuedMap(this.$K)
       : super.fromRef($K, const jni.JStringType(), _ctor().object);
 }
@@ -1539,7 +1543,6 @@
 
   /// The type which includes information such as the signature of this class.
   static const type = $JsonSerializable_CaseType();
-
   static final _values =
       jniLookup<ffi.NativeFunction<jni.JniResult Function()>>(
               "JsonSerializable_Case__values")
@@ -1599,12 +1602,12 @@
 
   /// The type which includes information such as the signature of this class.
   static const type = $MyDataClassType();
-
   static final _ctor = jniLookup<ffi.NativeFunction<jni.JniResult Function()>>(
           "MyDataClass__ctor")
       .asFunction<jni.JniResult Function()>();
 
   /// from: public void <init>()
+  /// The returned object must be deleted after use, by calling the `delete` method.
   MyDataClass() : super.fromRef(_ctor().object);
 }
 
diff --git a/pkgs/jnigen/test/yaml_config_test.dart b/pkgs/jnigen/test/yaml_config_test.dart
index 198457d..e995b6a 100644
--- a/pkgs/jnigen/test/yaml_config_test.dart
+++ b/pkgs/jnigen/test/yaml_config_test.dart
@@ -26,5 +26,5 @@
     ];
     final config = Config.parseArgs(args);
     await generateAndCompareBindings(config, lib, src);
-  }, timeout: Timeout.factor(4));
+  }, timeout: const Timeout.factor(4));
 }