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