1a5b8d64cc5dffca0908f80e027bcf1c06821a52
[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 #if JAVASCRIPT_PROFILING
96     Profiler::profiler()->willExecute(exec, this);
97 #endif
98   
99     JSValue *ret = callAsFunction(exec,thisObj,args); 
100
101 #if JAVASCRIPT_PROFILING
102     Profiler::profiler()->didExecute(exec, this);
103 #endif
104
105
106 #if KJS_MAX_STACK > 0
107   --depth;
108 #endif
109
110 #if JAVASCRIPT_CALL_TRACING
111     if (traceJavaScript() && !tracing) {
112         tracing = true;
113         for (int i = 0; i < depth; i++)
114             putchar (' ');
115         printf ("*** returning:  %s\n", ret->toString(exec).ascii());
116         tracing = false;
117     }
118 #endif
119
120   return ret;
121 }
122
123 // ------------------------------ JSObject ------------------------------------
124
125 void JSObject::mark()
126 {
127   JSCell::mark();
128
129 #if JAVASCRIPT_MARK_TRACING
130   static int markStackDepth = 0;
131   markStackDepth++;
132   for (int i = 0; i < markStackDepth; i++)
133     putchar('-');
134   
135   printf("%s (%p)\n", className().UTF8String().c_str(), this);
136 #endif
137   
138   JSValue *proto = _proto;
139   if (!proto->marked())
140     proto->mark();
141
142   _prop.mark();
143   
144 #if JAVASCRIPT_MARK_TRACING
145   markStackDepth--;
146 #endif
147 }
148
149 JSType JSObject::type() const
150 {
151   return ObjectType;
152 }
153
154 const ClassInfo *JSObject::classInfo() const
155 {
156   return 0;
157 }
158
159 UString JSObject::className() const
160 {
161   const ClassInfo *ci = classInfo();
162   if ( ci )
163     return ci->className;
164   return "Object";
165 }
166
167 bool JSObject::getOwnPropertySlot(ExecState *exec, unsigned propertyName, PropertySlot& slot)
168 {
169   return getOwnPropertySlot(exec, Identifier::from(propertyName), slot);
170 }
171
172 static void throwSetterError(ExecState *exec)
173 {
174   throwError(exec, TypeError, "setting a property that has only a getter");
175 }
176
177 // ECMA 8.6.2.2
178 void JSObject::put(ExecState* exec, const Identifier &propertyName, JSValue *value)
179 {
180   ASSERT(value);
181
182   if (propertyName == exec->propertyNames().underscoreProto) {
183     JSObject* proto = value->getObject();
184
185     // Setting __proto__ to a non-object, non-null value is silently ignored to match Mozilla
186     if (!proto && value != jsNull())
187       return;
188
189     while (proto) {
190       if (proto == this) {
191         throwError(exec, GeneralError, "cyclic __proto__ value");
192         return;
193       }
194       proto = proto->prototype() ? proto->prototype()->getObject() : 0;
195     }
196     
197     setPrototype(value);
198     return;
199   }
200
201   // Check if there are any setters or getters in the prototype chain
202   JSObject *obj = this;
203   bool hasGettersOrSetters = false;
204   while (true) {
205     if (obj->_prop.hasGetterSetterProperties()) {
206       hasGettersOrSetters = true;
207       break;
208     }
209       
210     if (obj->_proto == jsNull())
211       break;
212       
213     obj = static_cast<JSObject *>(obj->_proto);
214   }
215   
216   if (hasGettersOrSetters) {
217     unsigned attributes;
218     if (_prop.get(propertyName, attributes) && attributes & ReadOnly)
219         return;
220
221     obj = this;
222     while (true) {
223       if (JSValue *gs = obj->_prop.get(propertyName, attributes)) {
224         if (attributes & GetterSetter) {
225           JSObject *setterFunc = static_cast<GetterSetterImp *>(gs)->getSetter();
226         
227           if (!setterFunc) {
228             throwSetterError(exec);
229             return;
230           }
231             
232           List args;
233           args.append(value);
234         
235           setterFunc->call(exec, this->toThisObject(exec), args);
236           return;
237         } else {
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.
240           break;
241         }
242       }
243      
244       if (!obj->_proto->isObject())
245         break;
246         
247       obj = static_cast<JSObject *>(obj->_proto);
248     }
249   }
250   
251   _prop.put(propertyName, value, 0, true);
252 }
253
254 void JSObject::put(ExecState* exec, unsigned propertyName, JSValue* value)
255 {
256     put(exec, Identifier::from(propertyName), value);
257 }
258
259 void JSObject::putWithAttributes(ExecState*, const Identifier& propertyName, JSValue* value, unsigned attributes)
260 {
261     putDirect(propertyName, value, attributes);
262 }
263
264 void JSObject::putWithAttributes(ExecState* exec, unsigned propertyName, JSValue* value, unsigned attributes)
265 {
266     putWithAttributes(exec, Identifier::from(propertyName), value, attributes);
267 }
268
269 bool JSObject::hasProperty(ExecState *exec, const Identifier &propertyName) const
270 {
271   PropertySlot slot;
272   return const_cast<JSObject *>(this)->getPropertySlot(exec, propertyName, slot);
273 }
274
275 bool JSObject::hasProperty(ExecState *exec, unsigned propertyName) const
276 {
277   PropertySlot slot;
278   return const_cast<JSObject *>(this)->getPropertySlot(exec, propertyName, slot);
279 }
280
281 // ECMA 8.6.2.5
282 bool JSObject::deleteProperty(ExecState* exec, const Identifier &propertyName)
283 {
284   unsigned attributes;
285   JSValue *v = _prop.get(propertyName, attributes);
286   if (v) {
287     if ((attributes & DontDelete))
288       return false;
289     _prop.remove(propertyName);
290     if (attributes & GetterSetter) 
291         _prop.setHasGetterSetterProperties(_prop.containsGettersOrSetters());
292     return true;
293   }
294
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?
300   return true;
301 }
302
303 bool JSObject::hasOwnProperty(ExecState* exec, const Identifier& propertyName) const
304 {
305     PropertySlot slot;
306     return const_cast<JSObject*>(this)->getOwnPropertySlot(exec, propertyName, slot);
307 }
308
309 bool JSObject::deleteProperty(ExecState *exec, unsigned propertyName)
310 {
311   return deleteProperty(exec, Identifier::from(propertyName));
312 }
313
314 static ALWAYS_INLINE JSValue *tryGetAndCallProperty(ExecState *exec, const JSObject *object, const Identifier &propertyName) {
315   JSValue* v = object->get(exec, propertyName);
316   if (v->isObject()) {
317       JSObject* o = static_cast<JSObject*>(v);
318       CallData data;
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)
327               return def;
328     }
329   }
330   return NULL;
331 }
332
333 bool JSObject::getPrimitiveNumber(ExecState* exec, double& number, JSValue*& result)
334 {
335     result = defaultValue(exec, NumberType);
336     number = result->toNumber(exec);
337     return !result->isString();
338 }
339
340 // ECMA 8.6.2.6
341 JSValue* JSObject::defaultValue(ExecState* exec, JSType hint) const
342 {
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
345   // primitive
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))
351       return v;
352     if (JSValue* v = tryGetAndCallProperty(exec, this, exec->propertyNames().valueOf))
353       return v;
354   } else {
355     if (JSValue* v = tryGetAndCallProperty(exec, this, exec->propertyNames().valueOf))
356       return v;
357     if (JSValue* v = tryGetAndCallProperty(exec, this, exec->propertyNames().toString))
358       return v;
359   }
360
361   if (exec->hadException())
362     return exec->exception();
363
364   return throwError(exec, TypeError, "No default value");
365 }
366
367 const HashEntry* JSObject::findPropertyHashEntry(ExecState* exec, const Identifier& propertyName) const
368 {
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))
372                 return e;
373         }
374     }
375     return 0;
376 }
377
378 void JSObject::defineGetter(ExecState*, const Identifier& propertyName, JSObject* getterFunc)
379 {
380     JSValue *o = getDirect(propertyName);
381     GetterSetterImp *gs;
382     
383     if (o && o->type() == GetterSetterType) {
384         gs = static_cast<GetterSetterImp *>(o);
385     } else {
386         gs = new GetterSetterImp;
387         putDirect(propertyName, gs, GetterSetter);
388     }
389     
390     _prop.setHasGetterSetterProperties(true);
391     gs->setGetter(getterFunc);
392 }
393
394 void JSObject::defineSetter(ExecState*, const Identifier& propertyName, JSObject* setterFunc)
395 {
396     JSValue *o = getDirect(propertyName);
397     GetterSetterImp *gs;
398     
399     if (o && o->type() == GetterSetterType) {
400         gs = static_cast<GetterSetterImp *>(o);
401     } else {
402         gs = new GetterSetterImp;
403         putDirect(propertyName, gs, GetterSetter);
404     }
405     
406     _prop.setHasGetterSetterProperties(true);
407     gs->setSetter(setterFunc);
408 }
409
410 JSValue* JSObject::lookupGetter(ExecState*, const Identifier& propertyName)
411 {
412     JSObject* obj = this;
413     while (true) {
414         JSValue* v = obj->getDirect(propertyName);
415         if (v) {
416             if (v->type() != GetterSetterType)
417                 return jsUndefined();
418             JSObject* funcObj = static_cast<GetterSetterImp*>(v)->getGetter();
419             if (!funcObj)
420                 return jsUndefined();
421             return funcObj;
422         }
423
424         if (!obj->prototype() || !obj->prototype()->isObject())
425             return jsUndefined();
426         obj = static_cast<JSObject*>(obj->prototype());
427     }
428 }
429
430 JSValue* JSObject::lookupSetter(ExecState*, const Identifier& propertyName)
431 {
432     JSObject* obj = this;
433     while (true) {
434         JSValue* v = obj->getDirect(propertyName);
435         if (v) {
436             if (v->type() != GetterSetterType)
437                 return jsUndefined();
438             JSObject* funcObj = static_cast<GetterSetterImp*>(v)->getSetter();
439             if (!funcObj)
440                 return jsUndefined();
441             return funcObj;
442         }
443
444         if (!obj->prototype() || !obj->prototype()->isObject())
445             return jsUndefined();
446         obj = static_cast<JSObject*>(obj->prototype());
447     }
448 }
449
450 JSObject* JSObject::construct(ExecState*, const List& /*args*/)
451 {
452   ASSERT(false);
453   return NULL;
454 }
455
456 JSObject* JSObject::construct(ExecState* exec, const List& args, const Identifier& /*functionName*/, const UString& /*sourceURL*/, int /*lineNumber*/)
457 {
458   return construct(exec, args);
459 }
460
461 bool JSObject::implementsCall()
462 {
463     CallData callData;
464     return getCallData(callData) != CallTypeNone;
465 }
466
467 JSValue *JSObject::callAsFunction(ExecState* /*exec*/, JSObject* /*thisObj*/, const List &/*args*/)
468 {
469   ASSERT(false);
470   return NULL;
471 }
472
473 bool JSObject::implementsHasInstance() const
474 {
475   return false;
476 }
477
478 bool JSObject::hasInstance(ExecState* exec, JSValue* value)
479 {
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.");
483         return false;
484     }
485     
486     if (!value->isObject())
487         return false;
488     
489     JSObject* o = static_cast<JSObject*>(value);
490     while ((o = o->prototype()->getObject())) {
491         if (o == proto)
492             return true;
493     }
494     return false;
495 }
496
497 bool JSObject::propertyIsEnumerable(ExecState* exec, const Identifier& propertyName) const
498 {
499   unsigned attributes;
500  
501   if (!getPropertyAttributes(exec, propertyName, attributes))
502     return false;
503   else
504     return !(attributes & DontEnum);
505 }
506
507 bool JSObject::getPropertyAttributes(ExecState* exec, const Identifier& propertyName, unsigned& attributes) const
508 {
509   if (_prop.get(propertyName, attributes))
510     return true;
511     
512   // Look in the static hashtable of properties
513   const HashEntry* e = findPropertyHashEntry(exec, propertyName);
514   if (e) {
515     attributes = e->attributes;
516     return true;
517   }
518     
519   return false;
520 }
521
522 void JSObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
523 {
524     _prop.getEnumerablePropertyNames(propertyNames);
525
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);
529         if (!table)
530             continue;
531         if (!table->table)
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);
539         }
540     }
541
542     if (_proto->isObject())
543         static_cast<JSObject*>(_proto)->getPropertyNames(exec, propertyNames);
544 }
545
546 bool JSObject::toBoolean(ExecState*) const
547 {
548   return true;
549 }
550
551 double JSObject::toNumber(ExecState *exec) const
552 {
553   JSValue *prim = toPrimitive(exec,NumberType);
554   if (exec->hadException()) // should be picked up soon in nodes.cpp
555     return 0.0;
556   return prim->toNumber(exec);
557 }
558
559 UString JSObject::toString(ExecState *exec) const
560 {
561   JSValue *prim = toPrimitive(exec,StringType);
562   if (exec->hadException()) // should be picked up soon in nodes.cpp
563     return "";
564   return prim->toString(exec);
565 }
566
567 JSObject *JSObject::toObject(ExecState*) const
568 {
569   return const_cast<JSObject*>(this);
570 }
571
572 JSObject* JSObject::toThisObject(ExecState*) const
573 {
574     return const_cast<JSObject*>(this);
575 }
576
577 JSGlobalObject* JSObject::toGlobalObject(ExecState*) const
578 {
579     return 0;
580 }
581
582 void JSObject::removeDirect(const Identifier &propertyName)
583 {
584     _prop.remove(propertyName);
585 }
586
587 void JSObject::putDirectFunction(InternalFunctionImp* func, int attr)
588 {
589     putDirect(func->functionName(), func, attr); 
590 }
591
592 void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSValue **location)
593 {
594     GetterSetterImp *gs = static_cast<GetterSetterImp *>(*location);
595     JSObject *getterFunc = gs->getGetter();
596     if (getterFunc)
597         slot.setGetterSlot(this->toThisObject(0), getterFunc);
598     else
599         slot.setUndefined(this);
600 }
601
602 // ------------------------------ Error ----------------------------------------
603
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
612 };
613
614 const char * const * const Error::errorNames = errorNamesArr;
615
616 JSObject *Error::create(ExecState *exec, ErrorType errtype, const UString &message,
617                          int lineno, int sourceId, const UString &sourceURL)
618 {
619   JSObject *cons;
620   switch (errtype) {
621   case EvalError:
622     cons = exec->lexicalGlobalObject()->evalErrorConstructor();
623     break;
624   case RangeError:
625     cons = exec->lexicalGlobalObject()->rangeErrorConstructor();
626     break;
627   case ReferenceError:
628     cons = exec->lexicalGlobalObject()->referenceErrorConstructor();
629     break;
630   case SyntaxError:
631     cons = exec->lexicalGlobalObject()->syntaxErrorConstructor();
632     break;
633   case TypeError:
634     cons = exec->lexicalGlobalObject()->typeErrorConstructor();
635     break;
636   case URIError:
637     cons = exec->lexicalGlobalObject()->URIErrorConstructor();
638     break;
639   default:
640     cons = exec->lexicalGlobalObject()->errorConstructor();
641     break;
642   }
643
644   List args;
645   if (message.isEmpty())
646     args.append(jsString(errorNames[errtype]));
647   else
648     args.append(jsString(message));
649   JSObject *err = static_cast<JSObject *>(cons->construct(exec,args));
650
651   if (lineno != -1)
652     err->put(exec, "line", jsNumber(lineno));
653   if (sourceId != -1)
654     err->put(exec, "sourceId", jsNumber(sourceId));
655
656   if(!sourceURL.isNull())
657     err->put(exec, "sourceURL", jsString(sourceURL));
658  
659   return err;
660 }
661
662 JSObject *Error::create(ExecState *exec, ErrorType type, const char *message)
663 {
664     return create(exec, type, message, -1, -1, NULL);
665 }
666
667 JSObject *throwError(ExecState *exec, ErrorType type)
668 {
669     JSObject *error = Error::create(exec, type, UString(), -1, -1, NULL);
670     exec->setException(error);
671     return error;
672 }
673
674 JSObject *throwError(ExecState *exec, ErrorType type, const UString &message)
675 {
676     JSObject *error = Error::create(exec, type, message, -1, -1, NULL);
677     exec->setException(error);
678     return error;
679 }
680
681 JSObject *throwError(ExecState *exec, ErrorType type, const char *message)
682 {
683     JSObject *error = Error::create(exec, type, message, -1, -1, NULL);
684     exec->setException(error);
685     return error;
686 }
687
688 JSObject *throwError(ExecState *exec, ErrorType type, const UString &message, int line, int sourceId, const UString &sourceURL)
689 {
690     JSObject *error = Error::create(exec, type, message, line, sourceId, sourceURL);
691     exec->setException(error);
692     return error;
693 }
694
695 } // namespace KJS