kjs Library API Documentation

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., 59 Temple Place - Suite 330,
00021  *  Boston, MA 02111-1307, 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     --depth;
00063     Object err = Error::create(exec, RangeError,
00064                                "Exceeded maximum function call depth.");
00065     exec->setException(err);
00066     return err;
00067   }
00068 #endif
00069 
00070   Value ret = static_cast<ObjectImp*>(rep)->call(exec,thisObj,args);
00071 
00072 #if KJS_MAX_STACK > 0
00073   --depth;
00074 #endif
00075 
00076   return ret;
00077 }
00078 
00079 // ------------------------------ ObjectImp ------------------------------------
00080 
00081 ObjectImp::ObjectImp(const Object &proto)
00082   : _proto(static_cast<ObjectImp*>(proto.imp())), _internalValue(0L)
00083 {
00084   //fprintf(stderr,"ObjectImp::ObjectImp %p\n",(void*)this);
00085 }
00086 
00087 ObjectImp::ObjectImp(ObjectImp *proto)
00088   : _proto(proto), _internalValue(0L)
00089 {
00090 }
00091 
00092 ObjectImp::ObjectImp()
00093 {
00094   //fprintf(stderr,"ObjectImp::ObjectImp %p\n",(void*)this);
00095   _proto = NullImp::staticNull;
00096   _internalValue = 0L;
00097 }
00098 
00099 ObjectImp::~ObjectImp()
00100 {
00101   //fprintf(stderr,"ObjectImp::~ObjectImp %p\n",(void*)this);
00102 }
00103 
00104 void ObjectImp::mark()
00105 {
00106   //fprintf(stderr,"ObjectImp::mark() %p\n",(void*)this);
00107   ValueImp::mark();
00108 
00109   if (_proto && !_proto->marked())
00110     _proto->mark();
00111 
00112   _prop.mark();
00113 
00114   if (_internalValue && !_internalValue->marked())
00115     _internalValue->mark();
00116 
00117   _scope.mark();
00118 }
00119 
00120 const ClassInfo *ObjectImp::classInfo() const
00121 {
00122   return 0;
00123 }
00124 
00125 bool ObjectImp::inherits(const ClassInfo *info) const
00126 {
00127   if (!info)
00128     return false;
00129 
00130   const ClassInfo *ci = classInfo();
00131   if (!ci)
00132     return false;
00133 
00134   while (ci && ci != info)
00135     ci = ci->parentClass;
00136 
00137   return (ci == info);
00138 }
00139 
00140 Type ObjectImp::type() const
00141 {
00142   return ObjectType;
00143 }
00144 
00145 Value ObjectImp::prototype() const
00146 {
00147   return Value(_proto);
00148 }
00149 
00150 void ObjectImp::setPrototype(const Value &proto)
00151 {
00152   _proto = proto.imp();
00153 }
00154 
00155 UString ObjectImp::className() const
00156 {
00157   const ClassInfo *ci = classInfo();
00158   if ( ci )
00159     return ci->className;
00160   return "Object";
00161 }
00162 
00163 Value ObjectImp::get(ExecState *exec, const Identifier &propertyName) const
00164 {
00165   ValueImp *imp = getDirect(propertyName);
00166   if (imp)
00167     return Value(imp);
00168 
00169   Object proto = Object::dynamicCast(prototype());
00170 
00171   // non-standard netscape extension
00172   if (propertyName == specialPrototypePropertyName) {
00173     if (!proto.isValid())
00174       return Null();
00175     else
00176       return Value(proto);
00177   }
00178 
00179   if (proto.isNull())
00180     return Undefined();
00181 
00182   return proto.get(exec,propertyName);
00183 }
00184 
00185 Value ObjectImp::getPropertyByIndex(ExecState *exec,
00186                     unsigned propertyName) const
00187 {
00188   return get(exec, Identifier::from(propertyName));
00189 }
00190 
00191 // ECMA 8.6.2.2
00192 void ObjectImp::put(ExecState *exec, const Identifier &propertyName,
00193                      const Value &value, int attr)
00194 {
00195   assert(!value.isNull());
00196 
00197   // non-standard netscape extension
00198   if (propertyName == specialPrototypePropertyName) {
00199     setPrototype(value);
00200     return;
00201   }
00202 
00203   /* TODO: check for write permissions directly w/o this call */
00204   /* Doesn't look very easy with the PropertyMap API - David */
00205   // putValue() is used for JS assignemnts. It passes no attribute.
00206   // Assume that a C++ implementation knows what it is doing
00207   // and let it override the canPut() check.
00208   if ((attr == None || attr == DontDelete) && !canPut(exec,propertyName)) {
00209 #ifdef KJS_VERBOSE
00210     fprintf( stderr, "WARNING: canPut %s said NO\n", propertyName.ascii() );
00211 #endif
00212     return;
00213   }
00214 
00215   _prop.put(propertyName,value.imp(),attr);
00216 }
00217 
00218 // delme
00219 void ObjectImp::putPropertyByIndex(ExecState *exec, unsigned propertyName,
00220                      const Value &value, int attr)
00221 {
00222   put(exec, Identifier::from(propertyName), value, attr);
00223 }
00224 
00225 // ECMA 8.6.2.3
00226 bool ObjectImp::canPut(ExecState *, const Identifier &propertyName) const
00227 {
00228   int attributes;
00229   ValueImp *v = _prop.get(propertyName, attributes);
00230   if (v)
00231     return!(attributes & ReadOnly);
00232 
00233   // Look in the static hashtable of properties
00234   const HashEntry* e = findPropertyHashEntry(propertyName);
00235   if (e)
00236     return !(e->attr & ReadOnly);
00237 
00238   // Don't look in the prototype here. We can always put an override
00239   // in the object, even if the prototype has a ReadOnly property.
00240   return true;
00241 }
00242 
00243 // ECMA 8.6.2.4
00244 bool ObjectImp::hasProperty(ExecState *exec, const Identifier &propertyName) const
00245 {
00246   if (_prop.get(propertyName))
00247     return true;
00248 
00249   // Look in the static hashtable of properties
00250   if (findPropertyHashEntry(propertyName))
00251       return true;
00252 
00253   // non-standard netscape extension
00254   if (propertyName == specialPrototypePropertyName)
00255     return true;
00256 
00257   // Look in the prototype
00258   Object proto = Object::dynamicCast(prototype());
00259   return !proto.isNull() && proto.hasProperty(exec,propertyName);
00260 }
00261 
00262 bool ObjectImp::hasPropertyByIndex(ExecState *exec, unsigned propertyName) const
00263 {
00264   return hasProperty(exec, Identifier::from(propertyName));
00265 }
00266 
00267 // ECMA 8.6.2.5
00268 bool ObjectImp::deleteProperty(ExecState * /*exec*/, const Identifier &propertyName)
00269 {
00270   int attributes;
00271   ValueImp *v = _prop.get(propertyName, attributes);
00272   if (v) {
00273     if ((attributes & DontDelete))
00274       return false;
00275     _prop.remove(propertyName);
00276     return true;
00277   }
00278 
00279   // Look in the static hashtable of properties
00280   const HashEntry* entry = findPropertyHashEntry(propertyName);
00281   if (entry && entry->attr & DontDelete)
00282     return false; // this builtin property can't be deleted
00283   return true;
00284 }
00285 
00286 bool ObjectImp::deletePropertyByIndex(ExecState *exec, unsigned propertyName)
00287 {
00288   return deleteProperty(exec, Identifier::from(propertyName));
00289 }
00290 
00291 void ObjectImp::deleteAllProperties( ExecState * )
00292 {
00293   _prop.clear();
00294 }
00295 
00296 // ECMA 8.6.2.6
00297 Value ObjectImp::defaultValue(ExecState *exec, Type hint) const
00298 {
00299   if (hint != StringType && hint != NumberType) {
00300     /* Prefer String for Date objects */
00301     if (_proto == exec->interpreter()->builtinDatePrototype().imp())
00302       hint = StringType;
00303     else
00304       hint = NumberType;
00305   }
00306 
00307   Value v;
00308   if (hint == StringType)
00309     v = get(exec,toStringPropertyName);
00310   else
00311     v = get(exec,valueOfPropertyName);
00312 
00313   if (v.type() == ObjectType) {
00314     Object o = Object(static_cast<ObjectImp*>(v.imp()));
00315     if (o.implementsCall()) { // spec says "not primitive type" but ...
00316       Object thisObj = Object(const_cast<ObjectImp*>(this));
00317       Value def = o.call(exec,thisObj,List::empty());
00318       Type defType = def.type();
00319       if (defType == UnspecifiedType || defType == UndefinedType ||
00320           defType == NullType || defType == BooleanType ||
00321           defType == StringType || defType == NumberType) {
00322         return def;
00323       }
00324     }
00325   }
00326 
00327   if (hint == StringType)
00328     v = get(exec,valueOfPropertyName);
00329   else
00330     v = get(exec,toStringPropertyName);
00331 
00332   if (v.type() == ObjectType) {
00333     Object o = Object(static_cast<ObjectImp*>(v.imp()));
00334     if (o.implementsCall()) { // spec says "not primitive type" but ...
00335       Object thisObj = Object(const_cast<ObjectImp*>(this));
00336       Value def = o.call(exec,thisObj,List::empty());
00337       Type defType = def.type();
00338       if (defType == UnspecifiedType || defType == UndefinedType ||
00339           defType == NullType || defType == BooleanType ||
00340           defType == StringType || defType == NumberType) {
00341         return def;
00342       }
00343     }
00344   }
00345 
00346   Object err = Error::create(exec, TypeError, I18N_NOOP("No default value"));
00347   exec->setException(err);
00348   return err;
00349 }
00350 
00351 const HashEntry* ObjectImp::findPropertyHashEntry( const Identifier& propertyName ) const
00352 {
00353   const ClassInfo *info = classInfo();
00354   while (info) {
00355     if (info->propHashTable) {
00356       const HashEntry *e = Lookup::findEntry(info->propHashTable, propertyName);
00357       if (e)
00358         return e;
00359     }
00360     info = info->parentClass;
00361   }
00362   return 0L;
00363 }
00364 
00365 bool ObjectImp::implementsConstruct() const
00366 {
00367   return false;
00368 }
00369 
00370 Object ObjectImp::construct(ExecState* /*exec*/, const List &/*args*/)
00371 {
00372   assert(false);
00373   return Object(0);
00374 }
00375 
00376 bool ObjectImp::implementsCall() const
00377 {
00378   return false;
00379 }
00380 
00381 Value ObjectImp::call(ExecState* /*exec*/, Object &/*thisObj*/, const List &/*args*/)
00382 {
00383   assert(false);
00384   return Object(0);
00385 }
00386 
00387 bool ObjectImp::implementsHasInstance() const
00388 {
00389   return false;
00390 }
00391 
00392 Boolean ObjectImp::hasInstance(ExecState* /*exec*/, const Value &/*value*/)
00393 {
00394   assert(false);
00395   return Boolean(false);
00396 }
00397 
00398 ReferenceList ObjectImp::propList(ExecState *exec, bool recursive)
00399 {
00400   ReferenceList list;
00401   if (_proto && _proto->dispatchType() == ObjectType && recursive)
00402     list = static_cast<ObjectImp*>(_proto)->propList(exec,recursive);
00403 
00404   _prop.addEnumerablesToReferenceList(list, Object(this));
00405 
00406   // Add properties from the static hashtable of properties
00407   const ClassInfo *info = classInfo();
00408   while (info) {
00409     if (info->propHashTable) {
00410       int size = info->propHashTable->size;
00411       const HashEntry *e = info->propHashTable->entries;
00412       for (int i = 0; i < size; ++i, ++e) {
00413         if ( e->soffset && !(e->attr & DontEnum) )
00414           list.append(Reference(this, &info->propHashTable->sbase[e->soffset])); 
00415       }
00416     }
00417     info = info->parentClass;
00418   }
00419 
00420   return list;
00421 }
00422 
00423 Value ObjectImp::internalValue() const
00424 {
00425   return Value(_internalValue);
00426 }
00427 
00428 void ObjectImp::setInternalValue(const Value &v)
00429 {
00430   _internalValue = v.imp();
00431 }
00432 
00433 void ObjectImp::setInternalValue(ValueImp *v)
00434 {
00435   v->setGcAllowed();
00436   _internalValue = v;
00437 }
00438 
00439 Value ObjectImp::toPrimitive(ExecState *exec, Type preferredType) const
00440 {
00441   return defaultValue(exec,preferredType);
00442 }
00443 
00444 bool ObjectImp::toBoolean(ExecState* /*exec*/) const
00445 {
00446   return true;
00447 }
00448 
00449 double ObjectImp::toNumber(ExecState *exec) const
00450 {
00451   Value prim = toPrimitive(exec,NumberType);
00452   if (exec->hadException()) // should be picked up soon in nodes.cpp
00453     return 0.0;
00454   return prim.toNumber(exec);
00455 }
00456 
00457 UString ObjectImp::toString(ExecState *exec) const
00458 {
00459   Value prim = toPrimitive(exec,StringType);
00460   if (exec->hadException()) // should be picked up soon in nodes.cpp
00461     return "";
00462   return prim.toString(exec);
00463 }
00464 
00465 Object ObjectImp::toObject(ExecState * /*exec*/) const
00466 {
00467   return Object(const_cast<ObjectImp*>(this));
00468 }
00469 
00470 void ObjectImp::putDirect(const Identifier &propertyName, ValueImp *value, int attr)
00471 {
00472   value->setGcAllowed();
00473   _prop.put(propertyName, value, attr);
00474 }
00475 
00476 void ObjectImp::putDirect(const Identifier &propertyName, int value, int attr)
00477 {
00478   _prop.put(propertyName, NumberImp::create(value), attr);
00479 }
00480 
00481 void ObjectImp::setFunctionName(const Identifier &propertyName)
00482 {
00483   if (inherits(&InternalFunctionImp::info))
00484     static_cast<InternalFunctionImp*>(this)->setName(propertyName);
00485 }
00486 
00487 // ------------------------------ Error ----------------------------------------
00488 
00489 const char * const errorNamesArr[] = {
00490   I18N_NOOP("Error"), // GeneralError
00491   I18N_NOOP("Evaluation error"), // EvalError
00492   I18N_NOOP("Range error"), // RangeError
00493   I18N_NOOP("Reference error"), // ReferenceError
00494   I18N_NOOP("Syntax error"), // SyntaxError
00495   I18N_NOOP("Type error"), // TypeError
00496   I18N_NOOP("URI error"), // URIError
00497 };
00498 
00499 const char * const * const Error::errorNames = errorNamesArr;
00500 
00501 Object Error::create(ExecState *exec, ErrorType errtype, const char *message,
00502                      int lineno, int sourceId)
00503 {
00504 #ifdef KJS_VERBOSE
00505   // message could be 0L. Don't enable this on Solaris ;)
00506   fprintf(stderr, "WARNING: KJS %s: %s\n", errorNames[errtype], message);
00507 #endif
00508 
00509   Object cons;
00510 
00511   switch (errtype) {
00512   case EvalError:
00513     cons = exec->interpreter()->builtinEvalError();
00514     break;
00515   case RangeError:
00516     cons = exec->interpreter()->builtinRangeError();
00517     break;
00518   case ReferenceError:
00519     cons = exec->interpreter()->builtinReferenceError();
00520     break;
00521   case SyntaxError:
00522     cons = exec->interpreter()->builtinSyntaxError();
00523     break;
00524   case TypeError:
00525     cons = exec->interpreter()->builtinTypeError();
00526     break;
00527   case URIError:
00528     cons = exec->interpreter()->builtinURIError();
00529     break;
00530   default:
00531     cons = exec->interpreter()->builtinError();
00532     break;
00533   }
00534 
00535   if (!message)
00536     message = errorNames[errtype];
00537   List args;
00538   args.append(String(message));
00539   Object err = Object::dynamicCast(cons.construct(exec,args));
00540 
00541   if (lineno != -1)
00542     err.put(exec, "line", Number(lineno));
00543   if (sourceId != -1)
00544     err.put(exec, "sourceId", Number(sourceId));
00545 
00546   return err;
00547 
00548 /*
00549 #ifndef NDEBUG
00550   const char *msg = err.get(messagePropertyName).toString().value().ascii();
00551   if (l >= 0)
00552       fprintf(stderr, "KJS: %s at line %d. %s\n", estr, l, msg);
00553   else
00554       fprintf(stderr, "KJS: %s. %s\n", estr, msg);
00555 #endif
00556 
00557   return err;
00558 */
00559 }
00560 
KDE Logo
This file is part of the documentation for kjs Library Version 3.4.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed May 4 07:12:49 2005 by doxygen 1.4.2 written by Dimitri van Heesch, © 1997-2003