Bug 787271 - Add methods to call in jni through ctypes. r=mfinkle,blassey a=akeybl
authorWes Johnston <wjohnston@mozilla.com>
Wed, 12 Sep 2012 11:31:17 -0700
changeset 109788 46c7396b16225d69e450b349f9c35a2de8e15cc6
parent 109787 c51952868cb5fcfebdce6fb62afd90c9d0e2bf8f
child 109789 974494c2de324139b23603303e2b23cc53dfd516
push id214
push userakeybl@mozilla.com
push dateWed, 14 Nov 2012 20:38:59 +0000
treeherdermozilla-release@c8b08ec8e1aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmfinkle, blassey, akeybl
bugs787271
milestone17.0a2
Bug 787271 - Add methods to call in jni through ctypes. r=mfinkle,blassey a=akeybl
mobile/android/modules/JNI.jsm
mobile/android/modules/Makefile.in
widget/android/AndroidBridge.cpp
widget/android/AndroidBridge.h
new file mode 100644
--- /dev/null
+++ b/mobile/android/modules/JNI.jsm
@@ -0,0 +1,193 @@
+/* Very basic JNI support for JS
+ *
+ * Example Usage:
+ *   let jni = new JNI();
+ *   cls = jni.findClass("org.mozilla.gecko.GeckoAppShell");
+ *   method = jni.getStaticMethodID(cls, "getPreferredIconSize", "(I)I");
+ *
+ *   let val = jni.callStaticIntMethod(cls, method, 3);
+ *   // close the jni library when you are done
+ *   jni.close();
+ */
+let EXPORTED_SYMBOLS = ["JNI"];
+
+Components.utils.import("resource://gre/modules/ctypes.jsm")
+Components.utils.import("resource://gre/modules/Services.jsm")
+
+function JNI() { }
+
+JNI.prototype = {
+  get lib() {
+    delete this.lib;
+    return this.lib = ctypes.open("libxul.so");
+  },
+
+  getType: function(aType) {
+    switch(aType) {
+      case "B": return ctypes.char;
+      case "C": return ctypes.char;
+      case "D": return ctypes.double;
+      case "F": return ctypes.float;
+      case "I": return ctypes.int32_t;
+      case "J": return ctypes.long;
+      case "S": return ctypes.short;
+      case "V": return ctypes.void_t;
+      case "Z": return ctypes.bool;
+      default: return this.types.jobject
+    }
+  },
+
+  getArgs: function(aMethod, aArgs) {
+    if (aArgs.length != aMethod.parameters.length)
+      throw ("Incorrect number of arguments passed to " + aMethod.name);
+
+    // Convert arguments to an array of jvalue objects
+    let modifiedArgs = new ctypes.ArrayType(this.types.jvalue, aMethod.parameters.length)();
+    for (let i = 0; i < aMethod.parameters.length; i++) {
+      let parameter = aMethod.parameters[i];
+      let arg = new this.types.jvalue();
+
+      if (aArgs[i] instanceof Array || parameter[0] == "[")
+        throw "No support for array arguments yet";
+      else
+        ctypes.cast(arg, this.getType(parameter)).value = aArgs[i];
+
+      modifiedArgs[i] = arg;
+    }
+
+    return modifiedArgs;
+  },
+
+  types: {
+    jobject: ctypes.StructType("_jobject").ptr,
+    jclass: ctypes.StructType("_jobject").ptr,
+    jmethodID: ctypes.StructType("jmethodID").ptr,
+    jvalue: ctypes.double
+  },
+
+  get _findClass() {
+    delete this._findClass;
+    return this._findClass = this.lib.declare("FindClass",
+                                              ctypes.default_abi,
+                                              this.types.jclass,
+                                              ctypes.char.ptr);
+  },
+
+  findClass: function(name) {
+    let ret = this._findClass(name);
+    if (this.exceptionCheck())
+       throw("Can't find class " + name);
+    return ret;
+  },
+
+  get _getStaticMethodID() {
+    delete this._getStatisMethodID;
+    return this._getStaticMethodID = this.lib.declare("GetStaticMethodID",
+                                                      ctypes.default_abi,
+                                                      this.types.jmethodID,
+                                                      this.types.jclass, // class
+                                                      ctypes.char.ptr,   // method name
+                                                      ctypes.char.ptr);  // signature
+  },
+
+  getStaticMethodID: function(aClass, aName, aSignature) {
+    let ret = this._getStaticMethodID(aClass, aName, aSignature);
+    if (this.exceptionCheck())
+       throw("Can't find method " + aName);
+    return new jMethod(aName, ret, aSignature);
+  },
+
+  get _exceptionCheck() {
+    delete this._exceptionCheck;
+    return this._exceptionCheck = this.lib.declare("ExceptionCheck",
+                                                   ctypes.default_abi,
+                                                   ctypes.bool);
+  },
+
+  exceptionCheck: function() {
+    return this._exceptionCheck();
+  },
+
+  get _callStaticVoidMethod() {
+    delete this._callStaticVoidMethod;
+    return this._callStaticVoidMethod = this.lib.declare("CallStaticVoidMethodA",
+                                                   ctypes.default_abi,
+                                                   ctypes.void_t,
+                                                   this.types.jclass,
+                                                   this.types.jmethodID,
+                                                   this.types.jvalue.ptr);
+  },
+
+  callStaticVoidMethod: function(aClass, aMethod) {
+    let args = Array.prototype.slice.apply(arguments, [2]);
+    this._callStaticVoidMethod(aClass, aMethodId.methodId, this.getArgs(aMethod, args));
+    if (this.exceptionCheck())
+       throw("Error calling static void method");
+  },
+
+  get _callStaticIntMethod() {
+    delete this._callStaticIntMethod;
+    return this._callStaticIntMethod = this.lib.declare("CallStaticIntMethodA",
+                                                   ctypes.default_abi,
+                                                   ctypes.int,
+                                                   this.types.jclass,
+                                                   this.types.jmethodID,
+                                                   this.types.jvalue.ptr);
+  },
+
+  callStaticIntMethod: function(aClass, aMethod) {
+    let args = Array.prototype.slice.apply(arguments, [2]);
+    let ret = this._callStaticIntMethod(aClass, aMethod.methodId, this.getArgs(aMethod, args));
+    if (this.exceptionCheck())
+       throw("Error calling static int method");
+    return ret;
+  },
+
+  close: function() {
+    this.lib.close();
+  },
+}
+
+function jMethod(name, jMethodId, signature) {
+  this.name = name;
+  this.methodId = jMethodId;
+  this.signature = signature;
+}
+
+jMethod.prototype = {
+  parameters: [],
+  returnType: null,
+  _signature: "",
+
+  // this just splits up the return value from the parameters
+  signatureRegExp: /^\(([^\)]*)\)(.*)$/,
+
+  // This splits up the actual parameters
+  parameterRegExp: /(\[*)(B|C|D|F|I|J|S|V|Z|L[^;]*;)/y,
+
+  parseSignature: function(aSignature) {
+    let [, parameters, returnType] = this.signatureRegExp.exec(aSignature);
+
+    // parse the parameters that should be passed to this method
+    if (parameters) {
+      let parameter = this.parameterRegExp.exec(parameters);
+      while (parameter) {
+        this.parameters.push(parameter[0]);
+        parameter = this.parameterRegExp.exec(parameters);
+      }
+    } else {
+      this.parameters = [];
+    }
+
+    // parse the return type
+    this.returnType = returnType;
+  },
+
+  _signature: "",
+  get signature() { return this._signature; },
+  set signature(val) {
+    this.parameters = [];
+    this.returnType = null;
+    this.parseSignature(val);
+  }
+}
--- a/mobile/android/modules/Makefile.in
+++ b/mobile/android/modules/Makefile.in
@@ -8,15 +8,16 @@ srcdir     = @srcdir@
 VPATH      = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 EXTRA_JS_MODULES = \
   LocaleRepository.jsm \
   linuxTypes.jsm \
   video.jsm \
