[jnigen] Generate JValue wrapper classes in pure Dart bindings (https://github.com/dart-lang/jnigen/issues/212)

* closes https://github.com/dart-lang/jnigen/issues/221
diff --git a/pkgs/jni/example/integration_test/on_device_jni_test.dart b/pkgs/jni/example/integration_test/on_device_jni_test.dart
index 2bc0d9a..3b547ad 100644
--- a/pkgs/jni/example/integration_test/on_device_jni_test.dart
+++ b/pkgs/jni/example/integration_test/on_device_jni_test.dart
@@ -39,7 +39,7 @@
   testWidgets("call a static method using JniClass APIs", (t) async {
     final integerClass = JniClass.fromRef(Jni.findClass("java/lang/Integer"));
     final result = integerClass.callStaticMethodByName<JString>(
-        "toHexString", "(I)Ljava/lang/String;", [31]);
+        "toHexString", "(I)Ljava/lang/String;", [JValueInt(31)]);
 
     final resultString = result.toDartString();
 
@@ -58,10 +58,9 @@
     final nextIntMethod = random.getMethodID("nextInt", "(I)I");
 
     for (int i = 0; i < 100; i++) {
-      int r = random.callMethod<int>(nextIntMethod, [256 * 256]);
+      int r = random.callMethod<int>(nextIntMethod, [JValueInt(256 * 256)]);
       int bits = 0;
-      final jbc =
-          longClass.callStaticMethod<int>(bitCountMethod, [JValueLong(r)]);
+      final jbc = longClass.callStaticMethod<int>(bitCountMethod, [r]);
       while (r != 0) {
         bits += r % 2;
         r = (r / 2).floor();
@@ -74,8 +73,8 @@
 
   // Actually it's not even required to get a reference to class
   testWidgets("invoke_", (t) async {
-    final m = Jni.invokeStaticMethod<int>("java/lang/Long", "min", "(JJ)J",
-        [JValueLong(1234), JValueLong(1324)], JniCallType.longType);
+    final m = Jni.invokeStaticMethod<int>(
+        "java/lang/Long", "min", "(JJ)J", [1234, 1324], JniCallType.longType);
     expect(m, equals(1234));
   });
 
@@ -98,7 +97,7 @@
     final longClass = Jni.findJniClass("java/lang/Long");
     const n = 1223334444;
     final strFromJava = longClass.callStaticMethodByName<String>(
-        "toOctalString", "(J)Ljava/lang/String;", [JValueLong(n)]);
+        "toOctalString", "(J)Ljava/lang/String;", [n]);
     expect(strFromJava, equals(n.toRadixString(8)));
     longClass.delete();
   });
@@ -110,8 +109,9 @@
   });
 
   testWidgets("use() method", (t) async {
-    final randomInt = Jni.newInstance("java/util/Random", "()V", [])
-        .use((random) => random.callMethodByName<int>("nextInt", "(I)I", [15]));
+    final randomInt = Jni.newInstance("java/util/Random", "()V", []).use(
+        (random) =>
+            random.callMethodByName<int>("nextInt", "(I)I", [JValueInt(15)]));
     expect(randomInt, lessThan(15));
   });
 
diff --git a/pkgs/jni/lib/src/jni.dart b/pkgs/jni/lib/src/jni.dart
index b165f8e..4d5a2ab 100644
--- a/pkgs/jni/lib/src/jni.dart
+++ b/pkgs/jni/lib/src/jni.dart
@@ -214,9 +214,9 @@
 
   /// Converts passed arguments to JValue array.
   ///
