blob: 9b4711e8eb89cc8e9f0df7ad09a270f2b48e1b22 [file] [log] [blame]
/*
* 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 "config.h"
#import "WebScriptObjectPrivate.h"
#import "objc_instance.h"
#import "runtime_object.h"
#import <kxmlcore/UnusedParam.h>
using namespace KJS;
using namespace KJS::Bindings;
#define LOG_EXCEPTION(exec) \
if (Interpreter::shouldPrintExceptions()) \
printf("%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<JSObject*>([obj _executionContext]->rootObjectImp()));
}
- (void)_initializeWithObjectImp:(JSObject *)imp originExecutionContext:(const RootObject *)originExecutionContext executionContext:(const RootObject *)executionContext
{
_private->imp = imp;
_private->executionContext = executionContext;
_private->originExecutionContext = originExecutionContext;
addNativeReference (executionContext, imp);
}
- _initWithJSObject:(JSObject *)imp originExecutionContext:(const RootObject *)originExecutionContext executionContext:(const RootObject *)executionContext
{
assert (imp != 0);
//assert (root != 0);
self = [super init];
_private = [[WebScriptObjectPrivate alloc] init];
[self _initializeWithObjectImp:imp originExecutionContext:originExecutionContext executionContext:executionContext];
return self;
}
- (JSObject *)_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 RootObject *)_executionContext
{
return _private->executionContext;
}
- (void)_setExecutionContext:(const RootObject *)context
{
_private->executionContext = context;
}
- (const RootObject *)_originExecutionContext
{
return _private->originExecutionContext;
}
- (void)_setOriginExecutionContext:(const 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()) {
throwError(exec, GeneralError, exceptionMessage);
return YES;
}
interp = interp->nextInterpreter();
} while (interp != first);
return NO;
}
static List listFromNSArray(ExecState *exec, NSArray *array)
{
int i, numObjects = array ? [array count] : 0;
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();
JSLock lock;
JSValue *v = convertObjcValueToValue(exec, &name, ObjcObjectType);
Identifier identifier(v->toString(exec));
JSValue *func = [self _imp]->get (exec, identifier);
if (!func || func->isUndefined()) {
// Maybe throw an exception here?
return 0;
}
// Call the function object.
JSObject *funcImp = static_cast<JSObject*>(func);
JSObject *thisObj = const_cast<JSObject*>([self _imp]);
List argList = listFromNSArray(exec, args);
JSValue *result = funcImp->call (exec, thisObj, argList);
if (exec->hadException()) {
LOG_EXCEPTION (exec);
result = jsUndefined();
}
// 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();
JSValue *result;
JSLock lock;
JSValue *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)
result = jsUndefined();
} else
result = jsUndefined();
if (exec->hadException()) {
LOG_EXCEPTION (exec);
result = jsUndefined();
}
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();
JSLock lock;
JSValue *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();
JSLock lock;
JSValue *v = convertObjcValueToValue(exec, &key, ObjcObjectType);
JSValue *result = [self _imp]->get (exec, Identifier (v->toString(exec)));
if (exec->hadException()) {
LOG_EXCEPTION (exec);
result = jsUndefined();
}
id resultObj = [WebScriptObject _convertValueToObjcValue:result originExecutionContext:[self _originExecutionContext] executionContext:[self _executionContext]];
if ([resultObj isKindOfClass:[WebUndefined class]])
resultObj = [super valueForKey:key]; // ensure correct not-applicable key behavior
_didExecute(self);
return resultObj;
}
- (void)removeWebScriptKey:(NSString *)key
{
if (![self _executionContext])
return;
if (![self _isSafeScript])
return;
ExecState *exec = [self _executionContext]->interpreter()->globalExec();
JSLock lock;
JSValue *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])
// This is a workaround for a gcc 3.3 internal compiler error.
return @"Undefined";
JSLock lock;
JSObject *thisObj = const_cast<JSObject*>([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();
JSLock lock;
JSValue *result = [self _imp]->get (exec, (unsigned)index);
if (exec->hadException()) {
LOG_EXCEPTION (exec);
result = jsUndefined();
}
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();
JSLock lock;
[self _imp]->put (exec, (unsigned)index, (convertObjcValueToValue(exec, &value, ObjcObjectType)));
if (exec->hadException()) {
LOG_EXCEPTION (exec);
}
_didExecute(self);
}
- (void)setException:(NSString *)description
{
if (const RootObject *root = [self _executionContext])
throwError(root->interpreter()->globalExec(), GeneralError, description);
}
+ (id)_convertValueToObjcValue:(JSValue *)value originExecutionContext:(const RootObject *)originExecutionContext executionContext:(const RootObject *)executionContext
{
// First see if we have a ObjC instance.
if (value->isObject()) {
JSObject *objectImp = static_cast<JSObject*>(value);
Interpreter *intepreter = executionContext->interpreter();
ExecState *exec = intepreter->globalExec();
JSLock lock;
if (objectImp->classInfo() != &RuntimeObjectImp::info) {
JSValue *runtimeObject = objectImp->get(exec, "__apple_runtime_object");
if (runtimeObject && runtimeObject->isObject())
objectImp = static_cast<RuntimeObjectImp*>(runtimeObject);
}
if (objectImp->classInfo() == &RuntimeObjectImp::info) {
RuntimeObjectImp *imp = static_cast<RuntimeObjectImp *>(objectImp);
ObjcInstance *instance = static_cast<ObjcInstance*>(imp->getInternalInstance());
if (instance)
return instance->getObject();
} else
// JS Object --> WebScriptObject
return (id)intepreter->createLanguageInstanceForValue (exec, Instance::ObjectiveCLanguage, value->toObject(exec), originExecutionContext, executionContext);
} else if (value->isString()) {
// JS String --> NSString
UString u = value->getString();
NSString *string = [NSString stringWithCharacters:(const unichar*)u.data() length:u.size()];
return string;
} else if (value->isNumber())
// JS Number --> NSNumber
return [NSNumber numberWithDouble:value->getNumber()];
else if (value->isBoolean())
// JS Boolean --> NSNumber
return [NSNumber numberWithBool:value->getBoolean()];
else if (value->isUndefined())
// JS Undefined --> WebUndefined
return [WebUndefined undefined];
// Other types (UnspecifiedType and NullType) converted to 0.
return 0;
}
@end
@interface WebScriptObject (WebKitCocoaBindings)
- (unsigned int)count;
- (id)objectAtIndex:(unsigned int)index;
@end
@implementation WebScriptObject (WebKitCocoaBindings)
- (unsigned int)count
{
id length = [self valueForKey:@"length"];
if ([length respondsToSelector:@selector(intValue)])
return [length intValue];
else
return 0;
}
- (id)objectAtIndex:(unsigned int)index
{
return [self webScriptValueAtIndex:index];
}
@end
@implementation WebUndefined
+ (id)allocWithZone:(NSZone *)zone
{
UNUSED_PARAM(zone);
static WebUndefined *sharedUndefined = 0;
if (!sharedUndefined)
sharedUndefined = [super allocWithZone:NULL];
return sharedUndefined;
}
- (NSString *)description
{
return @"undefined";
}
- (id)initWithCoder:(NSCoder *)coder
{
UNUSED_PARAM(coder);
return self;
}
- (void)encodeWithCoder:(NSCoder *)encoder
{
UNUSED_PARAM(encoder);
}
- (id)copyWithZone:(NSZone *)zone
{
UNUSED_PARAM(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