[jnigen] Non static nested class construction (https://github.com/dart-lang/jnigen/issues/297)

Construct the non-static nested classes correctly
diff --git a/pkgs/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/TypeUtils.java b/pkgs/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/TypeUtils.java
index 7cb6f1e..521c02d 100644
--- a/pkgs/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/TypeUtils.java
+++ b/pkgs/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/TypeUtils.java
@@ -20,7 +20,11 @@
 class TypeUtils {
 
   public static String parentName(Type type) {
-    return type.getClassName().split("\\$")[0];
+    var className = type.getClassName();
+    if (!className.contains("$")) {
+      return null;
+    }
+    return className.split("\\$")[0];
   }
 
   public static String simpleName(Type type) {
diff --git a/pkgs/jnigen/lib/src/bindings/c_generator.dart b/pkgs/jnigen/lib/src/bindings/c_generator.dart
index 7ca6ef1..dedef76 100644
--- a/pkgs/jnigen/lib/src/bindings/c_generator.dart
+++ b/pkgs/jnigen/lib/src/bindings/c_generator.dart
@@ -390,9 +390,12 @@
   @override
   String visit(Param node) {
     final paramName =
-        _cTypeKeywords.contains(node.name) ? '${node.name}0' : node.name;
-    final type = node.type.accept(const _CReturnType());
-    if (addReturnType) return '$type $paramName';
+        (_cTypeKeywords.contains(node.name) ? '${node.name}0' : node.name)
+            .replaceAll('\$', '_');
+    if (addReturnType) {
+      final type = node.type.accept(const _CReturnType());
+      return '$type $paramName';
+    }
     return paramName;
   }
 }
diff --git a/pkgs/jnigen/lib/src/bindings/dart_generator.dart b/pkgs/jnigen/lib/src/bindings/dart_generator.dart
index a1559cf..2854e6b 100644
--- a/pkgs/jnigen/lib/src/bindings/dart_generator.dart
+++ b/pkgs/jnigen/lib/src/bindings/dart_generator.dart
@@ -333,7 +333,7 @@
         .map((typeParam) => 'this.$typeParam,')
         .join(_newLine(depth: 2));
     final superClass = (node.classDecl.superclass!.type as DeclaredType);
-    final superTypeClassesCall = superClass.classDecl.isObject()
+    final superTypeClassesCall = superClass.classDecl.isObject
         ? ''
         : superClass.params
             .accept(_TypeClassGenerator(resolver))
@@ -1331,7 +1331,7 @@
 
   @override
   Map<String, List<OutsideInBuffer>> visitDeclaredType(DeclaredType node) {
-    if (node.classDecl.isObject()) {
+    if (node.classDecl.isObject) {
       return {};
     }
     final offset = node.classDecl.allTypeParams.length - node.params.length;
diff --git a/pkgs/jnigen/lib/src/bindings/linker.dart b/pkgs/jnigen/lib/src/bindings/linker.dart
index cdddaf5..d6b44a5 100644
--- a/pkgs/jnigen/lib/src/bindings/linker.dart
+++ b/pkgs/jnigen/lib/src/bindings/linker.dart
@@ -92,20 +92,7 @@
     log.finest('Linking ${node.binaryName}.');
     _linked.add(node);
 
-    node.parent = resolve(node.parentName);
-    node.parent!.accept(this);
-    // Add 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;
+    node.parent = node.parentName == null ? null : resolve(node.parentName);
 
     final typeLinker = _TypeLinker(resolve);
     node.superclass ??= TypeUsage.object;
diff --git a/pkgs/jnigen/lib/src/bindings/renamer.dart b/pkgs/jnigen/lib/src/bindings/renamer.dart
index d5f9a73..607216e 100644
--- a/pkgs/jnigen/lib/src/bindings/renamer.dart
+++ b/pkgs/jnigen/lib/src/bindings/renamer.dart
@@ -65,6 +65,7 @@
   'throw',
   'true',
   'try',
+  'type', // Is used for type classes.
   'typedef',
   'var',
   'void',
@@ -213,8 +214,8 @@
       node.finalName = _renameConflict(nameCounts, name);
       node.classDecl.methodNumsAfterRenaming[sig] = nameCounts[name]! - 1;
     }
-    log.fine(
-        'Method ${node.classDecl.binaryName}#${node.name} is named ${node.finalName}');
+    log.fine('Method ${node.classDecl.binaryName}#${node.name}'
+        ' is named ${node.finalName}');
 
     final paramRenamer = _ParamRenamer(config);
     for (final param in node.params) {
@@ -242,8 +243,8 @@
       nameCounts,
       node.name,
     );
-    log.fine(
-        'Field ${node.classDecl.binaryName}#${node.name} is named ${node.finalName}');
+    log.fine('Field ${node.classDecl.binaryName}#${node.name}'
+        ' is named ${node.finalName}');
   }
 }
 
diff --git a/pkgs/jnigen/lib/src/bindings/unnester.dart b/pkgs/jnigen/lib/src/bindings/unnester.dart
new file mode 100644
index 0000000..0052205
--- /dev/null
+++ b/pkgs/jnigen/lib/src/bindings/unnester.dart
@@ -0,0 +1,83 @@
+// 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';
+
+/// A [Visitor] that processes nested classes.
+///
+/// Nested classes are not supported in Dart. So this "unnests" them into
+/// separate classes.
+class Unnester extends Visitor<Classes, void> {
+  @override
+  void visit(node) {
+    final classProcessor = _ClassUnnester();
+    for (final classDecl in node.decls.values) {
+      classDecl.accept(classProcessor);
+    }
+  }
+}
+
+class _ClassUnnester extends Visitor<ClassDecl, void> {
+  final processed = <ClassDecl>{};
+
+  @override
+  void visit(ClassDecl node) {
+    if (processed.contains(node)) return;
+    processed.add(node);
+    // We need to visit the ancestors first.
+    node.parent?.accept(this);
+
+    // Add type params of outer classes to the nested classes.
+    final allTypeParams = <TypeParam>[];
+    if (!node.isStatic) {
+      allTypeParams.addAll(node.parent?.allTypeParams ?? []);
+    }
+    allTypeParams.addAll(node.typeParams);
+    node.allTypeParams = allTypeParams;
+
+    if (node.isNested && !node.isStatic) {
+      const methodProcessor = _MethodUnnester();
+      for (final method in node.methods) {
+        method.accept(methodProcessor);
+      }
+    }
+  }
+}
+
+class _MethodUnnester extends Visitor<Method, void> {
+  const _MethodUnnester();
+
+  @override
+  void visit(Method node) {
+    assert(!node.classDecl.isStatic);
+    assert(node.classDecl.isNested);
+    if (node.isCtor || node.isStatic) {
+      // Non-static nested classes take an instance of their outer class as the
+      // first parameter. This is not accounted for by the summarizer, so we
+      // manually add it as the first parameter.
+      final parentTypeParamCount = node.classDecl.allTypeParams.length -
+          node.classDecl.typeParams.length;
+      final parentTypeParams = [
+        for (final typeParam
+            in node.classDecl.allTypeParams.take(parentTypeParamCount)) ...[
+          TypeUsage(
+              shorthand: typeParam.name, kind: Kind.typeVariable, typeJson: {})
+            ..type = TypeVar(name: typeParam.name),
+        ]
+      ];
+      final parentType = DeclaredType(
+        binaryName: node.classDecl.parent!.binaryName,
+        params: parentTypeParams,
+      )..classDecl = node.classDecl.parent!;
+      final parentTypeUsage = TypeUsage(
+          shorthand: parentType.binaryName, kind: Kind.declared, typeJson: {})
+        ..type = parentType;
+      final param = Param(name: '\$parent', type: parentTypeUsage);
+      // Make the list modifiable.
+      if (node.params.isEmpty) node.params = [];
+      node.params.insert(0, param);
+    }
+  }
+}
diff --git a/pkgs/jnigen/lib/src/config/config_types.dart b/pkgs/jnigen/lib/src/config/config_types.dart
index 63a418c..58fc9a5 100644
--- a/pkgs/jnigen/lib/src/config/config_types.dart
+++ b/pkgs/jnigen/lib/src/config/config_types.dart
@@ -401,7 +401,8 @@
             ..finalName = decl['name']
             ..typeClassName = decl['type_class']
             ..superCount = decl['super_count']
-            ..allTypeParams = [];
+            ..allTypeParams = []
+            ..parent = null;
           for (final typeParamEntry
               in ((decl['type_params'] as YamlMap?)?.entries) ??
                   <MapEntry<dynamic, dynamic>>[]) {
diff --git a/pkgs/jnigen/lib/src/elements/elements.dart b/pkgs/jnigen/lib/src/elements/elements.dart
index 15f2f45..51c19b9 100644
--- a/pkgs/jnigen/lib/src/elements/elements.dart
+++ b/pkgs/jnigen/lib/src/elements/elements.dart
@@ -167,7 +167,9 @@
   @override
   String get name => finalName;
 
-  bool isObject() => superCount == 0;
+  bool get isObject => superCount == 0;
+
+  bool get isNested => parentName != null;
 }
 
 @JsonEnum()
@@ -455,7 +457,7 @@
   final List<Annotation> annotations;
   final JavaDocComment? javadoc;
   final List<TypeParam> typeParams;
-  final List<Param> params;
+  List<Param> params;
   final TypeUsage returnType;
 
   /// The [ClassDecl] where this method is defined.
diff --git a/pkgs/jnigen/lib/src/generate_bindings.dart b/pkgs/jnigen/lib/src/generate_bindings.dart
index 5c9dc9b..87727dc 100644
--- a/pkgs/jnigen/lib/src/generate_bindings.dart
+++ b/pkgs/jnigen/lib/src/generate_bindings.dart
@@ -9,6 +9,7 @@
 import 'bindings/dart_generator.dart';
 import 'bindings/excluder.dart';
 import 'bindings/linker.dart';
+import 'bindings/unnester.dart';
 import 'bindings/renamer.dart';
 import 'elements/elements.dart';
 import 'summary/summary.dart';
@@ -36,6 +37,7 @@
 
   classes.accept(Excluder(config));
   await classes.accept(Linker(config));
+  classes.accept(Unnester());
   classes.accept(Renamer(config));
 
   final cBased = config.outputConfig.bindingsType == BindingsType.cBased;
diff --git a/pkgs/jnigen/test/simple_package_test/c_based/c_bindings/simple_package.c b/pkgs/jnigen/test/simple_package_test/c_based/c_bindings/simple_package.c
index e388ec7..4a170e7 100644
--- a/pkgs/jnigen/test/simple_package_test/c_based/c_bindings/simple_package.c
+++ b/pkgs/jnigen/test/simple_package_test/c_based/c_bindings/simple_package.c
@@ -1513,7 +1513,7 @@
 
 jmethodID _m_GrandParent_Parent__ctor = NULL;
 FFI_PLUGIN_EXPORT
-JniResult GrandParent_Parent__ctor(jobject parentValue, jobject value) {
+JniResult GrandParent_Parent__ctor(jobject _parent, jobject newValue) {
   load_env();
   load_class_global_ref(
       &_c_GrandParent_Parent,
@@ -1521,12 +1521,13 @@
   if (_c_GrandParent_Parent == NULL)
     return (JniResult){.value = {.j = 0}, .exception = check_exception()};
   load_method(_c_GrandParent_Parent, &_m_GrandParent_Parent__ctor, "<init>",
-              "(Ljava/lang/Object;Ljava/lang/Object;)V");
+              "(Lcom/github/dart_lang/jnigen/generics/GrandParent;Ljava/lang/"
+              "Object;)V");
   if (_m_GrandParent_Parent__ctor == NULL)
     return (JniResult){.value = {.j = 0}, .exception = check_exception()};
   jobject _result =
       (*jniEnv)->NewObject(jniEnv, _c_GrandParent_Parent,
-                           _m_GrandParent_Parent__ctor, parentValue, value);
+                           _m_GrandParent_Parent__ctor, _parent, newValue);
   return to_global_ref_result(_result);
 }
 
@@ -1596,9 +1597,7 @@
 
 jmethodID _m_GrandParent_Parent_Child__ctor = NULL;
 FFI_PLUGIN_EXPORT
-JniResult GrandParent_Parent_Child__ctor(jobject grandParentValue,
-                                         jobject parentValue,
-                                         jobject value) {
+JniResult GrandParent_Parent_Child__ctor(jobject _parent, jobject newValue) {
   load_env();
   load_class_global_ref(
       &_c_GrandParent_Parent_Child,
@@ -1607,12 +1606,13 @@
     return (JniResult){.value = {.j = 0}, .exception = check_exception()};
   load_method(_c_GrandParent_Parent_Child, &_m_GrandParent_Parent_Child__ctor,
               "<init>",
-              "(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V");
+              "(Lcom/github/dart_lang/jnigen/generics/GrandParent$Parent;Ljava/"
+              "lang/Object;)V");
   if (_m_GrandParent_Parent_Child__ctor == NULL)
     return (JniResult){.value = {.j = 0}, .exception = check_exception()};
   jobject _result = (*jniEnv)->NewObject(jniEnv, _c_GrandParent_Parent_Child,
                                          _m_GrandParent_Parent_Child__ctor,
-                                         grandParentValue, parentValue, value);
+                                         _parent, newValue);
   return to_global_ref_result(_result);
 }
 
@@ -1773,7 +1773,8 @@
 
 jmethodID _m_GrandParent_StaticParent_Child__ctor = NULL;
 FFI_PLUGIN_EXPORT
-JniResult GrandParent_StaticParent_Child__ctor(jobject parentValue,
+JniResult GrandParent_StaticParent_Child__ctor(jobject _parent,
+                                               jobject parentValue,
                                                jobject value) {
   load_env();
   load_class_global_ref(
@@ -1781,14 +1782,16 @@
       "com/github/dart_lang/jnigen/generics/GrandParent$StaticParent$Child");
   if (_c_GrandParent_StaticParent_Child == NULL)
     return (JniResult){.value = {.j = 0}, .exception = check_exception()};
-  load_method(_c_GrandParent_StaticParent_Child,
-              &_m_GrandParent_StaticParent_Child__ctor, "<init>",
-              "(Ljava/lang/Object;Ljava/lang/Object;)V");
+  load_method(
+      _c_GrandParent_StaticParent_Child,
+      &_m_GrandParent_StaticParent_Child__ctor, "<init>",
+      "(Lcom/github/dart_lang/jnigen/generics/GrandParent$StaticParent;Ljava/"
+      "lang/Object;Ljava/lang/Object;)V");
   if (_m_GrandParent_StaticParent_Child__ctor == NULL)
     return (JniResult){.value = {.j = 0}, .exception = check_exception()};
   jobject _result = (*jniEnv)->NewObject(
       jniEnv, _c_GrandParent_StaticParent_Child,
-      _m_GrandParent_StaticParent_Child__ctor, parentValue, value);
+      _m_GrandParent_StaticParent_Child__ctor, _parent, parentValue, value);
   return to_global_ref_result(_result);
 }
 
@@ -1934,18 +1937,19 @@
 
 jmethodID _m_MyMap_MyEntry__ctor = NULL;
 FFI_PLUGIN_EXPORT
-JniResult MyMap_MyEntry__ctor(jobject key, jobject value) {
+JniResult MyMap_MyEntry__ctor(jobject _parent, jobject key, jobject value) {
   load_env();
   load_class_global_ref(&_c_MyMap_MyEntry,
                         "com/github/dart_lang/jnigen/generics/MyMap$MyEntry");
   if (_c_MyMap_MyEntry == NULL)
     return (JniResult){.value = {.j = 0}, .exception = check_exception()};
   load_method(_c_MyMap_MyEntry, &_m_MyMap_MyEntry__ctor, "<init>",
-              "(Ljava/lang/Object;Ljava/lang/Object;)V");
+              "(Lcom/github/dart_lang/jnigen/generics/MyMap;Ljava/lang/"
+              "Object;Ljava/lang/Object;)V");
   if (_m_MyMap_MyEntry__ctor == NULL)
     return (JniResult){.value = {.j = 0}, .exception = check_exception()};
-  jobject _result = (*jniEnv)->NewObject(jniEnv, _c_MyMap_MyEntry,
-                                         _m_MyMap_MyEntry__ctor, key, value);
+  jobject _result = (*jniEnv)->NewObject(
+      jniEnv, _c_MyMap_MyEntry, _m_MyMap_MyEntry__ctor, _parent, key, value);
   return to_global_ref_result(_result);
 }
 
diff --git a/pkgs/jnigen/test/simple_package_test/c_based/dart_bindings/simple_package.dart b/pkgs/jnigen/test/simple_package_test/c_based/dart_bindings/simple_package.dart
index 5cdec37..4e16f1e 100644
--- a/pkgs/jnigen/test/simple_package_test/c_based/dart_bindings/simple_package.dart
+++ b/pkgs/jnigen/test/simple_package_test/c_based/dart_bindings/simple_package.dart
@@ -1578,22 +1578,22 @@
           jni.JniResult Function(
               ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>)>();
 
-  /// from: public void <init>(T parentValue, S value)
+  /// from: public void <init>(com.github.dart_lang.jnigen.generics.GrandParent $parent, S newValue)
   /// The returned object must be deleted after use, by calling the `delete` method.
   factory GrandParent_Parent(
-    $T parentValue,
-    $S value, {
+    GrandParent<$T> $parent,
+    $S newValue, {
     jni.JObjType<$T>? T,
     jni.JObjType<$S>? S,
   }) {
     T ??= jni.lowestCommonSuperType([
-      parentValue.$type,
+      ($parent.$type as $GrandParentType).T,
     ]) as jni.JObjType<$T>;
     S ??= jni.lowestCommonSuperType([
-      value.$type,
+      newValue.$type,
     ]) as jni.JObjType<$S>;
     return GrandParent_Parent.fromRef(
-        T, S, _ctor(parentValue.reference, value.reference).object);
+        T, S, _ctor($parent.reference, newValue.reference).object);
   }
 }
 