-  /// int, bool, double and JObject types are converted out of the box.
-  /// Wrap values in types such as [JValueLong] to convert to other primitive
-  /// types such as `long`, `short` and `char`.
+  /// long, bool, double and JObject types are converted out of the box.
+  /// Wrap values in types such as [JValueInt] to convert to other primitive
+  /// types such as `int`, `short` and `char`.
   static Pointer<JValue> jvalues(List<dynamic> args,
       {Allocator allocator = calloc}) {
     return toJValues(args, allocator: allocator);
diff --git a/pkgs/jni/lib/src/jvalues.dart b/pkgs/jni/lib/src/jvalues.dart
index 8718bb1..ce0cac6 100644
--- a/pkgs/jni/lib/src/jvalues.dart
+++ b/pkgs/jni/lib/src/jvalues.dart
@@ -17,7 +17,7 @@
 
   switch (arg.runtimeType) {
     case int:
-      pos.ref.i = arg;
+      pos.ref.j = arg;
       break;
     case bool:
       pos.ref.z = arg ? 1 : 0;
@@ -32,8 +32,8 @@
     case JValueFloat:
       pos.ref.f = (arg as JValueFloat).value;
       break;
-    case JValueLong:
-      pos.ref.j = (arg as JValueLong).value;
+    case JValueInt:
+      pos.ref.i = (arg as JValueInt).value;
       break;
     case JValueShort:
       pos.ref.s = (arg as JValueShort).value;
@@ -66,10 +66,10 @@
 }
 
 /// Use this class as wrapper to convert an integer
-/// to Java `long` in jvalues method.
-class JValueLong {
+/// to Java `int` in jvalues method.
+class JValueInt {
   int value;
-  JValueLong(this.value);
+  JValueInt(this.value);
 }
 
 /// Use this class as wrapper to convert an integer
diff --git a/pkgs/jni/test/exception_test.dart b/pkgs/jni/test/exception_test.dart
index c954678..c277b01 100644
--- a/pkgs/jni/test/exception_test.dart
+++ b/pkgs/jni/test/exception_test.dart
@@ -41,7 +41,7 @@
   test("Use after free throws exception", () {
     final r = Jni.newInstance("java/util/Random", "()V", []);
     r.delete();
-    expect(() => r.callMethodByName<int>("nextInt", "(I)I", [256]),
+    expect(() => r.callMethodByName<int>("nextInt", "(I)I", [JValueInt(256)]),
         throwsA(isA<UseAfterFreeException>()));
   });
 
@@ -57,13 +57,13 @@
     final r = Jni.newInstance("java/util/Random", "()V", []);
     expect(
         () => r.callMethodByName<int>(
-            "nextInt", "(I)I", [256], JniCallType.doubleType),
+            "nextInt", "(I)I", [JValueInt(256)], JniCallType.doubleType),
         throwsA(isA<InvalidCallTypeException>()));
   });
 
   test("An exception in JNI throws JniException in Dart", () {
     final r = Jni.newInstance("java/util/Random", "()V", []);
-    expect(() => r.callMethodByName<int>("nextInt", "(I)I", [-1]),
+    expect(() => r.callMethodByName<int>("nextInt", "(I)I", [JValueInt(-1)]),
         throwsA(isA<JniException>()));
   });
 }
