]> git.saurik.com Git - apple/javascriptcore.git/blob - bindings/objc/objc_runtime.mm
ec0b41fc0764935b274611d80edecc1a765c07e6
[apple/javascriptcore.git] / bindings / objc / objc_runtime.mm
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_runtime.h"
28
29 #include "objc_instance.h"
30 #include "runtime_array.h"
31 #include "runtime_object.h"
32 #include "WebScriptObject.h"
33
34 using namespace KJS;
35 using namespace KJS::Bindings;
36
37 extern ClassStructPtr KJS::Bindings::webScriptObjectClass()
38 {
39 static ClassStructPtr<WebScriptObject> webScriptObjectClass = NSClassFromString(@"WebScriptObject");
40 return webScriptObjectClass;
41 }
42
43 extern ClassStructPtr KJS::Bindings::webUndefinedClass()
44 {
45 static ClassStructPtr<WebUndefined> webUndefinedClass = NSClassFromString(@"WebUndefined");
46 return webUndefinedClass;
47 }
48
49 // ---------------------- ObjcMethod ----------------------
50
51 ObjcMethod::ObjcMethod(ClassStructPtr aClass, const char* name)
52 {
53 _objcClass = aClass;
54 _selector = name; // Assume ObjC runtime keeps these around forever.
55 _javaScriptName = 0;
56 }
57
58 const char* ObjcMethod::name() const
59 {
60 return _selector;
61 }
62
63 int ObjcMethod::numParameters() const
64 {
65 return [getMethodSignature() numberOfArguments] - 2;
66 }
67
68 NSMethodSignature* ObjcMethod::getMethodSignature() const
69 {
70 #if defined(OBJC_API_VERSION) && OBJC_API_VERSION >= 2
71 return [_objcClass instanceMethodSignatureForSelector:sel_registerName(_selector)];
72 #else
73 return [_objcClass instanceMethodSignatureForSelector:(SEL)_selector];
74 #endif
75 }
76
77 // ---------------------- ObjcField ----------------------
78
79 ObjcField::ObjcField(Ivar ivar)
80 {
81 _ivar = ivar; // Assume ObjectiveC runtime will keep this alive forever
82 _name = 0;
83 }
84
85 ObjcField::ObjcField(CFStringRef name)
86 {
87 _ivar = 0;
88 _name = (CFStringRef)CFRetain(name);
89 }
90
91 const char* ObjcField::name() const
92 {
93 #if defined(OBJC_API_VERSION) && OBJC_API_VERSION >= 2
94 if (_ivar)
95 return ivar_getName(_ivar);
96 #else
97 if (_ivar)
98 return _ivar->ivar_name;
99 #endif
100 return [(NSString*)_name.get() UTF8String];
101 }
102
103 JSValue* ObjcField::valueFromInstance(ExecState* exec, const Instance* instance) const
104 {
105 JSValue* result = jsUndefined();
106
107 id targetObject = (static_cast<const ObjcInstance*>(instance))->getObject();
108
109 JSLock::DropAllLocks dropAllLocks; // Can't put this inside the @try scope because it unwinds incorrectly.
110
111 @try {
112 NSString* key = [NSString stringWithCString:name() encoding:NSASCIIStringEncoding];
113 if (id objcValue = [targetObject valueForKey:key])
114 result = convertObjcValueToValue(exec, &objcValue, ObjcObjectType, instance->rootObject());
115 } @catch(NSException* localException) {
116 JSLock::lock();
117 throwError(exec, GeneralError, [localException reason]);
118 JSLock::unlock();
119 }
120
121 return result;
122 }
123
124 static id convertValueToObjcObject(ExecState* exec, JSValue* value)
125 {
126 RefPtr<RootObject> rootObject = findRootObject(exec->dynamicGlobalObject());
127 if (!rootObject)
128 return nil;
129 return [webScriptObjectClass() _convertValueToObjcValue:value originRootObject:rootObject.get() rootObject:rootObject.get()];
130 }
131
132 void ObjcField::setValueToInstance(ExecState* exec, const Instance* instance, JSValue* aValue) const
133 {
134 id targetObject = (static_cast<const ObjcInstance*>(instance))->getObject();
135 id value = convertValueToObjcObject(exec, aValue);
136
137 JSLock::DropAllLocks dropAllLocks; // Can't put this inside the @try scope because it unwinds incorrectly.
138
139 @try {
140 NSString* key = [NSString stringWithCString:name() encoding:NSASCIIStringEncoding];
141 [targetObject setValue:value forKey:key];
142 } @catch(NSException* localException) {
143 JSLock::lock();
144 throwError(exec, GeneralError, [localException reason]);
145 JSLock::unlock();
146 }
147 }
148
149 // ---------------------- ObjcArray ----------------------
150
151 ObjcArray::ObjcArray(ObjectStructPtr a, PassRefPtr<RootObject> rootObject)
152 : Array(rootObject)
153 , _array(a)
154 {
155 }
156
157 void ObjcArray::setValueAt(ExecState* exec, unsigned int index, JSValue* aValue) const
158 {
159 if (![_array.get() respondsToSelector:@selector(insertObject:atIndex:)]) {
160 throwError(exec, TypeError, "Array is not mutable.");
161 return;
162 }
163
164 if (index > [_array.get() count]) {
165 throwError(exec, RangeError, "Index exceeds array size.");
166 return;
167 }
168
169 // Always try to convert the value to an ObjC object, so it can be placed in the
170 // array.
171 ObjcValue oValue = convertValueToObjcValue (exec, aValue, ObjcObjectType);
172
173 @try {
174 [_array.get() insertObject:oValue.objectValue atIndex:index];
175 } @catch(NSException* localException) {
176 throwError(exec, GeneralError, "Objective-C exception.");
177 }
178 }
179
180 JSValue* ObjcArray::valueAt(ExecState* exec, unsigned int index) const
181 {
182 if (index > [_array.get() count])
183 return throwError(exec, RangeError, "Index exceeds array size.");
184 @try {
185 id obj = [_array.get() objectAtIndex:index];
186 if (obj)
187 return convertObjcValueToValue (exec, &obj, ObjcObjectType, _rootObject.get());
188 } @catch(NSException* localException) {
189 return throwError(exec, GeneralError, "Objective-C exception.");
190 }
191 return jsUndefined();
192 }
193
194 unsigned int ObjcArray::getLength() const
195 {
196 return [_array.get() count];
197 }
198
199 const ClassInfo ObjcFallbackObjectImp::info = { "ObjcFallbackObject", 0, 0 };
200
201 ObjcFallbackObjectImp::ObjcFallbackObjectImp(ObjcInstance* i, const KJS::Identifier propertyName)
202 : _instance(i)
203 , _item(propertyName)
204 {
205 }
206
207 bool ObjcFallbackObjectImp::getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot& slot)
208 {
209 // keep the prototype from getting called instead of just returning false
210 slot.setUndefined(this);
211 return true;
212 }
213
214 void ObjcFallbackObjectImp::put(ExecState*, const Identifier&, JSValue*, int)
215 {
216 }
217
218 bool ObjcFallbackObjectImp::canPut(ExecState*, const Identifier&) const
219 {
220 return false;
221 }
222
223
224 JSType ObjcFallbackObjectImp::type() const
225 {
226 id targetObject = _instance->getObject();
227
228 if ([targetObject respondsToSelector:@selector(invokeUndefinedMethodFromWebScript:withArguments:)])
229 return ObjectType;
230
231 return UndefinedType;
232 }
233
234 bool ObjcFallbackObjectImp::implementsCall() const
235 {
236 id targetObject = _instance->getObject();
237
238 if ([targetObject respondsToSelector:@selector(invokeUndefinedMethodFromWebScript:withArguments:)])
239 return true;
240
241 return false;
242 }
243
244 JSValue* ObjcFallbackObjectImp::callAsFunction(ExecState* exec, JSObject* thisObj, const List &args)
245 {
246 if (thisObj->classInfo() != &KJS::RuntimeObjectImp::info)
247 return throwError(exec, TypeError);
248
249 JSValue* result = jsUndefined();
250
251 RuntimeObjectImp* imp = static_cast<RuntimeObjectImp*>(thisObj);
252 Instance* instance = imp->getInternalInstance();
253
254 if (!instance)
255 return RuntimeObjectImp::throwInvalidAccessError(exec);
256
257 instance->begin();
258
259 ObjcInstance* objcInstance = static_cast<ObjcInstance*>(instance);
260 id targetObject = objcInstance->getObject();
261
262 if ([targetObject respondsToSelector:@selector(invokeUndefinedMethodFromWebScript:withArguments:)]){
263 MethodList methodList;
264 ObjcClass* objcClass = static_cast<ObjcClass*>(instance->getClass());
265 ObjcMethod* fallbackMethod = new ObjcMethod (objcClass->isa(), sel_getName(@selector(invokeUndefinedMethodFromWebScript:withArguments:)));
266 fallbackMethod->setJavaScriptName((CFStringRef)[NSString stringWithCString:_item.ascii() encoding:NSASCIIStringEncoding]);
267 methodList.append(fallbackMethod);
268 result = instance->invokeMethod(exec, methodList, args);
269 delete fallbackMethod;
270 }
271
272 instance->end();
273
274 return result;
275 }
276
277 bool ObjcFallbackObjectImp::deleteProperty(ExecState*, const Identifier&)
278 {
279 return false;
280 }
281
282 JSValue* ObjcFallbackObjectImp::defaultValue(ExecState* exec, JSType hint) const
283 {
284 return _instance->getValueOfUndefinedField(exec, _item, hint);
285 }
286
287 bool ObjcFallbackObjectImp::toBoolean(ExecState *) const
288 {
289 id targetObject = _instance->getObject();
290
291 if ([targetObject respondsToSelector:@selector(invokeUndefinedMethodFromWebScript:withArguments:)])
292 return true;
293
294 return false;
295 }