Refactor manually written managed bindings to use C-style exported P/Invoke

This commit is contained in:
Wojciech Figat
2023-01-23 18:14:38 +01:00
parent 38fcfee9a4
commit f3366178ea
19 changed files with 1157 additions and 1254 deletions

View File

@@ -29,12 +29,20 @@ struct FLAXENGINE_API VTableFunctionInjector
*VTableAddr = OriginalValue;
}
};
#elif defined(_MSC_VER)
#define MSVC_FUNC_EXPORT(name) __pragma(comment(linker, "/EXPORT:" #name "=" __FUNCDNAME__))
#endif
#if USE_MONO
#if USE_NETCORE
#define ADD_INTERNAL_CALL(fullName, method)
#define DEFINE_INTERNAL_CALL(returnType) extern "C" DLLEXPORT returnType
#else
extern "C" FLAXENGINE_API void mono_add_internal_call(const char* name, const void* method);
#define ADD_INTERNAL_CALL(fullName, method) mono_add_internal_call(fullName, (const void*)method)
#define DEFINE_INTERNAL_CALL(returnType) static returnType
#endif
#if BUILD_RELEASE && 0
@@ -76,9 +84,7 @@ extern "C" FLAXENGINE_API void mono_add_internal_call(const char* name, const vo
#else
extern void DotNetAddInternalCall(const wchar_t* fullName, void* function);
#define ADD_INTERNAL_CALL(fullName, method) DotNetAddInternalCall(TEXT(fullName), (void*)(method))
#define ADD_INTERNAL_CALL(fullName, method)
#define INTERNAL_CALL_CHECK(obj)
#define INTERNAL_CALL_CHECK_EXP(expression)
#define INTERNAL_CALL_CHECK_RETURN(obj, defaultValue)

View File

@@ -240,7 +240,7 @@ namespace FlaxEngine
/// </summary>
/// <param name="ptr">The pointer to the unmanaged (native) object.</param>
/// <returns>The object.</returns>
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Object::FromUnmanagedPtr", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_FromUnmanagedPtr", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
public static partial Object FromUnmanagedPtr(IntPtr ptr);
/// <inheritdoc />
@@ -251,34 +251,34 @@ namespace FlaxEngine
#region Internal Calls
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Object::Internal_Create1", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_Create1", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
internal static partial Object Internal_Create1([MarshalUsing(typeof(SystemTypeMarshaller))] Type type);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Object::Internal_Create2", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_Create2", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
internal static partial Object Internal_Create2(string typeName);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Object::Internal_ManagedInstanceCreated", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_ManagedInstanceCreated", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
internal static partial void Internal_ManagedInstanceCreated(Object managedInstance);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Object::Internal_ManagedInstanceDeleted", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_ManagedInstanceDeleted", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
internal static partial void Internal_ManagedInstanceDeleted(IntPtr nativeInstance);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Object::Internal_Destroy", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_Destroy", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
internal static partial void Internal_Destroy(IntPtr obj, float timeLeft);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Object::Internal_GetTypeName", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_GetTypeName", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
internal static partial string Internal_GetTypeName(IntPtr obj);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Object::Internal_FindObject", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_FindObject", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
internal static partial Object Internal_FindObject(ref Guid id, [MarshalUsing(typeof(SystemTypeMarshaller))] Type type);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Object::Internal_TryFindObject", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_TryFindObject", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
internal static partial Object Internal_TryFindObject(ref Guid id, [MarshalUsing(typeof(SystemTypeMarshaller))] Type type);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Object::Internal_ChangeID", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_ChangeID", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
internal static partial void Internal_ChangeID(IntPtr obj, ref Guid id);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Object::Internal_GetUnmanagedInterface", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_GetUnmanagedInterface", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
internal static partial IntPtr Internal_GetUnmanagedInterface(IntPtr obj, [MarshalUsing(typeof(SystemTypeMarshaller))] Type type);
#endregion

View File

@@ -20,7 +20,7 @@
#if USE_MONO
namespace ProfilerInternal
namespace
{
#if COMPILE_WITH_PROFILER
Array<int32, InlinedAllocation<32>> ManagedEventsGPU;
@@ -37,74 +37,86 @@ namespace ProfilerInternal
ChunkedArray<Location, 256> ManagedSourceLocations;
#endif
#endif
}
void BeginEvent(MonoString* nameObj)
{
SCRIPTING_EXPORT("FlaxEngine.Profiler::BeginEvent")
DEFINE_INTERNAL_CALL(void) ProfilerInternal_BeginEvent(MonoString* nameObj)
{
#if COMPILE_WITH_PROFILER
const StringView name((const Char*)mono_string_chars(nameObj), mono_string_length(nameObj));
ProfilerCPU::BeginEvent(*name);
const StringView name((const Char*)mono_string_chars(nameObj), mono_string_length(nameObj));
ProfilerCPU::BeginEvent(*name);
#if TRACY_ENABLE
#if PROFILE_CPU_USE_TRANSIENT_DATA
tracy::ScopedZone::Begin(__LINE__, __FILE__, strlen( __FILE__ ), __FUNCTION__, strlen( __FUNCTION__ ), name.Get(), name.Length() );
tracy::ScopedZone::Begin(__LINE__, __FILE__, strlen( __FILE__ ), __FUNCTION__, strlen( __FUNCTION__ ), name.Get(), name.Length() );
#else
ScopeLock lock(ManagedSourceLocationsLocker);
tracy::SourceLocationData* srcLoc = nullptr;
for (auto e = ManagedSourceLocations.Begin(); e.IsNotEnd(); ++e)
{
if (name == e->Name)
{
srcLoc = &e->SrcLocation;
break;
}
}
if (!srcLoc)
{
auto& e = ManagedSourceLocations.AddOne();
e.Name = name;
e.NameAnsi = name.Get();
srcLoc = &e.SrcLocation;
srcLoc->name = e.NameAnsi.Get();
srcLoc->function = nullptr;
srcLoc->file = nullptr;
srcLoc->line = 0;
srcLoc->color = 0;
}
//static constexpr tracy::SourceLocationData tracySrcLoc{ nullptr, __FUNCTION__, __FILE__, (uint32_t)__LINE__, 0 };
tracy::ScopedZone::Begin(srcLoc);
#endif
#endif
#endif
}
void EndEvent()
ScopeLock lock(ManagedSourceLocationsLocker);
tracy::SourceLocationData* srcLoc = nullptr;
for (auto e = ManagedSourceLocations.Begin(); e.IsNotEnd(); ++e)
{
SCRIPTING_EXPORT("FlaxEngine.Profiler::EndEvent")
if (name == e->Name)
{
srcLoc = &e->SrcLocation;
break;
}
}
if (!srcLoc)
{
auto& e = ManagedSourceLocations.AddOne();
e.Name = name;
e.NameAnsi = name.Get();
srcLoc = &e.SrcLocation;
srcLoc->name = e.NameAnsi.Get();
srcLoc->function = nullptr;
srcLoc->file = nullptr;
srcLoc->line = 0;
srcLoc->color = 0;
}
//static constexpr tracy::SourceLocationData tracySrcLoc{ nullptr, __FUNCTION__, __FILE__, (uint32_t)__LINE__, 0 };
tracy::ScopedZone::Begin(srcLoc);
#endif
#endif
#endif
}
DEFINE_INTERNAL_CALL(void) ProfilerInternal_EndEvent()
{
#if COMPILE_WITH_PROFILER
#if TRACY_ENABLE
tracy::ScopedZone::End();
tracy::ScopedZone::End();
#endif
ProfilerCPU::EndEvent();
ProfilerCPU::EndEvent();
#endif
}
}
void BeginEventGPU(MonoString* nameObj)
{
SCRIPTING_EXPORT("FlaxEngine.Profiler::BeginEventGPU")
DEFINE_INTERNAL_CALL(void) ProfilerInternal_BeginEventGPU(MonoString* nameObj)
{
#if COMPILE_WITH_PROFILER
const auto index = ProfilerGPU::BeginEvent((const Char*)mono_string_chars(nameObj));
ManagedEventsGPU.Push(index);
const auto index = ProfilerGPU::BeginEvent((const Char*)mono_string_chars(nameObj));
ManagedEventsGPU.Push(index);
#endif
}
}
void EndEventGPU()
{
SCRIPTING_EXPORT("FlaxEngine.Profiler::EndEventGPU")
DEFINE_INTERNAL_CALL(void) ProfilerInternal_EndEventGPU()
{
#if COMPILE_WITH_PROFILER
const auto index = ManagedEventsGPU.Pop();
ProfilerGPU::EndEvent(index);
const auto index = ManagedEventsGPU.Pop();
ProfilerGPU::EndEvent(index);
#endif
}
}
DEFINE_INTERNAL_CALL(bool) ScriptingInternal_HasGameModulesLoaded()
{
return Scripting::HasGameModulesLoaded();
}
DEFINE_INTERNAL_CALL(bool) ScriptingInternal_IsTypeFromGameScripts(MonoReflectionType* type)
{
return Scripting::IsTypeFromGameScripts(Scripting::FindClass(MUtils::GetClass(type)));
}
DEFINE_INTERNAL_CALL(void) ScriptingInternal_FlushRemovedObjects()
{
ASSERT(IsInMainThread());
ObjectsRemovalService::Flush();
}
#endif
@@ -112,41 +124,18 @@ namespace ProfilerInternal
class ScriptingInternal
{
public:
#if USE_MONO
static bool HasGameModulesLoaded()
{
SCRIPTING_EXPORT("FlaxEngine.Scripting::HasGameModulesLoaded")
return Scripting::HasGameModulesLoaded();
}
static bool IsTypeFromGameScripts(MonoReflectionType* type)
{
SCRIPTING_EXPORT("FlaxEngine.Scripting::IsTypeFromGameScripts")
return Scripting::IsTypeFromGameScripts(Scripting::FindClass(MUtils::GetClass(type)));
}
static void FlushRemovedObjects()
{
SCRIPTING_EXPORT("FlaxEngine.Scripting::FlushRemovedObjects")
ASSERT(IsInMainThread());
ObjectsRemovalService::Flush();
}
#endif
static void InitRuntime()
{
#if USE_MONO
// Scripting API
ADD_INTERNAL_CALL("FlaxEngine.Scripting::HasGameModulesLoaded", &HasGameModulesLoaded);
ADD_INTERNAL_CALL("FlaxEngine.Scripting::IsTypeFromGameScripts", &IsTypeFromGameScripts);
ADD_INTERNAL_CALL("FlaxEngine.Scripting::FlushRemovedObjects", &FlushRemovedObjects);
ADD_INTERNAL_CALL("FlaxEngine.Scripting::HasGameModulesLoaded", &ScriptingInternal_HasGameModulesLoaded);
ADD_INTERNAL_CALL("FlaxEngine.Scripting::IsTypeFromGameScripts", &ScriptingInternal_IsTypeFromGameScripts);
ADD_INTERNAL_CALL("FlaxEngine.Scripting::FlushRemovedObjects", &ScriptingInternal_FlushRemovedObjects);
// Profiler API
ADD_INTERNAL_CALL("FlaxEngine.Profiler::BeginEvent", &ProfilerInternal::BeginEvent);
ADD_INTERNAL_CALL("FlaxEngine.Profiler::EndEvent", &ProfilerInternal::EndEvent);
ADD_INTERNAL_CALL("FlaxEngine.Profiler::BeginEventGPU", &ProfilerInternal::BeginEventGPU);
ADD_INTERNAL_CALL("FlaxEngine.Profiler::EndEventGPU", &ProfilerInternal::EndEventGPU);
#endif
ADD_INTERNAL_CALL("FlaxEngine.Profiler::BeginEvent", &ProfilerInternal_BeginEvent);
ADD_INTERNAL_CALL("FlaxEngine.Profiler::EndEvent", &ProfilerInternal_EndEvent);
ADD_INTERNAL_CALL("FlaxEngine.Profiler::BeginEventGPU", &ProfilerInternal_BeginEventGPU);
ADD_INTERNAL_CALL("FlaxEngine.Profiler::EndEventGPU", &ProfilerInternal_EndEventGPU);
}
};

