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