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

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

Add fast multi-level scope lookup

Reviewed by Geoff, Darin and Weinig

Add logic and AST nodes to provide rapid variable resolution across
static scope boundaries. This also adds logic that allows us to skip
any static scopes that do not contain the variable to be resolved.

This results in a ~2.5% speedup in SunSpider, and gives a 25-30% speedup
in some simple and ad hoc closure and global variable access tests.

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