81cac2715ca9823a2f9ce338f16a07ab7e8b592b
[WebKit-https.git] / JavaScriptCore / 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 #include "config.h"
26 #include <Foundation/Foundation.h>
27
28
29 #include <JavaScriptCore/internal.h>
30
31 #include <objc_instance.h>
32 #include <WebScriptObjectPrivate.h>
33
34 #include <runtime_array.h>
35 #include <runtime_object.h>
36
37
38 using namespace KJS;
39 using namespace KJS::Bindings;
40
41 // ---------------------- ObjcMethod ----------------------
42
43 ObjcMethod::ObjcMethod(ClassStructPtr aClass, const char *name)
44 {
45     _objcClass = aClass;
46     _selector = name;   // Assume ObjC runtime keeps these around forever.
47     _javaScriptName = 0;
48 }
49
50 const char *ObjcMethod::name() const
51 {
52     return (const char *)_selector;
53 }
54
55 int ObjcMethod::numParameters() const
56 {
57     return [getMethodSignature() numberOfArguments] - 2;
58 }
59
60
61 NSMethodSignature *ObjcMethod::getMethodSignature() const
62 {
63     return [(id)_objcClass instanceMethodSignatureForSelector:(SEL)_selector];
64 }
65
66 void ObjcMethod::setJavaScriptName (CFStringRef n)
67 {
68     if (n != _javaScriptName) {
69         if (_javaScriptName != 0)
70             CFRelease (_javaScriptName);
71         _javaScriptName = (CFStringRef)CFRetain (n);
72     }
73 }
74
75 // ---------------------- ObjcField ----------------------
76
77
78 ObjcField::ObjcField(Ivar ivar) 
79 {
80     _ivar = ivar;    // Assume ObjectiveC runtime will keep this alive forever
81     _name = 0;
82 }
83
84 ObjcField::ObjcField(CFStringRef name) 
85 {
86     _ivar = 0;
87     _name = (CFStringRef)CFRetain(name);
88 }
89
90 const char *ObjcField::name() const 
91 {
92     if (_ivar)
93         return _ivar->ivar_name;
94     return [(NSString *)_name UTF8String];
95 }
96
97 RuntimeType ObjcField::type() const 
98
99     if (_ivar)
100         return _ivar->ivar_type;
101     
102     // Type is irrelevant if we use KV to set/get the value.
103     return "";
104 }
105
106 JSValue *ObjcField::valueFromInstance(ExecState *exec, const Instance *instance) const
107 {
108     JSValue *aValue;
109     id targetObject = (static_cast<const ObjcInstance*>(instance))->getObject();
110     id objcValue = nil;
111
112     NS_DURING
113     
114         NSString *key = [NSString stringWithCString:name()];
115         objcValue = [targetObject valueForKey:key];
116         
117     NS_HANDLER
118         
119         throwError(exec, GeneralError, [localException reason]);
120         
121     NS_ENDHANDLER
122
123     if (objcValue)
124         aValue = convertObjcValueToValue (exec, &objcValue, ObjcObjectType);
125     else
126         aValue = jsUndefined();
127
128     return aValue;
129 }
130
131 static id convertValueToObjcObject (ExecState *exec, JSValue *value)
132 {
133     const Bindings::RootObject *root = rootForInterpreter(exec->interpreter());
134     if (!root) {
135         Bindings::RootObject *newRoot = new Bindings::RootObject(0);
136         newRoot->setInterpreter (exec->interpreter());
137         root = newRoot;
138     }
139     return [WebScriptObject _convertValueToObjcValue:value originExecutionContext:root executionContext:root ];
140 }
141
142
143 void ObjcField::setValueToInstance(ExecState *exec, const Instance *instance, JSValue *aValue) const
144 {
145     id targetObject = (static_cast<const ObjcInstance*>(instance))->getObject();
146     id value = convertValueToObjcObject(exec, aValue);
147     
148     NS_DURING
149     
150         NSString *key = [NSString stringWithCString:name()];
151         [targetObject setValue:value forKey:key];
152
153     NS_HANDLER
154         
155         throwError(exec, GeneralError, [localException reason]);
156         
157     NS_ENDHANDLER
158 }
159
160 // ---------------------- ObjcArray ----------------------
161
162 ObjcArray::ObjcArray (ObjectStructPtr a) 
163 {
164     _array = (id)CFRetain(a);
165 }
166
167 ObjcArray::~ObjcArray () 
168 {
169     CFRelease(_array);
170 }
171
172
173 ObjcArray::ObjcArray (const ObjcArray &other) : Array() 
174 {
175     _array = other._array;
176     CFRetain(_array);
177 }
178
179 ObjcArray &ObjcArray::operator=(const ObjcArray &other)
180 {
181     ObjectStructPtr _oldArray = _array;
182     _array = other._array;
183     CFRetain(_array);
184     CFRelease(_oldArray);
185     return *this;
186 }
187
188 void ObjcArray::setValueAt(ExecState *exec, unsigned int index, JSValue *aValue) const
189 {
190     if (![_array respondsToSelector:@selector(insertObject:atIndex:)]) {
191         throwError(exec, TypeError, "Array is not mutable.");
192         return;
193     }
194
195     if (index > [_array count]) {
196         throwError(exec, RangeError, "Index exceeds array size.");
197         return;
198     }
199     
200     // Always try to convert the value to an ObjC object, so it can be placed in the
201     // array.
202     ObjcValue oValue = convertValueToObjcValue (exec, aValue, ObjcObjectType);
203
204 NS_DURING
205
206     [_array insertObject:oValue.objectValue atIndex:index];
207
208 NS_HANDLER
209     
210     throwError(exec, GeneralError, "Objective-C exception.");
211     
212 NS_ENDHANDLER
213 }
214
215
216 JSValue *ObjcArray::valueAt(ExecState *exec, unsigned int index) const
217 {
218     if (index > [_array count])
219         return throwError(exec, RangeError, "Index exceeds array size.");
220     
221     ObjectStructPtr obj = 0;
222     JSObject * volatile error;
223     volatile bool haveError = false;
224
225 NS_DURING
226
227     obj = [_array objectAtIndex:index];
228     
229 NS_HANDLER
230     
231     error = throwError(exec, GeneralError, "Objective-C exception.");
232     haveError = true;
233     
234 NS_ENDHANDLER
235
236     if (haveError)
237         return error;
238         
239     return convertObjcValueToValue (exec, &obj, ObjcObjectType);
240 }
241
242 unsigned int ObjcArray::getLength() const
243 {
244     return [_array count];
245 }
246
247
248 const ClassInfo ObjcFallbackObjectImp::info = {"ObjcFallbackObject", 0, 0, 0};
249
250 ObjcFallbackObjectImp::ObjcFallbackObjectImp(JSObject *proto)
251   : JSObject(proto)
252 {
253     _instance = 0;
254 }
255
256 ObjcFallbackObjectImp::ObjcFallbackObjectImp(ObjcInstance *i, const KJS::Identifier propertyName)
257 {
258     _instance = i;
259     _item = propertyName;
260 }
261
262 bool ObjcFallbackObjectImp::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
263 {
264     // keep the prototype from getting called instead of just returning false
265     slot.setUndefined(this);
266     return true;
267 }
268
269 void ObjcFallbackObjectImp::put(ExecState *exec, const Identifier &propertyName,
270                  JSValue *value, int attr)
271 {
272 }
273
274 bool ObjcFallbackObjectImp::canPut(ExecState *exec, const Identifier &propertyName) const
275 {
276     return false;
277 }
278
279
280 JSType ObjcFallbackObjectImp::type() const
281 {
282     id targetObject = _instance->getObject();
283     
284     if ([targetObject respondsToSelector:@selector(invokeUndefinedMethodFromWebScript:withArguments:)])
285         return ObjectType;
286     
287     return UndefinedType;
288 }
289
290 bool ObjcFallbackObjectImp::implementsCall() const
291 {
292     id targetObject = _instance->getObject();
293     
294     if ([targetObject respondsToSelector:@selector(invokeUndefinedMethodFromWebScript:withArguments:)])
295         return true;
296     
297     return false;
298 }
299
300 JSValue *ObjcFallbackObjectImp::callAsFunction(ExecState *exec, JSObject *thisObj, const List &args)
301 {
302     JSValue *result = jsUndefined();
303     
304     RuntimeObjectImp *imp = static_cast<RuntimeObjectImp*>(thisObj);
305     if (imp) {
306         Instance *instance = imp->getInternalInstance();
307         
308         instance->begin();
309
310         ObjcInstance *objcInstance = static_cast<ObjcInstance*>(instance);
311         id targetObject = objcInstance->getObject();
312         
313         if ([targetObject respondsToSelector:@selector(invokeUndefinedMethodFromWebScript:withArguments:)]){
314             MethodList methodList;
315             ObjcClass *objcClass = static_cast<ObjcClass*>(instance->getClass());
316             ObjcMethod *fallbackMethod = new ObjcMethod (objcClass->isa(), (const char *)@selector(invokeUndefinedMethodFromWebScript:withArguments:));
317             fallbackMethod->setJavaScriptName((CFStringRef)[NSString stringWithCString:_item.ascii()]);
318             methodList.addMethod ((Method *)fallbackMethod);
319             result = instance->invokeMethod(exec, methodList, args);
320             delete fallbackMethod;
321         }
322                 
323         instance->end();
324     }
325
326     return result;
327 }
328
329 bool ObjcFallbackObjectImp::deleteProperty(ExecState *exec,
330                             const Identifier &propertyName)
331 {
332     return false;
333 }
334
335 JSValue *ObjcFallbackObjectImp::defaultValue(ExecState *exec, JSType hint) const
336 {
337     return _instance->getValueOfUndefinedField(exec, _item, hint);
338 }
339
340 bool ObjcFallbackObjectImp::toBoolean(ExecState *exec) const
341 {
342     id targetObject = _instance->getObject();
343     
344     if ([targetObject respondsToSelector:@selector(invokeUndefinedMethodFromWebScript:withArguments:)])
345         return true;
346     
347     return false;
348 }