/* * 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. */ #import #include #include #include #include #include #include #include #include #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_3 @interface NSObject (WebExtras) - (void)finalize; @end #endif using namespace KJS; using namespace KJS::Bindings; #define LOG_EXCEPTION(exec) \ if (Interpreter::shouldPrintExceptions()) \ NSLog (@"%s:%d:[%d] JavaScript exception: %s\n", __FILE__, __LINE__, getpid(), exec->exception().toObject(exec).get(exec, messagePropertyName).toString(exec).ascii()); @implementation WebScriptObjectPrivate @end @implementation WebScriptObject static void _didExecute(WebScriptObject *obj) { ExecState *exec = [obj _executionContext]->interpreter()->globalExec(); KJSDidExecuteFunctionPtr func = Instance::didExecuteFunction(); if (func) func (exec, static_cast([obj _executionContext]->rootObjectImp())); } - (void)_initializeWithObjectImp:(KJS::ObjectImp *)imp originExecutionContext:(const Bindings::RootObject *)originExecutionContext executionContext:(const Bindings::RootObject *)executionContext { _private->imp = imp; _private->executionContext = executionContext; _private->originExecutionContext = originExecutionContext; addNativeReference (executionContext, imp); } - _initWithObjectImp:(KJS::ObjectImp *)imp originExecutionContext:(const Bindings::RootObject *)originExecutionContext executionContext:(const Bindings::RootObject *)executionContext { assert (imp != 0); //assert (root != 0); self = [super init]; _private = [[WebScriptObjectPrivate alloc] init]; [self _initializeWithObjectImp:imp originExecutionContext:originExecutionContext executionContext:executionContext]; return self; } - (KJS::ObjectImp *)_imp { if (!_private->imp && _private->isCreatedByDOMWrapper) { // Associate the WebScriptObject with the JS wrapper for the ObjC DOM // wrapper. This is done on lazily, on demand. [self _initializeScriptDOMNodeImp]; } return _private->imp; } - (const KJS::Bindings::RootObject *)_executionContext { return _private->executionContext; } - (void)_setExecutionContext:(const KJS::Bindings::RootObject *)context { _private->executionContext = context; } - (const KJS::Bindings::RootObject *)_originExecutionContext { return _private->originExecutionContext; } - (void)_setOriginExecutionContext:(const KJS::Bindings::RootObject *)originExecutionContext { _private->originExecutionContext = originExecutionContext; } - (BOOL)_isSafeScript { if ([self _originExecutionContext]) { Interpreter *originInterpreter = [self _originExecutionContext]->interpreter(); if (originInterpreter) { return originInterpreter->isSafeScript ([self _executionContext]->interpreter()); } } return true; } - (void)dealloc { removeNativeReference(_private->imp); [_private release]; [super dealloc]; } - (void)finalize { removeNativeReference(_private->imp); [super finalize]; } + (BOOL)throwException:(NSString *)exceptionMessage { InterpreterImp *first, *interp = InterpreterImp::firstInterpreter(); // This code assumes that we only ever have one running interpreter. A // good assumption for now, as we depend on that elsewhere. However, // in the future we may have the ability to run multiple interpreters, // in which case this will have to change. first = interp; do { ExecState *exec = interp->globalExec(); // If the interpreter has a context, we set the exception. if (interp->context()) { Object err = Error::create(exec, GeneralError, [exceptionMessage UTF8String]); exec->setException (err); return YES; } interp = interp->nextInterpreter(); } while (interp != first); return NO; } static KJS::List listFromNSArray(ExecState *exec, NSArray *array) { long i, numObjects = array ? [array count] : 0; KJS::List aList; for (i = 0; i < numObjects; i++) { id anObject = [array objectAtIndex:i]; aList.append (convertObjcValueToValue(exec, &anObject, ObjcObjectType)); } return aList; } - (id)callWebScriptMethod:(NSString *)name withArguments:(NSArray *)args { if (![self _executionContext]) return nil; if (![self _isSafeScript]) return nil; // Lookup the function object. ExecState *exec = [self _executionContext]->interpreter()->globalExec(); InterpreterLock lock; Value v = convertObjcValueToValue(exec, &name, ObjcObjectType); Identifier identifier(v.toString(exec)); Value func = [self _imp]->get (exec, identifier); if (func.isNull() || func.type() == UndefinedType) { // Maybe throw an exception here? return 0; } // Call the function object. ObjectImp *funcImp = static_cast(func.imp()); Object thisObj = Object(const_cast([self _imp])); List argList = listFromNSArray(exec, args); Value result = Object(funcImp).call (exec, thisObj, argList); if (exec->hadException()) { LOG_EXCEPTION (exec); result = Undefined(); } // Convert and return the result of the function call. id resultObj = [WebScriptObject _convertValueToObjcValue:result originExecutionContext:[self _originExecutionContext] executionContext:[self _executionContext]]; _didExecute(self); return resultObj; } - (id)evaluateWebScript:(NSString *)script { if (![self _executionContext]) return nil; if (![self _isSafeScript]) return nil; ExecState *exec = [self _executionContext]->interpreter()->globalExec(); Object thisObj = Object(const_cast([self _imp])); Value result; InterpreterLock lock; Value v = convertObjcValueToValue(exec, &script, ObjcObjectType); Completion completion = [self _executionContext]->interpreter()->evaluate(UString(), 0, v.toString(exec)); ComplType type = completion.complType(); if (type == Normal) { result = completion.value(); if (result.isNull()) result = Undefined(); } else result = Undefined(); if (exec->hadException()) { LOG_EXCEPTION (exec); result = Undefined(); } id resultObj = [WebScriptObject _convertValueToObjcValue:result originExecutionContext:[self _originExecutionContext] executionContext:[self _executionContext]]; _didExecute(self); return resultObj; } - (void)setValue:(id)value forKey:(NSString *)key { if (![self _executionContext]) return; if (![self _isSafeScript]) return; ExecState *exec = [self _executionContext]->interpreter()->globalExec(); InterpreterLock lock; Value v = convertObjcValueToValue(exec, &key, ObjcObjectType); [self _imp]->put (exec, Identifier (v.toString(exec)), (convertObjcValueToValue(exec, &value, ObjcObjectType))); if (exec->hadException()) { LOG_EXCEPTION (exec); } _didExecute(self); } - (id)valueForKey:(NSString *)key { if (![self _executionContext]) return nil; if (![self _isSafeScript]) return nil; ExecState *exec = [self _executionContext]->interpreter()->globalExec(); InterpreterLock lock; Value v = convertObjcValueToValue(exec, &key, ObjcObjectType); Value result = [self _imp]->get (exec, Identifier (v.toString(exec))); if (exec->hadException()) { LOG_EXCEPTION (exec); result = Undefined(); } id resultObj = [WebScriptObject _convertValueToObjcValue:result originExecutionContext:[self _originExecutionContext] executionContext:[self _executionContext]]; _didExecute(self); return resultObj; } - (void)removeWebScriptKey:(NSString *)key { if (![self _executionContext]) return; if (![self _isSafeScript]) return; ExecState *exec = [self _executionContext]->interpreter()->globalExec(); InterpreterLock lock; Value v = convertObjcValueToValue(exec, &key, ObjcObjectType); [self _imp]->deleteProperty (exec, Identifier (v.toString(exec))); if (exec->hadException()) { LOG_EXCEPTION (exec); } _didExecute(self); } - (NSString *)stringRepresentation { if (![self _isSafeScript]) return @"Undefined"; InterpreterLock lock; Object thisObj = Object(const_cast([self _imp])); ExecState *exec = [self _executionContext]->interpreter()->globalExec(); id result = convertValueToObjcValue(exec, thisObj, ObjcObjectType).objectValue; id resultObj = [result description]; _didExecute(self); return resultObj; } - (id)webScriptValueAtIndex:(unsigned int)index { if (![self _executionContext]) return nil; if (![self _isSafeScript]) return nil; ExecState *exec = [self _executionContext]->interpreter()->globalExec(); InterpreterLock lock; Value result = [self _imp]->get (exec, (unsigned)index); if (exec->hadException()) { LOG_EXCEPTION (exec); result = Undefined(); } id resultObj = [WebScriptObject _convertValueToObjcValue:result originExecutionContext:[self _originExecutionContext] executionContext:[self _executionContext]]; _didExecute(self); return resultObj; } - (void)setWebScriptValueAtIndex:(unsigned int)index value:(id)value { if (![self _executionContext]) return; if (![self _isSafeScript]) return; ExecState *exec = [self _executionContext]->interpreter()->globalExec(); InterpreterLock lock; [self _imp]->put (exec, (unsigned)index, (convertObjcValueToValue(exec, &value, ObjcObjectType))); if (exec->hadException()) { LOG_EXCEPTION (exec); } _didExecute(self); } - (void)setException: (NSString *)description { if (![self _executionContext]) return; ExecState *exec = [self _executionContext]->interpreter()->globalExec(); Object err = Error::create(exec, GeneralError, [description UTF8String]); exec->setException (err); } + (id)_convertValueToObjcValue:(KJS::Value)value originExecutionContext:(const Bindings::RootObject *)originExecutionContext executionContext:(const Bindings::RootObject *)executionContext { // First see if we have a ObjC instance. if (value.type() == KJS::ObjectType){ ObjectImp *objectImp = static_cast(value.imp()); Interpreter *intepreter = executionContext->interpreter(); ExecState *exec = intepreter->globalExec(); InterpreterLock lock; if (objectImp->classInfo() != &KJS::RuntimeObjectImp::info) { Value runtimeObject = objectImp->get(exec, "__apple_runtime_object"); if (!runtimeObject.isNull() && runtimeObject.type() == KJS::ObjectType) objectImp = static_cast(runtimeObject.imp()); } if (objectImp->classInfo() == &KJS::RuntimeObjectImp::info) { RuntimeObjectImp *imp = static_cast(objectImp); ObjcInstance *instance = static_cast(imp->getInternalInstance()); if (instance) return instance->getObject(); } // Convert to a WebScriptObject else { return (id)intepreter->createLanguageInstanceForValue (exec, Instance::ObjectiveCLanguage, value.toObject(exec), originExecutionContext, executionContext); } } // Convert JavaScript String value to NSString? else if (value.type() == KJS::StringType) { StringImp *s = static_cast(value.imp()); UString u = s->value(); NSString *string = [NSString stringWithCharacters:(const unichar*)u.data() length:u.size()]; return string; } // Convert JavaScript Number value to NSNumber? else if (value.type() == KJS::NumberType) { Number n = Number::dynamicCast(value); return [NSNumber numberWithDouble:n.value()]; } else if (value.type() == KJS::BooleanType) { KJS::BooleanImp *b = static_cast(value.imp()); return [NSNumber numberWithBool:b->value()]; } // Convert JavaScript Undefined types to WebUndefined else if (value.type() == KJS::UndefinedType) { return [WebUndefined undefined]; } // Other types (UnspecifiedType and NullType) converted to 0. return 0; } @end @implementation WebUndefined + (id)allocWithZone:(NSZone *)zone { static WebUndefined *sharedUndefined = 0; if (!sharedUndefined) sharedUndefined = [super allocWithZone:NULL]; return sharedUndefined; } - (id)initWithCoder:(NSCoder *)coder { return self; } - (void)encodeWithCoder:(NSCoder *)encoder { } - (id)copyWithZone:(NSZone *)zone { return self; } - (id)retain { return self; } - (void)release { } - (unsigned)retainCount { return UINT_MAX; } - (id)autorelease { return self; } - (void)dealloc { assert(false); return; [super dealloc]; // make -Wdealloc-check happy } + (WebUndefined *)undefined { return [WebUndefined allocWithZone:NULL]; } @end