1 // -*- c-basic-offset: 2 -*-
3 * Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
4 * Copyright (C) 2001 Peter Kelly (pmk@post.com)
5 * Copyright (C) 2003, 2004, 2005, 2006, 2008 Apple Inc. All rights reserved.
6 * Copyright (C) 2007 Eric Seidel (eric@webkit.org)
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
28 #include "date_object.h"
29 #include "error_object.h"
31 #include "operations.h"
32 #include "PropertyNameArray.h"
34 #include <profiler/Profiler.h>
35 #include <wtf/Assertions.h>
37 // maximum global call stack size. Protects against accidental or
38 // malicious infinite recursions. Define to -1 if you want no limit.
39 // In real-world testing it appears ok to bump the stack depth count to 500.
40 // This of course is dependent on stack frame size.
41 #define KJS_MAX_STACK 500
43 #define JAVASCRIPT_CALL_TRACING 0
44 #define JAVASCRIPT_MARK_TRACING 0
46 #if JAVASCRIPT_CALL_TRACING
47 static bool _traceJavaScript = false;
50 void setTraceJavaScript(bool f)
55 static bool traceJavaScript()
57 return _traceJavaScript;
64 // ------------------------------ Object ---------------------------------------
66 JSValue *JSObject::call(ExecState *exec, JSObject *thisObj, const List &args)
68 ASSERT(implementsCall());
71 static int depth = 0; // sum of all extant function calls
73 #if JAVASCRIPT_CALL_TRACING
74 static bool tracing = false;
75 if (traceJavaScript() && !tracing) {
77 for (int i = 0; i < depth; i++)
79 printf ("*** calling: %s\n", toString(exec).ascii());
80 for (int j = 0; j < args.size(); j++) {
81 for (int i = 0; i < depth; i++)
83 printf ("*** arg[%d] = %s\n", j, args[j]->toString(exec).ascii());
89 if (++depth > KJS_MAX_STACK) {
91 return throwError(exec, RangeError, "Maximum call stack size exceeded.");
95 #if JAVASCRIPT_PROFILING
96 Profiler::profiler()->willExecute(exec, this);
99 JSValue *ret = callAsFunction(exec,thisObj,args);
101 #if JAVASCRIPT_PROFILING
102 Profiler::profiler()->didExecute(exec, this);
106 #if KJS_MAX_STACK > 0
110 #if JAVASCRIPT_CALL_TRACING
111 if (traceJavaScript() && !tracing) {
113 for (int i = 0; i < depth; i++)
115 printf ("*** returning: %s\n", ret->toString(exec).ascii());
123 // ------------------------------ JSObject ------------------------------------
125 void JSObject::mark()
129 #if JAVASCRIPT_MARK_TRACING
130 static int markStackDepth = 0;
132 for (int i = 0; i < markStackDepth; i++)
135 printf("%s (%p)\n", className().UTF8String().c_str(), this);
138 JSValue *proto = _proto;
139 if (!proto->marked())
144 #if JAVASCRIPT_MARK_TRACING
149 JSType JSObject::type() const
154 const ClassInfo *JSObject::classInfo() const
159 UString JSObject::className() const
161 const ClassInfo *ci = classInfo();
163 return ci->className;
167 bool JSObject::getOwnPropertySlot(ExecState *exec, unsigned propertyName, PropertySlot& slot)
169 return getOwnPropertySlot(exec, Identifier::from(propertyName), slot);
172 static void throwSetterError(ExecState *exec)
174 throwError(exec, TypeError, "setting a property that has only a getter");
178 void JSObject::put(ExecState* exec, const Identifier &propertyName, JSValue *value)
182 if (propertyName == exec->propertyNames().underscoreProto) {
183 JSObject* proto = value->getObject();
185 // Setting __proto__ to a non-object, non-null value is silently ignored to match Mozilla
186 if (!proto && value != jsNull())
191 throwError(exec, GeneralError, "cyclic __proto__ value");
194 proto = proto->prototype() ? proto->prototype()->getObject() : 0;
201 // Check if there are any setters or getters in the prototype chain
202 JSObject *obj = this;
203 bool hasGettersOrSetters = false;
205 if (obj->_prop.hasGetterSetterProperties()) {
206 hasGettersOrSetters = true;
210 if (obj->_proto == jsNull())
213 obj = static_cast<JSObject *>(obj->_proto);
216 if (hasGettersOrSetters) {
218 if (_prop.get(propertyName, attributes) && attributes & ReadOnly)
223 if (JSValue *gs = obj->_prop.get(propertyName, attributes)) {
224 if (attributes & GetterSetter) {
225 JSObject *setterFunc = static_cast<GetterSetterImp *>(gs)->getSetter();
228 throwSetterError(exec);
235 setterFunc->call(exec, this->toThisObject(exec), args);
238 // If there's an existing property on the object or one of its
239 // prototype it should be replaced, so we just break here.
244 if (!obj->_proto->isObject())
247 obj = static_cast<JSObject *>(obj->_proto);
251 _prop.put(propertyName, value, 0, true);
254 void JSObject::put(ExecState* exec, unsigned propertyName, JSValue* value)
256 put(exec, Identifier::from(propertyName), value);
259 void JSObject::putWithAttributes(ExecState*, const Identifier& propertyName, JSValue* value, unsigned attributes)
261 putDirect(propertyName, value, attributes);
264 void JSObject::putWithAttributes(ExecState* exec, unsigned propertyName, JSValue* value, unsigned attributes)
266 putWithAttributes(exec, Identifier::from(propertyName), value, attributes);
269 bool JSObject::hasProperty(ExecState *exec, const Identifier &propertyName) const
272 return const_cast<JSObject *>(this)->getPropertySlot(exec, propertyName, slot);
275 bool JSObject::hasProperty(ExecState *exec, unsigned propertyName) const
278 return const_cast<JSObject *>(this)->getPropertySlot(exec, propertyName, slot);
282 bool JSObject::deleteProperty(ExecState* exec, const Identifier &propertyName)
285 JSValue *v = _prop.get(propertyName, attributes);
287 if ((attributes & DontDelete))
289 _prop.remove(propertyName);
290 if (attributes & GetterSetter)
291 _prop.setHasGetterSetterProperties(_prop.containsGettersOrSetters());
295 // Look in the static hashtable of properties
296 const HashEntry* entry = findPropertyHashEntry(exec, propertyName);
297 if (entry && entry->attributes & DontDelete)
298 return false; // this builtin property can't be deleted
299 // FIXME: Should the code here actually do some deletion?
303 bool JSObject::hasOwnProperty(ExecState* exec, const Identifier& propertyName) const
306 return const_cast<JSObject*>(this)->getOwnPropertySlot(exec, propertyName, slot);
309 bool JSObject::deleteProperty(ExecState *exec, unsigned propertyName)
311 return deleteProperty(exec, Identifier::from(propertyName));
314 static ALWAYS_INLINE JSValue *tryGetAndCallProperty(ExecState *exec, const JSObject *object, const Identifier &propertyName) {
315 JSValue* v = object->get(exec, propertyName);
317 JSObject* o = static_cast<JSObject*>(v);
319 CallType callType = o->getCallData(data);
320 // spec says "not primitive type" but ...
321 if (callType != CallTypeNone) {
322 JSObject* thisObj = const_cast<JSObject*>(object);
323 JSValue* def = o->call(exec, thisObj->toThisObject(exec), exec->emptyList());
324 JSType defType = def->type();
325 ASSERT(defType != GetterSetterType);
326 if (defType != ObjectType)
333 bool JSObject::getPrimitiveNumber(ExecState* exec, double& number, JSValue*& result)
335 result = defaultValue(exec, NumberType);
336 number = result->toNumber(exec);
337 return !result->isString();
341 JSValue* JSObject::defaultValue(ExecState* exec, JSType hint) const
343 // We need this check to guard against the case where this object is rhs of
344 // a binary expression where lhs threw an exception in its conversion to
346 if (exec->hadException())
347 return exec->exception();
348 /* Prefer String for Date objects */
349 if ((hint == StringType) || (hint != NumberType && _proto == exec->lexicalGlobalObject()->datePrototype())) {
350 if (JSValue* v = tryGetAndCallProperty(exec, this, exec->propertyNames().toString))
352 if (JSValue* v = tryGetAndCallProperty(exec, this, exec->propertyNames().valueOf))
355 if (JSValue* v = tryGetAndCallProperty(exec, this, exec->propertyNames().valueOf))
357 if (JSValue* v = tryGetAndCallProperty(exec, this, exec->propertyNames().toString))
361 if (exec->hadException())
362 return exec->exception();
364 return throwError(exec, TypeError, "No default value");
367 const HashEntry* JSObject::findPropertyHashEntry(ExecState* exec, const Identifier& propertyName) const
369 for (const ClassInfo* info = classInfo(); info; info = info->parentClass) {
370 if (const HashTable* propHashTable = info->propHashTable(exec)) {
371 if (const HashEntry* e = propHashTable->entry(propertyName))
378 void JSObject::defineGetter(ExecState*, const Identifier& propertyName, JSObject* getterFunc)
380 JSValue *o = getDirect(propertyName);
383 if (o && o->type() == GetterSetterType) {
384 gs = static_cast<GetterSetterImp *>(o);
386 gs = new GetterSetterImp;
387 putDirect(propertyName, gs, GetterSetter);
390 _prop.setHasGetterSetterProperties(true);
391 gs->setGetter(getterFunc);
394 void JSObject::defineSetter(ExecState*, const Identifier& propertyName, JSObject* setterFunc)
396 JSValue *o = getDirect(propertyName);
399 if (o && o->type() == GetterSetterType) {
400 gs = static_cast<GetterSetterImp *>(o);
402 gs = new GetterSetterImp;
403 putDirect(propertyName, gs, GetterSetter);
406 _prop.setHasGetterSetterProperties(true);
407 gs->setSetter(setterFunc);
410 JSValue* JSObject::lookupGetter(ExecState*, const Identifier& propertyName)
412 JSObject* obj = this;
414 JSValue* v = obj->getDirect(propertyName);
416 if (v->type() != GetterSetterType)
417 return jsUndefined();
418 JSObject* funcObj = static_cast<GetterSetterImp*>(v)->getGetter();
420 return jsUndefined();
424 if (!obj->prototype() || !obj->prototype()->isObject())
425 return jsUndefined();
426 obj = static_cast<JSObject*>(obj->prototype());
430 JSValue* JSObject::lookupSetter(ExecState*, const Identifier& propertyName)
432 JSObject* obj = this;
434 JSValue* v = obj->getDirect(propertyName);
436 if (v->type() != GetterSetterType)
437 return jsUndefined();
438 JSObject* funcObj = static_cast<GetterSetterImp*>(v)->getSetter();
440 return jsUndefined();
444 if (!obj->prototype() || !obj->prototype()->isObject())
445 return jsUndefined();
446 obj = static_cast<JSObject*>(obj->prototype());
450 JSObject* JSObject::construct(ExecState*, const List& /*args*/)
456 JSObject* JSObject::construct(ExecState* exec, const List& args, const Identifier& /*functionName*/, const UString& /*sourceURL*/, int /*lineNumber*/)
458 return construct(exec, args);
461 bool JSObject::implementsCall()
464 return getCallData(callData) != CallTypeNone;
467 JSValue *JSObject::callAsFunction(ExecState* /*exec*/, JSObject* /*thisObj*/, const List &/*args*/)
473 bool JSObject::implementsHasInstance() const
478 bool JSObject::hasInstance(ExecState* exec, JSValue* value)
480 JSValue* proto = get(exec, exec->propertyNames().prototype);
481 if (!proto->isObject()) {
482 throwError(exec, TypeError, "intanceof called on an object with an invalid prototype property.");
486 if (!value->isObject())
489 JSObject* o = static_cast<JSObject*>(value);
490 while ((o = o->prototype()->getObject())) {
497 bool JSObject::propertyIsEnumerable(ExecState* exec, const Identifier& propertyName) const
501 if (!getPropertyAttributes(exec, propertyName, attributes))
504 return !(attributes & DontEnum);
507 bool JSObject::getPropertyAttributes(ExecState* exec, const Identifier& propertyName, unsigned& attributes) const
509 if (_prop.get(propertyName, attributes))
512 // Look in the static hashtable of properties
513 const HashEntry* e = findPropertyHashEntry(exec, propertyName);
515 attributes = e->attributes;
522 void JSObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
524 _prop.getEnumerablePropertyNames(propertyNames);
526 // Add properties from the static hashtables of properties
527 for (const ClassInfo* info = classInfo(); info; info = info->parentClass) {
528 const HashTable* table = info->propHashTable(exec);
532 table->createTable();
533 ASSERT(table->table);
534 int hashSizeMask = table->hashSizeMask;
535 const HashEntry* e = table->table;
536 for (int i = 0; i <= hashSizeMask; ++i, ++e) {
537 if (e->key && !(e->attributes & DontEnum))
538 propertyNames.add(e->key);
542 if (_proto->isObject())
543 static_cast<JSObject*>(_proto)->getPropertyNames(exec, propertyNames);
546 bool JSObject::toBoolean(ExecState*) const
551 double JSObject::toNumber(ExecState *exec) const
553 JSValue *prim = toPrimitive(exec,NumberType);
554 if (exec->hadException()) // should be picked up soon in nodes.cpp
556 return prim->toNumber(exec);
559 UString JSObject::toString(ExecState *exec) const
561 JSValue *prim = toPrimitive(exec,StringType);
562 if (exec->hadException()) // should be picked up soon in nodes.cpp
564 return prim->toString(exec);
567 JSObject *JSObject::toObject(ExecState*) const
569 return const_cast<JSObject*>(this);
572 JSObject* JSObject::toThisObject(ExecState*) const
574 return const_cast<JSObject*>(this);
577 JSGlobalObject* JSObject::toGlobalObject(ExecState*) const
582 void JSObject::removeDirect(const Identifier &propertyName)
584 _prop.remove(propertyName);
587 void JSObject::putDirectFunction(InternalFunctionImp* func, int attr)
589 putDirect(func->functionName(), func, attr);
592 void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSValue **location)
594 GetterSetterImp *gs = static_cast<GetterSetterImp *>(*location);
595 JSObject *getterFunc = gs->getGetter();
597 slot.setGetterSlot(this->toThisObject(0), getterFunc);
599 slot.setUndefined(this);
602 // ------------------------------ Error ----------------------------------------
604 const char * const errorNamesArr[] = {
605 I18N_NOOP("Error"), // GeneralError
606 I18N_NOOP("Evaluation error"), // EvalError
607 I18N_NOOP("Range error"), // RangeError
608 I18N_NOOP("Reference error"), // ReferenceError
609 I18N_NOOP("Syntax error"), // SyntaxError
610 I18N_NOOP("Type error"), // TypeError
611 I18N_NOOP("URI error"), // URIError
614 const char * const * const Error::errorNames = errorNamesArr;
616 JSObject *Error::create(ExecState *exec, ErrorType errtype, const UString &message,
617 int lineno, int sourceId, const UString &sourceURL)
622 cons = exec->lexicalGlobalObject()->evalErrorConstructor();
625 cons = exec->lexicalGlobalObject()->rangeErrorConstructor();
628 cons = exec->lexicalGlobalObject()->referenceErrorConstructor();
631 cons = exec->lexicalGlobalObject()->syntaxErrorConstructor();
634 cons = exec->lexicalGlobalObject()->typeErrorConstructor();
637 cons = exec->lexicalGlobalObject()->URIErrorConstructor();
640 cons = exec->lexicalGlobalObject()->errorConstructor();
645 if (message.isEmpty())
646 args.append(jsString(errorNames[errtype]));
648 args.append(jsString(message));
649 JSObject *err = static_cast<JSObject *>(cons->construct(exec,args));
652 err->put(exec, "line", jsNumber(lineno));
654 err->put(exec, "sourceId", jsNumber(sourceId));
656 if(!sourceURL.isNull())
657 err->put(exec, "sourceURL", jsString(sourceURL));
662 JSObject *Error::create(ExecState *exec, ErrorType type, const char *message)
664 return create(exec, type, message, -1, -1, NULL);
667 JSObject *throwError(ExecState *exec, ErrorType type)
669 JSObject *error = Error::create(exec, type, UString(), -1, -1, NULL);
670 exec->setException(error);
674 JSObject *throwError(ExecState *exec, ErrorType type, const UString &message)
676 JSObject *error = Error::create(exec, type, message, -1, -1, NULL);
677 exec->setException(error);
681 JSObject *throwError(ExecState *exec, ErrorType type, const char *message)
683 JSObject *error = Error::create(exec, type, message, -1, -1, NULL);
684 exec->setException(error);
688 JSObject *throwError(ExecState *exec, ErrorType type, const UString &message, int line, int sourceId, const UString &sourceURL)
690 JSObject *error = Error::create(exec, type, message, line, sourceId, sourceURL);
691 exec->setException(error);