diff options
Diffstat (limited to 'kjs/object.cpp')
-rw-r--r-- | kjs/object.cpp | 563 |
1 files changed, 563 insertions, 0 deletions
diff --git a/kjs/object.cpp b/kjs/object.cpp new file mode 100644 index 000000000..fba1e0257 --- /dev/null +++ b/kjs/object.cpp @@ -0,0 +1,563 @@ +// -*- 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])); /// ######### check for duplicates with the propertymap + } + } + 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; +*/ +} + |