michael@0: /* Very basic JNI support for JS michael@0: * michael@0: * Example Usage: michael@0: * let jni = new JNI(); michael@0: * cls = jni.findClass("org/mozilla/gecko/GeckoAppShell"); michael@0: * method = jni.getStaticMethodID(cls, "getPreferredIconSize", "(I)I"); michael@0: * michael@0: * let val = jni.callStaticIntMethod(cls, method, 3); michael@0: * // close the jni library when you are done michael@0: * jni.close(); michael@0: * michael@0: * Note: the getters in this file are deleted and replaced with static michael@0: * values once computed, as in, for example michael@0: * http://code.activestate.com/recipes/577310-using-a-getter-for-a-one-time-calculation-of-a-jav/ michael@0: */ michael@0: this.EXPORTED_SYMBOLS = ["JNI"]; michael@0: michael@0: Components.utils.import("resource://gre/modules/ctypes.jsm") michael@0: Components.utils.import("resource://gre/modules/Services.jsm") michael@0: michael@0: this.JNI = function JNI() { } michael@0: michael@0: JNI.prototype = { michael@0: get lib() { michael@0: delete this.lib; michael@0: return this.lib = ctypes.open("libxul.so"); michael@0: }, michael@0: michael@0: getType: function(aType) { michael@0: switch(aType) { michael@0: case "B": return ctypes.char; michael@0: case "C": return ctypes.char; michael@0: case "D": return ctypes.double; michael@0: case "F": return ctypes.float; michael@0: case "I": return ctypes.int32_t; michael@0: case "J": return ctypes.int64_t; michael@0: case "S": return ctypes.int16_t; michael@0: case "V": return ctypes.void_t; michael@0: case "Z": return ctypes.bool; michael@0: default: return this.types.jobject michael@0: } michael@0: }, michael@0: michael@0: getArgs: function(aMethod, aArgs) { michael@0: if (aArgs.length != aMethod.parameters.length) michael@0: throw ("Incorrect number of arguments passed to " + aMethod.name); michael@0: michael@0: // Convert arguments to an array of jvalue objects michael@0: let modifiedArgs = new ctypes.ArrayType(this.types.jvalue, aMethod.parameters.length)(); michael@0: for (let i = 0; i < aMethod.parameters.length; i++) { michael@0: let parameter = aMethod.parameters[i]; michael@0: let arg = new this.types.jvalue(); michael@0: michael@0: if (aArgs[i] instanceof Array || parameter[0] == "[") michael@0: throw "No support for array arguments yet"; michael@0: else michael@0: ctypes.cast(arg, this.getType(parameter)).value = aArgs[i]; michael@0: michael@0: modifiedArgs[i] = arg; michael@0: } michael@0: michael@0: return modifiedArgs; michael@0: }, michael@0: michael@0: types: { michael@0: jobject: ctypes.StructType("_jobject").ptr, michael@0: jclass: ctypes.StructType("_jobject").ptr, michael@0: jmethodID: ctypes.StructType("jmethodID").ptr, michael@0: jvalue: ctypes.double michael@0: }, michael@0: michael@0: get _findClass() { michael@0: delete this._findClass; michael@0: return this._findClass = this.lib.declare("jsjni_FindClass", michael@0: ctypes.default_abi, michael@0: this.types.jclass, michael@0: ctypes.char.ptr); michael@0: }, michael@0: michael@0: findClass: function(name) { michael@0: let ret = this._findClass(name); michael@0: if (this.exceptionCheck()) michael@0: throw("Can't find class " + name); michael@0: return ret; michael@0: }, michael@0: michael@0: get _getStaticMethodID() { michael@0: delete this._getStaticMethodID; michael@0: return this._getStaticMethodID = this.lib.declare("jsjni_GetStaticMethodID", michael@0: ctypes.default_abi, michael@0: this.types.jmethodID, michael@0: this.types.jclass, // class michael@0: ctypes.char.ptr, // method name michael@0: ctypes.char.ptr); // signature michael@0: }, michael@0: michael@0: getStaticMethodID: function(aClass, aName, aSignature) { michael@0: let ret = this._getStaticMethodID(aClass, aName, aSignature); michael@0: if (this.exceptionCheck()) michael@0: throw("Can't find method " + aName); michael@0: return new jMethod(aName, ret, aSignature); michael@0: }, michael@0: michael@0: get _exceptionCheck() { michael@0: delete this._exceptionCheck; michael@0: return this._exceptionCheck = this.lib.declare("jsjni_ExceptionCheck", michael@0: ctypes.default_abi, michael@0: ctypes.bool); michael@0: }, michael@0: michael@0: exceptionCheck: function() { michael@0: return this._exceptionCheck(); michael@0: }, michael@0: michael@0: get _callStaticVoidMethod() { michael@0: delete this._callStaticVoidMethod; michael@0: return this._callStaticVoidMethod = this.lib.declare("jsjni_CallStaticVoidMethodA", michael@0: ctypes.default_abi, michael@0: ctypes.void_t, michael@0: this.types.jclass, michael@0: this.types.jmethodID, michael@0: this.types.jvalue.ptr); michael@0: }, michael@0: michael@0: callStaticVoidMethod: function(aClass, aMethod) { michael@0: let args = Array.prototype.slice.apply(arguments, [2]); michael@0: this._callStaticVoidMethod(aClass, aMethod.methodId, this.getArgs(aMethod, args)); michael@0: if (this.exceptionCheck()) michael@0: throw("Error calling static void method"); michael@0: }, michael@0: michael@0: get _callStaticIntMethod() { michael@0: delete this._callStaticIntMethod; michael@0: return this._callStaticIntMethod = this.lib.declare("jsjni_CallStaticIntMethodA", michael@0: ctypes.default_abi, michael@0: ctypes.int, michael@0: this.types.jclass, michael@0: this.types.jmethodID, michael@0: this.types.jvalue.ptr); michael@0: }, michael@0: michael@0: callStaticIntMethod: function(aClass, aMethod) { michael@0: let args = Array.prototype.slice.apply(arguments, [2]); michael@0: let ret = this._callStaticIntMethod(aClass, aMethod.methodId, this.getArgs(aMethod, args)); michael@0: if (this.exceptionCheck()) michael@0: throw("Error calling static int method"); michael@0: return ret; michael@0: }, michael@0: michael@0: close: function() { michael@0: this.lib.close(); michael@0: }, michael@0: } michael@0: michael@0: function jMethod(name, jMethodId, signature) { michael@0: this.name = name; michael@0: this.methodId = jMethodId; michael@0: this.signature = signature; michael@0: } michael@0: michael@0: jMethod.prototype = { michael@0: parameters: [], michael@0: returnType: null, michael@0: _signature: "", michael@0: michael@0: // this just splits up the return value from the parameters michael@0: signatureRegExp: /^\(([^\)]*)\)(.*)$/, michael@0: michael@0: // This splits up the actual parameters michael@0: parameterRegExp: /(\[*)(B|C|D|F|I|J|S|V|Z|L[^;]*;)/y, michael@0: michael@0: parseSignature: function(aSignature) { michael@0: let [, parameters, returnType] = this.signatureRegExp.exec(aSignature); michael@0: michael@0: // parse the parameters that should be passed to this method michael@0: if (parameters) { michael@0: let parameter = this.parameterRegExp.exec(parameters); michael@0: while (parameter) { michael@0: this.parameters.push(parameter[0]); michael@0: parameter = this.parameterRegExp.exec(parameters); michael@0: } michael@0: } else { michael@0: this.parameters = []; michael@0: } michael@0: michael@0: // parse the return type michael@0: this.returnType = returnType; michael@0: }, michael@0: michael@0: _signature: "", michael@0: get signature() { return this._signature; }, michael@0: set signature(val) { michael@0: this.parameters = []; michael@0: this.returnType = null; michael@0: this.parseSignature(val); michael@0: } michael@0: }