1/*
2 * Copyright (C) 2003, 2004, 2005, 2007, 2008, 2009, 2010 Apple 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 "JNIUtility.h"
28
29#if ENABLE(JAVA_BRIDGE)
30
31#include <dlfcn.h>
32
33namespace JSC {
34
35namespace Bindings {
36
37static jint KJSGetCreatedJavaVMs(JavaVM** vmBuf, jsize bufLen, jsize* nVMs)
38{
39    static void* javaVMFramework = 0;
40    if (!javaVMFramework)
41        javaVMFramework = dlopen("/System/Library/Frameworks/JavaVM.framework/JavaVM", RTLD_LAZY);
42    if (!javaVMFramework)
43        return JNI_ERR;
44
45    typedef jint(*FunctionPointerType)(JavaVM**, jsize, jsize*);
46    static FunctionPointerType functionPointer = 0;
47    if (!functionPointer)
48        functionPointer = reinterpret_cast<FunctionPointerType>(dlsym(javaVMFramework, "JNI_GetCreatedJavaVMs"));
49    if (!functionPointer)
50        return JNI_ERR;
51    return functionPointer(vmBuf, bufLen, nVMs);
52}
53
54static JavaVM* jvm = 0;
55
56// Provide the ability for an outside component to specify the JavaVM to use
57// If the jvm value is set, the getJavaVM function below will just return.
58// In getJNIEnv(), if AttachCurrentThread is called to a VM that is already
59// attached, the result is a no-op.
60void setJavaVM(JavaVM* javaVM)
61{
62    jvm = javaVM;
63}
64
65JavaVM* getJavaVM()
66{
67    if (jvm)
68        return jvm;
69
70    JavaVM* jvmArray[1];
71    jsize bufLen = 1;
72    jsize nJVMs = 0;
73    jint jniError = 0;
74
75    // Assumes JVM is already running ..., one per process
76    jniError = KJSGetCreatedJavaVMs(jvmArray, bufLen, &nJVMs);
77    if (jniError == JNI_OK && nJVMs > 0)
78        jvm = jvmArray[0];
79    else
80        LOG_ERROR("JNI_GetCreatedJavaVMs failed, returned %ld", static_cast<long>(jniError));
81
82    return jvm;
83}
84
85JNIEnv* getJNIEnv()
86{
87    union {
88        JNIEnv* env;
89        void* dummy;
90    } u;
91    jint jniError = 0;
92
93#if OS(ANDROID)
94    jniError = getJavaVM()->AttachCurrentThread(&u.env, 0);
95#else
96    jniError = getJavaVM()->AttachCurrentThread(&u.dummy, 0);
97#endif
98    if (jniError == JNI_OK)
99        return u.env;
100    LOG_ERROR("AttachCurrentThread failed, returned %ld", static_cast<long>(jniError));
101    return 0;
102}
103
104jmethodID getMethodID(jobject obj, const char* name, const char* sig)
105{
106    JNIEnv* env = getJNIEnv();
107    jmethodID mid = 0;
108
109    if (env) {
110        jclass cls = env->GetObjectClass(obj);
111        if (cls) {
112            mid = env->GetMethodID(cls, name, sig);
113            if (!mid) {
114                env->ExceptionClear();
115                mid = env->GetStaticMethodID(cls, name, sig);
116                if (!mid)
117                    env->ExceptionClear();
118            }
119        }
120        env->DeleteLocalRef(cls);
121    }
122    return mid;
123}
124
125const char* getCharactersFromJString(jstring aJString)
126{
127    return getCharactersFromJStringInEnv(getJNIEnv(), aJString);
128}
129
130void releaseCharactersForJString(jstring aJString, const char* s)
131{
132    releaseCharactersForJStringInEnv(getJNIEnv(), aJString, s);
133}
134
135const char* getCharactersFromJStringInEnv(JNIEnv* env, jstring aJString)
136{
137    jboolean isCopy;
138    const char* s = env->GetStringUTFChars(aJString, &isCopy);
139    if (!s) {
140        env->ExceptionDescribe();
141        env->ExceptionClear();
142        fprintf(stderr, "\n");
143    }
144    return s;
145}
146
147void releaseCharactersForJStringInEnv(JNIEnv* env, jstring aJString, const char* s)
148{
149    env->ReleaseStringUTFChars(aJString, s);
150}
151
152const jchar* getUCharactersFromJStringInEnv(JNIEnv* env, jstring aJString)
153{
154    jboolean isCopy;
155    const jchar* s = env->GetStringChars(aJString, &isCopy);
156    if (!s) {
157        env->ExceptionDescribe();
158        env->ExceptionClear();
159        fprintf(stderr, "\n");
160    }
161    return s;
162}
163
164void releaseUCharactersForJStringInEnv(JNIEnv* env, jstring aJString, const jchar* s)
165{
166    env->ReleaseStringChars(aJString, s);
167}
168
169JavaType javaTypeFromClassName(const char* name)
170{
171    JavaType type;
172
173    if (!strcmp("byte", name))
174        type = JavaTypeByte;
175    else if (!strcmp("short", name))
176        type = JavaTypeShort;
177    else if (!strcmp("int", name))
178        type = JavaTypeInt;
179    else if (!strcmp("long", name))
180        type = JavaTypeLong;
181    else if (!strcmp("float", name))
182        type = JavaTypeFloat;
183    else if (!strcmp("double", name))
184        type = JavaTypeDouble;
185    else if (!strcmp("char", name))
186        type = JavaTypeChar;
187    else if (!strcmp("boolean", name))
188        type = JavaTypeBoolean;
189    else if (!strcmp("void", name))
190        type = JavaTypeVoid;
191    else if ('[' == name[0])
192        type = JavaTypeArray;
193#if USE(V8)
194    else if (!strcmp("java.lang.String", name))
195        type = JavaTypeString;
196#endif
197    else
198        type = JavaTypeObject;
199
200    return type;
201}
202
203const char* signatureFromJavaType(JavaType type)
204{
205    switch (type) {
206    case JavaTypeVoid:
207        return "V";
208
209    case JavaTypeArray:
210        return "[";
211
212    case JavaTypeObject:
213#if USE(V8)
214    case JavaTypeString:
215#endif
216        return "L";
217
218    case JavaTypeBoolean:
219        return "Z";
220
221    case JavaTypeByte:
222        return "B";
223
224    case JavaTypeChar:
225        return "C";
226
227    case JavaTypeShort:
228        return "S";
229
230    case JavaTypeInt:
231        return "I";
232
233    case JavaTypeLong:
234        return "J";
235
236    case JavaTypeFloat:
237        return "F";
238
239    case JavaTypeDouble:
240        return "D";
241
242    case JavaTypeInvalid:
243    default:
244        break;
245    }
246    return "";
247}
248
249JavaType javaTypeFromPrimitiveType(char type)
250{
251    switch (type) {
252    case 'V':
253        return JavaTypeVoid;
254
255    case 'L':
256        return JavaTypeObject;
257
258    case '[':
259        return JavaTypeArray;
260
261    case 'Z':
262        return JavaTypeBoolean;
263
264    case 'B':
265        return JavaTypeByte;
266
267    case 'C':
268        return JavaTypeChar;
269
270    case 'S':
271        return JavaTypeShort;
272
273    case 'I':
274        return JavaTypeInt;
275
276    case 'J':
277        return JavaTypeLong;
278
279    case 'F':
280        return JavaTypeFloat;
281
282    case 'D':
283        return JavaTypeDouble;
284
285    default:
286        break;
287    }
288    return JavaTypeInvalid;
289}
290
291jvalue getJNIField(jobject obj, JavaType type, const char* name, const char* signature)
292{
293    JavaVM* jvm = getJavaVM();
294    JNIEnv* env = getJNIEnv();
295    jvalue result;
296
297    memset(&result, 0, sizeof(jvalue));
298    if (obj && jvm && env) {
299        jclass cls = env->GetObjectClass(obj);
300        if (cls) {
301            jfieldID field = env->GetFieldID(cls, name, signature);
302            if (field) {
303                switch (type) {
304                case JavaTypeArray:
305                case JavaTypeObject:
306#if USE(V8)
307                case JavaTypeString:
308#endif
309                    result.l = env->functions->GetObjectField(env, obj, field);
310                    break;
311                case JavaTypeBoolean:
312                    result.z = env->functions->GetBooleanField(env, obj, field);
313                    break;
314                case JavaTypeByte:
315                    result.b = env->functions->GetByteField(env, obj, field);
316                    break;
317                case JavaTypeChar:
318                    result.c = env->functions->GetCharField(env, obj, field);
319                    break;
320                case JavaTypeShort:
321                    result.s = env->functions->GetShortField(env, obj, field);
322                    break;
323                case JavaTypeInt:
324                    result.i = env->functions->GetIntField(env, obj, field);
325                    break;
326                case JavaTypeLong:
327                    result.j = env->functions->GetLongField(env, obj, field);
328                    break;
329                case JavaTypeFloat:
330                    result.f = env->functions->GetFloatField(env, obj, field);
331                    break;
332                case JavaTypeDouble:
333                    result.d = env->functions->GetDoubleField(env, obj, field);
334                    break;
335                default:
336                    LOG_ERROR("Invalid field type (%d)", static_cast<int>(type));
337                }
338            } else {
339                LOG_ERROR("Could not find field: %s", name);
340                env->ExceptionDescribe();
341                env->ExceptionClear();
342                fprintf(stderr, "\n");
343            }
344
345            env->DeleteLocalRef(cls);
346        } else
347            LOG_ERROR("Could not find class for object");
348    }
349
350    return result;
351}
352
353jvalue callJNIMethod(jobject object, JavaType returnType, const char* name, const char* signature, jvalue* args)
354{
355    jmethodID methodId = getMethodID(object, name, signature);
356    jvalue result;
357    switch (returnType) {
358    case JavaTypeVoid:
359        callJNIMethodIDA<void>(object, methodId, args);
360        break;
361    case JavaTypeObject:
362#if USE(V8)
363    case JavaTypeString:
364#endif
365        result.l = callJNIMethodIDA<jobject>(object, methodId, args);
366        break;
367    case JavaTypeBoolean:
368        result.z = callJNIMethodIDA<jboolean>(object, methodId, args);
369        break;
370    case JavaTypeByte:
371        result.b = callJNIMethodIDA<jbyte>(object, methodId, args);
372        break;
373    case JavaTypeChar:
374        result.c = callJNIMethodIDA<jchar>(object, methodId, args);
375        break;
376    case JavaTypeShort:
377        result.s = callJNIMethodIDA<jshort>(object, methodId, args);
378        break;
379    case JavaTypeInt:
380        result.i = callJNIMethodIDA<jint>(object, methodId, args);
381        break;
382    case JavaTypeLong:
383        result.j = callJNIMethodIDA<jlong>(object, methodId, args);
384        break;
385    case JavaTypeFloat:
386        result.f = callJNIMethodIDA<jfloat>(object, methodId, args);
387        break;
388    case JavaTypeDouble:
389        result.d = callJNIMethodIDA<jdouble>(object, methodId, args);
390        break;
391    default:
392        break;
393    }
394    return result;
395}
396
397} // namespace Bindings
398
399} // namespace JSC
400
401#endif // ENABLE(JAVA_BRIDGE)
402