source: webkit/trunk/JavaScriptCore/kjs/object.h@ 15310

Last change on this file since 15310 was 15310, checked in by ggaren, 19 years ago

Reviewed by Darin.

Improved type safety by implementing opaque JSValue/JSObject typing through
abuse of 'const', not void*. Also fixed an alarming number of bugs
exposed by this new type safety.


I made one design change in JavaScriptCore, which is that the JSObject
constructor should take a JSValue* as its prototype argument, not a JSObject*,
since we allow the prototype to be any JSValue*, including jsNull(), for
example.


  • API/APICast.h: (toJS):
  • API/JSBase.h:
  • API/JSCallbackConstructor.cpp: (KJS::JSCallbackConstructor::construct):
  • API/JSCallbackFunction.cpp: (KJS::JSCallbackFunction::callAsFunction):
  • API/JSCallbackObject.cpp: (KJS::JSCallbackObject::JSCallbackObject): (KJS::JSCallbackObject::getOwnPropertySlot): (KJS::JSCallbackObject::put): (KJS::JSCallbackObject::construct): (KJS::JSCallbackObject::callAsFunction): (KJS::JSCallbackObject::staticFunctionGetter):
  • API/JSCallbackObject.h:
  • API/JSContextRef.cpp: (JSEvaluate):
  • API/JSNode.c: (JSNodePrototype_appendChild): (JSNodePrototype_removeChild): (JSNodePrototype_replaceChild):
  • API/JSObjectRef.cpp: (JSObjectMake): (JSFunctionMakeWithBody): (JSObjectGetProperty): (JSObjectCallAsFunction): (JSObjectCallAsConstructor):
  • API/JSObjectRef.h:
  • API/testapi.c: (main):
  • ChangeLog:
  • kjs/object.h: (KJS::JSObject::JSObject):
  • Property svn:eol-style set to native
