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 41 using namespace WebCore; 42 43 namespace JSC { 44 namespace Bindings { 45 46 ClassStructPtr webScriptObjectClass() 47 { 48 static ClassStructPtr<WebScriptObject> webScriptObjectClass = NSClassFromString(@"WebScriptObject"); 49 return webScriptObjectClass; 50 } 51 52 ClassStructPtr webUndefinedClass() 53 { 54 static ClassStructPtr<WebUndefined> webUndefinedClass = NSClassFromString(@"WebUndefined"); 55 return webUndefinedClass; 56 } 57 58 // ---------------------- ObjcMethod ---------------------- 59 60 ObjcMethod::ObjcMethod(ClassStructPtr aClass, SEL selector) 61 : _objcClass(aClass) 62 , _selector(selector) 63 { 64 } 65 66 int ObjcMethod::numParameters() const 67 { 68 return [getMethodSignature() numberOfArguments] - 2; 69 } 70 71 NSMethodSignature* ObjcMethod::getMethodSignature() const 72 { 73 return [_objcClass instanceMethodSignatureForSelector:_selector]; 74 } 75 76 // ---------------------- ObjcField ---------------------- 77 78 ObjcField::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 88 ObjcField::ObjcField(CFStringRef name) 89 : _ivar(0) 90 , _name(name) 91 { 92 } 93 94 JSValue 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 116 static 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 124 void 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 142 ObjcArray::ObjcArray(ObjectStructPtr a, PassRefPtr<RootObject> rootObject) 143 : Array(rootObject) 144 , _array(a) 145 { 146 } 147 148 void 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 171 JSValue 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 185 unsigned int ObjcArray::getLength() const 186 { 187 return [_array.get() count]; 188 } 189 190 const ClassInfo ObjcFallbackObjectImp::s_info = { "ObjcFallbackObject", &JSObjectWithGlobalObject::s_info, 0, 0 }; 191 192 ObjcFallbackObjectImp::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 201 bool 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 208 bool 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 215 void ObjcFallbackObjectImp::put(ExecState*, const Identifier&, JSValue, PutPropertySlot&) 216 { 217 } 218 219 static 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 251 CallType 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 260 bool ObjcFallbackObjectImp::deleteProperty(ExecState*, const Identifier&) 261 { 262 return false; 263 } 264 265 JSValue ObjcFallbackObjectImp::defaultValue(ExecState* exec, PreferredPrimitiveType) const 266 { 267 return _instance->getValueOfUndefinedField(exec, _item); 268 } 269 270 bool 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