From a9c22e9d8cb9b35bd706fe2ced75f700939b006a Mon Sep 17 00:00:00 2001 From: Jason Simmons Date: Wed, 6 Jan 2016 13:41:52 -0800 Subject: [PATCH] Initial work toward exposing the Java native interface APIs in Dart --- sky/engine/bindings/BUILD.gn | 5 + sky/engine/bindings/dart_ui.cc | 9 ++ sky/engine/bindings/dart_ui.dart | 1 + sky/engine/bindings/jni/BUILD.gn | 17 +++ sky/engine/bindings/jni/dart_jni.cc | 145 +++++++++++++++++++ sky/engine/bindings/jni/dart_jni.h | 83 +++++++++++ sky/engine/bindings/jni/jni.dart | 21 +++ sky/shell/BUILD.gn | 1 + sky/shell/platform/android/library_loader.cc | 12 +- 9 files changed, 292 insertions(+), 2 deletions(-) create mode 100644 sky/engine/bindings/jni/BUILD.gn create mode 100644 sky/engine/bindings/jni/dart_jni.cc create mode 100644 sky/engine/bindings/jni/dart_jni.h create mode 100644 sky/engine/bindings/jni/jni.dart diff --git a/sky/engine/bindings/BUILD.gn b/sky/engine/bindings/BUILD.gn index 9cb1131a77b..d66ea464e73 100644 --- a/sky/engine/bindings/BUILD.gn +++ b/sky/engine/bindings/BUILD.gn @@ -37,6 +37,10 @@ source_set("bindings") { "//sky/engine/wtf", ] + if (is_android) { + deps += [ "//sky/engine/bindings/jni" ] + } + # On iOS (device), precompiled snapshots contain the instruction buffer. # Generation of the same requires all application specific script code to be # specified up front. In such cases, there can be no updater or generic @@ -144,6 +148,7 @@ source_set("snapshot_cc") { copy("generate_dart_ui") { sources = [ "dart_ui.dart", + "jni/jni.dart", ] sources += core_dart_files diff --git a/sky/engine/bindings/dart_ui.cc b/sky/engine/bindings/dart_ui.cc index 5ecdc2c5df1..20abafbdc1b 100644 --- a/sky/engine/bindings/dart_ui.cc +++ b/sky/engine/bindings/dart_ui.cc @@ -25,6 +25,10 @@ #include "sky/engine/tonic/dart_converter.h" #include "sky/engine/tonic/dart_error.h" +#ifdef OS_ANDROID +#include "sky/engine/bindings/jni/dart_jni.h" +#endif + namespace blink { namespace { @@ -63,6 +67,11 @@ void DartUI::InitForGlobal() { Scene::RegisterNatives(g_natives); SceneBuilder::RegisterNatives(g_natives); Window::RegisterNatives(g_natives); + +#ifdef OS_ANDROID + // TODO(jsimmons): move this into a dart:jni library + DartJni::RegisterNatives(g_natives); +#endif } } diff --git a/sky/engine/bindings/dart_ui.dart b/sky/engine/bindings/dart_ui.dart index 27d55f7188d..82df9eba6fd 100644 --- a/sky/engine/bindings/dart_ui.dart +++ b/sky/engine/bindings/dart_ui.dart @@ -24,6 +24,7 @@ part 'TransferMode.dart'; part 'compositing.dart'; part 'hash_codes.dart'; part 'hooks.dart'; +part 'jni.dart'; part 'lerp.dart'; part 'natives.dart'; part 'painting.dart'; diff --git a/sky/engine/bindings/jni/BUILD.gn b/sky/engine/bindings/jni/BUILD.gn new file mode 100644 index 00000000000..55ce2944d4c --- /dev/null +++ b/sky/engine/bindings/jni/BUILD.gn @@ -0,0 +1,17 @@ +# 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. + +source_set("jni") { + sources = [ + "dart_jni.cc", + "dart_jni.h", + ] + + deps = [ + "//base", + "//dart/runtime:libdart", + "//sky/engine/tonic", + "//sky/engine/wtf", + ] +} diff --git a/sky/engine/bindings/jni/dart_jni.cc b/sky/engine/bindings/jni/dart_jni.cc new file mode 100644 index 00000000000..e566a00d681 --- /dev/null +++ b/sky/engine/bindings/jni/dart_jni.cc @@ -0,0 +1,145 @@ +// 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 "sky/engine/bindings/jni/dart_jni.h" + +#include "base/logging.h" +#include "base/android/jni_android.h" +#include "base/android/jni_string.h" +#include "sky/engine/tonic/dart_args.h" +#include "sky/engine/tonic/dart_binding_macros.h" + +namespace blink { + +using base::android::ScopedJavaLocalRef; +using base::android::ScopedJavaGlobalRef; + +namespace { + +// Check if a JNI API has thrown an exception. If so, rethrow it as a +// Dart exception. +void CheckJniException(JNIEnv* env) { + if (env->ExceptionCheck() == JNI_FALSE) + return; + + jthrowable java_throwable = env->ExceptionOccurred(); + env->ExceptionClear(); + std::string info = base::android::GetJavaExceptionInfo( + env, java_throwable); + + Dart_ThrowException(StdStringToDart(info)); +} + +} // anonymous namespace + +DART_NATIVE_CALLBACK_STATIC(JniClass, fromName); + +#define FOR_EACH_BINDING(V) \ + V(JniClass, getFieldId) \ + V(JniClass, getStaticFieldId) \ + V(JniClass, getStaticIntField) \ + V(JniClass, getStaticObjectField) \ + V(JniObject, getIntField) + +FOR_EACH_BINDING(DART_NATIVE_CALLBACK) + +void DartJni::RegisterNatives(DartLibraryNatives* natives) { + natives->Register({ + DART_REGISTER_NATIVE_STATIC(JniClass, fromName) + FOR_EACH_BINDING(DART_REGISTER_NATIVE) + }); +} + +ScopedJavaGlobalRef DartJni::class_loader_; +jmethodID DartJni::load_class_method_id_; + +bool DartJni::InitJni() { + JNIEnv* env = base::android::AttachCurrentThread(); + + class_loader_.Reset(base::android::GetClassLoader(env)); + + ScopedJavaLocalRef class_loader_clazz( + env, env->FindClass("java/lang/ClassLoader")); + CHECK(!base::android::ClearException(env)); + + load_class_method_id_ = env->GetMethodID( + class_loader_clazz.obj(), + "loadClass", + "(Ljava/lang/String;)Ljava/lang/Class;"); + 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_, + base::android::ConvertUTF8ToJavaString(env, name).obj()); + + return ScopedJavaLocalRef(env, static_cast(clazz)); +} + +IMPLEMENT_WRAPPERTYPEINFO(JniClass); + +JniClass::JniClass(JNIEnv* env, jclass clazz) + : clazz_(env, clazz) { +} + +JniClass::~JniClass() { +} + +PassRefPtr JniClass::fromName(const char* name) { + JNIEnv* env = base::android::AttachCurrentThread(); + + ScopedJavaLocalRef clazz = DartJni::GetClass(env, name); + CheckJniException(env); + + return adoptRef(new JniClass(env, clazz.obj())); +} + +intptr_t JniClass::getFieldId(const char* name, const char* sig) { + JNIEnv* env = base::android::AttachCurrentThread(); + jfieldID id = env->GetFieldID(clazz_.obj(), name, sig); + CheckJniException(env); + return reinterpret_cast(id); +} + +intptr_t JniClass::getStaticFieldId(const char* name, const char* sig) { + JNIEnv* env = base::android::AttachCurrentThread(); + jfieldID id = env->GetStaticFieldID(clazz_.obj(), name, sig); + CheckJniException(env); + return reinterpret_cast(id); +} + +jint JniClass::getStaticIntField(jfieldID fieldId) { + JNIEnv* env = base::android::AttachCurrentThread(); + return env->GetStaticIntField(clazz_.obj(), fieldId); +} + +PassRefPtr JniClass::getStaticObjectField(jfieldID fieldId) { + JNIEnv* env = base::android::AttachCurrentThread(); + jobject obj = env->GetStaticObjectField(clazz_.obj(), fieldId); + return JniObject::create(env, obj); +} + +IMPLEMENT_WRAPPERTYPEINFO(JniObject); + +JniObject::JniObject(JNIEnv* env, jobject object) + : object_(env, object) { +} + +JniObject::~JniObject() { +} + +PassRefPtr JniObject::create(JNIEnv* env, jobject object) { + return adoptRef(new JniObject(env, object)); +} + +jint JniObject::getIntField(jfieldID fieldId) { + JNIEnv* env = base::android::AttachCurrentThread(); + return env->GetIntField(object_.obj(), fieldId); +} + +} // namespace blink diff --git a/sky/engine/bindings/jni/dart_jni.h b/sky/engine/bindings/jni/dart_jni.h new file mode 100644 index 00000000000..ee86bb4c568 --- /dev/null +++ b/sky/engine/bindings/jni/dart_jni.h @@ -0,0 +1,83 @@ +// 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. + +#ifndef SKY_ENGINE_BINDINGS_OBJC_DART_JNI_H_ +#define SKY_ENGINE_BINDINGS_OBJC_DART_JNI_H_ + +#include "base/android/jni_android.h" +#include "base/android/jni_utils.h" +#include "sky/engine/tonic/dart_library_natives.h" +#include "sky/engine/tonic/dart_wrappable.h" +#include "sky/engine/wtf/PassRefPtr.h" +#include "sky/engine/wtf/RefCounted.h" + +namespace blink { + +class DartJni { + public: + static void RegisterNatives(DartLibraryNatives* natives); + static bool InitJni(); + + static base::android::ScopedJavaLocalRef GetClass( + JNIEnv* env, const char* name); + + private: + static base::android::ScopedJavaGlobalRef class_loader_; + static jmethodID load_class_method_id_; +}; + +class JniObject; + +// Wrapper that exposes a JNI jclass to Dart +class JniClass : public RefCounted, public DartWrappable { + DEFINE_WRAPPERTYPEINFO(); + + public: + ~JniClass() override; + + static PassRefPtr fromName(const char* className); + + intptr_t getFieldId(const char* name, const char* sig); + intptr_t getStaticFieldId(const char* name, const char* sig); + + jint getStaticIntField(jfieldID fieldId); + PassRefPtr getStaticObjectField(jfieldID fieldId); + + private: + JniClass(JNIEnv* env, jclass clazz); + + base::android::ScopedJavaGlobalRef clazz_; +}; + +// Wrapper that exposes a JNI jobject to Dart +class JniObject : public RefCounted, public DartWrappable { + DEFINE_WRAPPERTYPEINFO(); + + public: + ~JniObject() override; + + static PassRefPtr create(JNIEnv* env, jobject object); + + jint getIntField(jfieldID fieldId); + + private: + JniObject(JNIEnv* env, jobject object); + + base::android::ScopedJavaGlobalRef object_; +}; + +template <> +struct DartConverter { + static jfieldID FromArguments(Dart_NativeArguments args, + int index, + Dart_Handle& exception) { + int64_t result = 0; + Dart_GetNativeIntegerArgument(args, index, &result); + return reinterpret_cast(result); + } +}; + +} // namespace blink + +#endif // SKY_ENGINE_BINDINGS_OBJC_DART_JNI_H_ diff --git a/sky/engine/bindings/jni/jni.dart b/sky/engine/bindings/jni/jni.dart new file mode 100644 index 00000000000..78d956511d1 --- /dev/null +++ b/sky/engine/bindings/jni/jni.dart @@ -0,0 +1,21 @@ +// 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. + +part of dart_ui; + +/// Wrapper for a Java class accessed via JNI. +class JniClass extends NativeFieldWrapperClass2 { + static JniClass fromName(String name) native 'JniClass_fromName'; + + int getFieldId(String name, String sig) native 'JniClass_getFieldId'; + int getStaticFieldId(String name, String sig) native 'JniClass_getStaticFieldId'; + + int getStaticIntField(int fieldId) native 'JniClass_getStaticIntField'; + JniObject getStaticObjectField(int fieldId) native 'JniClass_getStaticObjectField'; +} + +/// Wrapper for a Java object accessed via JNI. +class JniObject extends NativeFieldWrapperClass2 { + int getIntField(String name, String sig) native 'JniObject_getIntField'; +} diff --git a/sky/shell/BUILD.gn b/sky/shell/BUILD.gn index 3bf7faa06c3..db5d9551b65 100644 --- a/sky/shell/BUILD.gn +++ b/sky/shell/BUILD.gn @@ -136,6 +136,7 @@ if (is_android) { "//mojo/android:libsystem_java", "//mojo/edk/base_edk", "//mojo/edk/system", + "//sky/engine/bindings/jni", "//ui/gl", ":common", ":gpu_direct", diff --git a/sky/shell/platform/android/library_loader.cc b/sky/shell/platform/android/library_loader.cc index 90be9d3b919..e74ed6b93bf 100644 --- a/sky/shell/platform/android/library_loader.cc +++ b/sky/shell/platform/android/library_loader.cc @@ -11,6 +11,7 @@ #include "base/logging.h" #include "mojo/android/system/base_run_loop.h" #include "mojo/android/system/core_impl.h" +#include "sky/engine/bindings/jni/dart_jni.h" #include "sky/shell/platform/android/platform_view_android.h" #include "sky/shell/platform/android/sky_main.h" #include "sky/shell/platform/android/tracing_controller.h" @@ -35,15 +36,22 @@ bool RegisterJNI(JNIEnv* env) { arraysize(kSkyRegisteredMethods)); } +bool InitJNI() { + return blink::DartJni::InitJni(); +} + } // namespace // This is called by the VM when the shared library is first loaded. JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { std::vector register_callbacks; register_callbacks.push_back(base::Bind(&RegisterJNI)); + + std::vector init_callbacks; + init_callbacks.push_back(base::Bind(&InitJNI)); + if (!base::android::OnJNIOnLoadRegisterJNI(vm, register_callbacks) || - !base::android::OnJNIOnLoadInit( - std::vector())) { + !base::android::OnJNIOnLoadInit(init_callbacks)) { return -1; }