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