WebScriptObject.mm [plain text]
/*
* 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 <JavaScriptCore/WebScriptObjectPrivate.h>
#include <JavaScriptCore/internal.h>
#include <JavaScriptCore/list.h>
#include <JavaScriptCore/value.h>
#include <objc_jsobject.h>
#include <objc_instance.h>
#include <objc_utility.h>
#include <runtime_object.h>
#include <runtime_root.h>
#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<KJS::ObjectImp*>([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<ObjectImp*>(func.imp());
Object thisObj = Object(const_cast<ObjectImp*>([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<ObjectImp*>([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<ObjectImp*>([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<ObjectImp*>(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<RuntimeObjectImp*>(runtimeObject.imp());
}
if (objectImp->classInfo() == &KJS::RuntimeObjectImp::info) {
RuntimeObjectImp *imp = static_cast<RuntimeObjectImp *>(objectImp);
ObjcInstance *instance = static_cast<ObjcInstance*>(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<KJS::StringImp*>(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<KJS::BooleanImp*>(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