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

Last change on this file since 11773 was 11773, checked in by mjs, 19 years ago

Reviewed by Darin and Geoff.

Changes by me and Anders.

  • also fixed some warnings reported by -Winline
  • JavaScriptCorePrefix.h: Move new and delete definitions higher so there aren't conflicts with use in standard C++ headers
  • kjs/object.cpp: (KJS::throwSetterError): Moved this piece of put into a seprate function to avoid the PIC branch. (KJS::JSObject::put): Use hasGetterSetterProperties to avoid expensive stuff when not needed. Also use GetterSetter properties attribute. (KJS::JSObject::deleteProperty): Recompute whether any properties are getter/setter properties any more, if this one was one. (KJS::JSObject::defineGetter): Let the PropertyMap know that it has getter/setter properties now (and use the new attribute). (KJS::JSObject::defineSetter): Ditto. (KJS::JSObject::fillGetterPropertySlot): Out-of-line helper for getOwnPropertySlot, to avoid global variable access in the hot code path.
  • kjs/object.h: (KJS::): Added GetterSetter attribute. (KJS::JSCell::isObject): Moved lower to be after inline methods it uses. (KJS::JSValue::isObject): ditto (KJS::JSObject::getOwnPropertySlot): try to avoid impact of getters and setters as much as possible in the case where they are not being used
  • kjs/property_map.cpp: (KJS::PropertyMap::containsGettersOrSetters): New method to help with this
  • kjs/property_map.h: (KJS::PropertyMap::hasGetterSetterProperties): Ditto (KJS::PropertyMap::setHasGetterSetterProperties): Ditto (KJS::PropertyMap::PropertyMap): Added a crazy hack to store the global "has getter/setter properties" flag in the property map single entry, to avoid making objects any bigger.
  • kjs/value.h: Moved some things to object.h to make -Winline happier
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.9 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 Apple Computer, Inc.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 *
23 */
24
25#include "config.h"
26#include "value.h"
27#include "object.h"
28#include "types.h"
29#include "interpreter.h"
30#include "lookup.h"
31#include "reference_list.h"
32
33#include <assert.h>
34#include <math.h>
35#include <stdio.h>
36
37#include "internal.h"
38#include "collector.h"
39#include "operations.h"
40#include "error_object.h"
41#include "nodes.h"
42
43#ifndef NDEBUG
44//#define JAVASCRIPT_CALL_TRACING 1
45#endif
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 concurrent interpreters
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 JSValue *proto = _proto;
122 if (!proto->marked())
123 proto->mark();
124
125 _prop.mark();
126
127 if (_internalValue && !_internalValue->marked())
128 _internalValue->mark();
129
130 _scope.mark();
131}
132
133Type JSObject::type() const
134{
135 return ObjectType;
136}
137
138const ClassInfo *JSObject::classInfo() const
139{
140 return 0;
141}
142
143UString JSObject::className() const
144{
145 const ClassInfo *ci = classInfo();
146 if ( ci )
147 return ci->className;
148 return "Object";
149}
150
151JSValue *JSObject::get(ExecState *exec, const Identifier &propertyName) const
152{
153 PropertySlot slot;
154
155 if (const_cast<JSObject *>(this)->getPropertySlot(exec, propertyName, slot))
156 return slot.getValue(exec, const_cast<JSObject *>(this), propertyName);
157
158 return jsUndefined();
159}
160
161JSValue *JSObject::get(ExecState *exec, unsigned propertyName) const
162{
163 PropertySlot slot;
164 if (const_cast<JSObject *>(this)->getPropertySlot(exec, propertyName, slot))
165 return slot.getValue(exec, const_cast<JSObject *>(this), propertyName);
166
167 return jsUndefined();
168}
169
170bool JSObject::getPropertySlot(ExecState *exec, unsigned propertyName, PropertySlot& slot)
171{
172 JSObject *imp = this;
173
174 while (true) {
175 if (imp->getOwnPropertySlot(exec, propertyName, slot))
176 return true;
177
178 JSValue *proto = imp->_proto;
179 if (!proto->isObject())
180 break;
181
182 imp = static_cast<JSObject *>(proto);
183 }
184
185 return false;
186}
187
188bool JSObject::getOwnPropertySlot(ExecState *exec, unsigned propertyName, PropertySlot& slot)
189{
190 return getOwnPropertySlot(exec, Identifier::from(propertyName), slot);
191}
192
193static void throwSetterError(ExecState *exec)
194{
195 throwError(exec, TypeError, "setting a property that has only a getter");
196}
197
198// ECMA 8.6.2.2
199void JSObject::put(ExecState *exec, const Identifier &propertyName, JSValue *value, int attr)
200{
201 assert(value);
202
203 // non-standard netscape extension
204 if (propertyName == exec->dynamicInterpreter()->specialPrototypeIdentifier()) {
205 setPrototype(value);
206 return;
207 }
208
209 /* TODO: check for write permissions directly w/o this call */
210 /* Doesn't look very easy with the PropertyMap API - David */
211 // putValue() is used for JS assignemnts. It passes no attribute.
212 // Assume that a C++ implementation knows what it is doing
213 // and let it override the canPut() check.
214 if ((attr == None || attr == DontDelete) && !canPut(exec,propertyName)) {
215#ifdef KJS_VERBOSE
216 fprintf( stderr, "WARNING: canPut %s said NO\n", propertyName.ascii() );
217#endif
218 return;
219 }
220
221 JSObject *obj = this;
222 while (true) {
223 JSValue *gs;
224 int attributes;
225 if (obj->_prop.hasGetterSetterProperties() && (gs = obj->_prop.get(propertyName, attributes))) {
226 if (attributes & GetterSetter) {
227 JSObject *setterFunc = static_cast<GetterSetterImp *>(gs)->getSetter();
228
229 if (!setterFunc) {
230 throwSetterError(exec);
231 return;
232 }
233
234 List args;
235 args.append(value);
236
237 setterFunc->call(exec, this, args);
238 return;
239 } else {
240 // If there's an existing property on the object or one of its
241 // prototype it should be replaced, so we just break here.
242 break;
243 }
244 }
245
246 if (!obj->_proto->isObject())
247 break;
248
249 obj = static_cast<JSObject *>(obj->_proto);
250 }
251
252 _prop.put(propertyName,value,attr);
253}
254
255void JSObject::put(ExecState *exec, unsigned propertyName,
256 JSValue *value, int attr)
257{
258 put(exec, Identifier::from(propertyName), value, attr);
259}
260
261// ECMA 8.6.2.3
262bool JSObject::canPut(ExecState *, const Identifier &propertyName) const
263{
264 int attributes;
265
266 // Don't look in the prototype here. We can always put an override
267 // in the object, even if the prototype has a ReadOnly property.
268
269 if (!getPropertyAttributes(propertyName, attributes))
270 return true;
271 else
272 return !(attributes & ReadOnly);
273}
274
275// ECMA 8.6.2.4
276bool JSObject::hasProperty(ExecState *exec, const Identifier &propertyName) const
277{
278 PropertySlot slot;
279 return const_cast<JSObject *>(this)->getPropertySlot(exec, propertyName, slot);
280}
281
282bool JSObject::hasProperty(ExecState *exec, unsigned propertyName) const
283{
284 PropertySlot slot;
285 return const_cast<JSObject *>(this)->getPropertySlot(exec, propertyName, slot);
286}
287
288// ECMA 8.6.2.5
289bool JSObject::deleteProperty(ExecState */*exec*/, const Identifier &propertyName)
290{
291 int attributes;
292 JSValue *v = _prop.get(propertyName, attributes);
293 if (v) {
294 if ((attributes & DontDelete))
295 return false;
296 _prop.remove(propertyName);
297 if (attributes & GetterSetter)
298 _prop.setHasGetterSetterProperties(_prop.containsGettersOrSetters());
299 return true;
300 }
301
302 // Look in the static hashtable of properties
303 const HashEntry* entry = findPropertyHashEntry(propertyName);
304 if (entry && entry->attr & DontDelete)
305 return false; // this builtin property can't be deleted
306 return true;
307}
308
309bool JSObject::deleteProperty(ExecState *exec, unsigned propertyName)
310{
311 return deleteProperty(exec, Identifier::from(propertyName));
312}
313
314static inline
315#ifdef __GNUC__
316__attribute__((always_inline))
317#endif
318JSValue *tryGetAndCallProperty(ExecState *exec, const JSObject *object, const Identifier &propertyName) {
319 JSValue *v = object->get(exec, propertyName);
320 if (v->isObject()) {
321 JSObject *o = static_cast<JSObject*>(v);
322 if (o->implementsCall()) { // spec says "not primitive type" but ...
323 JSObject *thisObj = const_cast<JSObject*>(object);
324 JSValue *def = o->call(exec, thisObj, List::empty());
325 Type defType = def->type();
326 if (defType == UnspecifiedType || defType == UndefinedType ||
327 defType == NullType || defType == BooleanType ||
328 defType == StringType || defType == NumberType) {
329 return def;
330 }
331 }
332 }
333 return NULL;
334}
335
336// ECMA 8.6.2.6
337JSValue *JSObject::defaultValue(ExecState *exec, Type hint) const
338{
339 Identifier firstPropertyName;
340 Identifier secondPropertyName;
341 /* Prefer String for Date objects */
342 if ((hint == StringType) || (hint != StringType) && (hint != NumberType) && (_proto == exec->lexicalInterpreter()->builtinDatePrototype())) {
343 firstPropertyName = toStringPropertyName;
344 secondPropertyName = valueOfPropertyName;
345 } else {
346 firstPropertyName = valueOfPropertyName;
347 secondPropertyName = toStringPropertyName;
348 }
349
350 JSValue *v;
351 if ((v = tryGetAndCallProperty(exec, this, firstPropertyName)))
352 return v;
353 if ((v = tryGetAndCallProperty(exec, this, secondPropertyName)))
354 return v;
355
356 if (exec->hadException())
357 return exec->exception();
358
359 return throwError(exec, TypeError, "No default value");
360}
361
362const HashEntry* JSObject::findPropertyHashEntry(const Identifier& propertyName) const
363{
364 for (const ClassInfo *info = classInfo(); info; info = info->parentClass) {
365 if (const HashTable *propHashTable = info->propHashTable) {
366 if (const HashEntry *e = Lookup::findEntry(propHashTable, propertyName))
367 return e;
368 }
369 }
370 return 0;
371}
372
373void JSObject::defineGetter(ExecState *exec, const Identifier& propertyName, JSObject *getterFunc)
374{
375 JSValue *o = getDirect(propertyName);
376 GetterSetterImp *gs;
377
378 if (o && o->type() == GetterSetterType) {
379 gs = static_cast<GetterSetterImp *>(o);
380 } else {
381 gs = new GetterSetterImp;
382 putDirect(propertyName, gs, GetterSetter);
383 }
384
385 _prop.setHasGetterSetterProperties(true);
386 gs->setGetter(getterFunc);
387}
388
389void JSObject::defineSetter(ExecState *exec, const Identifier& propertyName, JSObject *setterFunc)
390{
391 JSValue *o = getDirect(propertyName);
392 GetterSetterImp *gs;
393
394 if (o && o->type() == GetterSetterType) {
395 gs = static_cast<GetterSetterImp *>(o);
396 } else {
397 gs = new GetterSetterImp;
398 putDirect(propertyName, gs);
399 putDirect(propertyName, gs, GetterSetter);
400 }
401
402 _prop.setHasGetterSetterProperties(true);
403 gs->setSetter(setterFunc);
404}
405
406bool JSObject::implementsConstruct() const
407{
408 return false;
409}
410
411JSObject *JSObject::construct(ExecState */*exec*/, const List &/*args*/)
412{
413 assert(false);
414 return NULL;
415}
416
417JSObject *JSObject::construct(ExecState *exec, const List &args, const UString &/*sourceURL*/, int /*lineNumber*/)
418{
419 return construct(exec, args);
420}
421
422bool JSObject::implementsCall() const
423{
424 return false;
425}
426
427JSValue *JSObject::callAsFunction(ExecState */*exec*/, JSObject */*thisObj*/, const List &/*args*/)
428{
429 assert(false);
430 return NULL;
431}
432
433bool JSObject::implementsHasInstance() const
434{
435 return false;
436}
437
438bool JSObject::hasInstance(ExecState */*exec*/, JSValue */*value*/)
439{
440 assert(false);
441 return false;
442}
443
444bool JSObject::propertyIsEnumerable(ExecState *exec, const Identifier &propertyName) const
445{
446 int attributes;
447
448 if (!getPropertyAttributes(propertyName, attributes))
449 return false;
450 else
451 return !(attributes & DontEnum);
452}
453
454bool JSObject::getPropertyAttributes(const Identifier& propertyName, int& attributes) const
455{
456 if (_prop.get(propertyName, attributes))
457 return true;
458
459 // Look in the static hashtable of properties
460 const HashEntry* e = findPropertyHashEntry(propertyName);
461 if (e) {
462 attributes = e->attr;
463 return true;
464 }
465
466 return false;
467}
468
469ReferenceList JSObject::propList(ExecState *exec, bool recursive)
470{
471 ReferenceList list;
472 if (_proto->isObject() && recursive)
473 list = static_cast<JSObject*>(_proto)->propList(exec,recursive);
474
475 _prop.addEnumerablesToReferenceList(list, this);
476
477 // Add properties from the static hashtable of properties
478 const ClassInfo *info = classInfo();
479 while (info) {
480 if (info->propHashTable) {
481 int size = info->propHashTable->size;
482 const HashEntry *e = info->propHashTable->entries;
483 for (int i = 0; i < size; ++i, ++e) {
484 if ( e->s && !(e->attr & DontEnum) )
485 list.append(Reference(this, e->s)); /// ######### check for duplicates with the propertymap
486 }
487 }
488 info = info->parentClass;
489 }
490
491 return list;
492}
493
494JSValue *JSObject::toPrimitive(ExecState *exec, Type preferredType) const
495{
496 return defaultValue(exec,preferredType);
497}
498
499bool JSObject::toBoolean(ExecState */*exec*/) const
500{
501 return true;
502}
503
504double JSObject::toNumber(ExecState *exec) const
505{
506 JSValue *prim = toPrimitive(exec,NumberType);
507 if (exec->hadException()) // should be picked up soon in nodes.cpp
508 return 0.0;
509 return prim->toNumber(exec);
510}
511
512UString JSObject::toString(ExecState *exec) const
513{
514 JSValue *prim = toPrimitive(exec,StringType);
515 if (exec->hadException()) // should be picked up soon in nodes.cpp
516 return "";
517 return prim->toString(exec);
518}
519
520JSObject *JSObject::toObject(ExecState */*exec*/) const
521{
522 return const_cast<JSObject*>(this);
523}
524
525void JSObject::putDirect(const Identifier &propertyName, JSValue *value, int attr)
526{
527 _prop.put(propertyName, value, attr);
528}
529
530void JSObject::putDirect(const Identifier &propertyName, int value, int attr)
531{
532 _prop.put(propertyName, jsNumber(value), attr);
533}
534
535void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSValue **location)
536{
537 GetterSetterImp *gs = static_cast<GetterSetterImp *>(*location);
538 JSObject *getterFunc = gs->getGetter();
539 if (getterFunc)
540 slot.setGetterSlot(this, getterFunc);
541 else
542 slot.setUndefined(this);
543}
544
545// ------------------------------ Error ----------------------------------------
546
547const char * const errorNamesArr[] = {
548 I18N_NOOP("Error"), // GeneralError
549 I18N_NOOP("Evaluation error"), // EvalError
550 I18N_NOOP("Range error"), // RangeError
551 I18N_NOOP("Reference error"), // ReferenceError
552 I18N_NOOP("Syntax error"), // SyntaxError
553 I18N_NOOP("Type error"), // TypeError
554 I18N_NOOP("URI error"), // URIError
555};
556
557const char * const * const Error::errorNames = errorNamesArr;
558
559JSObject *Error::create(ExecState *exec, ErrorType errtype, const UString &message,
560 int lineno, int sourceId, const UString *sourceURL)
561{
562 JSObject *cons;
563 switch (errtype) {
564 case EvalError:
565 cons = exec->lexicalInterpreter()->builtinEvalError();
566 break;
567 case RangeError:
568 cons = exec->lexicalInterpreter()->builtinRangeError();
569 break;
570 case ReferenceError:
571 cons = exec->lexicalInterpreter()->builtinReferenceError();
572 break;
573 case SyntaxError:
574 cons = exec->lexicalInterpreter()->builtinSyntaxError();
575 break;
576 case TypeError:
577 cons = exec->lexicalInterpreter()->builtinTypeError();
578 break;
579 case URIError:
580 cons = exec->lexicalInterpreter()->builtinURIError();
581 break;
582 default:
583 cons = exec->lexicalInterpreter()->builtinError();
584 break;
585 }
586
587 List args;
588 if (message.isEmpty())
589 args.append(jsString(errorNames[errtype]));
590 else
591 args.append(jsString(message));
592 JSObject *err = static_cast<JSObject *>(cons->construct(exec,args));
593
594 if (lineno != -1)
595 err->put(exec, "line", jsNumber(lineno));
596 if (sourceId != -1)
597 err->put(exec, "sourceId", jsNumber(sourceId));
598
599 if(sourceURL)
600 err->put(exec,"sourceURL", jsString(*sourceURL));
601
602 return err;
603
604/*
605#ifndef NDEBUG
606 const char *msg = err->get("message")->toString().value().ascii();
607 if (l >= 0)
608 fprintf(stderr, "KJS: %s at line %d. %s\n", estr, l, msg);
609 else
610 fprintf(stderr, "KJS: %s. %s\n", estr, msg);
611#endif
612
613 return err;
614*/
615}
616
617JSObject *Error::create(ExecState *exec, ErrorType type, const char *message)
618{
619 return create(exec, type, message, -1, -1, NULL);
620}
621
622JSObject *throwError(ExecState *exec, ErrorType type)
623{
624 JSObject *error = Error::create(exec, type, UString(), -1, -1, NULL);
625 exec->setException(error);
626 return error;
627}
628
629JSObject *throwError(ExecState *exec, ErrorType type, const UString &message)
630{
631 JSObject *error = Error::create(exec, type, message, -1, -1, NULL);
632 exec->setException(error);
633 return error;
634}
635
636JSObject *throwError(ExecState *exec, ErrorType type, const char *message)
637{
638 JSObject *error = Error::create(exec, type, message, -1, -1, NULL);
639 exec->setException(error);
640 return error;
641}
642
643JSObject *throwError(ExecState *exec, ErrorType type, const UString &message, int line, int sourceId, const UString *sourceURL)
644{
645 JSObject *error = Error::create(exec, type, message, line, sourceId, sourceURL);
646 exec->setException(error);
647 return error;
648}
649
650} // namespace KJS
Note: See TracBrowser for help on using the repository browser.