gecko/mobile/android/modules/JNI.jsm

194 lines
6.2 KiB
JavaScript

/* 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("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._getStatisMethodID;
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, 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("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);
}
}