+  JNI.jsm \
   $(NULL)
 
 EXTRA_PP_JS_MODULES = \
   contacts.jsm \
   $(NULL)
 
 include $(topsrcdir)/config/rules.mk
--- a/widget/android/AndroidBridge.cpp
+++ b/widget/android/AndroidBridge.cpp
@@ -2525,8 +2525,60 @@ AndroidBridge::NotifyPaintedRect(float t
 {
     JNIEnv* env = GetJNIEnv();
     if (!env)
         return;
 
     AutoLocalJNIFrame jniFrame(env, 0);
     env->CallStaticVoidMethod(AndroidBridge::Bridge()->mGeckoAppShellClass, AndroidBridge::Bridge()->jNotifyPaintedRect, top, left, bottom, right);
 }
+
+extern "C" {
+  __attribute__ ((visibility("default")))
+  jclass
+  jsjni_FindClass(const char *className) {
+    JNIEnv *env = AndroidBridge::GetJNIEnv();
+    if (!env) return NULL;
+    return env->FindClass(className);
+  }
+
+  __attribute__ ((visibility("default")))
+  jmethodID
+  jsjni_GetStaticMethodID(jclass methodClass,
+                    const char *methodName,
+                    const char *signature) {
+    JNIEnv *env = AndroidBridge::GetJNIEnv();
+    if (!env) return NULL;
+    return env->GetStaticMethodID(methodClass, methodName, signature);
+  }
+
+  __attribute__ ((visibility("default")))
+  bool
+  jsjni_ExceptionCheck() {
+    JNIEnv *env = AndroidBridge::GetJNIEnv();
+    if (!env) return NULL;
+    return env->ExceptionCheck();
+  }
+
+  __attribute__ ((visibility("default")))
+  void
+  jsjni_CallStaticVoidMethodA(jclass cls,
+                        jmethodID method,
+                        jvalue *values) {
+    JNIEnv *env = AndroidBridge::GetJNIEnv();
+    if (!env) return;
+
+    AutoLocalJNIFrame jniFrame(env);
+    env->CallStaticVoidMethodA(cls, method, values);
+  }
+
+  __attribute__ ((visibility("default")))
+  int
+  jsjni_CallStaticIntMethodA(jclass cls,
+                       jmethodID method,
+                       jvalue *values) {
+    JNIEnv *env = AndroidBridge::GetJNIEnv();
+    if (!env) return -1;
+
+    AutoLocalJNIFrame jniFrame(env);
+    return env->CallStaticIntMethodA(cls, method, values);
+  }
+}
--- a/widget/android/AndroidBridge.h
+++ b/widget/android/AndroidBridge.h
@@ -31,16 +31,23 @@
 // #define DEBUG_ANDROID_EVENTS
 // #define DEBUG_ANDROID_WIDGET
 
 class nsWindow;
 class nsIDOMMozSmsMessage;
 
 /* See the comment in AndroidBridge about this function before using it */
 extern "C" JNIEnv * GetJNIForThread();
+extern "C" jclass jsjni_FindClass(const char *className);
+extern "C" jmethodID jsjni_GetStaticMethodID(jclass methodClass,
+                                       const char *methodName,
+                                       const char *signature);
+extern "C" bool jsjni_ExceptionCheck();
+extern "C" void jsjni_CallStaticVoidMethodA(jclass cls, jmethodID method, jvalue *values);
+extern "C" int jsjni_CallStaticIntMethodA(jclass cls, jmethodID method, jvalue *values);
 
 extern bool mozilla_AndroidBridge_SetMainThread(void *);
 extern jclass GetGeckoAppShellClass();
 
 namespace base {
 class Thread;
 } // end namespace base