blob: 53ba0e0a0b8f98db4a922acb0d24bc44c446eec0 [file] [log] [blame]
// -*- c-basic-offset: 2 -*-
/*
* This file is part of the KDE libraries
* Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
* Copyright (C) 2001 Peter Kelly (pmk@post.com)
* Copyright (C) 2003 Apple Computer, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#include "value.h"
#include "object.h"
#include "types.h"
#include "interpreter.h"
#include "lookup.h"
#include "reference_list.h"
#include <assert.h>
#include <math.h>
#include <stdio.h>
#include "internal.h"
#include "collector.h"
#include "operations.h"
#include "error_object.h"
#include "nodes.h"
#ifndef NDEBUG
//#define JAVASCRIPT_CALL_TRACING 1
#endif
#if JAVASCRIPT_CALL_TRACING
static bool _traceJavaScript = false;
extern "C" {
void setTraceJavaScript(bool f)
{
_traceJavaScript = f;
}
static bool traceJavaScript()
{
return _traceJavaScript;
}
}
#endif
namespace KJS {
// ------------------------------ Object ---------------------------------------
ValueImp *ObjectImp::call(ExecState *exec, ObjectImp *thisObj, const List &args)
{
assert(implementsCall());
#if KJS_MAX_STACK > 0
static int depth = 0; // sum of all concurrent interpreters
#if JAVASCRIPT_CALL_TRACING
static bool tracing = false;
if (traceJavaScript() && !tracing) {
tracing = true;
for (int i = 0; i < depth; i++)
putchar (' ');
printf ("*** calling: %s\n", toString(exec).ascii());
for (int j = 0; j < args.size(); j++) {
for (int i = 0; i < depth; i++)
putchar (' ');
printf ("*** arg[%d] = %s\n", j, args[j]->toString(exec).ascii());
}
tracing = false;
}
#endif
if (++depth > KJS_MAX_STACK) {
--depth;
return throwError(exec, RangeError, "Maximum call stack size exceeded.");
}
#endif
ValueImp *ret = callAsFunction(exec,thisObj,args);
#if KJS_MAX_STACK > 0
--depth;
#endif
#if JAVASCRIPT_CALL_TRACING
if (traceJavaScript() && !tracing) {
tracing = true;
for (int i = 0; i < depth; i++)
putchar (' ');
printf ("*** returning: %s\n", ret->toString(exec).ascii());
tracing = false;
}
#endif
return ret;
}
// ------------------------------ ObjectImp ------------------------------------
void ObjectImp::mark()
{
AllocatedValueImp::mark();
ValueImp *proto = _proto;
if (!proto->marked())
proto->mark();
_prop.mark();
if (_internalValue && !_internalValue->marked())
_internalValue->mark();
_scope.mark();
}
Type ObjectImp::type() const
{
return ObjectType;
}
const ClassInfo *ObjectImp::classInfo() const
{
return 0;
}
UString ObjectImp::className() const
{
const ClassInfo *ci = classInfo();
if ( ci )
return ci->className;
return "Object";
}
ValueImp *ObjectImp::get(ExecState *exec, const Identifier &propertyName) const
{
PropertySlot slot;
if (const_cast<ObjectImp *>(this)->getPropertySlot(exec, propertyName, slot))
return slot.getValue(exec, propertyName);
return Undefined();
}
ValueImp *ObjectImp::get(ExecState *exec, unsigned propertyName) const
{
PropertySlot slot;
if (const_cast<ObjectImp *>(this)->getPropertySlot(exec, propertyName, slot))
return slot.getValue(exec, propertyName);
return Undefined();
}
bool ObjectImp::getPropertySlot(ExecState *exec, unsigned propertyName, PropertySlot& slot)
{
ObjectImp *imp = this;
while (true) {
if (imp->getOwnPropertySlot(exec, propertyName, slot))
return true;
ValueImp *proto = imp->_proto;
if (!proto->isObject())
break;
imp = static_cast<ObjectImp *>(proto);
}
return false;
}
bool ObjectImp::getOwnPropertySlot(ExecState *exec, unsigned propertyName, PropertySlot& slot)
{
return getOwnPropertySlot(exec, Identifier::from(propertyName), slot);
}
// ECMA 8.6.2.2
void ObjectImp::put(ExecState *exec, const Identifier &propertyName, ValueImp *value, int attr)
{
assert(value);
// non-standard netscape extension
if (propertyName == exec->dynamicInterpreter()->specialPrototypeIdentifier()) {
setPrototype(value);
return;
}
/* TODO: check for write permissions directly w/o this call */
/* Doesn't look very easy with the PropertyMap API - David */
// putValue() is used for JS assignemnts. It passes no attribute.
// Assume that a C++ implementation knows what it is doing
// and let it override the canPut() check.
if ((attr == None || attr == DontDelete) && !canPut(exec,propertyName)) {
#ifdef KJS_VERBOSE
fprintf( stderr, "WARNING: canPut %s said NO\n", propertyName.ascii() );
#endif
return;
}
_prop.put(propertyName,value,attr);
}
void ObjectImp::put(ExecState *exec, unsigned propertyName,
ValueImp *value, int attr)
{
put(exec, Identifier::from(propertyName), value, attr);
}
// ECMA 8.6.2.3
bool ObjectImp::canPut(ExecState *, const Identifier &propertyName) const
{
int attributes;
ValueImp *v = _prop.get(propertyName, attributes);
if (v)
return!(attributes & ReadOnly);
// Look in the static hashtable of properties
const HashEntry* e = findPropertyHashEntry(propertyName);
if (e)
return !(e->attr & ReadOnly);
// Don't look in the prototype here. We can always put an override
// in the object, even if the prototype has a ReadOnly property.
return true;
}
// ECMA 8.6.2.4
bool ObjectImp::hasProperty(ExecState *exec, const Identifier &propertyName) const
{
PropertySlot slot;
return const_cast<ObjectImp *>(this)->getPropertySlot(exec, propertyName, slot);
}
bool ObjectImp::hasProperty(ExecState *exec, unsigned propertyName) const
{
PropertySlot slot;
return const_cast<ObjectImp *>(this)->getPropertySlot(exec, propertyName, slot);
}
// ECMA 8.6.2.5
bool ObjectImp::deleteProperty(ExecState */*exec*/, const Identifier &propertyName)
{
int attributes;
ValueImp *v = _prop.get(propertyName, attributes);
if (v) {
if ((attributes & DontDelete))
return false;
_prop.remove(propertyName);
return true;
}
// Look in the static hashtable of properties
const HashEntry* entry = findPropertyHashEntry(propertyName);
if (entry && entry->attr & DontDelete)
return false; // this builtin property can't be deleted
return true;
}
bool ObjectImp::deleteProperty(ExecState *exec, unsigned propertyName)
{
return deleteProperty(exec, Identifier::from(propertyName));
}
// ECMA 8.6.2.6
ValueImp *ObjectImp::defaultValue(ExecState *exec, Type hint) const
{
if (hint != StringType && hint != NumberType) {
/* Prefer String for Date objects */
if (_proto == exec->lexicalInterpreter()->builtinDatePrototype())
hint = StringType;
else
hint = NumberType;
}
ValueImp *v;
if (hint == StringType)
v = get(exec,toStringPropertyName);
else
v = get(exec,valueOfPropertyName);
if (v->isObject()) {
ObjectImp *o = static_cast<ObjectImp*>(v);
if (o->implementsCall()) { // spec says "not primitive type" but ...
ObjectImp *thisObj = const_cast<ObjectImp*>(this);
ValueImp *def = o->call(exec,thisObj,List::empty());
Type defType = def->type();
if (defType == UnspecifiedType || defType == UndefinedType ||
defType == NullType || defType == BooleanType ||
defType == StringType || defType == NumberType) {
return def;
}
}
}
if (hint == StringType)
v = get(exec,valueOfPropertyName);
else
v = get(exec,toStringPropertyName);
if (v->isObject()) {
ObjectImp *o = static_cast<ObjectImp*>(v);
if (o->implementsCall()) { // spec says "not primitive type" but ...
ObjectImp *thisObj = const_cast<ObjectImp*>(this);
ValueImp *def = o->call(exec,thisObj,List::empty());
Type defType = def->type();
if (defType == UnspecifiedType || defType == UndefinedType ||
defType == NullType || defType == BooleanType ||
defType == StringType || defType == NumberType) {
return def;
}
}
}
if (exec->hadException())
return exec->exception();
return throwError(exec, TypeError, "No default value");
}
const HashEntry* ObjectImp::findPropertyHashEntry(const Identifier& propertyName) const
{
for (const ClassInfo *info = classInfo(); info; info = info->parentClass) {
if (const HashTable *propHashTable = info->propHashTable) {
if (const HashEntry *e = Lookup::findEntry(propHashTable, propertyName))
return e;
}
}
return 0;
}
bool ObjectImp::implementsConstruct() const
{
return false;
}
ObjectImp *ObjectImp::construct(ExecState */*exec*/, const List &/*args*/)
{
assert(false);
return NULL;
}
ObjectImp *ObjectImp::construct(ExecState *exec, const List &args, const UString &/*sourceURL*/, int /*lineNumber*/)
{
return construct(exec, args);
}
bool ObjectImp::implementsCall() const
{
return false;
}
ValueImp *ObjectImp::callAsFunction(ExecState */*exec*/, ObjectImp */*thisObj*/, const List &/*args*/)
{
assert(false);
return NULL;
}
bool ObjectImp::implementsHasInstance() const
{
return false;
}
bool ObjectImp::hasInstance(ExecState */*exec*/, ValueImp */*value*/)
{
assert(false);
return false;
}
ReferenceList ObjectImp::propList(ExecState *exec, bool recursive)
{
ReferenceList list;
if (_proto->isObject() && recursive)
list = static_cast<ObjectImp*>(_proto)->propList(exec,recursive);
_prop.addEnumerablesToReferenceList(list, this);
// Add properties from the static hashtable of properties
const ClassInfo *info = classInfo();
while (info) {
if (info->propHashTable) {
int size = info->propHashTable->size;
const HashEntry *e = info->propHashTable->entries;
for (int i = 0; i < size; ++i, ++e) {
if ( e->s && !(e->attr & DontEnum) )
list.append(Reference(this, e->s)); /// ######### check for duplicates with the propertymap
}
}
info = info->parentClass;
}
return list;
}
ValueImp *ObjectImp::toPrimitive(ExecState *exec, Type preferredType) const
{
return defaultValue(exec,preferredType);
}
bool ObjectImp::toBoolean(ExecState */*exec*/) const
{
return true;
}
double ObjectImp::toNumber(ExecState *exec) const
{
ValueImp *prim = toPrimitive(exec,NumberType);
if (exec->hadException()) // should be picked up soon in nodes.cpp
return 0.0;
return prim->toNumber(exec);
}
UString ObjectImp::toString(ExecState *exec) const
{
ValueImp *prim = toPrimitive(exec,StringType);
if (exec->hadException()) // should be picked up soon in nodes.cpp
return "";
return prim->toString(exec);
}
ObjectImp *ObjectImp::toObject(ExecState */*exec*/) const
{
return const_cast<ObjectImp*>(this);
}
void ObjectImp::putDirect(const Identifier &propertyName, ValueImp *value, int attr)
{
_prop.put(propertyName, value, attr);
}
void ObjectImp::putDirect(const Identifier &propertyName, int value, int attr)
{
_prop.put(propertyName, jsNumber(value), attr);
}
// ------------------------------ Error ----------------------------------------
const char * const errorNamesArr[] = {
I18N_NOOP("Error"), // GeneralError
I18N_NOOP("Evaluation error"), // EvalError
I18N_NOOP("Range error"), // RangeError
I18N_NOOP("Reference error"), // ReferenceError
I18N_NOOP("Syntax error"), // SyntaxError
I18N_NOOP("Type error"), // TypeError
I18N_NOOP("URI error"), // URIError
};
const char * const * const Error::errorNames = errorNamesArr;
ObjectImp *Error::create(ExecState *exec, ErrorType errtype, const UString &message,
int lineno, int sourceId, const UString *sourceURL)
{
ObjectImp *cons;
switch (errtype) {
case EvalError:
cons = exec->lexicalInterpreter()->builtinEvalError();
break;
case RangeError:
cons = exec->lexicalInterpreter()->builtinRangeError();
break;
case ReferenceError:
cons = exec->lexicalInterpreter()->builtinReferenceError();
break;
case SyntaxError:
cons = exec->lexicalInterpreter()->builtinSyntaxError();
break;
case TypeError:
cons = exec->lexicalInterpreter()->builtinTypeError();
break;
case URIError:
cons = exec->lexicalInterpreter()->builtinURIError();
break;
default:
cons = exec->lexicalInterpreter()->builtinError();
break;
}
List args;
if (message.isEmpty())
args.append(jsString(errorNames[errtype]));
else
args.append(jsString(message));
ObjectImp *err = static_cast<ObjectImp *>(cons->construct(exec,args));
if (lineno != -1)
err->put(exec, "line", Number(lineno));
if (sourceId != -1)
err->put(exec, "sourceId", Number(sourceId));
if(sourceURL)
err->put(exec,"sourceURL", String(*sourceURL));
return err;
/*
#ifndef NDEBUG
const char *msg = err->get("message")->toString().value().ascii();
if (l >= 0)
fprintf(stderr, "KJS: %s at line %d. %s\n", estr, l, msg);
else
fprintf(stderr, "KJS: %s. %s\n", estr, msg);
#endif
return err;
*/
}
ObjectImp *Error::create(ExecState *exec, ErrorType type, const char *message)
{
return create(exec, type, message, -1, -1, NULL);
}
ObjectImp *throwError(ExecState *exec, ErrorType type)
{
ObjectImp *error = Error::create(exec, type, UString(), -1, -1, NULL);
exec->setException(error);
return error;
}
ObjectImp *throwError(ExecState *exec, ErrorType type, const UString &message)
{
ObjectImp *error = Error::create(exec, type, message, -1, -1, NULL);
exec->setException(error);
return error;
}
ObjectImp *throwError(ExecState *exec, ErrorType type, const char *message)
{
ObjectImp *error = Error::create(exec, type, message, -1, -1, NULL);
exec->setException(error);
return error;
}
ObjectImp *throwError(ExecState *exec, ErrorType type, const UString &message, int line, int sourceId, const UString *sourceURL)
{
ObjectImp *error = Error::create(exec, type, message, line, sourceId, sourceURL);
exec->setException(error);
return error;
}
} // namespace KJS