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