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

Last change on this file since 25161 was 25161, checked in by kmccullo, 18 years ago

JavaScriptCore:

Reviewed by Geoff and Adam.

  • Changing stack depth to 500 (from 100 on mac and win) to help out some apps specifically gmail. <rdar://problem/3590522> JavaScript call stack limit of 99 is too small for some applications; needs to be closer to 500 (4045)
  • kjs/object.cpp:

LayoutTests:

Reviewed by Geoff and Adam.

  • Changing stack depth toi 500 (from 100 in mac and win) to help out some apps specifically gmail. <rdar://problem/3590522> JavaScript call stack limit of 99 is too small for some applications; needs to be closer to 500 (4045).
  • fast/js/deep-recursion-test-expected.txt: Added.
  • fast/js/deep-recursion-test.html: Added.
  • 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
36// maximum global call stack size. Protects against accidental or
37// malicious infinite recursions. Define to -1 if you want no limit.
38// In real-world testing it appears ok to bump the stack depth count to 500.
39// This of course is dependent on stack frame size.
40#define KJS_MAX_STACK 500
41
42#define JAVASCRIPT_CALL_TRACING 0
43#define JAVASCRIPT_MARK_TRACING 0
44
45#if JAVASCRIPT_CALL_TRACING
46static bool _traceJavaScript = false;
47
48extern "C" {
49 void setTraceJavaScript(bool f)
50 {
51 _traceJavaScript = f;
52 }
53
54 static bool traceJavaScript()
55 {
56 return _traceJavaScript;
57 }
58}
59#endif
60
61namespace KJS {
62
63// ------------------------------ Object ---------------------------------------
64
65JSValue *JSObject::call(ExecState *exec, JSObject *thisObj, const List &args)
66{
67 assert(implementsCall());
68
69#if KJS_MAX_STACK > 0
70 static int depth = 0; // sum of all concurrent interpreters
71
72#if JAVASCRIPT_CALL_TRACING
73 static bool tracing = false;
74 if (traceJavaScript() && !tracing) {
75 tracing = true;
76 for (int i = 0; i < depth; i++)
77 putchar (' ');
78 printf ("*** calling: %s\n", toString(exec).ascii());
79 for (int j = 0; j < args.size(); j++) {
80 for (int i = 0; i < depth; i++)
81 putchar (' ');
82 printf ("*** arg[%d] = %s\n", j, args[j]->toString(exec).ascii());
83 }
84 tracing = false;
85 }
86#endif
87
88 if (++depth > KJS_MAX_STACK) {
89 --depth;
90 return throwError(exec, RangeError, "Maximum call stack size exceeded.");
91 }
92#endif
93
94 JSValue *ret = callAsFunction(exec,thisObj,args);
95
96#if KJS_MAX_STACK > 0
97 --depth;
98#endif
99
100#if JAVASCRIPT_CALL_TRACING
101 if (traceJavaScript() && !tracing) {
102 tracing = true;
103 for (int i = 0; i < depth; i++)
104 putchar (' ');
105 printf ("*** returning: %s\n", ret->toString(exec).ascii());
106 tracing = false;
107 }
108#endif
109
110 return ret;
111}
112
113// ------------------------------ JSObject ------------------------------------
114
115void JSObject::mark()
116{
117 JSCell::mark();
118
119#if JAVASCRIPT_MARK_TRACING
120 static int markStackDepth = 0;
121 markStackDepth++;
122 for (int i = 0; i < markStackDepth; i++)
123 putchar('-');
124
125 printf("%s (%p)\n", className().UTF8String().c_str(), this);
126#endif
127
128 JSValue *proto = _proto;
129 if (!proto->marked())
130 proto->mark();
131
132 _prop.mark();
133
134#if JAVASCRIPT_MARK_TRACING
135 markStackDepth--;
136#endif
137}
138
139JSType JSObject::type() const
140{
141 return ObjectType;
142}
143
144const ClassInfo *JSObject::classInfo() const
145{
146 return 0;
147}
148
149UString JSObject::className() const
150{
151 const ClassInfo *ci = classInfo();
152 if ( ci )
153 return ci->className;
154 return "Object";
155}
156
157JSValue *JSObject::get(ExecState *exec, const Identifier &propertyName) const
158{
159 PropertySlot slot;
160
161 if (const_cast<JSObject *>(this)->getPropertySlot(exec, propertyName, slot))
162 return slot.getValue(exec, const_cast<JSObject *>(this), propertyName);
163
164 return jsUndefined();
165}
166
167JSValue *JSObject::get(ExecState *exec, unsigned propertyName) const
168{
169 PropertySlot slot;
170 if (const_cast<JSObject *>(this)->getPropertySlot(exec, propertyName, slot))
171 return slot.getValue(exec, const_cast<JSObject *>(this), propertyName);
172
173 return jsUndefined();
174}
175
176bool JSObject::getPropertySlot(ExecState *exec, unsigned propertyName, PropertySlot& slot)
177{
178 JSObject *imp = this;
179
180 while (true) {
181 if (imp->getOwnPropertySlot(exec, propertyName, slot))
182 return true;
183
184 JSValue *proto = imp->_proto;
185 if (!proto->isObject())
186 break;
187
188 imp = static_cast<JSObject *>(proto);
189 }
190
191 return false;
192}
193
194bool JSObject::getOwnPropertySlot(ExecState *exec, unsigned propertyName, PropertySlot& slot)
195{
196 return getOwnPropertySlot(exec, Identifier::from(propertyName), slot);
197}
198
199static void throwSetterError(ExecState *exec)
200{
201 throwError(exec, TypeError, "setting a property that has only a getter");
202}
203
204// ECMA 8.6.2.2
205void JSObject::put(ExecState* exec, const Identifier &propertyName, JSValue *value, int attr)
206{
207 assert(value);
208
209 // non-standard netscape extension
210 if (propertyName == exec->propertyNames().underscoreProto) {
211 JSObject* proto = value->getObject();
212 while (proto) {
213 if (proto == this)
214 throwError(exec, GeneralError, "cyclic __proto__ value");
215 proto = proto->prototype() ? proto->prototype()->getObject() : 0;
216 }
217
218 setPrototype(value);
219 return;
220 }
221
222 /* TODO: check for write permissions directly w/o this call */
223 /* Doesn't look very easy with the PropertyMap API - David */
224 // putValue() is used for JS assignemnts. It passes no attribute.
225 // Assume that a C++ implementation knows what it is doing
226 // and let it override the canPut() check.
227 if ((attr == None || attr == DontDelete) && !canPut(exec,propertyName)) {
228#ifdef KJS_VERBOSE
229 fprintf( stderr, "WARNING: canPut %s said NO\n", propertyName.ascii() );
230#endif
231 return;
232 }
233
234 // Check if there are any setters or getters in the prototype chain
235 JSObject *obj = this;
236 bool hasGettersOrSetters = false;
237 while (true) {
238 if (obj->_prop.hasGetterSetterProperties()) {
239 hasGettersOrSetters = true;
240 break;
241 }
242
243 if (!obj->_proto->isObject())
244 break;
245
246 obj = static_cast<JSObject *>(obj->_proto);
247 }
248
249 if (hasGettersOrSetters) {
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);
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
298 if (!getPropertyAttributes(propertyName, attributes))
299 return true;
300 else
301 return !(attributes & ReadOnly);
302}
303
304// ECMA 8.6.2.4
305bool JSObject::hasProperty(ExecState *exec, const Identifier &propertyName) const
306{
307 PropertySlot slot;
308 return const_cast<JSObject *>(this)->getPropertySlot(exec, propertyName, slot);
309}
310
311bool JSObject::hasProperty(ExecState *exec, unsigned propertyName) const
312{
313 PropertySlot slot;
314 return const_cast<JSObject *>(this)->getPropertySlot(exec, propertyName, slot);
315}
316
317// ECMA 8.6.2.5
318bool JSObject::deleteProperty(ExecState* /*exec*/, const Identifier &propertyName)
319{
320 unsigned attributes;
321 JSValue *v = _prop.get(propertyName, attributes);
322 if (v) {
323 if ((attributes & DontDelete))
324 return false;
325 _prop.remove(propertyName);
326 if (attributes & GetterSetter)
327 _prop.setHasGetterSetterProperties(_prop.containsGettersOrSetters());
328 return true;
329 }
330
331 // Look in the static hashtable of properties
332 const HashEntry* entry = findPropertyHashEntry(propertyName);
333 if (entry && entry->attr & DontDelete)
334 return false; // this builtin property can't be deleted
335 return true;
336}
337
338bool JSObject::deleteProperty(ExecState *exec, unsigned propertyName)
339{
340 return deleteProperty(exec, Identifier::from(propertyName));
341}
342
343static ALWAYS_INLINE JSValue *tryGetAndCallProperty(ExecState *exec, const JSObject *object, const Identifier &propertyName) {
344 JSValue *v = object->get(exec, propertyName);
345 if (v->isObject()) {
346 JSObject *o = static_cast<JSObject*>(v);
347 if (o->implementsCall()) { // spec says "not primitive type" but ...
348 JSObject *thisObj = const_cast<JSObject*>(object);
349 JSValue *def = o->call(exec, thisObj, List::empty());
350 JSType defType = def->type();
351 ASSERT(defType != GetterSetterType);
352 if (defType != ObjectType)
353 return def;
354 }
355 }
356 return NULL;
357}
358
359// ECMA 8.6.2.6
360JSValue* JSObject::defaultValue(ExecState* exec, JSType hint) const
361{
362 Identifier firstPropertyName;
363 Identifier secondPropertyName;
364 /* Prefer String for Date objects */
365 if ((hint == StringType) || (hint != StringType) && (hint != NumberType) && (_proto == exec->lexicalInterpreter()->builtinDatePrototype())) {
366 firstPropertyName = exec->propertyNames().toString;
367 secondPropertyName = exec->propertyNames().valueOf;
368 } else {
369 firstPropertyName = exec->propertyNames().valueOf;
370 secondPropertyName = exec->propertyNames().toString;
371 }
372
373 JSValue *v;
374 if ((v = tryGetAndCallProperty(exec, this, firstPropertyName)))
375 return v;
376 if ((v = tryGetAndCallProperty(exec, this, secondPropertyName)))
377 return v;
378
379 if (exec->hadException())
380 return exec->exception();
381
382 return throwError(exec, TypeError, "No default value");
383}
384
385const HashEntry* JSObject::findPropertyHashEntry(const Identifier& propertyName) const
386{
387 for (const ClassInfo *info = classInfo(); info; info = info->parentClass) {
388 if (const HashTable *propHashTable = info->propHashTable) {
389 if (const HashEntry *e = Lookup::findEntry(propHashTable, propertyName))
390 return e;
391 }
392 }
393 return 0;
394}
395
396void JSObject::defineGetter(ExecState*, const Identifier& propertyName, JSObject* getterFunc)
397{
398 JSValue *o = getDirect(propertyName);
399 GetterSetterImp *gs;
400
401 if (o && o->type() == GetterSetterType) {
402 gs = static_cast<GetterSetterImp *>(o);
403 } else {
404 gs = new GetterSetterImp;
405 putDirect(propertyName, gs, GetterSetter);
406 }
407
408 _prop.setHasGetterSetterProperties(true);
409 gs->setGetter(getterFunc);
410}
411
412void JSObject::defineSetter(ExecState*, const Identifier& propertyName, JSObject* setterFunc)
413{
414 JSValue *o = getDirect(propertyName);
415 GetterSetterImp *gs;
416
417 if (o && o->type() == GetterSetterType) {
418 gs = static_cast<GetterSetterImp *>(o);
419 } else {
420 gs = new GetterSetterImp;
421 putDirect(propertyName, gs, GetterSetter);
422 }
423
424 _prop.setHasGetterSetterProperties(true);
425 gs->setSetter(setterFunc);
426}
427
428bool JSObject::implementsConstruct() const
429{
430 return false;
431}
432
433JSObject* JSObject::construct(ExecState*, const List& /*args*/)
434{
435 assert(false);
436 return NULL;
437}
438
439JSObject* JSObject::construct(ExecState* exec, const List& args, const Identifier& /*functionName*/, const UString& /*sourceURL*/, int /*lineNumber*/)
440{
441 return construct(exec, args);
442}
443
444bool JSObject::implementsCall() const
445{
446 return false;
447}
448
449JSValue *JSObject::callAsFunction(ExecState* /*exec*/, JSObject* /*thisObj*/, const List &/*args*/)
450{
451 assert(false);
452 return NULL;
453}
454
455bool JSObject::implementsHasInstance() const
456{
457 return false;
458}
459
460bool JSObject::hasInstance(ExecState* exec, JSValue* value)
461{
462 JSValue* proto = get(exec, exec->propertyNames().prototype);
463 if (!proto->isObject()) {
464 throwError(exec, TypeError, "intanceof called on an object with an invalid prototype property.");
465 return false;
466 }
467
468 if (!value->isObject())
469 return false;
470
471 JSObject* o = static_cast<JSObject*>(value);
472 while ((o = o->prototype()->getObject())) {
473 if (o == proto)
474 return true;
475 }
476 return false;
477}
478
479bool JSObject::propertyIsEnumerable(ExecState*, const Identifier& propertyName) const
480{
481 unsigned attributes;
482
483 if (!getPropertyAttributes(propertyName, attributes))
484 return false;
485 else
486 return !(attributes & DontEnum);
487}
488
489bool JSObject::getPropertyAttributes(const Identifier& propertyName, unsigned& attributes) const
490{
491 if (_prop.get(propertyName, attributes))
492 return true;
493
494 // Look in the static hashtable of properties
495 const HashEntry* e = findPropertyHashEntry(propertyName);
496 if (e) {
497 attributes = e->attr;
498 return true;
499 }
500
501 return false;
502}
503
504void JSObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
505{
506 _prop.getEnumerablePropertyNames(propertyNames);
507
508 // Add properties from the static hashtable of properties
509 const ClassInfo *info = classInfo();
510 while (info) {
511 if (info->propHashTable) {
512 int size = info->propHashTable->size;
513 const HashEntry *e = info->propHashTable->entries;
514 for (int i = 0; i < size; ++i, ++e) {
515 if (e->s && !(e->attr & DontEnum))
516 propertyNames.add(e->s);
517 }
518 }
519 info = info->parentClass;
520 }
521 if (_proto->isObject())
522 static_cast<JSObject*>(_proto)->getPropertyNames(exec, propertyNames);
523}
524
525bool JSObject::toBoolean(ExecState*) const
526{
527 return true;
528}
529
530double JSObject::toNumber(ExecState *exec) const
531{
532 JSValue *prim = toPrimitive(exec,NumberType);
533 if (exec->hadException()) // should be picked up soon in nodes.cpp
534 return 0.0;
535 return prim->toNumber(exec);
536}
537
538UString JSObject::toString(ExecState *exec) const
539{
540 JSValue *prim = toPrimitive(exec,StringType);
541 if (exec->hadException()) // should be picked up soon in nodes.cpp
542 return "";
543 return prim->toString(exec);
544}
545
546JSObject *JSObject::toObject(ExecState*) const
547{
548 return const_cast<JSObject*>(this);
549}
550
551void JSObject::putDirect(const Identifier &propertyName, JSValue *value, int attr)
552{
553 _prop.put(propertyName, value, attr);
554}
555
556void JSObject::putDirect(const Identifier &propertyName, int value, int attr)
557{
558 _prop.put(propertyName, jsNumber(value), attr);
559}
560
561void JSObject::putDirectFunction(InternalFunctionImp* func, int attr)
562{
563 putDirect(func->functionName(), func, attr);
564}
565
566void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSValue **location)
567{
568 GetterSetterImp *gs = static_cast<GetterSetterImp *>(*location);
569 JSObject *getterFunc = gs->getGetter();
570 if (getterFunc)
571 slot.setGetterSlot(this, getterFunc);
572 else
573 slot.setUndefined(this);
574}
575
576// ------------------------------ Error ----------------------------------------
577
578const char * const errorNamesArr[] = {
579 I18N_NOOP("Error"), // GeneralError
580 I18N_NOOP("Evaluation error"), // EvalError
581 I18N_NOOP("Range error"), // RangeError
582 I18N_NOOP("Reference error"), // ReferenceError
583 I18N_NOOP("Syntax error"), // SyntaxError
584 I18N_NOOP("Type error"), // TypeError
585 I18N_NOOP("URI error"), // URIError
586};
587
588const char * const * const Error::errorNames = errorNamesArr;
589
590JSObject *Error::create(ExecState *exec, ErrorType errtype, const UString &message,
591 int lineno, int sourceId, const UString &sourceURL)
592{
593 JSObject *cons;
594 switch (errtype) {
595 case EvalError:
596 cons = exec->lexicalInterpreter()->builtinEvalError();
597 break;
598 case RangeError:
599 cons = exec->lexicalInterpreter()->builtinRangeError();
600 break;
601 case ReferenceError:
602 cons = exec->lexicalInterpreter()->builtinReferenceError();
603 break;
604 case SyntaxError:
605 cons = exec->lexicalInterpreter()->builtinSyntaxError();
606 break;
607 case TypeError:
608 cons = exec->lexicalInterpreter()->builtinTypeError();
609 break;
610 case URIError:
611 cons = exec->lexicalInterpreter()->builtinURIError();
612 break;
613 default:
614 cons = exec->lexicalInterpreter()->builtinError();
615 break;
616 }
617
618 List args;
619 if (message.isEmpty())
620 args.append(jsString(errorNames[errtype]));
621 else
622 args.append(jsString(message));
623 JSObject *err = static_cast<JSObject *>(cons->construct(exec,args));
624
625 if (lineno != -1)
626 err->put(exec, "line", jsNumber(lineno));
627 if (sourceId != -1)
628 err->put(exec, "sourceId", jsNumber(sourceId));
629
630 if(!sourceURL.isNull())
631 err->put(exec, "sourceURL", jsString(sourceURL));
632
633 return err;
634
635/*
636#ifndef NDEBUG
637 const char *msg = err->get(messagePropertyName)->toString().value().ascii();
638 if (l >= 0)
639 fprintf(stderr, "KJS: %s at line %d. %s\n", estr, l, msg);
640 else
641 fprintf(stderr, "KJS: %s. %s\n", estr, msg);
642#endif
643
644 return err;
645*/
646}
647
648JSObject *Error::create(ExecState *exec, ErrorType type, const char *message)
649{
650 return create(exec, type, message, -1, -1, NULL);
651}
652
653JSObject *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
660JSObject *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
667JSObject *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
674JSObject *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
Note: See TracBrowser for help on using the repository browser.