// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. #pragma once #include "ScriptingType.h" #include "Engine/Core/Types/Span.h" #include "Engine/Core/Types/String.h" #include "Engine/Core/Types/Variant.h" #include "Engine/Core/Collections/Dictionary.h" #include "Engine/Core/Collections/Array.h" #include "Engine/Core/ISerializable.h" /// /// The scripting type method metadata for code reflection. /// struct ScriptingTypeMethodSignature { struct Param { VariantType Type; bool IsOut; }; StringAnsiView Name; VariantType ReturnType; bool IsStatic; Array> Params; }; /// /// The scripting type field metadata for code reflection. /// struct ScriptingTypeFieldSignature { StringAnsiView Name; VariantType ValueType; bool IsStatic; }; /// /// The scripting assembly container that holds the scripting types information and metadata. /// class FLAXENGINE_API BinaryModule { public: typedef Array> BinaryModulesList; public: /// /// The list with all registered binary modules (including external for plugins and other modules). /// static BinaryModulesList& GetModules(); /// /// Finds the module by name. /// /// The module name. /// The found binary module or null if missing. static BinaryModule* GetModule(const StringAnsiView& name); // Global scripting locker for cached data. static CriticalSection Locker; protected: /// /// Initializes a new instance of the class. /// BinaryModule(); public: /// /// Finalizes an instance of the class. /// virtual ~BinaryModule() { } public: /// /// The scripting types collection that exist in this assembly. /// Array Types; /// /// The scripting types cache that maps the full typename to the scripting type index. Build after adding the type to the assembly. /// Dictionary TypeNameToTypeIndex; public: /// /// Gets the assembly name. /// /// The assembly name. virtual const StringAnsi& GetName() const = 0; /// /// Returns true if module is loaded, otherwise false (it might not be loaded yet or failed to load). /// virtual bool IsLoaded() const = 0; /// /// Tries to find a given scripting type by the full name. /// /// The full name of the type eg: System.Int64.MaxInt. /// The result type index in Types array of this module. Valid only if method returns true. /// True if found a type, otherwise false. virtual bool FindScriptingType(const StringAnsiView& typeName, int32& typeIndex) { return TypeNameToTypeIndex.TryGet(typeName, typeIndex); } /// /// Tries to find a method in a given scripting type by the method name and parameters count. /// /// If the type contains more than one method of the given name and parameters count the returned value can be non-deterministic (one of the matching methods). /// The type to find method inside it. /// The method name. /// The method parameters count. /// The method or null if failed to get it. virtual void* FindMethod(const ScriptingTypeHandle& typeHandle, const StringAnsiView& name, int32 numParams = 0) { return nullptr; } /// /// Tries to find a method in a given scripting type by the method signature. /// /// The type to find method inside it. /// The method signature. /// The method or null if failed to get it. virtual void* FindMethod(const ScriptingTypeHandle& typeHandle, const ScriptingTypeMethodSignature& signature); /// /// Invokes a given scripting method. /// /// The method. /// The object instance to call it's member method. Unused for static methods. /// The method parameters array. For output parameters the method writes the values back to the parameters. Length of this list has to match the method arguments amount. /// The output value method returned. Not used for void method. /// True if failed, otherwise false. virtual bool InvokeMethod(void* method, const Variant& instance, Span paramValues, Variant& result) { return true; } /// /// Gets the scripting type method signature metadata. /// /// The method. /// The output method signature info. virtual void GetMethodSignature(void* method, ScriptingTypeMethodSignature& signature) { } /// /// Tries to find a field in a given scripting type by the field name. /// /// The type to find field inside it. /// The field name. /// The field or null if failed to get it. virtual void* FindField(const ScriptingTypeHandle& typeHandle, const StringAnsiView& name) { return nullptr; } /// /// Gets the scripting type field signature metadata. /// /// The field. /// The output field signature info. virtual void GetFieldSignature(void* field, ScriptingTypeFieldSignature& fieldSignature) { } /// /// Gets the value of a given scripting field. /// /// The field. /// The object instance to get its member field. Unused for static fields. /// The output field value. /// True if failed, otherwise false. virtual bool GetFieldValue(void* field, const Variant& instance, Variant& result) { return true; } /// /// Sets the value of a given scripting field. /// /// The field. /// The object instance to set its member field. Unused for static fields. /// The field value to assign. /// True if failed, otherwise false. virtual bool SetFieldValue(void* field, const Variant& instance, Variant& value) { return true; } /// /// Serializes the scripting object data. Called for objects using IsCustomScriptingType. /// /// The output stream. /// The object instance to serialize. /// The instance of the object to compare with and serialize only the modified properties. If null, then serialize all properties. virtual void SerializeObject(JsonWriter& stream, ScriptingObject* object, const ScriptingObject* otherObj) { } /// /// Deserialize object from the input stream /// /// The input stream. /// The object instance to deserialize. /// The deserialization modifier object. Always valid. virtual void DeserializeObject(ISerializable::DeserializeStream& stream, ScriptingObject* object, ISerializeModifier* modifier) { } /// /// Called when object ID gets changed. Can be used to synchronize any cache in the scripting backend. /// /// The object instance. /// The previous object ID. virtual void OnObjectIdChanged(ScriptingObject* object, const Guid& oldId) { } /// /// Called when object gets removed (inside destructor). Can be used to clear any cached data inside the scripting backend. /// /// The object instance. virtual void OnObjectDeleted(ScriptingObject* object) { } /// /// Unloads the module (native library and C# assembly and any other scripting data). Unregisters the module. /// /// If true module is during reloading and should force release the runtime data. Used for C# assembly to clean up it's runtime data in Mono (or other scripting runtime). virtual void Destroy(bool isReloading); }; /// /// The C#-only scripting assembly container that holds the native types information and supports interop with managed runtime. /// class FLAXENGINE_API ManagedBinaryModule : public BinaryModule { public: /// /// Finds the module by C# assembly. /// /// The module C# assembly. /// The found binary module or null if missing. static ManagedBinaryModule* GetModule(const MAssembly* assembly); private: int32 _firstManagedTypeIndex = 0; Array _managedMemoryBlocks; public: /// /// Initializes a new instance of the class. /// /// The module name. ManagedBinaryModule(const StringAnsiView& name); /// /// Initializes a new instance of the class. /// /// The managed assembly. Object will be deleted within the scripting assembly. explicit ManagedBinaryModule(MAssembly* assembly); /// /// Finalizes an instance of the class. /// ~ManagedBinaryModule(); public: /// /// The managed assembly (C# DLL). /// MAssembly* Assembly; #if USE_CSHARP /// /// The scripting types cache that maps the managed class to the scripting type index. Build after assembly is loaded and scripting types get the managed classes information. /// Dictionary ClassToTypeIndex; #endif static ScriptingObject* ManagedObjectSpawn(const ScriptingObjectSpawnParams& params); static MMethod* FindMethod(MClass* mclass, const ScriptingTypeMethodSignature& signature); #if USE_CSHARP static ManagedBinaryModule* FindModule(const MClass* klass); static ScriptingTypeHandle FindType(const MClass* klass); #endif private: void OnLoading(MAssembly* assembly); void OnLoaded(MAssembly* assembly); void InitType(MClass* mclass); void OnUnloading(MAssembly* assembly); void OnUnloaded(MAssembly* assembly); public: // [BinaryModule] const StringAnsi& GetName() const override; bool IsLoaded() const override; void* FindMethod(const ScriptingTypeHandle& typeHandle, const StringAnsiView& name, int32 numParams = 0) override; void* FindMethod(const ScriptingTypeHandle& typeHandle, const ScriptingTypeMethodSignature& signature) override; bool InvokeMethod(void* method, const Variant& instance, Span paramValues, Variant& result) override; void GetMethodSignature(void* method, ScriptingTypeMethodSignature& signature) override; void* FindField(const ScriptingTypeHandle& typeHandle, const StringAnsiView& name) override; void GetFieldSignature(void* field, ScriptingTypeFieldSignature& fieldSignature) override; bool GetFieldValue(void* field, const Variant& instance, Variant& result) override; bool SetFieldValue(void* field, const Variant& instance, Variant& value) override; void Destroy(bool isReloading) override; }; /// /// The C# and C++ scripting assembly container that holds the native types information and supports interop with managed runtime. /// class FLAXENGINE_API NativeBinaryModule : public ManagedBinaryModule { public: /// /// Initializes a new instance of the class. /// /// The module name. NativeBinaryModule(const StringAnsiView& name); /// /// Initializes a new instance of the class. /// /// The managed assembly. Object will be deleted within the scripting assembly. explicit NativeBinaryModule(MAssembly* assembly); public: /// /// The native library (C++ DLL). /// void* Library; public: // [ManagedBinaryModule] void Destroy(bool isReloading) override; }; /// /// The C++ scripting assembly container. /// class FLAXENGINE_API NativeOnlyBinaryModule : public BinaryModule { private: StringAnsi _name; public: /// /// Initializes a new instance of the class. /// /// The module name. NativeOnlyBinaryModule(const StringAnsiView& name); public: /// /// The native library (C++ DLL). /// void* Library; public: // [BinaryModule] const StringAnsi& GetName() const override; bool IsLoaded() const override; void Destroy(bool isReloading) override; }; typedef BinaryModule* (*GetBinaryModuleFunc)(); // Helper utility for registering native binary modules that are statically linked. class FLAXENGINE_API StaticallyLinkedBinaryModuleInitializer { private: GetBinaryModuleFunc _getter; public: static Array>& GetStaticallyLinkedBinaryModules(); explicit StaticallyLinkedBinaryModuleInitializer(GetBinaryModuleFunc getter); ~StaticallyLinkedBinaryModuleInitializer(); };