/* * 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 using namespace KJS; using namespace KJS::Bindings; // ---------------------- 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 (const char *)_selector; } int ObjcMethod::numParameters() const { return [getMethodSignature() numberOfArguments] - 2; } NSMethodSignature *ObjcMethod::getMethodSignature() const { return [(id)_objcClass instanceMethodSignatureForSelector:(SEL)_selector]; } void ObjcMethod::setJavaScriptName (CFStringRef n) { if (n != _javaScriptName) { if (_javaScriptName != 0) CFRelease (_javaScriptName); _javaScriptName = (CFStringRef)CFRetain (n); } } // ---------------------- 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 (_ivar) return _ivar->ivar_name; return [(NSString *)_name UTF8String]; } RuntimeType ObjcField::type() const { if (_ivar) return _ivar->ivar_type; // Type is irrelevant if we use KV to set/get the value. return ""; } JSValue *ObjcField::valueFromInstance(ExecState *exec, const Instance *instance) const { id targetObject = (static_cast(instance))->getObject(); @try { NSString *key = [NSString stringWithCString:name()]; id objcValue = [targetObject valueForKey:key]; if (objcValue) return convertObjcValueToValue (exec, &objcValue, ObjcObjectType); } @catch(NSException *localException) { throwError(exec, GeneralError, [localException reason]); } return jsUndefined(); } static id convertValueToObjcObject (ExecState *exec, JSValue *value) { const Bindings::RootObject *root = rootForInterpreter(exec->dynamicInterpreter()); if (!root) { Bindings::RootObject *newRoot = new Bindings::RootObject(0); newRoot->setInterpreter (exec->dynamicInterpreter()); root = newRoot; } return [WebScriptObject _convertValueToObjcValue:value originExecutionContext:root executionContext:root ]; } void ObjcField::setValueToInstance(ExecState *exec, const Instance *instance, JSValue *aValue) const { id targetObject = (static_cast(instance))->getObject(); id value = convertValueToObjcObject(exec, aValue); @try { NSString *key = [NSString stringWithCString:name()]; [targetObject setValue:value forKey:key]; } @catch(NSException *localException) { throwError(exec, GeneralError, [localException reason]); } } // ---------------------- ObjcArray ---------------------- ObjcArray::ObjcArray (ObjectStructPtr a) { _array = (id)CFRetain(a); } ObjcArray::~ObjcArray () { CFRelease(_array); } ObjcArray::ObjcArray (const ObjcArray &other) : Array() { _array = other._array; CFRetain(_array); } ObjcArray &ObjcArray::operator=(const ObjcArray &other) { ObjectStructPtr _oldArray = _array; _array = other._array; CFRetain(_array); CFRelease(_oldArray); return *this; } void ObjcArray::setValueAt(ExecState *exec, unsigned int index, JSValue *aValue) const { if (![_array respondsToSelector:@selector(insertObject:atIndex:)]) { throwError(exec, TypeError, "Array is not mutable."); return; } if (index > [_array 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 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 count]) return throwError(exec, RangeError, "Index exceeds array size."); @try { id obj = [_array objectAtIndex:index]; if (obj) return convertObjcValueToValue (exec, &obj, ObjcObjectType); } @catch(NSException *localException) { return throwError(exec, GeneralError, "Objective-C exception."); } return jsUndefined(); } unsigned int ObjcArray::getLength() const { return [_array 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) { JSValue *result = jsUndefined(); RuntimeObjectImp *imp = static_cast(thisObj); if (imp) { Instance *instance = imp->getInternalInstance(); 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(), (const char *)@selector(invokeUndefinedMethodFromWebScript:withArguments:)); fallbackMethod->setJavaScriptName((CFStringRef)[NSString stringWithCString:_item.ascii()]); methodList.addMethod ((Method *)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; }