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