Script integration work - valueOf() is no longer required most of the time; also property-change watches and enumeration over entities (but not yet other native objects), rudimentary beginnings of a global events system, and adjusted the status orb to update only on changes.

This was SVN commit r2157.
This commit is contained in:
MarkT
2005-04-22 07:12:55 +00:00
parent 498d58ee61
commit bcabe3aa53
38 changed files with 942 additions and 959 deletions
+214 -626
View File
@@ -4,36 +4,6 @@
//
// Mark Thompson (mark@wildfiregames.com / mot20@cam.ac.uk)
//
// General idea:
//
// IJSProperty is the interface representing a property of an object.
// Objects contain a mapping of names->IJSProperties
// Some IJSProperties wrap C++ variables that are declared by the engine
// Others wrap pairs of getter/setter functions
// Most, however, wrap a jsval and are defined by scripts and XML files.
// Objects may also have a parent object. If an attempt is made to
// access a property that doesn't exist in this object, the parent's object
// is checked, and so on.
// To allow this parent system to work for C++ properties, when a C++
// is set on an object, it's also set on any object that inherits it
// (Unless that object specifies its own value for that property, or
// the property is marked as being uninheritable)
// Objects and properties may be flagged read-only, causing all attempts to
// set values to become no-ops. If an object is read-only, all properties are
// - even ones that are flagged as writable.
// Usage: Create a class CSomething inheriting CJSObject<CSomething>
// In CSomething's constructor, add properties to the new object with
// AddProperty( name, pointer-to-C++-variable ).
// Also, ScriptingInit( "Some Name" ) must be called at initialization
// - put it in main. There's also AddMethod<ReturnType, NativeFunction>( Name,
// MinArgs ) - call that at initialization, too.
// If you include data members or functions that return types that JSConversions.h
// doesn't handle sensibly, you need to make it do so.
// If you're looking for examples, DOMEvent.h is the simplest user of this class
// CBaseEntity and CEntity do also (and use other stuff not mentioned above)
// but are more complex.
#include "scripting/ScriptingHost.h"
#include "JSConversions.h"
@@ -41,72 +11,27 @@
#ifndef SCRIPTABLE_INCLUDED
#define SCRIPTABLE_INCLUDED
#define ALLOW_NONSHARED_NATIVES
class IJSObject;
class IJSProperty
{
public:
bool m_AllowsInheritance;
bool m_Inherited;
bool m_Intrinsic;
// This is to make sure that all the fields are initialized at construction
inline IJSProperty():
m_AllowsInheritance(true),
m_Inherited(true),
m_Intrinsic(true)
{}
virtual jsval Get( JSContext* cx, IJSObject* owner ) = 0;
virtual void Set( JSContext* cx, IJSObject* owner, jsval Value ) = 0;
// Copies the data directly out of a parent property
// Warning: Don't use if you're not certain the properties are not of the same type.
virtual void ImmediateCopy( IJSObject* CopyTo, IJSObject* CopyFrom, IJSProperty* CopyProperty ) = 0;
jsval Get( IJSObject* owner ) { return( Get( g_ScriptingHost.GetContext(), owner ) ); }
void Set( IJSObject* owner, jsval Value ) { return( Set( g_ScriptingHost.GetContext(), owner, Value ) ); }
virtual ~IJSProperty() {}
virtual jsval Get( JSContext* cx, IJSObject* obj ) = 0;
virtual void Set( JSContext* cx, IJSObject* obj, jsval value ) = 0;
};
class IJSObject
{
// Make copy constructor and assignment operator private - since copying of
// these objects is unsafe unless done specially.
// These will never be implemented (they are, after all, here to *prevent*
// copying)
IJSObject(const IJSObject &other);
IJSObject& operator=(const IJSObject &other);
public:
typedef STL_HASH_MAP<CStrW, IJSProperty*, CStrW_hash_compare> PropertyTable;
typedef std::vector<IJSObject*> InheritorsList;
// Used for freshen/update
typedef void (IJSObject::*NotifyFn)();
// Property getters and setters
typedef jsval (IJSObject::*GetFn)();
typedef void (IJSObject::*SetFn)( jsval );
typedef void (IJSObject::*SetFn)( jsval value );
// Properties of this object
PropertyTable m_Properties;
// Parent object
IJSObject* m_Parent;
// Objects that inherit from this
InheritorsList m_Inheritors;
// Set the base, and rebuild
void SetBase( IJSObject* m_Parent );
// Rebuild any intrinsic (mapped-to-C++-variable) properties
virtual void Rebuild() = 0;
// Check for a property
// Return a pointer to a property, if it exists
virtual IJSProperty* HasProperty( CStrW PropertyName ) = 0;
// Retrieve the value of a property (returning false if that property is not defined)
@@ -121,263 +46,49 @@ public:
template<typename T, bool ReadOnly = false> class CJSObject;
template<typename T> class CJSPropertyAccessor
{
T* m_Owner;
CStrW m_PropertyRoot;
template<typename Q, bool ReadOnly> friend class CJSObject;
public:
CJSPropertyAccessor( T* Owner, CStrW PropertyRoot )
{
m_Owner = Owner;
m_PropertyRoot = PropertyRoot;
}
static JSObject* CreateAccessor( JSContext* cx, T* Owner, CStrW PropertyRoot )
{
JSObject* Accessor = JS_NewObject( cx, &JSI_Class, NULL, NULL );
JS_SetPrivate( cx, Accessor, new CJSPropertyAccessor( Owner, PropertyRoot ) );
return( Accessor );
}
static JSBool JSGetProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp )
{
CJSPropertyAccessor* Instance = (CJSPropertyAccessor*)JS_GetPrivate( cx, obj );
if( !Instance ) return( JS_TRUE );
CStrW PropName = Instance->m_PropertyRoot + CStrW( L"." ) + g_ScriptingHost.ValueToUCString( id );
Instance->m_Owner->GetProperty( cx, PropName, vp );
return( JS_TRUE );
}
static JSBool JSSetProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp )
{
CJSPropertyAccessor* Instance = (CJSPropertyAccessor*)JS_GetPrivate( cx, obj );
if( !Instance ) return( JS_TRUE );
CStrW PropName = g_ScriptingHost.ValueToUCString( id );
Instance->m_Owner->SetProperty( cx, Instance->m_PropertyRoot + CStrW( L"." ) + PropName, vp );
return( JS_TRUE );
}
static JSBool JSPrimitive( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval )
{
CJSPropertyAccessor* Instance = (CJSPropertyAccessor*)JS_GetPrivate( cx, obj );
if( !Instance ) return( JS_TRUE );
// Check all along the inheritance tree
// Possible optimization: Store the hashed value over the lookups
IJSObject* Target = Instance->m_Owner;
IJSProperty* Property;
while( Target )
{
Property = Target->HasProperty( Instance->m_PropertyRoot );
if( Property )
{
*rval = Property->Get( cx, Target );
break;
}
Target = Target->m_Parent;
}
return( JS_TRUE );
}
static JSBool JSToString( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval )
{
CJSPropertyAccessor* Instance = (CJSPropertyAccessor*)JS_GetPrivate( cx, obj );
if( !Instance ) return( JS_TRUE );
// Check all along the inheritance tree
// TODO: Optimization: Store the hashed value over the lookups
IJSObject* Target = Instance->m_Owner;
IJSProperty* Property;
JSString* str;
while( Target )
{
Property = Target->HasProperty( Instance->m_PropertyRoot );
if( Property )
{
str = JS_ValueToString( cx, Property->Get( cx, Target ) );
break;
}
Target = Target->m_Parent;
}
*rval = STRING_TO_JSVAL( str );
return( JS_TRUE );
}
static JSClass JSI_Class;
static void ScriptingInit()
{
JSFunctionSpec JSI_methods[] = { { "valueOf", JSPrimitive, 0, 0, 0 }, { "toString", JSToString, 0, 0, 0 }, { 0 } };
JSPropertySpec JSI_props[] = { { 0 } };
g_ScriptingHost.DefineCustomObjectType( &JSI_Class, NULL, 0, JSI_props, JSI_methods, NULL, NULL );
}
};
template<typename T> JSClass CJSPropertyAccessor<T>::JSI_Class = {
"Property", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub,
JSGetProperty, JSSetProperty,
JS_EnumerateStub, JS_ResolveStub,
JS_ConvertStub, JS_FinalizeStub,
NULL, NULL, NULL, NULL
};
template<typename T, bool ReadOnly> class CJSSharedProperty : public IJSProperty
template<typename T, bool ReadOnly> class CJSProperty : public IJSProperty
{
T IJSObject::*m_Data;
// Function on Owner to call after value is changed
IJSObject::NotifyFn m_Update;
// Function on Owner to call before reading or writing the value
IJSObject::NotifyFn m_Freshen;
public:
CJSSharedProperty( T IJSObject::*Data, bool AllowsInheritance = false, IJSObject::NotifyFn Update = NULL, IJSObject::NotifyFn Freshen = NULL )
CJSProperty( T IJSObject::*Data )
{
m_Data = Data;
m_AllowsInheritance = AllowsInheritance;
m_Update = Update;
m_Freshen = Freshen;
m_Intrinsic = true;
m_Inherited = true;
}
jsval Get( JSContext* cx, IJSObject* owner )
{
if( m_Freshen ) (owner->*m_Freshen)();
return( ToJSVal( owner->*m_Data ) );
}
void ImmediateCopy( IJSObject* CopyTo, IJSObject* CopyFrom, IJSProperty* CopyProperty )
{
debug_warn( "Inheritance not supported for CJSSharedProperties" );
}
void Set( JSContext* cx, IJSObject* owner, jsval Value )
{
if( !ReadOnly )
{
if( m_Freshen ) (owner->*m_Freshen)();
if( ToPrimitive( cx, Value, owner->*m_Data ) )
if( m_Update ) (owner->*m_Update)();
}
ToPrimitive( cx, Value, owner->*m_Data );
}
};
template<typename T, bool ReadOnly> class CJSProperty : public IJSProperty
#ifdef ALLOW_NONSHARED_NATIVES
template<typename T, bool ReadOnly> class CJSNonsharedProperty : public IJSProperty
{
T* m_Data;
// Function on Owner to call after value is changed
IJSObject::NotifyFn m_Update;
// Function on Owner to call before reading or writing the value
IJSObject::NotifyFn m_Freshen;
public:
CJSProperty( T* Data, bool AllowsInheritance = false, IJSObject::NotifyFn Update = NULL, IJSObject::NotifyFn Freshen = NULL )
CJSNonsharedProperty( T* Data )
{
m_Data = Data;
m_AllowsInheritance = AllowsInheritance;
m_Update = Update;
m_Freshen = Freshen;
m_Intrinsic = true;
}
jsval Get( JSContext* cx, IJSObject* owner )
{
if( m_Freshen ) (owner->*m_Freshen)();
return( ToJSVal( *m_Data ) );
}
void ImmediateCopy( IJSObject* CopyTo, IJSObject* CopyFrom, IJSProperty* CopyProperty )
{
*m_Data = *( ( (CJSProperty*)CopyProperty )->m_Data );
}
void Set( JSContext* cx, IJSObject* owner, jsval Value )
{
if( !ReadOnly )
{
if( m_Freshen ) (owner->*m_Freshen)();
if( ToPrimitive( cx, Value, *m_Data ) )
if( m_Update ) (owner->*m_Update)();
}
}
};
class CJSReflector
{
template<typename Q, bool ReadOnly> friend class CJSObject;
JSObject* m_JSAccessor;
};
class CJSDynamicProperty : public IJSProperty
{
template<typename Q, bool ReadOnly> friend class CJSObject;
JSObject* m_JSAccessor;
public:
CJSDynamicProperty()
{
m_JSAccessor = NULL;
m_Intrinsic = false;
m_Inherited = false;
ToPrimitive( cx, Value, *m_Data );
}
};
class CJSValProperty : public CJSDynamicProperty
{
template<typename Q, bool ReadOnly> friend class CJSObject;
jsval m_Data;
public:
CJSValProperty( jsval Data, bool Inherited )
{
m_Inherited = Inherited;
m_Data = Data;
Root();
}
~CJSValProperty()
{
Uproot();
}
void Root()
{
if( JSVAL_IS_GCTHING( m_Data ) )
#ifndef NDEBUG
JS_AddNamedRoot( g_ScriptingHost.GetContext(), (void*)&m_Data, "jsval property" );
#else
JS_AddRoot( g_ScriptingHost.GetContext(), (void*)&m_Data );
#endif
}
void Uproot()
{
if( JSVAL_IS_GCTHING( m_Data ) )
JS_RemoveRoot( g_ScriptingHost.GetContext(), (void*)&m_Data );
}
jsval Get( JSContext* cx, IJSObject* owner )
{
return( m_Data );
}
void Set( JSContext* cx, IJSObject* owner, jsval Value )
{
Uproot();
m_Data = Value;
Root();
}
void ImmediateCopy( IJSObject* CopyTo, IJSObject* CopyFrom, IJSProperty* CopyProperty )
{
assert( 0 && "ImmediateCopy called on a CJSValProperty (something's gone wrong with the inheritance on this object)" );
}
};
#endif /* ALLOW_NONSHARED_NATIVES */
class CJSFunctionProperty : public IJSProperty
{
@@ -390,28 +101,59 @@ class CJSFunctionProperty : public IJSProperty
public:
CJSFunctionProperty( IJSObject::GetFn Getter, IJSObject::SetFn Setter )
{
m_Inherited = false;
m_Intrinsic = true;
m_Getter = Getter;
m_Setter = Setter;
// Must at least be able to read
assert( m_Getter );
}
jsval Get( JSContext* cx, IJSObject* owner )
jsval Get( JSContext* cx, IJSObject* obj )
{
return( (owner->*m_Getter)() );
return( (obj->*m_Getter)() );
}
void Set( JSContext* cx, IJSObject* owner, jsval Value )
void Set( JSContext* cx, IJSObject* obj, jsval value )
{
if( m_Setter )
(owner->*m_Setter)( Value );
}
void ImmediateCopy( IJSObject* CopyTo, IJSObject* CopyFrom, IJSProperty* CopyProperty )
{
assert( 0 && "ImmediateCopy called on a property wrapping getter/setter functions (something's gone wrong with the inheritance for this object)" );
(obj->*m_Setter)( value );
}
};
class CJSValProperty : public IJSProperty
{
template<typename Q, bool ReadOnly> friend class CJSObject;
jsval m_Data;
public:
CJSValProperty( jsval Data )
{
m_Data = Data;
Root();
}
~CJSValProperty()
{
Uproot();
}
void Root()
{
if( JSVAL_IS_GCTHING( m_Data ) )
JS_AddRoot( g_ScriptingHost.GetContext(), (void*)&m_Data );
}
void Uproot()
{
if( JSVAL_IS_GCTHING( m_Data ) )
JS_RemoveRoot( g_ScriptingHost.GetContext(), (void*)&m_Data );
}
jsval Get( JSContext* cx, IJSObject* object )
{
return( m_Data );
}
void Set( JSContext* cx, IJSObject* owner, jsval value )
{
Uproot();
m_Data = value;
Root();
}
};
// Wrapper around native functions that are attached to CJSObjects
@@ -432,14 +174,17 @@ public:
template<typename T, bool ReadOnly> class CJSObject : public IJSObject
{
typedef STL_HASH_MAP<CStrW, CJSReflector*, CStrW_hash_compare> ReflectorTable;
// This object
JSObject* m_JS;
ReflectorTable m_Reflectors;
public:
static JSClass JSI_class;
protected:
// The properties defined by the engine
static PropertyTable m_NativeProperties;
#ifdef ALLOW_NONSHARED_NATIVES
PropertyTable m_NonsharedProperties;
#endif
// Properties added by script
PropertyTable m_ScriptProperties;
// Whether native code is responsible for managing this object.
// Script constructors should clear this *BEFORE* creating a JS
@@ -447,8 +192,45 @@ public:
bool m_EngineOwned;
public:
// Property getters and setters
typedef jsval (T::*TGetFn)();
typedef void (T::*TSetFn)( jsval value );
static JSClass JSI_class;
static void ScriptingInit( const char* ClassName, JSNative Constructor = NULL, uintN ConstructorMinArgs = 0 )
{
JSFunctionSpec* JSI_methods = new JSFunctionSpec[ m_Methods.size() + 1 ];
unsigned int MethodID;
for( MethodID = 0; MethodID < m_Methods.size(); MethodID++ )
JSI_methods[MethodID] = m_Methods[MethodID];
JSI_methods[MethodID].name = 0;
JSI_class.name = ClassName;
g_ScriptingHost.DefineCustomObjectType( &JSI_class, Constructor, ConstructorMinArgs, JSI_props, JSI_methods, NULL, NULL );
delete[]( JSI_methods );
atexit( ScriptingShutdown );
}
static void ScriptingShutdown()
{
PropertyTable::iterator it;
for( it = m_NativeProperties.begin(); it != m_NativeProperties.end(); it++ )
delete( it->second );
}
// JS Property access
bool GetProperty( JSContext* cx, CStrW PropertyName, jsval* vp );
bool GetProperty( JSContext* cx, CStrW PropertyName, jsval* vp )
{
IJSProperty* Property = HasProperty( PropertyName );
if( Property )
*vp = Property->Get( cx, this );
return( true );
}
void SetProperty( JSContext* cx, CStrW PropertyName, jsval* vp )
{
if( !ReadOnly )
@@ -459,29 +241,6 @@ public:
{
// Already exists
prop->Set( cx, this, *vp );
prop->m_Inherited = false;
// If it's a C++ property, reflect this change in objects that inherit this.
if( prop->m_AllowsInheritance && prop->m_Intrinsic )
{
InheritorsList UpdateSet( m_Inheritors );
while( !UpdateSet.empty() )
{
IJSObject* UpdateObj = UpdateSet.back();
UpdateSet.pop_back();
IJSProperty* UpdateProp = UpdateObj->HasProperty( PropertyName );
// Property must exist, also be a C++ property, and not have its value specified.
if( UpdateProp && UpdateProp->m_Intrinsic && UpdateProp->m_Inherited )
{
UpdateProp->Set( cx, this, *vp );
InheritorsList::iterator it2;
for( it2 = UpdateObj->m_Inheritors.begin(); it2 != UpdateObj->m_Inheritors.end(); it2++ )
UpdateSet.push_back( *it2 );
}
}
}
}
else
{
@@ -491,9 +250,110 @@ public:
}
}
IJSProperty* HasProperty( CStrW PropertyName )
{
PropertyTable::iterator it;
// Engine-defined properties take precedence
it = m_NativeProperties.find( PropertyName );
if( it != m_NativeProperties.end() )
return( it->second );
// Next are the object-local engine-defined properties
// (if they're compiled in)
#ifdef ALLOW_NONSHARED_NATIVES
it = m_NonsharedProperties.find( PropertyName );
if( it != m_NonsharedProperties.end() )
return( it->second );
#endif
// Then check in script properties
it = m_ScriptProperties.find( PropertyName );
if( it != m_ScriptProperties.end() )
return( it->second );
// Otherwise not found
return( NULL );
}
void AddProperty( CStrW PropertyName, jsval Value )
{
assert( !HasProperty( PropertyName ) );
CJSValProperty* newProp = new CJSValProperty( Value );
m_ScriptProperties[PropertyName] = newProp;
}
void AddProperty( CStrW PropertyName, CStrW Value )
{
AddProperty( PropertyName, JSParseString( Value ) );
}
static void AddProperty( CStrW PropertyName, TGetFn Getter, TSetFn Setter = NULL )
{
m_NativeProperties[PropertyName] = new CJSFunctionProperty( (GetFn)Getter, (SetFn)Setter );
}
template<typename ReturnType, ReturnType (T::*NativeFunction)( JSContext* cx, uintN argc, jsval* argv )>
static void AddMethod( const char* Name, uintN MinArgs )
{
JSFunctionSpec FnInfo = { Name, CNativeFunction<T, ReadOnly, ReturnType, NativeFunction>::JSFunction, MinArgs, 0, 0 };
m_Methods.push_back( FnInfo );
}
template<typename PropType> static void AddProperty( CStrW PropertyName, PropType T::*Native, bool PropReadOnly = ReadOnly )
{
IJSProperty* prop;
if( PropReadOnly )
{
prop = new CJSProperty<PropType, true>( (PropType IJSObject::*)Native );
}
else
prop = new CJSProperty<PropType, ReadOnly>( (PropType IJSObject::*)Native );
m_NativeProperties[PropertyName] = prop;
}
#ifdef ALLOW_NONSHARED_NATIVES
template<typename PropType> void AddLocalProperty( CStrW PropertyName, PropType* Native, bool PropReadOnly = ReadOnly )
{
IJSProperty* prop;
if( PropReadOnly )
{
prop = new CJSNonsharedProperty<PropType, true>( Native );
}
else
prop = new CJSNonsharedProperty<PropType, ReadOnly>( Native );
m_NonsharedProperties[PropertyName] = prop;
}
#endif
// Object operations
JSObject* GetScript()
{
if( !m_JS )
CreateScriptObject();
return( m_JS );
}
// Creating and releasing script objects is done automatically most of the time, but you
// can do it explicitly.
void CreateScriptObject()
{
if( !m_JS )
{
m_JS = JS_NewObject( g_ScriptingHost.GetContext(), &JSI_class, NULL, NULL );
if( m_EngineOwned )
JS_AddRoot( g_ScriptingHost.GetContext(), (void*)&m_JS );
JS_SetPrivate( g_ScriptingHost.GetContext(), m_JS, (T*)this );
}
}
void ReleaseScriptObject()
{
if( m_JS )
{
JS_SetPrivate( g_ScriptingHost.GetContext(), m_JS, NULL );
if( m_EngineOwned )
JS_RemoveRoot( g_ScriptingHost.GetContext(), &m_JS );
m_JS = NULL;
}
}
//
// Functions that must be provided to JavaScript
// Functions and data that must be provided to JavaScript
//
private:
static JSBool JSGetProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp )
{
T* Instance = ToNative<T>( cx, obj );
@@ -519,28 +379,6 @@ public:
return( JS_TRUE );
}
static void ScriptingInit( const char* ClassName, JSNative Constructor = NULL, uintN ConstructorMinArgs = 0 )
{
JSFunctionSpec* JSI_methods = new JSFunctionSpec[ m_Methods.size() + 1 ];
unsigned int MethodID;
for( MethodID = 0; MethodID < m_Methods.size(); MethodID++ )
JSI_methods[MethodID] = m_Methods[MethodID];
JSI_methods[MethodID].name = 0;
JSI_class.name = ClassName;
g_ScriptingHost.DefineCustomObjectType( &JSI_class, Constructor, ConstructorMinArgs, JSI_props, JSI_methods, NULL, NULL );
delete[]( JSI_methods );
atexit( ScriptingShutdown );
}
static void ScriptingShutdown()
{
PropertyTable::iterator it;
for( it = m_IntrinsicProperties.begin(); it != m_IntrinsicProperties.end(); it++ )
delete( it->second );
}
static void DefaultFinalize( JSContext *cx, JSObject *obj )
{
T* Instance = ToNative<T>( cx, obj );
@@ -551,52 +389,14 @@ public:
JS_SetPrivate( cx, obj, NULL );
}
public:
JSObject* GetScript()
{
if( !m_JS )
CreateScriptObject();
return( m_JS );
}
// Creating and releasing script objects is done automatically most of the time, but you
// can do it explicitly.
void CreateScriptObject()
{
if( !m_JS )
{
m_JS = JS_NewObject( g_ScriptingHost.GetContext(), &JSI_class, NULL, NULL );
if( m_EngineOwned )
{
#ifndef NDEBUG // Name the GC roots something more useful than 'ScriptableObject.h'
JS_AddNamedRoot( g_ScriptingHost.GetContext(), (void*)&m_JS, JSI_class.name );
#else
JS_AddRoot( g_ScriptingHost.GetContext(), (void*)&m_JS );
#endif
}
JS_SetPrivate( g_ScriptingHost.GetContext(), m_JS, (T*)this );
}
}
void ReleaseScriptObject()
{
if( m_JS )
{
JS_SetPrivate( g_ScriptingHost.GetContext(), m_JS, NULL );
if( m_EngineOwned )
JS_RemoveRoot( g_ScriptingHost.GetContext(), &m_JS );
m_JS = NULL;
}
}
private:
static JSPropertySpec JSI_props[];
static std::vector<JSFunctionSpec> m_Methods;
static PropertyTable m_IntrinsicProperties;
public:
// Standard constructors/destructors
CJSObject()
{
m_Parent = NULL;
m_JS = NULL;
m_EngineOwned = true;
}
@@ -607,163 +407,15 @@ public:
void Shutdown()
{
PropertyTable::iterator it;
for( it = m_Properties.begin(); it != m_Properties.end(); it++ )
{
if( !it->second->m_Intrinsic )
{
CJSDynamicProperty* extProp = (CJSValProperty*)it->second;
if( extProp->m_JSAccessor )
{
CJSPropertyAccessor< CJSObject<T, ReadOnly> >* accessor = (CJSPropertyAccessor< CJSObject<T, ReadOnly> >*)JS_GetPrivate( g_ScriptingHost.GetContext(), extProp->m_JSAccessor );
assert( accessor );
delete( accessor );
JS_SetPrivate( g_ScriptingHost.GetContext(), extProp->m_JSAccessor, NULL );
JS_RemoveRoot( g_ScriptingHost.GetContext(), &( extProp->m_JSAccessor ) );
}
}
for( it = m_ScriptProperties.begin(); it != m_ScriptProperties.end(); it++ )
delete( it->second );
}
ReflectorTable::iterator it_a;
for( it_a = m_Reflectors.begin(); it_a != m_Reflectors.end(); it_a++ )
{
CJSPropertyAccessor< CJSObject<T, ReadOnly> >* accessor = (CJSPropertyAccessor< CJSObject<T, ReadOnly> >*)JS_GetPrivate( g_ScriptingHost.GetContext(), it_a->second->m_JSAccessor );
assert( accessor );
delete( accessor );
JS_SetPrivate( g_ScriptingHost.GetContext(), it_a->second->m_JSAccessor, NULL );
JS_RemoveRoot( g_ScriptingHost.GetContext(), &( it_a->second->m_JSAccessor ) );
delete( it_a->second );
}
m_ScriptProperties.clear();
ReleaseScriptObject();
}
void SetBase( IJSObject* Parent )
{
if( m_Parent )
{
// Remove this from the list of our parent's inheritors
InheritorsList::iterator it;
for( it = m_Parent->m_Inheritors.begin(); it != m_Parent->m_Inheritors.end(); it++ )
if( (*it) == this )
{
m_Parent->m_Inheritors.erase( it );
break;
}
}
m_Parent = Parent;
if( m_Parent )
{
// Place this in the list of our parent's inheritors
m_Parent->m_Inheritors.push_back( this );
Rebuild();
}
}
void Rebuild()
{
PropertyTable::iterator it;
// For each intrinsic property we have,
for( it = m_Properties.begin(); it != m_Properties.end(); it++ )
{
if( !it->second->m_Intrinsic || !it->second->m_Inherited )
continue;
// Attempt to locate it in the parent
IJSProperty* cp = m_Parent->HasProperty( it->first );
// If it doesn't have it, we've inherited from an object of a different type
// This isn't allowed at the moment; but I don't have an totally convincing
// reason for forbidding it entirely. Mind, I can't think of any use for it,
// either.
// If it can be inherited, inherit it.
if( cp && cp->m_AllowsInheritance )
{
assert( cp->m_Intrinsic );
it->second->ImmediateCopy( this, m_Parent, cp );
}
}
// Do the same for the shared properties table, too
for( it = m_IntrinsicProperties.begin(); it != m_IntrinsicProperties.end(); it++ )
{
if( !it->second->m_Inherited )
continue;
IJSProperty* cp = m_Parent->HasProperty( it->first );
if( cp && cp->m_AllowsInheritance )
{
assert( cp->m_Intrinsic );
it->second->ImmediateCopy( this, m_Parent, cp );
}
}
// Now recurse.
InheritorsList::iterator c;
for( c = m_Inheritors.begin(); c != m_Inheritors.end(); c++ )
(*c)->Rebuild();
}
IJSProperty* HasProperty( CStrW PropertyName )
{
PropertyTable::iterator it;
it = T::m_IntrinsicProperties.find( PropertyName );
if( it != T::m_IntrinsicProperties.end() )
return( it->second );
it = m_Properties.find( PropertyName );
if( it != m_Properties.end() )
return( it->second );
return( NULL );
}
void AddProperty( CStrW PropertyName, jsval Value )
{
assert( !HasProperty( PropertyName ) );
CJSDynamicProperty* newProp = new CJSValProperty( Value, false );
m_Properties[PropertyName] = newProp;
ReflectorTable::iterator it;
it = m_Reflectors.find( PropertyName );
if( it != m_Reflectors.end() )
{
// We had an accessor pointing to this property before it was defined.
newProp->m_JSAccessor = it->second->m_JSAccessor;
JS_RemoveRoot( g_ScriptingHost.GetContext(), &( it->second->m_JSAccessor ) );
JS_AddRoot( g_ScriptingHost.GetContext(), &( newProp->m_JSAccessor ) );
#ifdef ALLOW_NONSHARED_NATIVES
for( it = m_NonsharedProperties.begin(); it != m_NonsharedProperties.end(); it++ )
delete( it->second );
m_Reflectors.erase( it );
}
}
void AddProperty( CStrW PropertyName, CStrW Value )
{
AddProperty( PropertyName, JSParseString( Value ) );
}
static void AddClassProperty( CStrW PropertyName, GetFn Getter, SetFn Setter = NULL )
{
T::m_IntrinsicProperties[PropertyName] = new CJSFunctionProperty( Getter, Setter );
}
template<typename ReturnType, ReturnType (T::*NativeFunction)( JSContext* cx, uintN argc, jsval* argv )>
static void AddMethod( const char* Name, uintN MinArgs )
{
JSFunctionSpec FnInfo = { Name, CNativeFunction<T, ReadOnly, ReturnType, NativeFunction>::JSFunction, MinArgs, 0, 0 };
T::m_Methods.push_back( FnInfo );
}
template<typename PropType> static void AddClassProperty( CStrW PropertyName, PropType T::*Native, bool PropAllowInheritance = true, NotifyFn Update = NULL, NotifyFn Refresh = NULL )
{
T::m_IntrinsicProperties[PropertyName] = new CJSSharedProperty<PropType, ReadOnly>( (PropType IJSObject::*)Native, PropAllowInheritance, Update, Refresh );
}
template<typename PropType> static void AddReadOnlyClassProperty( CStrW PropertyName, PropType T::*Native, bool PropAllowInheritance = true, NotifyFn Update = NULL, NotifyFn Refresh = NULL )
{
T::m_IntrinsicProperties[PropertyName] = new CJSSharedProperty<PropType, true>( (PropType IJSObject::*)Native, PropAllowInheritance, Update, Refresh );
}
template<typename PropType> void AddProperty( CStrW PropertyName, PropType* Native, bool PropAllowInheritance = true, NotifyFn Update = NULL, NotifyFn Refresh = NULL )
{
m_Properties[PropertyName] = new CJSProperty<PropType, ReadOnly>( Native, PropAllowInheritance, Update, Refresh );
}
template<typename PropType> void AddReadOnlyProperty( CStrW PropertyName, PropType* Native, bool PropAllowInheritance = true, NotifyFn Update = NULL, NotifyFn Refresh = NULL )
{
m_Properties[PropertyName] = new CJSProperty<PropType, true>( Native, PropAllowInheritance, Update, Refresh );
m_NonsharedProperties.clear();
#endif
}
};
@@ -781,72 +433,8 @@ template<typename T, bool ReadOnly> JSPropertySpec CJSObject<T, ReadOnly>::JSI_p
};
template<typename T, bool ReadOnly> std::vector<JSFunctionSpec> CJSObject<T, ReadOnly>::m_Methods;
template<typename T, bool ReadOnly> typename CJSObject<typename T, ReadOnly>::PropertyTable CJSObject<T, ReadOnly>::m_IntrinsicProperties;
template<typename T, bool ReadOnly> bool CJSObject<T, ReadOnly>::GetProperty( JSContext* cx, CStrW PropertyName, jsval* vp )
{
IJSProperty* Property = HasProperty( PropertyName );
if( Property && Property->m_Intrinsic )
{
*vp = Property->Get( cx, this );
}
else
{
CJSDynamicProperty* extProp;
if( Property )
{
extProp = (CJSValProperty*)Property;
if( !extProp->m_JSAccessor )
{
extProp->m_JSAccessor = CJSPropertyAccessor< CJSObject<T, ReadOnly> >::CreateAccessor( cx, this, PropertyName );
JS_AddNamedRoot( cx, &extProp->m_JSAccessor, "property accessor" );
}
*vp = OBJECT_TO_JSVAL( extProp->m_JSAccessor );
}
else
{
// Check to see if it exists on a parent
IJSObject* check = m_Parent;
while( check )
{
if( check->HasProperty( PropertyName ) ) break;
check = check->m_Parent;
}
if( !check )
return( false );
// FIXME: Fiddle a way so this /doesn't/ require multiple kilobytes
// of memory. Can't think of any better way to do it yet. Problem is
// that script may access a property that isn't defined locally, but
// is defined by an ancestor. We can't return an accessor to the
// ancestor's property, because then if it's altered it affects that
// object, not this. At the moment, creating a 'reflector' property
// accessor that references /this/ object to be returned to script.
// (N.B. Can't just put JSObjects* in the table -> table entries can
// move -> root no longer refers to the JSObject.)
ReflectorTable::iterator it;
it = m_Reflectors.find( PropertyName );
if( it == m_Reflectors.end() )
{
CJSReflector* reflector = new CJSReflector();
reflector->m_JSAccessor = CJSPropertyAccessor< CJSObject<T, ReadOnly> >::CreateAccessor( cx, this, PropertyName );
JS_AddRoot( cx, &reflector->m_JSAccessor );
m_Reflectors.insert( std::pair<CStrW, CJSReflector*>( PropertyName, reflector ) );
*vp = OBJECT_TO_JSVAL( reflector->m_JSAccessor );
}
else
*vp = OBJECT_TO_JSVAL( it->second->m_JSAccessor );
}
}
return( true );
}
template<typename T, bool ReadOnly> typename CJSObject<typename T, ReadOnly>::PropertyTable CJSObject<T, ReadOnly>::m_NativeProperties;
#endif