View File

@@ -201,35 +201,30 @@ bool ScriptingService::Init()
void ScriptingService::Update()
{
PROFILE_CPU_NAMED("Scripting::Update");
INVOKE_EVENT(Update);
}
void ScriptingService::LateUpdate()
{
PROFILE_CPU_NAMED("Scripting::LateUpdate");
INVOKE_EVENT(LateUpdate);
}
void ScriptingService::FixedUpdate()
{
PROFILE_CPU_NAMED("Scripting::FixedUpdate");
INVOKE_EVENT(FixedUpdate);
}
void ScriptingService::Draw()
{
PROFILE_CPU_NAMED("Scripting::Draw");
INVOKE_EVENT(Draw);
}
void ScriptingService::BeforeExit()
{
PROFILE_CPU_NAMED("Scripting::BeforeExit");
INVOKE_EVENT(Exit);
}
@@ -927,7 +922,6 @@ ScriptingObject* Scripting::FindObject(const MObject* managedInstance)
void Scripting::OnManagedInstanceDeleted(ScriptingObject* obj)
{
SCRIPTING_EXPORT("FlaxEngine.Object::Internal_ManagedInstanceDeleted")
PROFILE_CPU();
ASSERT(obj);

View File

@@ -310,7 +310,7 @@ namespace FlaxEngine
/// Returns true if game scripts assembly has been loaded.
/// </summary>
/// <returns>True if game scripts assembly is loaded, otherwise false.</returns>
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Scripting::HasGameModulesLoaded", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "ScriptingInternal_HasGameModulesLoaded", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
[return: MarshalAs(UnmanagedType.U1)]
public static partial bool HasGameModulesLoaded();
@@ -318,14 +318,14 @@ namespace FlaxEngine
/// Returns true if given type is from one of the game scripts assemblies.
/// </summary>
/// <returns>True if the type is from game assembly, otherwise false.</returns>
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Scripting::IsTypeFromGameScripts", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "ScriptingInternal_IsTypeFromGameScripts", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
[return: MarshalAs(UnmanagedType.U1)]
public static partial bool IsTypeFromGameScripts([MarshalUsing(typeof(SystemTypeMarshaller))] Type type);
/// <summary>
/// Flushes the removed objects (disposed objects using Object.Destroy).
/// </summary>
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Scripting::FlushRemovedObjects", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "ScriptingInternal_FlushRemovedObjects", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
public static partial void FlushRemovedObjects();
}
@@ -341,26 +341,26 @@ namespace FlaxEngine
/// Begins profiling a piece of code with a custom label.
/// </summary>
/// <param name="name">The name of the event.</param>
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Profiler::BeginEvent", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "ProfilerInternal_BeginEvent", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
public static partial void BeginEvent(string name);
/// <summary>
/// Ends profiling an event.
/// </summary>
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Profiler::EndEvent", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "ProfilerInternal_EndEvent", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
public static partial void EndEvent();
/// <summary>
/// Begins GPU profiling a piece of code with a custom label.
/// </summary>
/// <param name="name">The name of the event.</param>
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Profiler::BeginEventGPU", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "ProfilerInternal_BeginEventGPU", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
public static partial void BeginEventGPU(string name);
/// <summary>
/// Ends GPU profiling an event.
/// </summary>
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Profiler::EndEventGPU", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "ProfilerInternal_EndEventGPU", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
public static partial void EndEventGPU();
}
}

