1 /*
   2  * Copyright (C) 2003, 2004, 2005, 2006, 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 "JavaClassJSC.h"
  28 
  29 #if ENABLE(JAVA_BRIDGE)
  30 
  31 #include "JSDOMWindow.h"
  32 #include "JavaFieldJSC.h"
  33 #include "JavaMethodJSC.h"
  34 #include "JNIUtilityPrivate.h"
  35 #include <runtime/Identifier.h>
  36 #include <runtime/JSLock.h>
  37 
  38 using namespace JSC;
  39 using namespace JSC::Bindings;
  40 
  41 JavaClass::JavaClass(jobject anInstance, RootObject* rootObject, jobject accessControlContext)
  42 {
  43     jobject aClass = callJNIMethod<jobject>(anInstance, "getClass", "()Ljava/lang/Class;");
  44 
  45     if (!aClass) {
  46         LOG_ERROR("Unable to call getClass on instance %p", anInstance);
  47         m_name = fastStrDup("<Unknown>");
  48         return;
  49     }
  50 
  51     if (jstring className = (jstring)callJNIMethod<jobject>(aClass, "getName", "()Ljava/lang/String;")) {
  52         const char* classNameC = getCharactersFromJString(className);
  53         m_name = fastStrDup(classNameC);
  54         releaseCharactersForJString(className, classNameC);
  55     } else
  56         m_name = fastStrDup("<Unknown>");
  57 
  58     int i;
  59     JNIEnv* env = getJNIEnv();
  60 
  61     // Object thisModule = ModuleHelper.getModule(ModuleHelper.class);
  62     // Object targetModule = ModuleHelper.getModule(aClass);
  63     // ModuleHelper.addReads(thisModule, targetModule);
  64     jclass helperCls = env->FindClass("com/sun/javafx/web/ModuleHelper");
  65     jmethodID getModuleMethod =
  66         env->GetStaticMethodID(helperCls, "getModule", "(Ljava/lang/Class;)Ljava/lang/Object;");
  67     jmethodID addReadsMethod =
  68         env->GetStaticMethodID(helperCls, "addReads", "(Ljava/lang/Object;Ljava/lang/Object;)V");
  69 
  70     jobject thisModule = env->CallStaticObjectMethod(helperCls, getModuleMethod, helperCls);
  71     jobject targetModule = env->CallStaticObjectMethod(helperCls, getModuleMethod, aClass);
  72     env->CallStaticVoidMethod(helperCls, addReadsMethod, thisModule, targetModule);
  73 
  74     // Get the fields
  75     jvalue result;
  76     jobject args[1];
  77     jmethodID methodId = getMethodID(aClass, "getFields", "()[Ljava/lang/reflect/Field;");
  78     if (dispatchJNICall(0, rootObject, aClass, false, JavaTypeArray, methodId,
  79                         args, result, accessControlContext) == NULL) {
  80         jarray fields = (jarray) result.l;
  81         int numFields = env->GetArrayLength(fields);
  82         for (i = 0; i < numFields; i++) {
  83             jobject aJField = env->GetObjectArrayElement((jobjectArray)fields, i);
  84             JavaField* aField = new JavaField(env, aJField); // deleted in the JavaClass destructor
  85             {
  86                 // FIXME: Should we acquire a JSLock here?
  87                 m_fields.set(aField->name().impl(), aField);
  88             }
  89             env->DeleteLocalRef(aJField);
  90         }
  91         env->DeleteLocalRef(fields);
  92     }
  93 
  94     // Get the methods
  95     methodId = getMethodID(aClass, "getMethods", "()[Ljava/lang/reflect/Method;");
  96     if (dispatchJNICall(0, rootObject, aClass, false, JavaTypeArray, methodId,
  97                         args, result, accessControlContext) == NULL) {
  98         jarray methods = (jarray) result.l;
  99         int numMethods = env->GetArrayLength(methods);
 100         for (i = 0; i < numMethods; i++) {
 101             jobject aJMethod = env->GetObjectArrayElement((jobjectArray)methods, i);
 102             JavaMethod* aMethod = new JavaMethod(env, aJMethod); // deleted in the JavaClass destructor
 103             MethodList* methodList;
 104             {
 105                 // FIXME: Should we acquire a JSLock here?
 106 
 107                 methodList = m_methods.get(aMethod->name().impl());
 108                 if (!methodList) {
 109                     methodList = new MethodList();
 110                     m_methods.set(aMethod->name().impl(), methodList);
 111                 }
 112             }
 113             methodList->append(aMethod);
 114             env->DeleteLocalRef(aJMethod);
 115         }
 116         env->DeleteLocalRef(methods);
 117     }
 118 
 119     env->DeleteLocalRef(aClass);
 120 }
 121 
 122 JavaClass::~JavaClass()
 123 {
 124     fastFree(const_cast<char*>(m_name));
 125 
 126     // FIXME: Should we acquire a JSLock here?
 127 
 128 //    deleteAllValues(m_fields);  todo tav
 129     m_fields.clear();
 130 
 131     MethodListMap::const_iterator end = m_methods.end();
 132     for (MethodListMap::const_iterator it = m_methods.begin(); it != end; ++it) {
 133         const MethodList* methodList = it->value;
 134 //        deleteAllValues(*methodList); todo tav
 135         delete methodList;
 136     }
 137     m_methods.clear();
 138 }
 139 
 140 Method *JavaClass::methodNamed(PropertyName propertyName, Instance*) const
 141 {
 142     const String name(propertyName.publicName());
 143     unsigned nameLength = name.length();
 144     MethodList* methodList;
 145     int i;
 146     if (nameLength >= 3 && name[nameLength-1] == ')'
 147         && (i = name.find('(', 1)) != WTF::notFound) {
 148         Vector<String> pnames;
 149         int pstart = i+1;
 150         if (pstart < nameLength-1) {
 151             do {
 152                 int pnext = name.find(',', pstart);
 153                 if (pnext == WTF::notFound)
 154                     pnext = nameLength-1;
 155                 String pname = name.substringSharingImpl(pstart, pnext-pstart);
 156                 pnames.append(pname);
 157                 pstart = pnext+1;
 158             } while (pstart < nameLength);
 159         }
 160         size_t plen = pnames.size();
 161         MethodList* allMethods
 162             = m_methods.get(name.substringSharingImpl(0, i).impl());
 163         methodList = NULL;
 164         size_t numMethods = allMethods == NULL ? 0 : allMethods->size();
 165         for (size_t methodIndex = 0; methodIndex < numMethods; methodIndex++) {
 166             JavaMethod* jMethod = static_cast<JavaMethod*>(allMethods->at(methodIndex));
 167             if (jMethod->numParameters() == plen) {
 168                 // Iterate over parameters.
 169                 for (int i = 0;  ;  i++) {
 170                     if (i == plen) {
 171                         if (methodList == NULL)
 172                             methodList = new MethodList();
 173                         methodList->append(jMethod);
 174                         break;
 175                     }
 176                     String methodParam = jMethod->parameterAt(i);
 177                     size_t methodParamLength = methodParam.length();
 178                     String pname = pnames[i];
 179                     size_t pnameLength = pname.length();
 180                     // Handle array type names.
 181                     while (methodParamLength >= 2 && methodParam[0] == '['
 182                            && pnameLength >= 3 && pname[pnameLength-2] == '['
 183                            && pname[pnameLength-1] == ']') {
 184                         // Primitive array type names.
 185                         if (methodParamLength == 2) {
 186                           UChar sig1 = methodParam[1];
 187                           const char *prim;
 188                           switch (methodParam[1]) {
 189                           case 'I': prim = "int[]"; break;
 190                           case 'J': prim = "long[]"; break;
 191                           case 'B': prim = "byte[]"; break;
 192                           case 'S': prim = "short[]"; break;
 193                           case 'F': prim = "float[]"; break;
 194                           case 'D': prim = "double[]"; break;
 195                           case 'C': prim = "char[]"; break;
 196                           case 'Z': prim = "boolean[]"; break;
 197                           default: prim = NULL;
 198                           }
 199                           if (pname == prim) {
 200                               methodParamLength = 0;
 201                               pnameLength = 0;
 202                           } else
 203                             break;
 204                         }
 205                         // Object array type names.
 206                         else if (methodParamLength > 3
 207                                 && methodParam[1] == 'L'
 208                                 && methodParam[methodParamLength-1] == ';') {
 209                             pnameLength -= 2;
 210                             pname = pname.substringSharingImpl(0, pnameLength);
 211                             methodParamLength -= 3;
 212                             methodParam = methodParam
 213                                 .substringSharingImpl(2, methodParamLength);
 214                         } else {
 215                           break;
 216                         }
 217                     }
 218                     if (methodParamLength == pnameLength + 10
 219                         && methodParam.find("java.lang.", 0) == 0) {
 220                         methodParam = methodParam.substringSharingImpl(10, pnameLength);
 221                         methodParamLength = pnameLength;
 222                     }
 223                     if (methodParamLength == pnameLength) {
 224                         int k = 0;
 225                         for (; k < methodParamLength;  k++) {
 226                             if (methodParam[k] != pname[k]) {
 227                                 break;
 228                             }
 229                         }
 230                         if (k < methodParamLength)
 231                             break;
 232                     } else
 233                         break;
 234                 }
 235             }
 236         }
 237     } else {
 238         methodList = m_methods.get(name.impl());
 239     }
 240     if (methodList)
 241         return methodList->at(0);
 242     return NULL;
 243 }
 244 
 245 Field* JavaClass::fieldNamed(PropertyName propertyName, Instance*) const
 246 {
 247     return m_fields.get(propertyName.publicName());
 248 }
 249 
 250 bool JavaClass::isNumberClass() const
 251 {
 252     return (!strcmp(m_name, "java.lang.Byte")
 253         || !strcmp(m_name, "java.lang.Short")
 254         || !strcmp(m_name, "java.lang.Integer")
 255         || !strcmp(m_name, "java.lang.Long")
 256         || !strcmp(m_name, "java.lang.Float")
 257         || !strcmp(m_name, "java.lang.Double"));
 258 }
 259 
 260 bool JavaClass::isBooleanClass() const
 261 {
 262     return !strcmp(m_name, "java.lang.Boolean");
 263 }
 264 
 265 bool JavaClass::isCharacterClass() const
 266 {
 267     return !strcmp(m_name, "java.lang.Character");
 268 }
 269 
 270 bool JavaClass::isStringClass() const
 271 {
 272     return !strcmp(m_name, "java.lang.String");
 273 }
 274 
 275 #endif // ENABLE(JAVA_BRIDGE)