]> git.saurik.com Git - apple/javascriptcore.git/blame - bindings/jni/jni_runtime.cpp
JavaScriptCore-461.tar.gz
[apple/javascriptcore.git] / bindings / jni / jni_runtime.cpp
CommitLineData
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
50using namespace KJS;
51using namespace KJS::Bindings;
52
53
54JavaParameter::JavaParameter (JNIEnv *env, jstring type)
55{
56 _type = JavaString (env, type);
57 _JNIType = JNITypeFromClassName (_type.UTF8String());
58}
59
60JavaField::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
75JSValue* 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
83jvalue 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
111JSValue *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
167void 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
194void 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
252JavaMethod::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
291JavaMethod::~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.
300static 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
318const 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
356JNIType JavaMethod::JNIReturnType() const
357{
358 return _JNIReturnType;
359}
360
361jmethodID JavaMethod::methodID (jobject obj) const
362{
363 if (_methodID == 0) {
364 _methodID = getMethodID (obj, name(), signature());
365 }
366 return _methodID;
367}
368
369
370JavaArray::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
381JavaArray::~JavaArray ()
382{
383 free ((void *)_type);
384}
385
386RootObject* JavaArray::rootObject() const
387{
388 return _rootObject && _rootObject->isValid() ? _rootObject.get() : 0;
389}
390
391void 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
459JSValue *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
544unsigned int JavaArray::getLength() const
545{
546 return _length;
547}
548
549#endif // ENABLE(JAVA_BINDINGS)