// Copyright (c) 2012-2020 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/Serialization/ISerializable.h"
#include "ManagedCLR/MAssemblyOptions.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);
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 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)
{
return FindMethod(typeHandle, signature.Name, signature.Params.Count());
}
///
/// 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 it's 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 it's 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 cleanup 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;
Array _managedTypesNames;
public:
///
/// Initializes a new instance of the class.
///
/// The module name.
/// The assembly options.
ManagedBinaryModule(const StringAnsiView& name, const MAssemblyOptions& options);
///
/// 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;
///
/// 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;
static ScriptingObject* ManagedObjectSpawn(const ScriptingObjectSpawnParams& params);
static MMethod* FindMethod(MClass* mclass, const ScriptingTypeMethodSignature& signature);
private:
void OnLoading(MAssembly* assembly);
void OnLoaded(MAssembly* assembly);
void OnUnloading(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.
/// The assembly options.
NativeBinaryModule(const StringAnsiView& name, const MAssemblyOptions& options);
///
/// 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;
};
typedef NativeBinaryModule* (*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();
};