@@ -1747,40 +1747,32 @@
 
   static final _ctor = jniLookup<
           ffi.NativeFunction<
-              jni.JniResult Function(
-                  ffi.Pointer<ffi.Void>,
-                  ffi.Pointer<ffi.Void>,
+              jni.JniResult Function(ffi.Pointer<ffi.Void>,
                   ffi.Pointer<ffi.Void>)>>("GrandParent_Parent_Child__ctor")
       .asFunction<
-          jni.JniResult Function(ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>,
-              ffi.Pointer<ffi.Void>)>();
+          jni.JniResult Function(
+              ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>)>();
 
-  /// from: public void <init>(T grandParentValue, S parentValue, U value)
+  /// from: public void <init>(com.github.dart_lang.jnigen.generics.GrandParent$Parent $parent, U newValue)
   /// The returned object must be deleted after use, by calling the `delete` method.
   factory GrandParent_Parent_Child(
-    $T grandParentValue,
-    $S parentValue,
-    $U value, {
+    GrandParent_Parent<$T, $S> $parent,
+    $U newValue, {
     jni.JObjType<$T>? T,
     jni.JObjType<$S>? S,
     jni.JObjType<$U>? U,
   }) {
     T ??= jni.lowestCommonSuperType([
-      grandParentValue.$type,
+      ($parent.$type as $GrandParent_ParentType).T,
     ]) as jni.JObjType<$T>;
     S ??= jni.lowestCommonSuperType([
-      parentValue.$type,
+      ($parent.$type as $GrandParent_ParentType).S,
     ]) as jni.JObjType<$S>;
     U ??= jni.lowestCommonSuperType([
-      value.$type,
+      newValue.$type,
     ]) as jni.JObjType<$U>;
     return GrandParent_Parent_Child.fromRef(
-        T,
-        S,
-        U,
-        _ctor(grandParentValue.reference, parentValue.reference,
-                value.reference)
-            .object);
+        T, S, U, _ctor($parent.reference, newValue.reference).object);
   }
 }
 
