2020-06-10 21:15:09 +00:00
/*************************************************************************/
/* gdscript_cache.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
2022-01-03 20:27:34 +00:00
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
2020-06-10 21:15:09 +00:00
/* */
/* 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. */
/*************************************************************************/
# include "gdscript_cache.h"
2021-06-11 12:51:48 +00:00
# include "core/io/file_access.h"
2020-11-07 22:33:38 +00:00
# include "core/templates/vector.h"
2020-06-10 21:15:09 +00:00
# include "gdscript.h"
# include "gdscript_analyzer.h"
2022-11-08 11:51:20 +00:00
# include "gdscript_compiler.h"
2020-06-10 21:15:09 +00:00
# include "gdscript_parser.h"
2022-10-09 16:41:28 +00:00
# include "scene/resources/packed_scene.h"
2020-06-10 21:15:09 +00: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 02:57:35 +00:00
GDScriptAnalyzer * GDScriptParserRef : : get_analyzer ( ) {
if ( analyzer = = nullptr ) {
analyzer = memnew ( GDScriptAnalyzer ( parser ) ) ;
}
return analyzer ;
}
2020-06-10 21:15:09 +00:00
Error GDScriptParserRef : : raise_status ( Status p_new_status ) {
ERR_FAIL_COND_V ( parser = = nullptr , ERR_INVALID_DATA ) ;
2021-08-14 15:56:09 +00:00
if ( result ! = OK ) {
return result ;
}
2020-06-10 21:15:09 +00:00
while ( p_new_status > status ) {
switch ( status ) {
case EMPTY :
status = PARSED ;
2021-10-05 12:56:59 +00:00
result = parser - > parse ( GDScriptCache : : get_source_code ( path ) , path , false ) ;
2020-06-10 21:15:09 +00:00
break ;
case PARSED : {
status = INHERITANCE_SOLVED ;
2022-12-11 02:57:35 +00:00
Error inheritance_result = get_analyzer ( ) - > resolve_inheritance ( ) ;
2020-06-10 21:15:09 +00:00
if ( result = = OK ) {
result = inheritance_result ;
}
} break ;
case INHERITANCE_SOLVED : {
status = INTERFACE_SOLVED ;
2022-12-11 02:57:35 +00:00
Error interface_result = get_analyzer ( ) - > resolve_interface ( ) ;
2020-06-10 21:15:09 +00:00
if ( result = = OK ) {
result = interface_result ;
}
} break ;
case INTERFACE_SOLVED : {
status = FULLY_SOLVED ;
2022-12-11 02:57:35 +00:00
Error body_result = get_analyzer ( ) - > resolve_body ( ) ;
2020-06-10 21:15:09 +00:00
if ( result = = OK ) {
result = body_result ;
}
} break ;
case FULLY_SOLVED : {
return result ;
}
}
2020-08-18 00:30:39 +00:00
if ( result ! = OK ) {
return result ;
}
2020-06-10 21:15:09 +00:00
}
return result ;
}
2022-10-09 16:41:28 +00:00
void GDScriptParserRef : : clear ( ) {
if ( cleared ) {
return ;
}
cleared = true ;
2020-06-10 21:15:09 +00:00
if ( parser ! = nullptr ) {
memdelete ( parser ) ;
}
2022-10-09 16:41:28 +00:00
2020-06-10 21:15:09 +00:00
if ( analyzer ! = nullptr ) {
memdelete ( analyzer ) ;
}
2022-10-09 16:41:28 +00:00
}
GDScriptParserRef : : ~ GDScriptParserRef ( ) {
clear ( ) ;
MutexLock lock ( GDScriptCache : : singleton - > mutex ) ;
2020-06-10 21:15:09 +00:00
GDScriptCache : : singleton - > parser_map . erase ( path ) ;
}
GDScriptCache * GDScriptCache : : singleton = nullptr ;
2022-10-09 16:41:28 +00:00
void GDScriptCache : : move_script ( const String & p_from , const String & p_to ) {
2022-11-20 20:21:57 +00:00
if ( singleton = = nullptr | | p_from = = p_to ) {
2022-10-09 16:41:28 +00:00
return ;
}
MutexLock lock ( singleton - > mutex ) ;
2022-12-10 17:48:07 +00:00
if ( singleton - > cleared ) {
return ;
}
2022-10-09 16:41:28 +00: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 21:15:09 +00:00
void GDScriptCache : : remove_script ( const String & p_path ) {
2022-10-09 16:41:28 +00:00
if ( singleton = = nullptr ) {
return ;
}
MutexLock lock ( singleton - > mutex ) ;
2022-12-10 17:48:07 +00:00
if ( singleton - > cleared ) {
return ;
}
2022-10-09 16:41:28 +00: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 21:15:09 +00:00
singleton - > shallow_gdscript_cache . erase ( p_path ) ;
singleton - > full_gdscript_cache . erase ( p_path ) ;
}
2020-07-16 01:02:44 +00:00
Ref < GDScriptParserRef > GDScriptCache : : get_parser ( const String & p_path , GDScriptParserRef : : Status p_status , Error & r_error , const String & p_owner ) {
2022-10-09 16:41:28 +00:00
MutexLock lock ( singleton - > mutex ) ;
2020-06-10 21:15:09 +00:00
Ref < GDScriptParserRef > ref ;
2021-12-09 09:42:46 +00:00
if ( ! p_owner . is_empty ( ) ) {
2020-07-16 01:02:44 +00:00
singleton - > dependencies [ p_owner ] . insert ( p_path ) ;
}
2020-06-10 21:15:09 +00:00
if ( singleton - > parser_map . has ( p_path ) ) {
2020-08-23 14:19:35 +00:00
ref = Ref < GDScriptParserRef > ( singleton - > parser_map [ p_path ] ) ;
2021-11-01 07:35:05 +00:00
if ( ref . is_null ( ) ) {
r_error = ERR_INVALID_DATA ;
return ref ;
}
2020-06-10 21:15:09 +00:00
} else {
2020-08-18 00:30:39 +00:00
if ( ! FileAccess : : exists ( p_path ) ) {
r_error = ERR_FILE_NOT_FOUND ;
return ref ;
}
2020-06-10 21:15:09 +00:00
GDScriptParser * parser = memnew ( GDScriptParser ) ;
2021-06-17 22:03:09 +00:00
ref . instantiate ( ) ;
2020-06-10 21:15:09 +00:00
ref - > parser = parser ;
ref - > path = p_path ;
2020-08-23 14:19:35 +00:00
singleton - > parser_map [ p_path ] = ref . ptr ( ) ;
2020-06-10 21:15:09 +00: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 09:08:58 +00:00
Ref < FileAccess > f = FileAccess : : open ( p_path , FileAccess : : READ , & err ) ;
2022-09-11 07:56:49 +00:00
ERR_FAIL_COND_V ( err , " " ) ;
2020-06-10 21:15:09 +00:00
2021-05-25 06:58:49 +00:00
uint64_t len = f - > get_length ( ) ;
2020-06-10 21:15:09 +00:00
source_file . resize ( len + 1 ) ;
2019-03-26 17:51:13 +00:00
uint64_t r = f - > get_buffer ( source_file . ptrw ( ) , len ) ;
2020-06-10 21:15:09 +00:00
ERR_FAIL_COND_V ( r ! = len , " " ) ;
source_file . write [ len ] = 0 ;
String source ;
2022-07-05 12:18:29 +00:00
if ( source . parse_utf8 ( ( const char * ) source_file . ptr ( ) ) ! = OK ) {
2020-06-10 21:15:09 +00: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 ;
}
2022-11-08 11:51:20 +00:00
Ref < GDScript > GDScriptCache : : get_shallow_script ( const String & p_path , Error & r_error , const String & p_owner ) {
2022-10-09 16:41:28 +00:00
MutexLock lock ( singleton - > mutex ) ;
2021-12-09 09:42:46 +00:00
if ( ! p_owner . is_empty ( ) ) {
2020-06-10 21:15:09 +00: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 ] ;
}
Ref < GDScript > script ;
2021-06-17 22:03:09 +00:00
script . instantiate ( ) ;
2020-07-16 01:02:44 +00:00
script - > set_path ( p_path , true ) ;
2020-06-10 21:15:09 +00:00
script - > load_source_code ( p_path ) ;
2022-11-20 08:17:16 +00:00
Ref < GDScriptParserRef > parser_ref = get_parser ( p_path , GDScriptParserRef : : PARSED , r_error ) ;
2022-11-20 18:06:14 +00:00
if ( r_error = = OK ) {
GDScriptCompiler : : make_scripts ( script . ptr ( ) , parser_ref - > get_parser ( ) - > get_tree ( ) , true ) ;
2022-11-20 08:17:16 +00:00
}
2022-10-09 16:41:28 +00:00
singleton - > shallow_gdscript_cache [ p_path ] = script ;
2020-06-10 21:15:09 +00:00
return script ;
}
2022-07-20 04:40:19 +00: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 16:41:28 +00:00
MutexLock lock ( singleton - > mutex ) ;
2020-06-10 21:15:09 +00:00
2021-12-09 09:42:46 +00:00
if ( ! p_owner . is_empty ( ) ) {
2020-06-10 21:15:09 +00:00
singleton - > dependencies [ p_owner ] . insert ( p_path ) ;
}
2022-07-20 04:40:19 +00:00
Ref < GDScript > script ;
2020-06-10 21:15:09 +00:00
r_error = OK ;
if ( singleton - > full_gdscript_cache . has ( p_path ) ) {
2022-11-27 15:31:53 +00:00
script = singleton - > full_gdscript_cache [ p_path ] ;
2022-07-20 04:40:19 +00:00
if ( ! p_update_from_disk ) {
return script ;
}
2020-06-10 21:15:09 +00:00
}
2021-08-16 23:27:25 +00:00
2022-07-20 04:40:19 +00:00
if ( script . is_null ( ) ) {
2022-11-08 11:51:20 +00:00
script = get_shallow_script ( p_path , r_error ) ;
if ( r_error ) {
return script ;
}
2022-07-20 04:40:19 +00:00
}
2020-06-10 21:15:09 +00:00
2022-11-08 11:51:20 +00:00
if ( p_update_from_disk ) {
r_error = script - > load_source_code ( p_path ) ;
}
2020-07-16 01:02:44 +00:00
if ( r_error ) {
return script ;
}
2022-10-09 16:41:28 +00:00
singleton - > full_gdscript_cache [ p_path ] = script ;
singleton - > shallow_gdscript_cache . erase ( p_path ) ;
2022-11-08 11:51:20 +00:00
r_error = script - > reload ( true ) ;
2020-06-10 21:15:09 +00:00
if ( r_error ) {
2022-10-09 16:41:28 +00:00
singleton - > shallow_gdscript_cache [ p_path ] = script ;
singleton - > full_gdscript_cache . erase ( p_path ) ;
2020-07-16 01:02:44 +00:00
return script ;
2020-06-10 21:15:09 +00:00
}
return script ;
}
2022-11-08 11:51:20 +00:00
Ref < GDScript > GDScriptCache : : get_cached_script ( const String & p_path ) {
2022-10-09 16:41:28 +00:00
MutexLock lock ( singleton - > mutex ) ;
2022-11-08 11:51:20 +00: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 21:15:09 +00:00
Error GDScriptCache : : finish_compiling ( const String & p_owner ) {
2022-10-09 16:41:28 +00:00
MutexLock lock ( singleton - > mutex ) ;
2022-11-08 11:51:20 +00:00
2020-07-16 01:02:44 +00:00
// Mark this as compiled.
2022-11-08 11:51:20 +00:00
Ref < GDScript > script = get_cached_script ( p_owner ) ;
2022-10-09 16:41:28 +00:00
singleton - > full_gdscript_cache [ p_owner ] = script ;
2020-07-16 01:02:44 +00:00
singleton - > shallow_gdscript_cache . erase ( p_owner ) ;
2022-05-19 15:00:06 +00:00
HashSet < String > depends = singleton - > dependencies [ p_owner ] ;
2020-06-10 21:15:09 +00:00
Error err = OK ;
2022-05-19 15:00:06 +00:00
for ( const String & E : depends ) {
2020-06-10 21:15:09 +00:00
Error this_err = OK ;
// No need to save the script. We assume it's already referenced in the owner.
2022-05-19 15:00:06 +00:00
get_full_script ( E , this_err ) ;
2020-06-10 21:15:09 +00:00
if ( this_err ! = OK ) {
err = this_err ;
}
}
2020-07-16 01:02:44 +00:00
singleton - > dependencies . erase ( p_owner ) ;
2020-06-10 21:15:09 +00:00
return err ;
}
2022-10-09 16:41:28 +00:00
Ref < PackedScene > GDScriptCache : : get_packed_scene ( const String & p_path , Error & r_error , const String & p_owner ) {
MutexLock lock ( singleton - > mutex ) ;
if ( singleton - > packed_scene_cache . has ( p_path ) ) {
singleton - > packed_scene_dependencies [ p_path ] . insert ( p_owner ) ;
return singleton - > packed_scene_cache [ p_path ] ;
}
2022-11-21 20:57:45 +00:00
Ref < PackedScene > scene = ResourceCache : : get_ref ( p_path ) ;
if ( scene . is_valid ( ) ) {
singleton - > packed_scene_cache [ p_path ] = scene ;
singleton - > packed_scene_dependencies [ p_path ] . insert ( p_owner ) ;
return scene ;
}
2022-10-09 16:41:28 +00:00
scene . instantiate ( ) ;
r_error = OK ;
if ( p_path . is_empty ( ) ) {
r_error = ERR_FILE_BAD_PATH ;
return scene ;
}
scene - > set_path ( p_path ) ;
singleton - > packed_scene_cache [ p_path ] = scene ;
singleton - > packed_scene_dependencies [ p_path ] . insert ( p_owner ) ;
scene - > reload_from_file ( ) ;
return scene ;
}
void GDScriptCache : : clear_unreferenced_packed_scenes ( ) {
if ( singleton = = nullptr ) {
return ;
}
MutexLock lock ( singleton - > mutex ) ;
2022-12-10 17:48:07 +00:00
if ( singleton - > cleared ) {
return ;
}
2022-10-09 16:41:28 +00: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 18:27:26 +00:00
void GDScriptCache : : clear ( ) {
if ( singleton = = nullptr ) {
return ;
}
2020-06-10 21:15:09 +00:00
2022-12-02 18:27:26 +00:00
MutexLock lock ( singleton - > mutex ) ;
2022-10-09 16:41:28 +00:00
2022-12-10 17:48:07 +00:00
if ( singleton - > cleared ) {
return ;
}
singleton - > cleared = true ;
2022-10-09 16:41:28 +00:00
RBSet < Ref < GDScriptParserRef > > parser_map_refs ;
2022-12-02 18:27:26 +00:00
for ( KeyValue < String , GDScriptParserRef * > & E : singleton - > parser_map ) {
2022-10-09 16:41:28 +00:00
parser_map_refs . insert ( E . value ) ;
}
for ( Ref < GDScriptParserRef > & E : parser_map_refs ) {
if ( E . is_valid ( ) )
E - > clear ( ) ;
}
2022-12-10 23:56:34 +00:00
singleton - > packed_scene_dependencies . clear ( ) ;
singleton - > packed_scene_cache . clear ( ) ;
2022-12-07 20:29:18 +00:00
2022-10-09 16:41:28 +00:00
parser_map_refs . clear ( ) ;
2022-12-02 18:27:26 +00:00
singleton - > parser_map . clear ( ) ;
singleton - > shallow_gdscript_cache . clear ( ) ;
singleton - > full_gdscript_cache . clear ( ) ;
2022-10-09 16:41:28 +00:00
2022-12-02 18:27:26 +00:00
singleton - > packed_scene_cache . clear ( ) ;
singleton - > packed_scene_dependencies . clear ( ) ;
}
GDScriptCache : : GDScriptCache ( ) {
singleton = this ;
}
2022-10-09 16:41:28 +00:00
2022-12-02 18:27:26 +00:00
GDScriptCache : : ~ GDScriptCache ( ) {
2022-12-10 17:48:07 +00:00
if ( ! cleared ) {
clear ( ) ;
}
2020-06-10 21:15:09 +00:00
singleton = nullptr ;
}