Kotlin: generalize native -> Kotlin field conversion.

This combines two conversions in method return (result when the return
type is a container vs plain value), and a conversion for callback
members.  It will also
enable the recursive conversion needed to convert whole structures for
b/350952562.

Bug: 352048981
Change-Id: Icb960f963f3adf720854077f4c39a735ccf73d7e
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/199634
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Jim Blackler <jimblackler@google.com>
diff --git a/generator/templates/art/api_jni_types.kt b/generator/templates/art/api_jni_types.kt
index f27cd22..3ef5851 100644
--- a/generator/templates/art/api_jni_types.kt
+++ b/generator/templates/art/api_jni_types.kt
@@ -84,3 +84,52 @@
         {{ unreachable_code('Unsupported type: ' + member.type.name.get()) }}
     {%- endif -%}
 {% endmacro %}
+
+{% macro convert_array_element_to_kotlin(input, output, size, member) %}
+    {% if member.type.category in ['bitmask', 'enum'] %}
+        //* Kotlin value classes do not get inlined in arrays, so the creation method is different.
+        jobject {{ output }};
+        {
+            jclass clz = env->FindClass("{{ jni_name(member.type) }}");
+            {{ output }} = env->NewObject(clz, env->GetMethodID(clz, "<init>", "(I)V"),
+                    static_cast<jint>({{ input }}));
+        }
+    {% else %}
+        {{ convert_to_kotlin(input, output, size, member) }}
+    {% endif %}
+{% endmacro %}
+
+{% macro convert_to_kotlin(input, output, size, member) %}
+    {% if member.type.name.get() in ['void const *', 'void *'] %}
+        jobject {{ output }} = toByteBuffer(env, {{ input }}, {{ size }});
+    {% elif size is string %}
+        //* Native container converted to a Kotlin container.
+        {% if member.type.category in ['bitmask', 'enum', 'object', 'structure'] %}
+            jobjectArray {{ output }} = env->NewObjectArray({{ size }},
+                    env->FindClass("{{ jni_name(member.type) }}"), 0);
+            for (int idx = 0; idx != {{ size }}; idx++) {
+                {{ convert_array_element_to_kotlin(input + '[idx]', 'element', None, {'type': member.type}) }}
+                env->SetObjectArrayElement({{ output }}, idx, element);
+            }
+        {% else %}
+            {{ unreachable_code() }}
+        {% endif %}
+    {% elif member.type.category == 'object' %}
+        jobject {{ output }};
+        {
+            jclass clz = env->FindClass("{{ jni_name(member.type) }}");
+            {{ output }} = env->NewObject(clz, env->GetMethodID(clz, "<init>", "(J)V"),
+                    reinterpret_cast<jlong>({{ input }}));
+        }
+    {% elif member.type.name.get() == 'void' %}
+        jlong {{ output }} = reinterpret_cast<jlong>({{ input }});
+    {% elif member.type.name.get() == 'char' %}
+        jstring {{ output }} = {{ input }} ? env->NewStringUTF({{ input }}) : nullptr;
+    {% elif member.type.category in ['bitmask', 'enum', 'native'] %}
+        //* We use Kotlin value classes for bitmask and enum, and they get inlined as lone values.
+        {{ to_jni_type(member.type) }} {{ output }} =
+                static_cast<{{ to_jni_type(member.type) }}>({{ input }});
+    {% else %}
+        {{ unreachable_code() }}
+    {% endif %}
+{% endmacro %}
diff --git a/generator/templates/art/methods.cpp b/generator/templates/art/methods.cpp
index ec4f0c4..cae1e36 100644
--- a/generator/templates/art/methods.cpp
+++ b/generator/templates/art/methods.cpp
@@ -24,7 +24,7 @@
 //* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 //* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 //* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-{% from 'art/api_jni_types.kt' import arg_to_jni_type, jni_signature, to_jni_type with context %}
+{% from 'art/api_jni_types.kt' import arg_to_jni_type, convert_to_kotlin, jni_signature, to_jni_type with context %}
 #include <jni.h>
 #include <stdlib.h>
 
@@ -180,6 +180,13 @@
                     return;
                 }
 
