]> git.saurik.com Git - apple/javascriptcore.git/blame - bindings/objc/objc_class.mm
JavaScriptCore-461.tar.gz
[apple/javascriptcore.git] / bindings / objc / objc_class.mm
CommitLineData
b37bf2e1
A
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
32namespace KJS {
33namespace Bindings {
34
35static void deleteMethod(CFAllocatorRef, const void* value)
36{
37 delete static_cast<const Method*>(value);
38}
39
40static void deleteField(CFAllocatorRef, const void* value)
41{
42 delete static_cast<const Field*>(value);
43}
44
45const CFDictionaryValueCallBacks MethodDictionaryValueCallBacks = { 0, 0, &deleteMethod, 0 , 0 };
46const CFDictionaryValueCallBacks FieldDictionaryValueCallBacks = { 0, 0, &deleteField, 0 , 0 };
47
48ObjcClass::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
55static CFMutableDictionaryRef classesByIsA = 0;
56
57static void _createClassesByIsAIfNecessary()
58{
59 if (!classesByIsA)
60 classesByIsA = CFDictionaryCreateMutable(NULL, 0, NULL, NULL);
61}
62
63ObjcClass* 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
76const char* ObjcClass::name() const
77{
78 return object_getClassName(_isa);
79}
80
81MethodList ObjcClass::methodsNamed(const Identifier& identifier, Instance*) const
82{
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))
91 return methodList;
92 }
93
94
95 RetainPtr<CFStringRef> methodName(AdoptCF, CFStringCreateWithCString(NULL, buffer, kCFStringEncodingASCII));
96 Method* method = (Method*)CFDictionaryGetValue(_methods.get(), methodName.get());
97 if (method) {
98 methodList.append(method);
99 return methodList;
100 }
101
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);
107#else
108 void* iterator = 0;
109 struct objc_method_list* objcMethodList;
110 while ((objcMethodList = class_nextMethodList(thisClass, &iterator))) {
111 unsigned numMethodsInClass = objcMethodList->method_count;
112#endif
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);
117#else
118 struct objc_method* objcMethod = &objcMethodList->method_list[i];
119 SEL objcMethodSelector = objcMethod->method_name;
120#endif
121 const char* objcMethodSelectorName = sel_getName(objcMethodSelector);
122 NSString* mappedName = nil;
123
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])
127 continue;
128
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
131 // of the class.
132 if ([thisClass respondsToSelector:@selector(webScriptNameForSelector:)])
133 mappedName = [thisClass webScriptNameForSelector:objcMethodSelector];
134
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);
139 break;
140 }
141 }
142#if defined(OBJC_API_VERSION) && OBJC_API_VERSION >= 2
143 thisClass = class_getSuperclass(thisClass);
144 free(objcMethodList);
145#else
146 }
147 thisClass = thisClass->super_class;
148#endif
149 }
150
151 if (buffer != fixedSizeBuffer)
152 delete [] buffer;
153
154 return methodList;
155}
156
157Field* ObjcClass::fieldNamed(const Identifier& identifier, Instance* instance) const
158{
159 ClassStructPtr thisClass = _isa;
160
161 const char* name = identifier.ascii();
162 RetainPtr<CFStringRef> fieldName(AdoptCF, CFStringCreateWithCString(NULL, name, kCFStringEncodingASCII));
163 Field* aField = (Field*)CFDictionaryGetValue(_fields.get(), fieldName.get());
164 if (aField)
165 return aField;
166
167 id targetObject = (static_cast<ObjcInstance*>(instance))->getObject();
168 id attributes = [targetObject respondsToSelector:@selector(attributeKeys)] ? [targetObject performSelector:@selector(attributeKeys)] : nil;
169 if (attributes) {
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.
175
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])
179 continue;
180
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
183 // of the class.
184 NSString* mappedName = nil;
185 if ([thisClass respondsToSelector:@selector(webScriptNameForKey:)])
186 mappedName = [thisClass webScriptNameForKey:UTF8KeyName];
187
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);
191 break;
192 }
193 }
194 } else {
195 // Class doesn't override attributeKeys, so fall back on class runtime
196 // introspection.
197
198 while (thisClass) {
199#if defined(OBJC_API_VERSION) && OBJC_API_VERSION >= 2
200 unsigned numFieldsInClass = 0;
201 IvarStructPtr* ivarsInClass = class_copyIvarList(thisClass, &numFieldsInClass);
202#else
203 struct objc_ivar_list* fieldsInClass = thisClass->ivars;
204 if (fieldsInClass) {
205 unsigned numFieldsInClass = fieldsInClass->ivar_count;
206#endif
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);
211#else
212 IvarStructPtr objcIVar = &fieldsInClass->ivar_list[i];
213 const char* objcIvarName = objcIVar->ivar_name;
214#endif
215 NSString* mappedName = 0;
216
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])
220 continue;
221
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
224 // of the class.
225 if ([thisClass respondsToSelector:@selector(webScriptNameForKey:)])
226 mappedName = [thisClass webScriptNameForKey:objcIvarName];
227
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);
231 break;
232 }
233 }
234#if defined(OBJC_API_VERSION) && OBJC_API_VERSION >= 2
235 thisClass = class_getSuperclass(thisClass);
236 free(ivarsInClass);
237#else
238 }
239 thisClass = thisClass->super_class;
240#endif
241 }
242 }
243
244 return aField;
245}
246
247JSValue* ObjcClass::fallbackObject(ExecState*, Instance* instance, const Identifier &propertyName)
248{
249 ObjcInstance* objcInstance = static_cast<ObjcInstance*>(instance);
250 id targetObject = objcInstance->getObject();
251
252 if (![targetObject respondsToSelector:@selector(invokeUndefinedMethodFromWebScript:withArguments:)])
253 return jsUndefined();
254 return new ObjcFallbackObjectImp(objcInstance, propertyName);
255}
256
257}
258}