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 "error_object.h"
32 #include "operations.h"
33 #include "PropertyNameArray.h"
36 // maximum global call stack size. Protects against accidental or
37 // malicious infinite recursions. Define to -1 if you want no limit.
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
43 #define KJS_MAX_STACK 900
46 #define JAVASCRIPT_CALL_TRACING 0
47 #define JAVASCRIPT_MARK_TRACING 0
49 #if JAVASCRIPT_CALL_TRACING
50 static bool _traceJavaScript
= false;
53 void setTraceJavaScript(bool f
)
58 static bool traceJavaScript()
60 return _traceJavaScript
;
67 // ------------------------------ Object ---------------------------------------
69 JSValue
*JSObject::call(ExecState
*exec
, JSObject
*thisObj
, const List
&args
)
71 assert(implementsCall());
74 static int depth
= 0; // sum of all concurrent interpreters
76 #if JAVASCRIPT_CALL_TRACING
77 static bool tracing
= false;
78 if (traceJavaScript() && !tracing
) {
80 for (int i
= 0; i
< depth
; i
++)
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
++)
86 printf ("*** arg[%d] = %s\n", j
, args
[j
]->toString(exec
).ascii());
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..
99 JSValue
*ret
= callAsFunction(exec
,thisObj
,args
);
102 #if KJS_MAX_STACK > 0
106 #if JAVASCRIPT_CALL_TRACING
107 if (traceJavaScript() && !tracing
) {
109 for (int i
= 0; i
< depth
; i
++)
111 printf ("*** returning: %s\n", ret
->toString(exec
).ascii());
119 // ------------------------------ JSObject ------------------------------------
121 void JSObject::mark()
125 #if JAVASCRIPT_MARK_TRACING
126 static int markStackDepth
= 0;
128 for (int i
= 0; i
< markStackDepth
; i
++)
131 printf("%s (%p)\n", className().UTF8String().c_str(), this);
134 JSValue
*proto
= _proto
;
135 if (!proto
->marked())
140 #if JAVASCRIPT_MARK_TRACING
145 JSType
JSObject::type() const
150 const ClassInfo
*JSObject::classInfo() const
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
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
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;
185 if (imp
->getOwnPropertySlot(exec
, propertyName
, slot
))
188 JSValue
*proto
= imp
->_proto
;
189 if (!proto
->isObject())
192 imp
= static_cast<JSObject
*>(proto
);
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
);
212 slot
.setValueSlot(this, location
);
216 // non-standard Netscape extension
217 if (propertyName
== exec
->propertyNames().underscoreProto
) {
218 slot
.setValueSlot(this, &_proto
);
226 static void throwSetterError(ExecState
*exec
)
228 throwError(exec
, TypeError
, "setting a property that has only a getter");
232 void JSObject::put(ExecState
*exec
, const Identifier
&propertyName
, JSValue
*value
, int attr
)
236 // non-standard netscape extension
237 if (propertyName
== exec
->propertyNames().underscoreProto
) {
238 JSObject
* proto
= value
->getObject();
241 throwError(exec
, GeneralError
, "cyclic __proto__ value");
244 proto
= proto
->prototype() ? proto
->prototype()->getObject() : 0;
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
);
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
) {
262 fprintf( stderr
, "WARNING: static property %s is ReadOnly\n", propertyName
.ascii() );
268 // Check if there are any setters or getters in the prototype chain
269 JSObject
*obj
= this;
270 bool hasGettersOrSetters
= false;
272 if (obj
->_prop
.hasGetterSetterProperties()) {
273 hasGettersOrSetters
= true;
277 if (!obj
->_proto
->isObject())
280 obj
= static_cast<JSObject
*>(obj
->_proto
);
283 if (hasGettersOrSetters
) {
287 if (JSValue
*gs
= obj
->_prop
.get(propertyName
, attributes
)) {
288 if (attributes
& GetterSetter
) {
289 JSObject
*setterFunc
= static_cast<GetterSetterImp
*>(gs
)->getSetter();
292 throwSetterError(exec
);
299 setterFunc
->call(exec
, this, args
);
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.
308 if (!obj
->_proto
->isObject())
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
);
325 bool JSObject::canPut(ExecState
*, const Identifier
&propertyName
) const
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
))
335 return !(attributes
& ReadOnly
);
339 bool JSObject::hasProperty(ExecState
*exec
, const Identifier
&propertyName
) const
342 return const_cast<JSObject
*>(this)->getPropertySlot(exec
, propertyName
, slot
);
345 bool JSObject::hasProperty(ExecState
*exec
, unsigned propertyName
) const
348 return const_cast<JSObject
*>(this)->getPropertySlot(exec
, propertyName
, slot
);
352 bool JSObject::deleteProperty(ExecState
* /*exec*/, const Identifier
&propertyName
)
355 JSValue
*v
= _prop
.get(propertyName
, attributes
);
357 if ((attributes
& DontDelete
))
359 _prop
.remove(propertyName
);
360 if (attributes
& GetterSetter
)
361 _prop
.setHasGetterSetterProperties(_prop
.containsGettersOrSetters());
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
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
);
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
)
393 bool JSObject::getPrimitiveNumber(ExecState
* exec
, double& number
, JSValue
*& result
)
395 result
= defaultValue(exec
, NumberType
);
396 number
= result
->toNumber(exec
);
397 return !result
->isString();
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
;
410 firstPropertyName
= &exec
->propertyNames().valueOf
;
411 secondPropertyName
= &exec
->propertyNames().toString
;
415 if ((v
= tryGetAndCallProperty(exec
, this, *firstPropertyName
)))
417 if ((v
= tryGetAndCallProperty(exec
, this, *secondPropertyName
)))
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
))
437 void JSObject::defineGetter(ExecState
*, const Identifier
& propertyName
, JSObject
* getterFunc
)
439 JSValue
*o
= getDirect(propertyName
);
442 if (o
&& o
->type() == GetterSetterType
) {
443 gs
= static_cast<GetterSetterImp
*>(o
);
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
);
458 if (o
&& o
->type() == GetterSetterType
) {
459 gs
= static_cast<GetterSetterImp
*>(o
);
461 gs
= new GetterSetterImp
;
462 putDirect(propertyName
, gs
, GetterSetter
);
465 _prop
.setHasGetterSetterProperties(true);
466 gs
->setSetter(setterFunc
);
469 bool JSObject::implementsConstruct() const
474 JSObject
* JSObject::construct(ExecState
*, const List
& /*args*/)
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*/)
496 bool JSObject::implementsHasInstance() const
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.");
509 if (!value
->isObject())
512 JSObject
* o
= static_cast<JSObject
*>(value
);
513 while ((o
= o
->prototype()->getObject())) {
520 bool JSObject::propertyIsEnumerable(ExecState
*, const Identifier
& propertyName
) const
524 if (!getPropertyAttributes(propertyName
, attributes
))
527 return !(attributes
& DontEnum
);
530 bool JSObject::getPropertyAttributes(const Identifier
& propertyName
, unsigned& attributes
) const
532 if (_prop
.get(propertyName
, attributes
))
535 // Look in the static hashtable of properties
536 const HashEntry
* e
= findPropertyHashEntry(propertyName
);
538 attributes
= e
->attr
;
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();
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
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
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();
610 slot
.setGetterSlot(this, getterFunc
);
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
)
633 // message could be 0L. Don't enable this on Solaris ;)
634 fprintf(stderr
, "WARNING: KJS %s: %s\n", errorNamesArr
[errtype
], message
.ascii());
638 Interpreter
* interp
= exec
->lexicalInterpreter();
642 cons
= interp
->builtinEvalError();
645 cons
= interp
->builtinRangeError();
648 cons
= interp
->builtinReferenceError();
651 cons
= interp
->builtinSyntaxError();
654 cons
= interp
->builtinTypeError();
657 cons
= interp
->builtinURIError();
660 cons
= interp
->builtinError();
665 if (message
.isEmpty())
666 args
.append(jsString(errorNames
[errtype
]));
668 args
.append(jsString(message
));
669 JSObject
*err
= static_cast<JSObject
*>(cons
->construct(exec
,args
));
672 err
->put(exec
, "line", jsNumber(lineno
));
674 err
->put(exec
, "sourceId", jsNumber(sourceId
));
676 if(!sourceURL
.isNull())
677 err
->put(exec
, "sourceURL", jsString(sourceURL
));
683 const char *msg = err->get(messagePropertyName)->toString().value().ascii();
685 fprintf(stderr, "KJS: %s at line %d. %s\n", estr, l, msg);
687 fprintf(stderr, "KJS: %s. %s\n", estr, msg);
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
);
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
);
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
);
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
);