0ad/source/scripting/ScriptableComplex.inl
janwas c0ed950657 had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).

it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.

after several hours, the code now requires fewer casts and less
guesswork.

other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.

This was SVN commit r5942.
2008-05-11 18:48:32 +00:00

1105 lines
34 KiB
C++

/*
Implementation of CJSComplex's functions and related helper functions and classes.
This file must be #included in any CPP file that accesses these functions directly,
but may be omitted in those that don't.
rationale:
we are changing CJSComplex often for purposes of optimization. this triggers
close to a full rebuild, which is unacceptable.
ideally, we would move the method implementations into a cpp file, and
then only that need be recompiled.
unfortunately that is exactly what the export keyword enables,
which is not likely to be implemented in VC.
several workarounds have been investigated.
1) reduce symptoms of the problem by reducing #includes of entity.h, which is
what pulls CJSComplex in. done; it's been removed entirely from headers and
is currently only left in ~30 cpp files (could probably be
further reduced)
2) de-templatize CJSComplex. result would be enabling the normal split of
interface vs. implementation in cpp file.
this would be mostly feasible because it's really only used by
CEntity and CEntityTemplate. however, since there are 2 users, the
static class data (notably m_Methods) would have to be duplicated for each.
one possibility would be an 'array' of each, indexed via class id.
also, static data could be reduced by having property lookup be done
via per-property hash tables, instead of the root object holding one
large table of the entire property names (e.g. traits.health.max).
in retrospect, this would probably have been better, but is probably
too much work to change now.
3) instead of CEntity IS-A CJSComplex, use composition and pImpl. this would
decouple entity.h from changes in scriptablecomplex.h.
however, we'd have to dynamically allocate the CJSComplex in each entity
(required by pImpl and less efficient/more annoying). also, there would
potentially be trouble with ToJSVal, since we no longer derive from
CJSComplex.
this is not deemed worth the effort due to steps taken in #1.
We decided to split off the implementation of CJSComplex as well as many of its
helper classes into a separate header, ScriptableComplex.inl. This way,
ScriptableComplex.h does not need to be modified unless we change the API,
but the implementations of CJSComplex's methods can be changed. However, this
also means that this header (ScriptableComplex.inl) must be #included in any
CPP file that directly accesses ScriptableComplex's methods - otherwise, the
linker won't find the definitions of these functions. Right now this is only
5 files, which results in much faster rebuilds after modifying this code.
*/
#ifndef SCRIPTABLE_COMPLEX_INL_INCLUDED
#define SCRIPTABLE_COMPLEX_INL_INCLUDED
#include "ScriptableComplex.h"
//-----------------------------------------------------------------------------
// CJSComplexPropertyAccessor
//-----------------------------------------------------------------------------
template<typename T, bool ReadOnly> class CJSComplex;
template<typename T> class CJSComplexPropertyAccessor
{
T* m_Owner;
CStrW m_PropertyRoot;
template<typename Q, bool ReadOnly> friend class CJSComplex;
public:
CJSComplexPropertyAccessor( T* Owner, const CStrW& PropertyRoot )
{
m_Owner = Owner;
m_PropertyRoot = PropertyRoot;
}
static JSObject* CreateAccessor( JSContext* cx, T* Owner, const CStrW& PropertyRoot )
{
JSObject* Accessor = JS_NewObject( cx, &JSI_Class, NULL, NULL );
JS_SetPrivate( cx, Accessor, new CJSComplexPropertyAccessor( Owner, PropertyRoot ) );
return( Accessor );
}
// JW: ugly, but more efficient than previous approach
// (string = root + "." + id;). saves 50ms during init.
// made macro to ensure there is no extra overhead.
#define BUILD_PROPNAME(root, id)\
PropName.reserve(50);\
PropName += root;\
PropName += '.';\
PropName += id;
static JSBool JSGetProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp )
{
CJSComplexPropertyAccessor* Instance = (CJSComplexPropertyAccessor*)JS_GetPrivate( cx, obj );
if( !Instance ) return( JS_TRUE );
CStrW PropName;
BUILD_PROPNAME(Instance->m_PropertyRoot, 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 )
{
CJSComplexPropertyAccessor* Instance = (CJSComplexPropertyAccessor*)JS_GetPrivate( cx, obj );
if( !Instance ) return( JS_TRUE );
CStrW PropName;
BUILD_PROPNAME(Instance->m_PropertyRoot, g_ScriptingHost.ValueToUCString(id));
Instance->m_Owner->SetProperty( cx, PropName, vp );
return( JS_TRUE );
}
static JSBool JSEnumerate( JSContext* cx, JSObject* obj, JSIterateOp enum_op, jsval* statep, jsid *idp )
{
IJSComplex::IteratorState* it;
switch( enum_op )
{
case JSENUMERATE_INIT:
{
CJSComplexPropertyAccessor* Instance = (CJSComplexPropertyAccessor*)JS_GetPrivate( cx, obj );
it = new IJSComplex::IteratorState;
if( Instance )
{
size_t rlen = Instance->m_PropertyRoot.length();
IJSComplex::PropertyTable::iterator iit;
for( iit = T::m_IntrinsicProperties.begin(); iit != T::m_IntrinsicProperties.end(); iit++ )
if( ( iit->first.length() > rlen ) && ( iit->first.Left( rlen ) == Instance->m_PropertyRoot ) && ( iit->first[rlen] == '.' ) )
it->first.insert( CStrW( iit->first.substr( rlen + 1 ) ).BeforeFirst( L"." ) );
Instance->m_Owner->FillEnumerateSet( it, &( Instance->m_PropertyRoot ) );
}
it->second = it->first.begin();
*statep = PRIVATE_TO_JSVAL( it );
if( idp )
*idp = INT_TO_JSVAL( it->first.size() );
return( JS_TRUE );
}
case JSENUMERATE_NEXT:
it = (IJSComplex::IteratorState*)JSVAL_TO_PRIVATE( *statep );
if( it->second == it->first.end() )
{
delete( it );
*statep = JSVAL_NULL;
return( JS_TRUE );
}
// I think this is what I'm supposed to do... (cheers, Philip)
if( !JS_ValueToId( cx, ToJSVal<CStrW>( *( it->second ) ), idp ) )
return( JS_FALSE );
// EVIL HACK: since https://bugzilla.mozilla.org/show_bug.cgi?id=261887 (which is in
// the SpiderMonkey 1.6 release, and not in 1.5), you can't enumerate properties that
// don't actually exist on the object. This should probably be fixed by defining a custom
// Resolve function to make them look like they exist, but for now we just define the
// property on the object here so that it will exist by the time the JS iteration code
// does its checks.
JS_DefineProperty(cx, obj, CStr(*it->second).c_str(), JSVAL_VOID, NULL, NULL, 0);
(it->second)++;
*statep = PRIVATE_TO_JSVAL( it );
return( JS_TRUE );
case JSENUMERATE_DESTROY:
it = (IJSComplex::IteratorState*)JSVAL_TO_PRIVATE( *statep );
delete( it );
*statep = JSVAL_NULL;
return( JS_TRUE );
}
return( JS_FALSE );
}
static JSBool JSPrimitive( JSContext* cx, JSObject* obj, uintN UNUSED(argc), jsval* UNUSED(argv), jsval* rval )
{
CJSComplexPropertyAccessor* Instance = (CJSComplexPropertyAccessor*)JS_GetPrivate( cx, obj );
if( !Instance ) return( JS_TRUE );
// Check all along the inheritance tree
// Possible optimization: Store the hashed value over the lookups
IJSComplex* Target = Instance->m_Owner;
IJSComplexProperty* 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 UNUSED(argc), jsval* UNUSED(argv), jsval* rval )
{
CJSComplexPropertyAccessor* Instance = (CJSComplexPropertyAccessor*)JS_GetPrivate( cx, obj );
if( !Instance ) return( JS_TRUE );
// Check all along the inheritance tree
// TODO: Optimization: Store the hashed value over the lookups
IJSComplex* Target = Instance->m_Owner;
IJSComplexProperty* Property;
JSString* str = NULL;
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 CJSComplexPropertyAccessor<T>::JSI_Class = {
"Property", JSCLASS_HAS_PRIVATE | JSCLASS_NEW_ENUMERATE,
JS_PropertyStub, JS_PropertyStub,
JSGetProperty, JSSetProperty,
(JSEnumerateOp)JSEnumerate, JS_ResolveStub,
JS_ConvertStub, JS_FinalizeStub,
NULL, NULL, NULL, NULL
};
template<typename T>
void ScriptableComplex_InitComplexPropertyAccessor()
{
CJSComplexPropertyAccessor<T>::ScriptingInit();
}
//-----------------------------------------------------------------------------
// various property types
//-----------------------------------------------------------------------------
template<typename T, bool ReadOnly> class CJSSharedProperty : public IJSComplexProperty
{
T IJSComplex::*m_Data;
// Function on Owner to call after value is changed
IJSComplex::NotifyFn m_Update;
// Function on Owner to call before reading or writing the value
IJSComplex::NotifyFn m_Freshen;
public:
CJSSharedProperty( T IJSComplex::*Data, bool AllowsInheritance = false, IJSComplex::NotifyFn Update = NULL, IJSComplex::NotifyFn Freshen = NULL )
{
m_Data = Data;
m_AllowsInheritance = AllowsInheritance;
m_Update = Update;
m_Freshen = Freshen;
m_Intrinsic = true;
m_Inherited = true;
}
jsval Get( JSContext* UNUSED(cx), IJSComplex* owner )
{
if( m_Freshen ) (owner->*m_Freshen)();
return( ToJSVal( owner->*m_Data ) );
}
void ImmediateCopy( IJSComplex* CopyTo, IJSComplex* CopyFrom, IJSComplexProperty* CopyProperty )
{
CJSSharedProperty* otherProp = (CJSSharedProperty*) CopyProperty;
T IJSComplex::*otherData = otherProp->m_Data;
CopyTo->*m_Data = CopyFrom->*otherData;
}
void Set( JSContext* cx, IJSComplex* owner, jsval Value )
{
if( !ReadOnly )
{
if( m_Freshen ) (owner->*m_Freshen)();
if( ToPrimitive( cx, Value, owner->*m_Data ) )
if( m_Update ) (owner->*m_Update)();
}
}
};
template<typename T, bool ReadOnly> class CJSComplexProperty : public IJSComplexProperty
{
T* m_Data;
// Function on Owner to call after value is changed
IJSComplex::NotifyFn m_Update;
// Function on Owner to call before reading or writing the value
IJSComplex::NotifyFn m_Freshen;
public:
CJSComplexProperty( T* Data, bool AllowsInheritance = false, IJSComplex::NotifyFn Update = NULL, IJSComplex::NotifyFn Freshen = NULL )
{
m_Data = Data;
m_AllowsInheritance = AllowsInheritance;
m_Update = Update;
m_Freshen = Freshen;
m_Intrinsic = true;
}
jsval Get( JSContext* UNUSED(cx), IJSComplex* owner )
{
if( m_Freshen ) (owner->*m_Freshen)();
return( ToJSVal( *m_Data ) );
}
void ImmediateCopy( IJSComplex* UNUSED(CopyTo), IJSComplex* UNUSED(CopyFrom), IJSComplexProperty* CopyProperty )
{
*m_Data = *( ( (CJSComplexProperty*)CopyProperty )->m_Data );
}
void Set( JSContext* cx, IJSComplex* 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 CJSComplex;
JSObject* m_JSAccessor;
};
class CJSDynamicComplexProperty : public IJSComplexProperty
{
template<typename Q, bool ReadOnly> friend class CJSComplex;
JSObject* m_JSAccessor;
public:
CJSDynamicComplexProperty()
{
m_JSAccessor = NULL;
m_Intrinsic = false;
m_Inherited = false;
}
};
class CJSValComplexProperty : public CJSDynamicComplexProperty
{
template<typename Q, bool ReadOnly> friend class CJSComplex;
jsval m_Data;
public:
CJSValComplexProperty( jsval Data, bool Inherited )
{
m_Inherited = Inherited;
m_Data = Data;
Root();
}
~CJSValComplexProperty()
{
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* UNUSED(cx), IJSComplex* UNUSED(owner) )
{
return( m_Data );
}
void Set( JSContext* UNUSED(cx), IJSComplex* UNUSED(owner), jsval Value )
{
Uproot();
m_Data = Value;
Root();
}
void ImmediateCopy( IJSComplex* UNUSED(CopyTo), IJSComplex* UNUSED(CopyFrom),
IJSComplexProperty* UNUSED(CopyProperty) )
{
debug_warn("ImmediateCopy called on a CJSValComplexProperty (something's gone wrong with the inheritance on this object)" );
}
};
class CJSFunctionComplexProperty : public IJSComplexProperty
{
// Function on Owner to get the value
IJSComplex::GetFn m_Getter;
// Function on Owner to set the value
IJSComplex::SetFn m_Setter;
public:
CJSFunctionComplexProperty( IJSComplex::GetFn Getter, IJSComplex::SetFn Setter )
{
m_Inherited = false;
m_Intrinsic = true;
m_Getter = Getter;
m_Setter = Setter;
// Must at least be able to read
debug_assert( m_Getter );
}
jsval Get( JSContext* UNUSED(cx), IJSComplex* owner )
{
return( (owner->*m_Getter)() );
}
void Set( JSContext* UNUSED(cx), IJSComplex* owner, jsval Value )
{
if( m_Setter )
(owner->*m_Setter)( Value );
}
void ImmediateCopy( IJSComplex* UNUSED(CopyTo), IJSComplex* UNUSED(CopyFrom), IJSComplexProperty* UNUSED(CopyProperty) )
{
debug_warn("ImmediateCopy called on a property wrapping getter/setter functions (something's gone wrong with the inheritance for this object)" );
}
};
// Wrapper around native functions that are attached to CJSComplexs
template<typename T, bool ReadOnly, typename RType, RType (T::*NativeFunction)( JSContext* cx, uintN argc, jsval* argv )> class CNativeComplexFunction
{
public:
static JSBool JSFunction( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval )
{
T* Native = ToNative<T>( cx, obj );
if( !Native )
return( JS_TRUE );
*rval = ToJSVal<RType>( (Native->*NativeFunction)( cx, argc, argv ) );
return( JS_TRUE );
}
};
//-----------------------------------------------------------------------------
// CJSComplex implementation
//-----------------------------------------------------------------------------
template<typename T, bool ReadOnly>
void CJSComplex<T, ReadOnly>::SetProperty( JSContext* cx, const CStrW& PropertyName, jsval* vp )
{
if( !ReadOnly )
{
IJSComplexProperty* prop = HasProperty( PropertyName );
if( prop )
{
// Already exists
WatchNotify( cx, PropertyName, vp );
prop->Set( cx, this, *vp );
if(!prop->m_Intrinsic)
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() )
{
IJSComplex* UpdateObj = UpdateSet.back();
UpdateSet.pop_back();
IJSComplexProperty* 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
{
// Need to add it
WatchNotify( cx, PropertyName, vp );
AddProperty( PropertyName, *vp );
}
}
}
template<typename T, bool ReadOnly>
void CJSComplex<T, ReadOnly>::WatchNotify( JSContext* cx, const CStrW& PropertyName, jsval* newval )
{
if( m_Watches.empty() ) return;
jsval oldval = JSVAL_VOID;
GetProperty( cx, PropertyName, &oldval );
jsval args[3] = { ToJSVal( PropertyName ), oldval, *newval };
std::vector<CScriptObject>::iterator it;
for( it = m_Watches.begin(); it != m_Watches.end(); it++ )
if( it->Run( GetScript(), newval, 3, args ) )
args[2] = *newval;
}
//
// Functions that must be provided to JavaScript
//
template<typename T, bool ReadOnly>
JSBool CJSComplex<T, ReadOnly>::JSGetProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp )
{
T* Instance = ToNative<T>( cx, obj );
if( !Instance )
return( JS_TRUE );
CStrW PropName = g_ScriptingHost.ValueToUCString( id );
if( !Instance->GetProperty( cx, PropName, vp ) )
return( JS_TRUE );
return( JS_TRUE );
}
template<typename T, bool ReadOnly>
JSBool CJSComplex<T, ReadOnly>::JSSetProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp )
{
T* Instance = ToNative<T>( cx, obj );
if( !Instance )
return( JS_TRUE );
CStrW PropName = g_ScriptingHost.ValueToUCString( id );
Instance->SetProperty( cx, PropName, vp );
return( JS_TRUE );
}
template<typename T, bool ReadOnly>
JSBool CJSComplex<T, ReadOnly>::JSEnumerate( JSContext* cx, JSObject* obj, JSIterateOp enum_op, jsval* statep, jsid *idp )
{
IteratorState* it;
switch( enum_op )
{
case JSENUMERATE_INIT:
{
T* Instance = ToNative<T>( cx, obj );
it = new IteratorState;
if( Instance )
{
PropertyTable::iterator iit;
for( iit = T::m_IntrinsicProperties.begin(); iit != T::m_IntrinsicProperties.end(); iit++ )
it->first.insert( iit->first );
Instance->FillEnumerateSet( it );
}
it->second = it->first.begin();
*statep = PRIVATE_TO_JSVAL( it );
if( idp )
*idp = INT_TO_JSVAL( it->first.size() );
return( JS_TRUE );
}
case JSENUMERATE_NEXT:
it = (IteratorState*)JSVAL_TO_PRIVATE( *statep );
if( it->second == it->first.end() )
{
delete( it );
*statep = JSVAL_NULL;
return( JS_TRUE );
}
// I think this is what I'm supposed to do... (cheers, Philip)
if( !JS_ValueToId( cx, ToJSVal<CStrW>( *( it->second ) ), idp ) )
return( JS_FALSE );
// EVIL HACK: see the comment in the other JSEnumerate
JS_DefineProperty(cx, obj, CStr(*it->second).c_str(), JSVAL_VOID, NULL, NULL, 0);
(it->second)++;
*statep = PRIVATE_TO_JSVAL( it );
return( JS_TRUE );
case JSENUMERATE_DESTROY:
it = (IteratorState*)JSVAL_TO_PRIVATE( *statep );
delete( it );
*statep = JSVAL_NULL;
return( JS_TRUE );
}
return( JS_FALSE );
}
template<typename T, bool ReadOnly>
JSBool CJSComplex<T, ReadOnly>::SetWatchAll( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval )
{
T* Native = ToNative<T>( cx, obj );
if( !Native )
return( JS_TRUE );
debug_assert( argc >= 1 );
CScriptObject watch( argv[0] );
std::vector<CScriptObject>::iterator it;
for( it = Native->m_Watches.begin(); it != Native->m_Watches.end(); it++ )
if( *it == watch )
{
*rval = JSVAL_FALSE;
return( JS_TRUE );
}
Native->m_Watches.push_back( watch );
*rval = JSVAL_TRUE;
return( JS_TRUE );
}
template<typename T, bool ReadOnly>
JSBool CJSComplex<T, ReadOnly>::UnWatchAll( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval )
{
T* Native = ToNative<T>( cx, obj );
if( !Native )
return( JS_TRUE );
if( argc >= 1 )
{
CScriptObject watch( argv[0] );
std::vector<CScriptObject>::iterator it;
for( it = Native->m_Watches.begin(); it != Native->m_Watches.end(); it++ )
if( *it == watch )
{
Native->m_Watches.erase( it );
*rval = JSVAL_TRUE;
return( JS_TRUE );
}
*rval = JSVAL_FALSE;
}
else
{
Native->m_Watches.clear();
*rval = JSVAL_TRUE;
}
return( JS_TRUE );
}
template<typename T, bool ReadOnly>
void CJSComplex<T, ReadOnly>::ScriptingInit( const char* ClassName, JSNative Constructor, uintN ConstructorMinArgs )
{
JSFunctionSpec* JSI_methods = new JSFunctionSpec[ m_Methods.size() + 3 ];
size_t MethodID;
for( MethodID = 0; MethodID < m_Methods.size(); MethodID++ )
JSI_methods[MethodID] = m_Methods[MethodID];
JSFunctionSpec watchAll = { "watchAll", SetWatchAll, 1, 0, 0 };
JSI_methods[MethodID + 0] = watchAll;
JSFunctionSpec unwatchAll = { "unwatchAll", UnWatchAll, 1, 0, 0 };
JSI_methods[MethodID + 1] = unwatchAll;
JSI_methods[MethodID + 2].name = 0;
JSI_class.name = ClassName;
g_ScriptingHost.DefineCustomObjectType( &JSI_class, Constructor, ConstructorMinArgs, JSI_props, JSI_methods, NULL, NULL );
delete[]( JSI_methods );
atexit( ScriptingShutdown );
}
template<typename T, bool ReadOnly>
void CJSComplex<T, ReadOnly>::ScriptingShutdown()
{
PropertyTable::iterator it;
for( it = m_IntrinsicProperties.begin(); it != m_IntrinsicProperties.end(); it++ )
delete( it->second );
}
template<typename T, bool ReadOnly>
void CJSComplex<T, ReadOnly>::DefaultFinalize( JSContext *cx, JSObject *obj )
{
T* Instance = ToNative<T>( cx, obj );
if( !Instance || Instance->m_EngineOwned )
return;
delete( Instance );
JS_SetPrivate( cx, obj, NULL );
}
// Creating and releasing script objects is done automatically most of the time, but you
// can do it explicitly.
template<typename T, bool ReadOnly>
void CJSComplex<T, ReadOnly>::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 );
}
}
template<typename T, bool ReadOnly>
void CJSComplex<T, ReadOnly>::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;
}
}
template<typename T, bool ReadOnly>
CJSComplex<T, ReadOnly>::CJSComplex()
{
jscomplexproperty_suballoc_attach();
m_Parent = NULL;
m_JS = NULL;
m_EngineOwned = true;
}
template<typename T, bool ReadOnly>
CJSComplex<T, ReadOnly>::~CJSComplex()
{
Shutdown();
jscomplexproperty_suballoc_detach();
}
template<typename T, bool ReadOnly>
void CJSComplex<T, ReadOnly>::Shutdown()
{
PropertyTable::iterator it;
for( it = m_Properties.begin(); it != m_Properties.end(); it++ )
{
if( !it->second->m_Intrinsic )
{
CJSDynamicComplexProperty* extProp = (CJSValComplexProperty*)it->second;
if( extProp->m_JSAccessor )
{
CJSComplexPropertyAccessor< CJSComplex<T, ReadOnly> >* accessor = (CJSComplexPropertyAccessor< CJSComplex<T, ReadOnly> >*)JS_GetPrivate( g_ScriptingHost.GetContext(), extProp->m_JSAccessor );
debug_assert( accessor );
delete( accessor );
JS_SetPrivate( g_ScriptingHost.GetContext(), extProp->m_JSAccessor, NULL );
JS_RemoveRoot( g_ScriptingHost.GetContext(), &( extProp->m_JSAccessor ) );
}
}
jscomplexproperty_suballoc_free(it->second);
}
ReflectorTable::iterator it_a;
for( it_a = m_Reflectors.begin(); it_a != m_Reflectors.end(); it_a++ )
{
CJSComplexPropertyAccessor< CJSComplex<T, ReadOnly> >* accessor = (CJSComplexPropertyAccessor< CJSComplex<T, ReadOnly> >*)JS_GetPrivate( g_ScriptingHost.GetContext(), it_a->second->m_JSAccessor );
debug_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 );
}
ReleaseScriptObject();
}
template<typename T, bool ReadOnly>
void CJSComplex<T, ReadOnly>::SetBase( IJSComplex* 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();
}
}
template<typename T, bool ReadOnly>
void CJSComplex<T, ReadOnly>::Rebuild()
{
PropertyTable::iterator it;
// For each intrinsic property we have,
for( it = m_Properties.begin(); it != m_Properties.end(); it++ )
{
const CStrW& prop_name = it->first;
IJSComplexProperty* prop = it->second;
if( !prop->m_Intrinsic || !prop->m_Inherited )
continue;
// Attempt to locate it in the parent
IJSComplexProperty* parent_prop = m_Parent->HasProperty( prop_name );
// 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( parent_prop && parent_prop->m_AllowsInheritance )
{
debug_assert( parent_prop->m_Intrinsic );
prop->ImmediateCopy( this, m_Parent, parent_prop );
}
}
// Do the same for the shared properties table, too
for( it = m_IntrinsicProperties.begin(); it != m_IntrinsicProperties.end(); it++ )
{
const CStrW& prop_name = it->first;
IJSComplexProperty* prop = it->second;
if( !prop->m_Inherited )
continue;
IJSComplexProperty* parent_prop = m_Parent->HasProperty( prop_name );
if( parent_prop && parent_prop->m_AllowsInheritance )
{
debug_assert( parent_prop->m_Intrinsic );
prop->ImmediateCopy( this, m_Parent, parent_prop );
}
}
// Now recurse.
InheritorsList::iterator c;
for( c = m_Inheritors.begin(); c != m_Inheritors.end(); c++ )
(*c)->Rebuild();
}
template<typename T, bool ReadOnly>
IJSComplexProperty* CJSComplex<T, ReadOnly>::HasProperty( const 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 );
}
template<typename T, bool ReadOnly>
void CJSComplex<T, ReadOnly>::FillEnumerateSet( IteratorState* it, CStrW* PropertyRoot)
{
PropertyTable::iterator iit;
if( PropertyRoot )
{
size_t rlen = PropertyRoot->length();
for( iit = m_Properties.begin(); iit != m_Properties.end(); iit++ )
if( ( iit->first.length() > rlen ) && ( iit->first.Left( rlen ) == *PropertyRoot ) && ( iit->first[rlen] == '.' ) )
it->first.insert( CStrW( iit->first.substr( rlen + 1 ) ).BeforeFirst( L"." ) );
}
else
{
for( iit = m_Properties.begin(); iit != m_Properties.end(); iit++ )
it->first.insert( iit->first.BeforeFirst( L"." ) );
}
if( m_Parent )
m_Parent->FillEnumerateSet( it, PropertyRoot );
}
template<typename T, bool ReadOnly>
void CJSComplex<T, ReadOnly>::AddProperty( const CStrW& PropertyName, jsval Value )
{
DeletePreviouslyAssignedProperty( PropertyName );
void* mem = jscomplexproperty_suballoc();
CJSDynamicComplexProperty* newProp = new(mem) CJSValComplexProperty( 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 ) );
delete( it->second );
m_Reflectors.erase( it );
}
}
template<typename T, bool ReadOnly>
void CJSComplex<T, ReadOnly>::AddProperty( const CStrW& PropertyName, const CStrW& Value )
{
AddProperty( PropertyName, JSParseString( Value ) );
}
template<typename T, bool ReadOnly>
void CJSComplex<T, ReadOnly>::AddClassProperty( const CStrW& PropertyName, GetFn Getter, SetFn Setter )
{
T::m_IntrinsicProperties[PropertyName] = new CJSFunctionComplexProperty( Getter, Setter );
}
// helper routine for Add*Property. Their interface requires the
// property not already exist; we check for this (in debug builds)
// and if so, warn and free the previously new-ed memory in
// m_Properties[PropertyName] (avoids mem leak).
template<typename T, bool ReadOnly>
void CJSComplex<T, ReadOnly>::DeletePreviouslyAssignedProperty( const CStrW& PropertyName )
{
#ifdef NDEBUG
UNUSED2(PropertyName);
#else
PropertyTable::iterator it;
it = m_Properties.find( PropertyName );
if( it != m_Properties.end() )
{
debug_warn("BUG: CJSComplexProperty added but already existed!");
jscomplexproperty_suballoc_free(it->second);
}
#endif
}
template<typename T, bool ReadOnly>
bool CJSComplex<T, ReadOnly>::GetProperty( JSContext* cx, const CStrW& PropertyName, jsval* vp )
{
IJSComplexProperty* Property = HasProperty( PropertyName );
if( Property && Property->m_Intrinsic )
{
*vp = Property->Get( cx, this );
}
else
{
CJSValComplexProperty* extProp;
if( Property )
{
extProp = (CJSValComplexProperty*)Property;
// If it's already a JS object, there's no point in creating
// a PropertyAccessor for it; it can manage far better on its
// own (this was why valueOf() was necessary)
if( !JSVAL_IS_OBJECT( extProp->m_Data ) )
{
if( !extProp->m_JSAccessor )
{
extProp->m_JSAccessor = CJSComplexPropertyAccessor< CJSComplex<T, ReadOnly> >::CreateAccessor( cx, this, PropertyName );
JS_AddNamedRoot( cx, &extProp->m_JSAccessor, "property accessor" );
}
*vp = OBJECT_TO_JSVAL( extProp->m_JSAccessor );
}
else
*vp = extProp->m_Data;
}
else
{
// Check to see if it exists on a parent
IJSComplex* 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 JSComplexs* 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 = CJSComplexPropertyAccessor< CJSComplex<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 ReturnType, ReturnType (T::*NativeFunction)( JSContext* cx, uintN argc, jsval* argv )>
void AddMethodImpl( const char* Name, uintN MinArgs )
{
JSFunctionSpec FnInfo = { Name, CNativeComplexFunction<T, ReadOnly, ReturnType, NativeFunction>::JSFunction, MinArgs, 0, 0 };
T::m_Methods.push_back( FnInfo );
}
template<typename T, bool ReadOnly, typename PropType>
void AddClassPropertyImpl( const CStrW& PropertyName, PropType T::*Native, bool PropAllowInheritance, IJSComplex::NotifyFn Update, IJSComplex::NotifyFn Refresh )
{
T::m_IntrinsicProperties[PropertyName] = new CJSSharedProperty<PropType, ReadOnly>( (PropType IJSComplex::*)Native, PropAllowInheritance, Update, Refresh );
}
template<typename T, bool ReadOnly, typename PropType>
void AddReadOnlyClassPropertyImpl( const CStrW& PropertyName, PropType T::*Native, bool PropAllowInheritance, IJSComplex::NotifyFn Update, IJSComplex::NotifyFn Refresh )
{
T::m_IntrinsicProperties[PropertyName] = new CJSSharedProperty<PropType, true>( (PropType IJSComplex::*)Native, PropAllowInheritance, Update, Refresh );
}
// PropertyName must not already exist! (verified in debug build)
template<typename T, bool ReadOnly, typename PropType>
void MemberAddPropertyImpl( IJSComplex* obj, const CStrW& PropertyName, PropType* Native, bool PropAllowInheritance, IJSComplex::NotifyFn Update, IJSComplex::NotifyFn Refresh )
{
((T*)obj)->DeletePreviouslyAssignedProperty( PropertyName );
void* mem = jscomplexproperty_suballoc();
obj->m_Properties[PropertyName] = new(mem) CJSComplexProperty<PropType, ReadOnly>( Native, PropAllowInheritance, Update, Refresh );
}
// PropertyName must not already exist! (verified in debug build)
template<typename T, bool ReadOnly, typename PropType>
void MemberAddReadOnlyPropertyImpl( IJSComplex* obj, const CStrW& PropertyName, PropType* Native, bool PropAllowInheritance, IJSComplex::NotifyFn Update, IJSComplex::NotifyFn Refresh )
{
((T*)obj)->DeletePreviouslyAssignedProperty( PropertyName );
void* mem = jscomplexproperty_suballoc();
obj->m_Properties[PropertyName] = new(mem) CJSComplexProperty<PropType, true>( Native, PropAllowInheritance, Update, Refresh );
}
#endif