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