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