Home | History | Annotate | Download | only in objc
      1 /*
      2  * Copyright (C) 2004 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 "objc_class.h"
     28 
     29 #include "objc_instance.h"
     30 #include "WebScriptObject.h"
     31 
     32 namespace JSC {
     33 namespace Bindings {
     34 
     35 static void deleteMethod(CFAllocatorRef, const void* value)
     36 {
     37     delete static_cast<const Method*>(value);
     38 }
     39 
     40 static void deleteField(CFAllocatorRef, const void* value)
     41 {
     42     delete static_cast<const Field*>(value);
     43 }
     44 
     45 const CFDictionaryValueCallBacks MethodDictionaryValueCallBacks = { 0, 0, &deleteMethod, 0 , 0 };
     46 const CFDictionaryValueCallBacks FieldDictionaryValueCallBacks = { 0, 0, &deleteField, 0 , 0 };
     47 
     48 ObjcClass::ObjcClass(ClassStructPtr aClass)
     49     : _isa(aClass)
     50     , _methods(AdoptCF, CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &MethodDictionaryValueCallBacks))
     51     , _fields(AdoptCF, CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &FieldDictionaryValueCallBacks))
     52 {
     53 }
     54 
     55 static CFMutableDictionaryRef classesByIsA = 0;
     56 
     57 static void _createClassesByIsAIfNecessary()
     58 {
     59     if (!classesByIsA)
     60         classesByIsA = CFDictionaryCreateMutable(NULL, 0, NULL, NULL);
     61 }
     62 
     63 ObjcClass* ObjcClass::classForIsA(ClassStructPtr isa)
     64 {
     65     _createClassesByIsAIfNecessary();
     66 
     67     ObjcClass* aClass = (ObjcClass*)CFDictionaryGetValue(classesByIsA, isa);
     68     if (!aClass) {
     69         aClass = new ObjcClass(isa);
     70         CFDictionaryAddValue(classesByIsA, isa, aClass);
     71     }
     72 
     73     return aClass;
     74 }
     75 
     76 MethodList ObjcClass::methodsNamed(const Identifier& identifier, Instance*) const
     77 {
     78     MethodList methodList;
     79     char fixedSizeBuffer[1024];
     80     char* buffer = fixedSizeBuffer;
     81     CString jsName = identifier.ascii();
     82     if (!convertJSMethodNameToObjc(jsName.data(), buffer, sizeof(fixedSizeBuffer))) {
     83         int length = jsName.length() + 1;
     84         buffer = new char[length];
     85         if (!buffer || !convertJSMethodNameToObjc(jsName.data(), buffer, length))
     86             return methodList;
     87     }
     88 
     89 
     90     RetainPtr<CFStringRef> methodName(AdoptCF, CFStringCreateWithCString(NULL, buffer, kCFStringEncodingASCII));
     91     Method* method = (Method*)CFDictionaryGetValue(_methods.get(), methodName.get());
     92     if (method) {
     93         methodList.append(method);
     94         return methodList;
     95     }
     96 
     97     ClassStructPtr thisClass = _isa;
     98     while (thisClass && methodList.isEmpty()) {
     99 #if defined(OBJC_API_VERSION) && OBJC_API_VERSION >= 2
    100         unsigned numMethodsInClass = 0;
    101         MethodStructPtr* objcMethodList = class_copyMethodList(thisClass, &numMethodsInClass);
    102 #else
    103         void* iterator = 0;
    104         struct objc_method_list* objcMethodList;
    105         while ((objcMethodList = class_nextMethodList(thisClass, &iterator))) {
    106             unsigned numMethodsInClass = objcMethodList->method_count;
    107 #endif
    108             for (unsigned i = 0; i < numMethodsInClass; i++) {
    109 #if defined(OBJC_API_VERSION) && OBJC_API_VERSION >= 2
    110                 MethodStructPtr objcMethod = objcMethodList[i];
    111                 SEL objcMethodSelector = method_getName(objcMethod);
    112 #else
    113                 struct objc_method* objcMethod = &objcMethodList->method_list[i];
    114                 SEL objcMethodSelector = objcMethod->method_name;
    115 #endif
    116                 const char* objcMethodSelectorName = sel_getName(objcMethodSelector);
    117                 NSString* mappedName = nil;
    118 
    119                 // See if the class wants to exclude the selector from visibility in JavaScript.
    120                 if ([thisClass respondsToSelector:@selector(isSelectorExcludedFromWebScript:)])
    121                     if ([thisClass isSelectorExcludedFromWebScript:objcMethodSelector])
    122                         continue;
    123 
    124                 // See if the class want to provide a different name for the selector in JavaScript.
    125                 // Note that we do not do any checks to guarantee uniqueness. That's the responsiblity
    126                 // of the class.
    127                 if ([thisClass respondsToSelector:@selector(webScriptNameForSelector:)])
    128                     mappedName = [thisClass webScriptNameForSelector:objcMethodSelector];
    129 
    130                 if ((mappedName && [mappedName isEqual:(NSString*)methodName.get()]) || strcmp(objcMethodSelectorName, buffer) == 0) {
    131                     Method* aMethod = new ObjcMethod(thisClass, objcMethodSelector); // deleted when the dictionary is destroyed
    132                     CFDictionaryAddValue(_methods.get(), methodName.get(), aMethod);
    133                     methodList.append(aMethod);
    134                     break;
    135                 }
    136             }
    137 #if defined(OBJC_API_VERSION) && OBJC_API_VERSION >= 2
    138             thisClass = class_getSuperclass(thisClass);
    139             free(objcMethodList);
    140 #else
    141         }
    142         thisClass = thisClass->super_class;
    143 #endif
    144     }
    145 
    146     if (buffer != fixedSizeBuffer)
    147         delete [] buffer;
    148 
    149     return methodList;
    150 }
    151 
    152 Field* ObjcClass::fieldNamed(const Identifier& identifier, Instance* instance) const
    153 {
    154     ClassStructPtr thisClass = _isa;
    155 
    156     CString jsName = identifier.ascii();
    157     RetainPtr<CFStringRef> fieldName(AdoptCF, CFStringCreateWithCString(NULL, jsName.data(), kCFStringEncodingASCII));
    158     Field* aField = (Field*)CFDictionaryGetValue(_fields.get(), fieldName.get());
    159     if (aField)
    160         return aField;
    161 
    162     id targetObject = (static_cast<ObjcInstance*>(instance))->getObject();
    163     id attributes = [targetObject attributeKeys];
    164     if (attributes) {
    165         // Class overrides attributeKeys, use that array of key names.
    166         unsigned count = [attributes count];
    167         for (unsigned i = 0; i < count; i++) {
    168             NSString* keyName = [attributes objectAtIndex:i];
    169             const char* UTF8KeyName = [keyName UTF8String]; // ObjC actually only supports ASCII names.
    170 
    171             // See if the class wants to exclude the selector from visibility in JavaScript.
    172             if ([thisClass respondsToSelector:@selector(isKeyExcludedFromWebScript:)])
    173                 if ([thisClass isKeyExcludedFromWebScript:UTF8KeyName])
    174                     continue;
    175 
    176             // See if the class want to provide a different name for the selector in JavaScript.
    177             // Note that we do not do any checks to guarantee uniqueness. That's the responsiblity
    178             // of the class.
    179             NSString* mappedName = nil;
    180             if ([thisClass respondsToSelector:@selector(webScriptNameForKey:)])
    181                 mappedName = [thisClass webScriptNameForKey:UTF8KeyName];
    182 
    183             if ((mappedName && [mappedName isEqual:(NSString*)fieldName.get()]) || [keyName isEqual:(NSString*)fieldName.get()]) {
    184                 aField = new ObjcField((CFStringRef)keyName); // deleted when the dictionary is destroyed
    185                 CFDictionaryAddValue(_fields.get(), fieldName.get(), aField);
    186                 break;
    187             }
    188         }
    189     } else {
    190         // Class doesn't override attributeKeys, so fall back on class runtime
    191         // introspection.
    192 
    193         while (thisClass) {
    194 #if defined(OBJC_API_VERSION) && OBJC_API_VERSION >= 2
    195             unsigned numFieldsInClass = 0;
    196             IvarStructPtr* ivarsInClass = class_copyIvarList(thisClass, &numFieldsInClass);
    197 #else
    198             struct objc_ivar_list* fieldsInClass = thisClass->ivars;
    199             if (fieldsInClass) {
    200                 unsigned numFieldsInClass = fieldsInClass->ivar_count;
    201 #endif
    202                 for (unsigned i = 0; i < numFieldsInClass; i++) {
    203 #if defined(OBJC_API_VERSION) && OBJC_API_VERSION >= 2
    204                     IvarStructPtr objcIVar = ivarsInClass[i];
    205                     const char* objcIvarName = ivar_getName(objcIVar);
    206 #else
    207                     IvarStructPtr objcIVar = &fieldsInClass->ivar_list[i];
    208                     const char* objcIvarName = objcIVar->ivar_name;
    209 #endif
    210                     NSString* mappedName = 0;
    211 
    212                     // See if the class wants to exclude the selector from visibility in JavaScript.
    213                     if ([thisClass respondsToSelector:@selector(isKeyExcludedFromWebScript:)])
    214                         if ([thisClass isKeyExcludedFromWebScript:objcIvarName])
    215                             continue;
    216 
    217                     // See if the class want to provide a different name for the selector in JavaScript.
    218                     // Note that we do not do any checks to guarantee uniqueness. That's the responsiblity
    219                     // of the class.
    220                     if ([thisClass respondsToSelector:@selector(webScriptNameForKey:)])
    221                         mappedName = [thisClass webScriptNameForKey:objcIvarName];
    222 
    223                     if ((mappedName && [mappedName isEqual:(NSString*)fieldName.get()]) || strcmp(objcIvarName, jsName.data()) == 0) {
    224                         aField = new ObjcField(objcIVar); // deleted when the dictionary is destroyed
    225                         CFDictionaryAddValue(_fields.get(), fieldName.get(), aField);
    226                         break;
    227                     }
    228                 }
    229 #if defined(OBJC_API_VERSION) && OBJC_API_VERSION >= 2
    230             thisClass = class_getSuperclass(thisClass);
    231             free(ivarsInClass);
    232 #else
    233             }
    234             thisClass = thisClass->super_class;
    235 #endif
    236         }
    237     }
    238 
    239     return aField;
    240 }
    241 
    242 JSValue ObjcClass::fallbackObject(ExecState* exec, Instance* instance, const Identifier &propertyName)
    243 {
    244     ObjcInstance* objcInstance = static_cast<ObjcInstance*>(instance);
    245     id targetObject = objcInstance->getObject();
    246 
    247     if (![targetObject respondsToSelector:@selector(invokeUndefinedMethodFromWebScript:withArguments:)])
    248         return jsUndefined();
    249     return new (exec) ObjcFallbackObjectImp(exec, exec->lexicalGlobalObject(), objcInstance, propertyName);
    250 }
    251 
    252 }
    253 }
    254