2023-01-05 12:25:55 +00: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 21:15:09 +00:00
# include "gdscript_cache.h"
# 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"
2023-06-13 14:56:21 +00:00
# include "core/io/file_access.h"
# include "core/templates/vector.h"
2020-06-10 21:15:09 +00:00
GDScriptParserRef : : Status GDScriptParserRef : : get_status ( ) const {
return status ;
}
2024-04-12 23:13:25 +00:00
uint32_t GDScriptParserRef : : get_source_hash ( ) const {
return source_hash ;
}
GDScriptParser * GDScriptParserRef : : get_parser ( ) {
if ( parser = = nullptr ) {
parser = memnew ( GDScriptParser ) ;
}
2020-06-10 21:15:09 +00:00
return parser ;
}
2022-12-11 02:57:35 +00:00
GDScriptAnalyzer * GDScriptParserRef : : get_analyzer ( ) {
if ( analyzer = = nullptr ) {
2024-04-12 23:13:25 +00:00
analyzer = memnew ( GDScriptAnalyzer ( get_parser ( ) ) ) ;
2022-12-11 02:57:35 +00:00
}
return analyzer ;
}
2020-06-10 21:15:09 +00:00
Error GDScriptParserRef : : raise_status ( Status p_new_status ) {
2024-04-12 23:13:25 +00:00
ERR_FAIL_COND_V ( clearing , ERR_BUG ) ;
ERR_FAIL_COND_V ( parser = = nullptr & & status ! = EMPTY , ERR_BUG ) ;
2020-06-10 21:15:09 +00:00
2024-04-12 23:13:25 +00:00
while ( result = = OK & & p_new_status > status ) {
2020-06-10 21:15:09 +00:00
switch ( status ) {
2024-01-22 14:31:55 +00:00
case EMPTY : {
2024-04-12 23:13:25 +00:00
// Calling parse will clear the parser, which can destruct another GDScriptParserRef which can clear the last reference to the script with this path, calling remove_script, which clears this GDScriptParserRef.
// It's ok if its the first thing done here.
get_parser ( ) - > clear ( ) ;
2020-06-10 21:15:09 +00:00
status = PARSED ;
2024-01-22 14:31:55 +00:00
String remapped_path = ResourceLoader : : path_remap ( path ) ;
if ( remapped_path . get_extension ( ) . to_lower ( ) = = " gdc " ) {
2024-04-12 23:13:25 +00:00
Vector < uint8_t > tokens = GDScriptCache : : get_binary_tokens ( remapped_path ) ;
source_hash = hash_djb2_buffer ( tokens . ptr ( ) , tokens . size ( ) ) ;
result = get_parser ( ) - > parse_binary ( tokens , path ) ;
2024-01-22 14:31:55 +00:00
} else {
2024-04-12 23:13:25 +00:00
String source = GDScriptCache : : get_source_code ( remapped_path ) ;
source_hash = source . hash ( ) ;
result = get_parser ( ) - > parse ( source , path , false ) ;
2024-01-22 14:31:55 +00:00
}
} break ;
2020-06-10 21:15:09 +00:00
case PARSED : {
status = INHERITANCE_SOLVED ;
2024-04-12 23:13:25 +00:00
result = get_analyzer ( ) - > resolve_inheritance ( ) ;
2020-06-10 21:15:09 +00:00
} break ;
case INHERITANCE_SOLVED : {
status = INTERFACE_SOLVED ;
2024-04-12 23:13:25 +00:00
result = get_analyzer ( ) - > resolve_interface ( ) ;
2020-06-10 21:15:09 +00:00
} break ;
case INTERFACE_SOLVED : {
2024-04-12 23:13:25 +00:00
status = BODY_SOLVED ;
result = get_analyzer ( ) - > resolve_body ( ) ;
} break ;
case BODY_SOLVED : {
2020-06-10 21:15:09 +00:00
status = FULLY_SOLVED ;
2024-04-12 23:13:25 +00:00
result = get_analyzer ( ) - > resolve_dependencies ( ) ;
2020-06-10 21:15:09 +00:00
} break ;
case FULLY_SOLVED : {
return result ;
}
}
}
return result ;
}
2022-10-09 16:41:28 +00:00
void GDScriptParserRef : : clear ( ) {
2024-04-12 23:13:25 +00:00
if ( clearing ) {
2022-10-09 16:41:28 +00:00
return ;
}
2024-04-12 23:13:25 +00:00
clearing = true ;
GDScriptParser * lparser = parser ;
GDScriptAnalyzer * lanalyzer = analyzer ;
2022-10-09 16:41:28 +00:00
2024-04-12 23:13:25 +00:00
parser = nullptr ;
analyzer = nullptr ;
status = EMPTY ;
result = OK ;
source_hash = 0 ;
clearing = false ;
if ( lanalyzer ! = nullptr ) {
memdelete ( lanalyzer ) ;
2020-06-10 21:15:09 +00:00
}
2022-10-09 16:41:28 +00:00
2024-04-12 23:13:25 +00:00
if ( lparser ! = nullptr ) {
memdelete ( lparser ) ;
2020-06-10 21:15:09 +00:00
}
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
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
if ( singleton - > parser_map . has ( p_path ) ) {
2024-04-12 23:13:25 +00:00
// Keep a local reference until it goes out of scope.
// Clearing it can trigger a reference to itself to go out of scope, destructing it before clear finishes.
Ref < GDScriptParserRef > parser_ref = singleton - > parser_map [ p_path ] ;
2022-10-09 16:41:28 +00:00
singleton - > parser_map . erase ( p_path ) ;
2024-04-12 23:13:25 +00:00
parser_ref - > clear ( ) ;
2022-10-09 16:41:28 +00:00
}
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 {
2024-01-22 14:31:55 +00:00
String remapped_path = ResourceLoader : : path_remap ( p_path ) ;
if ( ! FileAccess : : exists ( remapped_path ) ) {
2020-08-18 00:30:39 +00:00
r_error = ERR_FILE_NOT_FOUND ;
return ref ;
}
2021-06-17 22:03:09 +00:00
ref . instantiate ( ) ;
2020-06-10 21:15:09 +00:00
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 ;
}
2024-04-12 23:13:25 +00:00
bool GDScriptCache : : has_parser ( const String & p_path ) {
MutexLock lock ( singleton - > mutex ) ;
return singleton - > parser_map . has ( p_path ) ;
}
void GDScriptCache : : remove_parser ( const String & p_path ) {
MutexLock lock ( singleton - > mutex ) ;
// Can't clear the parser because some other parser might be currently using it in the chain of calls.
singleton - > parser_map . erase ( p_path ) ;
}
2020-06-10 21:15:09 +00:00
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 ;
}
2024-01-22 14:31:55 +00: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 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 ] ;
}
2024-01-22 14:31:55 +00:00
String remapped_path = ResourceLoader : : path_remap ( p_path ) ;
2020-06-10 21:15:09 +00:00
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 ) ;
2024-01-22 14:31:55 +00: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 14:32:16 +00:00
if ( r_error ) {
return Ref < GDScript > ( ) ; // Returns null and does not cache when the script fails to load.
}
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 ) ;
2023-10-18 08:18:29 +00:00
// Only exit early if script failed to load, otherwise let reload report errors.
if ( script . is_null ( ) ) {
2022-11-08 11:51:20 +00:00
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 ) {
2024-01-22 14:31:55 +00: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 15:13:48 +00:00
}
2022-11-08 11:51:20 +00:00
}
2020-07-16 01:02:44 +00:00
2023-07-07 15:13:48 +00:00
r_error = script - > reload ( true ) ;
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 ) ;
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 ;
}
2023-04-19 14:10:35 +00: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-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 ) ;
}
2024-04-12 23:13:25 +00:00
singleton - > parser_map . clear ( ) ;
2022-10-09 16:41:28 +00:00
for ( Ref < GDScriptParserRef > & E : parser_map_refs ) {
2024-04-12 23:13:25 +00:00
if ( E . is_valid ( ) ) {
2022-10-09 16:41:28 +00:00
E - > clear ( ) ;
2024-04-12 23:13:25 +00:00
}
2022-10-09 16:41:28 +00:00
}
parser_map_refs . clear ( ) ;
2022-12-02 18:27:26 +00:00
singleton - > shallow_gdscript_cache . clear ( ) ;
singleton - > full_gdscript_cache . 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 ;
}