]>
Commit | Line | Data |
---|---|---|
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 | ||
32 | namespace KJS { | |
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 | const char* ObjcClass::name() const | |
77 | { | |
78 | return object_getClassName(_isa); | |
79 | } | |
80 | ||
81 | MethodList 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 | ||
157 | Field* 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 | ||
247 | JSValue* 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 | } |