Home | History | Annotate | Download | only in objc
      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