blob: 8a914ff5f553307c2baf423012005a63eb7e0e1c [file] [log] [blame]
// -*- c-basic-offset: 2 -*-
/*
* This file is part of the KDE libraries
* Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
* Copyright (C) 2001 Peter Kelly (pmk@post.com)
* Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#ifndef KJS_OBJECT_H
#define KJS_OBJECT_H
#include "CommonIdentifiers.h"
#include "ExecState.h"
#include "JSType.h"
#include "list.h"
#include "property_map.h"
#include "property_slot.h"
#include "scope_chain.h"
namespace KJS {
class InternalFunctionImp;
class PropertyNameArray;
struct HashEntry;
struct HashTable;
// ECMA 262-3 8.6.1
// Property attributes
enum Attribute { None = 0,
ReadOnly = 1 << 1, // property can be only read, not written
DontEnum = 1 << 2, // property doesn't appear in (for .. in ..)
DontDelete = 1 << 3, // property can't be deleted
Internal = 1 << 4, // an internal property, set to bypass checks
Function = 1 << 5, // property is a function - only used by static hashtables
GetterSetter = 1 << 6 }; // property is a getter or setter
/**
* Class Information
*/
struct ClassInfo {
/**
* A string denoting the class name. Example: "Window".
*/
const char* className;
/**
* Pointer to the class information of the base class.
* 0L if there is none.
*/
const ClassInfo* parentClass;
/**
* Static hash-table of properties.
*/
const HashTable* propHashTable;
};
// This is an internal value object which stores getter and setter functions
// for a property.
class GetterSetterImp : public JSCell {
public:
JSType type() const { return GetterSetterType; }
GetterSetterImp() : getter(0), setter(0) { }
virtual JSValue* toPrimitive(ExecState*, JSType preferred = UnspecifiedType) const;
virtual bool getPrimitiveNumber(ExecState*, double& number, JSValue*& value);
virtual bool toBoolean(ExecState *exec) const;
virtual double toNumber(ExecState *exec) const;
virtual UString toString(ExecState *exec) const;
virtual JSObject *toObject(ExecState *exec) const;
virtual void mark();
JSObject *getGetter() { return getter; }
void setGetter(JSObject *g) { getter = g; }
JSObject *getSetter() { return setter; }
void setSetter(JSObject *s) { setter = s; }
private:
JSObject *getter;
JSObject *setter;
};
class JSObject : public JSCell {
public:
/**
* Creates a new JSObject with the specified prototype
*
* @param proto The prototype
*/
JSObject(JSValue* proto);
/**
* Creates a new JSObject with a prototype of jsNull()
* (that is, the ECMAScript "null" value, not a null object pointer).
*/
JSObject();
virtual void mark();
virtual JSType type() const;
/**
* A pointer to a ClassInfo struct for this class. This provides a basic
* facility for run-time type information, and can be used to check an
* object's class an inheritance (see inherits()). This should
* always return a statically declared pointer, or 0 to indicate that
* there is no class information.
*
* This is primarily useful if you have application-defined classes that you
* wish to check against for casting purposes.
*
* For example, to specify the class info for classes FooImp and BarImp,
* where FooImp inherits from BarImp, you would add the following in your
* class declarations:
*
* \code
* class BarImp : public JSObject {
* virtual const ClassInfo *classInfo() const { return &info; }
* static const ClassInfo info;
* // ...
* };
*
* class FooImp : public JSObject {
* virtual const ClassInfo *classInfo() const { return &info; }
* static const ClassInfo info;
* // ...
* };
* \endcode
*
* And in your source file:
*
* \code
* const ClassInfo BarImp::info = { "Bar", 0, 0 }; // no parent class
* const ClassInfo FooImp::info = { "Foo", &BarImp::info, 0 };
* \endcode
*
* @see inherits()
*/
virtual const ClassInfo *classInfo() const;
/**
* Checks whether this object inherits from the class with the specified
* classInfo() pointer. This requires that both this class and the other
* class return a non-NULL pointer for their classInfo() methods (otherwise
* it will return false).
*
* For example, for two JSObject pointers obj1 and obj2, you can check
* if obj1's class inherits from obj2's class using the following:
*
* if (obj1->inherits(obj2->classInfo())) {
* // ...
* }
*
* If you have a handle to a statically declared ClassInfo, such as in the
* classInfo() example, you can check for inheritance without needing
* an instance of the other class:
*
* if (obj1->inherits(FooImp::info)) {
* // ...
* }
*
* @param cinfo The ClassInfo pointer for the class you want to check
* inheritance against.
* @return true if this object's class inherits from class with the
* ClassInfo pointer specified in cinfo
*/
bool inherits(const ClassInfo *cinfo) const;
// internal properties (ECMA 262-3 8.6.2)
/**
* Returns the prototype of this object. Note that this is not the same as
* the "prototype" property.
*
* See ECMA 8.6.2
*
* @return The object's prototype
*/
JSValue *prototype() const;
void setPrototype(JSValue *proto);
/**
* Returns the class name of the object
*
* See ECMA 8.6.2
*
* @return The object's class name
*/
/**
* Implementation of the [[Class]] internal property (implemented by all
* Objects)
*
* The default implementation uses classInfo().
* You should either implement classInfo(), or
* if you simply need a classname, you can reimplement className()
* instead.
*/
virtual UString className() const;
/**
* Retrieves the specified property from the object. If neither the object
* or any other object in it's prototype chain have the property, this
* function will return Undefined.
*
* See ECMA 8.6.2.1
*
* @param exec The current execution state
* @param propertyName The name of the property to retrieve
*
* @return The specified property, or Undefined
*/
JSValue *get(ExecState *exec, const Identifier &propertyName) const;
JSValue *get(ExecState *exec, unsigned propertyName) const;
bool getPropertySlot(ExecState *, const Identifier&, PropertySlot&);
bool getPropertySlot(ExecState *, unsigned, PropertySlot&);
virtual bool getOwnPropertySlot(ExecState *, const Identifier&, PropertySlot&);
virtual bool getOwnPropertySlot(ExecState *, unsigned index, PropertySlot&);
/**
* Sets the specified property.
*
* See ECMA 8.6.2.2
*
* @param exec The current execution state
* @param propertyName The name of the property to set
* @param propertyValue The value to set
*/
virtual void put(ExecState* exec, const Identifier &propertyName, JSValue* value, int attr = None);
virtual void put(ExecState* exec, unsigned propertyName, JSValue* value, int attr = None);
/**
* Used to check whether or not a particular property is allowed to be set
* on an object
*
* See ECMA 8.6.2.3
*
* @param exec The current execution state
* @param propertyName The name of the property
* @return true if the property can be set, otherwise false
*/
/**
* Implementation of the [[CanPut]] internal property (implemented by all
* Objects)
*/
virtual bool canPut(ExecState *exec, const Identifier &propertyName) const;
/**
* Checks if a property is enumerable, that is if it doesn't have the DontEnum
* flag set
*
* See ECMA 15.2.4
* @param exec The current execution state
* @param propertyName The name of the property
* @return true if the property is enumerable, otherwise false
*/
bool propertyIsEnumerable(ExecState *exec, const Identifier &propertyName) const;
/**
* Checks to see whether the object (or any object in it's prototype chain)
* has a property with the specified name.
*
* See ECMA 8.6.2.4
*
* @param exec The current execution state
* @param propertyName The name of the property to check for
* @return true if the object has the property, otherwise false
*/
bool hasProperty(ExecState*, const Identifier&) const;
bool hasProperty(ExecState*, unsigned) const;
bool hasOwnProperty(ExecState*, const Identifier&) const;
/**
* Removes the specified property from the object.
*
* See ECMA 8.6.2.5
*
* @param exec The current execution state
* @param propertyName The name of the property to delete
* @return true if the property was successfully deleted or did not
* exist on the object. false if deleting the specified property is not
* allowed.
*/
virtual bool deleteProperty(ExecState *exec, const Identifier &propertyName);
virtual bool deleteProperty(ExecState *exec, unsigned propertyName);
/**
* Converts the object into a primitive value. The value return may differ
* depending on the supplied hint
*
* See ECMA 8.6.2.6
*
* @param exec The current execution state
* @param hint The desired primitive type to convert to
* @return A primitive value converted from the objetc. Note that the
* type of primitive value returned may not be the same as the requested
* hint.
*/
/**
* Implementation of the [[DefaultValue]] internal property (implemented by
* all Objects)
*/
virtual JSValue *defaultValue(ExecState *exec, JSType hint) const;
/**
* Whether or not the object implements the construct() method. If this
* returns false you should not call the construct() method on this
* object (typically, an assertion will fail to indicate this).
*
* @return true if this object implements the construct() method, otherwise
* false
*/
virtual bool implementsConstruct() const;
/**
* Creates a new object based on this object. Typically this means the
* following:
* 1. A new object is created
* 2. The prototype of the new object is set to the value of this object's
* "prototype" property
* 3. The call() method of this object is called, with the new object
* passed as the this value
* 4. The new object is returned
*
* In some cases, Host objects may differ from these semantics, although
* this is discouraged.
*
* If an error occurs during construction, the execution state's exception
* will be set. This can be tested for with ExecState::hadException().
* Under some circumstances, the exception object may also be returned.
*
* Note: This function should not be called if implementsConstruct() returns
* false, in which case it will result in an assertion failure.
*
* @param exec The current execution state
* @param args The arguments to be passed to call() once the new object has
* been created
* @return The newly created &amp; initialized object
*/
/**
* Implementation of the [[Construct]] internal property
*/
virtual JSObject* construct(ExecState* exec, const List& args);
virtual JSObject* construct(ExecState* exec, const List& args, const Identifier& functionName, const UString& sourceURL, int lineNumber);
/**
* Whether or not the object implements the call() method. If this returns
* false you should not call the call() method on this object (typically,
* an assertion will fail to indicate this).
*
* @return true if this object implements the call() method, otherwise
* false
*/
virtual bool implementsCall() const;
/**
* Calls this object as if it is a function.
*
* Note: This function should not be called if implementsCall() returns
* false, in which case it will result in an assertion failure.
*
* See ECMA 8.6.2.3
*
* @param exec The current execution state
* @param thisObj The obj to be used as "this" within function execution.
* Note that in most cases this will be different from the C++ "this"
* object. For example, if the ECMAScript code "window.location->toString()"
* is executed, call() will be invoked on the C++ object which implements
* the toString method, with the thisObj being window.location
* @param args List of arguments to be passed to the function
* @return The return value from the function
*/
JSValue *call(ExecState *exec, JSObject *thisObj, const List &args);
virtual JSValue *callAsFunction(ExecState *exec, JSObject *thisObj, const List &args);
/**
* Whether or not the object implements the hasInstance() method. If this
* returns false you should not call the hasInstance() method on this
* object (typically, an assertion will fail to indicate this).
*
* @return true if this object implements the hasInstance() method,
* otherwise false
*/
virtual bool implementsHasInstance() const;
/**
* Checks whether value delegates behavior to this object. Used by the
* instanceof operator.
*
* @param exec The current execution state
* @param value The value to check
* @return true if value delegates behavior to this object, otherwise
* false
*/
virtual bool hasInstance(ExecState *exec, JSValue *value);
virtual void getPropertyNames(ExecState*, PropertyNameArray&);
virtual JSValue* toPrimitive(ExecState*, JSType preferredType = UnspecifiedType) const;
virtual bool getPrimitiveNumber(ExecState*, double& number, JSValue*& value);
virtual bool toBoolean(ExecState *exec) const;
virtual double toNumber(ExecState *exec) const;
virtual UString toString(ExecState *exec) const;
virtual JSObject *toObject(ExecState *exec) const;
bool getPropertyAttributes(const Identifier& propertyName, unsigned& attributes) const;
// WebCore uses this to make document.all and style.filter undetectable
virtual bool masqueradeAsUndefined() const { return false; }
// This get function only looks at the property map.
// This is used e.g. by lookupOrCreateFunction (to cache a function, we don't want
// to look up in the prototype, it might already exist there)
JSValue *getDirect(const Identifier& propertyName) const
{ return _prop.get(propertyName); }
JSValue **getDirectLocation(const Identifier& propertyName)
{ return _prop.getLocation(propertyName); }
void putDirect(const Identifier &propertyName, JSValue *value, int attr = 0);
void putDirect(const Identifier &propertyName, int value, int attr = 0);
void removeDirect(const Identifier &propertyName);
// convenience to add a function property under the function's own built-in name
void putDirectFunction(InternalFunctionImp*, int attr = 0);
void fillGetterPropertySlot(PropertySlot& slot, JSValue **location);
void defineGetter(ExecState *exec, const Identifier& propertyName, JSObject *getterFunc);
void defineSetter(ExecState *exec, const Identifier& propertyName, JSObject *setterFunc);
void saveProperties(SavedProperties &p) const { _prop.save(p); }
void restoreProperties(const SavedProperties &p) { _prop.restore(p); }
virtual bool isActivationObject() { return false; }
virtual bool isGlobalObject() const { return false; }
protected:
PropertyMap _prop;
private:
const HashEntry* findPropertyHashEntry( const Identifier& propertyName ) const;
JSValue *_proto;
};
/**
* Types of Native Errors available. For custom errors, GeneralError
* should be used.
*/
enum ErrorType { GeneralError = 0,
EvalError = 1,
RangeError = 2,
ReferenceError = 3,
SyntaxError = 4,
TypeError = 5,
URIError = 6};
/**
* @short Factory methods for error objects.
*/
class Error {
public:
/**
* Factory method for error objects.
*
* @param exec The current execution state
* @param errtype Type of error.
* @param message Optional error message.
* @param lineNumber Optional line number.
* @param sourceId Optional source id.
* @param sourceURL Optional source URL.
*/
static JSObject *create(ExecState *, ErrorType, const UString &message, int lineNumber, int sourceId, const UString &sourceURL);
static JSObject *create(ExecState *, ErrorType, const char *message);
/**
* Array of error names corresponding to ErrorType
*/
static const char * const * const errorNames;
};
JSObject *throwError(ExecState *, ErrorType, const UString &message, int lineNumber, int sourceId, const UString &sourceURL);
JSObject *throwError(ExecState *, ErrorType, const UString &message);
JSObject *throwError(ExecState *, ErrorType, const char *message);
JSObject *throwError(ExecState *, ErrorType);
inline JSObject::JSObject(JSValue* proto)
: _proto(proto)
{
ASSERT(proto);
}
inline JSObject::JSObject()
: _proto(jsNull())
{
}
inline JSValue *JSObject::prototype() const
{
return _proto;
}
inline void JSObject::setPrototype(JSValue *proto)
{
ASSERT(proto);
_proto = proto;
}
inline bool JSObject::inherits(const ClassInfo *info) const
{
for (const ClassInfo *ci = classInfo(); ci; ci = ci->parentClass)
if (ci == info)
return true;
return false;
}
// this method is here to be after the inline declaration of JSObject::inherits
inline bool JSCell::isObject(const ClassInfo *info) const
{
return isObject() && static_cast<const JSObject *>(this)->inherits(info);
}
// this method is here to be after the inline declaration of JSCell::isObject
inline bool JSValue::isObject(const ClassInfo *c) const
{
return !JSImmediate::isImmediate(this) && asCell()->isObject(c);
}
// It may seem crazy to inline a function this large but it makes a big difference
// since this is function very hot in variable lookup
inline bool JSObject::getPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
{
JSObject *object = this;
while (true) {
if (object->getOwnPropertySlot(exec, propertyName, slot))
return true;
JSValue *proto = object->_proto;
if (!proto->isObject())
return false;
object = static_cast<JSObject *>(proto);
}
}
// It may seem crazy to inline a function this large, especially a virtual function,
// but it makes a big difference to property lookup that derived classes can inline their
// base class call to this.
ALWAYS_INLINE bool JSObject::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
{
if (JSValue **location = getDirectLocation(propertyName)) {
if (_prop.hasGetterSetterProperties() && location[0]->type() == GetterSetterType)
fillGetterPropertySlot(slot, location);
else
slot.setValueSlot(this, location);
return true;
}
// non-standard Netscape extension
if (propertyName == exec->propertyNames().underscoreProto) {
slot.setValueSlot(this, &_proto);
return true;
}
return false;
}
inline void ScopeChain::release()
{
// This function is only called by deref(),
// Deref ensures these conditions are true.
ASSERT(_node && _node->refCount == 0);
ScopeChainNode *n = _node;
do {
ScopeChainNode *next = n->next;
delete n;
n = next;
} while (n && --n->refCount == 0);
}
inline JSValue* JSObject::toPrimitive(ExecState* exec, JSType preferredType) const
{
return defaultValue(exec, preferredType);
}
} // namespace
#endif // KJS_OBJECT_H