]>
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 <internal.h> | |
31 | #include <ustring.h> | |
32 | #include <value.h> | |
33 | ||
34 | #include <jni_utility.h> | |
35 | #include <jni_runtime.h> | |
36 | ||
37 | #include <runtime_array.h> | |
38 | #include <runtime_object.h> | |
39 | #include <runtime_root.h> | |
40 | ||
41 | #ifdef NDEBUG | |
42 | #define JS_LOG(formatAndArgs...) ((void)0) | |
43 | #else | |
44 | #define JS_LOG(formatAndArgs...) { \ | |
45 | fprintf (stderr, "%s:%d -- %s: ", __FILE__, __LINE__, __FUNCTION__); \ | |
46 | fprintf(stderr, formatAndArgs); \ | |
47 | } | |
48 | #endif | |
49 | ||
50 | using namespace KJS; | |
51 | using namespace KJS::Bindings; | |
52 | ||
53 | ||
54 | JavaParameter::JavaParameter (JNIEnv *env, jstring type) | |
55 | { | |
56 | _type = JavaString (env, type); | |
57 | _JNIType = JNITypeFromClassName (_type.UTF8String()); | |
58 | } | |
59 | ||
60 | JavaField::JavaField (JNIEnv *env, jobject aField) | |
61 | { | |
62 | // Get field type | |
63 | jobject fieldType = callJNIObjectMethod (aField, "getType", "()Ljava/lang/Class;"); | |
64 | jstring fieldTypeName = (jstring)callJNIObjectMethod (fieldType, "getName", "()Ljava/lang/String;"); | |
65 | _type = JavaString(env, fieldTypeName); | |
66 | _JNIType = JNITypeFromClassName (_type.UTF8String()); | |
67 | ||
68 | // Get field name | |
69 | jstring fieldName = (jstring)callJNIObjectMethod (aField, "getName", "()Ljava/lang/String;"); | |
70 | _name = JavaString(env, fieldName); | |
71 | ||
72 | _field = new JObjectWrapper(aField); | |
73 | } | |
74 | ||
75 | JSValue* JavaArray::convertJObjectToArray(ExecState* exec, jobject anObject, const char* type, PassRefPtr<RootObject> rootObject) | |
76 | { | |
77 | if (type[0] != '[') | |
78 | return jsUndefined(); | |
79 | ||
80 | return new RuntimeArray(exec, new JavaArray((jobject)anObject, type, rootObject)); | |
81 | } | |
82 | ||
83 | jvalue JavaField::dispatchValueFromInstance(ExecState *exec, const JavaInstance *instance, const char *name, const char *sig, JNIType returnType) const | |
84 | { | |
85 | jobject jinstance = instance->javaInstance(); | |
86 | jobject fieldJInstance = _field->_instance; | |
87 | JNIEnv *env = getJNIEnv(); | |
88 | jvalue result; | |
89 | ||
90 | bzero (&result, sizeof(jvalue)); | |
91 | jclass cls = env->GetObjectClass(fieldJInstance); | |
92 | if ( cls != NULL ) { | |
93 | jmethodID mid = env->GetMethodID(cls, name, sig); | |
94 | if ( mid != NULL ) | |
95 | { | |
96 | RootObject* rootObject = instance->rootObject(); | |
97 | if (rootObject && rootObject->nativeHandle()) { | |
98 | JSValue *exceptionDescription = NULL; | |
99 | jvalue args[1]; | |
100 | ||
101 | args[0].l = jinstance; | |
102 | dispatchJNICall(rootObject->nativeHandle(), fieldJInstance, false, returnType, mid, args, result, 0, exceptionDescription); | |
103 | if (exceptionDescription) | |
104 | throwError(exec, GeneralError, exceptionDescription->toString(exec)); | |
105 | } | |
106 | } | |
107 | } | |
108 | return result; | |
109 | } | |
110 | ||
111 | JSValue *JavaField::valueFromInstance(ExecState *exec, const Instance *i) const | |
112 | { | |
113 | const JavaInstance *instance = static_cast<const JavaInstance *>(i); | |
114 | ||
115 | JSValue *jsresult = jsUndefined(); | |
116 | ||
117 | switch (_JNIType) { | |
118 | case array_type: | |
119 | case object_type: { | |
120 | jvalue result = dispatchValueFromInstance (exec, instance, "get", "(Ljava/lang/Object;)Ljava/lang/Object;", object_type); | |
121 | jobject anObject = result.l; | |
122 | ||
123 | const char *arrayType = type(); | |
124 | if (arrayType[0] == '[') { | |
125 | jsresult = JavaArray::convertJObjectToArray(exec, anObject, arrayType, instance->rootObject()); | |
126 | } | |
127 | else if (anObject != 0){ | |
128 | jsresult = Instance::createRuntimeObject(Instance::JavaLanguage, anObject, instance->rootObject()); | |
129 | } | |
130 | } | |
131 | break; | |
132 | ||
133 | case boolean_type: | |
134 | jsresult = jsBoolean(dispatchValueFromInstance(exec, instance, "getBoolean", "(Ljava/lang/Object;)Z", boolean_type).z); | |
135 | break; | |
136 | ||
137 | case byte_type: | |
138 | case char_type: | |
139 | case short_type: | |
140 | ||
141 | case int_type: { | |
142 | jint value; | |
143 | jvalue result = dispatchValueFromInstance (exec, instance, "getInt", "(Ljava/lang/Object;)I", int_type); | |
144 | value = result.i; | |
145 | jsresult = jsNumber((int)value); | |
146 | } | |
147 | break; | |
148 | ||
149 | case long_type: | |
150 | case float_type: | |
151 | case double_type: { | |
152 | jdouble value; | |
153 | jvalue result = dispatchValueFromInstance (exec, instance, "getDouble", "(Ljava/lang/Object;)D", double_type); | |
154 | value = result.i; | |
155 | jsresult = jsNumber((double)value); | |
156 | } | |
157 | break; | |
158 | default: | |
159 | break; | |
160 | } | |
161 | ||
162 | JS_LOG ("getting %s = %s\n", name(), jsresult->toString(exec).ascii()); | |
163 | ||
164 | return jsresult; | |
165 | } | |
166 | ||
167 | void JavaField::dispatchSetValueToInstance(ExecState *exec, const JavaInstance *instance, jvalue javaValue, const char *name, const char *sig) const | |
168 | { | |
169 | jobject jinstance = instance->javaInstance(); | |
170 | jobject fieldJInstance = _field->_instance; | |
171 | JNIEnv *env = getJNIEnv(); | |
172 | ||
173 | jclass cls = env->GetObjectClass(fieldJInstance); | |
174 | if ( cls != NULL ) { | |
175 | jmethodID mid = env->GetMethodID(cls, name, sig); | |
176 | if ( mid != NULL ) | |
177 | { | |
178 | RootObject* rootObject = instance->rootObject(); | |
179 | if (rootObject && rootObject->nativeHandle()) { | |
180 | JSValue *exceptionDescription = NULL; | |
181 | jvalue args[2]; | |
182 | jvalue result; | |
183 | ||
184 | args[0].l = jinstance; | |
185 | args[1] = javaValue; | |
186 | dispatchJNICall(rootObject->nativeHandle(), fieldJInstance, false, void_type, mid, args, result, 0, exceptionDescription); | |
187 | if (exceptionDescription) | |
188 | throwError(exec, GeneralError, exceptionDescription->toString(exec)); | |
189 | } | |
190 | } | |
191 | } | |
192 | } | |
193 | ||
194 | void JavaField::setValueToInstance(ExecState *exec, const Instance *i, JSValue *aValue) const | |
195 | { | |
196 | const JavaInstance *instance = static_cast<const JavaInstance *>(i); | |
197 | jvalue javaValue = convertValueToJValue (exec, aValue, _JNIType, type()); | |
198 | ||
199 | JS_LOG ("setting value %s to %s\n", name(), aValue->toString(exec).ascii()); | |
200 | ||
201 | switch (_JNIType) { | |
202 | case array_type: | |
203 | case object_type: { | |
204 | dispatchSetValueToInstance (exec, instance, javaValue, "set", "(Ljava/lang/Object;Ljava/lang/Object;)V"); | |
205 | } | |
206 | break; | |
207 | ||
208 | case boolean_type: { | |
209 | dispatchSetValueToInstance (exec, instance, javaValue, "setBoolean", "(Ljava/lang/Object;Z)V"); | |
210 | } | |
211 | break; | |
212 | ||
213 | case byte_type: { | |
214 | dispatchSetValueToInstance (exec, instance, javaValue, "setByte", "(Ljava/lang/Object;B)V"); | |
215 | } | |
216 | break; | |
217 | ||
218 | case char_type: { | |
219 | dispatchSetValueToInstance (exec, instance, javaValue, "setChar", "(Ljava/lang/Object;C)V"); | |
220 | } | |
221 | break; | |
222 | ||
223 | case short_type: { | |
224 | dispatchSetValueToInstance (exec, instance, javaValue, "setShort", "(Ljava/lang/Object;S)V"); | |
225 | } | |
226 | break; | |
227 | ||
228 | case int_type: { | |
229 | dispatchSetValueToInstance (exec, instance, javaValue, "setInt", "(Ljava/lang/Object;I)V"); | |
230 | } | |
231 | break; | |
232 | ||
233 | case long_type: { | |
234 | dispatchSetValueToInstance (exec, instance, javaValue, "setLong", "(Ljava/lang/Object;J)V"); | |
235 | } | |
236 | break; | |
237 | ||
238 | case float_type: { | |
239 | dispatchSetValueToInstance (exec, instance, javaValue, "setFloat", "(Ljava/lang/Object;F)V"); | |
240 | } | |
241 | break; | |
242 | ||
243 | case double_type: { | |
244 | dispatchSetValueToInstance (exec, instance, javaValue, "setDouble", "(Ljava/lang/Object;D)V"); | |
245 | } | |
246 | break; | |
247 | default: | |
248 | break; | |
249 | } | |
250 | } | |
251 | ||
252 | JavaMethod::JavaMethod (JNIEnv *env, jobject aMethod) | |
253 | { | |
254 | // Get return type | |
255 | jobject returnType = callJNIObjectMethod (aMethod, "getReturnType", "()Ljava/lang/Class;"); | |
256 | jstring returnTypeName = (jstring)callJNIObjectMethod (returnType, "getName", "()Ljava/lang/String;"); | |
257 | _returnType =JavaString (env, returnTypeName); | |
258 | _JNIReturnType = JNITypeFromClassName (_returnType.UTF8String()); | |
259 | env->DeleteLocalRef (returnType); | |
260 | env->DeleteLocalRef (returnTypeName); | |
261 | ||
262 | // Get method name | |
263 | jstring methodName = (jstring)callJNIObjectMethod (aMethod, "getName", "()Ljava/lang/String;"); | |
264 | _name = JavaString (env, methodName); | |
265 | env->DeleteLocalRef (methodName); | |
266 | ||
267 | // Get parameters | |
268 | jarray jparameters = (jarray)callJNIObjectMethod (aMethod, "getParameterTypes", "()[Ljava/lang/Class;"); | |
269 | _numParameters = env->GetArrayLength (jparameters); | |
270 | _parameters = new JavaParameter[_numParameters]; | |
271 | ||
272 | int i; | |
273 | for (i = 0; i < _numParameters; i++) { | |
274 | jobject aParameter = env->GetObjectArrayElement ((jobjectArray)jparameters, i); | |
275 | jstring parameterName = (jstring)callJNIObjectMethod (aParameter, "getName", "()Ljava/lang/String;"); | |
276 | _parameters[i] = JavaParameter(env, parameterName); | |
277 | env->DeleteLocalRef (aParameter); | |
278 | env->DeleteLocalRef (parameterName); | |
279 | } | |
280 | env->DeleteLocalRef (jparameters); | |
281 | ||
282 | // Created lazily. | |
283 | _signature = 0; | |
284 | _methodID = 0; | |
285 | ||
286 | jclass modifierClass = env->FindClass("java/lang/reflect/Modifier"); | |
287 | int modifiers = callJNIIntMethod (aMethod, "getModifiers", "()I"); | |
288 | _isStatic = (bool)callJNIStaticBooleanMethod (modifierClass, "isStatic", "(I)Z", modifiers); | |
289 | } | |
290 | ||
291 | JavaMethod::~JavaMethod() | |
292 | { | |
293 | if (_signature) | |
294 | free(_signature); | |
295 | delete [] _parameters; | |
296 | }; | |
297 | ||
298 | // JNI method signatures use '/' between components of a class name, but | |
299 | // we get '.' between components from the reflection API. | |
300 | static void appendClassName(UString& aString, const char* className) | |
301 | { | |
302 | ASSERT(JSLock::lockCount() > 0); | |
303 | ||
304 | char *result, *cp = strdup(className); | |
305 | ||
306 | result = cp; | |
307 | while (*cp) { | |
308 | if (*cp == '.') | |
309 | *cp = '/'; | |
310 | cp++; | |
311 | } | |
312 | ||
313 | aString.append(result); | |
314 | ||
315 | free (result); | |
316 | } | |
317 | ||
318 | const char *JavaMethod::signature() const | |
319 | { | |
320 | if (!_signature) { | |
321 | JSLock lock; | |
322 | ||
323 | UString signatureBuilder("("); | |
324 | for (int i = 0; i < _numParameters; i++) { | |
325 | JavaParameter* aParameter = parameterAt(i); | |
326 | JNIType _JNIType = aParameter->getJNIType(); | |
327 | if (_JNIType == array_type) | |
328 | appendClassName(signatureBuilder, aParameter->type()); | |
329 | else { | |
330 | signatureBuilder.append(signatureFromPrimitiveType(_JNIType)); | |
331 | if (_JNIType == object_type) { | |
332 | appendClassName(signatureBuilder, aParameter->type()); | |
333 | signatureBuilder.append(";"); | |
334 | } | |
335 | } | |
336 | } | |
337 | signatureBuilder.append(")"); | |
338 | ||
339 | const char *returnType = _returnType.UTF8String(); | |
340 | if (_JNIReturnType == array_type) { | |
341 | appendClassName(signatureBuilder, returnType); | |
342 | } else { | |
343 | signatureBuilder.append(signatureFromPrimitiveType(_JNIReturnType)); | |
344 | if (_JNIReturnType == object_type) { | |
345 | appendClassName(signatureBuilder, returnType); | |
346 | signatureBuilder.append(";"); | |
347 | } | |
348 | } | |
349 | ||
350 | _signature = strdup(signatureBuilder.ascii()); | |
351 | } | |
352 | ||
353 | return _signature; | |
354 | } | |
355 | ||
356 | JNIType JavaMethod::JNIReturnType() const | |
357 | { | |
358 | return _JNIReturnType; | |
359 | } | |
360 | ||
361 | jmethodID JavaMethod::methodID (jobject obj) const | |
362 | { | |
363 | if (_methodID == 0) { | |
364 | _methodID = getMethodID (obj, name(), signature()); | |
365 | } | |
366 | return _methodID; | |
367 | } | |
368 | ||
369 | ||
370 | JavaArray::JavaArray(jobject array, const char* type, PassRefPtr<RootObject> rootObject) | |
371 | : Array(rootObject) | |
372 | { | |
373 | _array = new JObjectWrapper(array); | |
374 | // Java array are fixed length, so we can cache length. | |
375 | JNIEnv *env = getJNIEnv(); | |
376 | _length = env->GetArrayLength((jarray)_array->_instance); | |
377 | _type = strdup(type); | |
378 | _rootObject = rootObject; | |
379 | } | |
380 | ||
381 | JavaArray::~JavaArray () | |
382 | { | |
383 | free ((void *)_type); | |
384 | } | |
385 | ||
386 | RootObject* JavaArray::rootObject() const | |
387 | { | |
388 | return _rootObject && _rootObject->isValid() ? _rootObject.get() : 0; | |
389 | } | |
390 | ||
391 | void JavaArray::setValueAt(ExecState *exec, unsigned int index, JSValue *aValue) const | |
392 | { | |
393 | JNIEnv *env = getJNIEnv(); | |
394 | char *javaClassName = 0; | |
395 | ||
396 | JNIType arrayType = JNITypeFromPrimitiveType(_type[1]); | |
397 | if (_type[1] == 'L'){ | |
398 | // The type of the array will be something like: | |
399 | // "[Ljava.lang.string;". This is guaranteed, so no need | |
400 | // for extra sanity checks. | |
401 | javaClassName = strdup(&_type[2]); | |
402 | javaClassName[strchr(javaClassName, ';')-javaClassName] = 0; | |
403 | } | |
404 | jvalue aJValue = convertValueToJValue (exec, aValue, arrayType, javaClassName); | |
405 | ||
406 | switch (arrayType) { | |
407 | case object_type: { | |
408 | env->SetObjectArrayElement((jobjectArray)javaArray(), index, aJValue.l); | |
409 | break; | |
410 | } | |
411 | ||
412 | case boolean_type: { | |
413 | env->SetBooleanArrayRegion((jbooleanArray)javaArray(), index, 1, &aJValue.z); | |
414 | break; | |
415 | } | |
416 | ||
417 | case byte_type: { | |
418 | env->SetByteArrayRegion((jbyteArray)javaArray(), index, 1, &aJValue.b); | |
419 | break; | |
420 | } | |
421 | ||
422 | case char_type: { | |
423 | env->SetCharArrayRegion((jcharArray)javaArray(), index, 1, &aJValue.c); | |
424 | break; | |
425 | } | |
426 | ||
427 | case short_type: { | |
428 | env->SetShortArrayRegion((jshortArray)javaArray(), index, 1, &aJValue.s); | |
429 | break; | |
430 | } | |
431 | ||
432 | case int_type: { | |
433 | env->SetIntArrayRegion((jintArray)javaArray(), index, 1, &aJValue.i); | |
434 | break; | |
435 | } | |
436 | ||
437 | case long_type: { | |
438 | env->SetLongArrayRegion((jlongArray)javaArray(), index, 1, &aJValue.j); | |
439 | } | |
440 | ||
441 | case float_type: { | |
442 | env->SetFloatArrayRegion((jfloatArray)javaArray(), index, 1, &aJValue.f); | |
443 | break; | |
444 | } | |
445 | ||
446 | case double_type: { | |
447 | env->SetDoubleArrayRegion((jdoubleArray)javaArray(), index, 1, &aJValue.d); | |
448 | break; | |
449 | } | |
450 | default: | |
451 | break; | |
452 | } | |
453 | ||
454 | if (javaClassName) | |
455 | free ((void *)javaClassName); | |
456 | } | |
457 | ||
458 | ||
459 | JSValue *JavaArray::valueAt(ExecState *exec, unsigned int index) const | |
460 | { | |
461 | JNIEnv *env = getJNIEnv(); | |
462 | JNIType arrayType = JNITypeFromPrimitiveType(_type[1]); | |
463 | switch (arrayType) { | |
464 | case object_type: { | |
465 | jobjectArray objectArray = (jobjectArray)javaArray(); | |
466 | jobject anObject; | |
467 | anObject = env->GetObjectArrayElement(objectArray, index); | |
468 | ||
469 | // No object? | |
470 | if (!anObject) { | |
471 | return jsNull(); | |
472 | } | |
473 | ||
474 | // Nested array? | |
475 | if (_type[1] == '[') { | |
476 | return JavaArray::convertJObjectToArray(exec, anObject, _type+1, rootObject()); | |
477 | } | |
478 | // or array of other object type? | |
479 | return Instance::createRuntimeObject(Instance::JavaLanguage, anObject, rootObject()); | |
480 | } | |
481 | ||
482 | case boolean_type: { | |
483 | jbooleanArray booleanArray = (jbooleanArray)javaArray(); | |
484 | jboolean aBoolean; | |
485 | env->GetBooleanArrayRegion(booleanArray, index, 1, &aBoolean); | |
486 | return jsBoolean(aBoolean); | |
487 | } | |
488 | ||
489 | case byte_type: { | |
490 | jbyteArray byteArray = (jbyteArray)javaArray(); | |
491 | jbyte aByte; | |
492 | env->GetByteArrayRegion(byteArray, index, 1, &aByte); | |
493 | return jsNumber(aByte); | |
494 | } | |
495 | ||
496 | case char_type: { | |
497 | jcharArray charArray = (jcharArray)javaArray(); | |
498 | jchar aChar; | |
499 | env->GetCharArrayRegion(charArray, index, 1, &aChar); | |
500 | return jsNumber(aChar); | |
501 | break; | |
502 | } | |
503 | ||
504 | case short_type: { | |
505 | jshortArray shortArray = (jshortArray)javaArray(); | |
506 | jshort aShort; | |
507 | env->GetShortArrayRegion(shortArray, index, 1, &aShort); | |
508 | return jsNumber(aShort); | |
509 | } | |
510 | ||
511 | case int_type: { | |
512 | jintArray intArray = (jintArray)javaArray(); | |
513 | jint anInt; | |
514 | env->GetIntArrayRegion(intArray, index, 1, &anInt); | |
515 | return jsNumber(anInt); | |
516 | } | |
517 | ||
518 | case long_type: { | |
519 | jlongArray longArray = (jlongArray)javaArray(); | |
520 | jlong aLong; | |
521 | env->GetLongArrayRegion(longArray, index, 1, &aLong); | |
522 | return jsNumber(aLong); | |
523 | } | |
524 | ||
525 | case float_type: { | |
526 | jfloatArray floatArray = (jfloatArray)javaArray(); | |
527 | jfloat aFloat; | |
528 | env->GetFloatArrayRegion(floatArray, index, 1, &aFloat); | |
529 | return jsNumber(aFloat); | |
530 | } | |
531 | ||
532 | case double_type: { | |
533 | jdoubleArray doubleArray = (jdoubleArray)javaArray(); | |
534 | jdouble aDouble; | |
535 | env->GetDoubleArrayRegion(doubleArray, index, 1, &aDouble); | |
536 | return jsNumber(aDouble); | |
537 | } | |
538 | default: | |
539 | break; | |
540 | } | |
541 | return jsUndefined(); | |
542 | } | |
543 | ||
544 | unsigned int JavaArray::getLength() const | |
545 | { | |
546 | return _length; | |
547 | } | |
548 | ||
549 | #endif // ENABLE(JAVA_BINDINGS) |