mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-04 02:09:05 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			251 lines
		
	
	
	
		
			7.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			251 lines
		
	
	
	
		
			7.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | 
						|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | 
						|
/* This Source Code Form is subject to the terms of the Mozilla Public
 | 
						|
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
						|
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | 
						|
 | 
						|
#ifndef mozilla_jni_Accessors_h__
 | 
						|
#define mozilla_jni_Accessors_h__
 | 
						|
 | 
						|
#include <jni.h>
 | 
						|
 | 
						|
#include "mozilla/jni/Refs.h"
 | 
						|
#include "mozilla/jni/Types.h"
 | 
						|
#include "mozilla/jni/Utils.h"
 | 
						|
#include "AndroidBridge.h"
 | 
						|
 | 
						|
namespace mozilla {
 | 
						|
namespace jni {
 | 
						|
 | 
						|
namespace detail {
 | 
						|
 | 
						|
// Helper class to convert an arbitrary type to a jvalue, e.g. Value(123).val.
 | 
						|
struct Value {
 | 
						|
  explicit Value(jboolean z) { val.z = z; }
 | 
						|
  explicit Value(jbyte b) { val.b = b; }
 | 
						|
  explicit Value(jchar c) { val.c = c; }
 | 
						|
  explicit Value(jshort s) { val.s = s; }
 | 
						|
  explicit Value(jint i) { val.i = i; }
 | 
						|
  explicit Value(jlong j) { val.j = j; }
 | 
						|
  explicit Value(jfloat f) { val.f = f; }
 | 
						|
  explicit Value(jdouble d) { val.d = d; }
 | 
						|
  explicit Value(jobject l) { val.l = l; }
 | 
						|
 | 
						|
  jvalue val;
 | 
						|
};
 | 
						|
 | 
						|
}  // namespace detail
 | 
						|
 | 
						|
using namespace detail;
 | 
						|
 | 
						|
// Base class for Method<>, Field<>, and Constructor<>.
 | 
						|
class Accessor {
 | 
						|
  static void GetNsresult(JNIEnv* env, nsresult* rv) {
 | 
						|
    if (env->ExceptionCheck()) {
 | 
						|
#ifdef MOZ_CHECK_JNI
 | 
						|
      env->ExceptionDescribe();
 | 
						|
#endif
 | 
						|
      env->ExceptionClear();
 | 
						|
      *rv = NS_ERROR_FAILURE;
 | 
						|
    } else {
 | 
						|
      *rv = NS_OK;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
 protected:
 | 
						|
  // Called after making a JNIEnv call.
 | 
						|
  template <class Traits>
 | 
						|
  static void EndAccess(const typename Traits::Owner::Context& ctx,
 | 
						|
                        nsresult* rv) {
 | 
						|
    if (Traits::exceptionMode == ExceptionMode::ABORT) {
 | 
						|
      MOZ_CATCH_JNI_EXCEPTION(ctx.Env());
 | 
						|
 | 
						|
    } else if (Traits::exceptionMode == ExceptionMode::NSRESULT) {
 | 
						|
      GetNsresult(ctx.Env(), rv);
 | 
						|
    }
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
// Member<> is used to call a JNI method given a traits class.
 | 
						|
template <class Traits, typename ReturnType = typename Traits::ReturnType>
 | 
						|
class Method : public Accessor {
 | 
						|
  typedef Accessor Base;
 | 
						|
  typedef typename Traits::Owner::Context Context;
 | 
						|
 | 
						|
 protected:
 | 
						|
  static jmethodID sID;
 | 
						|
 | 
						|
  static void BeginAccess(const Context& ctx) {
 | 
						|
    MOZ_ASSERT_JNI_THREAD(Traits::callingThread);
 | 
						|
    static_assert(Traits::dispatchTarget == DispatchTarget::CURRENT,
 | 
						|
                  "Dispatching not supported for method call");
 | 
						|
 | 
						|
    if (sID) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    if (Traits::isStatic) {
 | 
						|
      MOZ_ALWAYS_TRUE(
 | 
						|
          sID = AndroidBridge::GetStaticMethodID(
 | 
						|
              ctx.Env(), ctx.ClassRef(), Traits::name, Traits::signature));
 | 
						|
    } else {
 | 
						|
      MOZ_ALWAYS_TRUE(
 | 
						|
          sID = AndroidBridge::GetMethodID(ctx.Env(), ctx.ClassRef(),
 | 
						|
                                           Traits::name, Traits::signature));
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  static void EndAccess(const Context& ctx, nsresult* rv) {
 | 
						|
    return Base::EndAccess<Traits>(ctx, rv);
 | 
						|
  }
 | 
						|
 | 
						|
 public:
 | 
						|
  template <typename... Args>
 | 
						|
  static ReturnType Call(const Context& ctx, nsresult* rv,
 | 
						|
                         const Args&... args) {
 | 
						|
    JNIEnv* const env = ctx.Env();
 | 
						|
    BeginAccess(ctx);
 | 
						|
 | 
						|
    jvalue jargs[] = {Value(TypeAdapter<Args>::FromNative(env, args)).val...};
 | 
						|
 | 
						|
    auto result = TypeAdapter<ReturnType>::ToNative(
 | 
						|
        env, Traits::isStatic ? (env->*TypeAdapter<ReturnType>::StaticCall)(
 | 
						|
                                    ctx.ClassRef(), sID, jargs)
 | 
						|
                              : (env->*TypeAdapter<ReturnType>::Call)(
 | 
						|
                                    ctx.Get(), sID, jargs));
 | 
						|
 | 
						|
    EndAccess(ctx, rv);
 | 
						|
    return result;
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
// Define sID member.
 | 
						|
template <class T, typename R>
 | 
						|
jmethodID Method<T, R>::sID;
 | 
						|
 | 
						|
// Specialize void because C++ forbids us from
 | 
						|
// using a "void" temporary result variable.
 | 
						|
template <class Traits>
 | 
						|
class Method<Traits, void> : public Method<Traits, bool> {
 | 
						|
  typedef Method<Traits, bool> Base;
 | 
						|
  typedef typename Traits::Owner::Context Context;
 | 
						|
 | 
						|
 public:
 | 
						|
  template <typename... Args>
 | 
						|
  static void Call(const Context& ctx, nsresult* rv, const Args&... args) {
 | 
						|
    JNIEnv* const env = ctx.Env();
 | 
						|
    Base::BeginAccess(ctx);
 | 
						|
 | 
						|
    jvalue jargs[] = {Value(TypeAdapter<Args>::FromNative(env, args)).val...};
 | 
						|
 | 
						|
    if (Traits::isStatic) {
 | 
						|
      env->CallStaticVoidMethodA(ctx.ClassRef(), Base::sID, jargs);
 | 
						|
    } else {
 | 
						|
      env->CallVoidMethodA(ctx.Get(), Base::sID, jargs);
 | 
						|
    }
 | 
						|
 | 
						|
    Base::EndAccess(ctx, rv);
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
// Constructor<> is used to construct a JNI instance given a traits class.
 | 
						|
template <class Traits>
 | 
						|
class Constructor : protected Method<Traits, typename Traits::ReturnType> {
 | 
						|
  typedef typename Traits::Owner::Context Context;
 | 
						|
  typedef typename Traits::ReturnType ReturnType;
 | 
						|
  typedef Method<Traits, ReturnType> Base;
 | 
						|
 | 
						|
 public:
 | 
						|
  template <typename... Args>
 | 
						|
  static ReturnType Call(const Context& ctx, nsresult* rv,
 | 
						|
                         const Args&... args) {
 | 
						|
    JNIEnv* const env = ctx.Env();
 | 
						|
    Base::BeginAccess(ctx);
 | 
						|
 | 
						|
    jvalue jargs[] = {Value(TypeAdapter<Args>::FromNative(env, args)).val...};
 | 
						|
 | 
						|
    auto result = TypeAdapter<ReturnType>::ToNative(
 | 
						|
        env, env->NewObjectA(ctx.ClassRef(), Base::sID, jargs));
 | 
						|
 | 
						|
    Base::EndAccess(ctx, rv);
 | 
						|
    return result;
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
// Field<> is used to access a JNI field given a traits class.
 | 
						|
template <class Traits>
 | 
						|
class Field : public Accessor {
 | 
						|
  typedef Accessor Base;
 | 
						|
  typedef typename Traits::Owner::Context Context;
 | 
						|
  typedef typename Traits::ReturnType GetterType;
 | 
						|
  typedef typename Traits::SetterType SetterType;
 | 
						|
 | 
						|
 private:
 | 
						|
  static jfieldID sID;
 | 
						|
 | 
						|
  static void BeginAccess(const Context& ctx) {
 | 
						|
    MOZ_ASSERT_JNI_THREAD(Traits::callingThread);
 | 
						|
    static_assert(Traits::dispatchTarget == DispatchTarget::CURRENT,
 | 
						|
                  "Dispatching not supported for field access");
 | 
						|
 | 
						|
    if (sID) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    if (Traits::isStatic) {
 | 
						|
      MOZ_ALWAYS_TRUE(
 | 
						|
          sID = AndroidBridge::GetStaticFieldID(
 | 
						|
              ctx.Env(), ctx.ClassRef(), Traits::name, Traits::signature));
 | 
						|
    } else {
 | 
						|
      MOZ_ALWAYS_TRUE(sID = AndroidBridge::GetFieldID(ctx.Env(), ctx.ClassRef(),
 | 
						|
                                                      Traits::name,
 | 
						|
                                                      Traits::signature));
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  static void EndAccess(const Context& ctx, nsresult* rv) {
 | 
						|
    return Base::EndAccess<Traits>(ctx, rv);
 | 
						|
  }
 | 
						|
 | 
						|
 public:
 | 
						|
  static GetterType Get(const Context& ctx, nsresult* rv) {
 | 
						|
    JNIEnv* const env = ctx.Env();
 | 
						|
    BeginAccess(ctx);
 | 
						|
 | 
						|
    auto result = TypeAdapter<GetterType>::ToNative(
 | 
						|
        env, Traits::isStatic
 | 
						|
                 ?
 | 
						|
 | 
						|
                 (env->*TypeAdapter<GetterType>::StaticGet)(ctx.ClassRef(), sID)
 | 
						|
                 :
 | 
						|
 | 
						|
                 (env->*TypeAdapter<GetterType>::Get)(ctx.Get(), sID));
 | 
						|
 | 
						|
    EndAccess(ctx, rv);
 | 
						|
    return result;
 | 
						|
  }
 | 
						|
 | 
						|
  static void Set(const Context& ctx, nsresult* rv, SetterType val) {
 | 
						|
    JNIEnv* const env = ctx.Env();
 | 
						|
    BeginAccess(ctx);
 | 
						|
 | 
						|
    if (Traits::isStatic) {
 | 
						|
      (env->*TypeAdapter<SetterType>::StaticSet)(
 | 
						|
          ctx.ClassRef(), sID, TypeAdapter<SetterType>::FromNative(env, val));
 | 
						|
    } else {
 | 
						|
      (env->*TypeAdapter<SetterType>::Set)(
 | 
						|
          ctx.Get(), sID, TypeAdapter<SetterType>::FromNative(env, val));
 | 
						|
    }
 | 
						|
 | 
						|
    EndAccess(ctx, rv);
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
// Define sID member.
 | 
						|
template <class T>
 | 
						|
jfieldID Field<T>::sID;
 | 
						|
 | 
						|
}  // namespace jni
 | 
						|
}  // namespace mozilla
 | 
						|
 | 
						|
#endif  // mozilla_jni_Accessors_h__
 |