// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved. #include "DebugLog.h" #include "Engine/Core/Log.h" #include "Engine/Scripting/InternalCalls.h" #include "Engine/Scripting/ScriptingObject.h" #include "Engine/Scripting/MException.h" #include "Engine/Scripting/Scripting.h" #include "Engine/Scripting/ScriptingType.h" #include "Engine/Scripting/BinaryModule.h" #include "Engine/Scripting/MainThreadManagedInvokeAction.h" #include "Engine/Scripting/ManagedCLR/MDomain.h" #include "Engine/Scripting/ManagedCLR/MAssembly.h" #include "Engine/Scripting/ManagedCLR/MClass.h" #include "FlaxEngine.Gen.h" #include #include namespace DebugLogHandlerInternal { void LogWrite(LogType level, MonoString* msgObj) { StringView msg; MUtils::ToString(msgObj, msg); Log::Logger::Write(level, msg); } void Log(LogType level, MonoString* msgObj, ScriptingObject* obj, MonoString* stackTrace) { if (msgObj == nullptr) return; // Get info StringView msg; MUtils::ToString(msgObj, msg); //const String objName = obj ? obj->ToString() : String::Empty; // Send event // TODO: maybe option for build to threat warnings and errors as fatal errors? //const String logMessage = String::Format(TEXT("Debug:{1} {2}"), objName, *msg); Log::Logger::Write(level, msg); } void LogException(MonoException* exception, ScriptingObject* obj) { if (exception == nullptr) return; // Get info MException ex(exception); String objName = obj ? obj->ToString() : String::Empty; // Print exception including inner exceptions // TODO: maybe option for build to threat warnings and errors as fatal errors? ex.Log(LogType::Warning, objName.GetText()); } } namespace FlaxLogWriterInternal { void WriteStringToLog(MonoString* msg) { if (msg == nullptr) return; auto msgStr = (Char*)mono_string_to_utf16(msg); LOG_STR(Info, msgStr); } } void DebugLog::RegisterInternalCalls() { // DebugLogHandler ADD_INTERNAL_CALL("FlaxEngine.DebugLogHandler::Internal_LogWrite", &DebugLogHandlerInternal::LogWrite); ADD_INTERNAL_CALL("FlaxEngine.DebugLogHandler::Internal_Log", &DebugLogHandlerInternal::Log); ADD_INTERNAL_CALL("FlaxEngine.DebugLogHandler::Internal_LogException", &DebugLogHandlerInternal::LogException); // FlaxLogWriter ADD_INTERNAL_CALL("FlaxEngine.FlaxLogWriter::Internal_WriteStringToLog", &FlaxLogWriterInternal::WriteStringToLog); } namespace DebugLogImpl { MMethod* Internal_SendLog = nullptr; MMethod* Internal_SendLogException = nullptr; MMethod* Internal_GetStackTrace = nullptr; CriticalSection Locker; } using namespace DebugLogImpl; void ClearMethods(MAssembly*) { Internal_SendLog = nullptr; Internal_SendLogException = nullptr; Internal_GetStackTrace = nullptr; } bool CacheMethods() { if (Internal_SendLog && Internal_SendLogException && Internal_GetStackTrace) return false; ScopeLock lock(Locker); if (Internal_SendLog && Internal_SendLogException && Internal_GetStackTrace) return false; auto engine = GetBinaryModuleFlaxEngine()->Assembly; if (engine == nullptr || !engine->IsLoaded()) return true; auto debugLogHandlerClass = engine->GetClass("FlaxEngine.DebugLogHandler"); if (!debugLogHandlerClass) return false; Internal_SendLog = debugLogHandlerClass->GetMethod("Internal_SendLog", 2); if (!Internal_SendLog) return false; Internal_SendLogException = debugLogHandlerClass->GetMethod("Internal_SendLogException", 1); if (!Internal_SendLogException) return false; Internal_GetStackTrace = debugLogHandlerClass->GetMethod("Internal_GetStackTrace"); if (!Internal_GetStackTrace) return false; engine->Unloading.Bind(); return false; } void DebugLog::Log(LogType type, const String& message) { if (CacheMethods()) return; auto scriptsDomain = Scripting::GetScriptsDomain(); MainThreadManagedInvokeAction::ParamsBuilder params; params.AddParam(type); params.AddParam(message, scriptsDomain->GetNative()); MainThreadManagedInvokeAction::Invoke(Internal_SendLog, params); } void DebugLog::LogException(MonoObject* exceptionObject) { if (exceptionObject == nullptr) return; if (CacheMethods()) return; MainThreadManagedInvokeAction::ParamsBuilder params; params.AddParam(exceptionObject); MainThreadManagedInvokeAction::Invoke(Internal_SendLogException, params); } String DebugLog::GetStackTrace() { String result; if (!CacheMethods()) { auto stackTraceObj = Internal_GetStackTrace->Invoke(nullptr, nullptr, nullptr); MUtils::ToString((MonoString*)stackTraceObj, result); } return result; } void DebugLog::ThrowException(const char* msg) { // Throw exception to the C# world auto ex = mono_exception_from_name_msg(mono_get_corlib(), "System", "Exception", msg); mono_raise_exception(ex); } void DebugLog::ThrowNullReference() { //LOG(Warning, "Invalid null reference."); //LOG_STR(Warning, DebugLog::GetStackTrace()); // Throw exception to the C# world auto ex = mono_get_exception_null_reference(); mono_raise_exception(ex); } void DebugLog::ThrowArgument(const char* arg, const char* msg) { // Throw exception to the C# world auto ex = mono_get_exception_argument(arg, msg); mono_raise_exception(ex); } void DebugLog::ThrowArgumentNull(const char* arg) { // Throw exception to the C# world auto ex = mono_get_exception_argument_null(arg); mono_raise_exception(ex); } void DebugLog::ThrowArgumentOutOfRange(const char* arg) { // Throw exception to the C# world auto ex = mono_get_exception_argument_out_of_range(arg); mono_raise_exception(ex); } void DebugLog::ThrowNotSupported(const char* msg) { // Throw exception to the C# world auto ex = mono_get_exception_not_supported(msg); mono_raise_exception(ex); }