tizen beta release
[profile/ivi/webkit-efl.git] / Source / WebCore / bridge / objc / objc_runtime.mm
1 /*
2  * Copyright (C) 2004, 2008 Apple 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 "JSDOMBinding.h"
30 #include "ObjCRuntimeObject.h"
31 #include "WebScriptObject.h"
32 #include "objc_instance.h"
33 #include "runtime_array.h"
34 #include "runtime_object.h"
35 #include <runtime/Error.h>
36 #include <runtime/JSGlobalObject.h>
37 #include <runtime/JSLock.h>
38 #include <wtf/RetainPtr.h>
39
40 using namespace WebCore;
41
42 namespace JSC {
43 namespace Bindings {
44
45 ClassStructPtr webScriptObjectClass()
46 {
47     static ClassStructPtr<WebScriptObject> webScriptObjectClass = NSClassFromString(@"WebScriptObject");
48     return webScriptObjectClass;
49 }
50
51 ClassStructPtr webUndefinedClass()
52 {
53     static ClassStructPtr<WebUndefined> webUndefinedClass = NSClassFromString(@"WebUndefined");
54     return webUndefinedClass;
55 }
56
57 // ---------------------- ObjcMethod ----------------------
58
59 ObjcMethod::ObjcMethod(ClassStructPtr aClass, SEL selector)
60     : _objcClass(aClass)
61     , _selector(selector)
62 {
63 }
64
65 int ObjcMethod::numParameters() const
66 {
67     return [getMethodSignature() numberOfArguments] - 2;
68 }
69
70 NSMethodSignature* ObjcMethod::getMethodSignature() const
71 {
72     return [_objcClass instanceMethodSignatureForSelector:_selector];
73 }
74
75 // ---------------------- ObjcField ----------------------
76
77 ObjcField::ObjcField(Ivar ivar) 
78     : _ivar(ivar)
79     , _name(AdoptCF, CFStringCreateWithCString(0, ivar_getName(_ivar), kCFStringEncodingASCII))
80 {
81 }
82
83 ObjcField::ObjcField(CFStringRef name)
84     : _ivar(0)
85     , _name(name)
86 {
87 }
88
89 JSValue ObjcField::valueFromInstance(ExecState* exec, const Instance* instance) const
90 {
91     JSValue result = jsUndefined();
92     
93     id targetObject = (static_cast<const ObjcInstance*>(instance))->getObject();
94
95     JSLock::DropAllLocks dropAllLocks(SilenceAssertionsOnly); // Can't put this inside the @try scope because it unwinds incorrectly.
96
97     @try {
98         if (id objcValue = [targetObject valueForKey:(NSString *)_name.get()])
99             result = convertObjcValueToValue(exec, &objcValue, ObjcObjectType, instance->rootObject());
100         {
101             JSLock lock(SilenceAssertionsOnly);
102             ObjcInstance::moveGlobalExceptionToExecState(exec);
103         }
104     } @catch(NSException* localException) {
105         JSLock::lock(SilenceAssertionsOnly);
106         throwError(exec, [localException reason]);
107         JSLock::unlock(SilenceAssertionsOnly);
108     }
109
110     // Work around problem in some versions of GCC where result gets marked volatile and
111     // it can't handle copying from a volatile to non-volatile.
112     return const_cast<JSValue&>(result);
113 }
114
115 static id convertValueToObjcObject(ExecState* exec, JSValue value)
116 {
117     RefPtr<RootObject> rootObject = findRootObject(exec->dynamicGlobalObject());
118     if (!rootObject)
119         return nil;
120     return [webScriptObjectClass() _convertValueToObjcValue:value originRootObject:rootObject.get() rootObject:rootObject.get()];
121 }
122
123 void ObjcField::setValueToInstance(ExecState* exec, const Instance* instance, JSValue aValue) const
124 {
125     id targetObject = (static_cast<const ObjcInstance*>(instance))->getObject();
126     id value = convertValueToObjcObject(exec, aValue);
127
128     JSLock::DropAllLocks dropAllLocks(SilenceAssertionsOnly); // Can't put this inside the @try scope because it unwinds incorrectly.
129
130     @try {
131         [targetObject setValue:value forKey:(NSString *)_name.get()];
132         {
133             JSLock lock(SilenceAssertionsOnly);
134             ObjcInstance::moveGlobalExceptionToExecState(exec);
135         }
136     } @catch(NSException* localException) {
137         JSLock::lock(SilenceAssertionsOnly);
138         throwError(exec, [localException reason]);
139         JSLock::unlock(SilenceAssertionsOnly);
140     }
141 }
142
143 // ---------------------- ObjcArray ----------------------
144
145 ObjcArray::ObjcArray(ObjectStructPtr a, PassRefPtr<RootObject> rootObject)
146     : Array(rootObject)
147     , _array(a)
148 {
149 }
150
151 void ObjcArray::setValueAt(ExecState* exec, unsigned int index, JSValue aValue) const
152 {
153     if (![_array.get() respondsToSelector:@selector(insertObject:atIndex:)]) {
154         throwError(exec, createTypeError(exec, "Array is not mutable."));
155         return;
156     }
157
158     if (index > [_array.get() count]) {
159         throwError(exec, createRangeError(exec, "Index exceeds array size."));
160         return;
161     }
162     
163     // Always try to convert the value to an ObjC object, so it can be placed in the
164     // array.
165     ObjcValue oValue = convertValueToObjcValue (exec, aValue, ObjcObjectType);
166
167     @try {
168         [_array.get() insertObject:oValue.objectValue atIndex:index];
169     } @catch(NSException* localException) {
170         throwError(exec, createError(exec, "Objective-C exception."));
171     }
172 }
173
174 JSValue ObjcArray::valueAt(ExecState* exec, unsigned int index) const
175 {
176     if (index > [_array.get() count])
177         return throwError(exec, createRangeError(exec, "Index exceeds array size."));
178     @try {
179         id obj = [_array.get() objectAtIndex:index];
180         if (obj)
181             return convertObjcValueToValue (exec, &obj, ObjcObjectType, m_rootObject.get());
182     } @catch(NSException* localException) {
183         return throwError(exec, createError(exec, "Objective-C exception."));
184     }
185     return jsUndefined();
186 }
187
188 unsigned int ObjcArray::getLength() const
189 {
190     return [_array.get() count];
191 }
192
193 const ClassInfo ObjcFallbackObjectImp::s_info = { "ObjcFallbackObject", &JSNonFinalObject::s_info, 0, 0, CREATE_METHOD_TABLE(ObjcFallbackObjectImp) };
194
195 ObjcFallbackObjectImp::ObjcFallbackObjectImp(JSGlobalObject* globalObject, Structure* structure, ObjcInstance* i, const Identifier& propertyName)
196     : JSNonFinalObject(globalObject->globalData(), structure)
197     , _instance(i)
198     , _item(propertyName)
199 {
200 }
201
202 void ObjcFallbackObjectImp::finishCreation(JSGlobalObject* globalObject)
203 {
204     Base::finishCreation(globalObject->globalData());
205     ASSERT(inherits(&s_info));
206 }
207
208 bool ObjcFallbackObjectImp::getOwnPropertySlot(JSCell*, ExecState*, const Identifier&, PropertySlot& slot)
209 {
210     // keep the prototype from getting called instead of just returning false
211     slot.setUndefined();
212     return true;
213 }
214
215 bool ObjcFallbackObjectImp::getOwnPropertyDescriptor(JSObject*, ExecState*, const Identifier&, PropertyDescriptor& descriptor)
216 {
217     // keep the prototype from getting called instead of just returning false
218     descriptor.setUndefined();
219     return true;
220 }
221
222 void ObjcFallbackObjectImp::put(JSCell*, ExecState*, const Identifier&, JSValue, PutPropertySlot&)
223 {
224 }
225
226 static EncodedJSValue JSC_HOST_CALL callObjCFallbackObject(ExecState* exec)
227 {
228     JSValue thisValue = exec->hostThisValue();
229     if (!thisValue.inherits(&ObjCRuntimeObject::s_info))
230         return throwVMTypeError(exec);
231
232     JSValue result = jsUndefined();
233
234     ObjCRuntimeObject* runtimeObject = static_cast<ObjCRuntimeObject*>(asObject(thisValue));
235     ObjcInstance* objcInstance = runtimeObject->getInternalObjCInstance();
236
237     if (!objcInstance)
238         return JSValue::encode(RuntimeObject::throwInvalidAccessError(exec));
239     
240     objcInstance->begin();
241
242     id targetObject = objcInstance->getObject();
243     
244     if ([targetObject respondsToSelector:@selector(invokeUndefinedMethodFromWebScript:withArguments:)]){
245         ObjcClass* objcClass = static_cast<ObjcClass*>(objcInstance->getClass());
246         OwnPtr<ObjcMethod> fallbackMethod(adoptPtr(new ObjcMethod(objcClass->isa(), @selector(invokeUndefinedMethodFromWebScript:withArguments:))));
247         const Identifier& nameIdentifier = static_cast<ObjcFallbackObjectImp*>(exec->callee())->propertyName();
248         RetainPtr<CFStringRef> name(AdoptCF, CFStringCreateWithCharacters(0, nameIdentifier.characters(), nameIdentifier.length()));
249         fallbackMethod->setJavaScriptName(name.get());
250         result = objcInstance->invokeObjcMethod(exec, fallbackMethod.get());
251     }
252             
253     objcInstance->end();
254
255     return JSValue::encode(result);
256 }
257
258 CallType ObjcFallbackObjectImp::getCallData(JSCell* cell, CallData& callData)
259 {
260     ObjcFallbackObjectImp* thisObject = jsCast<ObjcFallbackObjectImp*>(cell);
261     id targetObject = thisObject->_instance->getObject();
262     if (![targetObject respondsToSelector:@selector(invokeUndefinedMethodFromWebScript:withArguments:)])
263         return CallTypeNone;
264     callData.native.function = callObjCFallbackObject;
265     return CallTypeHost;
266 }
267
268 bool ObjcFallbackObjectImp::deleteProperty(JSCell*, ExecState*, const Identifier&)
269 {
270     return false;
271 }
272
273 JSValue ObjcFallbackObjectImp::defaultValue(const JSObject* object, ExecState* exec, PreferredPrimitiveType)
274 {
275     const ObjcFallbackObjectImp* thisObject = jsCast<const ObjcFallbackObjectImp*>(object);
276     return thisObject->_instance->getValueOfUndefinedField(exec, thisObject->_item);
277 }
278
279 bool ObjcFallbackObjectImp::toBoolean(ExecState *) const
280 {
281     id targetObject = _instance->getObject();
282     
283     if ([targetObject respondsToSelector:@selector(invokeUndefinedMethodFromWebScript:withArguments:)])
284         return true;
285     
286     return false;
287 }
288
289 }
290 }