@@ -2006,16 +1998,17 @@
 
   static final _ctor = jniLookup<
               ffi.NativeFunction<
-                  jni.JniResult Function(
+                  jni.JniResult Function(ffi.Pointer<ffi.Void>,
                       ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>)>>(
           "GrandParent_StaticParent_Child__ctor")
       .asFunction<
-          jni.JniResult Function(
-              ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>)>();
+          jni.JniResult Function(ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>,
+              ffi.Pointer<ffi.Void>)>();
 
-  /// from: public void <init>(S parentValue, U value)
+  /// from: public void <init>(com.github.dart_lang.jnigen.generics.GrandParent$StaticParent $parent, S parentValue, U value)
   /// The returned object must be deleted after use, by calling the `delete` method.
   factory GrandParent_StaticParent_Child(
+    GrandParent_StaticParent<$S> $parent,
     $S parentValue,
     $U value, {
     jni.JObjType<$S>? S,
@@ -2023,12 +2016,16 @@
   }) {
     S ??= jni.lowestCommonSuperType([
       parentValue.$type,
+      ($parent.$type as $GrandParent_StaticParentType).S,
     ]) as jni.JObjType<$S>;
     U ??= jni.lowestCommonSuperType([
       value.$type,
     ]) as jni.JObjType<$U>;
     return GrandParent_StaticParent_Child.fromRef(
-        S, U, _ctor(parentValue.reference, value.reference).object);
+        S,
+        U,
+        _ctor($parent.reference, parentValue.reference, value.reference)
+            .object);
   }
 }
 
