2020-06-10 23:15:09 +02:00
/*************************************************************************/
/* gdscript_cache.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
2022-01-03 21:27:34 +01:00
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
2020-06-10 23:15:09 +02: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 14:51:48 +02:00
# include "core/io/file_access.h"
2020-11-07 23:33:38 +01:00
# include "core/templates/vector.h"
2020-06-10 23:15:09 +02:00
# include "gdscript.h"
# include "gdscript_analyzer.h"
# include "gdscript_parser.h"
bool GDScriptParserRef : : is_valid ( ) const {
return parser ! = nullptr ;
}
GDScriptParserRef : : Status GDScriptParserRef : : get_status ( ) const {
return status ;
}
GDScriptParser * GDScriptParserRef : : get_parser ( ) const {
return parser ;
}
Error GDScriptParserRef : : raise_status ( Status p_new_status ) {
ERR_FAIL_COND_V ( parser = = nullptr , ERR_INVALID_DATA ) ;
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 ) {
case EMPTY :
status = PARSED ;
2021-10-05 14:56:59 +02:00
result = parser - > parse ( GDScriptCache : : get_source_code ( path ) , path , false ) ;
2020-06-10 23:15:09 +02:00
break ;
case PARSED : {
analyzer = memnew ( GDScriptAnalyzer ( parser ) ) ;
status = INHERITANCE_SOLVED ;
2021-10-05 14:56:59 +02:00
Error inheritance_result = analyzer - > resolve_inheritance ( ) ;
2020-06-10 23:15:09 +02:00
if ( result = = OK ) {
result = inheritance_result ;
}
} break ;
case INHERITANCE_SOLVED : {
status = INTERFACE_SOLVED ;
2021-10-05 14:56:59 +02:00
Error interface_result = analyzer - > resolve_interface ( ) ;
2020-06-10 23:15:09 +02:00
if ( result = = OK ) {
result = interface_result ;
}
} break ;
case INTERFACE_SOLVED : {
status = FULLY_SOLVED ;
2021-10-05 14:56:59 +02:00
Error body_result = 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 ;
}
GDScriptParserRef : : ~ GDScriptParserRef ( ) {
if ( parser ! = nullptr ) {
memdelete ( parser ) ;
}
if ( analyzer ! = nullptr ) {
memdelete ( analyzer ) ;
}
2020-09-09 14:56:57 +02:00
MutexLock lock ( GDScriptCache : : singleton - > lock ) ;
2020-06-10 23:15:09 +02:00
GDScriptCache : : singleton - > parser_map . erase ( path ) ;
}
GDScriptCache * GDScriptCache : : singleton = nullptr ;
void GDScriptCache : : remove_script ( const String & p_path ) {
2020-09-09 14:56:57 +02:00
MutexLock lock ( singleton - > lock ) ;
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 ) {
2020-09-09 14:56:57 +02:00
MutexLock lock ( singleton - > lock ) ;
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 {
2020-08-18 02:30:39 +02:00
if ( ! FileAccess : : exists ( p_path ) ) {
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 ) ;
2020-06-10 23:15:09 +02:00
if ( err ) {
ERR_FAIL_COND_V ( err , " " ) ;
}
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 ;
}
Ref < GDScript > GDScriptCache : : get_shallow_script ( const String & p_path , const String & p_owner ) {
2020-09-09 14:56:57 +02:00
MutexLock lock ( singleton - > lock ) ;
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 ] ;
}
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 ) ;
2020-06-10 23:15:09 +02:00
script - > set_script_path ( p_path ) ;
script - > load_source_code ( p_path ) ;
2020-08-23 16:19:35 +02:00
singleton - > shallow_gdscript_cache [ p_path ] = script . ptr ( ) ;
2020-06-10 23:15:09 +02:00
return script ;
}
Ref < GDScript > GDScriptCache : : get_full_script ( const String & p_path , Error & r_error , const String & p_owner ) {
2020-09-09 14:56:57 +02:00
MutexLock lock ( singleton - > lock ) ;
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 ) ;
}
r_error = OK ;
if ( singleton - > full_gdscript_cache . has ( p_path ) ) {
2021-12-10 22:40:20 +01:00
return singleton - > full_gdscript_cache [ p_path ] ;
2020-06-10 23:15:09 +02:00
}
2021-08-17 01:27:25 +02:00
2020-06-10 23:15:09 +02:00
Ref < GDScript > script = get_shallow_script ( p_path ) ;
2021-08-17 01:27:25 +02:00
ERR_FAIL_COND_V ( script . is_null ( ) , Ref < GDScript > ( ) ) ;
2020-06-10 23:15:09 +02:00
2020-07-16 03:02:44 +02:00
r_error = script - > load_source_code ( p_path ) ;
if ( r_error ) {
return script ;
}
2020-06-10 23:15:09 +02:00
r_error = script - > reload ( ) ;
if ( r_error ) {
2020-07-16 03:02:44 +02:00
return script ;
2020-06-10 23:15:09 +02:00
}
2020-08-23 16:19:35 +02:00
singleton - > full_gdscript_cache [ p_path ] = script . ptr ( ) ;
2020-06-10 23:15:09 +02:00
singleton - > shallow_gdscript_cache . erase ( p_path ) ;
return script ;
}
Error GDScriptCache : : finish_compiling ( const String & p_owner ) {
2020-07-16 03:02:44 +02:00
// Mark this as compiled.
Ref < GDScript > script = get_shallow_script ( p_owner ) ;
2020-08-23 16:19:35 +02:00
singleton - > full_gdscript_cache [ p_owner ] = script . ptr ( ) ;
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 ;
}
GDScriptCache : : GDScriptCache ( ) {
singleton = this ;
}
GDScriptCache : : ~ GDScriptCache ( ) {
parser_map . clear ( ) ;
shallow_gdscript_cache . clear ( ) ;
full_gdscript_cache . clear ( ) ;
singleton = nullptr ;
}