0ad/source/scripting/ScriptableComplex.h
2006-07-27 12:54:47 +00:00

282 lines
10 KiB
C++

// ScriptableComplex.h
//
// The version of CJSObject<> that retains the ability to use inheritance
// in its objects. Shouldn't be used any more for anything but entity code.
#include "scripting/ScriptingHost.h"
#include "simulation/ScriptObject.h"
#include "JSConversions.h"
#include <set>
#ifndef SCRIPTABLE_COMPLEX_INCLUDED
#define SCRIPTABLE_COMPLEX_INCLUDED
class IJSComplex;
class IJSComplexProperty
{
public:
bool m_AllowsInheritance;
bool m_Inherited;
bool m_Intrinsic;
// This is to make sure that all the fields are initialized at construction
inline IJSComplexProperty():
m_AllowsInheritance(true),
m_Inherited(true),
m_Intrinsic(true)
{}
virtual jsval Get( JSContext* cx, IJSComplex* owner ) = 0;
virtual void Set( JSContext* cx, IJSComplex* owner, jsval Value ) = 0;
// Copies the data directly out of a parent property
// Warning: Don't use if you're not certain the properties are not of the same type.
virtual void ImmediateCopy( IJSComplex* CopyTo, IJSComplex* CopyFrom, IJSComplexProperty* CopyProperty ) = 0;
jsval Get( IJSComplex* owner ) { return( Get( g_ScriptingHost.GetContext(), owner ) ); }
void Set( IJSComplex* owner, jsval Value ) { return( Set( g_ScriptingHost.GetContext(), owner, Value ) ); }
virtual ~IJSComplexProperty() {}
};
class IJSComplex
{
// Make copy constructor and assignment operator private - since copying of
// these objects is unsafe unless done specially.
// These will never be implemented (they are, after all, here to *prevent*
// copying)
IJSComplex(const IJSComplex &other);
IJSComplex& operator=(const IJSComplex &other);
public:
typedef STL_HASH_MAP<CStrW, IJSComplexProperty*, CStrW_hash_compare> PropertyTable;
typedef std::vector<IJSComplex*> InheritorsList;
typedef std::set<CStrW> StringTable;
typedef std::pair<StringTable, StringTable::iterator> IteratorState;
// Used for freshen/update
typedef void (IJSComplex::*NotifyFn)();
// Property getters and setters
typedef jsval (IJSComplex::*GetFn)();
typedef void (IJSComplex::*SetFn)( jsval );
// Properties of this object
PropertyTable m_Properties;
// Parent object
IJSComplex* m_Parent;
// Objects that inherit from this
InheritorsList m_Inheritors;
// Destructor
virtual ~IJSComplex() { }
// Set the base, and rebuild
void SetBase( IJSComplex* m_Parent );
// Rebuild any intrinsic (mapped-to-C++-variable) properties
virtual void Rebuild() = 0;
// HACK: Doesn't belong here.
virtual void rebuildClassSet() = 0;
// Check for a property
virtual IJSComplexProperty* HasProperty( const CStrW& PropertyName ) = 0;
// Get all properties of an object
virtual void FillEnumerateSet( IteratorState* it, CStrW* PropertyRoot = NULL ) = 0;
// Retrieve the value of a property (returning false if that property is not defined)
virtual bool GetProperty( JSContext* cx, const CStrW& PropertyName, jsval* vp ) = 0;
// Add a property (with immediate value)
virtual void AddProperty( const CStrW& PropertyName, jsval Value ) = 0;
virtual void AddProperty( const CStrW& PropertyName, const CStrW& Value ) = 0;
inline IJSComplex() {}
};
class CJSReflector;
template<typename T, bool ReadOnly, typename ReturnType, ReturnType (T::*NativeFunction)( JSContext* cx, uintN argc, jsval* argv )>
void AddMethodImpl( const char* Name, uintN MinArgs );
template<typename T, bool ReadOnly, typename PropType>
void AddClassPropertyImpl( const CStrW& PropertyName, PropType T::*Native, bool PropAllowInheritance = true, IJSComplex::NotifyFn Update = NULL, IJSComplex::NotifyFn Refresh = NULL );
template<typename T, bool ReadOnly, typename PropType>
void AddReadOnlyClassPropertyImpl( const CStrW& PropertyName, PropType T::*Native, bool PropAllowInheritance = true, IJSComplex::NotifyFn Update = NULL, IJSComplex::NotifyFn Refresh = NULL );
template<typename T, bool ReadOnly, typename PropType>
void MemberAddPropertyImpl( IJSComplex* obj, const CStrW& PropertyName, PropType* Native, bool PropAllowInheritance = true, IJSComplex::NotifyFn Update = NULL, IJSComplex::NotifyFn Refresh = NULL );
template<typename T, bool ReadOnly, typename PropType>
void MemberAddReadOnlyPropertyImpl( IJSComplex* obj, const CStrW& PropertyName, PropType* Native, bool PropAllowInheritance = true, IJSComplex::NotifyFn Update = NULL, IJSComplex::NotifyFn Refresh = NULL );
template<typename T, bool ReadOnly = false> class CJSComplex : public IJSComplex
{
public:
typedef STL_HASH_MAP<CStrW, CJSReflector*, CStrW_hash_compare> ReflectorTable;
template<typename Q> friend class CJSComplexPropertyAccessor;
JSObject* m_JS;
std::vector<CScriptObject> m_Watches;
ReflectorTable m_Reflectors;
static JSPropertySpec JSI_props[];
static std::vector<JSFunctionSpec> m_Methods;
static PropertyTable m_IntrinsicProperties;
public:
static JSClass JSI_class;
// Whether native code is responsible for managing this object.
// Script constructors should clear this *BEFORE* creating a JS
// mirror (otherwise it'll be rooted).
bool m_EngineOwned;
// JS Property access
bool GetProperty( JSContext* cx, const CStrW& PropertyName, jsval* vp );
void SetProperty( JSContext* cx, const CStrW& PropertyName, jsval* vp );
void WatchNotify( JSContext* cx, const CStrW& PropertyName, jsval* newval );
//
// Functions that must be provided to JavaScript
//
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();
// Creating and releasing script objects is done automatically most of the time, but you
// can do it explicitly.
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 );
void AddProperty( const CStrW& PropertyName, jsval Value );
void AddProperty( const CStrW& PropertyName, const CStrW& Value );
static void AddClassProperty( const CStrW& PropertyName, GetFn Getter, SetFn Setter = NULL );
// 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 visible functions actually only call out to
// external friend functions implemented in the .inl 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.
template<typename ReturnType, ReturnType (T::*NativeFunction)( JSContext* cx, uintN argc, jsval* argv )>
static void AddMethod( const char* Name, uintN MinArgs )
{
AddMethodImpl<T, ReadOnly, ReturnType, NativeFunction>(Name, MinArgs);
}
template<typename PropType>
static void AddClassProperty( const CStrW& PropertyName, PropType T::*Native, bool PropAllowInheritance = true, NotifyFn Update = NULL, NotifyFn Refresh = NULL )
{
AddClassPropertyImpl<T, ReadOnly, PropType>(PropertyName, Native, PropAllowInheritance, Update, Refresh);
}
template<typename PropType>
static void AddReadOnlyClassProperty( const CStrW& PropertyName, PropType T::*Native, bool PropAllowInheritance = true, NotifyFn Update = NULL, NotifyFn Refresh = NULL )
{
AddReadOnlyClassPropertyImpl<T, ReadOnly, PropType>(PropertyName, Native, PropAllowInheritance, Update, Refresh);
}
// PropertyName must not already exist! (verified in debug build)
template<typename PropType>
void AddProperty( const CStrW& PropertyName, PropType* Native, bool PropAllowInheritance = true, NotifyFn Update = NULL, NotifyFn Refresh = NULL )
{
MemberAddPropertyImpl<T, ReadOnly, PropType>(this, PropertyName, Native, PropAllowInheritance, Update, Refresh);
}
// PropertyName must not already exist! (verified in debug build)
template<typename PropType>
void AddReadOnlyProperty( const CStrW& PropertyName, PropType* Native, bool PropAllowInheritance = true, NotifyFn Update = NULL, NotifyFn Refresh = NULL )
{
MemberAddReadOnlyPropertyImpl<T, ReadOnly, PropType>(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 );
};
//
// static members
//
template<typename T, bool ReadOnly> JSClass CJSComplex<T, ReadOnly>::JSI_class = {
NULL, JSCLASS_HAS_PRIVATE | JSCLASS_NEW_ENUMERATE,
JS_PropertyStub, JS_PropertyStub,
JSGetProperty, JSSetProperty,
(JSEnumerateOp)JSEnumerate, JS_ResolveStub,
JS_ConvertStub, DefaultFinalize,
NULL, NULL, NULL, NULL
};
template<typename T, bool ReadOnly> JSPropertySpec CJSComplex<T, ReadOnly>::JSI_props[] = {
{ 0 },
};
template<typename T, bool ReadOnly> std::vector<JSFunctionSpec> CJSComplex<T, ReadOnly>::m_Methods;
template<typename T, bool ReadOnly> typename CJSComplex<T, ReadOnly>::PropertyTable CJSComplex<T, ReadOnly>::m_IntrinsicProperties;
template<typename T>
void ScriptableComplex_InitComplexPropertyAccessor();
//
// suballocator for CJSComplex.m_Properties elements
// (referenced from implementation in .inl)
//
extern void jscomplexproperty_suballoc_attach();
extern void jscomplexproperty_suballoc_detach();
extern void* jscomplexproperty_suballoc();
extern void jscomplexproperty_suballoc_free(IJSComplexProperty* p);
#include "ScriptableComplex.inl"
#endif