@@ -2270,15 +2267,18 @@
 
   static final _ctor = jniLookup<
           ffi.NativeFunction<
-              jni.JniResult Function(ffi.Pointer<ffi.Void>,
+              jni.JniResult Function(
+                  ffi.Pointer<ffi.Void>,
+                  ffi.Pointer<ffi.Void>,
                   ffi.Pointer<ffi.Void>)>>("MyMap_MyEntry__ctor")
       .asFunction<
-          jni.JniResult Function(
-              ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>)>();
+          jni.JniResult Function(ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>,
+              ffi.Pointer<ffi.Void>)>();
 
-  /// from: public void <init>(K key, V value)
+  /// from: public void <init>(com.github.dart_lang.jnigen.generics.MyMap $parent, K key, V value)
   /// The returned object must be deleted after use, by calling the `delete` method.
   factory MyMap_MyEntry(
+    MyMap<$K, $V> $parent,
     $K key,
     $V value, {
     jni.JObjType<$K>? K,
@@ -2286,12 +2286,14 @@
   }) {
     K ??= jni.lowestCommonSuperType([
       key.$type,
+      ($parent.$type as $MyMapType).K,
     ]) as jni.JObjType<$K>;
     V ??= jni.lowestCommonSuperType([
       value.$type,
+      ($parent.$type as $MyMapType).V,
     ]) as jni.JObjType<$V>;
     return MyMap_MyEntry.fromRef(
-        K, V, _ctor(key.reference, value.reference).object);
+        K, V, _ctor($parent.reference, key.reference, value.reference).object);
   }
 }
 