View File

@@ -221,6 +221,16 @@ void ScriptingObject::ChangeID(const Guid& newId)
_type.GetType().Module->OnObjectIdChanged(this, prevId);
}
void ScriptingObject::SetManagedInstance(MonoObject* instance)
{
ASSERT(_gcHandle == 0);
#if USE_NETCORE
_gcHandle = (MGCHandle)instance;
#else
_gcHandle = MUtils::NewGCHandle(instance, false);
#endif
}
void ScriptingObject::OnManagedInstanceDeleted()
{
// Release the handle
@@ -444,6 +454,16 @@ ManagedScriptingObject::ManagedScriptingObject(const SpawnParams& params)
{
}
void ManagedScriptingObject::SetManagedInstance(MonoObject* instance)
{
ASSERT(_gcHandle == 0);
#if USE_NETCORE
_gcHandle = (MGCHandle)instance;
#else
_gcHandle = MUtils::NewGCHandleWeakref(instance, false);
#endif
}
void ManagedScriptingObject::OnManagedInstanceDeleted()
{
// Base
@@ -508,302 +528,274 @@ PersistentScriptingObject::PersistentScriptingObject(const SpawnParams& params)
{
}
#if !COMPILE_WITHOUT_CSHARP
DEFINE_INTERNAL_CALL(MonoObject*) ObjectInternal_Create1(MonoReflectionType* type)
{
// Peek class for that type (handle generic class cases)
if (!type)
DebugLog::ThrowArgumentNull("type");
MonoType* monoType = mono_reflection_type_get_type(type);
const int32 monoTypeType = mono_type_get_type(monoType);
if (monoTypeType == MONO_TYPE_GENERICINST)
{
LOG(Error, "Generic scripts are not supported.");
return nullptr;
}
MonoClass* typeClass = mono_type_get_class(monoType);
if (typeClass == nullptr)
{
LOG(Error, "Invalid type.");
return nullptr;
}
// Get the assembly with that class
auto module = ManagedBinaryModule::FindModule(typeClass);
if (module == nullptr)
{
LOG(Error, "Cannot find scripting assembly for type \'{0}.{1}\'.", String(mono_class_get_namespace(typeClass)), String(mono_class_get_name(typeClass)));
return nullptr;
}
// Try to find the scripting type for this class
int32 typeIndex;
if (!module->ClassToTypeIndex.TryGet(typeClass, typeIndex))
{
LOG(Error, "Cannot spawn objects of type \'{0}.{1}\'.", String(mono_class_get_namespace(typeClass)), String(mono_class_get_name(typeClass)));
return nullptr;
}
const ScriptingType& scriptingType = module->Types[typeIndex];
// Create unmanaged object
const ScriptingObjectSpawnParams params(Guid::New(), ScriptingTypeHandle(module, typeIndex));
ScriptingObject* obj = scriptingType.Script.Spawn(params);
if (obj == nullptr)
{
LOG(Error, "Failed to spawn object of type \'{0}.{1}\'.", String(mono_class_get_namespace(typeClass)), String(mono_class_get_name(typeClass)));
return nullptr;
}
// Set default name for actors
if (auto* actor = dynamic_cast<Actor*>(obj))
{
actor->SetName(String(mono_class_get_name(typeClass)));
}
// Create managed object
obj->CreateManaged();
MonoObject* managedInstance = obj->GetManagedInstance();
if (managedInstance == nullptr)
{
LOG(Error, "Cannot create managed instance for type \'{0}.{1}\'.", String(mono_class_get_namespace(typeClass)), String(mono_class_get_name(typeClass)));
Delete(obj);
}
return managedInstance;
}
DEFINE_INTERNAL_CALL(MonoObject*) ObjectInternal_Create2(MonoString* typeNameObj)
{
// Get typename
if (typeNameObj == nullptr)
DebugLog::ThrowArgumentNull("typeName");
const StringAsANSI<> typeNameData((const Char*)mono_string_chars(typeNameObj), (int32)mono_string_length(typeNameObj));
const StringAnsiView typeName(typeNameData.Get(), (int32)mono_string_length(typeNameObj));
// Try to find the scripting type for this typename
const ScriptingTypeHandle type = Scripting::FindScriptingType(typeName);
if (!type)
{
LOG(Error, "Cannot find scripting type for \'{0}\'.", String(typeName));
return nullptr;
}
// Create unmanaged object
const ScriptingObjectSpawnParams params(Guid::New(), type);
ScriptingObject* obj = type.GetType().Script.Spawn(params);
if (obj == nullptr)
{
LOG(Error, "Failed to spawn object of type \'{0}\'.", String(typeName));
return nullptr;
}
// Create managed object
obj->CreateManaged();
MonoObject* managedInstance = obj->GetManagedInstance();
if (managedInstance == nullptr)
{
LOG(Error, "Cannot create managed instance for type \'{0}\'.", String(typeName));
Delete(obj);
}
return managedInstance;
}
DEFINE_INTERNAL_CALL(void) ObjectInternal_ManagedInstanceCreated(MonoObject* managedInstance)
{
MonoClass* typeClass = mono_object_get_class(managedInstance);
// Get the assembly with that class
auto module = ManagedBinaryModule::FindModule(typeClass);
if (module == nullptr)
{
LOG(Error, "Cannot find scripting assembly for type \'{0}.{1}\'.", String(mono_class_get_namespace(typeClass)), String(mono_class_get_name(typeClass)));
return;
}
// Try to find the scripting type for this class
int32 typeIndex;
if (!module->ClassToTypeIndex.TryGet(typeClass, typeIndex))
{
LOG(Error, "Cannot spawn objects of type \'{0}.{1}\'.", String(mono_class_get_namespace(typeClass)), String(mono_class_get_name(typeClass)));
return;
}
const ScriptingType& scriptingType = module->Types[typeIndex];
// Create unmanaged object
const ScriptingObjectSpawnParams params(Guid::New(), ScriptingTypeHandle(module, typeIndex));
ScriptingObject* obj = scriptingType.Script.Spawn(params);
if (obj == nullptr)
{
LOG(Error, "Failed to spawn object of type \'{0}.{1}\'.", String(mono_class_get_namespace(typeClass)), String(mono_class_get_name(typeClass)));
return;
}
// Set default name for actors
if (auto* actor = dynamic_cast<Actor*>(obj))
{
actor->SetName(String(mono_class_get_name(typeClass)));
}
// Link created managed instance to the unmanaged object
obj->SetManagedInstance(managedInstance);
MClass* monoClass = obj->GetClass();
// Set handle to unmanaged object
const MField* monoUnmanagedPtrField = monoClass->GetField(ScriptingObject_unmanagedPtr);
if (monoUnmanagedPtrField)
{
const void* value = obj;
monoUnmanagedPtrField->SetValue(managedInstance, &value);
}
// Set object id
const MField* monoIdField = monoClass->GetField(ScriptingObject_id);
if (monoIdField)
{
const Guid id = obj->GetID();
monoIdField->SetValue(managedInstance, (void*)&id);
}
// Register object
if (!obj->IsRegistered())
obj->RegisterObject();
}
DEFINE_INTERNAL_CALL(void) ObjectInternal_ManagedInstanceDeleted(ScriptingObject* obj)
{
Scripting::OnManagedInstanceDeleted(obj);
}
DEFINE_INTERNAL_CALL(void) ObjectInternal_Destroy(ScriptingObject* obj, float timeLeft)
{
// Use scaled game time for removing actors/scripts by the user (maybe expose it to the api?)
const bool useGameTime = timeLeft > ZeroTolerance;
if (obj)
obj->DeleteObject(timeLeft, useGameTime);
}
DEFINE_INTERNAL_CALL(MonoString*) ObjectInternal_GetTypeName(ScriptingObject* obj)
{
INTERNAL_CALL_CHECK_RETURN(obj, nullptr);
return MUtils::ToString(obj->GetType().Fullname);
}
DEFINE_INTERNAL_CALL(MonoObject*) ObjectInternal_FindObject(Guid* id, MonoReflectionType* type)
{
if (!id->IsValid())
return nullptr;
auto klass = MUtils::GetClass(type);
ScriptingObject* obj = Scripting::TryFindObject(*id);
if (!obj)
{
if (!klass || klass == ScriptingObject::GetStaticClass()->GetNative() || mono_class_is_subclass_of(klass, Asset::GetStaticClass()->GetNative(), false) != 0)
{
obj = Content::LoadAsync<Asset>(*id);
}
}
if (obj)
{
if (klass && !obj->Is(klass))
{
LOG(Warning, "Found scripting object with ID={0} of type {1} that doesn't match type {2}.", *id, String(obj->GetType().Fullname), String(MUtils::GetClassFullname(klass)));
return nullptr;
}
return obj->GetOrCreateManagedInstance();
}
if (klass)
LOG(Warning, "Unable to find scripting object with ID={0}. Required type {1}.", *id, String(MUtils::GetClassFullname(klass)));
else
LOG(Warning, "Unable to find scripting object with ID={0}", *id);
return nullptr;
}
DEFINE_INTERNAL_CALL(MonoObject*) ObjectInternal_TryFindObject(Guid* id, MonoReflectionType* type)
{
ScriptingObject* obj = Scripting::TryFindObject(*id);
if (obj && !obj->Is(MUtils::GetClass(type)))
obj = nullptr;
return obj ? obj->GetOrCreateManagedInstance() : nullptr;
}
DEFINE_INTERNAL_CALL(void) ObjectInternal_ChangeID(ScriptingObject* obj, Guid* id)
{
INTERNAL_CALL_CHECK(obj);
obj->ChangeID(*id);
}
DEFINE_INTERNAL_CALL(void*) ObjectInternal_GetUnmanagedInterface(ScriptingObject* obj, MonoReflectionType* type)
{
if (obj && type)
{
auto typeClass = MUtils::GetClass(type);
const ScriptingTypeHandle interfaceType = ManagedBinaryModule::FindType(typeClass);
if (interfaceType)
{
return ScriptingObject::ToInterface(obj, interfaceType);
}
}
return nullptr;
}
DEFINE_INTERNAL_CALL(MonoObject*) ObjectInternal_FromUnmanagedPtr(ScriptingObject* obj)
{
MonoObject* result = nullptr;
if (obj)
result = obj->GetOrCreateManagedInstance();
return result;
}
#endif
class ScriptingObjectInternal
{
public:
#if !COMPILE_WITHOUT_CSHARP
static MonoObject* Create1(MonoReflectionType* type)
{
SCRIPTING_EXPORT("FlaxEngine.Object::Internal_Create1")
// Peek class for that type (handle generic class cases)
if (!type)
DebugLog::ThrowArgumentNull("type");
MonoType* monoType = mono_reflection_type_get_type(type);
const int32 monoTypeType = mono_type_get_type(monoType);
if (monoTypeType == MONO_TYPE_GENERICINST)
{
LOG(Error, "Generic scripts are not supported.");
return nullptr;
}
MonoClass* typeClass = mono_type_get_class(monoType);
if (typeClass == nullptr)
{
LOG(Error, "Invalid type.");
return nullptr;
}
// Get the assembly with that class
auto module = ManagedBinaryModule::FindModule(typeClass);
if (module == nullptr)
{
LOG(Error, "Cannot find scripting assembly for type \'{0}.{1}\'.", String(mono_class_get_namespace(typeClass)), String(mono_class_get_name(typeClass)));
return nullptr;
}
// Try to find the scripting type for this class
int32 typeIndex;
if (!module->ClassToTypeIndex.TryGet(typeClass, typeIndex))
{
LOG(Error, "Cannot spawn objects of type \'{0}.{1}\'.", String(mono_class_get_namespace(typeClass)), String(mono_class_get_name(typeClass)));
return nullptr;
}
const ScriptingType& scriptingType = module->Types[typeIndex];
// Create unmanaged object
const ScriptingObjectSpawnParams params(Guid::New(), ScriptingTypeHandle(module, typeIndex));
ScriptingObject* obj = scriptingType.Script.Spawn(params);
if (obj == nullptr)
{
LOG(Error, "Failed to spawn object of type \'{0}.{1}\'.", String(mono_class_get_namespace(typeClass)), String(mono_class_get_name(typeClass)));
return nullptr;
}
// Set default name for actors
if (auto* actor = dynamic_cast<Actor*>(obj))
{
actor->SetName(String(mono_class_get_name(typeClass)));
}
// Create managed object
obj->CreateManaged();
MonoObject* managedInstance = obj->GetManagedInstance();
if (managedInstance == nullptr)
{
LOG(Error, "Cannot create managed instance for type \'{0}.{1}\'.", String(mono_class_get_namespace(typeClass)), String(mono_class_get_name(typeClass)));
Delete(obj);
}
return managedInstance;
}
static MonoObject* Create2(MonoString* typeNameObj)
{
SCRIPTING_EXPORT("FlaxEngine.Object::Internal_Create2")
// Get typename
if (typeNameObj == nullptr)
DebugLog::ThrowArgumentNull("typeName");
const StringAsANSI<> typeNameData((const Char*)mono_string_chars(typeNameObj), (int32)mono_string_length(typeNameObj));
const StringAnsiView typeName(typeNameData.Get(), (int32)mono_string_length(typeNameObj));
// Try to find the scripting type for this typename
const ScriptingTypeHandle type = Scripting::FindScriptingType(typeName);
if (!type)
{
LOG(Error, "Cannot find scripting type for \'{0}\'.", String(typeName));
return nullptr;
}
// Create unmanaged object
const ScriptingObjectSpawnParams params(Guid::New(), type);
ScriptingObject* obj = type.GetType().Script.Spawn(params);
if (obj == nullptr)
{
LOG(Error, "Failed to spawn object of type \'{0}\'.", String(typeName));
return nullptr;
}
// Create managed object
obj->CreateManaged();
MonoObject* managedInstance = obj->GetManagedInstance();
if (managedInstance == nullptr)
{
LOG(Error, "Cannot create managed instance for type \'{0}\'.", String(typeName));
Delete(obj);
}
return managedInstance;
}
static void ManagedInstanceCreated(MonoObject* managedInstance)
{
SCRIPTING_EXPORT("FlaxEngine.Object::Internal_ManagedInstanceCreated")
MonoClass* typeClass = mono_object_get_class(managedInstance);
// Get the assembly with that class
auto module = ManagedBinaryModule::FindModule(typeClass);
if (module == nullptr)
{
LOG(Error, "Cannot find scripting assembly for type \'{0}.{1}\'.", String(mono_class_get_namespace(typeClass)), String(mono_class_get_name(typeClass)));
return;
}
// Try to find the scripting type for this class
int32 typeIndex;
if (!module->ClassToTypeIndex.TryGet(typeClass, typeIndex))
{
LOG(Error, "Cannot spawn objects of type \'{0}.{1}\'.", String(mono_class_get_namespace(typeClass)), String(mono_class_get_name(typeClass)));
return;
}
const ScriptingType& scriptingType = module->Types[typeIndex];
// Create unmanaged object
const ScriptingObjectSpawnParams params(Guid::New(), ScriptingTypeHandle(module, typeIndex));
ScriptingObject* obj = scriptingType.Script.Spawn(params);
if (obj == nullptr)
{
LOG(Error, "Failed to spawn object of type \'{0}.{1}\'.", String(mono_class_get_namespace(typeClass)), String(mono_class_get_name(typeClass)));
return;
}
// Set default name for actors
if (auto* actor = dynamic_cast<Actor*>(obj))
{
actor->SetName(String(mono_class_get_name(typeClass)));
}
// Link created managed instance to the unmanaged object
if (auto* managedScriptingObject = dynamic_cast<ManagedScriptingObject*>(obj))
{
// Managed
#if USE_NETCORE
managedScriptingObject->_gcHandle = (MGCHandle)managedInstance;
#else
managedScriptingObject->_gcHandle = MUtils::NewGCHandleWeakref(managedInstance, false);
#endif
}
else
{
// Persistent
#if USE_NETCORE
obj->_gcHandle = (MGCHandle)managedInstance;
#else
obj->_gcHandle = MUtils::NewGCHandle(managedInstance, false);
#endif
}
MClass* monoClass = obj->GetClass();
// Set handle to unmanaged object
const MField* monoUnmanagedPtrField = monoClass->GetField(ScriptingObject_unmanagedPtr);
if (monoUnmanagedPtrField)
{
const void* value = obj;
monoUnmanagedPtrField->SetValue(managedInstance, &value);
}
// Set object id
const MField* monoIdField = monoClass->GetField(ScriptingObject_id);
if (monoIdField)
{
monoIdField->SetValue(managedInstance, (void*)&obj->_id);
}
// Register object
if (!obj->IsRegistered())
obj->RegisterObject();
}
static void Destroy(ScriptingObject* obj, float timeLeft)
{
SCRIPTING_EXPORT("FlaxEngine.Object::Internal_Destroy")
// Use scaled game time for removing actors/scripts by the user (maybe expose it to the api?)
const bool useGameTime = timeLeft > ZeroTolerance;
if (obj)
obj->DeleteObject(timeLeft, useGameTime);
}
static MonoString* GetTypeName(ScriptingObject* obj)
{
SCRIPTING_EXPORT("FlaxEngine.Object::Internal_GetTypeName")
INTERNAL_CALL_CHECK_RETURN(obj, nullptr);
return MUtils::ToString(obj->GetType().Fullname);
}
static MonoObject* FindObject(Guid* id, MonoReflectionType* type)
{
SCRIPTING_EXPORT("FlaxEngine.Object::Internal_FindObject")
if (!id->IsValid())
return nullptr;
auto klass = MUtils::GetClass(type);
ScriptingObject* obj = Scripting::TryFindObject(*id);
if (!obj)
{
if (!klass || klass == ScriptingObject::GetStaticClass()->GetNative() || mono_class_is_subclass_of(klass, Asset::GetStaticClass()->GetNative(), false) != 0)
{
obj = Content::LoadAsync<Asset>(*id);
}
}
if (obj)
{
if (klass && !obj->Is(klass))
{
LOG(Warning, "Found scripting object with ID={0} of type {1} that doesn't match type {2}.", *id, String(obj->GetType().Fullname), String(MUtils::GetClassFullname(klass)));
return nullptr;
}
return obj->GetOrCreateManagedInstance();
}
if (klass)
LOG(Warning, "Unable to find scripting object with ID={0}. Required type {1}.", *id, String(MUtils::GetClassFullname(klass)));
else
LOG(Warning, "Unable to find scripting object with ID={0}", *id);
return nullptr;
}
static MonoObject* TryFindObject(Guid* id, MonoReflectionType* type)
{
SCRIPTING_EXPORT("FlaxEngine.Object::Internal_TryFindObject")
ScriptingObject* obj = Scripting::TryFindObject(*id);
if (obj && !obj->Is(MUtils::GetClass(type)))
obj = nullptr;
return obj ? obj->GetOrCreateManagedInstance() : nullptr;
}
static void ChangeID(ScriptingObject* obj, Guid* id)
{
SCRIPTING_EXPORT("FlaxEngine.Object::Internal_ChangeID")
INTERNAL_CALL_CHECK(obj);
obj->ChangeID(*id);
}
static void* GetUnmanagedInterface(ScriptingObject* obj, MonoReflectionType* type)
{
SCRIPTING_EXPORT("FlaxEngine.Object::Internal_GetUnmanagedInterface")
if (obj && type)
{
auto typeClass = MUtils::GetClass(type);
const ScriptingTypeHandle interfaceType = ManagedBinaryModule::FindType(typeClass);
if (interfaceType)
{
return ScriptingObject::ToInterface(obj, interfaceType);
}
}
return nullptr;
}
static MonoObject* FromUnmanagedPtr(ScriptingObject* obj)
{
SCRIPTING_EXPORT("FlaxEngine.Object::FromUnmanagedPtr")
MonoObject* result = nullptr;
if (obj)
result = obj->GetOrCreateManagedInstance();
return result;
}
static void InitRuntime()
{
ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_Create1", &Create1);
ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_Create2", &Create2);
ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_ManagedInstanceCreated", &ManagedInstanceCreated);
ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_ManagedInstanceDeleted", &Scripting::OnManagedInstanceDeleted);
ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_Destroy", &Destroy);
ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_GetTypeName", &GetTypeName);
ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_FindObject", &FindObject);
ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_TryFindObject", &TryFindObject);
ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_ChangeID", &ChangeID);
ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_GetUnmanagedInterface", &GetUnmanagedInterface);
ADD_INTERNAL_CALL("FlaxEngine.Object::FromUnmanagedPtr", &FromUnmanagedPtr);
ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_Create1", &ObjectInternal_Create1);
ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_Create2", &ObjectInternal_Create2);
ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_ManagedInstanceCreated", &ObjectInternal_ManagedInstanceCreated);
ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_ManagedInstanceDeleted", &ObjectInternal_ManagedInstanceDeleted);
ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_Destroy", &ObjectInternal_Destroy);
ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_GetTypeName", &ObjectInternal_GetTypeName);
ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_FindObject", &ObjectInternal_FindObject);
ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_TryFindObject", &ObjectInternal_TryFindObject);
ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_ChangeID", &ObjectInternal_ChangeID);
ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_GetUnmanagedInterface", &ObjectInternal_GetUnmanagedInterface);
ADD_INTERNAL_CALL("FlaxEngine.Object::FromUnmanagedPtr", &ObjectInternal_FromUnmanagedPtr);
}
#else
static void InitRuntime()
{
}
#endif
static ScriptingObject* Spawn(const ScriptingObjectSpawnParams& params)
{
return New<ScriptingObject>(params);

View File

@@ -14,19 +14,17 @@ API_CLASS(InBuild) class FLAXENGINE_API ScriptingObject : public Object
{
friend class Scripting;
friend class BinaryModule;
DECLARE_SCRIPTING_TYPE_NO_SPAWN(ScriptingObject);
public:
DECLARE_SCRIPTING_TYPE_NO_SPAWN(ScriptingObject);
public:
typedef ScriptingObjectSpawnParams SpawnParams;
protected:
MGCHandle _gcHandle;
ScriptingTypeHandle _type;
Guid _id;
public:
/// <summary>
/// Initializes a new instance of the <see cref="ScriptingObject"/> class.
/// </summary>
@@ -39,14 +37,15 @@ public:
virtual ~ScriptingObject();
public:
// Spawns a new objects of the given type.
static ScriptingObject* NewObject(const ScriptingTypeHandle& typeHandle);
template<typename T>
static T* NewObject()
{
return (T*)NewObject(T::TypeInitializer);
}
template<typename T>
static T* NewObject(const ScriptingTypeHandle& typeHandle)
{
@@ -60,14 +59,12 @@ public:
}
public:
/// <summary>
/// Event fired when object gets deleted.
/// </summary>
Delegate<ScriptingObject*> Deleted;
public:
/// <summary>
/// Gets the managed instance object.
/// </summary>
@@ -116,15 +113,17 @@ public:
MClass* GetClass() const;
public:
// Tries to cast native interface object to scripting object instance. Returns null if fails.
static ScriptingObject* FromInterface(void* interfaceObj, const ScriptingTypeHandle& interfaceType);
template<typename T>
static ScriptingObject* FromInterface(T* interfaceObj)
{
return FromInterface(interfaceObj, T::TypeInitializer);
}
static void* ToInterface(ScriptingObject* obj, const ScriptingTypeHandle& interfaceType);
template<typename T>
static T* ToInterface(ScriptingObject* obj)
{
@@ -184,7 +183,6 @@ public:
}
public:
/// <summary>
/// Changes the object id (both managed and unmanaged). Warning! Use with caution as object ID is what it identifies it and change might cause issues.
/// </summary>
@@ -192,7 +190,7 @@ public:
virtual void ChangeID(const Guid& newId);
public:
virtual void SetManagedInstance(MonoObject* instance);
virtual void OnManagedInstanceDeleted();
virtual void OnScriptingDispose();
@@ -200,7 +198,6 @@ public:
virtual void DestroyManaged();
public:
/// <summary>
/// Determines whether this object is registered or not (can be found by the queries and used in a game).
/// </summary>
@@ -220,7 +217,6 @@ public:
void UnregisterObject();
protected:
#if USE_MONO
/// <summary>
/// Create a new managed object.
@@ -229,7 +225,6 @@ protected:
#endif
public:
// [Object]
void OnDeleteObject() override;
String ToString() const override;
@@ -243,7 +238,6 @@ public:
API_CLASS(InBuild) class FLAXENGINE_API ManagedScriptingObject : public ScriptingObject
{
public:
/// <summary>
/// Initializes a new instance of the <see cref="ManagedScriptingObject"/> class.
/// </summary>
@@ -251,8 +245,8 @@ public:
explicit ManagedScriptingObject(const SpawnParams& params);
public:
// [ScriptingObject]
void SetManagedInstance(MonoObject* instance) override;
void OnManagedInstanceDeleted() override;
void OnScriptingDispose() override;
bool CreateManaged() override;