// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
#pragma once
#include "MTypes.h"
#include "Engine/Core/Delegate.h"
#include "Engine/Core/Types/String.h"
#include "Engine/Core/Collections/Array.h"
#include "Engine/Core/Collections/Dictionary.h"
#include "Engine/Platform/CriticalSection.h"
///
/// Represents a managed assembly, which is a reusable, versionable, and self-describing building block of a common language runtime application.
///
class FLAXENGINE_API MAssembly
{
friend MDomain;
friend Scripting;
public:
typedef Dictionary ClassesDictionary;
private:
#if USE_MONO
MonoAssembly* _monoAssembly = nullptr;
MonoImage* _monoImage = nullptr;
#elif USE_NETCORE
void* _handle = nullptr;
StringAnsi _fullname;
#endif
MDomain* _domain;
int32 _isLoaded : 1;
int32 _isLoading : 1;
mutable int32 _hasCachedClasses : 1;
mutable ClassesDictionary _classes;
int32 _reloadCount;
StringAnsi _name;
String _assemblyPath;
Array _debugData;
public:
///
/// Initializes a new instance of the class.
///
/// The assembly domain.
/// The assembly name.
MAssembly(MDomain* domain, const StringAnsiView& name);
#if USE_NETCORE
///
/// Initializes a new instance of the class.
///
/// The assembly domain.
/// The assembly name.
/// The assembly full name.
/// The managed handle of the assembly.
MAssembly(MDomain* domain, const StringAnsiView& name, const StringAnsiView& fullname, void* handle);
#endif
///
/// Finalizes an instance of the class.
///
~MAssembly();
public:
///
/// Managed assembly actions delegate type.
///
typedef Delegate AssemblyDelegate;
///
/// Action fired when assembly starts loading.
///
AssemblyDelegate Loading;
///
/// Action fired when assembly gets loaded.
///
AssemblyDelegate Loaded;
///
/// Action fired when assembly loading fails.
///
AssemblyDelegate LoadFailed;
///
/// Action fired when assembly start unloading.
///
AssemblyDelegate Unloading;
///
/// Action fired when assembly gets unloaded.
///
AssemblyDelegate Unloaded;
public:
///
/// Returns true if assembly is during loading state.
///
FORCE_INLINE bool IsLoading() const
{
return _isLoading != 0;
}
///
/// Returns true if assembly has been loaded.
///
FORCE_INLINE bool IsLoaded() const
{
return _isLoaded != 0;
}
///
/// Gets the assembly name.
///
FORCE_INLINE const StringAnsi& GetName() const
{
return _name;
}
///
/// Gets the assembly name as string.
///
String ToString() const;
///
/// Gets the assembly path.
///
///
/// If assembly was made from scratch (empty), path will return null.
///
FORCE_INLINE const String& GetAssemblyPath() const
{
return _assemblyPath;
}
///
/// Gets the parent domain.
///
FORCE_INLINE MDomain* GetDomain() const
{
return _domain;
}
#if USE_MONO
FORCE_INLINE MonoAssembly* GetMonoAssembly() const
{
return _monoAssembly;
}
FORCE_INLINE MonoImage* GetMonoImage() const
{
return _monoImage;
}
#elif USE_NETCORE
FORCE_INLINE void* GetHandle() const
{
return _handle;
}
#endif
public:
///
/// Loads assembly for domain.
///
/// The assembly path.
/// The optional path to the native code assembly (eg. if C# assembly contains bindings).
/// True if cannot load, otherwise false
bool Load(const String& assemblyPath, const StringView& nativePath = StringView::Empty);
#if USE_MONO
///
/// Loads assembly for domain.
///
/// The assembly image.
/// True if cannot load, otherwise false.
bool Load(MonoImage* monoImage);
#endif
///
/// Cleanup data. Caller must ensure not to use any types from this assembly after it has been unloaded.
///
/// If true assembly is during reloading and should force release the runtime data.
void Unload(bool isReloading = false);
public:
///
/// Attempts to find a managed class with the specified namespace and name in this assembly. Returns null if one cannot be found.
///
/// The type name.
/// The class object or null if failed to find it.
MClass* GetClass(const StringAnsiView& typeName) const;
#if USE_MONO
///
/// Converts an internal mono representation of a class into engine class.
///
/// The Mono class.
/// The class object.
MClass* GetClass(MonoClass* monoClass) const;
///
/// Gets the native of the assembly (for the current domain). Can be used to pass to the scripting backend as a parameter.
///
MonoReflectionAssembly* GetNative() const;
#endif
///
/// Gets the classes lookup cache. Performs full initialization if not cached. The result cache contains all classes from the assembly.
///
const ClassesDictionary& GetClasses() const;
private:
bool LoadCorlib();
bool LoadImage(const String& assemblyPath, const StringView& nativePath);
bool UnloadImage(bool isReloading);
void OnLoading();
void OnLoaded(const struct DateTime& startTime);
void OnLoadFailed();
bool ResolveMissingFile(String& assemblyPath) const;
};