diff --git a/pkgs/jni/test/jni_test.dart b/pkgs/jni/test/jni_test.dart
index a40ba81..19bae21 100644
--- a/pkgs/jni/test/jni_test.dart
+++ b/pkgs/jni/test/jni_test.dart
@@ -49,26 +49,26 @@
   });
 
   test(
-      'Manually lookup & call Long.toHexString',
+      'Manually lookup & call Integer.toHexString',
       () => using((arena) {
             // Method names on JniEnv* from C JNI API are capitalized
             // like in original, while other extension methods
             // follow Dart naming conventions.
-            final longClass =
-                env.FindClass("java/lang/Long".toNativeChars(arena));
+            final integerClass =
+                env.FindClass("java/lang/Integer".toNativeChars(arena));
             // Refer JNI spec on how to construct method signatures
             // Passing wrong signature leads to a segfault
             final hexMethod = env.GetStaticMethodID(
-                longClass,
+                integerClass,
                 "toHexString".toNativeChars(arena),
-                "(J)Ljava/lang/String;".toNativeChars(arena));
+                "(I)Ljava/lang/String;".toNativeChars(arena));
 
-            for (var i in [1, 80, 13, 76, 1134453224145]) {
+            for (var i in [1, 80, 13, 76, 11344]) {
               // if your argument is int, bool, or JObject (`Pointer<Void>`)
               // it can be directly placed in the list. To convert into different primitive
               // types, use JValue<Type> wrappers.
-              final jres = env.CallStaticObjectMethodA(longClass, hexMethod,
-                  Jni.jvalues([JValueLong(i)], allocator: arena));
+              final jres = env.CallStaticObjectMethodA(integerClass, hexMethod,
+                  Jni.jvalues([JValueInt(i)], allocator: arena));
 
               // use asDartString extension method on Pointer<JniEnv>
               // to convert a String jobject result to string
@@ -82,7 +82,7 @@
               // java class exists.
               env.DeleteGlobalRef(jres);
             }
-            env.DeleteGlobalRef(longClass);
+            env.DeleteGlobalRef(integerClass);
           }));
 
   test("asJString extension method", () {
diff --git a/pkgs/jni/test/jobject_test.dart b/pkgs/jni/test/jobject_test.dart
index 5bafe75..b055f7d 100644
--- a/pkgs/jni/test/jobject_test.dart
+++ b/pkgs/jni/test/jobject_test.dart
@@ -54,7 +54,7 @@
   test("call a static method using JniClass APIs", () {
     final integerClass = Jni.findJniClass("java/lang/Integer");
     final result = integerClass.callStaticMethodByName<JString>(
-        "toHexString", "(I)Ljava/lang/String;", [31]);
+        "toHexString", "(I)Ljava/lang/String;", [JValueInt(31)]);
 
     // if the object is supposed to be a Java string
     // you can call toDartString on it.
@@ -95,10 +95,9 @@
     final nextIntMethod = random.getMethodID("nextInt", "(I)I");
 
     for (int i = 0; i < 100; i++) {
-      int r = random.callMethod<int>(nextIntMethod, [256 * 256]);
+      int r = random.callMethod<int>(nextIntMethod, [JValueInt(256 * 256)]);
       int bits = 0;
-      final jbc =
-          longClass.callStaticMethod<int>(bitCountMethod, [JValueLong(r)]);
+      final jbc = longClass.callStaticMethod<int>(bitCountMethod, [r]);
       while (r != 0) {
         bits += r % 2;
         r = (r / 2).floor();
@@ -133,7 +132,7 @@
     final longClass = Jni.findJniClass("java/lang/Long");
     const n = 1223334444;
     final strFromJava = longClass.callStaticMethodByName<String>(
-        "toOctalString", "(J)Ljava/lang/String;", [JValueLong(n)]);
+        "toOctalString", "(J)Ljava/lang/String;", [n]);
     expect(strFromJava, equals(n.toRadixString(8)));
     longClass.delete();
   });
@@ -163,8 +162,9 @@
 
   // You can use() method on JObject for using once and deleting.
   test("use() method", () {
-    final randomInt = Jni.newInstance("java/util/Random", "()V", [])
-        .use((random) => random.callMethodByName<int>("nextInt", "(I)I", [15]));
+    final randomInt = Jni.newInstance("java/util/Random", "()V", []).use(
+        (random) =>
+            random.callMethodByName<int>("nextInt", "(I)I", [JValueInt(15)]));
     expect(randomInt, lessThan(15));
   });
 
diff --git a/pkgs/jnigen/lib/src/bindings/dart_generator.dart b/pkgs/jnigen/lib/src/bindings/dart_generator.dart
index 6ae3396..db53275 100644
--- a/pkgs/jnigen/lib/src/bindings/dart_generator.dart
+++ b/pkgs/jnigen/lib/src/bindings/dart_generator.dart
@@ -925,13 +925,15 @@
 
   String cCtor(Method node) {
     final name = node.finalName;
-    final params = node.params.accept(const _ParamCall()).join(', ');
+    final params =
+        node.params.accept(const _ParamCall(isCBased: true)).join(', ');
     return '_$name($params)';
   }
 
   String dartOnlyCtor(Method node) {
     final name = node.finalName;
-    final params = node.params.accept(const _ParamCall()).join(', ');
+    final params =
+        node.params.accept(const _ParamCall(isCBased: false)).join(', ');
     return '$_accessors.newObjectWithArgs($_classRef, _id_$name, [$params])';
   }
 
@@ -939,7 +941,7 @@
     final name = node.finalName;
     final params = [
       if (!node.isStatic) _selfPointer,
-      ...node.params.accept(const _ParamCall()),
+      ...node.params.accept(const _ParamCall(isCBased: true)),
     ].join(', ');
     final resultGetter = node.returnType.accept(const _JniResultGetter());
     return '_$name($params).$resultGetter';
@@ -950,7 +952,8 @@
     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 params =
+        node.params.accept(const _ParamCall(isCBased: false)).join(', ');
     final resultGetter = node.returnType.accept(const _JniResultGetter());
     return '$_accessors.call${ifStatic}MethodWithArgs($self, _id_$name, $callType, [$params]).$resultGetter';
   }
@@ -1105,11 +1108,43 @@
 /// void bar(Foo foo) => _bar(foo.reference);
 /// ```
 class _ParamCall extends Visitor<Param, String> {
-  const _ParamCall();
+  final bool isCBased;
+
+  const _ParamCall({required this.isCBased});
 
   @override
   String visit(Param node) {
     final nativeSuffix = node.type.accept(const _ToNativeSuffix());
-    return '${node.finalName}$nativeSuffix';
+    final paramCall = '${node.finalName}$nativeSuffix';
+    if (!isCBased) {
+      // We need to wrap [paramCall] in the appropriate wrapper class.
+      return node.type.accept(_JValueWrapper(paramCall));
+    }
+    return paramCall;
+  }
+}
+
+/// Wraps the parameter in the appropriate JValue wrapper class.
+///
+/// For instance, `int` in Dart can be mapped to `long` or `int` or ... in Java.
+/// The wrapper class is how we identify the type in pure dart bindings.
+class _JValueWrapper extends TypeVisitor<String> {
+  final String param;
+
+  _JValueWrapper(this.param);
+
+  @override
+  String visitNonPrimitiveType(ReferredType node) {
+    return param;
+  }
+
+  @override
+  String visitPrimitiveType(PrimitiveType node) {
+    if (node.name == 'long' ||
+        node.name == 'double' ||
+        node.name == 'boolean') {
+      return param;
+    }
+    return '$_jni.JValue${node.name.capitalize()}($param)';
   }
 }
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 9c8decb..899ab00 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
@@ -1018,10 +1018,11 @@
           jni.JArray<jni.JByte> data, int offset, int len) =>
       const jsonparser_.$JsonParserType().fromRef(jniAccessors
           .callMethodWithArgs(
-              reference,
-              _id_createParser5,
-              jni.JniCallType.objectType,
-              [data.reference, offset, len]).object);
+              reference, _id_createParser5, jni.JniCallType.objectType, [
+        data.reference,
+        jni.JValueInt(offset),
+        jni.JValueInt(len)
+      ]).object);
 
   static final _id_createParser6 = jniAccessors.getMethodIDOf(
       _classRef,
@@ -1065,10 +1066,11 @@
           jni.JArray<jni.JChar> content, int offset, int len) =>
       const jsonparser_.$JsonParserType().fromRef(jniAccessors
           .callMethodWithArgs(
-              reference,
-              _id_createParser8,
-              jni.JniCallType.objectType,
-              [content.reference, offset, len]).object);
+              reference, _id_createParser8, jni.JniCallType.objectType, [
+        content.reference,
+        jni.JValueInt(offset),
+        jni.JValueInt(len)
+      ]).object);
 
   static final _id_createParser9 = jniAccessors.getMethodIDOf(
       _classRef,
@@ -1414,10 +1416,11 @@
           jni.JArray<jni.JByte> data, int offset, int len) =>
       const jsonparser_.$JsonParserType().fromRef(jniAccessors
           .callMethodWithArgs(
-              reference,
-              _id_createJsonParser5,
-              jni.JniCallType.objectType,
-              [data.reference, offset, len]).object);
+              reference, _id_createJsonParser5, jni.JniCallType.objectType, [
+        data.reference,
+        jni.JValueInt(offset),
+        jni.JValueInt(len)
+      ]).object);
 
   static final _id_createJsonParser6 = jniAccessors.getMethodIDOf(
       _classRef,
@@ -1610,7 +1613,10 @@
 
   /// from: public boolean enabledIn(int flags)
   bool enabledIn(int flags) => jniAccessors.callMethodWithArgs(
-      reference, _id_enabledIn, jni.JniCallType.booleanType, [flags]).boolean;
+      reference,
+      _id_enabledIn,
+      jni.JniCallType.booleanType,
+      [jni.JValueInt(flags)]).boolean;
 
   static final _id_getMask =
       jniAccessors.getMethodIDOf(_classRef, r"getMask", r"()I");
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 c371560..ee9ceff 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
@@ -93,8 +93,8 @@
   /// from: protected void <init>(int features)
   /// The returned object must be deleted after use, by calling the `delete` method.
   JsonParser.ctor1(int features)
