/* * Copyright (C) 2004 Apple Computer, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "objc_runtime.h" #include "objc_instance.h" #include "runtime_array.h" #include "runtime_object.h" #include "WebScriptObject.h" using namespace KJS; using namespace KJS::Bindings; extern ClassStructPtr KJS::Bindings::webScriptObjectClass() { static ClassStructPtr webScriptObjectClass = NSClassFromString(@"WebScriptObject"); return webScriptObjectClass; } extern ClassStructPtr KJS::Bindings::webUndefinedClass() { static ClassStructPtr webUndefinedClass = NSClassFromString(@"WebUndefined"); return webUndefinedClass; } // ---------------------- ObjcMethod ---------------------- ObjcMethod::ObjcMethod(ClassStructPtr aClass, const char* name) { _objcClass = aClass; _selector = name; // Assume ObjC runtime keeps these around forever. _javaScriptName = 0; } const char* ObjcMethod::name() const { return _selector; } int ObjcMethod::numParameters() const { return [getMethodSignature() numberOfArguments] - 2; } NSMethodSignature* ObjcMethod::getMethodSignature() const { #if defined(OBJC_API_VERSION) && OBJC_API_VERSION >= 2 return [_objcClass instanceMethodSignatureForSelector:sel_registerName(_selector)]; #else return [_objcClass instanceMethodSignatureForSelector:(SEL)_selector]; #endif } // ---------------------- ObjcField ---------------------- ObjcField::ObjcField(Ivar ivar) { _ivar = ivar; // Assume ObjectiveC runtime will keep this alive forever _name = 0; } ObjcField::ObjcField(CFStringRef name) { _ivar = 0; _name = (CFStringRef)CFRetain(name); } const char* ObjcField::name() const { #if defined(OBJC_API_VERSION) && OBJC_API_VERSION >= 2 if (_ivar) return ivar_getName(_ivar); #else if (_ivar) return _ivar->ivar_name; #endif return [(NSString*)_name.get() UTF8String]; } JSValue* ObjcField::valueFromInstance(ExecState* exec, const Instance* instance) const { JSValue* result = jsUndefined(); id targetObject = (static_cast(instance))->getObject(); JSLock::DropAllLocks dropAllLocks; // Can't put this inside the @try scope because it unwinds incorrectly. @try { NSString* key = [NSString stringWithCString:name() encoding:NSASCIIStringEncoding]; if (id objcValue = [targetObject valueForKey:key]) result = convertObjcValueToValue(exec, &objcValue, ObjcObjectType, instance->rootObject()); } @catch(NSException* localException) { JSLock::lock(); throwError(exec, GeneralError, [localException reason]); JSLock::unlock(); } return result; } static id convertValueToObjcObject(ExecState* exec, JSValue* value) { RefPtr rootObject = findRootObject(exec->dynamicInterpreter()); if (!rootObject) return nil; return [webScriptObjectClass() _convertValueToObjcValue:value originRootObject:rootObject.get() rootObject:rootObject.get()]; } void ObjcField::setValueToInstance(ExecState* exec, const Instance* instance, JSValue* aValue) const { id targetObject = (static_cast(instance))->getObject(); id value = convertValueToObjcObject(exec, aValue); JSLock::DropAllLocks dropAllLocks; // Can't put this inside the @try scope because it unwinds incorrectly. @try { NSString* key = [NSString stringWithCString:name() encoding:NSASCIIStringEncoding]; [targetObject setValue:value forKey:key]; } @catch(NSException* localException) { JSLock::lock(); throwError(exec, GeneralError, [localException reason]); JSLock::unlock(); } } // ---------------------- ObjcArray ---------------------- ObjcArray::ObjcArray(ObjectStructPtr a, PassRefPtr rootObject) : Array(rootObject) , _array(a) { } void ObjcArray::setValueAt(ExecState* exec, unsigned int index, JSValue* aValue) const { if (![_array.get() respondsToSelector:@selector(insertObject:atIndex:)]) { throwError(exec, TypeError, "Array is not mutable."); return; } if (index > [_array.get() count]) { throwError(exec, RangeError, "Index exceeds array size."); return; } // Always try to convert the value to an ObjC object, so it can be placed in the // array. ObjcValue oValue = convertValueToObjcValue (exec, aValue, ObjcObjectType); @try { [_array.get() insertObject:oValue.objectValue atIndex:index]; } @catch(NSException* localException) { throwError(exec, GeneralError, "Objective-C exception."); } } JSValue* ObjcArray::valueAt(ExecState* exec, unsigned int index) const { if (index > [_array.get() count]) return throwError(exec, RangeError, "Index exceeds array size."); @try { id obj = [_array.get() objectAtIndex:index]; if (obj) return convertObjcValueToValue (exec, &obj, ObjcObjectType, _rootObject.get()); } @catch(NSException* localException) { return throwError(exec, GeneralError, "Objective-C exception."); } return jsUndefined(); } unsigned int ObjcArray::getLength() const { return [_array.get() count]; } const ClassInfo ObjcFallbackObjectImp::info = {"ObjcFallbackObject", 0, 0, 0}; ObjcFallbackObjectImp::ObjcFallbackObjectImp(ObjcInstance* i, const KJS::Identifier propertyName) : _instance(i) , _item(propertyName) { } bool ObjcFallbackObjectImp::getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot& slot) { // keep the prototype from getting called instead of just returning false slot.setUndefined(this); return true; } void ObjcFallbackObjectImp::put(ExecState*, const Identifier&, JSValue*, int) { } bool ObjcFallbackObjectImp::canPut(ExecState*, const Identifier&) const { return false; } JSType ObjcFallbackObjectImp::type() const { id targetObject = _instance->getObject(); if ([targetObject respondsToSelector:@selector(invokeUndefinedMethodFromWebScript:withArguments:)]) return ObjectType; return UndefinedType; } bool ObjcFallbackObjectImp::implementsCall() const { id targetObject = _instance->getObject(); if ([targetObject respondsToSelector:@selector(invokeUndefinedMethodFromWebScript:withArguments:)]) return true; return false; } JSValue* ObjcFallbackObjectImp::callAsFunction(ExecState* exec, JSObject* thisObj, const List &args) { if (thisObj->classInfo() != &KJS::RuntimeObjectImp::info) return throwError(exec, TypeError); JSValue* result = jsUndefined(); RuntimeObjectImp* imp = static_cast(thisObj); Instance* instance = imp->getInternalInstance(); if (!instance) return RuntimeObjectImp::throwInvalidAccessError(exec); instance->begin(); ObjcInstance* objcInstance = static_cast(instance); id targetObject = objcInstance->getObject(); if ([targetObject respondsToSelector:@selector(invokeUndefinedMethodFromWebScript:withArguments:)]){ MethodList methodList; ObjcClass* objcClass = static_cast(instance->getClass()); ObjcMethod* fallbackMethod = new ObjcMethod (objcClass->isa(), sel_getName(@selector(invokeUndefinedMethodFromWebScript:withArguments:))); fallbackMethod->setJavaScriptName((CFStringRef)[NSString stringWithCString:_item.ascii() encoding:NSASCIIStringEncoding]); methodList.append(fallbackMethod); result = instance->invokeMethod(exec, methodList, args); delete fallbackMethod; } instance->end(); return result; } bool ObjcFallbackObjectImp::deleteProperty(ExecState*, const Identifier&) { return false; } JSValue* ObjcFallbackObjectImp::defaultValue(ExecState* exec, JSType hint) const { return _instance->getValueOfUndefinedField(exec, _item, hint); } bool ObjcFallbackObjectImp::toBoolean(ExecState *) const { id targetObject = _instance->getObject(); if ([targetObject respondsToSelector:@selector(invokeUndefinedMethodFromWebScript:withArguments:)]) return true; return false; }