diff --git a/pkgs/jnigen/test/simple_package_test/dart_only/dart_bindings/simple_package.dart b/pkgs/jnigen/test/simple_package_test/dart_only/dart_bindings/simple_package.dart
index 76effbb..fb92e1d 100644
--- a/pkgs/jnigen/test/simple_package_test/dart_only/dart_bindings/simple_package.dart
+++ b/pkgs/jnigen/test/simple_package_test/dart_only/dart_bindings/simple_package.dart
@@ -1459,27 +1459,29 @@
       jni.Jni.env.SetObjectField(reference, _id_value, value.reference);
 
   static final _id_ctor = jni.Jni.accessors.getMethodIDOf(
-      _class.reference, r"<init>", r"(Ljava/lang/Object;Ljava/lang/Object;)V");
+      _class.reference,
+      r"<init>",
+      r"(Lcom/github/dart_lang/jnigen/generics/GrandParent;Ljava/lang/Object;)V");
 
-  /// from: public void <init>(T parentValue, S value)
+  /// from: public void <init>(com.github.dart_lang.jnigen.generics.GrandParent $parent, S newValue)
   /// The returned object must be deleted after use, by calling the `delete` method.
   factory GrandParent_Parent(
-    $T parentValue,
-    $S value, {
+    GrandParent<$T> $parent,
+    $S newValue, {
     jni.JObjType<$T>? T,
     jni.JObjType<$S>? S,
   }) {
     T ??= jni.lowestCommonSuperType([
-      parentValue.$type,
+      ($parent.$type as $GrandParentType).T,
     ]) as jni.JObjType<$T>;
     S ??= jni.lowestCommonSuperType([
-      value.$type,
+      newValue.$type,
     ]) as jni.JObjType<$S>;
     return GrandParent_Parent.fromRef(
         T,
         S,
         jni.Jni.accessors.newObjectWithArgs(_class.reference, _id_ctor,
-            [parentValue.reference, value.reference]).object);
+            [$parent.reference, newValue.reference]).object);
   }
 }
 