File size: 23.6 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 *
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 Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 *
23 */
24
25#ifndef KJS_OBJECT_H
26#define KJS_OBJECT_H
27
28#include "JSType.h"
29#include "interpreter.h"
30#include "property_map.h"
31#include "property_slot.h"
32#include "scope_chain.h"
33#include <wtf/AlwaysInline.h>
34
35namespace KJS {
36
37 class HashTable;
38 class HashEntry;
39 class ListImp;
40 class InternalFunctionImp;
41
42 // ECMA 262-3 8.6.1
43 // Property attributes
44 enum Attribute { None = 0,
45 ReadOnly = 1 << 1, // property can be only read, not written
46 DontEnum = 1 << 2, // property doesn't appear in (for .. in ..)
47 DontDelete = 1 << 3, // property can't be deleted
48 Internal = 1 << 4, // an internal property, set to bypass checks
49 Function = 1 << 5, // property is a function - only used by static hashtables
50 GetterSetter = 1 << 6 }; // property is a getter or setter
51
52 /**
53 * Class Information
54 */
55 struct ClassInfo {
56 /**
57 * A string denoting the class name. Example: "Window".
58 */
59 const char* className;
60 /**
61 * Pointer to the class information of the base class.
62 * 0L if there is none.
63 */
64 const ClassInfo *parentClass;
65 /**
66 * Static hash-table of properties.
67 */
68 const HashTable *propHashTable;
69 /**
70 * Reserved for future extension.
71 */
72 void *dummy;
73 };
74
75 // This is an internal value object which stores getter and setter functions
76 // for a property.
77 class GetterSetterImp : public JSCell {
78 public:
79 JSType type() const { return GetterSetterType; }
80
81 GetterSetterImp() : getter(0), setter(0) { }
82
83 virtual JSValue *toPrimitive(ExecState *exec, JSType preferred = UnspecifiedType) const;
84 virtual bool toBoolean(ExecState *exec) const;
85 virtual double toNumber(ExecState *exec) const;
86 virtual UString toString(ExecState *exec) const;
87 virtual JSObject *toObject(ExecState *exec) const;
88
89 virtual void mark();
90
91 JSObject *getGetter() { return getter; }
92 void setGetter(JSObject *g) { getter = g; }
93 JSObject *getSetter() { return setter; }
94 void setSetter(JSObject *s) { setter = s; }
95
96 private:
97 JSObject *getter;
98 JSObject *setter;
99 };
100
101 class JSObject : public JSCell {
102 public:
103 /**
104 * Creates a new JSObject with the specified prototype
105 *
106 * @param proto The prototype
107 */
108 JSObject(JSValue* proto, bool destructorIsThreadSafe = true);
109
110 /**
111 * Creates a new JSObject with a prototype of jsNull()
112 * (that is, the ECMAScript "null" value, not a null object pointer).
113 */
114 explicit JSObject(bool destructorIsThreadSafe = true);
115
116 virtual void mark();
117 virtual JSType type() const;
118
119 /**
120 * A pointer to a ClassInfo struct for this class. This provides a basic
121 * facility for run-time type information, and can be used to check an
122 * object's class an inheritance (see inherits()). This should
123 * always return a statically declared pointer, or 0 to indicate that
124 * there is no class information.
125 *
126 * This is primarily useful if you have application-defined classes that you
127 * wish to check against for casting purposes.
128 *
129 * For example, to specify the class info for classes FooImp and BarImp,
130 * where FooImp inherits from BarImp, you would add the following in your
131 * class declarations:
132 *
133 * \code
134 * class BarImp : public JSObject {
135 * virtual const ClassInfo *classInfo() const { return &info; }
136 * static const ClassInfo info;
137 * // ...
138 * };
139 *
140 * class FooImp : public JSObject {
141 * virtual const ClassInfo *classInfo() const { return &info; }
142 * static const ClassInfo info;
143 * // ...
144 * };
145 * \endcode
146 *
147 * And in your source file:
148 *
149 * \code
150 * const ClassInfo BarImp::info = {"Bar", 0, 0, 0}; // no parent class
151 * const ClassInfo FooImp::info = {"Foo", &BarImp::info, 0, 0};
152 * \endcode
153 *
154 * @see inherits()
155 */
156 virtual const ClassInfo *classInfo() const;
157
158 /**
159 * Checks whether this object inherits from the class with the specified
160 * classInfo() pointer. This requires that both this class and the other
161 * class return a non-NULL pointer for their classInfo() methods (otherwise
162 * it will return false).
163 *
164 * For example, for two JSObject pointers obj1 and obj2, you can check
165 * if obj1's class inherits from obj2's class using the following:
166 *
167 * if (obj1->inherits(obj2->classInfo())) {
168 * // ...
169 * }
170 *
171 * If you have a handle to a statically declared ClassInfo, such as in the
172 * classInfo() example, you can check for inheritance without needing
173 * an instance of the other class:
174 *
175 * if (obj1->inherits(FooImp::info)) {
176 * // ...
177 * }
178 *
179 * @param cinfo The ClassInfo pointer for the class you want to check
180 * inheritance against.
181 * @return true if this object's class inherits from class with the
182 * ClassInfo pointer specified in cinfo
183 */
184 bool inherits(const ClassInfo *cinfo) const;
185
186 // internal properties (ECMA 262-3 8.6.2)
187
188 /**
189 * Returns the prototype of this object. Note that this is not the same as
190 * the "prototype" property.
191 *
192 * See ECMA 8.6.2
193 *
194 * @return The object's prototype
195 */
196 JSValue *prototype() const;
197 void setPrototype(JSValue *proto);
198
199 /**
200 * Returns the class name of the object
201 *
202 * See ECMA 8.6.2
203 *
204 * @return The object's class name
205 */
206 /**
207 * Implementation of the [[Class]] internal property (implemented by all
208 * Objects)
209 *
210 * The default implementation uses classInfo().
211 * You should either implement classInfo(), or
212 * if you simply need a classname, you can reimplement className()
213 * instead.
214 */
215 virtual UString className() const;
216
217 /**
218 * Retrieves the specified property from the object. If neither the object
219 * or any other object in it's prototype chain have the property, this
220 * function will return Undefined.
221 *
222 * See ECMA 8.6.2.1
223 *
224 * @param exec The current execution state
225 * @param propertyName The name of the property to retrieve
226 *
227 * @return The specified property, or Undefined
228 */
229 JSValue *get(ExecState *exec, const Identifier &propertyName) const;
230 JSValue *get(ExecState *exec, unsigned propertyName) const;
231
232 bool getPropertySlot(ExecState *, const Identifier&, PropertySlot&);
233 bool getPropertySlot(ExecState *, unsigned, PropertySlot&);
234
235 virtual bool getOwnPropertySlot(ExecState *, const Identifier&, PropertySlot&);
236 virtual bool getOwnPropertySlot(ExecState *, unsigned index, PropertySlot&);
237
238 /**
239 * Sets the specified property.
240 *
241 * See ECMA 8.6.2.2
242 *
243 * @param exec The current execution state
244 * @param propertyName The name of the property to set
245 * @param propertyValue The value to set
246 */
247 virtual void put(ExecState *exec, const Identifier &propertyName, JSValue *value, int attr = None);
248 virtual void put(ExecState *exec, unsigned propertyName, JSValue *value, int attr = None);
249
250 /**
251 * Used to check whether or not a particular property is allowed to be set
252 * on an object
253 *
254 * See ECMA 8.6.2.3
255 *
256 * @param exec The current execution state
257 * @param propertyName The name of the property
258 * @return true if the property can be set, otherwise false
259 */
260 /**
261 * Implementation of the [[CanPut]] internal property (implemented by all
262 * Objects)
263 */
264 virtual bool canPut(ExecState *exec, const Identifier &propertyName) const;
265
266 /**
267 * Checks if a property is enumerable, that is if it doesn't have the DontEnum
268 * flag set
269 *
270 * See ECMA 15.2.4
271 * @param exec The current execution state
272 * @param propertyName The name of the property
273 * @return true if the property is enumerable, otherwise false
274 */
275 bool propertyIsEnumerable(ExecState *exec, const Identifier &propertyName) const;
276
277 /**
278 * Checks to see whether the object (or any object in it's prototype chain)
279 * has a property with the specified name.
280 *
281 * See ECMA 8.6.2.4
282 *
283 * @param exec The current execution state
284 * @param propertyName The name of the property to check for
285 * @return true if the object has the property, otherwise false
286 */
287 bool hasProperty(ExecState *exec, const Identifier &propertyName) const;
288 bool hasProperty(ExecState *exec, unsigned propertyName) const;
289
290 /**
291 * Removes the specified property from the object.
292 *
293 * See ECMA 8.6.2.5
294 *
295 * @param exec The current execution state
296 * @param propertyName The name of the property to delete
297 * @return true if the property was successfully deleted or did not
298 * exist on the object. false if deleting the specified property is not
299 * allowed.
300 */
301 virtual bool deleteProperty(ExecState *exec, const Identifier &propertyName);
302 virtual bool deleteProperty(ExecState *exec, unsigned propertyName);
303
304 /**
305 * Converts the object into a primitive value. The value return may differ
306 * depending on the supplied hint
307 *
308 * See ECMA 8.6.2.6
309 *
310 * @param exec The current execution state
311 * @param hint The desired primitive type to convert to
312 * @return A primitive value converted from the objetc. Note that the
313 * type of primitive value returned may not be the same as the requested
314 * hint.
315 */
316 /**
317 * Implementation of the [[DefaultValue]] internal property (implemented by
318 * all Objects)
319 */
320 virtual JSValue *defaultValue(ExecState *exec, JSType hint) const;
321
322 /**
323 * Whether or not the object implements the construct() method. If this
324 * returns false you should not call the construct() method on this
325 * object (typically, an assertion will fail to indicate this).
326 *
327 * @return true if this object implements the construct() method, otherwise
328 * false
329 */
330 virtual bool implementsConstruct() const;
331
332 /**
333 * Creates a new object based on this object. Typically this means the
334 * following:
335 * 1. A new object is created
336 * 2. The prototype of the new object is set to the value of this object's
337 * "prototype" property
338 * 3. The call() method of this object is called, with the new object
339 * passed as the this value
340 * 4. The new object is returned
341 *
342 * In some cases, Host objects may differ from these semantics, although
343 * this is discouraged.
344 *
345 * If an error occurs during construction, the execution state's exception
346 * will be set. This can be tested for with ExecState::hadException().
347 * Under some circumstances, the exception object may also be returned.
348 *
349 * Note: This function should not be called if implementsConstruct() returns
350 * false, in which case it will result in an assertion failure.
351 *
352 * @param exec The current execution state
353 * @param args The arguments to be passed to call() once the new object has
354 * been created
355 * @return The newly created &amp; initialized object
356 */
357 /**
358 * Implementation of the [[Construct]] internal property
359 */
360 virtual JSObject* construct(ExecState* exec, const List& args);
361 virtual JSObject* construct(ExecState* exec, const List& args, const Identifier& functionName, const UString& sourceURL, int lineNumber);
362
363 /**
364 * Whether or not the object implements the call() method. If this returns
365 * false you should not call the call() method on this object (typically,
366 * an assertion will fail to indicate this).
367 *
368 * @return true if this object implements the call() method, otherwise
369 * false
370 */
371 virtual bool implementsCall() const;
372
373 /**
374 * Calls this object as if it is a function.
375 *
376 * Note: This function should not be called if implementsCall() returns
377 * false, in which case it will result in an assertion failure.
378 *
379 * See ECMA 8.6.2.3
380 *
381 * @param exec The current execution state
382 * @param thisObj The obj to be used as "this" within function execution.
383 * Note that in most cases this will be different from the C++ "this"
384 * object. For example, if the ECMAScript code "window.location->toString()"
385 * is executed, call() will be invoked on the C++ object which implements
386 * the toString method, with the thisObj being window.location
387 * @param args List of arguments to be passed to the function
388 * @return The return value from the function
389 */
390 JSValue *call(ExecState *exec, JSObject *thisObj, const List &args);
391 virtual JSValue *callAsFunction(ExecState *exec, JSObject *thisObj, const List &args);
392
393 /**
394 * Whether or not the object implements the hasInstance() method. If this
395 * returns false you should not call the hasInstance() method on this
396 * object (typically, an assertion will fail to indicate this).
397 *
398 * @return true if this object implements the hasInstance() method,
399 * otherwise false
400 */
401 virtual bool implementsHasInstance() const;
402
403 /**
404 * Checks whether value delegates behavior to this object. Used by the
405 * instanceof operator.
406 *
407 * @param exec The current execution state
408 * @param value The value to check
409 * @return true if value delegates behavior to this object, otherwise
410 * false
411 */
412 virtual bool hasInstance(ExecState *exec, JSValue *value);
413
414 /**
415 * Returns the scope of this object. This is used when execution declared
416 * functions - the execution context for the function is initialized with
417 * extra object in it's scope. An example of this is functions declared
418 * inside other functions:
419 *
420 * \code
421 * function f() {
422 *
423 * function b() {
424 * return prototype;
425 * }
426 *
427 * var x = 4;
428 * // do some stuff
429 * }
430 * f.prototype = new String();
431 * \endcode
432 *
433 * When the function f.b is executed, its scope will include properties of
434 * f. So in the example above the return value of f.b() would be the new
435 * String object that was assigned to f.prototype.
436 *
437 * @param exec The current execution state
438 * @return The function's scope
439 */
440 const ScopeChain &scope() const { return _scope; }
441 void setScope(const ScopeChain &s) { _scope = s; }
442
443 /**
444 * Returns a List of References to all the properties of the object. Used
445 * in "for x in y" statements. The list is created new, so it can be freely
446 * modified without affecting the object's properties. It should be deleted
447 * by the caller.
448 *
449 * Subclasses can override this method in ObjectImpl to provide the
450 * appearance of
451 * having extra properties other than those set specifically with put().
452 *
453 * @param exec The current execution state
454 * @param recursive Whether or not properties in the object's prototype
455 * chain should be
456 * included in the list.
457 * @return A List of References to properties of the object.
458 **/
459 virtual void getPropertyList(ExecState *exec, ReferenceList& propertyList, bool recursive = true);
460
461 /**
462 * Returns the internal value of the object. This is used for objects such
463 * as String and Boolean which are wrappers for native types. The interal
464 * value is the actual value represented by the wrapper objects.
465 *
466 * @see ECMA 8.6.2
467 * @return The internal value of the object
468 */
469 JSValue *internalValue() const;
470
471 /**
472 * Sets the internal value of the object
473 *
474 * @see internalValue()
475 *
476 * @param v The new internal value
477 */
478 void setInternalValue(JSValue *v);
479
480 virtual JSValue *toPrimitive(ExecState *exec, JSType preferredType = UnspecifiedType) const;
481 virtual bool toBoolean(ExecState *exec) const;
482 virtual double toNumber(ExecState *exec) const;
483 virtual UString toString(ExecState *exec) const;
484 virtual JSObject *toObject(ExecState *exec) const;
485
486 bool getPropertyAttributes(const Identifier& propertyName, unsigned& attributes) const;
487
488 // Returns whether the object should be treated as undefined when doing equality comparisons
489 virtual bool masqueradeAsUndefined() const { return false; }
490
491 // This get function only looks at the property map.
492 // This is used e.g. by lookupOrCreateFunction (to cache a function, we don't want
493 // to look up in the prototype, it might already exist there)
494 JSValue *getDirect(const Identifier& propertyName) const
495 { return _prop.get(propertyName); }
496 JSValue **getDirectLocation(const Identifier& propertyName)
497 { return _prop.getLocation(propertyName); }
498 void putDirect(const Identifier &propertyName, JSValue *value, int attr = 0);
499 void putDirect(const Identifier &propertyName, int value, int attr = 0);
500
501 // convenience to add a function property under the function's own built-in name
502 void putDirectFunction(InternalFunctionImp*, int attr = 0);
503
504 void fillGetterPropertySlot(PropertySlot& slot, JSValue **location);
505
506 void defineGetter(ExecState *exec, const Identifier& propertyName, JSObject *getterFunc);
507 void defineSetter(ExecState *exec, const Identifier& propertyName, JSObject *setterFunc);
508
509 /**
510 * Remove all properties from this object.
511 * This doesn't take DontDelete into account, and isn't in the ECMA spec.
512 * It's simply a quick way to remove everything stored in the property map.
513 */
514 void clearProperties() { _prop.clear(); }
515
516 void saveProperties(SavedProperties &p) const { _prop.save(p); }
517 void restoreProperties(const SavedProperties &p) { _prop.restore(p); }
518
519 virtual bool isActivation() { return false; }
520 protected:
521 PropertyMap _prop;
522 private:
523 const HashEntry* findPropertyHashEntry( const Identifier& propertyName ) const;
524 JSValue *_proto;
525 JSValue *_internalValue;
526 ScopeChain _scope;
527 };
528
529 /**
530 * Types of Native Errors available. For custom errors, GeneralError
531 * should be used.
532 */
533 enum ErrorType { GeneralError = 0,
534 EvalError = 1,
535 RangeError = 2,
536 ReferenceError = 3,
537 SyntaxError = 4,
538 TypeError = 5,
539 URIError = 6};
540
541 /**
542 * @short Factory methods for error objects.
543 */
544 class Error {
545 public:
546 /**
547 * Factory method for error objects.
548 *
549 * @param exec The current execution state
550 * @param errtype Type of error.
551 * @param message Optional error message.
552 * @param lineNumber Optional line number.
553 * @param sourceId Optional source id.
554 * @param sourceURL Optional source URL.
555 */
556 static JSObject *create(ExecState *, ErrorType, const UString &message, int lineNumber, int sourceId, const UString &sourceURL);
557 static JSObject *create(ExecState *, ErrorType, const char *message);
558
559 /**
560 * Array of error names corresponding to ErrorType
561 */
562 static const char * const * const errorNames;
563 };
564
565JSObject *throwError(ExecState *, ErrorType, const UString &message, int lineNumber, int sourceId, const UString &sourceURL);
566JSObject *throwError(ExecState *, ErrorType, const UString &message);
567JSObject *throwError(ExecState *, ErrorType, const char *message);
568JSObject *throwError(ExecState *, ErrorType);
569
570inline JSObject::JSObject(JSValue* proto, bool destructorIsThreadSafe)
571 : JSCell(destructorIsThreadSafe)
572 , _proto(proto)
573 , _internalValue(0)
574{
575 assert(proto);
576}
577
578inline JSObject::JSObject(bool destructorIsThreadSafe)
579 : JSCell(destructorIsThreadSafe)
580 , _proto(jsNull())
581 , _internalValue(0)
582{
583}
584
585inline JSValue *JSObject::internalValue() const
586{
587 return _internalValue;
588}
589
590inline void JSObject::setInternalValue(JSValue *v)
591{
592 _internalValue = v;
593}
594
595inline JSValue *JSObject::prototype() const
596{
597 return _proto;
598}
599
600inline void JSObject::setPrototype(JSValue *proto)
601{
602 assert(proto);
603 _proto = proto;
604}
605
606inline bool JSObject::inherits(const ClassInfo *info) const
607{
608 for (const ClassInfo *ci = classInfo(); ci; ci = ci->parentClass)
609 if (ci == info)
610 return true;
611 return false;
612}
613
614// this method is here to be after the inline declaration of JSObject::inherits
615inline bool JSCell::isObject(const ClassInfo *info) const
616{
617 return isObject() && static_cast<const JSObject *>(this)->inherits(info);
618}
619
620// this method is here to be after the inline declaration of JSCell::isObject
621inline bool JSValue::isObject(const ClassInfo *c) const
622{
623 return !JSImmediate::isImmediate(this) && downcast()->isObject(c);
624}
625
626// It may seem crazy to inline a function this large but it makes a big difference
627// since this is function very hot in variable lookup
628inline bool JSObject::getPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
629{
630 JSObject *object = this;
631 while (true) {
632 if (object->getOwnPropertySlot(exec, propertyName, slot))
633 return true;
634
635 JSValue *proto = object->_proto;
636 if (!proto->isObject())
637 return false;
638
639 object = static_cast<JSObject *>(proto);
640 }
641}
642
643// It may seem crazy to inline a function this large, especially a virtual function,
644// but it makes a big difference to property lookup that derived classes can inline their
645// base class call to this.
646ALWAYS_INLINE bool JSObject::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
647{
648 if (JSValue **location = getDirectLocation(propertyName)) {
649 if (_prop.hasGetterSetterProperties() && location[0]->type() == GetterSetterType)
650 fillGetterPropertySlot(slot, location);
651 else
652 slot.setValueSlot(this, location);
653 return true;
654 }
655
656 // non-standard Netscape extension
657 if (propertyName == exec->dynamicInterpreter()->specialPrototypeIdentifier()) {
658 slot.setValueSlot(this, &_proto);
659 return true;
660 }
661
662 return false;
663}
664
665// FIXME: Put this function in a separate file named something like scope_chain_mark.h -- can't put it in scope_chain.h since it depends on JSObject.
666
667inline void ScopeChain::mark()
668{
669 for (ScopeChainNode *n = _node; n; n = n->next) {
670 JSObject *o = n->object;
671 if (!o->marked())
672 o->mark();
673 }
674}
675
676inline void ScopeChain::release()
677{
678 // This function is only called by deref(),
679 // Deref ensures these conditions are true.
680 assert(_node && _node->refCount == 0);
681 ScopeChainNode *n = _node;
682 do {
683 ScopeChainNode *next = n->next;
684 delete n;
685 n = next;
686 } while (n && --n->refCount == 0);
687}
688
689inline JSValue* JSObject::toPrimitive(ExecState* exec, JSType preferredType) const
690{
691 return defaultValue(exec, preferredType);
692}
693
694} // namespace
695
696#endif // KJS_OBJECT_H
Note: See TracBrowser for help on using the repository browser.