2023-01-05 13:25:55 +01:00
/**************************************************************************/
/* gdscript_cache.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
2020-06-10 23:15:09 +02:00
# include "gdscript_cache.h"
# include "gdscript.h"
# include "gdscript_analyzer.h"
2022-11-08 12:51:20 +01:00
# include "gdscript_compiler.h"
2020-06-10 23:15:09 +02:00
# include "gdscript_parser.h"
2023-06-13 16:56:21 +02:00
# include "core/io/file_access.h"
# include "core/templates/vector.h"
2022-10-09 18:41:28 +02:00
# include "scene/resources/packed_scene.h"
2020-06-10 23:15:09 +02:00
bool GDScriptParserRef : : is_valid ( ) const {
return parser ! = nullptr ;
}
GDScriptParserRef : : Status GDScriptParserRef : : get_status ( ) const {
return status ;
}
GDScriptParser * GDScriptParserRef : : get_parser ( ) const {
return parser ;
}
2022-12-11 03:57:35 +01:00
GDScriptAnalyzer * GDScriptParserRef : : get_analyzer ( ) {
if ( analyzer = = nullptr ) {
analyzer = memnew ( GDScriptAnalyzer ( parser ) ) ;
}
return analyzer ;
}
2020-06-10 23:15:09 +02:00
Error GDScriptParserRef : : raise_status ( Status p_new_status ) {
2023-09-09 17:40:07 +02:00
ERR_FAIL_NULL_V ( parser , ERR_INVALID_DATA ) ;
2020-06-10 23:15:09 +02:00
2021-08-14 17:56:09 +02:00
if ( result ! = OK ) {
return result ;
}
2020-06-10 23:15:09 +02:00
while ( p_new_status > status ) {
switch ( status ) {
2024-01-22 15:31:55 +01:00
case EMPTY : {
2020-06-10 23:15:09 +02:00
status = PARSED ;
2024-01-22 15:31:55 +01:00
String remapped_path = ResourceLoader : : path_remap ( path ) ;
if ( remapped_path . get_extension ( ) . to_lower ( ) = = " gdc " ) {
result = parser - > parse_binary ( GDScriptCache : : get_binary_tokens ( remapped_path ) , path ) ;
} else {
result = parser - > parse ( GDScriptCache : : get_source_code ( remapped_path ) , path , false ) ;
}
} break ;
2020-06-10 23:15:09 +02:00
case PARSED : {
status = INHERITANCE_SOLVED ;
2022-12-11 03:57:35 +01:00
Error inheritance_result = get_analyzer ( ) - > resolve_inheritance ( ) ;
2020-06-10 23:15:09 +02:00
if ( result = = OK ) {
result = inheritance_result ;
}
} break ;
case INHERITANCE_SOLVED : {
status = INTERFACE_SOLVED ;
2022-12-11 03:57:35 +01:00
Error interface_result = get_analyzer ( ) - > resolve_interface ( ) ;
2020-06-10 23:15:09 +02:00
if ( result = = OK ) {
result = interface_result ;
}
} break ;
case INTERFACE_SOLVED : {
status = FULLY_SOLVED ;
2022-12-11 03:57:35 +01:00
Error body_result = get_analyzer ( ) - > resolve_body ( ) ;
2020-06-10 23:15:09 +02:00
if ( result = = OK ) {
result = body_result ;
}
} break ;
case FULLY_SOLVED : {
return result ;
}
}
2020-08-18 02:30:39 +02:00
if ( result ! = OK ) {
return result ;
}
2020-06-10 23:15:09 +02:00
}
return result ;
}
2022-10-09 18:41:28 +02:00
void GDScriptParserRef : : clear ( ) {
if ( cleared ) {
return ;
}
cleared = true ;
2020-06-10 23:15:09 +02:00
if ( parser ! = nullptr ) {
memdelete ( parser ) ;
}
2022-10-09 18:41:28 +02:00
2020-06-10 23:15:09 +02:00
if ( analyzer ! = nullptr ) {
memdelete ( analyzer ) ;
}
2022-10-09 18:41:28 +02:00
}
GDScriptParserRef : : ~ GDScriptParserRef ( ) {
clear ( ) ;
MutexLock lock ( GDScriptCache : : singleton - > mutex ) ;
2020-06-10 23:15:09 +02:00
GDScriptCache : : singleton - > parser_map . erase ( path ) ;
}
GDScriptCache * GDScriptCache : : singleton = nullptr ;
2022-10-09 18:41:28 +02:00
void GDScriptCache : : move_script ( const String & p_from , const String & p_to ) {
2022-11-20 21:21:57 +01:00
if ( singleton = = nullptr | | p_from = = p_to ) {
2022-10-09 18:41:28 +02:00
return ;
}
MutexLock lock ( singleton - > mutex ) ;
2022-12-10 18:48:07 +01:00
if ( singleton - > cleared ) {
return ;
}
2022-10-09 18:41:28 +02:00
for ( KeyValue < String , HashSet < String > > & E : singleton - > packed_scene_dependencies ) {
if ( E . value . has ( p_from ) ) {
E . value . insert ( p_to ) ;
E . value . erase ( p_from ) ;
}
}
if ( singleton - > parser_map . has ( p_from ) & & ! p_from . is_empty ( ) ) {
singleton - > parser_map [ p_to ] = singleton - > parser_map [ p_from ] ;
}
singleton - > parser_map . erase ( p_from ) ;
if ( singleton - > shallow_gdscript_cache . has ( p_from ) & & ! p_from . is_empty ( ) ) {
singleton - > shallow_gdscript_cache [ p_to ] = singleton - > shallow_gdscript_cache [ p_from ] ;
}
singleton - > shallow_gdscript_cache . erase ( p_from ) ;
if ( singleton - > full_gdscript_cache . has ( p_from ) & & ! p_from . is_empty ( ) ) {
singleton - > full_gdscript_cache [ p_to ] = singleton - > full_gdscript_cache [ p_from ] ;
}
singleton - > full_gdscript_cache . erase ( p_from ) ;
}
2020-06-10 23:15:09 +02:00
void GDScriptCache : : remove_script ( const String & p_path ) {
2022-10-09 18:41:28 +02:00
if ( singleton = = nullptr ) {
return ;
}
MutexLock lock ( singleton - > mutex ) ;
2022-12-10 18:48:07 +01:00
if ( singleton - > cleared ) {
return ;
}
2022-10-09 18:41:28 +02:00
for ( KeyValue < String , HashSet < String > > & E : singleton - > packed_scene_dependencies ) {
if ( ! E . value . has ( p_path ) ) {
continue ;
}
E . value . erase ( p_path ) ;
}
GDScriptCache : : clear_unreferenced_packed_scenes ( ) ;
if ( singleton - > parser_map . has ( p_path ) ) {
singleton - > parser_map [ p_path ] - > clear ( ) ;
singleton - > parser_map . erase ( p_path ) ;
}
singleton - > dependencies . erase ( p_path ) ;
2020-06-10 23:15:09 +02:00
singleton - > shallow_gdscript_cache . erase ( p_path ) ;
singleton - > full_gdscript_cache . erase ( p_path ) ;
}
2020-07-16 03:02:44 +02:00
Ref < GDScriptParserRef > GDScriptCache : : get_parser ( const String & p_path , GDScriptParserRef : : Status p_status , Error & r_error , const String & p_owner ) {
2022-10-09 18:41:28 +02:00
MutexLock lock ( singleton - > mutex ) ;
2020-06-10 23:15:09 +02:00
Ref < GDScriptParserRef > ref ;
2021-12-09 10:42:46 +01:00
if ( ! p_owner . is_empty ( ) ) {
2020-07-16 03:02:44 +02:00
singleton - > dependencies [ p_owner ] . insert ( p_path ) ;
}
2020-06-10 23:15:09 +02:00
if ( singleton - > parser_map . has ( p_path ) ) {
2020-08-23 16:19:35 +02:00
ref = Ref < GDScriptParserRef > ( singleton - > parser_map [ p_path ] ) ;
2021-11-01 08:35:05 +01:00
if ( ref . is_null ( ) ) {
r_error = ERR_INVALID_DATA ;
return ref ;
}
2020-06-10 23:15:09 +02:00
} else {
2024-01-22 15:31:55 +01:00
String remapped_path = ResourceLoader : : path_remap ( p_path ) ;
if ( ! FileAccess : : exists ( remapped_path ) ) {
2020-08-18 02:30:39 +02:00
r_error = ERR_FILE_NOT_FOUND ;
return ref ;
}
2020-06-10 23:15:09 +02:00
GDScriptParser * parser = memnew ( GDScriptParser ) ;
2021-06-18 00:03:09 +02:00
ref . instantiate ( ) ;
2020-06-10 23:15:09 +02:00
ref - > parser = parser ;
ref - > path = p_path ;
2020-08-23 16:19:35 +02:00
singleton - > parser_map [ p_path ] = ref . ptr ( ) ;
2020-06-10 23:15:09 +02:00
}
r_error = ref - > raise_status ( p_status ) ;
return ref ;
}
String GDScriptCache : : get_source_code ( const String & p_path ) {
Vector < uint8_t > source_file ;
Error err ;
2022-03-23 10:08:58 +01:00
Ref < FileAccess > f = FileAccess : : open ( p_path , FileAccess : : READ , & err ) ;
2022-09-11 09:56:49 +02:00
ERR_FAIL_COND_V ( err , " " ) ;
2020-06-10 23:15:09 +02:00
2021-05-25 08:58:49 +02:00
uint64_t len = f - > get_length ( ) ;
2020-06-10 23:15:09 +02:00
source_file . resize ( len + 1 ) ;
2019-03-26 18:51:13 +01:00
uint64_t r = f - > get_buffer ( source_file . ptrw ( ) , len ) ;
2020-06-10 23:15:09 +02:00
ERR_FAIL_COND_V ( r ! = len , " " ) ;
source_file . write [ len ] = 0 ;
String source ;
2022-07-05 14:18:29 +02:00
if ( source . parse_utf8 ( ( const char * ) source_file . ptr ( ) ) ! = OK ) {
2020-06-10 23:15:09 +02:00
ERR_FAIL_V_MSG ( " " , " Script ' " + p_path + " ' contains invalid unicode (UTF-8), so it was not loaded. Please ensure that scripts are saved in valid UTF-8 unicode. " ) ;
}
return source ;
}
2024-01-22 15:31:55 +01:00
Vector < uint8_t > GDScriptCache : : get_binary_tokens ( const String & p_path ) {
Vector < uint8_t > buffer ;
Error err = OK ;
Ref < FileAccess > f = FileAccess : : open ( p_path , FileAccess : : READ , & err ) ;
ERR_FAIL_COND_V_MSG ( err ! = OK , buffer , " Failed to open binary GDScript file ' " + p_path + " '. " ) ;
uint64_t len = f - > get_length ( ) ;
buffer . resize ( len ) ;
uint64_t read = f - > get_buffer ( buffer . ptrw ( ) , buffer . size ( ) ) ;
ERR_FAIL_COND_V_MSG ( read ! = len , Vector < uint8_t > ( ) , " Failed to read binary GDScript file ' " + p_path + " '. " ) ;
return buffer ;
}
2022-11-08 12:51:20 +01:00
Ref < GDScript > GDScriptCache : : get_shallow_script ( const String & p_path , Error & r_error , const String & p_owner ) {
2022-10-09 18:41:28 +02:00
MutexLock lock ( singleton - > mutex ) ;
2021-12-09 10:42:46 +01:00
if ( ! p_owner . is_empty ( ) ) {
2020-06-10 23:15:09 +02:00
singleton - > dependencies [ p_owner ] . insert ( p_path ) ;
}
if ( singleton - > full_gdscript_cache . has ( p_path ) ) {
return singleton - > full_gdscript_cache [ p_path ] ;
}
if ( singleton - > shallow_gdscript_cache . has ( p_path ) ) {
return singleton - > shallow_gdscript_cache [ p_path ] ;
}
2024-01-22 15:31:55 +01:00
String remapped_path = ResourceLoader : : path_remap ( p_path ) ;
2020-06-10 23:15:09 +02:00
Ref < GDScript > script ;
2021-06-18 00:03:09 +02:00
script . instantiate ( ) ;
2020-07-16 03:02:44 +02:00
script - > set_path ( p_path , true ) ;
2024-01-22 15:31:55 +01:00
if ( remapped_path . get_extension ( ) . to_lower ( ) = = " gdc " ) {
Vector < uint8_t > buffer = get_binary_tokens ( remapped_path ) ;
if ( buffer . is_empty ( ) ) {
r_error = ERR_FILE_CANT_READ ;
}
script - > set_binary_tokens_source ( buffer ) ;
} else {
r_error = script - > load_source_code ( remapped_path ) ;
}
2023-05-11 16:32:16 +02:00
if ( r_error ) {
return Ref < GDScript > ( ) ; // Returns null and does not cache when the script fails to load.
}
2022-11-20 09:17:16 +01:00
Ref < GDScriptParserRef > parser_ref = get_parser ( p_path , GDScriptParserRef : : PARSED , r_error ) ;
2022-11-20 19:06:14 +01:00
if ( r_error = = OK ) {
GDScriptCompiler : : make_scripts ( script . ptr ( ) , parser_ref - > get_parser ( ) - > get_tree ( ) , true ) ;
2022-11-20 09:17:16 +01:00
}
2022-10-09 18:41:28 +02:00
singleton - > shallow_gdscript_cache [ p_path ] = script ;
2020-06-10 23:15:09 +02:00
return script ;
}
2022-07-20 06:40:19 +02:00
Ref < GDScript > GDScriptCache : : get_full_script ( const String & p_path , Error & r_error , const String & p_owner , bool p_update_from_disk ) {
2022-10-09 18:41:28 +02:00
MutexLock lock ( singleton - > mutex ) ;
2020-06-10 23:15:09 +02:00
2021-12-09 10:42:46 +01:00
if ( ! p_owner . is_empty ( ) ) {
2020-06-10 23:15:09 +02:00
singleton - > dependencies [ p_owner ] . insert ( p_path ) ;
}
2022-07-20 06:40:19 +02:00
Ref < GDScript > script ;
2020-06-10 23:15:09 +02:00
r_error = OK ;
if ( singleton - > full_gdscript_cache . has ( p_path ) ) {
2022-11-27 16:31:53 +01:00
script = singleton - > full_gdscript_cache [ p_path ] ;
2022-07-20 06:40:19 +02:00
if ( ! p_update_from_disk ) {
return script ;
}
2020-06-10 23:15:09 +02:00
}
2021-08-17 01:27:25 +02:00
2022-07-20 06:40:19 +02:00
if ( script . is_null ( ) ) {
2022-11-08 12:51:20 +01:00
script = get_shallow_script ( p_path , r_error ) ;
2023-10-18 10:18:29 +02:00
// Only exit early if script failed to load, otherwise let reload report errors.
if ( script . is_null ( ) ) {
2022-11-08 12:51:20 +01:00
return script ;
}
2022-07-20 06:40:19 +02:00
}
2020-06-10 23:15:09 +02:00
2022-11-08 12:51:20 +01:00
if ( p_update_from_disk ) {
2024-01-22 15:31:55 +01:00
if ( p_path . get_extension ( ) . to_lower ( ) = = " gdc " ) {
Vector < uint8_t > buffer = get_binary_tokens ( p_path ) ;
if ( buffer . is_empty ( ) ) {
r_error = ERR_FILE_CANT_READ ;
return script ;
}
script - > set_binary_tokens_source ( buffer ) ;
} else {
r_error = script - > load_source_code ( p_path ) ;
if ( r_error ) {
return script ;
}
2023-07-07 17:13:48 +02:00
}
2022-11-08 12:51:20 +01:00
}
2020-07-16 03:02:44 +02:00
2023-07-07 17:13:48 +02:00
r_error = script - > reload ( true ) ;
2020-07-16 03:02:44 +02:00
if ( r_error ) {
return script ;
}
2022-10-09 18:41:28 +02:00
singleton - > full_gdscript_cache [ p_path ] = script ;
singleton - > shallow_gdscript_cache . erase ( p_path ) ;
2020-06-10 23:15:09 +02:00
return script ;
}
2022-11-08 12:51:20 +01:00
Ref < GDScript > GDScriptCache : : get_cached_script ( const String & p_path ) {
2022-10-09 18:41:28 +02:00
MutexLock lock ( singleton - > mutex ) ;
2022-11-08 12:51:20 +01:00
if ( singleton - > full_gdscript_cache . has ( p_path ) ) {
return singleton - > full_gdscript_cache [ p_path ] ;
}
if ( singleton - > shallow_gdscript_cache . has ( p_path ) ) {
return singleton - > shallow_gdscript_cache [ p_path ] ;
}
return Ref < GDScript > ( ) ;
}
2020-06-10 23:15:09 +02:00
Error GDScriptCache : : finish_compiling ( const String & p_owner ) {
2022-10-09 18:41:28 +02:00
MutexLock lock ( singleton - > mutex ) ;
2022-11-08 12:51:20 +01:00
2020-07-16 03:02:44 +02:00
// Mark this as compiled.
2022-11-08 12:51:20 +01:00
Ref < GDScript > script = get_cached_script ( p_owner ) ;
2022-10-09 18:41:28 +02:00
singleton - > full_gdscript_cache [ p_owner ] = script ;
2020-07-16 03:02:44 +02:00
singleton - > shallow_gdscript_cache . erase ( p_owner ) ;
2022-05-19 17:00:06 +02:00
HashSet < String > depends = singleton - > dependencies [ p_owner ] ;
2020-06-10 23:15:09 +02:00
Error err = OK ;
2022-05-19 17:00:06 +02:00
for ( const String & E : depends ) {
2020-06-10 23:15:09 +02:00
Error this_err = OK ;
// No need to save the script. We assume it's already referenced in the owner.
2022-05-19 17:00:06 +02:00
get_full_script ( E , this_err ) ;
2020-06-10 23:15:09 +02:00
if ( this_err ! = OK ) {
err = this_err ;
}
}
2020-07-16 03:02:44 +02:00
singleton - > dependencies . erase ( p_owner ) ;
2020-06-10 23:15:09 +02:00
return err ;
}
2023-04-19 16:10:35 +02:00
void GDScriptCache : : add_static_script ( Ref < GDScript > p_script ) {
ERR_FAIL_COND_MSG ( p_script . is_null ( ) , " Trying to cache empty script as static. " ) ;
ERR_FAIL_COND_MSG ( ! p_script - > is_valid ( ) , " Trying to cache non-compiled script as static. " ) ;
singleton - > static_gdscript_cache [ p_script - > get_fully_qualified_name ( ) ] = p_script ;
}
void GDScriptCache : : remove_static_script ( const String & p_fqcn ) {
singleton - > static_gdscript_cache . erase ( p_fqcn ) ;
}
2022-10-09 18:41:28 +02:00
Ref < PackedScene > GDScriptCache : : get_packed_scene ( const String & p_path , Error & r_error , const String & p_owner ) {
MutexLock lock ( singleton - > mutex ) ;
2023-10-09 13:57:31 +02:00
String path = p_path ;
if ( path . begins_with ( " uid:// " ) ) {
path = ResourceUID : : get_singleton ( ) - > get_id_path ( ResourceUID : : get_singleton ( ) - > text_to_id ( path ) ) ;
2022-10-09 18:41:28 +02:00
}
2023-10-09 13:57:31 +02:00
if ( singleton - > packed_scene_cache . has ( path ) ) {
singleton - > packed_scene_dependencies [ path ] . insert ( p_owner ) ;
return singleton - > packed_scene_cache [ path ] ;
}
Ref < PackedScene > scene = ResourceCache : : get_ref ( path ) ;
2022-11-21 21:57:45 +01:00
if ( scene . is_valid ( ) ) {
2023-10-09 13:57:31 +02:00
singleton - > packed_scene_cache [ path ] = scene ;
singleton - > packed_scene_dependencies [ path ] . insert ( p_owner ) ;
2022-11-21 21:57:45 +01:00
return scene ;
}
2022-10-09 18:41:28 +02:00
scene . instantiate ( ) ;
r_error = OK ;
2023-10-09 13:57:31 +02:00
if ( path . is_empty ( ) ) {
2022-10-09 18:41:28 +02:00
r_error = ERR_FILE_BAD_PATH ;
return scene ;
}
2023-10-09 13:57:31 +02:00
scene - > set_path ( path ) ;
singleton - > packed_scene_cache [ path ] = scene ;
singleton - > packed_scene_dependencies [ path ] . insert ( p_owner ) ;
2022-10-09 18:41:28 +02:00
scene - > reload_from_file ( ) ;
return scene ;
}
void GDScriptCache : : clear_unreferenced_packed_scenes ( ) {
if ( singleton = = nullptr ) {
return ;
}
MutexLock lock ( singleton - > mutex ) ;
2022-12-10 18:48:07 +01:00
if ( singleton - > cleared ) {
return ;
}
2022-10-09 18:41:28 +02:00
for ( KeyValue < String , HashSet < String > > & E : singleton - > packed_scene_dependencies ) {
if ( E . value . size ( ) > 0 | | ! ResourceLoader : : is_imported ( E . key ) ) {
continue ;
}
singleton - > packed_scene_dependencies . erase ( E . key ) ;
singleton - > packed_scene_cache . erase ( E . key ) ;
}
}
2022-12-02 19:27:26 +01:00
void GDScriptCache : : clear ( ) {
if ( singleton = = nullptr ) {
return ;
}
2020-06-10 23:15:09 +02:00
2022-12-02 19:27:26 +01:00
MutexLock lock ( singleton - > mutex ) ;
2022-10-09 18:41:28 +02:00
2022-12-10 18:48:07 +01:00
if ( singleton - > cleared ) {
return ;
}
singleton - > cleared = true ;
2022-10-09 18:41:28 +02:00
RBSet < Ref < GDScriptParserRef > > parser_map_refs ;
2022-12-02 19:27:26 +01:00
for ( KeyValue < String , GDScriptParserRef * > & E : singleton - > parser_map ) {
2022-10-09 18:41:28 +02:00
parser_map_refs . insert ( E . value ) ;
}
for ( Ref < GDScriptParserRef > & E : parser_map_refs ) {
if ( E . is_valid ( ) )
E - > clear ( ) ;
}
2022-12-11 00:56:34 +01:00
singleton - > packed_scene_dependencies . clear ( ) ;
singleton - > packed_scene_cache . clear ( ) ;
2022-12-07 21:29:18 +01:00
2022-10-09 18:41:28 +02:00
parser_map_refs . clear ( ) ;
2022-12-02 19:27:26 +01:00
singleton - > parser_map . clear ( ) ;
singleton - > shallow_gdscript_cache . clear ( ) ;
singleton - > full_gdscript_cache . clear ( ) ;
2022-10-09 18:41:28 +02:00
2022-12-02 19:27:26 +01:00
singleton - > packed_scene_cache . clear ( ) ;
singleton - > packed_scene_dependencies . clear ( ) ;
}
GDScriptCache : : GDScriptCache ( ) {
singleton = this ;
}
2022-10-09 18:41:28 +02:00
2022-12-02 19:27:26 +01:00
GDScriptCache : : ~ GDScriptCache ( ) {
2022-12-10 18:48:07 +01:00
if ( ! cleared ) {
clear ( ) ;
}
2020-06-10 23:15:09 +02:00
singleton = nullptr ;
}