@@ -1605,37 +1607,35 @@
   set value($U value) =>
       jni.Jni.env.SetObjectField(reference, _id_value, value.reference);
 
-  static final _id_ctor = jni.Jni.accessors.getMethodIDOf(_class.reference,
-      r"<init>", r"(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V");
+  static final _id_ctor = jni.Jni.accessors.getMethodIDOf(
+      _class.reference,
+      r"<init>",
+      r"(Lcom/github/dart_lang/jnigen/generics/GrandParent$Parent;Ljava/lang/Object;)V");
 
-  /// from: public void <init>(T grandParentValue, S parentValue, U value)
+  /// from: public void <init>(com.github.dart_lang.jnigen.generics.GrandParent$Parent $parent, U newValue)
   /// The returned object must be deleted after use, by calling the `delete` method.
   factory GrandParent_Parent_Child(
-    $T grandParentValue,
-    $S parentValue,
-    $U value, {
+    GrandParent_Parent<$T, $S> $parent,
+    $U newValue, {
     jni.JObjType<$T>? T,
     jni.JObjType<$S>? S,
     jni.JObjType<$U>? U,
   }) {
     T ??= jni.lowestCommonSuperType([
-      grandParentValue.$type,
+      ($parent.$type as $GrandParent_ParentType).T,
     ]) as jni.JObjType<$T>;
     S ??= jni.lowestCommonSuperType([
-      parentValue.$type,
+      ($parent.$type as $GrandParent_ParentType).S,
     ]) as jni.JObjType<$S>;
     U ??= jni.lowestCommonSuperType([
-      value.$type,
+      newValue.$type,
     ]) as jni.JObjType<$U>;
     return GrandParent_Parent_Child.fromRef(
         T,
         S,
         U,
-        jni.Jni.accessors.newObjectWithArgs(_class.reference, _id_ctor, [
-          grandParentValue.reference,
-          parentValue.reference,
-          value.reference
-        ]).object);
+        jni.Jni.accessors.newObjectWithArgs(_class.reference, _id_ctor,
+            [$parent.reference, newValue.reference]).object);
   }
 }
 
