// 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 #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 PropertyTable; typedef std::vector InheritorsList; typedef std::set StringTable; typedef std::pair 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 void AddMethodImpl( const char* Name, uintN MinArgs ); template void AddClassPropertyImpl( const CStrW& PropertyName, PropType T::*Native, bool PropAllowInheritance = true, IJSComplex::NotifyFn Update = NULL, IJSComplex::NotifyFn Refresh = NULL ); template void AddReadOnlyClassPropertyImpl( const CStrW& PropertyName, PropType T::*Native, bool PropAllowInheritance = true, IJSComplex::NotifyFn Update = NULL, IJSComplex::NotifyFn Refresh = NULL ); template void MemberAddPropertyImpl( IJSComplex* obj, const CStrW& PropertyName, PropType* Native, bool PropAllowInheritance = true, IJSComplex::NotifyFn Update = NULL, IJSComplex::NotifyFn Refresh = NULL ); template void MemberAddReadOnlyPropertyImpl( IJSComplex* obj, const CStrW& PropertyName, PropType* Native, bool PropAllowInheritance = true, IJSComplex::NotifyFn Update = NULL, IJSComplex::NotifyFn Refresh = NULL ); template class CJSComplex : public IJSComplex { public: 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; // 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 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. template static void AddMethod( const char* Name, uintN MinArgs ) { AddMethodImpl(Name, MinArgs); } template static void AddClassProperty( const CStrW& PropertyName, PropType T::*Native, bool PropAllowInheritance = true, NotifyFn Update = NULL, NotifyFn Refresh = NULL ) { 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 ) { 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 ); }; // // static members // template JSClass CJSComplex::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 JSPropertySpec CJSComplex::JSI_props[] = { { 0 }, }; template std::vector CJSComplex::m_Methods; template typename CJSComplex::PropertyTable CJSComplex::m_IntrinsicProperties; template void ScriptableComplex_InitComplexPropertyAccessor(); //----------------------------------------------------------------------------- // suballocator for CJSComplex.m_Properties elements //----------------------------------------------------------------------------- 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