source: webkit/trunk/JavaScriptCore/kjs/object.cpp@ 26690

Last change on this file since 26690 was 26690, checked in by ggaren, 18 years ago

Reviewed by Darin Adler.

Removed KJS_VERBOSE because it was getting in the way of readability,
and the messages didn't seem very helpful.

  • kjs/function.cpp: (KJS::FunctionImp::callAsFunction): (KJS::FunctionImp::passInParameters):
  • kjs/lookup.h: (KJS::lookupPut):
  • kjs/object.cpp: (KJS::JSObject::put):
  • kjs/value.h:
  • Property svn:eol-style set to native
File size: 18.1 KB
Line 
1// -*- c-basic-offset: 2 -*-
2/*
3 * This file is part of the KDE libraries
4 * Copyright (C) 1999-2001 Harri Porten ([email protected])
5 * Copyright (C) 2001 Peter Kelly ([email protected])
6 * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc.
7 * Copyright (C) 2007 Eric Seidel ([email protected])
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 "error_object.h"
30#include "lookup.h"
31#include "nodes.h"
32#include "operations.h"
33#include "PropertyNameArray.h"
34#include <math.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
47static bool _traceJavaScript = false;
48
49extern "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
62namespace KJS {
63
64// ------------------------------ Object ---------------------------------------
65
66JSValue *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 concurrent interpreters
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 JSValue *ret = callAsFunction(exec,thisObj,args);
96
97#if KJS_MAX_STACK > 0
98 --depth;
99#endif
100
101#if JAVASCRIPT_CALL_TRACING
102 if (traceJavaScript() && !tracing) {
103 tracing = true;
104 for (int i = 0; i < depth; i++)
105 putchar (' ');
106 printf ("*** returning: %s\n", ret->toString(exec).ascii());
107 tracing = false;
108 }
109#endif
110
111 return ret;
112}
113
114// ------------------------------ JSObject ------------------------------------
115
116void JSObject::mark()
117{
118 JSCell::mark();
119
120#if JAVASCRIPT_MARK_TRACING
121 static int markStackDepth = 0;
122 markStackDepth++;
123 for (int i = 0; i < markStackDepth; i++)
124 putchar('-');
125
126 printf("%s (%p)\n", className().UTF8String().c_str(), this);
127#endif
128
129 JSValue *proto = _proto;
130 if (!proto->marked())
131 proto->mark();
132
133 _prop.mark();
134
135#if JAVASCRIPT_MARK_TRACING
136 markStackDepth--;
137#endif
138}
139
140JSType JSObject::type() const
141{
142 return ObjectType;
143}
144
145const ClassInfo *JSObject::classInfo() const
146{
147 return 0;
148}
149
150UString JSObject::className() const
151{
152 const ClassInfo *ci = classInfo();
153 if ( ci )
154 return ci->className;
155 return "Object";
156}
157
158JSValue *JSObject::get(ExecState *exec, const Identifier &propertyName) const
159{
160 PropertySlot slot;
161
162 if (const_cast<JSObject *>(this)->getPropertySlot(exec, propertyName, slot))
163 return slot.getValue(exec, const_cast<JSObject *>(this), propertyName);
164
165 return jsUndefined();
166}
167
168JSValue *JSObject::get(ExecState *exec, unsigned propertyName) const
169{
170 PropertySlot slot;
171 if (const_cast<JSObject *>(this)->getPropertySlot(exec, propertyName, slot))
172 return slot.getValue(exec, const_cast<JSObject *>(this), propertyName);
173
174 return jsUndefined();
175}
176
177bool JSObject::getPropertySlot(ExecState *exec, unsigned propertyName, PropertySlot& slot)
178{
179 JSObject *imp = this;
180
181 while (true) {
182 if (imp->getOwnPropertySlot(exec, propertyName, slot))
183 return true;
184
185 JSValue *proto = imp->_proto;
186 if (!proto->isObject())
187 break;
188
189 imp = static_cast<JSObject *>(proto);
190 }
191
192 return false;
193}
194
195bool JSObject::getOwnPropertySlot(ExecState *exec, unsigned propertyName, PropertySlot& slot)
196{
197 return getOwnPropertySlot(exec, Identifier::from(propertyName), slot);
198}
199
200static void throwSetterError(ExecState *exec)
201{
202 throwError(exec, TypeError, "setting a property that has only a getter");
203}
204
205// ECMA 8.6.2.2
206void JSObject::put(ExecState* exec, const Identifier &propertyName, JSValue *value, int attr)
207{
208 ASSERT(value);
209
210 // non-standard netscape extension
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 /* TODO: check for write permissions directly w/o this call */
224 /* Doesn't look very easy with the PropertyMap API - David */
225 // putValue() is used for JS assignemnts. It passes no attribute.
226 // Assume that a C++ implementation knows what it is doing
227 // and let it override the canPut() check.
228 if ((attr == None || attr == DontDelete) && !canPut(exec,propertyName)) {
229 return;
230 }
231
232 // Check if there are any setters or getters in the prototype chain
233 JSObject *obj = this;
234 bool hasGettersOrSetters = false;
235 while (true) {
236 if (obj->_prop.hasGetterSetterProperties()) {
237 hasGettersOrSetters = true;
238 break;
239 }
240
241 if (!obj->_proto->isObject())
242 break;
243
244 obj = static_cast<JSObject *>(obj->_proto);
245 }
246
247 if (hasGettersOrSetters) {
248 obj = this;
249 while (true) {
250 unsigned attributes;
251 if (JSValue *gs = obj->_prop.get(propertyName, attributes)) {
252 if (attributes & GetterSetter) {
253 JSObject *setterFunc = static_cast<GetterSetterImp *>(gs)->getSetter();
254
255 if (!setterFunc) {
256 throwSetterError(exec);
257 return;
258 }
259
260 List args;
261 args.append(value);
262
263 setterFunc->call(exec, this, args);
264 return;
265 } else {
266 // If there's an existing property on the object or one of its
267 // prototype it should be replaced, so we just break here.
268 break;
269 }
270 }
271
272 if (!obj->_proto->isObject())
273 break;
274
275 obj = static_cast<JSObject *>(obj->_proto);
276 }
277 }
278
279 _prop.put(propertyName,value,attr);
280}
281
282void JSObject::put(ExecState *exec, unsigned propertyName,
283 JSValue *value, int attr)
284{
285 put(exec, Identifier::from(propertyName), value, attr);
286}
287
288// ECMA 8.6.2.3
289bool JSObject::canPut(ExecState *, const Identifier &propertyName) const
290{
291 unsigned attributes;
292
293 // Don't look in the prototype here. We can always put an override
294 // in the object, even if the prototype has a ReadOnly property.
295
296 if (!getPropertyAttributes(propertyName, attributes))
297 return true;
298 else
299 return !(attributes & ReadOnly);
300}
301
302// ECMA 8.6.2.4
303bool JSObject::hasProperty(ExecState *exec, const Identifier &propertyName) const
304{
305 PropertySlot slot;
306 return const_cast<JSObject *>(this)->getPropertySlot(exec, propertyName, slot);
307}
308
309bool 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
316bool 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
336bool JSObject::deleteProperty(ExecState *exec, unsigned propertyName)
337{
338 return deleteProperty(exec, Identifier::from(propertyName));
339}
340
341static ALWAYS_INLINE JSValue *tryGetAndCallProperty(ExecState *exec, const JSObject *object, const Identifier &propertyName) {
342 JSValue *v = object->get(exec, propertyName);
343 if (v->isObject()) {
344 JSObject *o = static_cast<JSObject*>(v);
345 if (o->implementsCall()) { // spec says "not primitive type" but ...
346 JSObject *thisObj = const_cast<JSObject*>(object);
347 JSValue *def = o->call(exec, thisObj, List::empty());
348 JSType defType = def->type();
349 ASSERT(defType != GetterSetterType);
350 if (defType != ObjectType)
351 return def;
352 }
353 }
354 return NULL;
355}
356
357// ECMA 8.6.2.6
358JSValue* JSObject::defaultValue(ExecState* exec, JSType hint) const
359{
360 Identifier firstPropertyName;
361 Identifier secondPropertyName;
362 /* Prefer String for Date objects */
363 if ((hint == StringType) || (hint != StringType) && (hint != NumberType) && (_proto == exec->lexicalInterpreter()->builtinDatePrototype())) {
364 firstPropertyName = exec->propertyNames().toString;
365 secondPropertyName = exec->propertyNames().valueOf;
366 } else {
367 firstPropertyName = exec->propertyNames().valueOf;
368 secondPropertyName = exec->propertyNames().toString;
369 }
370
371 JSValue *v;
372 if ((v = tryGetAndCallProperty(exec, this, firstPropertyName)))
373 return v;
374 if ((v = tryGetAndCallProperty(exec, this, secondPropertyName)))
375 return v;
376
377 if (exec->hadException())
378 return exec->exception();
379
380 return throwError(exec, TypeError, "No default value");
381}
382
383const HashEntry* JSObject::findPropertyHashEntry(const Identifier& propertyName) const
384{
385 for (const ClassInfo *info = classInfo(); info; info = info->parentClass) {
386 if (const HashTable *propHashTable = info->propHashTable) {
387 if (const HashEntry *e = Lookup::findEntry(propHashTable, propertyName))
388 return e;
389 }
390 }
391 return 0;
392}
393
394void JSObject::defineGetter(ExecState*, const Identifier& propertyName, JSObject* getterFunc)
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->setGetter(getterFunc);
408}
409
410void JSObject::defineSetter(ExecState*, const Identifier& propertyName, JSObject* setterFunc)
411{
412 JSValue *o = getDirect(propertyName);
413 GetterSetterImp *gs;
414
415 if (o && o->type() == GetterSetterType) {
416 gs = static_cast<GetterSetterImp *>(o);
417 } else {
418 gs = new GetterSetterImp;
419 putDirect(propertyName, gs, GetterSetter);
420 }
421
422 _prop.setHasGetterSetterProperties(true);
423 gs->setSetter(setterFunc);
424}
425
426bool JSObject::implementsConstruct() const
427{
428 return false;
429}
430
431JSObject* JSObject::construct(ExecState*, const List& /*args*/)
432{
433 ASSERT(false);
434 return NULL;
435}
436
437JSObject* JSObject::construct(ExecState* exec, const List& args, const Identifier& /*functionName*/, const UString& /*sourceURL*/, int /*lineNumber*/)
438{
439 return construct(exec, args);
440}
441
442bool JSObject::implementsCall() const
443{
444 return false;
445}
446
447JSValue *JSObject::callAsFunction(ExecState* /*exec*/, JSObject* /*thisObj*/, const List &/*args*/)
448{
449 ASSERT(false);
450 return NULL;
451}
452
453bool JSObject::implementsHasInstance() const
454{
455 return false;
456}
457
458bool JSObject::hasInstance(ExecState* exec, JSValue* value)
459{
460 JSValue* proto = get(exec, exec->propertyNames().prototype);
461 if (!proto->isObject()) {
462 throwError(exec, TypeError, "intanceof called on an object with an invalid prototype property.");
463 return false;
464 }
465
466 if (!value->isObject())
467 return false;
468
469 JSObject* o = static_cast<JSObject*>(value);
470 while ((o = o->prototype()->getObject())) {
471 if (o == proto)
472 return true;
473 }
474 return false;
475}
476
477bool JSObject::propertyIsEnumerable(ExecState*, const Identifier& propertyName) const
478{
479 unsigned attributes;
480
481 if (!getPropertyAttributes(propertyName, attributes))
482 return false;
483 else
484 return !(attributes & DontEnum);
485}
486
487bool JSObject::getPropertyAttributes(const Identifier& propertyName, unsigned& attributes) const
488{
489 if (_prop.get(propertyName, attributes))
490 return true;
491
492 // Look in the static hashtable of properties
493 const HashEntry* e = findPropertyHashEntry(propertyName);
494 if (e) {
495 attributes = e->attr;
496 return true;
497 }
498
499 return false;
500}
501
502void JSObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
503{
504 _prop.getEnumerablePropertyNames(propertyNames);
505
506 // Add properties from the static hashtable of properties
507 const ClassInfo *info = classInfo();
508 while (info) {
509 if (info->propHashTable) {
510 int size = info->propHashTable->size;
511 const HashEntry *e = info->propHashTable->entries;
512 for (int i = 0; i < size; ++i, ++e) {
513 if (e->s && !(e->attr & DontEnum))
514 propertyNames.add(e->s);
515 }
516 }
517 info = info->parentClass;
518 }
519 if (_proto->isObject())
520 static_cast<JSObject*>(_proto)->getPropertyNames(exec, propertyNames);
521}
522
523bool JSObject::toBoolean(ExecState*) const
524{
525 return true;
526}
527
528double JSObject::toNumber(ExecState *exec) const
529{
530 JSValue *prim = toPrimitive(exec,NumberType);
531 if (exec->hadException()) // should be picked up soon in nodes.cpp
532 return 0.0;
533 return prim->toNumber(exec);
534}
535
536UString JSObject::toString(ExecState *exec) const
537{
538 JSValue *prim = toPrimitive(exec,StringType);
539 if (exec->hadException()) // should be picked up soon in nodes.cpp
540 return "";
541 return prim->toString(exec);
542}
543
544JSObject *JSObject::toObject(ExecState*) const
545{
546 return const_cast<JSObject*>(this);
547}
548
549void JSObject::putDirect(const Identifier &propertyName, JSValue *value, int attr)
550{
551 _prop.put(propertyName, value, attr);
552}
553
554void JSObject::putDirect(const Identifier &propertyName, int value, int attr)
555{
556 _prop.put(propertyName, jsNumber(value), attr);
557}
558
559void JSObject::removeDirect(const Identifier &propertyName)
560{
561 _prop.remove(propertyName);
562}
563
564void JSObject::putDirectFunction(InternalFunctionImp* func, int attr)
565{
566 putDirect(func->functionName(), func, attr);
567}
568
569void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSValue **location)
570{
571 GetterSetterImp *gs = static_cast<GetterSetterImp *>(*location);
572 JSObject *getterFunc = gs->getGetter();
573 if (getterFunc)
574 slot.setGetterSlot(this, getterFunc);
575 else
576 slot.setUndefined(this);
577}
578
579// ------------------------------ Error ----------------------------------------
580
581const char * const errorNamesArr[] = {
582 I18N_NOOP("Error"), // GeneralError
583 I18N_NOOP("Evaluation error"), // EvalError
584 I18N_NOOP("Range error"), // RangeError
585 I18N_NOOP("Reference error"), // ReferenceError
586 I18N_NOOP("Syntax error"), // SyntaxError
587 I18N_NOOP("Type error"), // TypeError
588 I18N_NOOP("URI error"), // URIError
589};
590
591const char * const * const Error::errorNames = errorNamesArr;
592
593JSObject *Error::create(ExecState *exec, ErrorType errtype, const UString &message,
594 int lineno, int sourceId, const UString &sourceURL)
595{
596 JSObject *cons;
597 switch (errtype) {
598 case EvalError:
599 cons = exec->lexicalInterpreter()->builtinEvalError();
600 break;
601 case RangeError:
602 cons = exec->lexicalInterpreter()->builtinRangeError();
603 break;
604 case ReferenceError:
605 cons = exec->lexicalInterpreter()->builtinReferenceError();
606 break;
607 case SyntaxError:
608 cons = exec->lexicalInterpreter()->builtinSyntaxError();
609 break;
610 case TypeError:
611 cons = exec->lexicalInterpreter()->builtinTypeError();
612 break;
613 case URIError:
614 cons = exec->lexicalInterpreter()->builtinURIError();
615 break;
616 default:
617 cons = exec->lexicalInterpreter()->builtinError();
618 break;
619 }
620
621 List args;
622 if (message.isEmpty())
623 args.append(jsString(errorNames[errtype]));
624 else
625 args.append(jsString(message));
626 JSObject *err = static_cast<JSObject *>(cons->construct(exec,args));
627
628 if (lineno != -1)
629 err->put(exec, "line", jsNumber(lineno));
630 if (sourceId != -1)
631 err->put(exec, "sourceId", jsNumber(sourceId));
632
633 if(!sourceURL.isNull())
634 err->put(exec, "sourceURL", jsString(sourceURL));
635
636 return err;
637
638/*
639#ifndef NDEBUG
640 const char *msg = err->get(messagePropertyName)->toString().value().ascii();
641 if (l >= 0)
642 fprintf(stderr, "KJS: %s at line %d. %s\n", estr, l, msg);
643 else
644 fprintf(stderr, "KJS: %s. %s\n", estr, msg);
645#endif
646
647 return err;
648*/
649}
650
651JSObject *Error::create(ExecState *exec, ErrorType type, const char *message)
652{
653 return create(exec, type, message, -1, -1, NULL);
654}
655
656JSObject *throwError(ExecState *exec, ErrorType type)
657{
658 JSObject *error = Error::create(exec, type, UString(), -1, -1, NULL);
659 exec->setException(error);
660 return error;
661}
662
663JSObject *throwError(ExecState *exec, ErrorType type, const UString &message)
664{
665 JSObject *error = Error::create(exec, type, message, -1, -1, NULL);
666 exec->setException(error);
667 return error;
668}
669
670JSObject *throwError(ExecState *exec, ErrorType type, const char *message)
671{
672 JSObject *error = Error::create(exec, type, message, -1, -1, NULL);
673 exec->setException(error);
674 return error;
675}
676
677JSObject *throwError(ExecState *exec, ErrorType type, const UString &message, int line, int sourceId, const UString &sourceURL)
678{
679 JSObject *error = Error::create(exec, type, message, line, sourceId, sourceURL);
680 exec->setException(error);
681 return error;
682}
683
684} // namespace KJS
Note: See TracBrowser for help on using the repository browser.