object.cpp
// -*- 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 Street, 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"
using namespace KJS;
// ------------------------------ Object ---------------------------------------
Object Object::dynamicCast(const Value &v)
{
if (!v.isValid() || v.type() != ObjectType)
return Object(0);
return Object(static_cast<ObjectImp*>(v.imp()));
}
Value Object::call(ExecState *exec, Object &thisObj, const List &args)
{
#if KJS_MAX_STACK > 0
static int depth = 0; // sum of all concurrent interpreters
if (++depth > KJS_MAX_STACK) {
#ifndef NDEBUG
fprintf(stderr, "Exceeded maximum function call depth\n");
#endif
int saveDepth = depth - 1;
Object err = Error::create(exec, RangeError,
"Exceeded maximum function call depth.");
depth = depth - 10; //Give some room for the debugger to operate,
//so if it tries to examine things we don't get here again
exec->setException(err);
depth = saveDepth;
return err;
}
#endif
Value ret = static_cast<ObjectImp*>(rep)->call(exec,thisObj,args);
#if KJS_MAX_STACK > 0
--depth;
#endif
return ret;
}
// ------------------------------ ObjectImp ------------------------------------
ObjectImp::ObjectImp(const Object &proto)
: _proto(static_cast<ObjectImp*>(proto.imp())), _internalValue(0L)
{
//fprintf(stderr,"ObjectImp::ObjectImp %p\n",(void*)this);
}
ObjectImp::ObjectImp(ObjectImp *proto)
: _proto(proto), _internalValue(0L)
{
}
ObjectImp::ObjectImp()
{
//fprintf(stderr,"ObjectImp::ObjectImp %p\n",(void*)this);
_proto = NullImp::staticNull;
_internalValue = 0L;
}
ObjectImp::~ObjectImp()
{
//fprintf(stderr,"ObjectImp::~ObjectImp %p\n",(void*)this);
}
void ObjectImp::mark()
{
//fprintf(stderr,"ObjectImp::mark() %p\n",(void*)this);
ValueImp::mark();
if (_proto && !_proto->marked())
_proto->mark();
_prop.mark();
if (_internalValue && !_internalValue->marked())
_internalValue->mark();
_scope.mark();
}
const ClassInfo *ObjectImp::classInfo() const
{
return 0;
}
bool ObjectImp::inherits(const ClassInfo *info) const
{
if (!info)
return false;
const ClassInfo *ci = classInfo();
if (!ci)
return false;
while (ci && ci != info)
ci = ci->parentClass;
return (ci == info);
}
Type ObjectImp::type() const
{
return ObjectType;
}
Value ObjectImp::prototype() const
{
return Value(_proto);
}
void ObjectImp::setPrototype(const Value &proto)
{
_proto = proto.imp();
}
UString ObjectImp::className() const
{
const ClassInfo *ci = classInfo();
if ( ci )
return ci->className;
return "Object";
}
Value ObjectImp::get(ExecState *exec, const Identifier &propertyName) const
{
ValueImp *imp = getDirect(propertyName);
if (imp)
return Value(imp);
Object proto = Object::dynamicCast(prototype());
// non-standard netscape extension
if (propertyName == specialPrototypePropertyName) {
if (!proto.isValid())
return Null();
else
return Value(proto);
}
if (!proto.isValid())
return Undefined();
return proto.get(exec,propertyName);
}
Value ObjectImp::getPropertyByIndex(ExecState *exec,
unsigned propertyName) const
{
return get(exec, Identifier::from(propertyName));
}
// ECMA 8.6.2.2
void ObjectImp::put(ExecState *exec, const Identifier &propertyName,
const Value &value, int attr)
{
assert(value.isValid());
// non-standard netscape extension
if (propertyName == specialPrototypePropertyName) {
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.imp(),attr);
}
// delme
void ObjectImp::putPropertyByIndex(ExecState *exec, unsigned propertyName,
const Value &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
{
if (_prop.get(propertyName))
return true;
// Look in the static hashtable of properties
if (findPropertyHashEntry(propertyName))
return true;
// non-standard netscape extension
if (propertyName == specialPrototypePropertyName)
return true;
// Look in the prototype
Object proto = Object::dynamicCast(prototype());
return proto.isValid() && proto.hasProperty(exec,propertyName);
}
bool ObjectImp::hasPropertyByIndex(ExecState *exec, unsigned propertyName) const
{
return hasProperty(exec, Identifier::from(propertyName));
}
// 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::deletePropertyByIndex(ExecState *exec, unsigned propertyName)
{
return deleteProperty(exec, Identifier::from(propertyName));
}
void ObjectImp::deleteAllProperties( ExecState * )
{
_prop.clear();
}
// ECMA 8.6.2.6
Value ObjectImp::defaultValue(ExecState *exec, Type hint) const
{
if (hint != StringType && hint != NumberType) {
/* Prefer String for Date objects */
if (_proto == exec->lexicalInterpreter()->builtinDatePrototype().imp())
hint = StringType;
else
hint = NumberType;
}
Value v;
if (hint == StringType)
v = get(exec,toStringPropertyName);
else
v = get(exec,valueOfPropertyName);
if (v.type() == ObjectType) {
Object o = Object(static_cast<ObjectImp*>(v.imp()));
if (o.implementsCall()) { // spec says "not primitive type" but ...
Object thisObj = Object(const_cast<ObjectImp*>(this));
Value 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.type() == ObjectType) {
Object o = Object(static_cast<ObjectImp*>(v.imp()));
if (o.implementsCall()) { // spec says "not primitive type" but ...
Object thisObj = Object(const_cast<ObjectImp*>(this));
Value 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;
}
}
}
Object err = Error::create(exec, TypeError, I18N_NOOP("No default value"));
exec->setException(err);
return err;
}
const HashEntry* ObjectImp::findPropertyHashEntry( const Identifier& propertyName ) const
{
const ClassInfo *info = classInfo();
while (info) {
if (info->propHashTable) {
const HashEntry *e = Lookup::findEntry(info->propHashTable, propertyName);
if (e)
return e;
}
info = info->parentClass;
}
return 0L;
}
bool ObjectImp::implementsConstruct() const
{
return false;
}
Object ObjectImp::construct(ExecState* /*exec*/, const List &/*args*/)
{
assert(false);
return Object(0);
}
bool ObjectImp::implementsCall() const
{
return false;
}
Value ObjectImp::call(ExecState* /*exec*/, Object &/*thisObj*/, const List &/*args*/)
{
assert(false);
return Object(0);
}
bool ObjectImp::implementsHasInstance() const
{
return false;
}
Boolean ObjectImp::hasInstance(ExecState* /*exec*/, const Value &/*value*/)
{
assert(false);
return Boolean(false);
}
ReferenceList ObjectImp::propList(ExecState *exec, bool recursive)
{
ReferenceList list;
if (_proto && _proto->dispatchType() == ObjectType && recursive)
list = static_cast<ObjectImp*>(_proto)->propList(exec,recursive);
_prop.addEnumerablesToReferenceList(list, Object(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->soffset && !(e->attr & DontEnum) )
list.append(Reference(this, &info->propHashTable->sbase[e->soffset]));
}
}
info = info->parentClass;
}
return list;
}
Value ObjectImp::internalValue() const
{
return Value(_internalValue);
}
void ObjectImp::setInternalValue(const Value &v)
{
_internalValue = v.imp();
}
void ObjectImp::setInternalValue(ValueImp *v)
{
v->setGcAllowed();
_internalValue = v;
}
Value 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
{
Value 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
{
Value prim = toPrimitive(exec,StringType);
if (exec->hadException()) // should be picked up soon in nodes.cpp
return "";
return prim.toString(exec);
}
Object ObjectImp::toObject(ExecState * /*exec*/) const
{
return Object(const_cast<ObjectImp*>(this));
}
void ObjectImp::putDirect(const Identifier &propertyName, ValueImp *value, int attr)
{
value->setGcAllowed();
_prop.put(propertyName, value, attr);
}
void ObjectImp::putDirect(const Identifier &propertyName, int value, int attr)
{
_prop.put(propertyName, NumberImp::create(value), attr);
}
void ObjectImp::setFunctionName(const Identifier &propertyName)
{
if (inherits(&InternalFunctionImp::info))
static_cast<InternalFunctionImp*>(this)->setName(propertyName);
}
// ------------------------------ 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;
Object Error::create(ExecState *exec, ErrorType errtype, const char *message,
int lineno, int sourceId)
{
#ifdef KJS_VERBOSE
// message could be 0L. Don't enable this on Solaris ;)
fprintf(stderr, "WARNING: KJS %s: %s\n", errorNames[errtype], message);
#endif
Object 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;
}
if (!message)
message = errorNames[errtype];
List args;
args.append(String(message));
Object err = Object::dynamicCast(cons.construct(exec,args));
if (lineno != -1)
err.put(exec, "line", Number(lineno));
if (sourceId != -1)
err.put(exec, "sourceId", Number(sourceId));
return err;
/*
#ifndef NDEBUG
const char *msg = err.get(messagePropertyName).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;
*/
}
KJS::Value
Value objects are act as wrappers ("smart pointers") around ValueImp objects and their descendents...
Definition: value.h:168
KJS::Value::type
Type type() const
Returns the type of value.
Definition: value.h:196
KJS::ClassInfo::parentClass
const ClassInfo * parentClass
Pointer to the class information of the base class.
Definition: object.h:68
KJS::InternalFunctionImp
Base class for all function objects.
Definition: function.h:41
KJS::HashTable::sbase
const char *const sbase
pointer to the string table.
Definition: lookup.h:96
KJS::Object::get
Value get(ExecState *exec, const Identifier &propertyName) const
Retrieves the specified property from the object.
Definition: object.h:664
KJS::Object::construct
Object construct(ExecState *exec, const List &args)
Creates a new object based on this object.
Definition: object.h:697
KJS::HashTable::size
int size
size is the total number of entries in the hashtable, including the null entries, i...
Definition: lookup.h:82
KJS::Number
Represents an primitive Number value.
Definition: value.h:368
KJS::List::append
void append(const Value &val)
Append an object to the end of the list.
Definition: list.h:66
KJS::Null
Represents an primitive Null value.
Definition: value.h:295
KJS::ExecState::lexicalInterpreter
Interpreter * lexicalInterpreter() const
Returns the interpreter associated with the current scope's global object.
Definition: interpreter.cpp:395
KJS::Object::hasProperty
bool hasProperty(ExecState *exec, const Identifier &propertyName) const
Checks to see whether the object (or any object in it's prototype chain) has a property with the spec...
Definition: object.h:679
KJS::Interpreter::builtinDatePrototype
Object builtinDatePrototype() const
Returns the builtin "Date.prototype" object.
Definition: interpreter.cpp:249
KJS::HashEntry
An entry in a hash table.
Definition: lookup.h:37
KJS::Error::create
static Object create(ExecState *exec, ErrorType errtype=GeneralError, const char *message=0, int lineno=-1, int sourceId=-1)
Factory method for error objects.
Definition: object.cpp:504
KJS::Value::toNumber
double toNumber(ExecState *exec) const
Performs the ToNumber type conversion operation on this value (ECMA 9.3)
Definition: value.h:222
KJS::Object
Represents an Object.
Definition: object.h:82
KJS::HashEntry::attr
unsigned char attr
attr is a set for flags (e.g.
Definition: lookup.h:49
KJS::HashEntry::soffset
unsigned short soffset
s is the offset to the string key (e.g.
Definition: lookup.h:41
KJS::Undefined
Represents an primitive Undefined value.
Definition: value.h:270
KJS::ClassInfo::className
const char * className
A string denoting the class name.
Definition: object.h:63
KJS::Interpreter::builtinError
Object builtinError() const
Returns the builtin "Error" object.
Definition: interpreter.cpp:214
KJS::Identifier::ascii
const char * ascii() const
Char * of the identifier's string.
Definition: identifier.h:71
KJS::Object::implementsCall
bool implementsCall() const
Whether or not the object implements the call() method.
Definition: object.h:700
KJS::UString
Unicode string class.
Definition: ustring.h:189
KJS::String
Represents an primitive String value.
Definition: value.h:341
KJS::Reference
Defines a Javascript reference.
Definition: reference.h:35
KJS::Object::dynamicCast
static Object dynamicCast(const Value &v)
Converts a Value into an Object.
Definition: object.cpp:46
KJS::Error::errorNames
static const char *const *const errorNames
Array of error names corresponding to ErrorType.
Definition: object.h:645
KJS::ValueImp
ValueImp is the base type for all primitives (Undefined, Null, Boolean, String, Number) and objects i...
Definition: value.h:79
KJS::Interpreter::builtinEvalError
Object builtinEvalError() const
The initial value of "Error" global property.
Definition: interpreter.cpp:264
KJS::Lookup::findEntry
static const HashEntry * findEntry(const struct HashTable *table, const Identifier &s)
Find an entry in the table, and return the entry This variant gives access to the other attributes of...
Definition: lookup.cpp:72
KJS::ClassInfo::propHashTable
const HashTable * propHashTable
Static hash-table of properties.
Definition: object.h:72
KJS::List::empty
static const List & empty()
Returns a pointer to a static instance of an empty list.
Definition: list.cpp:322
KJS::ReferenceList
A list of Reference objects.
Definition: reference_list.h:54
KJS::HashTable::entries
const HashEntry *const entries
pointer to the array of entries Mind that some entries in the array are null (0,0,0,0).
Definition: lookup.h:87
KJS::Object::put
void put(ExecState *exec, const Identifier &propertyName, const Value &value, int attr=None)
Sets the specified property.
Definition: object.h:670
KJS::List
Native list type.
Definition: list.h:48
KJS::Boolean
Represents an primitive Boolean value.
Definition: value.h:317
KJS::Value::toString
UString toString(ExecState *exec) const
Performs the ToString type conversion operation on this value (ECMA 9.8)
Definition: value.h:247
KJS::ClassInfo
Class Information.
Definition: object.h:59
KJS::Object::call
Value call(ExecState *exec, Object &thisObj, const List &args)
Calls this object as if it is a function.
Definition: object.cpp:54
KJS::Value::isValid
bool isValid() const
Returns whether or not this is a valid value.
Definition: value.h:182
KJS::ExecState
Represents the current state of script execution.
Definition: interpreter.h:439
KJS::Identifier
Represents an Identifier for a Javascript object.
Definition: identifier.h:32

KDE Home | KDE Accessibility Home | Description of Access Keys