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

Last change on this file since 27031 was 26959, checked in by darin, 18 years ago

Reviewed by Eric.

  • kjs/object.cpp: (KJS::JSObject::defaultValue): Get rid of a little Identifier ref/deref for what SunSpider claims is a 0.4% speedup.
  • 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 /* Prefer String for Date objects */
361 if ((hint == StringType) || (hint != StringType) && (hint != NumberType) && (_proto == exec->lexicalInterpreter()->builtinDatePrototype())) {
362 if (JSValue* v = tryGetAndCallProperty(exec, this, exec->propertyNames().toString))
363 return v;
364 if (JSValue* v = tryGetAndCallProperty(exec, this, exec->propertyNames().valueOf))
365 return v;
366 } else {
367 if (JSValue* v = tryGetAndCallProperty(exec, this, exec->propertyNames().valueOf))
368 return v;
369 if (JSValue* v = tryGetAndCallProperty(exec, this, exec->propertyNames().toString))
370 return v;
371 }
372
373 if (exec->hadException())
374 return exec->exception();
375
376 return throwError(exec, TypeError, "No default value");
377}
378
379const HashEntry* JSObject::findPropertyHashEntry(const Identifier& propertyName) const
380{
381 for (const ClassInfo *info = classInfo(); info; info = info->parentClass) {
382 if (const HashTable *propHashTable = info->propHashTable) {
383 if (const HashEntry *e = Lookup::findEntry(propHashTable, propertyName))
384 return e;
385 }
386 }
387 return 0;
388}
389
390void JSObject::defineGetter(ExecState*, const Identifier& propertyName, JSObject* getterFunc)
391{
392 JSValue *o = getDirect(propertyName);
393 GetterSetterImp *gs;
394
395 if (o && o->type() == GetterSetterType) {
396 gs = static_cast<GetterSetterImp *>(o);
397 } else {
398 gs = new GetterSetterImp;
399 putDirect(propertyName, gs, GetterSetter);
400 }
401
402 _prop.setHasGetterSetterProperties(true);
403 gs->setGetter(getterFunc);
404}
405
406void JSObject::defineSetter(ExecState*, const Identifier& propertyName, JSObject* setterFunc)
407{
408 JSValue *o = getDirect(propertyName);
409 GetterSetterImp *gs;
410
411 if (o && o->type() == GetterSetterType) {
412 gs = static_cast<GetterSetterImp *>(o);
413 } else {
414 gs = new GetterSetterImp;
415 putDirect(propertyName, gs, GetterSetter);
416 }
417
418 _prop.setHasGetterSetterProperties(true);
419 gs->setSetter(setterFunc);
420}
421
422bool JSObject::implementsConstruct() const
423{
424 return false;
425}
426
427JSObject* JSObject::construct(ExecState*, const List& /*args*/)
428{
429 ASSERT(false);
430 return NULL;
431}
432
433JSObject* JSObject::construct(ExecState* exec, const List& args, const Identifier& /*functionName*/, const UString& /*sourceURL*/, int /*lineNumber*/)
434{
435 return construct(exec, args);
436}
437
438bool JSObject::implementsCall() const
439{
440 return false;
441}
442
443JSValue *JSObject::callAsFunction(ExecState* /*exec*/, JSObject* /*thisObj*/, const List &/*args*/)
444{
445 ASSERT(false);
446 return NULL;
447}
448
449bool JSObject::implementsHasInstance() const
450{
451 return false;
452}
453
454bool JSObject::hasInstance(ExecState* exec, JSValue* value)
455{
456 JSValue* proto = get(exec, exec->propertyNames().prototype);
457 if (!proto->isObject()) {
458 throwError(exec, TypeError, "intanceof called on an object with an invalid prototype property.");
459 return false;
460 }
461
462 if (!value->isObject())
463 return false;
464
465 JSObject* o = static_cast<JSObject*>(value);
466 while ((o = o->prototype()->getObject())) {
467 if (o == proto)
468 return true;
469 }
470 return false;
471}
472
473bool JSObject::propertyIsEnumerable(ExecState*, const Identifier& propertyName) const
474{
475 unsigned attributes;
476
477 if (!getPropertyAttributes(propertyName, attributes))
478 return false;
479 else
480 return !(attributes & DontEnum);
481}
482
483bool JSObject::getPropertyAttributes(const Identifier& propertyName, unsigned& attributes) const
484{
485 if (_prop.get(propertyName, attributes))
486 return true;
487
488 // Look in the static hashtable of properties
489 const HashEntry* e = findPropertyHashEntry(propertyName);
490 if (e) {
491 attributes = e->attr;
492 return true;
493 }
494
495 return false;
496}
497
498void JSObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
499{
500 _prop.getEnumerablePropertyNames(propertyNames);
501
502 // Add properties from the static hashtable of properties
503 const ClassInfo *info = classInfo();
504 while (info) {
505 if (info->propHashTable) {
506 int size = info->propHashTable->size;
507 const HashEntry *e = info->propHashTable->entries;
508 for (int i = 0; i < size; ++i, ++e) {
509 if (e->s && !(e->attr & DontEnum))
510 propertyNames.add(e->s);
511 }
512 }
513 info = info->parentClass;
514 }
515 if (_proto->isObject())
516 static_cast<JSObject*>(_proto)->getPropertyNames(exec, propertyNames);
517}
518
519bool JSObject::toBoolean(ExecState*) const
520{
521 return true;
522}
523
524double JSObject::toNumber(ExecState *exec) const
525{
526 JSValue *prim = toPrimitive(exec,NumberType);
527 if (exec->hadException()) // should be picked up soon in nodes.cpp
528 return 0.0;
529 return prim->toNumber(exec);
530}
531
532UString JSObject::toString(ExecState *exec) const
533{
534 JSValue *prim = toPrimitive(exec,StringType);
535 if (exec->hadException()) // should be picked up soon in nodes.cpp
536 return "";
537 return prim->toString(exec);
538}
539
540JSObject *JSObject::toObject(ExecState*) const
541{
542 return const_cast<JSObject*>(this);
543}
544
545void JSObject::putDirect(const Identifier &propertyName, JSValue *value, int attr)
546{
547 _prop.put(propertyName, value, attr);
548}
549
550void JSObject::putDirect(const Identifier &propertyName, int value, int attr)
551{
552 _prop.put(propertyName, jsNumber(value), attr);
553}
554
555void JSObject::removeDirect(const Identifier &propertyName)
556{
557 _prop.remove(propertyName);
558}
559
560void JSObject::putDirectFunction(InternalFunctionImp* func, int attr)
561{
562 putDirect(func->functionName(), func, attr);
563}
564
565void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSValue **location)
566{
567 GetterSetterImp *gs = static_cast<GetterSetterImp *>(*location);
568 JSObject *getterFunc = gs->getGetter();
569 if (getterFunc)
570 slot.setGetterSlot(this, getterFunc);
571 else
572 slot.setUndefined(this);
573}
574
575// ------------------------------ Error ----------------------------------------
576
577const char * const errorNamesArr[] = {
578 I18N_NOOP("Error"), // GeneralError
579 I18N_NOOP("Evaluation error"), // EvalError
580 I18N_NOOP("Range error"), // RangeError
581 I18N_NOOP("Reference error"), // ReferenceError
582 I18N_NOOP("Syntax error"), // SyntaxError
583 I18N_NOOP("Type error"), // TypeError
584 I18N_NOOP("URI error"), // URIError
585};
586
587const char * const * const Error::errorNames = errorNamesArr;
588
589JSObject *Error::create(ExecState *exec, ErrorType errtype, const UString &message,
590 int lineno, int sourceId, const UString &sourceURL)
591{
592 JSObject *cons;
593 switch (errtype) {
594 case EvalError:
595 cons = exec->lexicalInterpreter()->builtinEvalError();
596 break;
597 case RangeError:
598 cons = exec->lexicalInterpreter()->builtinRangeError();
599 break;
600 case ReferenceError:
601 cons = exec->lexicalInterpreter()->builtinReferenceError();
602 break;
603 case SyntaxError:
604 cons = exec->lexicalInterpreter()->builtinSyntaxError();
605 break;
606 case TypeError:
607 cons = exec->lexicalInterpreter()->builtinTypeError();
608 break;
609 case URIError:
610 cons = exec->lexicalInterpreter()->builtinURIError();
611 break;
612 default:
613 cons = exec->lexicalInterpreter()->builtinError();
614 break;
615 }
616
617 List args;
618 if (message.isEmpty())
619 args.append(jsString(errorNames[errtype]));
620 else
621 args.append(jsString(message));
622 JSObject *err = static_cast<JSObject *>(cons->construct(exec,args));
623
624 if (lineno != -1)
625 err->put(exec, "line", jsNumber(lineno));
626 if (sourceId != -1)
627 err->put(exec, "sourceId", jsNumber(sourceId));
628
629 if(!sourceURL.isNull())
630 err->put(exec, "sourceURL", jsString(sourceURL));
631
632 return err;
633
634/*
635#ifndef NDEBUG
636 const char *msg = err->get(messagePropertyName)->toString().value().ascii();
637 if (l >= 0)
638 fprintf(stderr, "KJS: %s at line %d. %s\n", estr, l, msg);
639 else
640 fprintf(stderr, "KJS: %s. %s\n", estr, msg);
641#endif
642
643 return err;
644*/
645}
646
647JSObject *Error::create(ExecState *exec, ErrorType type, const char *message)
648{
649 return create(exec, type, message, -1, -1, NULL);
650}
651
652JSObject *throwError(ExecState *exec, ErrorType type)
653{
654 JSObject *error = Error::create(exec, type, UString(), -1, -1, NULL);
655 exec->setException(error);
656 return error;
657}
658
659JSObject *throwError(ExecState *exec, ErrorType type, const UString &message)
660{
661 JSObject *error = Error::create(exec, type, message, -1, -1, NULL);
662 exec->setException(error);
663 return error;
664}
665
666JSObject *throwError(ExecState *exec, ErrorType type, const char *message)
667{
668 JSObject *error = Error::create(exec, type, message, -1, -1, NULL);
669 exec->setException(error);
670 return error;
671}
672
673JSObject *throwError(ExecState *exec, ErrorType type, const UString &message, int line, int sourceId, const UString &sourceURL)
674{
675 JSObject *error = Error::create(exec, type, message, line, sourceId, sourceURL);
676 exec->setException(error);
677 return error;
678}
679
680} // namespace KJS
Note: See TracBrowser for help on using the repository browser.