object.cpp

00001 // -*- c-basic-offset: 2 -*-
00002 /*
00003  *  This file is part of the KDE libraries
00004  *  Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
00005  *  Copyright (C) 2001 Peter Kelly (pmk@post.com)
00006  *  Copyright (C) 2003 Apple Computer, Inc.
00007  *
00008  *  This library is free software; you can redistribute it and/or
00009  *  modify it under the terms of the GNU Library General Public
00010  *  License as published by the Free Software Foundation; either
00011  *  version 2 of the License, or (at your option) any later version.
00012  *
00013  *  This library is distributed in the hope that it will be useful,
00014  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016  *  Library General Public License for more details.
00017  *
00018  *  You should have received a copy of the GNU Library General Public License
00019  *  along with this library; see the file COPYING.LIB.  If not, write to
00020  *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00021  *  Boston, MA 02110-1301, USA.
00022  *
00023  */
00024 
00025 #include "value.h"
00026 #include "object.h"
00027 #include "types.h"
00028 #include "interpreter.h"
00029 #include "lookup.h"
00030 #include "reference_list.h"
00031 
00032 #include <assert.h>
00033 #include <math.h>
00034 #include <stdio.h>
00035 
00036 #include "internal.h"
00037 #include "collector.h"
00038 #include "operations.h"
00039 #include "error_object.h"
00040 #include "nodes.h"
00041 
00042 using namespace KJS;
00043 
00044 // ------------------------------ Object ---------------------------------------
00045 
00046 Object Object::dynamicCast(const Value &v)
00047 {
00048   if (!v.isValid() || v.type() != ObjectType)
00049     return Object(0);
00050 
00051   return Object(static_cast<ObjectImp*>(v.imp()));
00052 }
00053 
00054 Value Object::call(ExecState *exec, Object &thisObj, const List &args)
00055 {
00056 #if KJS_MAX_STACK > 0
00057   static int depth = 0; // sum of all concurrent interpreters
00058   if (++depth > KJS_MAX_STACK) {
00059 #ifndef NDEBUG
00060     fprintf(stderr, "Exceeded maximum function call depth\n");
00061 #endif
00062     int saveDepth = depth - 1;
00063     Object err = Error::create(exec, RangeError,
00064                                "Exceeded maximum function call depth.");
00065     depth = depth - 10; //Give some room for the debugger to operate,
00066                         //so if it tries to examine things we don't get here again
00067     exec->setException(err);
00068     depth         = saveDepth;
00069     return err;
00070   }
00071 #endif
00072 
00073   Value ret = static_cast<ObjectImp*>(rep)->call(exec,thisObj,args);
00074 
00075 #if KJS_MAX_STACK > 0
00076   --depth;
00077 #endif
00078 
00079   return ret;
00080 }
00081 
00082 // ------------------------------ ObjectImp ------------------------------------
00083 
00084 ObjectImp::ObjectImp(const Object &proto)
00085   : _proto(static_cast<ObjectImp*>(proto.imp())), _internalValue(0L)
00086 {
00087   //fprintf(stderr,"ObjectImp::ObjectImp %p\n",(void*)this);
00088 }
00089 
00090 ObjectImp::ObjectImp(ObjectImp *proto)
00091   : _proto(proto), _internalValue(0L)
00092 {
00093 }
00094 
00095 ObjectImp::ObjectImp()
00096 {
00097   //fprintf(stderr,"ObjectImp::ObjectImp %p\n",(void*)this);
00098   _proto = NullImp::staticNull;
00099   _internalValue = 0L;
00100 }
00101 
00102 ObjectImp::~ObjectImp()
00103 {
00104   //fprintf(stderr,"ObjectImp::~ObjectImp %p\n",(void*)this);
00105 }
00106 
00107 void ObjectImp::mark()
00108 {
00109   //fprintf(stderr,"ObjectImp::mark() %p\n",(void*)this);
00110   ValueImp::mark();
00111 
00112   if (_proto && !_proto->marked())
00113     _proto->mark();
00114 
00115   _prop.mark();
00116 
00117   if (_internalValue && !_internalValue->marked())
00118     _internalValue->mark();
00119 
00120   _scope.mark();
00121 }
00122 
00123 const ClassInfo *ObjectImp::classInfo() const
00124 {
00125   return 0;
00126 }
00127 
00128 bool ObjectImp::inherits(const ClassInfo *info) const
00129 {
00130   if (!info)
00131     return false;
00132 
00133   const ClassInfo *ci = classInfo();
00134   if (!ci)
00135     return false;
00136 
00137   while (ci && ci != info)
00138     ci = ci->parentClass;
00139 
00140   return (ci == info);
00141 }
00142 
00143 Type ObjectImp::type() const
00144 {
00145   return ObjectType;
00146 }
00147 
00148 Value ObjectImp::prototype() const
00149 {
00150   return Value(_proto);
00151 }
00152 
00153 void ObjectImp::setPrototype(const Value &proto)
00154 {
00155   _proto = proto.imp();
00156 }
00157 
00158 UString ObjectImp::className() const
00159 {
00160   const ClassInfo *ci = classInfo();
00161   if ( ci )
00162     return ci->className;
00163   return "Object";
00164 }
00165 
00166 Value ObjectImp::get(ExecState *exec, const Identifier &propertyName) const
00167 {
00168   ValueImp *imp = getDirect(propertyName);
00169   if (imp)
00170     return Value(imp);
00171 
00172   Object proto = Object::dynamicCast(prototype());
00173 
00174   // non-standard netscape extension
00175   if (propertyName == specialPrototypePropertyName) {
00176     if (!proto.isValid())
00177       return Null();
00178     else
00179       return Value(proto);
00180   }
00181 
00182   if (!proto.isValid())
00183     return Undefined();
00184 
00185   return proto.get(exec,propertyName);
00186 }
00187 
00188 Value ObjectImp::getPropertyByIndex(ExecState *exec,
00189                     unsigned propertyName) const
00190 {
00191   return get(exec, Identifier::from(propertyName));
00192 }
00193 
00194 // ECMA 8.6.2.2
00195 void ObjectImp::put(ExecState *exec, const Identifier &propertyName,
00196                      const Value &value, int attr)
00197 {
00198   assert(value.isValid());
00199 
00200   // non-standard netscape extension
00201   if (propertyName == specialPrototypePropertyName) {
00202     setPrototype(value);
00203     return;
00204   }
00205 
00206   /* TODO: check for write permissions directly w/o this call */
00207   /* Doesn't look very easy with the PropertyMap API - David */
00208   // putValue() is used for JS assignemnts. It passes no attribute.
00209   // Assume that a C++ implementation knows what it is doing
00210   // and let it override the canPut() check.
00211   if ((attr == None || attr == DontDelete) && !canPut(exec,propertyName)) {
00212 #ifdef KJS_VERBOSE
00213     fprintf( stderr, "WARNING: canPut %s said NO\n", propertyName.ascii() );
00214 #endif
00215     return;
00216   }
00217 
00218   _prop.put(propertyName,value.imp(),attr);
00219 }
00220 
00221 // delme
00222 void ObjectImp::putPropertyByIndex(ExecState *exec, unsigned propertyName,
00223                      const Value &value, int attr)
00224 {
00225   put(exec, Identifier::from(propertyName), value, attr);
00226 }
00227 
00228 // ECMA 8.6.2.3
00229 bool ObjectImp::canPut(ExecState *, const Identifier &propertyName) const
00230 {
00231   int attributes;
00232   ValueImp *v = _prop.get(propertyName, attributes);
00233   if (v)
00234     return!(attributes & ReadOnly);
00235 
00236   // Look in the static hashtable of properties
00237   const HashEntry* e = findPropertyHashEntry(propertyName);
00238   if (e)
00239     return !(e->attr & ReadOnly);
00240 
00241   // Don't look in the prototype here. We can always put an override
00242   // in the object, even if the prototype has a ReadOnly property.
00243   return true;
00244 }
00245 
00246 // ECMA 8.6.2.4
00247 bool ObjectImp::hasProperty(ExecState *exec, const Identifier &propertyName) const
00248 {
00249   if (_prop.get(propertyName))
00250     return true;
00251 
00252   // Look in the static hashtable of properties
00253   if (findPropertyHashEntry(propertyName))
00254       return true;
00255 
00256   // non-standard netscape extension
00257   if (propertyName == specialPrototypePropertyName)
00258     return true;
00259 
00260   // Look in the prototype
00261   Object proto = Object::dynamicCast(prototype());
00262   return proto.isValid() && proto.hasProperty(exec,propertyName);
00263 }
00264 
00265 bool ObjectImp::hasPropertyByIndex(ExecState *exec, unsigned propertyName) const
00266 {
00267   return hasProperty(exec, Identifier::from(propertyName));
00268 }
00269 
00270 // ECMA 8.6.2.5
00271 bool ObjectImp::deleteProperty(ExecState * /*exec*/, const Identifier &propertyName)
00272 {
00273   int attributes;
00274   ValueImp *v = _prop.get(propertyName, attributes);
00275   if (v) {
00276     if ((attributes & DontDelete))
00277       return false;
00278     _prop.remove(propertyName);
00279     return true;
00280   }
00281 
00282   // Look in the static hashtable of properties
00283   const HashEntry* entry = findPropertyHashEntry(propertyName);
00284   if (entry && entry->attr & DontDelete)
00285     return false; // this builtin property can't be deleted
00286   return true;
00287 }
00288 
00289 bool ObjectImp::deletePropertyByIndex(ExecState *exec, unsigned propertyName)
00290 {
00291   return deleteProperty(exec, Identifier::from(propertyName));
00292 }
00293 
00294 void ObjectImp::deleteAllProperties( ExecState * )
00295 {
00296   _prop.clear();
00297 }
00298 
00299 // ECMA 8.6.2.6
00300 Value ObjectImp::defaultValue(ExecState *exec, Type hint) const
00301 {
00302   if (hint != StringType && hint != NumberType) {
00303     /* Prefer String for Date objects */
00304     if (_proto == exec->lexicalInterpreter()->builtinDatePrototype().imp())
00305       hint = StringType;
00306     else
00307       hint = NumberType;
00308   }
00309 
00310   Value v;
00311   if (hint == StringType)
00312     v = get(exec,toStringPropertyName);
00313   else
00314     v = get(exec,valueOfPropertyName);
00315 
00316   if (v.type() == ObjectType) {
00317     Object o = Object(static_cast<ObjectImp*>(v.imp()));
00318     if (o.implementsCall()) { // spec says "not primitive type" but ...
00319       Object thisObj = Object(const_cast<ObjectImp*>(this));
00320       Value def = o.call(exec,thisObj,List::empty());
00321       Type defType = def.type();
00322       if (defType == UnspecifiedType || defType == UndefinedType ||
00323           defType == NullType || defType == BooleanType ||
00324           defType == StringType || defType == NumberType) {
00325         return def;
00326       }
00327     }
00328   }
00329 
00330   if (hint == StringType)
00331     v = get(exec,valueOfPropertyName);
00332   else
00333     v = get(exec,toStringPropertyName);
00334 
00335   if (v.type() == ObjectType) {
00336     Object o = Object(static_cast<ObjectImp*>(v.imp()));
00337     if (o.implementsCall()) { // spec says "not primitive type" but ...
00338       Object thisObj = Object(const_cast<ObjectImp*>(this));
00339       Value def = o.call(exec,thisObj,List::empty());
00340       Type defType = def.type();
00341       if (defType == UnspecifiedType || defType == UndefinedType ||
00342           defType == NullType || defType == BooleanType ||
00343           defType == StringType || defType == NumberType) {
00344         return def;
00345       }
00346     }
00347   }
00348 
00349   Object err = Error::create(exec, TypeError, I18N_NOOP("No default value"));
00350   exec->setException(err);
00351   return err;
00352 }
00353 
00354 const HashEntry* ObjectImp::findPropertyHashEntry( const Identifier& propertyName ) const
00355 {
00356   const ClassInfo *info = classInfo();
00357   while (info) {
00358     if (info->propHashTable) {
00359       const HashEntry *e = Lookup::findEntry(info->propHashTable, propertyName);
00360       if (e)
00361         return e;
00362     }
00363     info = info->parentClass;
00364   }
00365   return 0L;
00366 }
00367 
00368 bool ObjectImp::implementsConstruct() const
00369 {
00370   return false;
00371 }
00372 
00373 Object ObjectImp::construct(ExecState* /*exec*/, const List &/*args*/)
00374 {
00375   assert(false);
00376   return Object(0);
00377 }
00378 
00379 bool ObjectImp::implementsCall() const
00380 {
00381   return false;
00382 }
00383 
00384 Value ObjectImp::call(ExecState* /*exec*/, Object &/*thisObj*/, const List &/*args*/)
00385 {
00386   assert(false);
00387   return Object(0);
00388 }
00389 
00390 bool ObjectImp::implementsHasInstance() const
00391 {
00392   return false;
00393 }
00394 
00395 Boolean ObjectImp::hasInstance(ExecState* /*exec*/, const Value &/*value*/)
00396 {
00397   assert(false);
00398   return Boolean(false);
00399 }
00400 
00401 ReferenceList ObjectImp::propList(ExecState *exec, bool recursive)
00402 {
00403   ReferenceList list;
00404   if (_proto && _proto->dispatchType() == ObjectType && recursive)
00405     list = static_cast<ObjectImp*>(_proto)->propList(exec,recursive);
00406 
00407   _prop.addEnumerablesToReferenceList(list, Object(this));
00408 
00409   // Add properties from the static hashtable of properties
00410   const ClassInfo *info = classInfo();
00411   while (info) {
00412     if (info->propHashTable) {
00413       int size = info->propHashTable->size;
00414       const HashEntry *e = info->propHashTable->entries;
00415       for (int i = 0; i < size; ++i, ++e) {
00416         if ( e->soffset && !(e->attr & DontEnum) )
00417           list.append(Reference(this, &info->propHashTable->sbase[e->soffset])); 
00418       }
00419     }
00420     info = info->parentClass;
00421   }
00422 
00423   return list;
00424 }
00425 
00426 Value ObjectImp::internalValue() const
00427 {
00428   return Value(_internalValue);
00429 }
00430 
00431 void ObjectImp::setInternalValue(const Value &v)
00432 {
00433   _internalValue = v.imp();
00434 }
00435 
00436 void ObjectImp::setInternalValue(ValueImp *v)
00437 {
00438   v->setGcAllowed();
00439   _internalValue = v;
00440 }
00441 
00442 Value ObjectImp::toPrimitive(ExecState *exec, Type preferredType) const
00443 {
00444   return defaultValue(exec,preferredType);
00445 }
00446 
00447 bool ObjectImp::toBoolean(ExecState* /*exec*/) const
00448 {
00449   return true;
00450 }
00451 
00452 double ObjectImp::toNumber(ExecState *exec) const
00453 {
00454   Value prim = toPrimitive(exec,NumberType);
00455   if (exec->hadException()) // should be picked up soon in nodes.cpp
00456     return 0.0;
00457   return prim.toNumber(exec);
00458 }
00459 
00460 UString ObjectImp::toString(ExecState *exec) const
00461 {
00462   Value prim = toPrimitive(exec,StringType);
00463   if (exec->hadException()) // should be picked up soon in nodes.cpp
00464     return "";
00465   return prim.toString(exec);
00466 }
00467 
00468 Object ObjectImp::toObject(ExecState * /*exec*/) const
00469 {
00470   return Object(const_cast<ObjectImp*>(this));
00471 }
00472 
00473 void ObjectImp::putDirect(const Identifier &propertyName, ValueImp *value, int attr)
00474 {
00475   value->setGcAllowed();
00476   _prop.put(propertyName, value, attr);
00477 }
00478 
00479 void ObjectImp::putDirect(const Identifier &propertyName, int value, int attr)
00480 {
00481   _prop.put(propertyName, NumberImp::create(value), attr);
00482 }
00483 
00484 void ObjectImp::setFunctionName(const Identifier &propertyName)
00485 {
00486   if (inherits(&InternalFunctionImp::info))
00487     static_cast<InternalFunctionImp*>(this)->setName(propertyName);
00488 }
00489 
00490 // ------------------------------ Error ----------------------------------------
00491 
00492 const char * const errorNamesArr[] = {
00493   I18N_NOOP("Error"), // GeneralError
00494   I18N_NOOP("Evaluation error"), // EvalError
00495   I18N_NOOP("Range error"), // RangeError
00496   I18N_NOOP("Reference error"), // ReferenceError
00497   I18N_NOOP("Syntax error"), // SyntaxError
00498   I18N_NOOP("Type error"), // TypeError
00499   I18N_NOOP("URI error"), // URIError
00500 };
00501 
00502 const char * const * const Error::errorNames = errorNamesArr;
00503 
00504 Object Error::create(ExecState *exec, ErrorType errtype, const char *message,
00505                      int lineno, int sourceId)
00506 {
00507 #ifdef KJS_VERBOSE
00508   // message could be 0L. Don't enable this on Solaris ;)
00509   fprintf(stderr, "WARNING: KJS %s: %s\n", errorNames[errtype], message);
00510 #endif
00511 
00512   Object cons;
00513 
00514   switch (errtype) {
00515   case EvalError:
00516     cons = exec->lexicalInterpreter()->builtinEvalError();
00517     break;
00518   case RangeError:
00519     cons = exec->lexicalInterpreter()->builtinRangeError();
00520     break;
00521   case ReferenceError:
00522     cons = exec->lexicalInterpreter()->builtinReferenceError();
00523     break;
00524   case SyntaxError:
00525     cons = exec->lexicalInterpreter()->builtinSyntaxError();
00526     break;
00527   case TypeError:
00528     cons = exec->lexicalInterpreter()->builtinTypeError();
00529     break;
00530   case URIError:
00531     cons = exec->lexicalInterpreter()->builtinURIError();
00532     break;
00533   default:
00534     cons = exec->lexicalInterpreter()->builtinError();
00535     break;
00536   }
00537 
00538   if (!message)
00539     message = errorNames[errtype];
00540   List args;
00541   args.append(String(message));
00542   Object err = Object::dynamicCast(cons.construct(exec,args));
00543 
00544   if (lineno != -1)
00545     err.put(exec, "line", Number(lineno));
00546   if (sourceId != -1)
00547     err.put(exec, "sourceId", Number(sourceId));
00548 
00549   return err;
00550 
00551 /*
00552 #ifndef NDEBUG
00553   const char *msg = err.get(messagePropertyName).toString().value().ascii();
00554   if (l >= 0)
00555       fprintf(stderr, "KJS: %s at line %d. %s\n", estr, l, msg);
00556   else
00557       fprintf(stderr, "KJS: %s. %s\n", estr, msg);
00558 #endif
00559 
00560   return err;
00561 */
00562 }
00563 
KDE Home | KDE Accessibility Home | Description of Access Keys