-      : super.fromRef(jniAccessors
-            .newObjectWithArgs(_classRef, _id_ctor1, [features]).object);
+      : super.fromRef(jniAccessors.newObjectWithArgs(
+            _classRef, _id_ctor1, [jni.JValueInt(features)]).object);
 
   static final _id_getCodec = jniAccessors.getMethodIDOf(
       _classRef, r"getCodec", r"()Lcom/fasterxml/jackson/core/ObjectCodec;");
@@ -658,8 +658,11 @@
   ///@since 2.3
   ///@deprecated Since 2.7, use \#overrideStdFeatures(int, int) instead
   JsonParser setFeatureMask(int mask) =>
-      const $JsonParserType().fromRef(jniAccessors.callMethodWithArgs(reference,
-          _id_setFeatureMask, jni.JniCallType.objectType, [mask]).object);
+      const $JsonParserType().fromRef(jniAccessors.callMethodWithArgs(
+          reference,
+          _id_setFeatureMask,
+          jni.JniCallType.objectType,
+          [jni.JValueInt(mask)]).object);
 
   static final _id_overrideStdFeatures = jniAccessors.getMethodIDOf(_classRef,
       r"overrideStdFeatures", r"(II)Lcom/fasterxml/jackson/core/JsonParser;");
@@ -684,7 +687,7 @@
           reference,
           _id_overrideStdFeatures,
           jni.JniCallType.objectType,
