// -*- 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 #include #include #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(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(rep)->call(exec,thisObj,args); #if KJS_MAX_STACK > 0 --depth; #endif return ret; } // ------------------------------ ObjectImp ------------------------------------ ObjectImp::ObjectImp(const Object &proto) : _proto(static_cast(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 = tqfindPropertyHashEntry(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 (tqfindPropertyHashEntry(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 = tqfindPropertyHashEntry(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(v.imp())); if (o.implementsCall()) { // spec says "not primitive type" but ... Object thisObj = Object(const_cast(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(v.imp())); if (o.implementsCall()) { // spec says "not primitive type" but ... Object thisObj = Object(const_cast(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::tqfindPropertyHashEntry( 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(_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(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(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; */ }