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",