]>
Commit | Line | Data |
---|---|---|
b37bf2e1 A |
1 | /* |
2 | * Copyright (C) 2003 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * Redistribution and use in source and binary forms, with or without | |
5 | * modification, are permitted provided that the following conditions | |
6 | * are met: | |
7 | * 1. Redistributions of source code must retain the above copyright | |
8 | * notice, this list of conditions and the following disclaimer. | |
9 | * 2. Redistributions in binary form must reproduce the above copyright | |
10 | * notice, this list of conditions and the following disclaimer in the | |
11 | * documentation and/or other materials provided with the distribution. | |
12 | * | |
13 | * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY | |
14 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR | |
17 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
18 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
19 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
20 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | |
21 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
23 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
24 | */ | |
25 | ||
26 | #include "config.h" | |
27 | ||
28 | #if ENABLE(JAVA_BINDINGS) | |
29 | ||
30 | #include "jni_class.h" | |
31 | #include "jni_instance.h" | |
32 | #include "jni_runtime.h" | |
33 | #include "jni_utility.h" | |
34 | #include "runtime_object.h" | |
35 | #include "runtime_root.h" | |
36 | ||
37 | #ifdef NDEBUG | |
38 | #define JS_LOG(formatAndArgs...) ((void)0) | |
39 | #else | |
40 | #define JS_LOG(formatAndArgs...) { \ | |
41 | fprintf (stderr, "%s:%d -- %s: ", __FILE__, __LINE__, __FUNCTION__); \ | |
42 | fprintf(stderr, formatAndArgs); \ | |
43 | } | |
44 | #endif | |
45 | ||
46 | using namespace KJS::Bindings; | |
47 | using namespace KJS; | |
48 | ||
49 | JavaInstance::JavaInstance (jobject instance, PassRefPtr<RootObject> rootObject) | |
50 | : Instance(rootObject) | |
51 | { | |
52 | _instance = new JObjectWrapper (instance); | |
53 | _class = 0; | |
54 | } | |
55 | ||
56 | JavaInstance::~JavaInstance () | |
57 | { | |
58 | delete _class; | |
59 | } | |
60 | ||
61 | #define NUM_LOCAL_REFS 64 | |
62 | ||
63 | void JavaInstance::begin() | |
64 | { | |
65 | getJNIEnv()->PushLocalFrame (NUM_LOCAL_REFS); | |
66 | } | |
67 | ||
68 | void JavaInstance::end() | |
69 | { | |
70 | getJNIEnv()->PopLocalFrame (NULL); | |
71 | } | |
72 | ||
73 | Class *JavaInstance::getClass() const | |
74 | { | |
75 | if (_class == 0) | |
76 | _class = new JavaClass (_instance->_instance); | |
77 | return _class; | |
78 | } | |
79 | ||
80 | JSValue *JavaInstance::stringValue() const | |
81 | { | |
82 | JSLock lock; | |
83 | ||
84 | jstring stringValue = (jstring)callJNIObjectMethod (_instance->_instance, "toString", "()Ljava/lang/String;"); | |
85 | JNIEnv *env = getJNIEnv(); | |
86 | const jchar *c = getUCharactersFromJStringInEnv(env, stringValue); | |
87 | UString u((const UChar *)c, (int)env->GetStringLength(stringValue)); | |
88 | releaseUCharactersForJStringInEnv(env, stringValue, c); | |
89 | return jsString(u); | |
90 | } | |
91 | ||
92 | JSValue *JavaInstance::numberValue() const | |
93 | { | |
94 | jdouble doubleValue = callJNIDoubleMethod (_instance->_instance, "doubleValue", "()D"); | |
95 | return jsNumber(doubleValue); | |
96 | } | |
97 | ||
98 | JSValue *JavaInstance::booleanValue() const | |
99 | { | |
100 | jboolean booleanValue = callJNIBooleanMethod (_instance->_instance, "booleanValue", "()Z"); | |
101 | return jsBoolean(booleanValue); | |
102 | } | |
103 | ||
104 | JSValue *JavaInstance::invokeMethod (ExecState *exec, const MethodList &methodList, const List &args) | |
105 | { | |
106 | int i, count = args.size(); | |
107 | jvalue *jArgs; | |
108 | JSValue *resultValue; | |
109 | Method *method = 0; | |
110 | size_t numMethods = methodList.size(); | |
111 | ||
112 | // Try to find a good match for the overloaded method. The | |
113 | // fundamental problem is that JavaScript doesn have the | |
114 | // notion of method overloading and Java does. We could | |
115 | // get a bit more sophisticated and attempt to does some | |
116 | // type checking as we as checking the number of parameters. | |
117 | Method *aMethod; | |
118 | for (size_t methodIndex = 0; methodIndex < numMethods; methodIndex++) { | |
119 | aMethod = methodList[methodIndex]; | |
120 | if (aMethod->numParameters() == count) { | |
121 | method = aMethod; | |
122 | break; | |
123 | } | |
124 | } | |
125 | if (method == 0) { | |
126 | JS_LOG ("unable to find an appropiate method\n"); | |
127 | return jsUndefined(); | |
128 | } | |
129 | ||
130 | const JavaMethod *jMethod = static_cast<const JavaMethod*>(method); | |
131 | JS_LOG ("call %s %s on %p\n", method->name(), jMethod->signature(), _instance->_instance); | |
132 | ||
133 | if (count > 0) { | |
134 | jArgs = (jvalue *)malloc (count * sizeof(jvalue)); | |
135 | } | |
136 | else | |
137 | jArgs = 0; | |
138 | ||
139 | for (i = 0; i < count; i++) { | |
140 | JavaParameter* aParameter = jMethod->parameterAt(i); | |
141 | jArgs[i] = convertValueToJValue (exec, args.at(i), aParameter->getJNIType(), aParameter->type()); | |
142 | JS_LOG("arg[%d] = %s\n", i, args.at(i)->toString(exec).ascii()); | |
143 | } | |
144 | ||
145 | jvalue result; | |
146 | ||
147 | // Try to use the JNI abstraction first, otherwise fall back to | |
148 | // nornmal JNI. The JNI dispatch abstraction allows the Java plugin | |
149 | // to dispatch the call on the appropriate internal VM thread. | |
150 | RootObject* rootObject = this->rootObject(); | |
151 | if (!rootObject) | |
152 | return jsUndefined(); | |
153 | ||
154 | bool handled = false; | |
155 | if (rootObject->nativeHandle()) { | |
156 | jobject obj = _instance->_instance; | |
157 | JSValue *exceptionDescription = NULL; | |
158 | const char *callingURL = 0; // FIXME, need to propagate calling URL to Java | |
159 | handled = dispatchJNICall(rootObject->nativeHandle(), obj, jMethod->isStatic(), jMethod->JNIReturnType(), jMethod->methodID(obj), jArgs, result, callingURL, exceptionDescription); | |
160 | if (exceptionDescription) { | |
161 | throwError(exec, GeneralError, exceptionDescription->toString(exec)); | |
162 | free (jArgs); | |
163 | return jsUndefined(); | |
164 | } | |
165 | } | |
166 | ||
167 | // The following code can be conditionally removed once we have a Tiger update that | |
168 | // contains the new Java plugin. It is needed for builds prior to Tiger. | |
169 | if (!handled) { | |
170 | jobject obj = _instance->_instance; | |
171 | switch (jMethod->JNIReturnType()){ | |
172 | case void_type: { | |
173 | callJNIVoidMethodIDA (obj, jMethod->methodID(obj), jArgs); | |
174 | } | |
175 | break; | |
176 | ||
177 | case object_type: { | |
178 | result.l = callJNIObjectMethodIDA (obj, jMethod->methodID(obj), jArgs); | |
179 | } | |
180 | break; | |
181 | ||
182 | case boolean_type: { | |
183 | result.z = callJNIBooleanMethodIDA (obj, jMethod->methodID(obj), jArgs); | |
184 | } | |
185 | break; | |
186 | ||
187 | case byte_type: { | |
188 | result.b = callJNIByteMethodIDA (obj, jMethod->methodID(obj), jArgs); | |
189 | } | |
190 | break; | |
191 | ||
192 | case char_type: { | |
193 | result.c = callJNICharMethodIDA (obj, jMethod->methodID(obj), jArgs); | |
194 | } | |
195 | break; | |
196 | ||
197 | case short_type: { | |
198 | result.s = callJNIShortMethodIDA (obj, jMethod->methodID(obj), jArgs); | |
199 | } | |
200 | break; | |
201 | ||
202 | case int_type: { | |
203 | result.i = callJNIIntMethodIDA (obj, jMethod->methodID(obj), jArgs); | |
204 | } | |
205 | break; | |
206 | ||
207 | case long_type: { | |
208 | result.j = callJNILongMethodIDA (obj, jMethod->methodID(obj), jArgs); | |
209 | } | |
210 | break; | |
211 | ||
212 | case float_type: { | |
213 | result.f = callJNIFloatMethodIDA (obj, jMethod->methodID(obj), jArgs); | |
214 | } | |
215 | break; | |
216 | ||
217 | case double_type: { | |
218 | result.d = callJNIDoubleMethodIDA (obj, jMethod->methodID(obj), jArgs); | |
219 | } | |
220 | break; | |
221 | ||
222 | case invalid_type: | |
223 | default: { | |
224 | } | |
225 | break; | |
226 | } | |
227 | } | |
228 | ||
229 | switch (jMethod->JNIReturnType()){ | |
230 | case void_type: { | |
231 | resultValue = jsUndefined(); | |
232 | } | |
233 | break; | |
234 | ||
235 | case object_type: { | |
236 | if (result.l != 0) { | |
237 | const char *arrayType = jMethod->returnType(); | |
238 | if (arrayType[0] == '[') { | |
239 | resultValue = JavaArray::convertJObjectToArray(exec, result.l, arrayType, rootObject); | |
240 | } | |
241 | else { | |
242 | resultValue = Instance::createRuntimeObject(Instance::JavaLanguage, result.l, rootObject); | |
243 | } | |
244 | } | |
245 | else { | |
246 | resultValue = jsUndefined(); | |
247 | } | |
248 | } | |
249 | break; | |
250 | ||
251 | case boolean_type: { | |
252 | resultValue = jsBoolean(result.z); | |
253 | } | |
254 | break; | |
255 | ||
256 | case byte_type: { | |
257 | resultValue = jsNumber(result.b); | |
258 | } | |
259 | break; | |
260 | ||
261 | case char_type: { | |
262 | resultValue = jsNumber(result.c); | |
263 | } | |
264 | break; | |
265 | ||
266 | case short_type: { | |
267 | resultValue = jsNumber(result.s); | |
268 | } | |
269 | break; | |
270 | ||
271 | case int_type: { | |
272 | resultValue = jsNumber(result.i); | |
273 | } | |
274 | break; | |
275 | ||
276 | case long_type: { | |
277 | resultValue = jsNumber(result.j); | |
278 | } | |
279 | break; | |
280 | ||
281 | case float_type: { | |
282 | resultValue = jsNumber(result.f); | |
283 | } | |
284 | break; | |
285 | ||
286 | case double_type: { | |
287 | resultValue = jsNumber(result.d); | |
288 | } | |
289 | break; | |
290 | ||
291 | case invalid_type: | |
292 | default: { | |
293 | resultValue = jsUndefined(); | |
294 | } | |
295 | break; | |
296 | } | |
297 | ||
298 | free (jArgs); | |
299 | ||
300 | return resultValue; | |
301 | } | |
302 | ||
303 | JSValue *JavaInstance::defaultValue (JSType hint) const | |
304 | { | |
305 | if (hint == StringType) { | |
306 | return stringValue(); | |
307 | } | |
308 | else if (hint == NumberType) { | |
309 | return numberValue(); | |
310 | } | |
311 | else if (hint == BooleanType) { | |
312 | return booleanValue(); | |
313 | } | |
314 | else if (hint == UnspecifiedType) { | |
315 | JavaClass *aClass = static_cast<JavaClass*>(getClass()); | |
316 | if (aClass->isStringClass()) { | |
317 | return stringValue(); | |
318 | } | |
319 | else if (aClass->isNumberClass()) { | |
320 | return numberValue(); | |
321 | } | |
322 | else if (aClass->isBooleanClass()) { | |
323 | return booleanValue(); | |
324 | } | |
325 | } | |
326 | ||
327 | return valueOf(); | |
328 | } | |
329 | ||
330 | JSValue *JavaInstance::valueOf() const | |
331 | { | |
332 | return stringValue(); | |
333 | } | |
334 | ||
335 | JObjectWrapper::JObjectWrapper(jobject instance) | |
336 | : _refCount(0) | |
337 | { | |
338 | assert (instance != 0); | |
339 | ||
340 | // Cache the JNIEnv used to get the global ref for this java instanace. | |
341 | // It'll be used to delete the reference. | |
342 | _env = getJNIEnv(); | |
343 | ||
344 | _instance = _env->NewGlobalRef (instance); | |
345 | ||
346 | JS_LOG ("new global ref %p for %p\n", _instance, instance); | |
347 | ||
348 | if (_instance == NULL) { | |
349 | fprintf (stderr, "%s: could not get GlobalRef for %p\n", __PRETTY_FUNCTION__, instance); | |
350 | } | |
351 | } | |
352 | ||
353 | JObjectWrapper::~JObjectWrapper() { | |
354 | JS_LOG ("deleting global ref %p\n", _instance); | |
355 | _env->DeleteGlobalRef (_instance); | |
356 | } | |
357 | ||
358 | #endif // ENABLE(JAVA_BINDINGS) |