@@ -1838,11 +1838,14 @@
       jni.Jni.env.SetObjectField(reference, _id_value, value.reference);
 
   static final _id_ctor = jni.Jni.accessors.getMethodIDOf(
-      _class.reference, r"<init>", r"(Ljava/lang/Object;Ljava/lang/Object;)V");
+      _class.reference,
+      r"<init>",
+      r"(Lcom/github/dart_lang/jnigen/generics/GrandParent$StaticParent;Ljava/lang/Object;Ljava/lang/Object;)V");
 
-  /// from: public void <init>(S parentValue, U value)
+  /// from: public void <init>(com.github.dart_lang.jnigen.generics.GrandParent$StaticParent $parent, S parentValue, U value)
   /// The returned object must be deleted after use, by calling the `delete` method.
   factory GrandParent_StaticParent_Child(
+    GrandParent_StaticParent<$S> $parent,
     $S parentValue,
     $U value, {
     jni.JObjType<$S>? S,
@@ -1850,6 +1853,7 @@
   }) {
     S ??= jni.lowestCommonSuperType([
       parentValue.$type,
+      ($parent.$type as $GrandParent_StaticParentType).S,
     ]) as jni.JObjType<$S>;
     U ??= jni.lowestCommonSuperType([
       value.$type,
@@ -1857,8 +1861,11 @@
     return GrandParent_StaticParent_Child.fromRef(
         S,
         U,
-        jni.Jni.accessors.newObjectWithArgs(_class.reference, _id_ctor,
-            [parentValue.reference, value.reference]).object);
+        jni.Jni.accessors.newObjectWithArgs(_class.reference, _id_ctor, [
+          $parent.reference,
+          parentValue.reference,
+          value.reference
+        ]).object);
   }
 }
 
