fix logic
[personal-kdelibs.git] / kjs / object.cpp
blobb77b9206d4de8ba240fca1707cb4040572a1ec50
1 // -*- c-basic-offset: 2 -*-
2 /*
3 * This file is part of the KDE libraries
4 * Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
5 * Copyright (C) 2001 Peter Kelly (pmk@post.com)
6 * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc.
7 * Copyright (C) 2007 Eric Seidel (eric@webkit.org)
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
19 * You should have received a copy of the GNU Library General Public License
20 * along with this library; see the file COPYING.LIB. If not, write to
21 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
26 #include "object.h"
27 #include <config.h>
29 #include "error_object.h"
30 #include "lookup.h"
31 #include "nodes.h"
32 #include "operations.h"
33 #include "PropertyNameArray.h"
34 #include <math.h>
36 // maximum global call stack size. Protects against accidental or
37 // malicious infinite recursions. Define to -1 if you want no limit.
38 #if PLATFORM(DARWIN)
39 // Given OS X stack sizes we run out of stack at about 350 levels.
40 // If we improve our stack usage, we can bump this number.
41 #define KJS_MAX_STACK 100
42 #else
43 #define KJS_MAX_STACK 900
44 #endif
46 #define JAVASCRIPT_CALL_TRACING 0
47 #define JAVASCRIPT_MARK_TRACING 0
49 #if JAVASCRIPT_CALL_TRACING
50 static bool _traceJavaScript = false;
52 extern "C" {
53 void setTraceJavaScript(bool f)
55 _traceJavaScript = f;
58 static bool traceJavaScript()
60 return _traceJavaScript;
63 #endif
65 namespace KJS {
67 // ------------------------------ Object ---------------------------------------
69 JSValue *JSObject::call(ExecState *exec, JSObject *thisObj, const List &args)
71 assert(implementsCall());
73 #if KJS_MAX_STACK > 0
74 static int depth = 0; // sum of all concurrent interpreters
76 #if JAVASCRIPT_CALL_TRACING
77 static bool tracing = false;
78 if (traceJavaScript() && !tracing) {
79 tracing = true;
80 for (int i = 0; i < depth; i++)
81 putchar (' ');
82 printf ("*** calling: %s\n", toString(exec).ascii());
83 for (int j = 0; j < args.size(); j++) {
84 for (int i = 0; i < depth; i++)
85 putchar (' ');
86 printf ("*** arg[%d] = %s\n", j, args[j]->toString(exec).ascii());
88 tracing = false;
90 #endif
92 if (++depth > KJS_MAX_STACK) {
93 depth -= 11; //Give the debugger some room..
94 return throwError(exec, RangeError, "Maximum call stack size exceeded.");
95 depth += 10; //Put it back..
97 #endif
99 JSValue *ret = callAsFunction(exec,thisObj,args);
100 assert(ret);
102 #if KJS_MAX_STACK > 0
103 --depth;
104 #endif
106 #if JAVASCRIPT_CALL_TRACING
107 if (traceJavaScript() && !tracing) {
108 tracing = true;
109 for (int i = 0; i < depth; i++)
110 putchar (' ');
111 printf ("*** returning: %s\n", ret->toString(exec).ascii());
112 tracing = false;
114 #endif
116 return ret;
119 // ------------------------------ JSObject ------------------------------------
121 void JSObject::mark()
123 JSCell::mark();
125 #if JAVASCRIPT_MARK_TRACING
126 static int markStackDepth = 0;
127 markStackDepth++;
128 for (int i = 0; i < markStackDepth; i++)
129 putchar('-');
131 printf("%s (%p)\n", className().UTF8String().c_str(), this);
132 #endif
134 JSValue *proto = _proto;
135 if (!proto->marked())
136 proto->mark();
138 _prop.mark();
140 #if JAVASCRIPT_MARK_TRACING
141 markStackDepth--;
142 #endif
145 JSType JSObject::type() const
147 return ObjectType;
150 const ClassInfo *JSObject::classInfo() const
152 return 0;
155 UString JSObject::className() const
157 const ClassInfo *ci = classInfo();
158 return ci ? ci->className : "Object";
161 JSValue *JSObject::get(ExecState *exec, const Identifier &propertyName) const
163 PropertySlot slot;
165 if (const_cast<JSObject *>(this)->getPropertySlot(exec, propertyName, slot))
166 return slot.getValue(exec, const_cast<JSObject *>(this), propertyName);
168 return jsUndefined();
171 JSValue *JSObject::get(ExecState *exec, unsigned propertyName) const
173 PropertySlot slot;
174 if (const_cast<JSObject *>(this)->getPropertySlot(exec, propertyName, slot))
175 return slot.getValue(exec, const_cast<JSObject *>(this), propertyName);
177 return jsUndefined();
180 bool JSObject::getPropertySlot(ExecState *exec, unsigned propertyName, PropertySlot& slot)
182 JSObject *imp = this;
184 while (true) {
185 if (imp->getOwnPropertySlot(exec, propertyName, slot))
186 return true;
188 JSValue *proto = imp->_proto;
189 if (!proto->isObject())
190 break;
192 imp = static_cast<JSObject *>(proto);
195 return false;
198 bool JSObject::getOwnPropertySlot(ExecState *exec, unsigned propertyName, PropertySlot& slot)
200 return getOwnPropertySlot(exec, Identifier::from(propertyName), slot);
203 // Ideally, we would like to inline this, since it's ultra-hot, but with the large VM
204 // loop, it seems like the code side gets the g++-4.3.x inliner in the paranoid mode, so not only
205 // does it not inline this, but it also doesn't inline setValueSlot() and hasGetterSetterProperties() (!!!).
206 bool JSObject::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
208 if (JSValue **location = getDirectLocation(propertyName)) {
209 if (_prop.hasGetterSetterProperties() && location[0]->type() == GetterSetterType)
210 fillGetterPropertySlot(slot, location);
211 else
212 slot.setValueSlot(this, location);
213 return true;
216 // non-standard Netscape extension
217 if (propertyName == exec->propertyNames().underscoreProto) {
218 slot.setValueSlot(this, &_proto);
219 return true;
222 return false;
226 static void throwSetterError(ExecState *exec)
228 throwError(exec, TypeError, "setting a property that has only a getter");
231 // ECMA 8.6.2.2
232 void JSObject::put(ExecState *exec, const Identifier &propertyName, JSValue *value, int attr)
234 assert(value);
236 // non-standard netscape extension
237 if (propertyName == exec->propertyNames().underscoreProto) {
238 JSObject* proto = value->getObject();
239 while (proto) {
240 if (proto == this) {
241 throwError(exec, GeneralError, "cyclic __proto__ value");
242 return;
244 proto = proto->prototype() ? proto->prototype()->getObject() : 0;
247 setPrototype(value);
248 return;
251 // putValue() is used for JS assignemnts. It passes no attribute.
252 // Assume that a C++ implementation knows what it is doing
253 // and don't spend time doing a read-only check for it.
254 bool checkRO = (attr == None || attr == DontDelete);
256 if (checkRO) {
257 // Check for static properties that are ReadOnly; the property map will check the dynamic properties.
258 // We don't have to worry about setters being read-only as they can't be added with such an attribute.
259 const HashEntry* entry = findPropertyHashEntry(propertyName);
260 if (entry && entry->attr & ReadOnly) {
261 #ifdef KJS_VERBOSE
262 fprintf( stderr, "WARNING: static property %s is ReadOnly\n", propertyName.ascii() );
263 #endif
264 return;
268 // Check if there are any setters or getters in the prototype chain
269 JSObject *obj = this;
270 bool hasGettersOrSetters = false;
271 while (true) {
272 if (obj->_prop.hasGetterSetterProperties()) {
273 hasGettersOrSetters = true;
274 break;
277 if (!obj->_proto->isObject())
278 break;
280 obj = static_cast<JSObject *>(obj->_proto);
283 if (hasGettersOrSetters) {
284 obj = this;
285 while (true) {
286 unsigned attributes;
287 if (JSValue *gs = obj->_prop.get(propertyName, attributes)) {
288 if (attributes & GetterSetter) {
289 JSObject *setterFunc = static_cast<GetterSetterImp *>(gs)->getSetter();
291 if (!setterFunc) {
292 throwSetterError(exec);
293 return;
296 List args;
297 args.append(value);
299 setterFunc->call(exec, this, args);
300 return;
301 } else {
302 // If there's an existing property on the object or one of its
303 // prototype it should be replaced, so we just break here.
304 break;
308 if (!obj->_proto->isObject())
309 break;
311 obj = static_cast<JSObject *>(obj->_proto);
315 _prop.put(propertyName,value,attr,checkRO);
318 void JSObject::put(ExecState *exec, unsigned propertyName,
319 JSValue *value, int attr)
321 put(exec, Identifier::from(propertyName), value, attr);
324 // ECMA 8.6.2.3
325 bool JSObject::canPut(ExecState *, const Identifier &propertyName) const
327 unsigned attributes;
329 // Don't look in the prototype here. We can always put an override
330 // in the object, even if the prototype has a ReadOnly property.
332 if (!getPropertyAttributes(propertyName, attributes))
333 return true;
334 else
335 return !(attributes & ReadOnly);
338 // ECMA 8.6.2.4
339 bool JSObject::hasProperty(ExecState *exec, const Identifier &propertyName) const
341 PropertySlot slot;
342 return const_cast<JSObject *>(this)->getPropertySlot(exec, propertyName, slot);
345 bool JSObject::hasProperty(ExecState *exec, unsigned propertyName) const
347 PropertySlot slot;
348 return const_cast<JSObject *>(this)->getPropertySlot(exec, propertyName, slot);
351 // ECMA 8.6.2.5
352 bool JSObject::deleteProperty(ExecState * /*exec*/, const Identifier &propertyName)
354 unsigned attributes;
355 JSValue *v = _prop.get(propertyName, attributes);
356 if (v) {
357 if ((attributes & DontDelete))
358 return false;
359 _prop.remove(propertyName);
360 if (attributes & GetterSetter)
361 _prop.setHasGetterSetterProperties(_prop.containsGettersOrSetters());
362 return true;
365 // Look in the static hashtable of properties
366 const HashEntry* entry = findPropertyHashEntry(propertyName);
367 if (entry && entry->attr & DontDelete)
368 return false; // this builtin property can't be deleted
369 return true;
372 bool JSObject::deleteProperty(ExecState *exec, unsigned propertyName)
374 return deleteProperty(exec, Identifier::from(propertyName));
377 static ALWAYS_INLINE JSValue *tryGetAndCallProperty(ExecState *exec, const JSObject *object, const Identifier &propertyName) {
378 JSValue *v = object->get(exec, propertyName);
379 if (v->isObject()) {
380 JSObject *o = static_cast<JSObject*>(v);
381 if (o->implementsCall()) { // spec says "not primitive type" but ...
382 JSObject *thisObj = const_cast<JSObject*>(object);
383 JSValue *def = o->call(exec, thisObj, List::empty());
384 JSType defType = def->type();
385 ASSERT(defType != GetterSetterType);
386 if (defType != ObjectType)
387 return def;
390 return NULL;
393 bool JSObject::getPrimitiveNumber(ExecState* exec, double& number, JSValue*& result)
395 result = defaultValue(exec, NumberType);
396 number = result->toNumber(exec);
397 return !result->isString();
400 // ECMA 8.6.2.6
401 JSValue *JSObject::defaultValue(ExecState *exec, JSType hint) const
403 const Identifier* firstPropertyName;
404 const Identifier* secondPropertyName;
405 /* Prefer String for Date objects */
406 if ((hint == StringType) || ((hint != NumberType) && (_proto == exec->lexicalInterpreter()->builtinDatePrototype()))) {
407 firstPropertyName = &exec->propertyNames().toString;
408 secondPropertyName = &exec->propertyNames().valueOf;
409 } else {
410 firstPropertyName = &exec->propertyNames().valueOf;
411 secondPropertyName = &exec->propertyNames().toString;
414 JSValue *v;
415 if ((v = tryGetAndCallProperty(exec, this, *firstPropertyName)))
416 return v;
417 if ((v = tryGetAndCallProperty(exec, this, *secondPropertyName)))
418 return v;
420 if (exec->hadException())
421 return exec->exception();
423 return throwError(exec, TypeError, "No default value");
426 const HashEntry* JSObject::findPropertyHashEntry(const Identifier& propertyName) const
428 for (const ClassInfo *info = classInfo(); info; info = info->parentClass) {
429 if (const HashTable *propHashTable = info->propHashTable) {
430 if (const HashEntry *e = Lookup::findEntry(propHashTable, propertyName))
431 return e;
434 return 0;
437 void JSObject::defineGetter(ExecState*, const Identifier& propertyName, JSObject* getterFunc)
439 JSValue *o = getDirect(propertyName);
440 GetterSetterImp *gs;
442 if (o && o->type() == GetterSetterType) {
443 gs = static_cast<GetterSetterImp *>(o);
444 } else {
445 gs = new GetterSetterImp;
446 putDirect(propertyName, gs, GetterSetter);
449 _prop.setHasGetterSetterProperties(true);
450 gs->setGetter(getterFunc);
453 void JSObject::defineSetter(ExecState*, const Identifier& propertyName, JSObject* setterFunc)
455 JSValue *o = getDirect(propertyName);
456 GetterSetterImp *gs;
458 if (o && o->type() == GetterSetterType) {
459 gs = static_cast<GetterSetterImp *>(o);
460 } else {
461 gs = new GetterSetterImp;
462 putDirect(propertyName, gs, GetterSetter);
465 _prop.setHasGetterSetterProperties(true);
466 gs->setSetter(setterFunc);
469 bool JSObject::implementsConstruct() const
471 return false;
474 JSObject* JSObject::construct(ExecState*, const List& /*args*/)
476 assert(false);
477 return NULL;
480 JSObject* JSObject::construct(ExecState* exec, const List& args, const Identifier& /*functionName*/, const UString& /*sourceURL*/, int /*lineNumber*/)
482 return construct(exec, args);
485 bool JSObject::isFunctionType() const
487 return implementsCall();
490 JSValue *JSObject::callAsFunction(ExecState * /*exec*/, JSObject * /*thisObj*/, const List &/*args*/)
492 assert(false);
493 return NULL;
496 bool JSObject::implementsHasInstance() const
498 return false;
501 bool JSObject::hasInstance(ExecState* exec, JSValue* value)
503 JSValue* proto = get(exec, exec->propertyNames().prototype);
504 if (!proto->isObject()) {
505 throwError(exec, TypeError, "instanceof called on an object with an invalid prototype property.");
506 return false;
509 if (!value->isObject())
510 return false;
512 JSObject* o = static_cast<JSObject*>(value);
513 while ((o = o->prototype()->getObject())) {
514 if (o == proto)
515 return true;
517 return false;
520 bool JSObject::propertyIsEnumerable(ExecState*, const Identifier& propertyName) const
522 unsigned attributes;
524 if (!getPropertyAttributes(propertyName, attributes))
525 return false;
526 else
527 return !(attributes & DontEnum);
530 bool JSObject::getPropertyAttributes(const Identifier& propertyName, unsigned& attributes) const
532 if (_prop.get(propertyName, attributes))
533 return true;
535 // Look in the static hashtable of properties
536 const HashEntry* e = findPropertyHashEntry(propertyName);
537 if (e) {
538 attributes = e->attr;
539 return true;
542 return false;
545 void JSObject::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
547 _prop.getEnumerablePropertyNames(propertyNames);
549 // Add properties from the static hashtable of properties
550 const ClassInfo *info = classInfo();
551 while (info) {
552 if (info->propHashTable) {
553 int size = info->propHashTable->size;
554 const HashEntry *e = info->propHashTable->entries;
555 for (int i = 0; i < size; ++i, ++e) {
556 if (e->s && !(e->attr & DontEnum))
557 propertyNames.add(e->s);
560 info = info->parentClass;
564 bool JSObject::toBoolean(ExecState * /*exec*/) const
566 return true;
569 double JSObject::toNumber(ExecState *exec) const
571 JSValue *prim = toPrimitive(exec,NumberType);
572 if (exec->hadException()) // should be picked up soon in nodes.cpp
573 return 0.0;
574 return prim->toNumber(exec);
577 UString JSObject::toString(ExecState *exec) const
579 JSValue *prim = toPrimitive(exec,StringType);
580 if (exec->hadException()) // should be picked up soon in nodes.cpp
581 return UString(UString::empty);
582 return prim->toString(exec);
585 JSObject *JSObject::toObject(ExecState * /*exec*/) const
587 return const_cast<JSObject*>(this);
590 void JSObject::putDirect(const Identifier &propertyName, int value, int attr)
592 _prop.put(propertyName, jsNumber(value), attr);
595 void JSObject::removeDirect(const Identifier &propertyName)
597 _prop.remove(propertyName);
600 void JSObject::putDirectFunction(InternalFunctionImp* func, int attr)
602 putDirect(func->functionName(), func, attr);
605 void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSValue **location)
607 GetterSetterImp *gs = static_cast<GetterSetterImp *>(*location);
608 JSObject *getterFunc = gs->getGetter();
609 if (getterFunc)
610 slot.setGetterSlot(this, getterFunc);
611 else
612 slot.setUndefined(this);
615 // ------------------------------ Error ----------------------------------------
617 const char * const errorNamesArr[] = {
618 I18N_NOOP("Error"), // GeneralError
619 I18N_NOOP("Evaluation error"), // EvalError
620 I18N_NOOP("Range error"), // RangeError
621 I18N_NOOP("Reference error"), // ReferenceError
622 I18N_NOOP("Syntax error"), // SyntaxError
623 I18N_NOOP("Type error"), // TypeError
624 I18N_NOOP("URI error"), // URIError
627 const char * const * const Error::errorNames = errorNamesArr;
629 JSObject *Error::create(ExecState *exec, ErrorType errtype, const UString &message,
630 int lineno, int sourceId, const UString &sourceURL)
632 #ifdef KJS_VERBOSE
633 // message could be 0L. Don't enable this on Solaris ;)
634 fprintf(stderr, "WARNING: KJS %s: %s\n", errorNamesArr[errtype], message.ascii());
635 #endif
638 Interpreter* interp = exec->lexicalInterpreter();
639 JSObject *cons;
640 switch (errtype) {
641 case EvalError:
642 cons = interp->builtinEvalError();
643 break;
644 case RangeError:
645 cons = interp->builtinRangeError();
646 break;
647 case ReferenceError:
648 cons = interp->builtinReferenceError();
649 break;
650 case SyntaxError:
651 cons = interp->builtinSyntaxError();
652 break;
653 case TypeError:
654 cons = interp->builtinTypeError();
655 break;
656 case URIError:
657 cons = interp->builtinURIError();
658 break;
659 default:
660 cons = interp->builtinError();
661 break;
664 List args;
665 if (message.isEmpty())
666 args.append(jsString(errorNames[errtype]));
667 else
668 args.append(jsString(message));
669 JSObject *err = static_cast<JSObject *>(cons->construct(exec,args));
671 if (lineno != -1)
672 err->put(exec, "line", jsNumber(lineno));
673 if (sourceId != -1)
674 err->put(exec, "sourceId", jsNumber(sourceId));
676 if(!sourceURL.isNull())
677 err->put(exec, "sourceURL", jsString(sourceURL));
679 return err;
682 #ifndef NDEBUG
683 const char *msg = err->get(messagePropertyName)->toString().value().ascii();
684 if (l >= 0)
685 fprintf(stderr, "KJS: %s at line %d. %s\n", estr, l, msg);
686 else
687 fprintf(stderr, "KJS: %s. %s\n", estr, msg);
688 #endif
690 return err;
694 JSObject *Error::create(ExecState *exec, ErrorType type, const char *message)
696 return create(exec, type, message, -1, -1, NULL);
699 JSObject *throwError(ExecState *exec, ErrorType type)
701 JSObject *error = Error::create(exec, type, UString(), -1, -1, NULL);
702 exec->setException(error);
703 return error;
706 JSObject *throwError(ExecState *exec, ErrorType type, const UString &message)
708 JSObject *error = Error::create(exec, type, message, -1, -1, NULL);
709 exec->setException(error);
710 return error;
713 JSObject *throwError(ExecState *exec, ErrorType type, const char *message)
715 JSObject *error = Error::create(exec, type, message, -1, -1, NULL);
716 exec->setException(error);
717 return error;
720 JSObject *throwError(ExecState *exec, ErrorType type, const UString &message, int line, int sourceId, const UString &sourceURL)
722 JSObject *error = Error::create(exec, type, message, line, sourceId, sourceURL);
723 exec->setException(error);
724 return error;
727 } // namespace KJS