1/*
2 * Copyright 2010, The Android Open Source Project
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 *  * Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 *  * 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 THE COPYRIGHT HOLDERS ``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 THE COPYRIGHT OWNER 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 "JavaClassJobjectV8.h"
28
29#if ENABLE(JAVA_BRIDGE)
30
31#include "JavaFieldJobjectV8.h"
32#include "JavaMethodJobject.h"
33
34using namespace JSC::Bindings;
35
36#if PLATFORM(ANDROID)
37const char kJavaScriptInterfaceAnnotation[] = "android/webkit/JavascriptInterface";
38const char kIsAnnotationPresent[] = "isAnnotationPresent";
39const char kGetMethods[] = "getMethods";
40
41static jclass safeAnnotationClazz = NULL;
42
43JavaClassJobject::JavaClassJobject(jobject anInstance, bool requireAnnotation)
44    : m_requireAnnotation(requireAnnotation)
45#else
46JavaClassJobject::JavaClassJobject(jobject anInstance)
47#endif
48{
49    jobject aClass = callJNIMethod<jobject>(anInstance, "getClass", "()Ljava/lang/Class;");
50
51    if (!aClass) {
52        LOG_ERROR("unable to call getClass on instance %p", anInstance);
53        return;
54    }
55
56    JNIEnv* env = getJNIEnv();
57
58    // Get the fields
59    jarray fields = static_cast<jarray>(callJNIMethod<jobject>(aClass, "getFields", "()[Ljava/lang/reflect/Field;"));
60    int numFields = env->GetArrayLength(fields);
61    for (int i = 0; i < numFields; i++) {
62        jobject aJField = env->GetObjectArrayElement(static_cast<jobjectArray>(fields), i);
63        JavaField* aField = new JavaFieldJobject(env, aJField); // deleted in the JavaClass destructor
64        m_fields.set(aField->name(), aField);
65        env->DeleteLocalRef(aJField);
66    }
67
68    // Get the methods
69    jarray methods = static_cast<jarray>(callJNIMethod<jobject>(aClass, "getMethods", "()[Ljava/lang/reflect/Method;"));
70    int numMethods = env->GetArrayLength(methods);
71#if PLATFORM(ANDROID)
72    jmethodID isAnnotationPresentMethodID = getAnnotationMethodID(env);
73    if (!isAnnotationPresentMethodID) {
74        LOG_ERROR("unable to find method %s on instance %p", kIsAnnotationPresent, anInstance);
75        return;
76    }
77#endif
78    for (int i = 0; i < numMethods; i++) {
79        jobject aJMethod = env->GetObjectArrayElement(static_cast<jobjectArray>(methods), i);
80#if PLATFORM(ANDROID)
81        if (jsAccessAllowed(env, isAnnotationPresentMethodID, aJMethod)) {
82#endif
83            JavaMethod* aMethod = new JavaMethodJobject(env, aJMethod); // deleted in the JavaClass destructor
84            MethodList* methodList = m_methods.get(aMethod->name());
85            if (!methodList) {
86                methodList = new MethodList();
87                m_methods.set(aMethod->name(), methodList);
88            }
89            methodList->append(aMethod);
90#if PLATFORM(ANDROID)
91        }
92#endif
93        env->DeleteLocalRef(aJMethod);
94    }
95    env->DeleteLocalRef(fields);
96    env->DeleteLocalRef(methods);
97    env->DeleteLocalRef(aClass);
98}
99
100JavaClassJobject::~JavaClassJobject()
101{
102    deleteAllValues(m_fields);
103    m_fields.clear();
104
105    MethodListMap::const_iterator end = m_methods.end();
106    for (MethodListMap::const_iterator it = m_methods.begin(); it != end; ++it) {
107        const MethodList* methodList = it->second;
108        deleteAllValues(*methodList);
109        delete methodList;
110    }
111    m_methods.clear();
112}
113
114#if PLATFORM(ANDROID)
115bool JavaClassJobject::jsAccessAllowed(JNIEnv* env, jmethodID mid, jobject aJMethod)
116{
117    if (!m_requireAnnotation)
118        return true;
119    bool accessAllowed = env->CallBooleanMethod(aJMethod, mid, safeAnnotationClazz);
120    if (env->ExceptionCheck()) {
121        env->ExceptionDescribe();
122        env->ExceptionClear();
123        return false;
124    }
125    return accessAllowed;
126}
127
128jmethodID JavaClassJobject::getAnnotationMethodID(JNIEnv* env)
129{
130    jclass methodClass = env->FindClass("java/lang/reflect/Method");
131    jmethodID mid = 0;
132    if (methodClass)
133        mid = env->GetMethodID(methodClass, kIsAnnotationPresent, "(Ljava/lang/Class;)Z");
134    if (!methodClass || !mid) {
135        env->ExceptionDescribe();
136        env->ExceptionClear();
137    }
138    env->DeleteLocalRef(methodClass);
139    return mid;
140}
141
142bool JavaClassJobject::RegisterJavaClassJobject(JNIEnv* env) {
143    safeAnnotationClazz = reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass(kJavaScriptInterfaceAnnotation)));
144    if (!safeAnnotationClazz) {
145        LOG_ERROR("failed to register %s", kJavaScriptInterfaceAnnotation);
146        return false;
147    }
148    return true;
149}
150#endif
151
152MethodList JavaClassJobject::methodsNamed(const char* name) const
153{
154    MethodList* methodList = m_methods.get(name);
155
156    if (methodList)
157        return *methodList;
158    return MethodList();
159}
160
161JavaField* JavaClassJobject::fieldNamed(const char* name) const
162{
163    return m_fields.get(name);
164}
165
166#endif // ENABLE(JAVA_BRIDGE)
167