2 * Copyright (C) 2004, 2008 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "objc_runtime.h"
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>
40 using namespace WebCore;
45 ClassStructPtr webScriptObjectClass()
47 static ClassStructPtr<WebScriptObject> webScriptObjectClass = NSClassFromString(@"WebScriptObject");
48 return webScriptObjectClass;
51 ClassStructPtr webUndefinedClass()
53 static ClassStructPtr<WebUndefined> webUndefinedClass = NSClassFromString(@"WebUndefined");
54 return webUndefinedClass;
57 // ---------------------- ObjcMethod ----------------------
59 ObjcMethod::ObjcMethod(ClassStructPtr aClass, SEL selector)
65 int ObjcMethod::numParameters() const
67 return [getMethodSignature() numberOfArguments] - 2;
70 NSMethodSignature* ObjcMethod::getMethodSignature() const
72 return [_objcClass instanceMethodSignatureForSelector:_selector];
75 // ---------------------- ObjcField ----------------------
77 ObjcField::ObjcField(Ivar ivar)
79 , _name(AdoptCF, CFStringCreateWithCString(0, ivar_getName(_ivar), kCFStringEncodingASCII))
83 ObjcField::ObjcField(CFStringRef name)
89 JSValue ObjcField::valueFromInstance(ExecState* exec, const Instance* instance) const
91 JSValue result = jsUndefined();
93 id targetObject = (static_cast<const ObjcInstance*>(instance))->getObject();
95 JSLock::DropAllLocks dropAllLocks(SilenceAssertionsOnly); // Can't put this inside the @try scope because it unwinds incorrectly.
98 if (id objcValue = [targetObject valueForKey:(NSString *)_name.get()])
99 result = convertObjcValueToValue(exec, &objcValue, ObjcObjectType, instance->rootObject());
101 JSLock lock(SilenceAssertionsOnly);
102 ObjcInstance::moveGlobalExceptionToExecState(exec);
104 } @catch(NSException* localException) {
105 JSLock::lock(SilenceAssertionsOnly);
106 throwError(exec, [localException reason]);
107 JSLock::unlock(SilenceAssertionsOnly);
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);
115 static id convertValueToObjcObject(ExecState* exec, JSValue value)
117 RefPtr<RootObject> rootObject = findRootObject(exec->dynamicGlobalObject());
120 return [webScriptObjectClass() _convertValueToObjcValue:value originRootObject:rootObject.get() rootObject:rootObject.get()];
123 void ObjcField::setValueToInstance(ExecState* exec, const Instance* instance, JSValue aValue) const
125 id targetObject = (static_cast<const ObjcInstance*>(instance))->getObject();
126 id value = convertValueToObjcObject(exec, aValue);
128 JSLock::DropAllLocks dropAllLocks(SilenceAssertionsOnly); // Can't put this inside the @try scope because it unwinds incorrectly.
131 [targetObject setValue:value forKey:(NSString *)_name.get()];
133 JSLock lock(SilenceAssertionsOnly);
134 ObjcInstance::moveGlobalExceptionToExecState(exec);
136 } @catch(NSException* localException) {
137 JSLock::lock(SilenceAssertionsOnly);
138 throwError(exec, [localException reason]);
139 JSLock::unlock(SilenceAssertionsOnly);
143 // ---------------------- ObjcArray ----------------------
145 ObjcArray::ObjcArray(ObjectStructPtr a, PassRefPtr<RootObject> rootObject)
151 void ObjcArray::setValueAt(ExecState* exec, unsigned int index, JSValue aValue) const
153 if (![_array.get() respondsToSelector:@selector(insertObject:atIndex:)]) {
154 throwError(exec, createTypeError(exec, "Array is not mutable."));
158 if (index > [_array.get() count]) {
159 throwError(exec, createRangeError(exec, "Index exceeds array size."));
163 // Always try to convert the value to an ObjC object, so it can be placed in the
165 ObjcValue oValue = convertValueToObjcValue (exec, aValue, ObjcObjectType);
168 [_array.get() insertObject:oValue.objectValue atIndex:index];
169 } @catch(NSException* localException) {
170 throwError(exec, createError(exec, "Objective-C exception."));
174 JSValue ObjcArray::valueAt(ExecState* exec, unsigned int index) const
176 if (index > [_array.get() count])
177 return throwError(exec, createRangeError(exec, "Index exceeds array size."));
179 id obj = [_array.get() objectAtIndex:index];
181 return convertObjcValueToValue (exec, &obj, ObjcObjectType, m_rootObject.get());
182 } @catch(NSException* localException) {
183 return throwError(exec, createError(exec, "Objective-C exception."));
185 return jsUndefined();
188 unsigned int ObjcArray::getLength() const
190 return [_array.get() count];
193 const ClassInfo ObjcFallbackObjectImp::s_info = { "ObjcFallbackObject", &JSNonFinalObject::s_info, 0, 0, CREATE_METHOD_TABLE(ObjcFallbackObjectImp) };
195 ObjcFallbackObjectImp::ObjcFallbackObjectImp(JSGlobalObject* globalObject, Structure* structure, ObjcInstance* i, const Identifier& propertyName)
196 : JSNonFinalObject(globalObject->globalData(), structure)
198 , _item(propertyName)
202 void ObjcFallbackObjectImp::finishCreation(JSGlobalObject* globalObject)
204 Base::finishCreation(globalObject->globalData());
205 ASSERT(inherits(&s_info));
208 bool ObjcFallbackObjectImp::getOwnPropertySlot(JSCell*, ExecState*, const Identifier&, PropertySlot& slot)
210 // keep the prototype from getting called instead of just returning false
215 bool ObjcFallbackObjectImp::getOwnPropertyDescriptor(JSObject*, ExecState*, const Identifier&, PropertyDescriptor& descriptor)
217 // keep the prototype from getting called instead of just returning false
218 descriptor.setUndefined();
222 void ObjcFallbackObjectImp::put(JSCell*, ExecState*, const Identifier&, JSValue, PutPropertySlot&)
226 static EncodedJSValue JSC_HOST_CALL callObjCFallbackObject(ExecState* exec)
228 JSValue thisValue = exec->hostThisValue();
229 if (!thisValue.inherits(&ObjCRuntimeObject::s_info))
230 return throwVMTypeError(exec);
232 JSValue result = jsUndefined();
234 ObjCRuntimeObject* runtimeObject = static_cast<ObjCRuntimeObject*>(asObject(thisValue));
235 ObjcInstance* objcInstance = runtimeObject->getInternalObjCInstance();
238 return JSValue::encode(RuntimeObject::throwInvalidAccessError(exec));
240 objcInstance->begin();
242 id targetObject = objcInstance->getObject();
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());
255 return JSValue::encode(result);
258 CallType ObjcFallbackObjectImp::getCallData(JSCell* cell, CallData& callData)
260 ObjcFallbackObjectImp* thisObject = jsCast<ObjcFallbackObjectImp*>(cell);
261 id targetObject = thisObject->_instance->getObject();
262 if (![targetObject respondsToSelector:@selector(invokeUndefinedMethodFromWebScript:withArguments:)])
264 callData.native.function = callObjCFallbackObject;
268 bool ObjcFallbackObjectImp::deleteProperty(JSCell*, ExecState*, const Identifier&)
273 JSValue ObjcFallbackObjectImp::defaultValue(const JSObject* object, ExecState* exec, PreferredPrimitiveType)
275 const ObjcFallbackObjectImp* thisObject = jsCast<const ObjcFallbackObjectImp*>(object);
276 return thisObject->_instance->getValueOfUndefinedField(exec, thisObject->_item);
279 bool ObjcFallbackObjectImp::toBoolean(ExecState *) const
281 id targetObject = _instance->getObject();
283 if ([targetObject respondsToSelector:@selector(invokeUndefinedMethodFromWebScript:withArguments:)])