// 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; };