Remove JAVASCRIPT_PROFILER define
[WebKit-https.git] / JavaScriptCore / kjs / object.cpp
1 // -*- c-basic-offset: 2 -*-
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)
7  *
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.
12  *
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.
17  *
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.
22  *
23  */
24
25 #include "config.h"
26 #include "object.h"
27
28 #include "date_object.h"
29 #include "error_object.h"
30 #include "nodes.h"
31 #include "operations.h"
32 #include "PropertyNameArray.h"
33 #include <math.h>
34 #include <profiler/Profiler.h>
35 #include <wtf/Assertions.h>
36
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
42
43 #define JAVASCRIPT_CALL_TRACING 0
44 #define JAVASCRIPT_MARK_TRACING 0
45
46 #if JAVASCRIPT_CALL_TRACING
47 static bool _traceJavaScript = false;
48
49 extern "C" {
50     void setTraceJavaScript(bool f)
51     {
52         _traceJavaScript = f;
53     }
54
55     static bool traceJavaScript()
56     {
57         return _traceJavaScript;
58     }
59 }
60 #endif
61
62 namespace KJS {
63
64 // ------------------------------ Object ---------------------------------------
65
66 JSValue *JSObject::call(ExecState *exec, JSObject *thisObj, const List &args)
67 {
68   ASSERT(implementsCall());
69
70 #if KJS_MAX_STACK > 0
71   static int depth = 0; // sum of all extant function calls
72
73 #if JAVASCRIPT_CALL_TRACING
74     static bool tracing = false;
75     if (traceJavaScript() && !tracing) {
76         tracing = true;
77         for (int i = 0; i < depth; i++)
78             putchar (' ');
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++)
82                 putchar (' ');
83             printf ("*** arg[%d] = %s\n", j, args[j]->toString(exec).ascii());
84         }
85         tracing = false;
86     }
87 #endif
88
89   if (++depth > KJS_MAX_STACK) {
90     --depth;
91     return throwError(exec, RangeError, "Maximum call stack size exceeded.");
92   }
93 #endif
94
95     Profiler::profiler()->willExecute(exec, this);
96   
97     JSValue *ret = callAsFunction(exec,thisObj,args); 
98
99     Profiler::profiler()->didExecute(exec, this);
100
101 #if KJS_MAX_STACK > 0
102   --depth;
103 #endif
104
105 #if JAVASCRIPT_CALL_TRACING
106     if (traceJavaScript() && !tracing) {
107         tracing = true;
108         for (int i = 0; i < depth; i++)
109             putchar (' ');
110         printf ("*** returning:  %s\n", ret->toString(exec).ascii());
111         tracing = false;
112     }
113 #endif
114
115   return ret;
116 }
117
118 // ------------------------------ JSObject ------------------------------------
119
120 void JSObject::mark()
121 {
122   JSCell::mark();
123
124 #if JAVASCRIPT_MARK_TRACING
125   static int markStackDepth = 0;
126   markStackDepth++;
127   for (int i = 0; i < markStackDepth; i++)
128     putchar('-');
129   
130   printf("%s (%p)\n", className().UTF8String().c_str(), this);
131 #endif
132   
133   JSValue *proto = _proto;
134   if (!proto->marked())
135     proto->mark();
136
137   _prop.mark();
138   
139 #if JAVASCRIPT_MARK_TRACING
140   markStackDepth--;
141 #endif
142 }
143
144 JSType JSObject::type() const
145 {
146   return ObjectType;
147 }
148
149 const ClassInfo *JSObject::classInfo() const
150 {
151   return 0;
152 }
153
154 UString JSObject::className() const
155 {
156   const ClassInfo *ci = classInfo();
157   if ( ci )
158     return ci->className;
159   return "Object";
160 }
161
162 bool JSObject::getOwnPropertySlot(ExecState *exec, unsigned propertyName, PropertySlot& slot)
163 {
164   return getOwnPropertySlot(exec, Identifier::from(propertyName), slot);
165 }
166
167 static void throwSetterError(ExecState *exec)
168 {
169   throwError(exec, TypeError, "setting a property that has only a getter");
170 }
171
172 // ECMA 8.6.2.2
173 void JSObject::put(ExecState* exec, const Identifier &propertyName, JSValue *value)
174 {
175   ASSERT(value);
176
177   if (propertyName == exec->propertyNames().underscoreProto) {
178     JSObject* proto = value->getObject();
179
180     // Setting __proto__ to a non-object, non-null value is silently ignored to match Mozilla
181     if (!proto && value != jsNull())
182       return;
183
184     while (proto) {
185       if (proto == this) {
186         throwError(exec, GeneralError, "cyclic __proto__ value");
187         return;
188       }
189       proto = proto->prototype() ? proto->prototype()->getObject() : 0;
190     }
191     
192     setPrototype(value);
193     return;
194   }
195
196   // Check if there are any setters or getters in the prototype chain
197   JSObject *obj = this;
198   bool hasGettersOrSetters = false;
199   while (true) {
200     if (obj->_prop.hasGetterSetterProperties()) {
201       hasGettersOrSetters = true;
202       break;
203     }
204       
205     if (obj->_proto == jsNull())
206       break;
207       
208     obj = static_cast<JSObject *>(obj->_proto);
209   }
210   
211   if (hasGettersOrSetters) {
212     unsigned attributes;
213     if (_prop.get(propertyName, attributes) && attributes & ReadOnly)
214         return;
215
216     obj = this;
217     while (true) {
218       if (JSValue *gs = obj->_prop.get(propertyName, attributes)) {
219         if (attributes & GetterSetter) {
220           JSObject *setterFunc = static_cast<GetterSetterImp *>(gs)->getSetter();
221         
222           if (!setterFunc) {
223             throwSetterError(exec);
224             return;
225           }
226             
227           List args;
228           args.append(value);
229         
230           setterFunc->call(exec, this->toThisObject(exec), args);
231           return;
232         } else {
233           // If there's an existing property on the object or one of its 
234           // prototype it should be replaced, so we just break here.
235           break;
236         }
237       }
238      
239       if (!obj->_proto->isObject())
240         break;
241         
242       obj = static_cast<JSObject *>(obj->_proto);
243     }
244   }
245   
246   _prop.put(propertyName, value, 0, true);
247 }
248
249 void JSObject::put(ExecState* exec, unsigned propertyName, JSValue* value)
250 {
251     put(exec, Identifier::from(propertyName), value);
252 }
253
254 void JSObject::putWithAttributes(ExecState*, const Identifier& propertyName, JSValue* value, unsigned attributes)
255 {
256     putDirect(propertyName, value, attributes);
257 }
258
259 void JSObject::putWithAttributes(ExecState* exec, unsigned propertyName, JSValue* value, unsigned attributes)
260 {
261     putWithAttributes(exec, Identifier::from(propertyName), value, attributes);
262 }
263
264 bool JSObject::hasProperty(ExecState *exec, const Identifier &propertyName) const
265 {
266   PropertySlot slot;
267   return const_cast<JSObject *>(this)->getPropertySlot(exec, propertyName, slot);
268 }
269
270 bool JSObject::hasProperty(ExecState *exec, unsigned propertyName) const
271 {
272   PropertySlot slot;
273   return const_cast<JSObject *>(this)->getPropertySlot(exec, propertyName, slot);
274 }
275
276 // ECMA 8.6.2.5
277 bool JSObject::deleteProperty(ExecState* exec, const Identifier &propertyName)
278 {
279   unsigned attributes;
280   JSValue *v = _prop.get(propertyName, attributes);
281   if (v) {
282     if ((attributes & DontDelete))
283       return false;
284     _prop.remove(propertyName);
285     if (attributes & GetterSetter) 
286         _prop.setHasGetterSetterProperties(_prop.containsGettersOrSetters());
287     return true;
288   }
289
290   // Look in the static hashtable of properties
291   const HashEntry* entry = findPropertyHashEntry(exec, propertyName);
292   if (entry && entry->attributes & DontDelete)
293     return false; // this builtin property can't be deleted
294   // FIXME: Should the code here actually do some deletion?
295   return true;
296 }
297
298 bool JSObject::hasOwnProperty(ExecState* exec, const Identifier& propertyName) const
299 {
300     PropertySlot slot;
301     return const_cast<JSObject*>(this)->getOwnPropertySlot(exec, propertyName, slot);
302 }
303
304 bool JSObject::deleteProperty(ExecState *exec, unsigned propertyName)
305 {
306   return deleteProperty(exec, Identifier::from(propertyName));
307 }
308
309 static ALWAYS_INLINE JSValue *tryGetAndCallProperty(ExecState *exec, const JSObject *object, const Identifier &propertyName) {
310   JSValue* v = object->get(exec, propertyName);
311   if (v->isObject()) {
312       JSObject* o = static_cast<JSObject*>(v);
313       CallData data;
314       CallType callType = o->getCallData(data);
315       // spec says "not primitive type" but ...
316       if (callType != CallTypeNone) {
317           JSObject* thisObj = const_cast<JSObject*>(object);
318           JSValue* def = o->call(exec, thisObj->toThisObject(exec), exec->emptyList());
319           JSType defType = def->type();
320           ASSERT(defType != GetterSetterType);
321           if (defType != ObjectType)
322               return def;
323     }
324   }
325   return NULL;
326 }
327
328 bool JSObject::getPrimitiveNumber(ExecState* exec, double& number, JSValue*& result)
329 {
330     result = defaultValue(exec, NumberType);
331     number = result->toNumber(exec);
332     return !result->isString();
333 }
334
335 // ECMA 8.6.2.6
336 JSValue* JSObject::defaultValue(ExecState* exec, JSType hint) const
337 {
338   // We need this check to guard against the case where this object is rhs of
339   // a binary expression where lhs threw an exception in its conversion to
340   // primitive
341   if (exec->hadException())
342     return exec->exception();
343   /* Prefer String for Date objects */
344   if ((hint == StringType) || (hint != NumberType && _proto == exec->lexicalGlobalObject()->datePrototype())) {
345     if (JSValue* v = tryGetAndCallProperty(exec, this, exec->propertyNames().toString))
346       return v;
347     if (JSValue* v = tryGetAndCallProperty(exec, this, exec->propertyNames().valueOf))
348       return v;
349   } else {
350     if (JSValue* v = tryGetAndCallProperty(exec, this, exec->propertyNames().valueOf))
351       return v;
352     if (JSValue* v = tryGetAndCallProperty(exec, this, exec->propertyNames().toString))
353       return v;
354   }
355
356   if (exec->hadException())
357     return exec->exception();
358
359   return throwError(exec, TypeError, "No default value");
360 }
361
362 const HashEntry* JSObject::findPropertyHashEntry(ExecState* exec, const Identifier& propertyName) const
363 {
364     for (const ClassInfo* info = classInfo(); info; info = info->parentClass) {
365         if (const HashTable* propHashTable = info->propHashTable(exec)) {
366             if (const HashEntry* e = propHashTable->entry(propertyName))
367                 return e;
368         }
369     }
370     return 0;
371 }
372
373 void JSObject::defineGetter(ExecState*, const Identifier& propertyName, JSObject* getterFunc)
374 {
375     JSValue *o = getDirect(propertyName);
376     GetterSetterImp *gs;
377     
378     if (o && o->type() == GetterSetterType) {
379         gs = static_cast<GetterSetterImp *>(o);
380     } else {
381         gs = new GetterSetterImp;
382         putDirect(propertyName, gs, GetterSetter);
383     }
384     
385     _prop.setHasGetterSetterProperties(true);
386     gs->setGetter(getterFunc);
387 }
388
389 void JSObject::defineSetter(ExecState*, const Identifier& propertyName, JSObject* setterFunc)
390 {
391     JSValue *o = getDirect(propertyName);
392     GetterSetterImp *gs;
393     
394     if (o && o->type() == GetterSetterType) {
395         gs = static_cast<GetterSetterImp *>(o);
396     } else {
397         gs = new GetterSetterImp;
398         putDirect(propertyName, gs, GetterSetter);
399     }
400     
401     _prop.setHasGetterSetterProperties(true);
402     gs->setSetter(setterFunc);
403 }
404
405 JSValue* JSObject::lookupGetter(ExecState*, const Identifier& propertyName)
406 {
407     JSObject* obj = this;
408     while (true) {
409         JSValue* v = obj->getDirect(propertyName);
410         if (v) {
411             if (v->type() != GetterSetterType)
412                 return jsUndefined();
413             JSObject* funcObj = static_cast<GetterSetterImp*>(v)->getGetter();
414             if (!funcObj)
415                 return jsUndefined();
416             return funcObj;
417         }
418
419         if (!obj->prototype() || !obj->prototype()->isObject())
420             return jsUndefined();
421         obj = static_cast<JSObject*>(obj->prototype());
422     }
423 }
424
425 JSValue* JSObject::lookupSetter(ExecState*, const Identifier& propertyName)
426 {
427     JSObject* obj = this;
428     while (true) {
429         JSValue* v = obj->getDirect(propertyName);
430         if (v) {
431             if (v->type() != GetterSetterType)
432                 return jsUndefined();
433             JSObject* funcObj = static_cast<GetterSetterImp*>(v)->getSetter();
434             if (!funcObj)
435                 return jsUndefined();
436             return funcObj;
437         }
438
439         if (!obj->prototype() || !obj->prototype()->isObject())
440             return jsUndefined();
441         obj = static_cast<JSObject*>(obj->prototype());
442     }
443 }
444
445 JSObject* JSObject::construct(ExecState*, const List& /*args*/)
446 {
447   ASSERT(false);
448   return NULL;
449 }
450
451 JSObject* JSObject::construct(ExecState* exec, const List& args, const Identifier& /*functionName*/, const UString& /*sourceURL*/, int /*lineNumber*/)
452 {
453   return construct(exec, args);
454 }
455
456 bool JSObject::implementsCall()
457 {
458     CallData callData;
459     return getCallData(callData) != CallTypeNone;
460 }
461
462 JSValue *JSObject::callAsFunction(ExecState* /*exec*/, JSObject* /*thisObj*/, const List &/*args*/)
463 {
464   ASSERT(false);
465   return NULL;
466 }
467
468 bool JSObject::implementsHasInstance() const
469 {
470   return false;
471 }
472
473 bool JSObject::hasInstance(ExecState* exec, JSValue* value)
474 {
475     JSValue* proto = get(exec, exec->propertyNames().prototype);
476     if (!proto->isObject()) {
477         throwError(exec, TypeError, "intanceof called on an object with an invalid prototype property.");
478         return false;
479     }
480     
481     if (!value->isObject())
482         return false;
483     
484     JSObject* o = static_cast<JSObject*>(value);
485     while ((o = o->prototype()->getObject())) {
486         if (o == proto)
487             return true;
488     }
489     return false;
490 }
491
492 bool JSObject::propertyIsEnumerable(ExecState* exec, const Identifier& propertyName) const
493 {
494   unsigned attributes;
495  
496   if (!getPropertyAttributes(exec, propertyName, attributes))
497     return false;
498   else
499     return !(attributes & DontEnum);
500 }
501
502 bool JSObject::getPropertyAttributes(ExecState* exec, const Identifier& propertyName, unsigned& attributes) const
503 {
504   if (_prop.get(propertyName, attributes))
505     return true;
506     
507   // Look in the static hashtable of properties
508   const HashEntry* e = findPropertyHashEntry(exec, propertyName);
509   if (e) {
510     attributes = e->attributes;
511     return true;
512   }
513     
514   return false;
515 }
516
517 void JSObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
518 {
519     _prop.getEnumerablePropertyNames(propertyNames);
520
521     // Add properties from the static hashtables of properties
522     for (const ClassInfo* info = classInfo(); info; info = info->parentClass) {
523         const HashTable* table = info->propHashTable(exec);
524         if (!table)
525             continue;
526         if (!table->table)
527             table->createTable();
528         ASSERT(table->table);
529         int hashSizeMask = table->hashSizeMask;
530         const HashEntry* e = table->table;
531         for (int i = 0; i <= hashSizeMask; ++i, ++e) {
532             if (e->key && !(e->attributes & DontEnum))
533                 propertyNames.add(e->key);
534         }
535     }
536
537     if (_proto->isObject())
538         static_cast<JSObject*>(_proto)->getPropertyNames(exec, propertyNames);
539 }
540
541 bool JSObject::toBoolean(ExecState*) const
542 {
543   return true;
544 }
545
546 double JSObject::toNumber(ExecState *exec) const
547 {
548   JSValue *prim = toPrimitive(exec,NumberType);
549   if (exec->hadException()) // should be picked up soon in nodes.cpp
550     return 0.0;
551   return prim->toNumber(exec);
552 }
553
554 UString JSObject::toString(ExecState *exec) const
555 {
556   JSValue *prim = toPrimitive(exec,StringType);
557   if (exec->hadException()) // should be picked up soon in nodes.cpp
558     return "";
559   return prim->toString(exec);
560 }
561
562 JSObject *JSObject::toObject(ExecState*) const
563 {
564   return const_cast<JSObject*>(this);
565 }
566
567 JSObject* JSObject::toThisObject(ExecState*) const
568 {
569     return const_cast<JSObject*>(this);
570 }
571
572 JSGlobalObject* JSObject::toGlobalObject(ExecState*) const
573 {
574     return 0;
575 }
576
577 void JSObject::removeDirect(const Identifier &propertyName)
578 {
579     _prop.remove(propertyName);
580 }
581
582 void JSObject::putDirectFunction(InternalFunctionImp* func, int attr)
583 {
584     putDirect(func->functionName(), func, attr); 
585 }
586
587 void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSValue **location)
588 {
589     GetterSetterImp *gs = static_cast<GetterSetterImp *>(*location);
590     JSObject *getterFunc = gs->getGetter();
591     if (getterFunc)
592         slot.setGetterSlot(this->toThisObject(0), getterFunc);
593     else
594         slot.setUndefined(this);
595 }
596
597 // ------------------------------ Error ----------------------------------------
598
599 const char * const errorNamesArr[] = {
600   I18N_NOOP("Error"), // GeneralError
601   I18N_NOOP("Evaluation error"), // EvalError
602   I18N_NOOP("Range error"), // RangeError
603   I18N_NOOP("Reference error"), // ReferenceError
604   I18N_NOOP("Syntax error"), // SyntaxError
605   I18N_NOOP("Type error"), // TypeError
606   I18N_NOOP("URI error"), // URIError
607 };
608
609 const char * const * const Error::errorNames = errorNamesArr;
610
611 JSObject *Error::create(ExecState *exec, ErrorType errtype, const UString &message,
612                          int lineno, int sourceId, const UString &sourceURL)
613 {
614   JSObject *cons;
615   switch (errtype) {
616   case EvalError:
617     cons = exec->lexicalGlobalObject()->evalErrorConstructor();
618     break;
619   case RangeError:
620     cons = exec->lexicalGlobalObject()->rangeErrorConstructor();
621     break;
622   case ReferenceError:
623     cons = exec->lexicalGlobalObject()->referenceErrorConstructor();
624     break;
625   case SyntaxError:
626     cons = exec->lexicalGlobalObject()->syntaxErrorConstructor();
627     break;
628   case TypeError:
629     cons = exec->lexicalGlobalObject()->typeErrorConstructor();
630     break;
631   case URIError:
632     cons = exec->lexicalGlobalObject()->URIErrorConstructor();
633     break;
634   default:
635     cons = exec->lexicalGlobalObject()->errorConstructor();
636     break;
637   }
638
639   List args;
640   if (message.isEmpty())
641     args.append(jsString(errorNames[errtype]));
642   else
643     args.append(jsString(message));
644   JSObject *err = static_cast<JSObject *>(cons->construct(exec,args));
645
646   if (lineno != -1)
647     err->put(exec, "line", jsNumber(lineno));
648   if (sourceId != -1)
649     err->put(exec, "sourceId", jsNumber(sourceId));
650
651   if(!sourceURL.isNull())
652     err->put(exec, "sourceURL", jsString(sourceURL));
653  
654   return err;
655 }
656
657 JSObject *Error::create(ExecState *exec, ErrorType type, const char *message)
658 {
659     return create(exec, type, message, -1, -1, NULL);
660 }
661
662 JSObject *throwError(ExecState *exec, ErrorType type)
663 {
664     JSObject *error = Error::create(exec, type, UString(), -1, -1, NULL);
665     exec->setException(error);
666     return error;
667 }
668
669 JSObject *throwError(ExecState *exec, ErrorType type, const UString &message)
670 {
671     JSObject *error = Error::create(exec, type, message, -1, -1, NULL);
672     exec->setException(error);
673     return error;
674 }
675
676 JSObject *throwError(ExecState *exec, ErrorType type, const char *message)
677 {
678     JSObject *error = Error::create(exec, type, message, -1, -1, NULL);
679     exec->setException(error);
680     return error;
681 }
682
683 JSObject *throwError(ExecState *exec, ErrorType type, const UString &message, int line, int sourceId, const UString &sourceURL)
684 {
685     JSObject *error = Error::create(exec, type, message, line, sourceId, sourceURL);
686     exec->setException(error);
687     return error;
688 }
689
690 } // namespace KJS