diff --git a/source/ps/GameSetup/GameSetup.cpp b/source/ps/GameSetup/GameSetup.cpp index dadebf7927..3cef889011 100644 --- a/source/ps/GameSetup/GameSetup.cpp +++ b/source/ps/GameSetup/GameSetup.cpp @@ -59,6 +59,7 @@ #include "scripting/ScriptingHost.h" #include "scripting/GameEvents.h" +#include "scripting/ScriptableComplex.h" #include "maths/scripting/JSInterface_Vector3D.h" #include "graphics/scripting/JSInterface_Camera.h" #include "ps/scripting/JSInterface_Selection.h" @@ -501,7 +502,8 @@ static void InitScripting() CPlayer::ScriptingInit(); PlayerCollection::Init( "PlayerCollection" ); - CJSComplexPropertyAccessor::ScriptingInit(); // <-- Doesn't really matter which we use, but we know CJSPropertyAccessor is already being compiled for T = CEntity. + //CJSComplexPropertyAccessor::ScriptingInit(); // <-- Doesn't really matter which we use, but we know CJSPropertyAccessor is already being compiled for T = CEntity. + ScriptableComplex_InitComplexPropertyAccessor(); CScriptEvent::ScriptingInit(); CJSProgressTimer::ScriptingInit(); CProjectile::ScriptingInit(); diff --git a/source/scripting/ScriptableComplex.cpp b/source/scripting/ScriptableComplex.cpp index 613e0f6df9..6759b2d21d 100644 --- a/source/scripting/ScriptableComplex.cpp +++ b/source/scripting/ScriptableComplex.cpp @@ -3,7 +3,11 @@ #include "lib/allocators.h" +//----------------------------------------------------------------------------- // suballocator for CJSComplex.m_Properties elements +// (must come after property defs) +//----------------------------------------------------------------------------- + static Bucket bucket; // HACK: it needs to be created/destroyed; since there is no // global init/shutdown call here, we keep a refcnt. this assumes that @@ -38,3 +42,4 @@ void jscomplexproperty_suballoc_free(IJSComplexProperty* p) p->~IJSComplexProperty(); bucket_free(&bucket, p); } + diff --git a/source/scripting/ScriptableComplex.h b/source/scripting/ScriptableComplex.h index 5f294078db..42e0962953 100644 --- a/source/scripting/ScriptableComplex.h +++ b/source/scripting/ScriptableComplex.h @@ -101,402 +101,40 @@ public: inline IJSComplex() {} }; -template class CJSComplex; -template class CJSComplexPropertyAccessor -{ - T* m_Owner; - CStrW m_PropertyRoot; - template friend class CJSComplex; +class CJSReflector; -public: - CJSComplexPropertyAccessor( T* Owner, const CStrW& PropertyRoot ) - { - m_Owner = Owner; - m_PropertyRoot = PropertyRoot; - } +template +void AddMethodImpl( const char* Name, uintN MinArgs ); - 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 ) ); +template +void AddClassPropertyImpl( const CStrW& PropertyName, PropType T::*Native, bool PropAllowInheritance = true, IJSComplex::NotifyFn Update = NULL, IJSComplex::NotifyFn Refresh = NULL ); - return( Accessor ); - } +template +void AddReadOnlyClassPropertyImpl( const CStrW& PropertyName, PropType T::*Native, bool PropAllowInheritance = true, IJSComplex::NotifyFn Update = NULL, IJSComplex::NotifyFn Refresh = NULL ); - static JSBool JSGetProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp ) - { - CJSComplexPropertyAccessor* Instance = (CJSComplexPropertyAccessor*)JS_GetPrivate( cx, obj ); - if( !Instance ) return( JS_TRUE ); +template +void MemberAddPropertyImpl( IJSComplex* obj, const CStrW& PropertyName, PropType* Native, bool PropAllowInheritance = true, IJSComplex::NotifyFn Update = NULL, IJSComplex::NotifyFn Refresh = NULL ); - CStrW PropName = Instance->m_PropertyRoot + CStrW( L"." ) + g_ScriptingHost.ValueToUCString( id ); +template +void MemberAddReadOnlyPropertyImpl( IJSComplex* obj, const CStrW& PropertyName, PropType* Native, bool PropAllowInheritance = true, IJSComplex::NotifyFn Update = NULL, IJSComplex::NotifyFn Refresh = NULL ); - 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 = g_ScriptingHost.ValueToUCString( id ); - - Instance->m_Owner->SetProperty( cx, Instance->m_PropertyRoot + CStrW( L"." ) + 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( iit->first.GetSubstring( rlen + 1, iit->first.Length() - 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( g_ScriptingHost.GetContext(), ToJSVal( *( it->second ) ), idp ) ) - return( JS_FALSE ); - - (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 JSClass CJSComplexPropertyAccessor::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 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 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 friend class CJSComplex; - JSObject* m_JSAccessor; -}; - -class CJSDynamicComplexProperty : public IJSComplexProperty -{ - template friend class CJSComplex; - - JSObject* m_JSAccessor; -public: - CJSDynamicComplexProperty() - { - m_JSAccessor = NULL; - m_Intrinsic = false; - m_Inherited = false; - } -}; - -class CJSValComplexProperty : public CJSDynamicComplexProperty -{ - template 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 class CNativeComplexFunction +template class CJSComplex : public IJSComplex { public: - static JSBool JSFunction( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval ) - { - T* Native = ToNative( cx, obj ); - if( !Native ) - return( JS_TRUE ); - - *rval = ToJSVal( (Native->*NativeFunction)( cx, argc, argv ) ); - - return( JS_TRUE ); - } -}; - - - -extern void jscomplexproperty_suballoc_attach(); -extern void jscomplexproperty_suballoc_detach(); -extern void* jscomplexproperty_suballoc(); -extern void jscomplexproperty_suballoc_free(IJSComplexProperty* p); - -template class CJSComplex : public IJSComplex -{ typedef STL_HASH_MAP ReflectorTable; template friend class CJSComplexPropertyAccessor; JSObject* m_JS; + std::vector m_Watches; ReflectorTable m_Reflectors; + static JSPropertySpec JSI_props[]; + static std::vector m_Methods; + static PropertyTable m_IntrinsicProperties; + public: static JSClass JSI_class; @@ -509,511 +147,102 @@ public: // JS Property access bool GetProperty( JSContext* cx, const CStrW& PropertyName, jsval* vp ); - void 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; + void SetProperty( JSContext* cx, const CStrW& PropertyName, jsval* vp ); + void WatchNotify( JSContext* cx, const CStrW& PropertyName, jsval* newval ); - // 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 ); - } - } - } - void 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::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 // - static JSBool JSGetProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp ) - { - T* Instance = ToNative( 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 ); - } - static JSBool JSSetProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp ) - { - T* Instance = ToNative( cx, obj ); - if( !Instance ) - return( JS_TRUE ); - - CStrW PropName = g_ScriptingHost.ValueToUCString( id ); - - Instance->SetProperty( cx, PropName, vp ); - - return( JS_TRUE ); - } - static JSBool JSEnumerate( JSContext* cx, JSObject* obj, JSIterateOp enum_op, jsval* statep, jsid *idp ) - { - IteratorState* it; - - switch( enum_op ) - { - case JSENUMERATE_INIT: - { - T* Instance = ToNative( 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( g_ScriptingHost.GetContext(), ToJSVal( *( it->second ) ), idp ) ) - return( JS_FALSE ); - - (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 ); - } - static JSBool SetWatchAll( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval ) - { - T* Native = ToNative( cx, obj ); - if( !Native ) - return( JS_TRUE ); - - debug_assert( argc >= 1 ); - - CScriptObject watch( argv[0] ); - std::vector::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 ); - } - - static JSBool UnWatchAll( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval ) - { - T* Native = ToNative( cx, obj ); - if( !Native ) - return( JS_TRUE ); - - if( argc >= 1 ) - { - CScriptObject watch( argv[0] ); - std::vector::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 ); - } - - static void ScriptingInit( const char* ClassName, JSNative Constructor = NULL, uintN ConstructorMinArgs = 0 ) - { - JSFunctionSpec* JSI_methods = new JSFunctionSpec[ m_Methods.size() + 3 ]; - unsigned int 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 ); - } - 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( cx, obj ); - if( !Instance || Instance->m_EngineOwned ) - return; - - delete( Instance ); - JS_SetPrivate( cx, obj, NULL ); - } + static JSBool JSGetProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp ); + static JSBool JSSetProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp ); + static JSBool JSEnumerate( JSContext* cx, JSObject* obj, JSIterateOp enum_op, jsval* statep, jsid *idp ); + static JSBool SetWatchAll( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval ); + static JSBool UnWatchAll( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval ); + static void ScriptingInit( const char* ClassName, JSNative Constructor = NULL, uintN ConstructorMinArgs = 0 ); + static void ScriptingShutdown(); + static void DefaultFinalize( JSContext *cx, JSObject *obj ); public: - JSObject* GetScript() - { - if( !m_JS ) - CreateScriptObject(); - return( m_JS ); - } + JSObject* GetScript(); // 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 m_Methods; - static PropertyTable m_IntrinsicProperties; + void CreateScriptObject(); + void ReleaseScriptObject(); + + CJSComplex(); + virtual ~CJSComplex(); + void Shutdown(); + void SetBase( IJSComplex* Parent ); + void Rebuild(); + + + IJSComplexProperty* HasProperty( const CStrW& PropertyName ); + + void FillEnumerateSet( IteratorState* it, CStrW* PropertyRoot = NULL ); -public: - CJSComplex() - { - jscomplexproperty_suballoc_attach(); - - m_Parent = NULL; - m_JS = NULL; - m_EngineOwned = true; - } - virtual ~CJSComplex() - { - Shutdown(); - - jscomplexproperty_suballoc_detach(); - } - void 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 >* accessor = (CJSComplexPropertyAccessor< CJSComplex >*)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 >* accessor = (CJSComplexPropertyAccessor< CJSComplex >*)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(); - } - void 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(); - } - } - void 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(); + void AddProperty( const CStrW& PropertyName, jsval Value ); - } - IJSComplexProperty* HasProperty( const CStrW& PropertyName ) - { - PropertyTable::iterator it; - it = T::m_IntrinsicProperties.find( PropertyName ); - if( it != T::m_IntrinsicProperties.end() ) - return( it->second ); + void AddProperty( const CStrW& PropertyName, const CStrW& Value ); - it = m_Properties.find( PropertyName ); - if( it != m_Properties.end() ) - return( it->second ); + static void AddClassProperty( const CStrW& PropertyName, GetFn Getter, SetFn Setter = NULL ); - return( NULL ); - } - void FillEnumerateSet( IteratorState* it, CStrW* PropertyRoot = NULL ) - { - 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( iit->first.GetSubstring( rlen + 1, iit->first.Length() - 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 ); - } - void AddProperty( const CStrW& PropertyName, jsval Value ) - { - DeletePreviouslyAssignedProperty( PropertyName ); - void* mem = jscomplexproperty_suballoc(); -#include "lib/nommgr.h" - CJSDynamicComplexProperty* newProp = new(mem) CJSValComplexProperty( Value, false ); -#include "lib/mmgr.h" - 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 ); - } - } + // these functions are themselves templatized. we don't want to implement + // them in the header because that would drag in many dependencies. + // + // therefore, the publicly visibleare functions actually onl/ to y thunks + // external friend functions implemented in the cpp file. + // these receive the template parameters from the class as well as the + // ones added for the member function. + // + // for non-static members, the friends additionally take a "this" pointer. - void AddProperty( const CStrW& PropertyName, const CStrW& Value ) - { - AddProperty( PropertyName, JSParseString( Value ) ); - } - static void AddClassProperty( const CStrW& PropertyName, GetFn Getter, SetFn Setter = NULL ) - { - T::m_IntrinsicProperties[PropertyName] = new CJSFunctionComplexProperty( Getter, Setter ); - } template - static void AddMethod( const char* Name, uintN MinArgs ) + static void AddMethod( const char* Name, uintN MinArgs ) { - JSFunctionSpec FnInfo = { Name, CNativeComplexFunction::JSFunction, MinArgs, 0, 0 }; - T::m_Methods.push_back( FnInfo ); + AddMethodImpl(Name, MinArgs); } - template static void AddClassProperty( const CStrW& PropertyName, PropType T::*Native, bool PropAllowInheritance = true, NotifyFn Update = NULL, NotifyFn Refresh = NULL ) + + template + static void AddClassProperty( const CStrW& PropertyName, PropType T::*Native, bool PropAllowInheritance = true, NotifyFn Update = NULL, NotifyFn Refresh = NULL ) { - T::m_IntrinsicProperties[PropertyName] = new CJSSharedProperty( (PropType IJSComplex::*)Native, PropAllowInheritance, Update, Refresh ); + AddClassPropertyImpl(PropertyName, Native, PropAllowInheritance, Update, Refresh); } - template static void AddReadOnlyClassProperty( const CStrW& PropertyName, PropType T::*Native, bool PropAllowInheritance = true, NotifyFn Update = NULL, NotifyFn Refresh = NULL ) + + template + static void AddReadOnlyClassProperty( const CStrW& PropertyName, PropType T::*Native, bool PropAllowInheritance = true, NotifyFn Update = NULL, NotifyFn Refresh = NULL ) { - T::m_IntrinsicProperties[PropertyName] = new CJSSharedProperty( (PropType IJSComplex::*)Native, PropAllowInheritance, Update, Refresh ); + AddReadOnlyClassPropertyImpl(PropertyName, Native, PropAllowInheritance, Update, Refresh); + } + + // PropertyName must not already exist! (verified in debug build) + template + void AddProperty( const CStrW& PropertyName, PropType* Native, bool PropAllowInheritance = true, NotifyFn Update = NULL, NotifyFn Refresh = NULL ) + { + MemberAddPropertyImpl(this, PropertyName, Native, PropAllowInheritance, Update, Refresh); + } + + // PropertyName must not already exist! (verified in debug build) + template + void AddReadOnlyProperty( const CStrW& PropertyName, PropType* Native, bool PropAllowInheritance = true, NotifyFn Update = NULL, NotifyFn Refresh = NULL ) + { + MemberAddReadOnlyPropertyImpl(this, PropertyName, Native, PropAllowInheritance, Update, Refresh); } // 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). - void 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 - } - - // PropertyName must not already exist! (verified in debug build) - template void AddProperty( const CStrW& PropertyName, PropType* Native, bool PropAllowInheritance = true, NotifyFn Update = NULL, NotifyFn Refresh = NULL ) - { - DeletePreviouslyAssignedProperty( PropertyName ); - void* mem = jscomplexproperty_suballoc(); -#include "lib/nommgr.h" - m_Properties[PropertyName] = new(mem) CJSComplexProperty( Native, PropAllowInheritance, Update, Refresh ); -#include "lib/mmgr.h" - } - // PropertyName must not already exist! (verified in debug build) - template void AddReadOnlyProperty( const CStrW& PropertyName, PropType* Native, bool PropAllowInheritance = true, NotifyFn Update = NULL, NotifyFn Refresh = NULL ) - { - DeletePreviouslyAssignedProperty( PropertyName ); - void* mem = jscomplexproperty_suballoc(); -#include "lib/nommgr.h" - m_Properties[PropertyName] = new(mem) CJSComplexProperty( Native, PropAllowInheritance, Update, Refresh ); -#include "lib/mmgr.h" - } + void DeletePreviouslyAssignedProperty( const CStrW& PropertyName ); }; + +// +// static members +// + template JSClass CJSComplex::JSI_class = { NULL, JSCLASS_HAS_PRIVATE | JSCLASS_NEW_ENUMERATE, JS_PropertyStub, JS_PropertyStub, @@ -1030,79 +259,25 @@ template JSPropertySpec CJSComplex::JSI_ template std::vector CJSComplex::m_Methods; template typename CJSComplex::PropertyTable CJSComplex::m_IntrinsicProperties; -template bool CJSComplex::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 >::CreateAccessor( cx, this, PropertyName ); - JS_AddNamedRoot( cx, &extProp->m_JSAccessor, "property accessor" ); - } +template +void ScriptableComplex_InitComplexPropertyAccessor(); - *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. +//----------------------------------------------------------------------------- +// suballocator for CJSComplex.m_Properties elements +//----------------------------------------------------------------------------- - // (N.B. Can't just put JSComplexs* in the table -> table entries can - // move -> root no longer refers to the JSObject.) +extern void jscomplexproperty_suballoc_attach(); +extern void jscomplexproperty_suballoc_detach(); +extern void* jscomplexproperty_suballoc(); +extern void jscomplexproperty_suballoc_free(IJSComplexProperty* p); - ReflectorTable::iterator it; - it = m_Reflectors.find( PropertyName ); - if( it == m_Reflectors.end() ) - { - CJSReflector* reflector = new CJSReflector(); - reflector->m_JSAccessor = CJSComplexPropertyAccessor< CJSComplex >::CreateAccessor( cx, this, PropertyName ); - JS_AddRoot( cx, &reflector->m_JSAccessor ); - m_Reflectors.insert( std::pair( PropertyName, reflector ) ); - *vp = OBJECT_TO_JSVAL( reflector->m_JSAccessor ); - } - else - *vp = OBJECT_TO_JSVAL( it->second->m_JSAccessor ); - } - } - return( true ); -} +#include "ScriptableComplex.inl" + #endif diff --git a/source/scripting/ScriptableComplex.inl b/source/scripting/ScriptableComplex.inl new file mode 100644 index 0000000000..bc2afb7c00 --- /dev/null +++ b/source/scripting/ScriptableComplex.inl @@ -0,0 +1,1058 @@ + +#ifndef SCRIPTABLE_COMPLEX_INL_INCLUDED +#define SCRIPTABLE_COMPLEX_INL_INCLUDED + + +//----------------------------------------------------------------------------- +// CJSComplexPropertyAccessor +//----------------------------------------------------------------------------- + + +template class CJSComplex; + +template class CJSComplexPropertyAccessor +{ + T* m_Owner; + CStrW m_PropertyRoot; + template 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( iit->first.GetSubstring( rlen + 1, iit->first.Length() - 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( g_ScriptingHost.GetContext(), ToJSVal( *( it->second ) ), idp ) ) + return( JS_FALSE ); + + (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 JSClass CJSComplexPropertyAccessor::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 +void ScriptableComplex_InitComplexPropertyAccessor() +{ + CJSComplexPropertyAccessor::ScriptingInit(); +} + + +//----------------------------------------------------------------------------- +// various property types +//----------------------------------------------------------------------------- + +template 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 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 friend class CJSComplex; + JSObject* m_JSAccessor; +}; + +class CJSDynamicComplexProperty : public IJSComplexProperty +{ + template friend class CJSComplex; + + JSObject* m_JSAccessor; +public: + CJSDynamicComplexProperty() + { + m_JSAccessor = NULL; + m_Intrinsic = false; + m_Inherited = false; + } +}; + +class CJSValComplexProperty : public CJSDynamicComplexProperty +{ + template 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 class CNativeComplexFunction +{ +public: + static JSBool JSFunction( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval ) + { + T* Native = ToNative( cx, obj ); + if( !Native ) + return( JS_TRUE ); + + *rval = ToJSVal( (Native->*NativeFunction)( cx, argc, argv ) ); + + return( JS_TRUE ); + } +}; + +//----------------------------------------------------------------------------- +// CJSComplex implementation +//----------------------------------------------------------------------------- + +template +void CJSComplex::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 +void CJSComplex::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::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 +JSBool CJSComplex::JSGetProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp ) +{ + T* Instance = ToNative( 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 +JSBool CJSComplex::JSSetProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp ) +{ + T* Instance = ToNative( cx, obj ); + if( !Instance ) + return( JS_TRUE ); + + CStrW PropName = g_ScriptingHost.ValueToUCString( id ); + + Instance->SetProperty( cx, PropName, vp ); + + return( JS_TRUE ); +} + + +template +JSBool CJSComplex::JSEnumerate( JSContext* cx, JSObject* obj, JSIterateOp enum_op, jsval* statep, jsid *idp ) +{ + IteratorState* it; + + switch( enum_op ) + { + case JSENUMERATE_INIT: + { + T* Instance = ToNative( 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( g_ScriptingHost.GetContext(), ToJSVal( *( it->second ) ), idp ) ) + return( JS_FALSE ); + + (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 +JSBool CJSComplex::SetWatchAll( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval ) +{ + T* Native = ToNative( cx, obj ); + if( !Native ) + return( JS_TRUE ); + + debug_assert( argc >= 1 ); + + CScriptObject watch( argv[0] ); + std::vector::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 +JSBool CJSComplex::UnWatchAll( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval ) +{ + T* Native = ToNative( cx, obj ); + if( !Native ) + return( JS_TRUE ); + + if( argc >= 1 ) + { + CScriptObject watch( argv[0] ); + std::vector::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 +void CJSComplex::ScriptingInit( const char* ClassName, JSNative Constructor = NULL, uintN ConstructorMinArgs = 0 ) +{ + JSFunctionSpec* JSI_methods = new JSFunctionSpec[ m_Methods.size() + 3 ]; + unsigned int 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 +void CJSComplex::ScriptingShutdown() +{ + PropertyTable::iterator it; + for( it = m_IntrinsicProperties.begin(); it != m_IntrinsicProperties.end(); it++ ) + delete( it->second ); +} + + +template +void CJSComplex::DefaultFinalize( JSContext *cx, JSObject *obj ) +{ + T* Instance = ToNative( cx, obj ); + if( !Instance || Instance->m_EngineOwned ) + return; + + delete( Instance ); + JS_SetPrivate( cx, obj, NULL ); +} + + +template +JSObject* CJSComplex::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. +template +void CJSComplex::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 +void CJSComplex::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 +CJSComplex::CJSComplex() +{ + jscomplexproperty_suballoc_attach(); + + m_Parent = NULL; + m_JS = NULL; + m_EngineOwned = true; +} + + +template +CJSComplex::~CJSComplex() +{ + Shutdown(); + + jscomplexproperty_suballoc_detach(); +} + + +template +void CJSComplex::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 >* accessor = (CJSComplexPropertyAccessor< CJSComplex >*)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 >* accessor = (CJSComplexPropertyAccessor< CJSComplex >*)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 +void CJSComplex::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 +void CJSComplex::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 +IJSComplexProperty* CJSComplex::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 +void CJSComplex::FillEnumerateSet( IteratorState* it, CStrW* PropertyRoot = NULL ) +{ + 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( iit->first.GetSubstring( rlen + 1, iit->first.Length() - 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 +void CJSComplex::AddProperty( const CStrW& PropertyName, jsval Value ) +{ + DeletePreviouslyAssignedProperty( PropertyName ); + void* mem = jscomplexproperty_suballoc(); +#include "lib/nommgr.h" + CJSDynamicComplexProperty* newProp = new(mem) CJSValComplexProperty( Value, false ); +#include "lib/mmgr.h" + 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 +void CJSComplex::AddProperty( const CStrW& PropertyName, const CStrW& Value ) +{ + AddProperty( PropertyName, JSParseString( Value ) ); +} + +template +void CJSComplex::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 +void CJSComplex::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 +bool CJSComplex::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 >::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 >::CreateAccessor( cx, this, PropertyName ); + JS_AddRoot( cx, &reflector->m_JSAccessor ); + m_Reflectors.insert( std::pair( PropertyName, reflector ) ); + *vp = OBJECT_TO_JSVAL( reflector->m_JSAccessor ); + } + else + *vp = OBJECT_TO_JSVAL( it->second->m_JSAccessor ); + } + } + return( true ); +} + +template +void AddMethodImpl( const char* Name, uintN MinArgs ) +{ + JSFunctionSpec FnInfo = { Name, CNativeComplexFunction::JSFunction, MinArgs, 0, 0 }; + T::m_Methods.push_back( FnInfo ); +} + +template +void AddClassPropertyImpl( const CStrW& PropertyName, PropType T::*Native, bool PropAllowInheritance, IJSComplex::NotifyFn Update, IJSComplex::NotifyFn Refresh ) +{ + T::m_IntrinsicProperties[PropertyName] = new CJSSharedProperty( (PropType IJSComplex::*)Native, PropAllowInheritance, Update, Refresh ); +} + +template +void AddReadOnlyClassPropertyImpl( const CStrW& PropertyName, PropType T::*Native, bool PropAllowInheritance, IJSComplex::NotifyFn Update, IJSComplex::NotifyFn Refresh ) +{ + T::m_IntrinsicProperties[PropertyName] = new CJSSharedProperty( (PropType IJSComplex::*)Native, PropAllowInheritance, Update, Refresh ); +} + +// PropertyName must not already exist! (verified in debug build) +template +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(); +#include "lib/nommgr.h" + obj->m_Properties[PropertyName] = new(mem) CJSComplexProperty( Native, PropAllowInheritance, Update, Refresh ); +#include "lib/mmgr.h" +} + +// PropertyName must not already exist! (verified in debug build) +template +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(); +#include "lib/nommgr.h" + obj->m_Properties[PropertyName] = new(mem) CJSComplexProperty( Native, PropAllowInheritance, Update, Refresh ); +#include "lib/mmgr.h" +} + +#endif diff --git a/source/simulation/Technology.cpp b/source/simulation/Technology.cpp index 0f13e9bc9e..4a333750bb 100644 --- a/source/simulation/Technology.cpp +++ b/source/simulation/Technology.cpp @@ -441,8 +441,6 @@ void CTechnology::ScriptingInit() AddMethod( "getPlayerID", 0 ); CJSObject::ScriptingInit("Technology"); - - debug_printf("CTechnology::ScriptingInit complete"); } jsval CTechnology::ApplyEffects( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) )