-          [values, mask]).object);
+          [jni.JValueInt(values), jni.JValueInt(mask)]).object);
 
   static final _id_getFormatFeatures =
       jniAccessors.getMethodIDOf(_classRef, r"getFormatFeatures", r"()I");
@@ -721,7 +724,7 @@
           reference,
           _id_overrideFormatFeatures,
           jni.JniCallType.objectType,
-          [values, mask]).object);
+          [jni.JValueInt(values), jni.JValueInt(mask)]).object);
 
   static final _id_nextToken = jniAccessors.getMethodIDOf(
       _classRef, r"nextToken", r"()Lcom/fasterxml/jackson/core/JsonToken;");
@@ -860,7 +863,7 @@
       reference,
       _id_nextIntValue,
       jni.JniCallType.intType,
-      [defaultValue]).integer;
+      [jni.JValueInt(defaultValue)]).integer;
 
   static final _id_nextLongValue =
       jniAccessors.getMethodIDOf(_classRef, r"nextLongValue", r"(J)J");
@@ -1058,8 +1061,8 @@
   ///@param id Token id to match (from (@link JsonTokenId})
   ///@return {@code True} if the parser current points to specified token
   ///@since 2.5
-  bool hasTokenId(int id) => jniAccessors.callMethodWithArgs(
-      reference, _id_hasTokenId, jni.JniCallType.booleanType, [id]).boolean;
+  bool hasTokenId(int id) => jniAccessors.callMethodWithArgs(reference,
+      _id_hasTokenId, jni.JniCallType.booleanType, [jni.JValueInt(id)]).boolean;
 
   static final _id_hasToken = jniAccessors.getMethodIDOf(
       _classRef, r"hasToken", r"(Lcom/fasterxml/jackson/core/JsonToken;)Z");
@@ -1798,7 +1801,10 @@
   ///@throws IOException for low-level read issues, or
   ///   JsonParseException for decoding problems
   int getValueAsInt1(int def) => jniAccessors.callMethodWithArgs(
-      reference, _id_getValueAsInt1, jni.JniCallType.intType, [def]).integer;
+      reference,
+      _id_getValueAsInt1,
+      jni.JniCallType.intType,
+      [jni.JValueInt(def)]).integer;
 
   static final _id_getValueAsLong =
       jniAccessors.getMethodIDOf(_classRef, r"getValueAsLong", r"()J");
@@ -2282,7 +2288,10 @@
 
   /// from: public boolean enabledIn(int flags)
   bool enabledIn(int flags) => jniAccessors.callMethodWithArgs(
-      reference, _id_enabledIn, jni.JniCallType.booleanType, [flags]).boolean;
+      reference,
+      _id_enabledIn,
+      jni.JniCallType.booleanType,
+      [jni.JValueInt(flags)]).boolean;
 
   static final _id_getMask =
       jniAccessors.getMethodIDOf(_classRef, r"getMask", r"()I");