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

Last change on this file since 28980 was 28980, checked in by [email protected], 17 years ago

Reviewed by Oliver.



1.3% speedup on SunSpider.


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