0894e42d8975b74252d90596430e8484f956d6a2
[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   if ((attr == None || attr == DontDelete) && !canPut(exec,propertyName)) {
230     return;
231   }
232
233   // Check if there are any setters or getters in the prototype chain
234   JSObject *obj = this;
235   bool hasGettersOrSetters = false;
236   while (true) {
237     if (obj->_prop.hasGetterSetterProperties()) {
238       hasGettersOrSetters = true;
239       break;
240     }
241       
242     if (!obj->_proto->isObject())
243       break;
244       
245     obj = static_cast<JSObject *>(obj->_proto);
246   }
247   
248   if (hasGettersOrSetters) {
249     obj = this;
250     while (true) {
251       unsigned attributes;
252       if (JSValue *gs = obj->_prop.get(propertyName, attributes)) {
253         if (attributes & GetterSetter) {
254           JSObject *setterFunc = static_cast<GetterSetterImp *>(gs)->getSetter();
255         
256           if (!setterFunc) {
257             throwSetterError(exec);
258             return;
259           }
260             
261           List args;
262           args.append(value);
263         
264           setterFunc->call(exec, this, args);
265           return;
266         } else {
267           // If there's an existing property on the object or one of its 
268           // prototype it should be replaced, so we just break here.
269           break;
270         }
271       }
272      
273       if (!obj->_proto->isObject())
274         break;
275         
276       obj = static_cast<JSObject *>(obj->_proto);
277     }
278   }
279   
280   _prop.put(propertyName,value,attr);
281 }
282
283 void JSObject::put(ExecState *exec, unsigned propertyName,
284                      JSValue *value, int attr)
285 {
286   put(exec, Identifier::from(propertyName), value, attr);
287 }
288
289 // ECMA 8.6.2.3
290 bool JSObject::canPut(ExecState *, const Identifier &propertyName) const
291 {
292   unsigned attributes;
293     
294   // Don't look in the prototype here. We can always put an override
295   // in the object, even if the prototype has a ReadOnly property.
296
297   if (!getPropertyAttributes(propertyName, attributes))
298     return true;
299   else
300     return !(attributes & ReadOnly);
301 }
302
303 // ECMA 8.6.2.4
304 bool JSObject::hasProperty(ExecState *exec, const Identifier &propertyName) const
305 {
306   PropertySlot slot;
307   return const_cast<JSObject *>(this)->getPropertySlot(exec, propertyName, slot);
308 }
309
310 bool JSObject::hasProperty(ExecState *exec, unsigned propertyName) const
311 {
312   PropertySlot slot;
313   return const_cast<JSObject *>(this)->getPropertySlot(exec, propertyName, slot);
314 }
315
316 // ECMA 8.6.2.5
317 bool JSObject::deleteProperty(ExecState* /*exec*/, const Identifier &propertyName)
318 {
319   unsigned attributes;
320   JSValue *v = _prop.get(propertyName, attributes);
321   if (v) {
322     if ((attributes & DontDelete))
323       return false;
324     _prop.remove(propertyName);
325     if (attributes & GetterSetter) 
326         _prop.setHasGetterSetterProperties(_prop.containsGettersOrSetters());
327     return true;
328   }
329
330   // Look in the static hashtable of properties
331   const HashEntry* entry = findPropertyHashEntry(propertyName);
332   if (entry && entry->attr & DontDelete)
333     return false; // this builtin property can't be deleted
334   return true;
335 }
336
337 bool JSObject::hasOwnProperty(ExecState* exec, const Identifier& propertyName) const
338 {
339     PropertySlot slot;
340     return const_cast<JSObject*>(this)->getOwnPropertySlot(exec, propertyName, slot);
341 }
342
343 bool JSObject::deleteProperty(ExecState *exec, unsigned propertyName)
344 {
345   return deleteProperty(exec, Identifier::from(propertyName));
346 }
347
348 static ALWAYS_INLINE JSValue *tryGetAndCallProperty(ExecState *exec, const JSObject *object, const Identifier &propertyName) {
349   JSValue *v = object->get(exec, propertyName);
350   if (v->isObject()) {
351     JSObject *o = static_cast<JSObject*>(v);
352     if (o->implementsCall()) { // spec says "not primitive type" but ...
353       JSObject *thisObj = const_cast<JSObject*>(object);
354       JSValue *def = o->call(exec, thisObj, List::empty());
355       JSType defType = def->type();
356       ASSERT(defType != GetterSetterType);
357       if (defType != ObjectType)
358         return def;
359     }
360   }
361   return NULL;
362 }
363
364 bool JSObject::getPrimitiveNumber(ExecState* exec, double& number, JSValue*& result)
365 {
366     result = defaultValue(exec, NumberType);
367     number = result->toNumber(exec);
368     return !result->isString();
369 }
370
371 // ECMA 8.6.2.6
372 JSValue* JSObject::defaultValue(ExecState* exec, JSType hint) const
373 {
374   /* Prefer String for Date objects */
375   if ((hint == StringType) || (hint != NumberType && _proto == exec->lexicalGlobalObject()->datePrototype())) {
376     if (JSValue* v = tryGetAndCallProperty(exec, this, exec->propertyNames().toString))
377       return v;
378     if (JSValue* v = tryGetAndCallProperty(exec, this, exec->propertyNames().valueOf))
379       return v;
380   } else {
381     if (JSValue* v = tryGetAndCallProperty(exec, this, exec->propertyNames().valueOf))
382       return v;
383     if (JSValue* v = tryGetAndCallProperty(exec, this, exec->propertyNames().toString))
384       return v;
385   }
386
387   if (exec->hadException())
388     return exec->exception();
389
390   return throwError(exec, TypeError, "No default value");
391 }
392
393 const HashEntry* JSObject::findPropertyHashEntry(const Identifier& propertyName) const
394 {
395   for (const ClassInfo *info = classInfo(); info; info = info->parentClass) {
396     if (const HashTable *propHashTable = info->propHashTable) {
397       if (const HashEntry *e = Lookup::findEntry(propHashTable, propertyName))
398         return e;
399     }
400   }
401   return 0;
402 }
403
404 void JSObject::defineGetter(ExecState*, const Identifier& propertyName, JSObject* getterFunc)
405 {
406     JSValue *o = getDirect(propertyName);
407     GetterSetterImp *gs;
408     
409     if (o && o->type() == GetterSetterType) {
410         gs = static_cast<GetterSetterImp *>(o);
411     } else {
412         gs = new GetterSetterImp;
413         putDirect(propertyName, gs, GetterSetter);
414     }
415     
416     _prop.setHasGetterSetterProperties(true);
417     gs->setGetter(getterFunc);
418 }
419
420 void JSObject::defineSetter(ExecState*, const Identifier& propertyName, JSObject* setterFunc)
421 {
422     JSValue *o = getDirect(propertyName);
423     GetterSetterImp *gs;
424     
425     if (o && o->type() == GetterSetterType) {
426         gs = static_cast<GetterSetterImp *>(o);
427     } else {
428         gs = new GetterSetterImp;
429         putDirect(propertyName, gs, GetterSetter);
430     }
431     
432     _prop.setHasGetterSetterProperties(true);
433     gs->setSetter(setterFunc);
434 }
435
436 bool JSObject::implementsConstruct() const
437 {
438   return false;
439 }
440
441 JSObject* JSObject::construct(ExecState*, const List& /*args*/)
442 {
443   ASSERT(false);
444   return NULL;
445 }
446
447 JSObject* JSObject::construct(ExecState* exec, const List& args, const Identifier& /*functionName*/, const UString& /*sourceURL*/, int /*lineNumber*/)
448 {
449   return construct(exec, args);
450 }
451
452 bool JSObject::implementsCall() const
453 {
454   return false;
455 }
456
457 JSValue *JSObject::callAsFunction(ExecState* /*exec*/, JSObject* /*thisObj*/, const List &/*args*/)
458 {
459   ASSERT(false);
460   return NULL;
461 }
462
463 bool JSObject::implementsHasInstance() const
464 {
465   return false;
466 }
467
468 bool JSObject::hasInstance(ExecState* exec, JSValue* value)
469 {
470     JSValue* proto = get(exec, exec->propertyNames().prototype);
471     if (!proto->isObject()) {
472         throwError(exec, TypeError, "intanceof called on an object with an invalid prototype property.");
473         return false;
474     }
475     
476     if (!value->isObject())
477         return false;
478     
479     JSObject* o = static_cast<JSObject*>(value);
480     while ((o = o->prototype()->getObject())) {
481         if (o == proto)
482             return true;
483     }
484     return false;
485 }
486
487 bool JSObject::propertyIsEnumerable(ExecState*, const Identifier& propertyName) const
488 {
489   unsigned attributes;
490  
491   if (!getPropertyAttributes(propertyName, attributes))
492     return false;
493   else
494     return !(attributes & DontEnum);
495 }
496
497 bool JSObject::getPropertyAttributes(const Identifier& propertyName, unsigned& attributes) const
498 {
499   if (_prop.get(propertyName, attributes))
500     return true;
501     
502   // Look in the static hashtable of properties
503   const HashEntry* e = findPropertyHashEntry(propertyName);
504   if (e) {
505     attributes = e->attr;
506     return true;
507   }
508     
509   return false;
510 }
511
512 void JSObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
513 {
514    _prop.getEnumerablePropertyNames(propertyNames);
515
516   // Add properties from the static hashtable of properties
517   const ClassInfo *info = classInfo();
518   while (info) {
519     if (info->propHashTable) {
520       int size = info->propHashTable->size;
521       const HashEntry *e = info->propHashTable->entries;
522       for (int i = 0; i < size; ++i, ++e) {
523         if (e->s && !(e->attr & DontEnum))
524           propertyNames.add(e->s);
525       }
526     }
527     info = info->parentClass;
528   }
529   if (_proto->isObject())
530      static_cast<JSObject*>(_proto)->getPropertyNames(exec, propertyNames);
531 }
532
533 bool JSObject::toBoolean(ExecState*) const
534 {
535   return true;
536 }
537
538 double JSObject::toNumber(ExecState *exec) const
539 {
540   JSValue *prim = toPrimitive(exec,NumberType);
541   if (exec->hadException()) // should be picked up soon in nodes.cpp
542     return 0.0;
543   return prim->toNumber(exec);
544 }
545
546 UString JSObject::toString(ExecState *exec) const
547 {
548   JSValue *prim = toPrimitive(exec,StringType);
549   if (exec->hadException()) // should be picked up soon in nodes.cpp
550     return "";
551   return prim->toString(exec);
552 }
553
554 JSObject *JSObject::toObject(ExecState*) const
555 {
556   return const_cast<JSObject*>(this);
557 }
558
559 void JSObject::putDirect(const Identifier &propertyName, JSValue *value, int attr)
560 {
561     _prop.put(propertyName, value, attr);
562 }
563
564 void JSObject::putDirect(const Identifier &propertyName, int value, int attr)
565 {
566     _prop.put(propertyName, jsNumber(value), attr);
567 }
568
569 void JSObject::removeDirect(const Identifier &propertyName)
570 {
571     _prop.remove(propertyName);
572 }
573
574 void JSObject::putDirectFunction(InternalFunctionImp* func, int attr)
575 {
576     putDirect(func->functionName(), func, attr); 
577 }
578
579 void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSValue **location)
580 {
581     GetterSetterImp *gs = static_cast<GetterSetterImp *>(*location);
582     JSObject *getterFunc = gs->getGetter();
583     if (getterFunc)
584         slot.setGetterSlot(this, getterFunc);
585     else
586         slot.setUndefined(this);
587 }
588
589 // ------------------------------ Error ----------------------------------------
590
591 const char * const errorNamesArr[] = {
592   I18N_NOOP("Error"), // GeneralError
593   I18N_NOOP("Evaluation error"), // EvalError
594   I18N_NOOP("Range error"), // RangeError
595   I18N_NOOP("Reference error"), // ReferenceError
596   I18N_NOOP("Syntax error"), // SyntaxError
597   I18N_NOOP("Type error"), // TypeError
598   I18N_NOOP("URI error"), // URIError
599 };
600
601 const char * const * const Error::errorNames = errorNamesArr;
602
603 JSObject *Error::create(ExecState *exec, ErrorType errtype, const UString &message,
604                          int lineno, int sourceId, const UString &sourceURL)
605 {
606   JSObject *cons;
607   switch (errtype) {
608   case EvalError:
609     cons = exec->lexicalGlobalObject()->evalErrorConstructor();
610     break;
611   case RangeError:
612     cons = exec->lexicalGlobalObject()->rangeErrorConstructor();
613     break;
614   case ReferenceError:
615     cons = exec->lexicalGlobalObject()->referenceErrorConstructor();
616     break;
617   case SyntaxError:
618     cons = exec->lexicalGlobalObject()->syntaxErrorConstructor();
619     break;
620   case TypeError:
621     cons = exec->lexicalGlobalObject()->typeErrorConstructor();
622     break;
623   case URIError:
624     cons = exec->lexicalGlobalObject()->URIErrorConstructor();
625     break;
626   default:
627     cons = exec->lexicalGlobalObject()->errorConstructor();
628     break;
629   }
630
631   List args;
632   if (message.isEmpty())
633     args.append(jsString(errorNames[errtype]));
634   else
635     args.append(jsString(message));
636   JSObject *err = static_cast<JSObject *>(cons->construct(exec,args));
637
638   if (lineno != -1)
639     err->put(exec, "line", jsNumber(lineno));
640   if (sourceId != -1)
641     err->put(exec, "sourceId", jsNumber(sourceId));
642
643   if(!sourceURL.isNull())
644     err->put(exec, "sourceURL", jsString(sourceURL));
645  
646   return err;
647
648 /*
649 #ifndef NDEBUG
650   const char *msg = err->get(messagePropertyName)->toString().value().ascii();
651   if (l >= 0)
652       fprintf(stderr, "KJS: %s at line %d. %s\n", estr, l, msg);
653   else
654       fprintf(stderr, "KJS: %s. %s\n", estr, msg);
655 #endif
656
657   return err;
658 */
659 }
660
661 JSObject *Error::create(ExecState *exec, ErrorType type, const char *message)
662 {
663     return create(exec, type, message, -1, -1, NULL);
664 }
665
666 JSObject *throwError(ExecState *exec, ErrorType type)
667 {
668     JSObject *error = Error::create(exec, type, UString(), -1, -1, NULL);
669     exec->setException(error);
670     return error;
671 }
672
673 JSObject *throwError(ExecState *exec, ErrorType type, const UString &message)
674 {
675     JSObject *error = Error::create(exec, type, message, -1, -1, NULL);
676     exec->setException(error);
677     return error;
678 }
679
680 JSObject *throwError(ExecState *exec, ErrorType type, const char *message)
681 {
682     JSObject *error = Error::create(exec, type, message, -1, -1, NULL);
683     exec->setException(error);
684     return error;
685 }
686
687 JSObject *throwError(ExecState *exec, ErrorType type, const UString &message, int line, int sourceId, const UString &sourceURL)
688 {
689     JSObject *error = Error::create(exec, type, message, line, sourceId, sourceURL);
690     exec->setException(error);
691     return error;
692 }
693
694 } // namespace KJS