| // Copyright 2016 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "flutter/lib/jni/dart_jni.h" |
| |
| #include "base/android/jni_android.h" |
| #include "base/android/jni_string.h" |
| #include "base/logging.h" |
| #include "flutter/lib/jni/jni_api.h" |
| #include "flutter/lib/jni/jni_array.h" |
| #include "flutter/lib/jni/jni_class.h" |
| #include "flutter/lib/jni/jni_object.h" |
| #include "flutter/lib/jni/jni_string.h" |
| #include "lib/tonic/dart_args.h" |
| #include "lib/tonic/dart_binding_macros.h" |
| #include "lib/tonic/converter/dart_converter.h" |
| |
| namespace blink { |
| |
| using base::android::ScopedJavaGlobalRef; |
| using base::android::ScopedJavaLocalRef; |
| using tonic::DartConverter; |
| using tonic::StdStringToDart; |
| using tonic::ToDart; |
| |
| namespace { |
| |
| tonic::DartLibraryNatives* g_natives = nullptr; |
| |
| Dart_NativeFunction GetNativeFunction(Dart_Handle name, |
| int argument_count, |
| bool* auto_setup_scope) { |
| return g_natives->GetNativeFunction(name, argument_count, auto_setup_scope); |
| } |
| |
| const uint8_t* GetSymbol(Dart_NativeFunction native_function) { |
| return g_natives->GetSymbol(native_function); |
| } |
| |
| // Data cached from the Java VM. |
| struct DartJniJvmData { |
| ScopedJavaGlobalRef<jobject> class_loader; |
| ScopedJavaGlobalRef<jclass> class_clazz; |
| jmethodID class_loader_load_class_method_id; |
| jmethodID class_get_name_method_id; |
| }; |
| |
| DartJniJvmData* g_jvm_data = nullptr; |
| DartJniIsolateDataProvider g_isolate_data_provider = nullptr; |
| |
| DartJniIsolateData* IsolateData() { |
| return g_isolate_data_provider(); |
| } |
| |
| } // anonymous namespace |
| |
| // Check if a JNI API has thrown an exception. If so, convert it to a |
| // Dart exception. |
| bool CheckJniException(JNIEnv* env, Dart_Handle* exception) { |
| if (env->ExceptionCheck() == JNI_FALSE) |
| return false; |
| |
| jthrowable java_throwable = env->ExceptionOccurred(); |
| env->ExceptionClear(); |
| std::string info = base::android::GetJavaExceptionInfo(env, java_throwable); |
| |
| *exception = StdStringToDart(info); |
| return true; |
| } |
| |
| // Check if a Dart API returned an error handle. |
| bool CheckDartException(Dart_Handle result, Dart_Handle* exception) { |
| if (!Dart_IsError(result)) |
| return false; |
| |
| *exception = result; |
| return true; |
| } |
| |
| DART_NATIVE_CALLBACK_STATIC(JniApi, FromReflectedField); |
| DART_NATIVE_CALLBACK_STATIC(JniApi, FromReflectedMethod); |
| DART_NATIVE_CALLBACK_STATIC(JniApi, GetApplicationContext); |
| DART_NATIVE_CALLBACK_STATIC(JniApi, GetClassLoader); |
| DART_NATIVE_CALLBACK_STATIC(JniBooleanArray, Create); |
| DART_NATIVE_CALLBACK_STATIC(JniByteArray, Create); |
| DART_NATIVE_CALLBACK_STATIC(JniCharArray, Create); |
| DART_NATIVE_CALLBACK_STATIC(JniClass, FromName); |
| DART_NATIVE_CALLBACK_STATIC(JniClass, FromClassObject); |
| DART_NATIVE_CALLBACK_STATIC(JniDoubleArray, Create); |
| DART_NATIVE_CALLBACK_STATIC(JniFloatArray, Create); |
| DART_NATIVE_CALLBACK_STATIC(JniIntArray, Create); |
| DART_NATIVE_CALLBACK_STATIC(JniLongArray, Create); |
| DART_NATIVE_CALLBACK_STATIC(JniObjectArray, Create); |
| DART_NATIVE_CALLBACK_STATIC(JniShortArray, Create); |
| DART_NATIVE_CALLBACK_STATIC(JniString, Create); |
| |
| #define FOR_EACH_BINDING(V) \ |
| V(JniArray, GetLength) \ |
| V(JniBooleanArray, GetArrayElement) \ |
| V(JniBooleanArray, SetArrayElement) \ |
| V(JniByteArray, GetArrayElement) \ |
| V(JniByteArray, SetArrayElement) \ |
| V(JniCharArray, GetArrayElement) \ |
| V(JniCharArray, SetArrayElement) \ |
| V(JniClass, CallStaticBooleanMethod) \ |
| V(JniClass, CallStaticByteMethod) \ |
| V(JniClass, CallStaticCharMethod) \ |
| V(JniClass, CallStaticDoubleMethod) \ |
| V(JniClass, CallStaticFloatMethod) \ |
| V(JniClass, CallStaticIntMethod) \ |
| V(JniClass, CallStaticLongMethod) \ |
| V(JniClass, CallStaticObjectMethod) \ |
| V(JniClass, CallStaticShortMethod) \ |
| V(JniClass, CallStaticVoidMethod) \ |
| V(JniClass, GetFieldId) \ |
| V(JniClass, GetMethodId) \ |
| V(JniClass, GetStaticBooleanField) \ |
| V(JniClass, GetStaticByteField) \ |
| V(JniClass, GetStaticCharField) \ |
| V(JniClass, GetStaticDoubleField) \ |
| V(JniClass, GetStaticFieldId) \ |
| V(JniClass, GetStaticFloatField) \ |
| V(JniClass, GetStaticIntField) \ |
| V(JniClass, GetStaticLongField) \ |
| V(JniClass, GetStaticMethodId) \ |
| V(JniClass, GetStaticObjectField) \ |
| V(JniClass, GetStaticShortField) \ |
| V(JniClass, IsAssignable) \ |
| V(JniClass, NewObject) \ |
| V(JniClass, SetStaticBooleanField) \ |
| V(JniClass, SetStaticByteField) \ |
| V(JniClass, SetStaticCharField) \ |
| V(JniClass, SetStaticDoubleField) \ |
| V(JniClass, SetStaticFloatField) \ |
| V(JniClass, SetStaticIntField) \ |
| V(JniClass, SetStaticLongField) \ |
| V(JniClass, SetStaticObjectField) \ |
| V(JniClass, SetStaticShortField) \ |
| V(JniDoubleArray, GetArrayElement) \ |
| V(JniDoubleArray, SetArrayElement) \ |
| V(JniFloatArray, GetArrayElement) \ |
| V(JniFloatArray, SetArrayElement) \ |
| V(JniIntArray, GetArrayElement) \ |
| V(JniIntArray, SetArrayElement) \ |
| V(JniLongArray, GetArrayElement) \ |
| V(JniLongArray, SetArrayElement) \ |
| V(JniObject, CallBooleanMethod) \ |
| V(JniObject, CallByteMethod) \ |
| V(JniObject, CallCharMethod) \ |
| V(JniObject, CallDoubleMethod) \ |
| V(JniObject, CallFloatMethod) \ |
| V(JniObject, CallIntMethod) \ |
| V(JniObject, CallLongMethod) \ |
| V(JniObject, CallObjectMethod) \ |
| V(JniObject, CallShortMethod) \ |
| V(JniObject, CallVoidMethod) \ |
| V(JniObject, GetBooleanField) \ |
| V(JniObject, GetByteField) \ |
| V(JniObject, GetCharField) \ |
| V(JniObject, GetDoubleField) \ |
| V(JniObject, GetFloatField) \ |
| V(JniObject, GetIntField) \ |
| V(JniObject, GetLongField) \ |
| V(JniObject, GetObjectClass) \ |
| V(JniObject, GetObjectField) \ |
| V(JniObject, GetShortField) \ |
| V(JniObject, SetBooleanField) \ |
| V(JniObject, SetByteField) \ |
| V(JniObject, SetCharField) \ |
| V(JniObject, SetDoubleField) \ |
| V(JniObject, SetFloatField) \ |
| V(JniObject, SetIntField) \ |
| V(JniObject, SetLongField) \ |
| V(JniObject, SetObjectField) \ |
| V(JniObject, SetShortField) \ |
| V(JniObjectArray, GetArrayElement) \ |
| V(JniObjectArray, SetArrayElement) \ |
| V(JniShortArray, GetArrayElement) \ |
| V(JniShortArray, SetArrayElement) \ |
| V(JniString, GetText) |
| |
| FOR_EACH_BINDING(DART_NATIVE_CALLBACK) |
| |
| void DartJni::InitForGlobal(DartJniIsolateDataProvider provider) { |
| if (!g_isolate_data_provider) |
| g_isolate_data_provider = provider; |
| |
| if (!g_natives) { |
| g_natives = new tonic::DartLibraryNatives(); |
| g_natives->Register( |
| {DART_REGISTER_NATIVE_STATIC(JniApi, FromReflectedField), |
| DART_REGISTER_NATIVE_STATIC(JniApi, FromReflectedMethod), |
| DART_REGISTER_NATIVE_STATIC(JniApi, GetApplicationContext), |
| DART_REGISTER_NATIVE_STATIC(JniApi, GetClassLoader), |
| DART_REGISTER_NATIVE_STATIC(JniBooleanArray, Create), |
| DART_REGISTER_NATIVE_STATIC(JniByteArray, Create), |
| DART_REGISTER_NATIVE_STATIC(JniCharArray, Create), |
| DART_REGISTER_NATIVE_STATIC(JniClass, FromName), |
| DART_REGISTER_NATIVE_STATIC(JniClass, FromClassObject), |
| DART_REGISTER_NATIVE_STATIC(JniDoubleArray, Create), |
| DART_REGISTER_NATIVE_STATIC(JniFloatArray, Create), |
| DART_REGISTER_NATIVE_STATIC(JniIntArray, Create), |
| DART_REGISTER_NATIVE_STATIC(JniLongArray, Create), |
| DART_REGISTER_NATIVE_STATIC(JniObjectArray, Create), |
| DART_REGISTER_NATIVE_STATIC(JniShortArray, Create), |
| DART_REGISTER_NATIVE_STATIC(JniString, Create), |
| FOR_EACH_BINDING(DART_REGISTER_NATIVE)}); |
| } |
| } |
| |
| void DartJni::InitForIsolate() { |
| DCHECK(g_natives); |
| |
| Dart_Handle jni_library = Dart_LookupLibrary(ToDart("dart:jni")); |
| DART_CHECK_VALID(jni_library) |
| |
| DART_CHECK_VALID( |
| Dart_SetNativeResolver(jni_library, GetNativeFunction, GetSymbol)); |
| |
| Dart_Handle object_type = |
| Dart_GetType(jni_library, ToDart("JniObject"), 0, nullptr); |
| DART_CHECK_VALID(object_type); |
| IsolateData()->jni_object_type = Dart_NewPersistentHandle(object_type); |
| DART_CHECK_VALID(IsolateData()->jni_object_type); |
| |
| Dart_Handle float_type = |
| Dart_GetType(jni_library, ToDart("JniFloat"), 0, nullptr); |
| DART_CHECK_VALID(float_type); |
| IsolateData()->jni_float_type = Dart_NewPersistentHandle(float_type); |
| DART_CHECK_VALID(IsolateData()->jni_float_type); |
| } |
| |
| bool DartJni::InitJni() { |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| |
| DCHECK(!g_jvm_data); |
| g_jvm_data = new DartJniJvmData(); |
| |
| g_jvm_data->class_loader.Reset(base::android::GetClassLoader(env)); |
| |
| ScopedJavaLocalRef<jclass> class_loader_clazz( |
| env, env->FindClass("java/lang/ClassLoader")); |
| CHECK(!base::android::ClearException(env)); |
| |
| g_jvm_data->class_loader_load_class_method_id = |
| env->GetMethodID(class_loader_clazz.obj(), "loadClass", |
| "(Ljava/lang/String;)Ljava/lang/Class;"); |
| CHECK(!base::android::ClearException(env)); |
| |
| g_jvm_data->class_clazz.Reset(env, env->FindClass("java/lang/Class")); |
| CHECK(!base::android::ClearException(env)); |
| |
| g_jvm_data->class_get_name_method_id = env->GetMethodID( |
| g_jvm_data->class_clazz.obj(), "getName", "()Ljava/lang/String;"); |
| CHECK(!base::android::ClearException(env)); |
| |
| return true; |
| } |
| |
| void DartJni::OnThreadExit() { |
| base::android::DetachFromVM(); |
| } |
| |
| ScopedJavaLocalRef<jclass> DartJni::GetClass(JNIEnv* env, const char* name) { |
| jobject clazz = env->CallObjectMethod( |
| g_jvm_data->class_loader.obj(), |
| g_jvm_data->class_loader_load_class_method_id, |
| base::android::ConvertUTF8ToJavaString(env, name).obj()); |
| |
| return ScopedJavaLocalRef<jclass>(env, static_cast<jclass>(clazz)); |
| } |
| |
| std::string DartJni::GetObjectClassName(JNIEnv* env, jobject obj) { |
| jclass clazz = env->GetObjectClass(obj); |
| DCHECK(clazz); |
| jstring name = static_cast<jstring>( |
| env->CallObjectMethod(clazz, g_jvm_data->class_get_name_method_id)); |
| DCHECK(name); |
| |
| return base::android::ConvertJavaStringToUTF8(env, name); |
| } |
| |
| jstring DartJni::DartToJavaString(JNIEnv* env, |
| Dart_Handle dart_string, |
| Dart_Handle* exception) { |
| if (!Dart_IsString(dart_string)) { |
| *exception = ToDart("Argument must be a string"); |
| return nullptr; |
| } |
| |
| intptr_t length; |
| Dart_Handle result = Dart_StringLength(dart_string, &length); |
| if (CheckDartException(result, exception)) |
| return nullptr; |
| |
| std::vector<uint16_t> string_data(length); |
| result = Dart_StringToUTF16(dart_string, string_data.data(), &length); |
| if (CheckDartException(result, exception)) |
| return nullptr; |
| |
| jstring java_string = env->NewString(string_data.data(), length); |
| CheckJniException(env, exception); |
| return java_string; |
| } |
| |
| jobject DartJni::class_loader() { |
| return g_jvm_data->class_loader.obj(); |
| } |
| |
| jclass DartJni::class_clazz() { |
| return g_jvm_data->class_clazz.obj(); |
| } |
| |
| Dart_Handle DartJni::jni_object_type() { |
| Dart_Handle object_type = |
| Dart_HandleFromPersistent(IsolateData()->jni_object_type); |
| DCHECK(!Dart_IsError(object_type)); |
| return object_type; |
| } |
| |
| Dart_Handle DartJni::jni_float_type() { |
| Dart_Handle float_type = |
| Dart_HandleFromPersistent(IsolateData()->jni_float_type); |
| DCHECK(!Dart_IsError(float_type)); |
| return float_type; |
| } |
| |
| void JniMethodArgs::Convert(JNIEnv* env, |
| const std::vector<Dart_Handle>& dart_args, |
| Dart_Handle* exception) { |
| jvalues_.reserve(dart_args.size()); |
| |
| for (Dart_Handle dart_arg : dart_args) { |
| jvalue value = DartToJavaValue(env, dart_arg, exception); |
| if (*exception) |
| return; |
| jvalues_.push_back(value); |
| } |
| } |
| |
| jvalue JniMethodArgs::DartToJavaValue(JNIEnv* env, |
| Dart_Handle dart_value, |
| Dart_Handle* exception) { |
| jvalue java_value = jvalue(); |
| |
| if (Dart_IsBoolean(dart_value)) { |
| java_value.z = DartConverter<bool>::FromDart(dart_value); |
| return java_value; |
| } |
| |
| if (Dart_IsInteger(dart_value)) { |
| java_value.j = DartConverter<jlong>::FromDart(dart_value); |
| return java_value; |
| } |
| |
| if (Dart_IsDouble(dart_value)) { |
| java_value.d = DartConverter<jdouble>::FromDart(dart_value); |
| return java_value; |
| } |
| |
| if (Dart_IsString(dart_value)) { |
| java_value.l = DartJni::DartToJavaString(env, dart_value, exception); |
| return java_value; |
| } |
| |
| if (Dart_IsNull(dart_value)) { |
| java_value.l = nullptr; |
| return java_value; |
| } |
| |
| bool is_object; |
| Dart_Handle result = |
| Dart_ObjectIsType(dart_value, DartJni::jni_object_type(), &is_object); |
| if (CheckDartException(result, exception)) |
| return java_value; |
| |
| if (is_object) { |
| JniObject* jni_object = DartConverter<JniObject*>::FromDart(dart_value); |
| if (jni_object != nullptr) { |
| java_value.l = jni_object->java_object(); |
| } else { |
| *exception = ToDart("Invalid JniObject argument"); |
| } |
| return java_value; |
| } |
| |
| bool is_float; |
| result = Dart_ObjectIsType(dart_value, DartJni::jni_float_type(), &is_float); |
| if (CheckDartException(result, exception)) |
| return java_value; |
| |
| if (is_float) { |
| Dart_Handle value_handle = Dart_GetField(dart_value, ToDart("value")); |
| if (CheckDartException(value_handle, exception)) |
| return java_value; |
| |
| double double_value; |
| result = Dart_DoubleValue(value_handle, &double_value); |
| if (CheckDartException(result, exception)) |
| return java_value; |
| |
| java_value.f = static_cast<jfloat>(double_value); |
| return java_value; |
| } |
| |
| *exception = ToDart("Argument has unsupported data type"); |
| return java_value; |
| } |
| |
| } // namespace blink |