Bug 787271 - Add methods to call in jni through ctypes. r=mfinkle,blassey

This commit is contained in:
Wes Johnston 2012-09-12 11:31:17 -07:00
parent 40565bd9e7
commit 8234baf59c
4 changed files with 253 additions and 0 deletions

View File

@ -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);
}
}

View File

@ -13,6 +13,7 @@ EXTRA_JS_MODULES = \
LocaleRepository.jsm \
linuxTypes.jsm \
video.jsm \
JNI.jsm \
$(NULL)
EXTRA_PP_JS_MODULES = \

View File

@ -2538,3 +2538,55 @@ AndroidBridge::NotifyPaintedRect(float top, float left, float bottom, float righ
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);
}
}

View File

@ -36,6 +36,13 @@ 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();