]>
git.saurik.com Git - apple/javascriptcore.git/blob - kjs/object.cpp
1 // -*- c-basic-offset: 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.
29 #include "date_object.h"
30 #include "error_object.h"
33 #include "operations.h"
34 #include "PropertyNameArray.h"
36 #include <wtf/Assertions.h>
38 // maximum global call stack size. Protects against accidental or
39 // malicious infinite recursions. Define to -1 if you want no limit.
40 // In real-world testing it appears ok to bump the stack depth count to 500.
41 // This of course is dependent on stack frame size.
42 #define KJS_MAX_STACK 500
44 #define JAVASCRIPT_CALL_TRACING 0
45 #define JAVASCRIPT_MARK_TRACING 0
47 #if JAVASCRIPT_CALL_TRACING
48 static bool _traceJavaScript
= false;
51 void setTraceJavaScript(bool f
)
56 static bool traceJavaScript()
58 return _traceJavaScript
;
65 // ------------------------------ Object ---------------------------------------
67 JSValue
*JSObject::call(ExecState
*exec
, JSObject
*thisObj
, const List
&args
)
69 ASSERT(implementsCall());
72 static int depth
= 0; // sum of all extant function calls
74 #if JAVASCRIPT_CALL_TRACING
75 static bool tracing
= false;
76 if (traceJavaScript() && !tracing
) {
78 for (int i
= 0; i
< depth
; i
++)
80 printf ("*** calling: %s\n", toString(exec
).ascii());
81 for (int j
= 0; j
< args
.size(); j
++) {
82 for (int i
= 0; i
< depth
; i
++)
84 printf ("*** arg[%d] = %s\n", j
, args
[j
]->toString(exec
).ascii());
90 if (++depth
> KJS_MAX_STACK
) {
92 return throwError(exec
, RangeError
, "Maximum call stack size exceeded.");
96 JSValue
*ret
= callAsFunction(exec
,thisObj
,args
);
102 #if JAVASCRIPT_CALL_TRACING
103 if (traceJavaScript() && !tracing
) {
105 for (int i
= 0; i
< depth
; i
++)
107 printf ("*** returning: %s\n", ret
->toString(exec
).ascii());
115 // ------------------------------ JSObject ------------------------------------
117 void JSObject::mark()
121 #if JAVASCRIPT_MARK_TRACING
122 static int markStackDepth
= 0;
124 for (int i
= 0; i
< markStackDepth
; i
++)
127 printf("%s (%p)\n", className().UTF8String().c_str(), this);
130 JSValue
*proto
= _proto
;
131 if (!proto
->marked())
136 #if JAVASCRIPT_MARK_TRACING
141 JSType
JSObject::type() const
146 const ClassInfo
*JSObject::classInfo() const
151 UString
JSObject::className() const
153 const ClassInfo
*ci
= classInfo();
155 return ci
->className
;
159 JSValue
*JSObject::get(ExecState
*exec
, const Identifier
&propertyName
) const
163 if (const_cast<JSObject
*>(this)->getPropertySlot(exec
, propertyName
, slot
))
164 return slot
.getValue(exec
, const_cast<JSObject
*>(this), propertyName
);
166 return jsUndefined();
169 JSValue
*JSObject::get(ExecState
*exec
, unsigned propertyName
) const
172 if (const_cast<JSObject
*>(this)->getPropertySlot(exec
, propertyName
, slot
))
173 return slot
.getValue(exec
, const_cast<JSObject
*>(this), propertyName
);
175 return jsUndefined();
178 bool JSObject::getPropertySlot(ExecState
*exec
, unsigned propertyName
, PropertySlot
& slot
)
180 JSObject
*imp
= this;
183 if (imp
->getOwnPropertySlot(exec
, propertyName
, slot
))
186 JSValue
*proto
= imp
->_proto
;
187 if (!proto
->isObject())
190 imp
= static_cast<JSObject
*>(proto
);
196 bool JSObject::getOwnPropertySlot(ExecState
*exec
, unsigned propertyName
, PropertySlot
& slot
)
198 return getOwnPropertySlot(exec
, Identifier::from(propertyName
), slot
);
201 static void throwSetterError(ExecState
*exec
)
203 throwError(exec
, TypeError
, "setting a property that has only a getter");
207 void JSObject::put(ExecState
* exec
, const Identifier
&propertyName
, JSValue
*value
, int attr
)
211 if (propertyName
== exec
->propertyNames().underscoreProto
) {
212 JSObject
* proto
= value
->getObject();
215 throwError(exec
, GeneralError
, "cyclic __proto__ value");
216 proto
= proto
->prototype() ? proto
->prototype()->getObject() : 0;
223 // The put calls from JavaScript execution either have no attributes set, or in some cases
224 // have DontDelete set. For those calls, respect the ReadOnly flag.
225 bool checkReadOnly
= !(attr
& ~DontDelete
);
227 // Check if there are any setters or getters in the prototype chain
228 JSObject
*obj
= this;
229 bool hasGettersOrSetters
= false;
231 if (obj
->_prop
.hasGetterSetterProperties()) {
232 hasGettersOrSetters
= true;
236 if (!obj
->_proto
->isObject())
239 obj
= static_cast<JSObject
*>(obj
->_proto
);
242 if (hasGettersOrSetters
) {
243 if (checkReadOnly
&& !canPut(exec
, propertyName
))
249 if (JSValue
*gs
= obj
->_prop
.get(propertyName
, attributes
)) {
250 if (attributes
& GetterSetter
) {
251 JSObject
*setterFunc
= static_cast<GetterSetterImp
*>(gs
)->getSetter();
254 throwSetterError(exec
);
261 setterFunc
->call(exec
, this, args
);
264 // If there's an existing property on the object or one of its
265 // prototype it should be replaced, so we just break here.
270 if (!obj
->_proto
->isObject())
273 obj
= static_cast<JSObject
*>(obj
->_proto
);
277 _prop
.put(propertyName
, value
, attr
, checkReadOnly
);
280 void JSObject::put(ExecState
*exec
, unsigned propertyName
,
281 JSValue
*value
, int attr
)
283 put(exec
, Identifier::from(propertyName
), value
, attr
);
287 bool JSObject::canPut(ExecState
*, const Identifier
&propertyName
) const
291 // Don't look in the prototype here. We can always put an override
292 // in the object, even if the prototype has a ReadOnly property.
293 // Also, there is no need to check the static property table, as this
294 // would have been done by the subclass already.
296 if (!_prop
.get(propertyName
, attributes
))
299 return !(attributes
& ReadOnly
);
303 bool JSObject::hasProperty(ExecState
*exec
, const Identifier
&propertyName
) const
306 return const_cast<JSObject
*>(this)->getPropertySlot(exec
, propertyName
, slot
);
309 bool JSObject::hasProperty(ExecState
*exec
, unsigned propertyName
) const
312 return const_cast<JSObject
*>(this)->getPropertySlot(exec
, propertyName
, slot
);
316 bool JSObject::deleteProperty(ExecState
* /*exec*/, const Identifier
&propertyName
)
319 JSValue
*v
= _prop
.get(propertyName
, attributes
);
321 if ((attributes
& DontDelete
))
323 _prop
.remove(propertyName
);
324 if (attributes
& GetterSetter
)
325 _prop
.setHasGetterSetterProperties(_prop
.containsGettersOrSetters());
329 // Look in the static hashtable of properties
330 const HashEntry
* entry
= findPropertyHashEntry(propertyName
);
331 if (entry
&& entry
->attr
& DontDelete
)
332 return false; // this builtin property can't be deleted
336 bool JSObject::hasOwnProperty(ExecState
* exec
, const Identifier
& propertyName
) const
339 return const_cast<JSObject
*>(this)->getOwnPropertySlot(exec
, propertyName
, slot
);
342 bool JSObject::deleteProperty(ExecState
*exec
, unsigned propertyName
)
344 return deleteProperty(exec
, Identifier::from(propertyName
));
347 static ALWAYS_INLINE JSValue
*tryGetAndCallProperty(ExecState
*exec
, const JSObject
*object
, const Identifier
&propertyName
) {
348 JSValue
*v
= object
->get(exec
, propertyName
);
350 JSObject
*o
= static_cast<JSObject
*>(v
);
351 if (o
->implementsCall()) { // spec says "not primitive type" but ...
352 JSObject
*thisObj
= const_cast<JSObject
*>(object
);
353 JSValue
* def
= o
->call(exec
, thisObj
, exec
->emptyList());
354 JSType defType
= def
->type();
355 ASSERT(defType
!= GetterSetterType
);
356 if (defType
!= ObjectType
)
363 bool JSObject::getPrimitiveNumber(ExecState
* exec
, double& number
, JSValue
*& result
)
365 result
= defaultValue(exec
, NumberType
);
366 number
= result
->toNumber(exec
);
367 return !result
->isString();
371 JSValue
* JSObject::defaultValue(ExecState
* exec
, JSType hint
) const
373 /* Prefer String for Date objects */
374 if ((hint
== StringType
) || (hint
!= NumberType
&& _proto
== exec
->lexicalGlobalObject()->datePrototype())) {
375 if (JSValue
* v
= tryGetAndCallProperty(exec
, this, exec
->propertyNames().toString
))
377 if (JSValue
* v
= tryGetAndCallProperty(exec
, this, exec
->propertyNames().valueOf
))
380 if (JSValue
* v
= tryGetAndCallProperty(exec
, this, exec
->propertyNames().valueOf
))
382 if (JSValue
* v
= tryGetAndCallProperty(exec
, this, exec
->propertyNames().toString
))
386 if (exec
->hadException())
387 return exec
->exception();
389 return throwError(exec
, TypeError
, "No default value");
392 const HashEntry
* JSObject::findPropertyHashEntry(const Identifier
& propertyName
) const
394 for (const ClassInfo
*info
= classInfo(); info
; info
= info
->parentClass
) {
395 if (const HashTable
*propHashTable
= info
->propHashTable
) {
396 if (const HashEntry
*e
= Lookup::findEntry(propHashTable
, propertyName
))
403 void JSObject::defineGetter(ExecState
*, const Identifier
& propertyName
, JSObject
* getterFunc
)
405 JSValue
*o
= getDirect(propertyName
);
408 if (o
&& o
->type() == GetterSetterType
) {
409 gs
= static_cast<GetterSetterImp
*>(o
);
411 gs
= new GetterSetterImp
;
412 putDirect(propertyName
, gs
, GetterSetter
);
415 _prop
.setHasGetterSetterProperties(true);
416 gs
->setGetter(getterFunc
);
419 void JSObject::defineSetter(ExecState
*, const Identifier
& propertyName
, JSObject
* setterFunc
)
421 JSValue
*o
= getDirect(propertyName
);
424 if (o
&& o
->type() == GetterSetterType
) {
425 gs
= static_cast<GetterSetterImp
*>(o
);
427 gs
= new GetterSetterImp
;
428 putDirect(propertyName
, gs
, GetterSetter
);
431 _prop
.setHasGetterSetterProperties(true);
432 gs
->setSetter(setterFunc
);
435 bool JSObject::implementsConstruct() const
440 JSObject
* JSObject::construct(ExecState
*, const List
& /*args*/)
446 JSObject
* JSObject::construct(ExecState
* exec
, const List
& args
, const Identifier
& /*functionName*/, const UString
& /*sourceURL*/, int /*lineNumber*/)
448 return construct(exec
, args
);
451 bool JSObject::implementsCall() const
456 JSValue
*JSObject::callAsFunction(ExecState
* /*exec*/, JSObject
* /*thisObj*/, const List
&/*args*/)
462 bool JSObject::implementsHasInstance() const
467 bool JSObject::hasInstance(ExecState
* exec
, JSValue
* value
)
469 JSValue
* proto
= get(exec
, exec
->propertyNames().prototype
);
470 if (!proto
->isObject()) {
471 throwError(exec
, TypeError
, "intanceof called on an object with an invalid prototype property.");
475 if (!value
->isObject())
478 JSObject
* o
= static_cast<JSObject
*>(value
);
479 while ((o
= o
->prototype()->getObject())) {
486 bool JSObject::propertyIsEnumerable(ExecState
*, const Identifier
& propertyName
) const
490 if (!getPropertyAttributes(propertyName
, attributes
))
493 return !(attributes
& DontEnum
);
496 bool JSObject::getPropertyAttributes(const Identifier
& propertyName
, unsigned& attributes
) const
498 if (_prop
.get(propertyName
, attributes
))
501 // Look in the static hashtable of properties
502 const HashEntry
* e
= findPropertyHashEntry(propertyName
);
504 attributes
= e
->attr
;
511 void JSObject::getPropertyNames(ExecState
* exec
, PropertyNameArray
& propertyNames
)
513 _prop
.getEnumerablePropertyNames(propertyNames
);
515 // Add properties from the static hashtable of properties
516 const ClassInfo
*info
= classInfo();
518 if (info
->propHashTable
) {
519 int size
= info
->propHashTable
->size
;
520 const HashEntry
*e
= info
->propHashTable
->entries
;
521 for (int i
= 0; i
< size
; ++i
, ++e
) {
522 if (e
->s
&& !(e
->attr
& DontEnum
))
523 propertyNames
.add(e
->s
);
526 info
= info
->parentClass
;
528 if (_proto
->isObject())
529 static_cast<JSObject
*>(_proto
)->getPropertyNames(exec
, propertyNames
);
532 bool JSObject::toBoolean(ExecState
*) const
537 double JSObject::toNumber(ExecState
*exec
) const
539 JSValue
*prim
= toPrimitive(exec
,NumberType
);
540 if (exec
->hadException()) // should be picked up soon in nodes.cpp
542 return prim
->toNumber(exec
);
545 UString
JSObject::toString(ExecState
*exec
) const
547 JSValue
*prim
= toPrimitive(exec
,StringType
);
548 if (exec
->hadException()) // should be picked up soon in nodes.cpp
550 return prim
->toString(exec
);
553 JSObject
*JSObject::toObject(ExecState
*) const
555 return const_cast<JSObject
*>(this);
558 void JSObject::putDirect(const Identifier
&propertyName
, JSValue
*value
, int attr
)
560 _prop
.put(propertyName
, value
, attr
);
563 void JSObject::putDirect(const Identifier
&propertyName
, int value
, int attr
)
565 _prop
.put(propertyName
, jsNumber(value
), attr
);
568 void JSObject::removeDirect(const Identifier
&propertyName
)
570 _prop
.remove(propertyName
);
573 void JSObject::putDirectFunction(InternalFunctionImp
* func
, int attr
)
575 putDirect(func
->functionName(), func
, attr
);
578 void JSObject::fillGetterPropertySlot(PropertySlot
& slot
, JSValue
**location
)
580 GetterSetterImp
*gs
= static_cast<GetterSetterImp
*>(*location
);
581 JSObject
*getterFunc
= gs
->getGetter();
583 slot
.setGetterSlot(this, getterFunc
);
585 slot
.setUndefined(this);
588 // ------------------------------ Error ----------------------------------------
590 const char * const errorNamesArr
[] = {
591 I18N_NOOP("Error"), // GeneralError
592 I18N_NOOP("Evaluation error"), // EvalError
593 I18N_NOOP("Range error"), // RangeError
594 I18N_NOOP("Reference error"), // ReferenceError
595 I18N_NOOP("Syntax error"), // SyntaxError
596 I18N_NOOP("Type error"), // TypeError
597 I18N_NOOP("URI error"), // URIError
600 const char * const * const Error::errorNames
= errorNamesArr
;
602 JSObject
*Error::create(ExecState
*exec
, ErrorType errtype
, const UString
&message
,
603 int lineno
, int sourceId
, const UString
&sourceURL
)
608 cons
= exec
->lexicalGlobalObject()->evalErrorConstructor();
611 cons
= exec
->lexicalGlobalObject()->rangeErrorConstructor();
614 cons
= exec
->lexicalGlobalObject()->referenceErrorConstructor();
617 cons
= exec
->lexicalGlobalObject()->syntaxErrorConstructor();
620 cons
= exec
->lexicalGlobalObject()->typeErrorConstructor();
623 cons
= exec
->lexicalGlobalObject()->URIErrorConstructor();
626 cons
= exec
->lexicalGlobalObject()->errorConstructor();
631 if (message
.isEmpty())
632 args
.append(jsString(errorNames
[errtype
]));
634 args
.append(jsString(message
));
635 JSObject
*err
= static_cast<JSObject
*>(cons
->construct(exec
,args
));
638 err
->put(exec
, "line", jsNumber(lineno
));
640 err
->put(exec
, "sourceId", jsNumber(sourceId
));
642 if(!sourceURL
.isNull())
643 err
->put(exec
, "sourceURL", jsString(sourceURL
));
648 JSObject
*Error::create(ExecState
*exec
, ErrorType type
, const char *message
)
650 return create(exec
, type
, message
, -1, -1, NULL
);
653 JSObject
*throwError(ExecState
*exec
, ErrorType type
)
655 JSObject
*error
= Error::create(exec
, type
, UString(), -1, -1, NULL
);
656 exec
->setException(error
);
660 JSObject
*throwError(ExecState
*exec
, ErrorType type
, const UString
&message
)
662 JSObject
*error
= Error::create(exec
, type
, message
, -1, -1, NULL
);
663 exec
->setException(error
);
667 JSObject
*throwError(ExecState
*exec
, ErrorType type
, const char *message
)
669 JSObject
*error
= Error::create(exec
, type
, message
, -1, -1, NULL
);
670 exec
->setException(error
);
674 JSObject
*throwError(ExecState
*exec
, ErrorType type
, const UString
&message
, int line
, int sourceId
, const UString
&sourceURL
)
676 JSObject
*error
= Error::create(exec
, type
, message
, line
, sourceId
, sourceURL
);
677 exec
->setException(error
);