diff -r 000000000000 -r 6474c204b198 mobile/android/modules/JNI.jsm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mobile/android/modules/JNI.jsm Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,197 @@ +/* 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(); + * + * Note: the getters in this file are deleted and replaced with static + * values once computed, as in, for example + * http://code.activestate.com/recipes/577310-using-a-getter-for-a-one-time-calculation-of-a-jav/ + */ +this.EXPORTED_SYMBOLS = ["JNI"]; + +Components.utils.import("resource://gre/modules/ctypes.jsm") +Components.utils.import("resource://gre/modules/Services.jsm") + +this.JNI = 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.int64_t; + case "S": return ctypes.int16_t; + 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("jsjni_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._getStaticMethodID; + return this._getStaticMethodID = this.lib.declare("jsjni_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("jsjni_ExceptionCheck", + ctypes.default_abi, + ctypes.bool); + }, + + exceptionCheck: function() { + return this._exceptionCheck(); + }, + + get _callStaticVoidMethod() { + delete this._callStaticVoidMethod; + return this._callStaticVoidMethod = this.lib.declare("jsjni_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, aMethod.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("jsjni_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); + } +}