From acfbced1dc8ac8d0b450fcc672901970072d598a Mon Sep 17 00:00:00 2001 From: Jason Simmons Date: Tue, 12 Jan 2016 09:52:59 -0800 Subject: [PATCH] JNI bridge support for constructors, arrays, and strings --- sky/engine/bindings/jni/dart_jni.cc | 243 +++++++++++++++++++++++++++- sky/engine/bindings/jni/dart_jni.h | 65 +++++++- sky/engine/bindings/jni/jni.dart | 24 +++ 3 files changed, 321 insertions(+), 11 deletions(-) diff --git a/sky/engine/bindings/jni/dart_jni.cc b/sky/engine/bindings/jni/dart_jni.cc index 7d89605b5..f2ef8c9a4 100644 --- a/sky/engine/bindings/jni/dart_jni.cc +++ b/sky/engine/bindings/jni/dart_jni.cc @@ -9,6 +9,7 @@ #include "base/logging.h" #include "base/android/jni_android.h" #include "base/android/jni_string.h" +#include "base/strings/string_util.h" #include "sky/engine/tonic/dart_args.h" #include "sky/engine/tonic/dart_binding_macros.h" #include "sky/engine/tonic/dart_converter.h" @@ -65,14 +66,22 @@ bool CheckDartException(Dart_Handle result, Dart_Handle* exception) { DART_NATIVE_CALLBACK_STATIC(JniClass, FromName); #define FOR_EACH_BINDING(V) \ + V(JniArray, GetLength) \ V(JniClass, CallStaticLongMethod) \ + V(JniClass, NewObject) \ V(JniClass, GetFieldId) \ V(JniClass, GetMethodId) \ V(JniClass, GetStaticFieldId) \ V(JniClass, GetStaticIntField) \ V(JniClass, GetStaticMethodId) \ V(JniClass, GetStaticObjectField) \ - V(JniObject, GetIntField) + V(JniObject, CallBooleanMethod) \ + V(JniObject, CallIntMethod) \ + V(JniObject, CallObjectMethod) \ + V(JniObject, GetIntField) \ + V(JniObjectArray, GetArrayElement) \ + V(JniObjectArray, SetArrayElement) \ + V(JniString, GetText) FOR_EACH_BINDING(DART_NATIVE_CALLBACK) @@ -94,7 +103,8 @@ void DartJni::InitForIsolate() { } ScopedJavaGlobalRef DartJni::class_loader_; -jmethodID DartJni::load_class_method_id_; +jmethodID DartJni::class_loader_load_class_method_id_; +jmethodID DartJni::class_get_name_method_id_; bool DartJni::InitJni() { JNIEnv* env = base::android::AttachCurrentThread(); @@ -105,24 +115,44 @@ bool DartJni::InitJni() { env, env->FindClass("java/lang/ClassLoader")); CHECK(!base::android::ClearException(env)); - load_class_method_id_ = env->GetMethodID( + class_loader_load_class_method_id_ = env->GetMethodID( class_loader_clazz.obj(), "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); CHECK(!base::android::ClearException(env)); + ScopedJavaLocalRef class_clazz( + env, env->FindClass("java/lang/Class")); + CHECK(!base::android::ClearException(env)); + + class_get_name_method_id_ = env->GetMethodID( + class_clazz.obj(), + "getName", + "()Ljava/lang/String;"); + CHECK(!base::android::ClearException(env)); + return true; } ScopedJavaLocalRef DartJni::GetClass(JNIEnv* env, const char* name) { jobject clazz = env->CallObjectMethod( class_loader_.obj(), - load_class_method_id_, + class_loader_load_class_method_id_, base::android::ConvertUTF8ToJavaString(env, name).obj()); return ScopedJavaLocalRef(env, static_cast(clazz)); } +std::string DartJni::GetObjectClassName(JNIEnv* env, jobject obj) { + jclass clazz = env->GetObjectClass(obj); + DCHECK(clazz); + jstring name = static_cast( + env->CallObjectMethod(clazz, class_get_name_method_id_)); + DCHECK(name); + + return base::android::ConvertJavaStringToUTF8(env, name); +} + class JniMethodArgs { public: void Convert(JNIEnv* env, @@ -286,7 +316,27 @@ PassRefPtr JniClass::GetStaticObjectField(jfieldID fieldId) { jobject obj = env->GetStaticObjectField(clazz_.obj(), fieldId); if (CheckJniException(env, &exception)) goto fail; - return JniObject::create(env, obj); + return JniObject::Create(env, obj); + } +fail: + Dart_ThrowException(exception); + ASSERT_NOT_REACHED(); +} + +PassRefPtr JniClass::NewObject(jmethodID methodId, + const Vector& args) { + Dart_Handle exception = nullptr; + { + ENTER_JNI(); + + JniMethodArgs java_args; + java_args.Convert(env, args, &exception); + if (exception) goto fail; + + jobject obj = env->NewObjectA(clazz_.obj(), methodId, java_args.jvalues()); + if (CheckJniException(env, &exception)) goto fail; + + return JniObject::Create(env, obj); } fail: Dart_ThrowException(exception); @@ -323,8 +373,20 @@ JniObject::JniObject(JNIEnv* env, jobject object) JniObject::~JniObject() { } -PassRefPtr JniObject::create(JNIEnv* env, jobject object) { - return adoptRef(new JniObject(env, object)); +PassRefPtr JniObject::Create(JNIEnv* env, jobject object) { + std::string class_name = DartJni::GetObjectClassName(env, object); + + JniObject* result; + + if (class_name == "java.lang.String") { + result = new JniString(env, static_cast(object)); + } else if (base::StartsWith(class_name, "[L", base::CompareCase::SENSITIVE)) { + result = new JniObjectArray(env, static_cast(object)); + } else { + result = new JniObject(env, object); + } + + return adoptRef(result); } jint JniObject::GetIntField(jfieldID fieldId) { @@ -332,7 +394,7 @@ jint JniObject::GetIntField(jfieldID fieldId) { { ENTER_JNI(); - jint result = env->GetIntField(object_.obj(), fieldId); + jint result = env->GetIntField(java_object(), fieldId); if (CheckJniException(env, &exception)) goto fail; return result; @@ -342,4 +404,169 @@ fail: ASSERT_NOT_REACHED(); } +PassRefPtr JniObject::CallObjectMethod( + jmethodID methodId, + const Vector& args) { + Dart_Handle exception = nullptr; + { + ENTER_JNI(); + + JniMethodArgs java_args; + java_args.Convert(env, args, &exception); + if (exception) goto fail; + + jobject result = env->CallObjectMethodA(java_object(), methodId, + java_args.jvalues()); + if (CheckJniException(env, &exception)) goto fail; + + return JniObject::Create(env, result); + } +fail: + Dart_ThrowException(exception); + ASSERT_NOT_REACHED(); +} + +bool JniObject::CallBooleanMethod(jmethodID methodId, + const Vector& args) { + Dart_Handle exception = nullptr; + { + ENTER_JNI(); + + JniMethodArgs java_args; + java_args.Convert(env, args, &exception); + if (exception) goto fail; + + jboolean result = env->CallBooleanMethodA(java_object(), methodId, + java_args.jvalues()); + if (CheckJniException(env, &exception)) goto fail; + + return result == JNI_TRUE; + } +fail: + Dart_ThrowException(exception); + ASSERT_NOT_REACHED(); +} + +jint JniObject::CallIntMethod(jmethodID methodId, + const Vector& args) { + Dart_Handle exception = nullptr; + { + ENTER_JNI(); + + JniMethodArgs java_args; + java_args.Convert(env, args, &exception); + if (exception) goto fail; + + jint result = env->CallIntMethodA(java_object(), methodId, + java_args.jvalues()); + if (CheckJniException(env, &exception)) goto fail; + + return result; + } +fail: + Dart_ThrowException(exception); + ASSERT_NOT_REACHED(); +} + +IMPLEMENT_WRAPPERTYPEINFO(jni, JniString); + +JniString::JniString(JNIEnv* env, jstring object) + : JniObject(env, object) {} + +JniString::~JniString() { +} + +jstring JniString::java_string() { + return static_cast(java_object()); +} + +String JniString::GetText() { + Dart_Handle exception = nullptr; + { + ENTER_JNI(); + + jsize length = env->GetStringLength(java_string()); + if (CheckJniException(env, &exception)) goto fail; + + const jchar* chars = env->GetStringChars(java_string(), NULL); + if (CheckJniException(env, &exception)) goto fail; + + String result(chars, length); + env->ReleaseStringChars(java_string(), chars); + + return result; + } +fail: + Dart_ThrowException(exception); + ASSERT_NOT_REACHED(); +} + +IMPLEMENT_WRAPPERTYPEINFO(jni, JniArray); + +JniArray::JniArray(JNIEnv* env, jarray array) + : JniObject(env, array) {} + +JniArray::~JniArray() { +} + +jsize JniArray::GetLength() { + Dart_Handle exception = nullptr; + { + ENTER_JNI(); + + jsize result = env->GetArrayLength(java_array()); + if (CheckJniException(env, &exception)) goto fail; + + return result; + } +fail: + Dart_ThrowException(exception); + ASSERT_NOT_REACHED(); +} + +template +JArrayType JniArray::java_array() const { + return static_cast(java_object()); +} + +IMPLEMENT_WRAPPERTYPEINFO(jni, JniObjectArray); + +JniObjectArray::JniObjectArray(JNIEnv* env, jobjectArray array) + : JniArray(env, array) {} + +JniObjectArray::~JniObjectArray() { +} + +PassRefPtr JniObjectArray::GetArrayElement(jsize index) { + Dart_Handle exception = nullptr; + { + ENTER_JNI(); + + jobject obj = env->GetObjectArrayElement(java_array(), + index); + if (CheckJniException(env, &exception)) goto fail; + + return JniObject::Create(env, obj); + } +fail: + Dart_ThrowException(exception); + ASSERT_NOT_REACHED(); +} + +void JniObjectArray::SetArrayElement(jsize index, const JniObject* value) { + Dart_Handle exception = nullptr; + { + ENTER_JNI(); + + env->SetObjectArrayElement(java_array(), index, + value->java_object()); + if (CheckJniException(env, &exception)) goto fail; + + return; + } +fail: + Dart_ThrowException(exception); + ASSERT_NOT_REACHED(); +} + } // namespace blink diff --git a/sky/engine/bindings/jni/dart_jni.h b/sky/engine/bindings/jni/dart_jni.h index a56f9fcf8..2f2c6da50 100644 --- a/sky/engine/bindings/jni/dart_jni.h +++ b/sky/engine/bindings/jni/dart_jni.h @@ -23,9 +23,12 @@ class DartJni { static base::android::ScopedJavaLocalRef GetClass( JNIEnv* env, const char* name); + static std::string GetObjectClassName(JNIEnv* env, jobject obj); + private: static base::android::ScopedJavaGlobalRef class_loader_; - static jmethodID load_class_method_id_; + static jmethodID class_loader_load_class_method_id_; + static jmethodID class_get_name_method_id_; }; class JniObject; @@ -47,6 +50,9 @@ class JniClass : public RefCounted, public DartWrappable { jint GetStaticIntField(jfieldID fieldId); PassRefPtr GetStaticObjectField(jfieldID fieldId); + PassRefPtr NewObject(jmethodID methodId, + const Vector& args); + jlong CallStaticLongMethod(jmethodID methodId, const Vector& args); @@ -63,16 +69,69 @@ class JniObject : public RefCounted, public DartWrappable { public: ~JniObject() override; - static PassRefPtr create(JNIEnv* env, jobject object); + static PassRefPtr Create(JNIEnv* env, jobject object); + + jobject java_object() const { return object_.obj(); } jint GetIntField(jfieldID fieldId); - private: + PassRefPtr CallObjectMethod(jmethodID methodId, + const Vector& args); + bool CallBooleanMethod(jmethodID methodId, + const Vector& args); + jint CallIntMethod(jmethodID methodId, + const Vector& args); + + protected: JniObject(JNIEnv* env, jobject object); base::android::ScopedJavaGlobalRef object_; }; +// Wrapper for a JNI string +class JniString : public JniObject { + DEFINE_WRAPPERTYPEINFO(); + friend class JniObject; + + public: + ~JniString() override; + + String GetText(); + + private: + JniString(JNIEnv* env, jstring string); + + jstring java_string(); +}; + +// Wrapper for a JNI array +class JniArray : public JniObject { + DEFINE_WRAPPERTYPEINFO(); + + public: + ~JniArray() override; + + jsize GetLength(); + + protected: + JniArray(JNIEnv* env, jarray array); + template JArrayType java_array() const; +}; + +class JniObjectArray : public JniArray { + DEFINE_WRAPPERTYPEINFO(); + friend class JniObject; + + public: + ~JniObjectArray() override; + + PassRefPtr GetArrayElement(jsize index); + void SetArrayElement(jsize index, const JniObject* value); + + private: + JniObjectArray(JNIEnv* env, jobjectArray array); +}; + template <> struct DartConverter { static jfieldID FromArguments(Dart_NativeArguments args, diff --git a/sky/engine/bindings/jni/jni.dart b/sky/engine/bindings/jni/jni.dart index c086b4f9a..fc3537c2d 100644 --- a/sky/engine/bindings/jni/jni.dart +++ b/sky/engine/bindings/jni/jni.dart @@ -4,6 +4,7 @@ library dart_jni; +import 'dart:collection'; import 'dart:nativewrappers'; /// Wrapper for a Java class accessed via JNI. @@ -18,10 +19,33 @@ class JniClass extends NativeFieldWrapperClass2 { int getStaticIntField(int fieldId) native 'JniClass_GetStaticIntField'; JniObject getStaticObjectField(int fieldId) native 'JniClass_GetStaticObjectField'; + JniObject newObject(int methodId, List args) native 'JniClass_NewObject'; + int callStaticLongMethod(int methodId, List args) native 'JniClass_CallStaticLongMethod'; } /// Wrapper for a Java object accessed via JNI. class JniObject extends NativeFieldWrapperClass2 { int getIntField(String name, String sig) native 'JniObject_getIntField'; + + JniObject callObjectMethod(int methodId, List args) native 'JniObject_CallObjectMethod'; + bool callBooleanMethod(int methodId, List args) native 'JniObject_CallBooleanMethod'; + int callIntMethod(int methodId, List args) native 'JniObject_CallIntMethod'; +} + +/// Wrapper for a Java string. +class JniString extends JniObject { + // Retrieve the value as a Dart string. + String get text native 'JniString_GetText'; +} + +/// Wrapper for a Java array. +class JniArray extends JniObject { + int get length native 'JniArray_GetLength'; +} + +class JniObjectArray extends JniArray { + JniObject operator [](int index) native 'JniObjectArray_GetArrayElement'; + + void operator []=(int index, JniObject value) native 'JniObjectArray_SetArrayElement'; } -- GitLab