Reviewed by Darin
[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 Apple Computer, Inc.
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., 59 Temple Place - Suite 330,
21  *  Boston, MA 02111-1307, USA.
22  *
23  */
24
25 #include "value.h"
26 #include "object.h"
27 #include "types.h"
28 #include "interpreter.h"
29 #include "lookup.h"
30 #include "reference_list.h"
31
32 #include <assert.h>
33 #include <math.h>
34 #include <stdio.h>
35
36 #include "internal.h"
37 #include "collector.h"
38 #include "operations.h"
39 #include "error_object.h"
40 #include "nodes.h"
41
42 namespace KJS {
43
44 // ------------------------------ Object ---------------------------------------
45
46 Object Object::dynamicCast(const Value &v)
47 {
48   if (v.isNull() || v.type() != ObjectType)
49     return Object(0);
50
51   return Object(static_cast<ObjectImp*>(v.imp()));
52 }
53
54
55 Value Object::call(ExecState *exec, Object &thisObj, const List &args)
56
57 #if KJS_MAX_STACK > 0
58   static int depth = 0; // sum of all concurrent interpreters
59   if (++depth > KJS_MAX_STACK) {
60     --depth;
61     Object err = Error::create(exec, RangeError,
62                                "Maximum call stack size exceeded.");
63     exec->setException(err);
64     return err;
65   }
66 #endif
67
68   Value ret = imp()->call(exec,thisObj,args); 
69
70 #if KJS_MAX_STACK > 0
71   --depth;
72 #endif
73
74   return ret;
75 }
76
77 // ------------------------------ ObjectImp ------------------------------------
78
79 ObjectImp::ObjectImp(const Object &proto)
80   : _proto(static_cast<ObjectImp*>(proto.imp())), _internalValue(0L)
81 {
82   //fprintf(stderr,"ObjectImp::ObjectImp %p\n",(void*)this);
83 }
84
85 ObjectImp::ObjectImp(ObjectImp *proto)
86   : _proto(proto), _internalValue(0L)
87 {
88   //fprintf(stderr,"ObjectImp::ObjectImp %p\n",(void*)this);
89 }
90
91 ObjectImp::ObjectImp()
92 {
93   //fprintf(stderr,"ObjectImp::ObjectImp %p\n",(void*)this);
94   _proto = NullImp::staticNull;
95   _internalValue = 0L;
96 }
97
98 ObjectImp::~ObjectImp()
99 {
100   //fprintf(stderr,"ObjectImp::~ObjectImp %p\n",(void*)this);
101 }
102
103 void ObjectImp::mark()
104 {
105   //fprintf(stderr,"ObjectImp::mark() %p\n",(void*)this);
106   ValueImp::mark();
107
108   if (_proto && !_proto->marked())
109     _proto->mark();
110
111   _prop.mark();
112
113   if (_internalValue && !_internalValue->marked())
114     _internalValue->mark();
115
116   _scope.mark();
117 }
118
119 const ClassInfo *ObjectImp::classInfo() const
120 {
121   return 0;
122 }
123
124 bool ObjectImp::inherits(const ClassInfo *info) const
125 {
126   if (!info)
127     return false;
128
129   const ClassInfo *ci = classInfo();
130   if (!ci)
131     return false;
132
133   while (ci && ci != info)
134     ci = ci->parentClass;
135
136   return (ci == info);
137 }
138
139 Type ObjectImp::type() const
140 {
141   return ObjectType;
142 }
143
144 Value ObjectImp::prototype() const
145 {
146   return Value(_proto);
147 }
148
149 void ObjectImp::setPrototype(const Value &proto)
150 {
151   _proto = proto.imp();
152 }
153
154 UString ObjectImp::className() const
155 {
156   const ClassInfo *ci = classInfo();
157   if ( ci )
158     return ci->className;
159   return "Object";
160 }
161
162 Value ObjectImp::get(ExecState *exec, const Identifier &propertyName) const
163 {
164   ValueImp *imp = getDirect(propertyName);
165   if (imp)
166     return Value(imp);
167
168   // non-standard netscape extension
169   if (propertyName == specialPrototypePropertyName)
170     return Value(_proto);
171
172   if (_proto->dispatchType() != ObjectType) {
173     return Undefined();
174   }
175
176   return static_cast<ObjectImp *>(_proto)->get(exec, propertyName);
177 }
178
179 Value ObjectImp::get(ExecState *exec, unsigned propertyName) const
180 {
181   return get(exec, Identifier::from(propertyName));
182 }
183
184 // ECMA 8.6.2.2
185 void ObjectImp::put(ExecState *exec, const Identifier &propertyName,
186                      const Value &value, int attr)
187 {
188   assert(!value.isNull());
189
190   // non-standard netscape extension
191   if (propertyName == specialPrototypePropertyName) {
192     setPrototype(value);
193     return;
194   }
195
196   /* TODO: check for write permissions directly w/o this call */
197   /* Doesn't look very easy with the PropertyMap API - David */
198   // putValue() is used for JS assignemnts. It passes no attribute.
199   // Assume that a C++ implementation knows what it is doing
200   // and let it override the canPut() check.
201   if ((attr == None || attr == DontDelete) && !canPut(exec,propertyName)) {
202 #ifdef KJS_VERBOSE
203     fprintf( stderr, "WARNING: canPut %s said NO\n", propertyName.ascii() );
204 #endif
205     return;
206   }
207
208   _prop.put(propertyName,value.imp(),attr);
209 }
210
211 void ObjectImp::put(ExecState *exec, unsigned propertyName,
212                      const Value &value, int attr)
213 {
214   put(exec, Identifier::from(propertyName), value, attr);
215 }
216
217 // ECMA 8.6.2.3
218 bool ObjectImp::canPut(ExecState *, const Identifier &propertyName) const
219 {
220   int attributes;
221   ValueImp *v = _prop.get(propertyName, attributes);
222   if (v)
223     return!(attributes & ReadOnly);
224
225   // Look in the static hashtable of properties
226   const HashEntry* e = findPropertyHashEntry(propertyName);
227   if (e)
228     return !(e->attr & ReadOnly);
229
230   // Don't look in the prototype here. We can always put an override
231   // in the object, even if the prototype has a ReadOnly property.
232   return true;
233 }
234
235 // ECMA 8.6.2.4
236 bool ObjectImp::hasProperty(ExecState *exec, const Identifier &propertyName) const
237 {
238   if (_prop.get(propertyName))
239     return true;
240
241   // Look in the static hashtable of properties
242   if (findPropertyHashEntry(propertyName))
243       return true;
244
245   // non-standard netscape extension
246   if (propertyName == specialPrototypePropertyName)
247     return true;
248
249   if (_proto->dispatchType() != ObjectType) {
250     return false;
251   }
252
253   // Look in the prototype
254   return static_cast<ObjectImp *>(_proto)->hasProperty(exec, propertyName);
255 }
256
257 bool ObjectImp::hasProperty(ExecState *exec, unsigned propertyName) const
258 {
259   return hasProperty(exec, Identifier::from(propertyName));
260 }
261
262 // ECMA 8.6.2.5
263 bool ObjectImp::deleteProperty(ExecState */*exec*/, const Identifier &propertyName)
264 {
265   int attributes;
266   ValueImp *v = _prop.get(propertyName, attributes);
267   if (v) {
268     if ((attributes & DontDelete))
269       return false;
270     _prop.remove(propertyName);
271     return true;
272   }
273
274   // Look in the static hashtable of properties
275   const HashEntry* entry = findPropertyHashEntry(propertyName);
276   if (entry && entry->attr & DontDelete)
277     return false; // this builtin property can't be deleted
278   return true;
279 }
280
281 bool ObjectImp::deleteProperty(ExecState *exec, unsigned propertyName)
282 {
283   return deleteProperty(exec, Identifier::from(propertyName));
284 }
285
286 void ObjectImp::deleteAllProperties( ExecState * )
287 {
288   _prop.clear();
289 }
290
291 // ECMA 8.6.2.6
292 Value ObjectImp::defaultValue(ExecState *exec, Type hint) const
293 {
294   if (hint != StringType && hint != NumberType) {
295     /* Prefer String for Date objects */
296     if (_proto == exec->lexicalInterpreter()->builtinDatePrototype().imp())
297       hint = StringType;
298     else
299       hint = NumberType;
300   }
301
302   Value v;
303   if (hint == StringType)
304     v = get(exec,toStringPropertyName);
305   else
306     v = get(exec,valueOfPropertyName);
307
308   if (v.type() == ObjectType) {
309     Object o = Object(static_cast<ObjectImp*>(v.imp()));
310     if (o.implementsCall()) { // spec says "not primitive type" but ...
311       Object thisObj = Object(const_cast<ObjectImp*>(this));
312       Value def = o.call(exec,thisObj,List::empty());
313       Type defType = def.type();
314       if (defType == UnspecifiedType || defType == UndefinedType ||
315           defType == NullType || defType == BooleanType ||
316           defType == StringType || defType == NumberType) {
317         return def;
318       }
319     }
320   }
321
322   if (hint == StringType)
323     v = get(exec,valueOfPropertyName);
324   else
325     v = get(exec,toStringPropertyName);
326
327   if (v.type() == ObjectType) {
328     Object o = Object(static_cast<ObjectImp*>(v.imp()));
329     if (o.implementsCall()) { // spec says "not primitive type" but ...
330       Object thisObj = Object(const_cast<ObjectImp*>(this));
331       Value def = o.call(exec,thisObj,List::empty());
332       Type defType = def.type();
333       if (defType == UnspecifiedType || defType == UndefinedType ||
334           defType == NullType || defType == BooleanType ||
335           defType == StringType || defType == NumberType) {
336         return def;
337       }
338     }
339   }
340
341   Object err = Error::create(exec, TypeError, I18N_NOOP("No default value"));
342   exec->setException(err);
343   return err;
344 }
345
346 const HashEntry* ObjectImp::findPropertyHashEntry( const Identifier& propertyName ) const
347 {
348   const ClassInfo *info = classInfo();
349   while (info) {
350     if (info->propHashTable) {
351       const HashEntry *e = Lookup::findEntry(info->propHashTable, propertyName);
352       if (e)
353         return e;
354     }
355     info = info->parentClass;
356   }
357   return 0L;
358 }
359
360 bool ObjectImp::implementsConstruct() const
361 {
362   return false;
363 }
364
365 Object ObjectImp::construct(ExecState */*exec*/, const List &/*args*/)
366 {
367   assert(false);
368   return Object(0);
369 }
370
371 bool ObjectImp::implementsCall() const
372 {
373   return false;
374 }
375
376 Value ObjectImp::call(ExecState */*exec*/, Object &/*thisObj*/, const List &/*args*/)
377 {
378   assert(false);
379   return Object(0);
380 }
381
382 bool ObjectImp::implementsHasInstance() const
383 {
384   return false;
385 }
386
387 Boolean ObjectImp::hasInstance(ExecState */*exec*/, const Value &/*value*/)
388 {
389   assert(false);
390   return Boolean(false);
391 }
392
393 ReferenceList ObjectImp::propList(ExecState *exec, bool recursive)
394 {
395   ReferenceList list;
396   if (_proto && _proto->dispatchType() == ObjectType && recursive)
397     list = static_cast<ObjectImp*>(_proto)->propList(exec,recursive);
398
399   _prop.addEnumerablesToReferenceList(list, Object(this));
400
401   // Add properties from the static hashtable of properties
402   const ClassInfo *info = classInfo();
403   while (info) {
404     if (info->propHashTable) {
405       int size = info->propHashTable->size;
406       const HashEntry *e = info->propHashTable->entries;
407       for (int i = 0; i < size; ++i, ++e) {
408         if ( e->s && !(e->attr & DontEnum) )
409           list.append(Reference(this, e->s)); /// ######### check for duplicates with the propertymap
410       }
411     }
412     info = info->parentClass;
413   }
414
415   return list;
416 }
417
418 Value ObjectImp::internalValue() const
419 {
420   return Value(_internalValue);
421 }
422
423 void ObjectImp::setInternalValue(const Value &v)
424 {
425   _internalValue = v.imp();
426 }
427
428 void ObjectImp::setInternalValue(ValueImp *v)
429 {
430 #if !USE_CONSERVATIVE_GC
431   v->setGcAllowed();
432 #endif
433   _internalValue = v;
434 }
435
436 Value ObjectImp::toPrimitive(ExecState *exec, Type preferredType) const
437 {
438   return defaultValue(exec,preferredType);
439 }
440
441 bool ObjectImp::toBoolean(ExecState */*exec*/) const
442 {
443   return true;
444 }
445
446 double ObjectImp::toNumber(ExecState *exec) const
447 {
448   Value prim = toPrimitive(exec,NumberType);
449   if (exec->hadException()) // should be picked up soon in nodes.cpp
450     return 0.0;
451   return prim.toNumber(exec);
452 }
453
454 UString ObjectImp::toString(ExecState *exec) const
455 {
456   Value prim = toPrimitive(exec,StringType);
457   if (exec->hadException()) // should be picked up soon in nodes.cpp
458     return "";
459   return prim.toString(exec);
460 }
461
462 Object ObjectImp::toObject(ExecState */*exec*/) const
463 {
464   return Object(const_cast<ObjectImp*>(this));
465 }
466
467 void ObjectImp::putDirect(const Identifier &propertyName, ValueImp *value, int attr)
468 {
469 #if !USE_CONSERVATIVE_GC
470     value->setGcAllowed();
471 #endif
472     _prop.put(propertyName, value, attr);
473 }
474
475 void ObjectImp::putDirect(const Identifier &propertyName, int value, int attr)
476 {
477     _prop.put(propertyName, NumberImp::create(value), attr);
478 }
479
480 // ------------------------------ Error ----------------------------------------
481
482 const char * const errorNamesArr[] = {
483   I18N_NOOP("Error"), // GeneralError
484   I18N_NOOP("Evaluation error"), // EvalError
485   I18N_NOOP("Range error"), // RangeError
486   I18N_NOOP("Reference error"), // ReferenceError
487   I18N_NOOP("Syntax error"), // SyntaxError
488   I18N_NOOP("Type error"), // TypeError
489   I18N_NOOP("URI error"), // URIError
490 };
491
492 const char * const * const Error::errorNames = errorNamesArr;
493
494 Object Error::create(ExecState *exec, ErrorType errtype, const char *message,
495                      int lineno, int sourceId, const UString *sourceURL)
496 {
497   Object cons;
498   switch (errtype) {
499   case EvalError:
500     cons = exec->lexicalInterpreter()->builtinEvalError();
501     break;
502   case RangeError:
503     cons = exec->lexicalInterpreter()->builtinRangeError();
504     break;
505   case ReferenceError:
506     cons = exec->lexicalInterpreter()->builtinReferenceError();
507     break;
508   case SyntaxError:
509     cons = exec->lexicalInterpreter()->builtinSyntaxError();
510     break;
511   case TypeError:
512     cons = exec->lexicalInterpreter()->builtinTypeError();
513     break;
514   case URIError:
515     cons = exec->lexicalInterpreter()->builtinURIError();
516     break;
517   default:
518     cons = exec->lexicalInterpreter()->builtinError();
519     break;
520   }
521
522   if (!message)
523     message = errorNames[errtype];
524   List args;
525   args.append(String(message));
526   Object err = Object::dynamicCast(cons.construct(exec,args));
527
528   if (lineno != -1)
529     err.put(exec, "line", Number(lineno));
530   if (sourceId != -1)
531     err.put(exec, "sourceId", Number(sourceId));
532
533   if(sourceURL)
534    err.put(exec,"sourceURL", String(*sourceURL));
535  
536   return err;
537
538 /*
539 #ifndef NDEBUG
540   const char *msg = err.get("message").toString().value().ascii();
541   if (l >= 0)
542       fprintf(stderr, "KJS: %s at line %d. %s\n", estr, l, msg);
543   else
544       fprintf(stderr, "KJS: %s. %s\n", estr, msg);
545 #endif
546
547   return err;
548 */
549 }
550
551 } // namespace KJS