2 * Copyright (C) 2004 Apple Computer, Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "objc_class.h"
29 #include "objc_instance.h"
30 #include "WebScriptObject.h"
35 static void deleteMethod(CFAllocatorRef, const void* value)
37 delete static_cast<const Method*>(value);
40 static void deleteField(CFAllocatorRef, const void* value)
42 delete static_cast<const Field*>(value);
45 const CFDictionaryValueCallBacks MethodDictionaryValueCallBacks = { 0, 0, &deleteMethod, 0 , 0 };
46 const CFDictionaryValueCallBacks FieldDictionaryValueCallBacks = { 0, 0, &deleteField, 0 , 0 };
48 ObjcClass::ObjcClass(ClassStructPtr aClass)
50 , _methods(AdoptCF, CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &MethodDictionaryValueCallBacks))
51 , _fields(AdoptCF, CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &FieldDictionaryValueCallBacks))
55 static CFMutableDictionaryRef classesByIsA = 0;
57 static void _createClassesByIsAIfNecessary()
60 classesByIsA = CFDictionaryCreateMutable(NULL, 0, NULL, NULL);
63 ObjcClass* ObjcClass::classForIsA(ClassStructPtr isa)
65 _createClassesByIsAIfNecessary();
67 ObjcClass* aClass = (ObjcClass*)CFDictionaryGetValue(classesByIsA, isa);
69 aClass = new ObjcClass(isa);
70 CFDictionaryAddValue(classesByIsA, isa, aClass);
76 const char* ObjcClass::name() const
78 return object_getClassName(_isa);
81 MethodList ObjcClass::methodsNamed(const Identifier& identifier, Instance*) const
83 MethodList methodList;
84 char fixedSizeBuffer[1024];
85 char* buffer = fixedSizeBuffer;
86 const char* JSName = identifier.ascii();
87 if (!convertJSMethodNameToObjc(JSName, buffer, sizeof(fixedSizeBuffer))) {
88 int length = strlen(JSName) + 1;
89 buffer = new char[length];
90 if (!buffer || !convertJSMethodNameToObjc(JSName, buffer, length))
95 RetainPtr<CFStringRef> methodName(AdoptCF, CFStringCreateWithCString(NULL, buffer, kCFStringEncodingASCII));
96 Method* method = (Method*)CFDictionaryGetValue(_methods.get(), methodName.get());
98 methodList.append(method);
102 ClassStructPtr thisClass = _isa;
103 while (thisClass && methodList.isEmpty()) {
104 #if defined(OBJC_API_VERSION) && OBJC_API_VERSION >= 2
105 unsigned numMethodsInClass = 0;
106 MethodStructPtr* objcMethodList = class_copyMethodList(thisClass, &numMethodsInClass);
109 struct objc_method_list* objcMethodList;
110 while ((objcMethodList = class_nextMethodList(thisClass, &iterator))) {
111 unsigned numMethodsInClass = objcMethodList->method_count;
113 for (unsigned i = 0; i < numMethodsInClass; i++) {
114 #if defined(OBJC_API_VERSION) && OBJC_API_VERSION >= 2
115 MethodStructPtr objcMethod = objcMethodList[i];
116 SEL objcMethodSelector = method_getName(objcMethod);
118 struct objc_method* objcMethod = &objcMethodList->method_list[i];
119 SEL objcMethodSelector = objcMethod->method_name;
121 const char* objcMethodSelectorName = sel_getName(objcMethodSelector);
122 NSString* mappedName = nil;
124 // See if the class wants to exclude the selector from visibility in JavaScript.
125 if ([thisClass respondsToSelector:@selector(isSelectorExcludedFromWebScript:)])
126 if ([thisClass isSelectorExcludedFromWebScript:objcMethodSelector])
129 // See if the class want to provide a different name for the selector in JavaScript.
130 // Note that we do not do any checks to guarantee uniqueness. That's the responsiblity
132 if ([thisClass respondsToSelector:@selector(webScriptNameForSelector:)])
133 mappedName = [thisClass webScriptNameForSelector:objcMethodSelector];
135 if ((mappedName && [mappedName isEqual:(NSString*)methodName.get()]) || strcmp(objcMethodSelectorName, buffer) == 0) {
136 Method* aMethod = new ObjcMethod(thisClass, objcMethodSelectorName); // deleted when the dictionary is destroyed
137 CFDictionaryAddValue(_methods.get(), methodName.get(), aMethod);
138 methodList.append(aMethod);
142 #if defined(OBJC_API_VERSION) && OBJC_API_VERSION >= 2
143 thisClass = class_getSuperclass(thisClass);
144 free(objcMethodList);
147 thisClass = thisClass->super_class;
151 if (buffer != fixedSizeBuffer)
157 Field* ObjcClass::fieldNamed(const Identifier& identifier, Instance* instance) const
159 ClassStructPtr thisClass = _isa;
161 const char* name = identifier.ascii();
162 RetainPtr<CFStringRef> fieldName(AdoptCF, CFStringCreateWithCString(NULL, name, kCFStringEncodingASCII));
163 Field* aField = (Field*)CFDictionaryGetValue(_fields.get(), fieldName.get());
167 id targetObject = (static_cast<ObjcInstance*>(instance))->getObject();
168 id attributes = [targetObject respondsToSelector:@selector(attributeKeys)] ? [targetObject performSelector:@selector(attributeKeys)] : nil;
170 // Class overrides attributeKeys, use that array of key names.
171 unsigned count = [attributes count];
172 for (unsigned i = 0; i < count; i++) {
173 NSString* keyName = [attributes objectAtIndex:i];
174 const char* UTF8KeyName = [keyName UTF8String]; // ObjC actually only supports ASCII names.
176 // See if the class wants to exclude the selector from visibility in JavaScript.
177 if ([thisClass respondsToSelector:@selector(isKeyExcludedFromWebScript:)])
178 if ([thisClass isKeyExcludedFromWebScript:UTF8KeyName])
181 // See if the class want to provide a different name for the selector in JavaScript.
182 // Note that we do not do any checks to guarantee uniqueness. That's the responsiblity
184 NSString* mappedName = nil;
185 if ([thisClass respondsToSelector:@selector(webScriptNameForKey:)])
186 mappedName = [thisClass webScriptNameForKey:UTF8KeyName];
188 if ((mappedName && [mappedName isEqual:(NSString*)fieldName.get()]) || [keyName isEqual:(NSString*)fieldName.get()]) {
189 aField = new ObjcField((CFStringRef)keyName); // deleted when the dictionary is destroyed
190 CFDictionaryAddValue(_fields.get(), fieldName.get(), aField);
195 // Class doesn't override attributeKeys, so fall back on class runtime
199 #if defined(OBJC_API_VERSION) && OBJC_API_VERSION >= 2
200 unsigned numFieldsInClass = 0;
201 IvarStructPtr* ivarsInClass = class_copyIvarList(thisClass, &numFieldsInClass);
203 struct objc_ivar_list* fieldsInClass = thisClass->ivars;
205 unsigned numFieldsInClass = fieldsInClass->ivar_count;
207 for (unsigned i = 0; i < numFieldsInClass; i++) {
208 #if defined(OBJC_API_VERSION) && OBJC_API_VERSION >= 2
209 IvarStructPtr objcIVar = ivarsInClass[i];
210 const char* objcIvarName = ivar_getName(objcIVar);
212 IvarStructPtr objcIVar = &fieldsInClass->ivar_list[i];
213 const char* objcIvarName = objcIVar->ivar_name;
215 NSString* mappedName = 0;
217 // See if the class wants to exclude the selector from visibility in JavaScript.
218 if ([thisClass respondsToSelector:@selector(isKeyExcludedFromWebScript:)])
219 if ([thisClass isKeyExcludedFromWebScript:objcIvarName])
222 // See if the class want to provide a different name for the selector in JavaScript.
223 // Note that we do not do any checks to guarantee uniqueness. That's the responsiblity
225 if ([thisClass respondsToSelector:@selector(webScriptNameForKey:)])
226 mappedName = [thisClass webScriptNameForKey:objcIvarName];
228 if ((mappedName && [mappedName isEqual:(NSString*)fieldName.get()]) || strcmp(objcIvarName, name) == 0) {
229 aField = new ObjcField(objcIVar); // deleted when the dictionary is destroyed
230 CFDictionaryAddValue(_fields.get(), fieldName.get(), aField);
234 #if defined(OBJC_API_VERSION) && OBJC_API_VERSION >= 2
235 thisClass = class_getSuperclass(thisClass);
239 thisClass = thisClass->super_class;
247 JSValue* ObjcClass::fallbackObject(ExecState*, Instance* instance, const Identifier &propertyName)
249 ObjcInstance* objcInstance = static_cast<ObjcInstance*>(instance);
250 id targetObject = objcInstance->getObject();
252 if (![targetObject respondsToSelector:@selector(invokeUndefinedMethodFromWebScript:withArguments:)])
253 return jsUndefined();
254 return new ObjcFallbackObjectImp(objcInstance, propertyName);