+                {%- for callbackArg in kotlin_record_members(arg.type.arguments) -%}
+                    {{ convert_to_kotlin(callbackArg.name.camelCase(),
+                                         '_' + callbackArg.name.camelCase(),
+                                         'input->' + callbackArg.length.name.camelCase() if callbackArg.length.name,
+                                         callbackArg) }}
+                {% endfor %}
+
                 //* Get the client (Kotlin) callback so we can call it.
                 jmethodID callbackMethod = env->GetMethodID(
                         env->FindClass("{{ jni_name(arg.type) }}"), "callback", "(
@@ -190,21 +197,7 @@
                 //* Call the callback with all converted parameters.
                 env->CallVoidMethod(userData1->callback, callbackMethod
                 {%- for callbackArg in kotlin_record_members(arg.type.arguments) %}
-                    ,
-                    {%- if callbackArg.type.category == 'object' %}
-                        env->NewObject(env->FindClass("{{ jni_name(callbackArg.type) }}"),
-                                env->GetMethodID(env->FindClass("{{ jni_name(callbackArg.type) }}"),
-                                        "<init>", "(J)V"),
-                                reinterpret_cast<jlong>({{ callbackArg.name.camelCase() }}))
-                    {%- elif callbackArg.type.category in ['bitmask', 'enum'] %}
-                        static_cast<jint>({{ callbackArg.name.camelCase() }})
-                    {%- elif callbackArg.type.category == 'structure' %}
-                        {{ unreachable_code () }}  //* We don't yet handle structures in callbacks.
-                    {%- elif callbackArg.type.name.get() == 'char' %}
-                        env->NewStringUTF({{ callbackArg.name.camelCase() }})
-                    {%- else %}
-                        {{ callbackArg.name.camelCase() }}
-                    {%- endif %}
+                    ,_{{ callbackArg.name.camelCase() }}
                 {%- endfor %});
             };
             //* TODO(b/330293719): free associated resources.
@@ -247,15 +240,6 @@
         if (env->ExceptionCheck()) {  //* Early out if client (Kotlin) callback threw an exception.
             return nullptr;
         }
-        //* Native container converted to a Kotlin container.
-        jclass returnClass = env->FindClass("{{ jni_name(_kotlin_return.type) }}");
-        jobjectArray result = env->NewObjectArray(size, returnClass, 0);
-        auto constructor = env->GetMethodID(returnClass, "<init>", "(I)V");
-        for (int idx = 0; idx != size; idx++) {
-            jobject element = env->NewObject(returnClass, constructor,
-                    static_cast<jint>(returnAllocation[idx]));
-            env->SetObjectArrayElement(result, idx, element);
-        }
     {% else %}
         {{ 'auto result =' if method.return_type.name.get() != 'void' }}
         {% if object %}
@@ -271,16 +255,12 @@
             return {{ '0' if method.return_type.name.get() != 'void' }};
         }
     {% endif %}
-
-    //* We only handle objects and primitives to be returned.
-    {% if method.return_type.category == 'object' %}
-        jclass returnClass = env->FindClass("{{ jni_name(method.return_type) }}");
-        auto constructor = env->GetMethodID(returnClass, "<init>", "(J)V");
-        return env->NewObject(returnClass, constructor, reinterpret_cast<jlong>(result));
-    {% elif method.return_type.name.get() in ['void const *', 'void *'] %}
-        return toByteBuffer(env, result, size);
-    {% elif method.return_type.name.get() != 'void' %}
-        return result;  //* Primitives are implicitly converted by JNI.
+    {% if _kotlin_return.type.name.get() != 'void' %}
+        {{ convert_to_kotlin(_kotlin_return.name.get() if _kotlin_return.annotation == '*' else 'result',
+                             'result_kt',
+                             'size' if _kotlin_return.type.name.get() in ['void const *', 'void *'] or _kotlin_return.annotation == '*',
+                             _kotlin_return) }}
+        return result_kt;
     {% endif %}
 } {% endmacro %}
 
diff --git a/src/dawn/dawn_kotlin.json b/src/dawn/dawn_kotlin.json
index 87d1449..3db8ed3 100644
--- a/src/dawn/dawn_kotlin.json
+++ b/src/dawn/dawn_kotlin.json
@@ -34,9 +34,11 @@
 
     "jni_primitives" : {
         "bool": "jboolean",
+        "double": "jdouble",
         "float": "jfloat",
         "int32_t": "jint",
         "size_t": "jlong",
+        "uint16_t": "jshort",
         "uint32_t": "jint",
         "uint64_t": "jlong",
         "void *": "jobject",