summaryrefslogtreecommitdiffstats
path: root/kjs/object.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kjs/object.cpp')
-rw-r--r--kjs/object.cpp563
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;
+*/
+}
+