|
1 /* Very basic JNI support for JS |
|
2 * |
|
3 * Example Usage: |
|
4 * let jni = new JNI(); |
|
5 * cls = jni.findClass("org/mozilla/gecko/GeckoAppShell"); |
|
6 * method = jni.getStaticMethodID(cls, "getPreferredIconSize", "(I)I"); |
|
7 * |
|
8 * let val = jni.callStaticIntMethod(cls, method, 3); |
|
9 * // close the jni library when you are done |
|
10 * jni.close(); |
|
11 * |
|
12 * Note: the getters in this file are deleted and replaced with static |
|
13 * values once computed, as in, for example |
|
14 * http://code.activestate.com/recipes/577310-using-a-getter-for-a-one-time-calculation-of-a-jav/ |
|
15 */ |
|
16 this.EXPORTED_SYMBOLS = ["JNI"]; |
|
17 |
|
18 Components.utils.import("resource://gre/modules/ctypes.jsm") |
|
19 Components.utils.import("resource://gre/modules/Services.jsm") |
|
20 |
|
21 this.JNI = function JNI() { } |
|
22 |
|
23 JNI.prototype = { |
|
24 get lib() { |
|
25 delete this.lib; |
|
26 return this.lib = ctypes.open("libxul.so"); |
|
27 }, |
|
28 |
|
29 getType: function(aType) { |
|
30 switch(aType) { |
|
31 case "B": return ctypes.char; |
|
32 case "C": return ctypes.char; |
|
33 case "D": return ctypes.double; |
|
34 case "F": return ctypes.float; |
|
35 case "I": return ctypes.int32_t; |
|
36 case "J": return ctypes.int64_t; |
|
37 case "S": return ctypes.int16_t; |
|
38 case "V": return ctypes.void_t; |
|
39 case "Z": return ctypes.bool; |
|
40 default: return this.types.jobject |
|
41 } |
|
42 }, |
|
43 |
|
44 getArgs: function(aMethod, aArgs) { |
|
45 if (aArgs.length != aMethod.parameters.length) |
|
46 throw ("Incorrect number of arguments passed to " + aMethod.name); |
|
47 |
|
48 // Convert arguments to an array of jvalue objects |
|
49 let modifiedArgs = new ctypes.ArrayType(this.types.jvalue, aMethod.parameters.length)(); |
|
50 for (let i = 0; i < aMethod.parameters.length; i++) { |
|
51 let parameter = aMethod.parameters[i]; |
|
52 let arg = new this.types.jvalue(); |
|
53 |
|
54 if (aArgs[i] instanceof Array || parameter[0] == "[") |
|
55 throw "No support for array arguments yet"; |
|
56 else |
|
57 ctypes.cast(arg, this.getType(parameter)).value = aArgs[i]; |
|
58 |
|
59 modifiedArgs[i] = arg; |
|
60 } |
|
61 |
|
62 return modifiedArgs; |
|
63 }, |
|
64 |
|
65 types: { |
|
66 jobject: ctypes.StructType("_jobject").ptr, |
|
67 jclass: ctypes.StructType("_jobject").ptr, |
|
68 jmethodID: ctypes.StructType("jmethodID").ptr, |
|
69 jvalue: ctypes.double |
|
70 }, |
|
71 |
|
72 get _findClass() { |
|
73 delete this._findClass; |
|
74 return this._findClass = this.lib.declare("jsjni_FindClass", |
|
75 ctypes.default_abi, |
|
76 this.types.jclass, |
|
77 ctypes.char.ptr); |
|
78 }, |
|
79 |
|
80 findClass: function(name) { |
|
81 let ret = this._findClass(name); |
|
82 if (this.exceptionCheck()) |
|
83 throw("Can't find class " + name); |
|
84 return ret; |
|
85 }, |
|
86 |
|
87 get _getStaticMethodID() { |
|
88 delete this._getStaticMethodID; |
|
89 return this._getStaticMethodID = this.lib.declare("jsjni_GetStaticMethodID", |
|
90 ctypes.default_abi, |
|
91 this.types.jmethodID, |
|
92 this.types.jclass, // class |
|
93 ctypes.char.ptr, // method name |
|
94 ctypes.char.ptr); // signature |
|
95 }, |
|
96 |
|
97 getStaticMethodID: function(aClass, aName, aSignature) { |
|
98 let ret = this._getStaticMethodID(aClass, aName, aSignature); |
|
99 if (this.exceptionCheck()) |
|
100 throw("Can't find method " + aName); |
|
101 return new jMethod(aName, ret, aSignature); |
|
102 }, |
|
103 |
|
104 get _exceptionCheck() { |
|
105 delete this._exceptionCheck; |
|
106 return this._exceptionCheck = this.lib.declare("jsjni_ExceptionCheck", |
|
107 ctypes.default_abi, |
|
108 ctypes.bool); |
|
109 }, |
|
110 |
|
111 exceptionCheck: function() { |
|
112 return this._exceptionCheck(); |
|
113 }, |
|
114 |
|
115 get _callStaticVoidMethod() { |
|
116 delete this._callStaticVoidMethod; |
|
117 return this._callStaticVoidMethod = this.lib.declare("jsjni_CallStaticVoidMethodA", |
|
118 ctypes.default_abi, |
|
119 ctypes.void_t, |
|
120 this.types.jclass, |
|
121 this.types.jmethodID, |
|
122 this.types.jvalue.ptr); |
|
123 }, |
|
124 |
|
125 callStaticVoidMethod: function(aClass, aMethod) { |
|
126 let args = Array.prototype.slice.apply(arguments, [2]); |
|
127 this._callStaticVoidMethod(aClass, aMethod.methodId, this.getArgs(aMethod, args)); |
|
128 if (this.exceptionCheck()) |
|
129 throw("Error calling static void method"); |
|
130 }, |
|
131 |
|
132 get _callStaticIntMethod() { |
|
133 delete this._callStaticIntMethod; |
|
134 return this._callStaticIntMethod = this.lib.declare("jsjni_CallStaticIntMethodA", |
|
135 ctypes.default_abi, |
|
136 ctypes.int, |
|
137 this.types.jclass, |
|
138 this.types.jmethodID, |
|
139 this.types.jvalue.ptr); |
|
140 }, |
|
141 |
|
142 callStaticIntMethod: function(aClass, aMethod) { |
|
143 let args = Array.prototype.slice.apply(arguments, [2]); |
|
144 let ret = this._callStaticIntMethod(aClass, aMethod.methodId, this.getArgs(aMethod, args)); |
|
145 if (this.exceptionCheck()) |
|
146 throw("Error calling static int method"); |
|
147 return ret; |
|
148 }, |
|
149 |
|
150 close: function() { |
|
151 this.lib.close(); |
|
152 }, |
|
153 } |
|
154 |
|
155 function jMethod(name, jMethodId, signature) { |
|
156 this.name = name; |
|
157 this.methodId = jMethodId; |
|
158 this.signature = signature; |
|
159 } |
|
160 |
|
161 jMethod.prototype = { |
|
162 parameters: [], |
|
163 returnType: null, |
|
164 _signature: "", |
|
165 |
|
166 // this just splits up the return value from the parameters |
|
167 signatureRegExp: /^\(([^\)]*)\)(.*)$/, |
|
168 |
|
169 // This splits up the actual parameters |
|
170 parameterRegExp: /(\[*)(B|C|D|F|I|J|S|V|Z|L[^;]*;)/y, |
|
171 |
|
172 parseSignature: function(aSignature) { |
|
173 let [, parameters, returnType] = this.signatureRegExp.exec(aSignature); |
|
174 |
|
175 // parse the parameters that should be passed to this method |
|
176 if (parameters) { |
|
177 let parameter = this.parameterRegExp.exec(parameters); |
|
178 while (parameter) { |
|
179 this.parameters.push(parameter[0]); |
|
180 parameter = this.parameterRegExp.exec(parameters); |
|
181 } |
|
182 } else { |
|
183 this.parameters = []; |
|
184 } |
|
185 |
|
186 // parse the return type |
|
187 this.returnType = returnType; |
|
188 }, |
|
189 |
|
190 _signature: "", |
|
191 get signature() { return this._signature; }, |
|
192 set signature(val) { |
|
193 this.parameters = []; |
|
194 this.returnType = null; |
|
195 this.parseSignature(val); |
|
196 } |
|
197 } |