@@ -2084,11 +2091,14 @@
       jni.Jni.env.SetObjectField(reference, _id_value, value.reference);
 
   static final _id_ctor = jni.Jni.accessors.getMethodIDOf(
-      _class.reference, r"<init>", r"(Ljava/lang/Object;Ljava/lang/Object;)V");
+      _class.reference,
+      r"<init>",
+      r"(Lcom/github/dart_lang/jnigen/generics/MyMap;Ljava/lang/Object;Ljava/lang/Object;)V");
 
-  /// from: public void <init>(K key, V value)
+  /// from: public void <init>(com.github.dart_lang.jnigen.generics.MyMap $parent, K key, V value)
   /// The returned object must be deleted after use, by calling the `delete` method.
   factory MyMap_MyEntry(
+    MyMap<$K, $V> $parent,
     $K key,
     $V value, {
     jni.JObjType<$K>? K,
@@ -2096,15 +2106,17 @@
   }) {
     K ??= jni.lowestCommonSuperType([
       key.$type,
+      ($parent.$type as $MyMapType).K,
     ]) as jni.JObjType<$K>;
     V ??= jni.lowestCommonSuperType([
       value.$type,
+      ($parent.$type as $MyMapType).V,
     ]) as jni.JObjType<$V>;
     return MyMap_MyEntry.fromRef(
         K,
         V,
         jni.Jni.accessors.newObjectWithArgs(_class.reference, _id_ctor,
-            [key.reference, value.reference]).object);
+            [$parent.reference, key.reference, value.reference]).object);
   }
 }
 
diff --git a/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/generics/GrandParent.java b/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/generics/GrandParent.java
index a12675a..b703403 100644
--- a/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/generics/GrandParent.java
+++ b/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/generics/GrandParent.java
@@ -12,11 +12,11 @@
   }
 
   public Parent<String> stringParent() {
-    return new Parent<>(value, "Hello");
+    return new Parent<>("Hello");
   }
 
   public <S> Parent<S> varParent(S nestedValue) {
-    return new Parent<>(value, nestedValue);
+    return new Parent<>(nestedValue);
   }
 
   public static StaticParent<String> stringStaticParent() {
@@ -54,9 +54,9 @@
     public T parentValue;
     public S value;
 
-    public Parent(T parentValue, S value) {
-      this.parentValue = parentValue;
-      this.value = value;
+    public Parent(S newValue) {
+      parentValue = GrandParent.this.value;
+      value = newValue;
     }
 
     public class Child<U> {
@@ -64,10 +64,10 @@
       public S parentValue;
       public U value;
 
-      public Child(T grandParentValue, S parentValue, U value) {
-        this.grandParentValue = grandParentValue;
-        this.parentValue = parentValue;
-        this.value = value;
+      public Child(U newValue) {
+        this.grandParentValue = GrandParent.this.value;
+        this.parentValue = Parent.this.value;
+        this.value = newValue;
       }
     }
   }
diff --git a/pkgs/jnigen/test/simple_package_test/runtime_test_registrant.dart b/pkgs/jnigen/test/simple_package_test/runtime_test_registrant.dart
index 6472985..f3075ac 100644
--- a/pkgs/jnigen/test/simple_package_test/runtime_test_registrant.dart
+++ b/pkgs/jnigen/test/simple_package_test/runtime_test_registrant.dart
@@ -417,6 +417,22 @@
         });
       });
     });
+    test('Constructing non-static nested classes', () {
+      using((arena) {
+        final grandParent = GrandParent(1.toJInteger())..deletedIn(arena);
+        final parent = GrandParent_Parent(grandParent, 2.toJInteger())
+          ..deletedIn(arena);
+        final child = GrandParent_Parent_Child(parent, 3.toJInteger())
+          ..deletedIn(arena);
+        expect(grandParent.value.intValue(deleteOriginal: true), 1);
+        expect(parent.parentValue.intValue(deleteOriginal: true), 1);
+        expect(parent.value.intValue(deleteOriginal: true), 2);
+        expect(child.grandParentValue.intValue(deleteOriginal: true), 1);
+        expect(child.parentValue.intValue(deleteOriginal: true), 2);
+        expect(child.value.intValue(deleteOriginal: true), 3);
+      });
+    });
+
     group('Generic type inference', () {
       test('MyStack.of1', () {
         using((arena) {