2014-02-10 02:10:30 +01:00
/**************************************************************************/
2017-11-16 18:38:18 +01:00
/* gdscript_compiler.cpp */
2014-02-10 02:10:30 +01:00
/**************************************************************************/
/* 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. */
/**************************************************************************/
2018-01-05 00:50:27 +01:00
2017-11-16 18:38:18 +01:00
# include "gdscript_compiler.h"
2014-02-10 02:10:30 +01:00
2017-11-16 18:38:18 +01:00
# include "gdscript.h"
2020-08-07 19:51:56 +02:00
# include "gdscript_byte_codegen.h"
2020-07-16 03:02:44 +02:00
# include "gdscript_cache.h"
2020-11-26 15:56:32 +01:00
# include "gdscript_utility_functions.h"
2017-11-16 18:38:18 +01:00
2022-04-08 17:20:57 +02:00
# include "core/config/engine.h"
2021-09-01 21:01:39 +02:00
# include "core/config/project_settings.h"
2024-05-13 16:56:03 +02:00
# include "scene/scene_string_names.h"
2021-09-01 21:01:39 +02:00
2017-11-16 18:38:18 +01:00
bool GDScriptCompiler : : _is_class_member_property ( CodeGen & codegen , const StringName & p_name ) {
2020-05-02 00:14:56 +02:00
if ( codegen . function_node & & codegen . function_node - > is_static ) {
2017-01-04 21:37:45 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2017-01-04 21:37:45 +01:00
2022-12-05 19:24:11 +01:00
if ( _is_local_or_parameter ( codegen , p_name ) ) {
2018-01-20 21:41:05 +01:00
return false ; //shadowed
2020-05-14 16:41:43 +02:00
}
2018-01-20 21:41:05 +01:00
2017-01-04 21:37:45 +01:00
return _is_class_member_property ( codegen . script , p_name ) ;
}
2017-11-16 18:38:18 +01:00
bool GDScriptCompiler : : _is_class_member_property ( GDScript * owner , const StringName & p_name ) {
2017-01-04 21:37:45 +01:00
GDScript * scr = owner ;
2020-04-02 01:20:12 +02:00
GDScriptNativeClass * nc = nullptr ;
2017-01-04 21:37:45 +01:00
while ( scr ) {
2020-05-14 16:41:43 +02:00
if ( scr - > native . is_valid ( ) ) {
2017-01-04 21:37:45 +01:00
nc = scr - > native . ptr ( ) ;
2020-05-14 16:41:43 +02:00
}
2017-01-04 21:37:45 +01:00
scr = scr - > _base ;
}
2023-09-09 17:40:07 +02:00
ERR_FAIL_NULL_V ( nc , false ) ;
2017-01-04 21:37:45 +01:00
return ClassDB : : has_property ( nc - > get_name ( ) , p_name ) ;
}
2022-12-05 19:24:11 +01:00
bool GDScriptCompiler : : _is_local_or_parameter ( CodeGen & codegen , const StringName & p_name ) {
return codegen . parameters . has ( p_name ) | | codegen . locals . has ( p_name ) ;
}
2017-11-16 18:38:18 +01:00
void GDScriptCompiler : : _set_error ( const String & p_error , const GDScriptParser : : Node * p_node ) {
2021-12-09 10:42:46 +01:00
if ( ! error . is_empty ( ) ) {
2014-02-10 02:10:30 +01:00
return ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
error = p_error ;
2015-01-03 04:57:02 +01:00
if ( p_node ) {
2020-05-02 00:14:56 +02:00
err_line = p_node - > start_line ;
err_column = p_node - > leftmost_column ;
2015-01-03 04:57:02 +01:00
} else {
err_line = 0 ;
err_column = 0 ;
}
2014-02-10 02:10:30 +01:00
}
2023-09-12 20:55:55 +02:00
GDScriptDataType GDScriptCompiler : : _gdtype_from_datatype ( const GDScriptParser : : DataType & p_datatype , GDScript * p_owner , bool p_handle_metatype ) {
2023-02-26 07:30:33 +01:00
if ( ! p_datatype . is_set ( ) | | ! p_datatype . is_hard_type ( ) | | p_datatype . is_coroutine ) {
2018-05-30 04:16:57 +02:00
return GDScriptDataType ( ) ;
}
2014-02-10 02:10:30 +01:00
2018-05-30 04:16:57 +02:00
GDScriptDataType result ;
result . has_type = true ;
2014-02-10 02:10:30 +01:00
2018-05-30 04:16:57 +02:00
switch ( p_datatype . kind ) {
2020-07-16 03:02:44 +02:00
case GDScriptParser : : DataType : : VARIANT : {
result . has_type = false ;
} break ;
2018-05-30 04:16:57 +02:00
case GDScriptParser : : DataType : : BUILTIN : {
result . kind = GDScriptDataType : : BUILTIN ;
result . builtin_type = p_datatype . builtin_type ;
} break ;
case GDScriptParser : : DataType : : NATIVE : {
2023-09-12 20:55:55 +02:00
if ( p_handle_metatype & & p_datatype . is_meta_type ) {
result . kind = GDScriptDataType : : NATIVE ;
result . builtin_type = Variant : : OBJECT ;
2023-09-25 11:28:59 +02:00
// Fixes GH-82255. `GDScriptNativeClass` is obtainable in GDScript,
// but is not a registered and exposed class, so `GDScriptNativeClass`
// is missing from `GDScriptLanguage::get_singleton()->get_global_map()`.
//result.native_type = GDScriptNativeClass::get_class_static();
result . native_type = Object : : get_class_static ( ) ;
2023-09-12 20:55:55 +02:00
break ;
}
2018-05-30 04:16:57 +02:00
result . kind = GDScriptDataType : : NATIVE ;
2022-02-18 17:10:57 +01:00
result . builtin_type = p_datatype . builtin_type ;
2023-09-25 11:28:59 +02:00
result . native_type = p_datatype . native_type ;
# ifdef DEBUG_ENABLED
if ( unlikely ( ! GDScriptLanguage : : get_singleton ( ) - > get_global_map ( ) . has ( result . native_type ) ) ) {
ERR_PRINT ( vformat ( R " (GDScript bug: Native class " % s " not found.) " , result . native_type ) ) ;
result . native_type = Object : : get_class_static ( ) ;
}
# endif
2018-05-30 04:16:57 +02:00
} break ;
case GDScriptParser : : DataType : : SCRIPT : {
2023-09-12 20:55:55 +02:00
if ( p_handle_metatype & & p_datatype . is_meta_type ) {
result . kind = GDScriptDataType : : NATIVE ;
result . builtin_type = Variant : : OBJECT ;
result . native_type = p_datatype . script_type . is_valid ( ) ? p_datatype . script_type - > get_class ( ) : Script : : get_class_static ( ) ;
break ;
}
2018-05-30 04:16:57 +02:00
result . kind = GDScriptDataType : : SCRIPT ;
2022-11-08 12:51:20 +01:00
result . builtin_type = p_datatype . builtin_type ;
result . script_type_ref = p_datatype . script_type ;
2020-12-04 18:58:59 +01:00
result . script_type = result . script_type_ref . ptr ( ) ;
2022-11-08 12:51:20 +01:00
result . native_type = p_datatype . native_type ;
Fix many errors found by PVS-Studio
Fix errors 2, 3, 4, 6, 8, 9, 11, 12, 13, 14, and 15.
2018-11-28 03:58:00 +01:00
} break ;
2018-05-30 04:16:57 +02:00
case GDScriptParser : : DataType : : CLASS : {
2023-09-12 20:55:55 +02:00
if ( p_handle_metatype & & p_datatype . is_meta_type ) {
result . kind = GDScriptDataType : : NATIVE ;
result . builtin_type = Variant : : OBJECT ;
result . native_type = GDScript : : get_class_static ( ) ;
break ;
}
2022-11-08 12:51:20 +01:00
result . kind = GDScriptDataType : : GDSCRIPT ;
result . builtin_type = p_datatype . builtin_type ;
result . native_type = p_datatype . native_type ;
2020-07-16 03:02:44 +02:00
2022-12-12 13:35:55 +01:00
bool is_local_class = parser - > has_class ( p_datatype . class_type ) ;
2022-11-08 12:51:20 +01:00
Ref < GDScript > script ;
if ( is_local_class ) {
script = Ref < GDScript > ( main_script ) ;
} else {
Error err = OK ;
script = GDScriptCache : : get_shallow_script ( p_datatype . script_path , err , p_owner - > path ) ;
if ( err ) {
_set_error ( vformat ( R " (Could not find script " % s " : %s) " , p_datatype . script_path , error_names [ err ] ) , nullptr ) ;
}
}
2022-05-14 10:43:02 +02:00
2022-11-08 12:51:20 +01:00
if ( script . is_valid ( ) ) {
script = Ref < GDScript > ( script - > find_class ( p_datatype . class_type - > fqcn ) ) ;
}
if ( script . is_null ( ) ) {
_set_error ( vformat ( R " (Could not find class " % s " in " % s " .) " , p_datatype . class_type - > fqcn , p_datatype . script_path ) , nullptr ) ;
2022-11-16 05:20:49 +01:00
return GDScriptDataType ( ) ;
2022-11-08 12:51:20 +01:00
} else {
// Only hold a strong reference if the owner of the element qualified with this type is not local, to avoid cyclic references (leaks).
// TODO: Might lead to use after free if script_type is a subclass and is used after its parent is freed.
if ( ! is_local_class ) {
result . script_type_ref = script ;
2019-03-16 17:49:29 +01:00
}
2022-11-08 12:51:20 +01:00
result . script_type = script . ptr ( ) ;
2022-10-09 18:41:28 +02:00
result . native_type = p_datatype . native_type ;
2019-03-16 17:49:29 +01:00
}
2018-05-30 04:16:57 +02:00
} break ;
2021-03-17 18:58:05 +01:00
case GDScriptParser : : DataType : : ENUM :
2023-09-12 20:55:55 +02:00
if ( p_handle_metatype & & p_datatype . is_meta_type ) {
result . kind = GDScriptDataType : : BUILTIN ;
result . builtin_type = Variant : : DICTIONARY ;
break ;
}
2020-07-16 03:02:44 +02:00
result . kind = GDScriptDataType : : BUILTIN ;
2023-02-17 00:16:24 +01:00
result . builtin_type = p_datatype . builtin_type ;
2020-07-16 03:02:44 +02:00
break ;
2022-12-11 03:57:35 +01:00
case GDScriptParser : : DataType : : RESOLVING :
2020-07-06 17:24:24 +02:00
case GDScriptParser : : DataType : : UNRESOLVED : {
2018-05-30 04:16:57 +02:00
ERR_PRINT ( " Parser bug: converting unresolved type. " ) ;
2019-03-16 17:49:29 +01:00
return GDScriptDataType ( ) ;
2018-05-30 04:16:57 +02:00
}
2014-02-10 02:10:30 +01:00
}
2023-09-14 20:31:07 +02:00
for ( int i = 0 ; i < p_datatype . container_element_types . size ( ) ; i + + ) {
result . set_container_element_type ( i , _gdtype_from_datatype ( p_datatype . get_container_element_type_or_variant ( i ) , p_owner , false ) ) ;
2020-09-10 01:26:07 +02:00
}
2018-05-30 04:16:57 +02:00
return result ;
2014-02-10 02:10:30 +01:00
}
2020-11-17 14:44:52 +01:00
static bool _is_exact_type ( const PropertyInfo & p_par_type , const GDScriptDataType & p_arg_type ) {
if ( ! p_arg_type . has_type ) {
return false ;
}
if ( p_par_type . type = = Variant : : NIL ) {
return false ;
}
if ( p_par_type . type = = Variant : : OBJECT ) {
if ( p_arg_type . kind = = GDScriptDataType : : BUILTIN ) {
return false ;
}
StringName class_name ;
if ( p_arg_type . kind = = GDScriptDataType : : NATIVE ) {
class_name = p_arg_type . native_type ;
} else {
class_name = p_arg_type . native_type = = StringName ( ) ? p_arg_type . script_type - > get_instance_base_type ( ) : p_arg_type . native_type ;
}
return p_par_type . class_name = = class_name | | ClassDB : : is_parent_class ( class_name , p_par_type . class_name ) ;
} else {
if ( p_arg_type . kind ! = GDScriptDataType : : BUILTIN ) {
return false ;
}
return p_par_type . type = = p_arg_type . builtin_type ;
}
}
2023-07-25 17:42:07 +02:00
static bool _can_use_validate_call ( const MethodBind * p_method , const Vector < GDScriptCodeGenerator : : Address > & p_arguments ) {
2023-01-30 17:35:40 +01:00
if ( p_method - > is_vararg ( ) ) {
2023-07-25 17:42:07 +02:00
// Validated call won't work with vararg methods.
2023-01-30 17:35:40 +01:00
return false ;
}
2020-11-17 14:44:52 +01:00
if ( p_method - > get_argument_count ( ) ! = p_arguments . size ( ) ) {
2023-07-25 17:42:07 +02:00
// Validated call won't work with default arguments.
2020-11-17 14:44:52 +01:00
return false ;
}
MethodInfo info ;
ClassDB : : get_method_info ( p_method - > get_instance_class ( ) , p_method - > get_name ( ) , & info ) ;
2024-04-15 15:18:34 +02:00
int i = 0 ;
for ( List < PropertyInfo > : : ConstIterator itr = info . arguments . begin ( ) ; itr ! = info . arguments . end ( ) ; + + itr , + + i ) {
if ( ! _is_exact_type ( * itr , p_arguments [ i ] . type ) ) {
2020-11-17 14:44:52 +01:00
return false ;
}
}
return true ;
}
2024-02-27 11:25:33 +01:00
GDScriptCodeGenerator : : Address GDScriptCompiler : : _parse_expression ( CodeGen & codegen , Error & r_error , const GDScriptParser : : ExpressionNode * p_expression , bool p_root , bool p_initializer ) {
2023-01-14 21:58:34 +01:00
if ( p_expression - > is_constant & & ! ( p_expression - > get_datatype ( ) . is_meta_type & & p_expression - > get_datatype ( ) . kind = = GDScriptParser : : DataType : : CLASS ) ) {
2020-08-07 19:51:56 +02:00
return codegen . add_constant ( p_expression - > reduced_value ) ;
2020-06-11 00:53:25 +02:00
}
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator * gen = codegen . generator ;
2014-02-10 02:10:30 +01:00
switch ( p_expression - > type ) {
2020-05-02 00:14:56 +02:00
case GDScriptParser : : Node : : IDENTIFIER : {
2020-08-07 19:51:56 +02:00
// Look for identifiers in current scope.
2017-11-16 18:38:18 +01:00
const GDScriptParser : : IdentifierNode * in = static_cast < const GDScriptParser : : IdentifierNode * > ( p_expression ) ;
2014-02-10 02:10:30 +01:00
StringName identifier = in - > name ;
2023-07-25 13:21:49 +02:00
switch ( in - > source ) {
// LOCALS.
case GDScriptParser : : IdentifierNode : : FUNCTION_PARAMETER :
case GDScriptParser : : IdentifierNode : : LOCAL_VARIABLE :
case GDScriptParser : : IdentifierNode : : LOCAL_CONSTANT :
case GDScriptParser : : IdentifierNode : : LOCAL_ITERATOR :
case GDScriptParser : : IdentifierNode : : LOCAL_BIND : {
// Try function parameters.
if ( codegen . parameters . has ( identifier ) ) {
return codegen . parameters [ identifier ] ;
}
2017-01-04 21:37:45 +01:00
2023-07-25 13:21:49 +02:00
// Try local variables and constants.
if ( ! p_initializer & & codegen . locals . has ( identifier ) ) {
return codegen . locals [ identifier ] ;
}
} break ;
// MEMBERS.
case GDScriptParser : : IdentifierNode : : MEMBER_VARIABLE :
2023-08-02 14:42:36 +02:00
case GDScriptParser : : IdentifierNode : : MEMBER_FUNCTION :
case GDScriptParser : : IdentifierNode : : MEMBER_SIGNAL :
2023-07-25 13:21:49 +02:00
case GDScriptParser : : IdentifierNode : : INHERITED_VARIABLE : {
// Try class members.
if ( _is_class_member_property ( codegen , identifier ) ) {
// Get property.
GDScriptCodeGenerator : : Address temp = codegen . add_temporary ( _gdtype_from_datatype ( p_expression - > get_datatype ( ) , codegen . script ) ) ;
gen - > write_get_member ( temp , identifier ) ;
2020-08-07 19:51:56 +02:00
return temp ;
2020-06-01 21:41:05 +02:00
}
2014-02-10 02:10:30 +01:00
2023-07-25 13:21:49 +02:00
// Try members.
if ( ! codegen . function_node | | ! codegen . function_node - > is_static ) {
// Try member variables.
if ( codegen . script - > member_indices . has ( identifier ) ) {
if ( codegen . script - > member_indices [ identifier ] . getter ! = StringName ( ) & & codegen . script - > member_indices [ identifier ] . getter ! = codegen . function_name ) {
// Perform getter.
GDScriptCodeGenerator : : Address temp = codegen . add_temporary ( codegen . script - > member_indices [ identifier ] . data_type ) ;
Vector < GDScriptCodeGenerator : : Address > args ; // No argument needed.
gen - > write_call_self ( temp , codegen . script - > member_indices [ identifier ] . getter , args ) ;
return temp ;
} else {
// No getter or inside getter: direct member access.
int idx = codegen . script - > member_indices [ identifier ] . index ;
return GDScriptCodeGenerator : : Address ( GDScriptCodeGenerator : : Address : : MEMBER , idx , codegen . script - > get_member_type ( identifier ) ) ;
}
2023-05-16 12:03:53 +02:00
}
}
2023-07-25 13:21:49 +02:00
2023-08-02 14:42:36 +02:00
// Try methods and signals (can be Callable and Signal).
{
// Search upwards through parent classes:
const GDScriptParser : : ClassNode * base_class = codegen . class_node ;
while ( base_class ! = nullptr ) {
if ( base_class - > has_member ( identifier ) ) {
const GDScriptParser : : ClassNode : : Member & member = base_class - > get_member ( identifier ) ;
if ( member . type = = GDScriptParser : : ClassNode : : Member : : FUNCTION | | member . type = = GDScriptParser : : ClassNode : : Member : : SIGNAL ) {
// Get like it was a property.
GDScriptCodeGenerator : : Address temp = codegen . add_temporary ( ) ; // TODO: Get type here.
2023-12-12 20:15:16 +01:00
GDScriptCodeGenerator : : Address base ( GDScriptCodeGenerator : : Address : : SELF ) ;
if ( member . type = = GDScriptParser : : ClassNode : : Member : : FUNCTION & & member . function - > is_static ) {
base = GDScriptCodeGenerator : : Address ( GDScriptCodeGenerator : : Address : : CLASS ) ;
}
gen - > write_get_named ( temp , identifier , base ) ;
2023-08-02 14:42:36 +02:00
return temp ;
}
2023-07-25 13:21:49 +02:00
}
2023-08-02 14:42:36 +02:00
base_class = base_class - > base_type . class_type ;
2023-07-25 13:21:49 +02:00
}
2023-04-19 16:10:35 +02:00
2023-08-02 14:42:36 +02:00
// Try in native base.
GDScript * scr = codegen . script ;
GDScriptNativeClass * nc = nullptr ;
while ( scr ) {
if ( scr - > native . is_valid ( ) ) {
nc = scr - > native . ptr ( ) ;
}
scr = scr - > _base ;
2021-02-24 15:02:33 +01:00
}
2023-09-04 17:01:33 +02:00
if ( nc & & ( identifier = = CoreStringName ( free_ ) | | ClassDB : : has_signal ( nc - > get_name ( ) , identifier ) | | ClassDB : : has_method ( nc - > get_name ( ) , identifier ) ) ) {
2023-08-02 14:42:36 +02:00
// Get like it was a property.
GDScriptCodeGenerator : : Address temp = codegen . add_temporary ( ) ; // TODO: Get type here.
GDScriptCodeGenerator : : Address self ( GDScriptCodeGenerator : : Address : : SELF ) ;
2023-07-25 13:21:49 +02:00
2023-08-02 14:42:36 +02:00
gen - > write_get_named ( temp , identifier , self ) ;
return temp ;
}
2020-05-14 16:41:43 +02:00
}
2023-07-25 13:21:49 +02:00
} break ;
case GDScriptParser : : IdentifierNode : : MEMBER_CONSTANT :
case GDScriptParser : : IdentifierNode : : MEMBER_CLASS : {
// Try class constants.
GDScript * owner = codegen . script ;
while ( owner ) {
GDScript * scr = owner ;
GDScriptNativeClass * nc = nullptr ;
2023-08-02 14:42:36 +02:00
2023-07-25 13:21:49 +02:00
while ( scr ) {
if ( scr - > constants . has ( identifier ) ) {
return codegen . add_constant ( scr - > constants [ identifier ] ) ; // TODO: Get type here.
}
if ( scr - > native . is_valid ( ) ) {
nc = scr - > native . ptr ( ) ;
}
scr = scr - > _base ;
}
2021-02-24 15:02:33 +01:00
2023-07-25 13:21:49 +02:00
// Class C++ integer constant.
if ( nc ) {
bool success = false ;
int64_t constant = ClassDB : : get_integer_constant ( nc - > get_name ( ) , identifier , & success ) ;
if ( success ) {
return codegen . add_constant ( constant ) ;
}
}
2014-02-10 02:10:30 +01:00
2023-07-25 13:21:49 +02:00
owner = owner - > _owner ;
}
} break ;
case GDScriptParser : : IdentifierNode : : STATIC_VARIABLE : {
// Try static variables.
GDScript * scr = codegen . script ;
while ( scr ) {
if ( scr - > static_variables_indices . has ( identifier ) ) {
if ( scr - > static_variables_indices [ identifier ] . getter ! = StringName ( ) & & scr - > static_variables_indices [ identifier ] . getter ! = codegen . function_name ) {
// Perform getter.
GDScriptCodeGenerator : : Address temp = codegen . add_temporary ( scr - > static_variables_indices [ identifier ] . data_type ) ;
GDScriptCodeGenerator : : Address class_addr ( GDScriptCodeGenerator : : Address : : CLASS ) ;
Vector < GDScriptCodeGenerator : : Address > args ; // No argument needed.
gen - > write_call ( temp , class_addr , scr - > static_variables_indices [ identifier ] . getter , args ) ;
return temp ;
} else {
// No getter or inside getter: direct variable access.
GDScriptCodeGenerator : : Address temp = codegen . add_temporary ( scr - > static_variables_indices [ identifier ] . data_type ) ;
GDScriptCodeGenerator : : Address _class = codegen . add_constant ( scr ) ;
int index = scr - > static_variables_indices [ identifier ] . index ;
gen - > write_get_static_variable ( temp , _class , index ) ;
return temp ;
}
2022-01-16 06:31:31 +01:00
}
2023-07-25 13:21:49 +02:00
scr = scr - > _base ;
2014-02-10 02:10:30 +01:00
}
2023-07-25 13:21:49 +02:00
} break ;
// GLOBALS.
case GDScriptParser : : IdentifierNode : : UNDEFINED_SOURCE : {
// Try globals.
if ( GDScriptLanguage : : get_singleton ( ) - > get_global_map ( ) . has ( identifier ) ) {
// If it's an autoload singleton, we postpone to load it at runtime.
// This is so one autoload doesn't try to load another before it's compiled.
HashMap < StringName , ProjectSettings : : AutoloadInfo > autoloads = ProjectSettings : : get_singleton ( ) - > get_autoload_list ( ) ;
if ( autoloads . has ( identifier ) & & autoloads [ identifier ] . is_singleton ) {
GDScriptCodeGenerator : : Address global = codegen . add_temporary ( _gdtype_from_datatype ( in - > get_datatype ( ) , codegen . script ) ) ;
int idx = GDScriptLanguage : : get_singleton ( ) - > get_global_map ( ) [ identifier ] ;
gen - > write_store_global ( global , idx ) ;
return global ;
} else {
int idx = GDScriptLanguage : : get_singleton ( ) - > get_global_map ( ) [ identifier ] ;
Variant global = GDScriptLanguage : : get_singleton ( ) - > get_global_array ( ) [ idx ] ;
return codegen . add_constant ( global ) ;
}
2021-02-24 15:02:33 +01:00
}
2014-02-10 02:10:30 +01:00
2023-07-25 13:21:49 +02:00
// Try global classes.
if ( ScriptServer : : is_global_class ( identifier ) ) {
const GDScriptParser : : ClassNode * class_node = codegen . class_node ;
while ( class_node - > outer ) {
class_node = class_node - > outer ;
}
2014-02-10 02:10:30 +01:00
2023-07-25 13:21:49 +02:00
Ref < Resource > res ;
2018-07-16 00:29:00 +02:00
2023-07-25 13:21:49 +02:00
if ( class_node - > identifier & & class_node - > identifier - > name = = identifier ) {
res = Ref < GDScript > ( main_script ) ;
} else {
String global_class_path = ScriptServer : : get_global_class_path ( identifier ) ;
if ( ResourceLoader : : get_resource_type ( global_class_path ) = = " GDScript " ) {
Error err = OK ;
2023-09-12 17:11:09 +02:00
// Should not need to pass p_owner since analyzer will already have done it.
res = GDScriptCache : : get_shallow_script ( global_class_path , err ) ;
2023-07-25 13:21:49 +02:00
if ( err ! = OK ) {
_set_error ( " Can't load global class " + String ( identifier ) , p_expression ) ;
r_error = ERR_COMPILATION_FAILED ;
return GDScriptCodeGenerator : : Address ( ) ;
}
} else {
res = ResourceLoader : : load ( global_class_path ) ;
if ( res . is_null ( ) ) {
_set_error ( " Can't load global class " + String ( identifier ) + " , cyclic reference? " , p_expression ) ;
r_error = ERR_COMPILATION_FAILED ;
return GDScriptCodeGenerator : : Address ( ) ;
}
}
2022-10-09 18:41:28 +02:00
}
2018-07-16 00:29:00 +02:00
2023-07-25 13:21:49 +02:00
return codegen . add_constant ( res ) ;
}
2018-07-16 00:29:00 +02:00
2018-05-01 16:06:23 +02:00
# ifdef TOOLS_ENABLED
2023-07-25 13:21:49 +02:00
if ( GDScriptLanguage : : get_singleton ( ) - > get_named_globals_map ( ) . has ( identifier ) ) {
GDScriptCodeGenerator : : Address global = codegen . add_temporary ( ) ; // TODO: Get type.
gen - > write_store_named_global ( global , identifier ) ;
return global ;
}
2018-05-01 16:06:23 +02:00
# endif
2023-07-25 13:21:49 +02:00
} break ;
}
2020-08-07 19:51:56 +02:00
// Not found, error.
2014-02-10 02:10:30 +01:00
_set_error ( " Identifier not found: " + String ( identifier ) , p_expression ) ;
2020-08-07 19:51:56 +02:00
r_error = ERR_COMPILATION_FAILED ;
return GDScriptCodeGenerator : : Address ( ) ;
2014-02-10 02:10:30 +01:00
} break ;
2020-05-02 00:14:56 +02:00
case GDScriptParser : : Node : : LITERAL : {
2020-08-07 19:51:56 +02:00
// Return constant.
2020-05-02 00:14:56 +02:00
const GDScriptParser : : LiteralNode * cn = static_cast < const GDScriptParser : : LiteralNode * > ( p_expression ) ;
2014-02-10 02:10:30 +01:00
2020-08-07 19:51:56 +02:00
return codegen . add_constant ( cn - > value ) ;
2014-02-10 02:10:30 +01:00
} break ;
2020-05-02 00:14:56 +02:00
case GDScriptParser : : Node : : SELF : {
2014-02-10 02:10:30 +01:00
//return constant
2020-05-02 00:14:56 +02:00
if ( codegen . function_node & & codegen . function_node - > is_static ) {
2014-02-10 02:10:30 +01:00
_set_error ( " 'self' not present in static function! " , p_expression ) ;
2020-08-07 19:51:56 +02:00
r_error = ERR_COMPILATION_FAILED ;
return GDScriptCodeGenerator : : Address ( ) ;
2014-02-10 02:10:30 +01:00
}
2020-08-07 19:51:56 +02:00
return GDScriptCodeGenerator : : Address ( GDScriptCodeGenerator : : Address : : SELF ) ;
2014-02-10 02:10:30 +01:00
} break ;
2020-05-02 00:14:56 +02:00
case GDScriptParser : : Node : : ARRAY : {
2017-11-16 18:38:18 +01:00
const GDScriptParser : : ArrayNode * an = static_cast < const GDScriptParser : : ArrayNode * > ( p_expression ) ;
2020-08-07 19:51:56 +02:00
Vector < GDScriptCodeGenerator : : Address > values ;
2014-02-10 02:10:30 +01:00
2020-08-07 19:51:56 +02:00
// Create the result temporary first since it's the last to be killed.
2022-11-08 12:51:20 +01:00
GDScriptDataType array_type = _gdtype_from_datatype ( an - > get_datatype ( ) , codegen . script ) ;
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address result = codegen . add_temporary ( array_type ) ;
2014-02-10 02:10:30 +01:00
for ( int i = 0 ; i < an - > elements . size ( ) ; i + + ) {
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address val = _parse_expression ( codegen , r_error , an - > elements [ i ] ) ;
if ( r_error ) {
return GDScriptCodeGenerator : : Address ( ) ;
2020-05-14 16:41:43 +02:00
}
2020-08-07 19:51:56 +02:00
values . push_back ( val ) ;
2014-02-10 02:10:30 +01:00
}
2023-09-14 20:31:07 +02:00
if ( array_type . has_container_element_type ( 0 ) ) {
gen - > write_construct_typed_array ( result , array_type . get_container_element_type ( 0 ) , values ) ;
2021-03-09 16:32:35 +01:00
} else {
gen - > write_construct_array ( result , values ) ;
}
2020-08-07 19:51:56 +02:00
2020-05-14 16:41:43 +02:00
for ( int i = 0 ; i < values . size ( ) ; i + + ) {
2020-08-07 19:51:56 +02:00
if ( values [ i ] . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
gen - > pop_temporary ( ) ;
}
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-08-07 19:51:56 +02:00
return result ;
2014-02-10 02:10:30 +01:00
} break ;
2020-05-02 00:14:56 +02:00
case GDScriptParser : : Node : : DICTIONARY : {
2017-11-16 18:38:18 +01:00
const GDScriptParser : : DictionaryNode * dn = static_cast < const GDScriptParser : : DictionaryNode * > ( p_expression ) ;
2020-08-07 19:51:56 +02:00
Vector < GDScriptCodeGenerator : : Address > elements ;
2014-02-10 02:10:30 +01:00
2020-08-07 19:51:56 +02:00
// Create the result temporary first since it's the last to be killed.
2023-06-24 20:03:28 +02:00
GDScriptDataType dict_type = _gdtype_from_datatype ( dn - > get_datatype ( ) , codegen . script ) ;
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address result = codegen . add_temporary ( dict_type ) ;
2014-02-10 02:10:30 +01:00
for ( int i = 0 ; i < dn - > elements . size ( ) ; i + + ) {
2020-05-02 00:14:56 +02:00
// Key.
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address element ;
2020-05-02 00:14:56 +02:00
switch ( dn - > style ) {
case GDScriptParser : : DictionaryNode : : PYTHON_DICT :
// Python-style: key is any expression.
2020-08-07 19:51:56 +02:00
element = _parse_expression ( codegen , r_error , dn - > elements [ i ] . key ) ;
if ( r_error ) {
return GDScriptCodeGenerator : : Address ( ) ;
2020-05-02 00:14:56 +02:00
}
break ;
case GDScriptParser : : DictionaryNode : : LUA_TABLE :
2021-04-23 21:00:23 +02:00
// Lua-style: key is an identifier interpreted as StringName.
2021-09-15 14:56:24 +02:00
StringName key = dn - > elements [ i ] . key - > reduced_value . operator StringName ( ) ;
2020-08-07 19:51:56 +02:00
element = codegen . add_constant ( key ) ;
2020-05-02 00:14:56 +02:00
break ;
2014-02-10 02:10:30 +01:00
}
2020-08-07 19:51:56 +02:00
elements . push_back ( element ) ;
2014-02-10 02:10:30 +01:00
2020-08-07 19:51:56 +02:00
element = _parse_expression ( codegen , r_error , dn - > elements [ i ] . value ) ;
if ( r_error ) {
return GDScriptCodeGenerator : : Address ( ) ;
2014-02-10 02:10:30 +01:00
}
2020-08-07 19:51:56 +02:00
elements . push_back ( element ) ;
2014-02-10 02:10:30 +01:00
}
2023-06-24 20:03:28 +02:00
if ( dict_type . has_container_element_types ( ) ) {
gen - > write_construct_typed_dictionary ( result , dict_type . get_container_element_type_or_variant ( 0 ) , dict_type . get_container_element_type_or_variant ( 1 ) , elements ) ;
} else {
gen - > write_construct_dictionary ( result , elements ) ;
}
2020-08-07 19:51:56 +02:00
2020-05-02 00:14:56 +02:00
for ( int i = 0 ; i < elements . size ( ) ; i + + ) {
2020-08-07 19:51:56 +02:00
if ( elements [ i ] . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
gen - > pop_temporary ( ) ;
}
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-08-07 19:51:56 +02:00
return result ;
2014-02-10 02:10:30 +01:00
} break ;
2020-05-02 00:14:56 +02:00
case GDScriptParser : : Node : : CAST : {
2018-05-30 04:16:51 +02:00
const GDScriptParser : : CastNode * cn = static_cast < const GDScriptParser : : CastNode * > ( p_expression ) ;
2023-09-12 20:55:55 +02:00
GDScriptDataType cast_type = _gdtype_from_datatype ( cn - > get_datatype ( ) , codegen . script , false ) ;
2022-02-02 17:57:24 +01:00
2022-12-12 06:58:26 +01:00
GDScriptCodeGenerator : : Address result ;
if ( cast_type . has_type ) {
// Create temporary for result first since it will be deleted last.
result = codegen . add_temporary ( cast_type ) ;
2018-05-30 04:16:56 +02:00
2022-12-12 06:58:26 +01:00
GDScriptCodeGenerator : : Address src = _parse_expression ( codegen , r_error , cn - > operand ) ;
2019-03-16 17:49:29 +01:00
2022-12-12 06:58:26 +01:00
gen - > write_cast ( result , src , cast_type ) ;
2018-05-30 04:16:56 +02:00
2022-12-12 06:58:26 +01:00
if ( src . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
gen - > pop_temporary ( ) ;
}
} else {
result = _parse_expression ( codegen , r_error , cn - > operand ) ;
2018-05-30 04:16:56 +02:00
}
2020-11-25 15:37:51 +01:00
return result ;
2018-05-30 04:16:51 +02:00
} break ;
2020-05-02 00:14:56 +02:00
case GDScriptParser : : Node : : CALL : {
const GDScriptParser : : CallNode * call = static_cast < const GDScriptParser : : CallNode * > ( p_expression ) ;
2023-03-01 07:47:09 +01:00
bool is_awaited = p_expression = = awaited_node ;
2022-11-08 12:51:20 +01:00
GDScriptDataType type = _gdtype_from_datatype ( call - > get_datatype ( ) , codegen . script ) ;
2023-01-09 13:20:18 +01:00
GDScriptCodeGenerator : : Address result ;
if ( p_root ) {
result = GDScriptCodeGenerator : : Address ( GDScriptCodeGenerator : : Address : : NIL ) ;
} else {
result = codegen . add_temporary ( type ) ;
}
2020-05-02 00:14:56 +02:00
2020-08-07 19:51:56 +02:00
Vector < GDScriptCodeGenerator : : Address > arguments ;
for ( int i = 0 ; i < call - > arguments . size ( ) ; i + + ) {
GDScriptCodeGenerator : : Address arg = _parse_expression ( codegen , r_error , call - > arguments [ i ] ) ;
if ( r_error ) {
return GDScriptCodeGenerator : : Address ( ) ;
2020-05-02 00:14:56 +02:00
}
2020-08-07 19:51:56 +02:00
arguments . push_back ( arg ) ;
}
2014-02-10 02:10:30 +01:00
2023-09-21 11:42:55 +02:00
if ( ! call - > is_super & & call - > callee - > type = = GDScriptParser : : Node : : IDENTIFIER & & GDScriptParser : : get_builtin_type ( call - > function_name ) < Variant : : VARIANT_MAX ) {
gen - > write_construct ( result , GDScriptParser : : get_builtin_type ( call - > function_name ) , arguments ) ;
2020-11-26 15:56:32 +01:00
} else if ( ! call - > is_super & & call - > callee - > type = = GDScriptParser : : Node : : IDENTIFIER & & Variant : : has_utility_function ( call - > function_name ) ) {
// Variant utility function.
2023-01-09 13:20:18 +01:00
gen - > write_call_utility ( result , call - > function_name , arguments ) ;
2020-11-26 15:56:32 +01:00
} else if ( ! call - > is_super & & call - > callee - > type = = GDScriptParser : : Node : : IDENTIFIER & & GDScriptUtilityFunctions : : function_exists ( call - > function_name ) ) {
// GDScript utility function.
2022-12-29 14:47:53 +01:00
gen - > write_call_gdscript_utility ( result , call - > function_name , arguments ) ;
2020-05-02 00:14:56 +02:00
} else {
2020-08-07 19:51:56 +02:00
// Regular function.
2020-05-02 00:14:56 +02:00
const GDScriptParser : : ExpressionNode * callee = call - > callee ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
if ( call - > is_super ) {
// Super call.
2023-01-09 13:20:18 +01:00
gen - > write_super_call ( result , call - > function_name , arguments ) ;
2020-05-02 00:14:56 +02:00
} else {
if ( callee - > type = = GDScriptParser : : Node : : IDENTIFIER ) {
// Self function call.
2020-11-17 14:44:52 +01:00
if ( ClassDB : : has_method ( codegen . script - > native - > get_name ( ) , call - > function_name ) ) {
// Native method, use faster path.
GDScriptCodeGenerator : : Address self ;
self . mode = GDScriptCodeGenerator : : Address : : SELF ;
MethodBind * method = ClassDB : : get_method ( codegen . script - > native - > get_name ( ) , call - > function_name ) ;
2023-07-25 17:42:07 +02:00
if ( _can_use_validate_call ( method , arguments ) ) {
// Exact arguments, use validated call.
gen - > write_call_method_bind_validated ( result , self , method , arguments ) ;
2020-11-17 14:44:52 +01:00
} else {
// Not exact arguments, but still can use method bind call.
2023-01-09 13:20:18 +01:00
gen - > write_call_method_bind ( result , self , method , arguments ) ;
2020-11-17 14:44:52 +01:00
}
2024-04-04 23:15:17 +02:00
} else if ( call - > is_static | | codegen . is_static | | ( codegen . function_node & & codegen . function_node - > is_static ) | | call - > function_name = = " new " ) {
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address self ;
self . mode = GDScriptCodeGenerator : : Address : : CLASS ;
2023-03-01 07:47:09 +01:00
if ( is_awaited ) {
2023-01-09 13:20:18 +01:00
gen - > write_call_async ( result , self , call - > function_name , arguments ) ;
2020-12-05 00:42:45 +01:00
} else {
2023-01-09 13:20:18 +01:00
gen - > write_call ( result , self , call - > function_name , arguments ) ;
2020-12-05 00:42:45 +01:00
}
2020-05-02 00:14:56 +02:00
} else {
2023-03-01 07:47:09 +01:00
if ( is_awaited ) {
2023-01-09 13:20:18 +01:00
gen - > write_call_self_async ( result , call - > function_name , arguments ) ;
2020-12-05 00:42:45 +01:00
} else {
2023-01-09 13:20:18 +01:00
gen - > write_call_self ( result , call - > function_name , arguments ) ;
2020-12-05 00:42:45 +01:00
}
2020-05-02 00:14:56 +02:00
}
} else if ( callee - > type = = GDScriptParser : : Node : : SUBSCRIPT ) {
const GDScriptParser : : SubscriptNode * subscript = static_cast < const GDScriptParser : : SubscriptNode * > ( call - > callee ) ;
2023-02-07 16:28:52 +01:00
2020-05-02 00:14:56 +02:00
if ( subscript - > is_attribute ) {
2021-05-16 16:48:53 +02:00
// May be static built-in method call.
if ( ! call - > is_super & & subscript - > base - > type = = GDScriptParser : : Node : : IDENTIFIER & & GDScriptParser : : get_builtin_type ( static_cast < GDScriptParser : : IdentifierNode * > ( subscript - > base ) - > name ) < Variant : : VARIANT_MAX ) {
2023-01-09 13:20:18 +01:00
gen - > write_call_builtin_type_static ( result , GDScriptParser : : get_builtin_type ( static_cast < GDScriptParser : : IdentifierNode * > ( subscript - > base ) - > name ) , subscript - > attribute - > name , arguments ) ;
2022-04-08 17:20:57 +02:00
} else if ( ! call - > is_super & & subscript - > base - > type = = GDScriptParser : : Node : : IDENTIFIER & & call - > function_name ! = SNAME ( " new " ) & &
ClassDB : : class_exists ( static_cast < GDScriptParser : : IdentifierNode * > ( subscript - > base ) - > name ) & & ! Engine : : get_singleton ( ) - > has_singleton ( static_cast < GDScriptParser : : IdentifierNode * > ( subscript - > base ) - > name ) ) {
2022-04-06 19:14:38 +02:00
// It's a static native method call.
2024-04-26 02:05:53 +02:00
StringName class_name = static_cast < GDScriptParser : : IdentifierNode * > ( subscript - > base ) - > name ;
MethodBind * method = ClassDB : : get_method ( class_name , subscript - > attribute - > name ) ;
if ( _can_use_validate_call ( method , arguments ) ) {
// Exact arguments, use validated call.
gen - > write_call_native_static_validated ( result , method , arguments ) ;
} else {
// Not exact arguments, use regular static call
gen - > write_call_native_static ( result , class_name , subscript - > attribute - > name , arguments ) ;
}
2021-05-16 16:48:53 +02:00
} else {
GDScriptCodeGenerator : : Address base = _parse_expression ( codegen , r_error , subscript - > base ) ;
if ( r_error ) {
return GDScriptCodeGenerator : : Address ( ) ;
2020-11-17 14:44:52 +01:00
}
2023-03-01 07:47:09 +01:00
if ( is_awaited ) {
2023-01-09 13:20:18 +01:00
gen - > write_call_async ( result , base , call - > function_name , arguments ) ;
2021-05-16 16:48:53 +02:00
} else if ( base . type . has_type & & base . type . kind ! = GDScriptDataType : : BUILTIN ) {
// Native method, use faster path.
StringName class_name ;
if ( base . type . kind = = GDScriptDataType : : NATIVE ) {
class_name = base . type . native_type ;
2020-11-17 14:44:52 +01:00
} else {
2021-05-16 16:48:53 +02:00
class_name = base . type . native_type = = StringName ( ) ? base . type . script_type - > get_instance_base_type ( ) : base . type . native_type ;
2020-11-17 14:44:52 +01:00
}
2023-02-07 16:28:52 +01:00
if ( ClassDB : : class_exists ( class_name ) & & ClassDB : : has_method ( class_name , call - > function_name ) ) {
2021-05-16 16:48:53 +02:00
MethodBind * method = ClassDB : : get_method ( class_name , call - > function_name ) ;
2023-07-25 17:42:07 +02:00
if ( _can_use_validate_call ( method , arguments ) ) {
// Exact arguments, use validated call.
gen - > write_call_method_bind_validated ( result , base , method , arguments ) ;
2021-05-16 16:48:53 +02:00
} else {
// Not exact arguments, but still can use method bind call.
2023-01-09 13:20:18 +01:00
gen - > write_call_method_bind ( result , base , method , arguments ) ;
2021-05-16 16:48:53 +02:00
}
} else {
2023-01-09 13:20:18 +01:00
gen - > write_call ( result , base , call - > function_name , arguments ) ;
2021-05-16 16:48:53 +02:00
}
} else if ( base . type . has_type & & base . type . kind = = GDScriptDataType : : BUILTIN ) {
2023-01-09 13:20:18 +01:00
gen - > write_call_builtin_type ( result , base , base . type . builtin_type , call - > function_name , arguments ) ;
2020-11-17 14:44:52 +01:00
} else {
2023-01-09 13:20:18 +01:00
gen - > write_call ( result , base , call - > function_name , arguments ) ;
2020-11-17 14:44:52 +01:00
}
2021-05-16 16:48:53 +02:00
if ( base . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
gen - > pop_temporary ( ) ;
}
2014-02-10 02:10:30 +01:00
}
2020-05-02 00:14:56 +02:00
} else {
_set_error ( " Cannot call something that isn't a function. " , call - > callee ) ;
2020-08-07 19:51:56 +02:00
r_error = ERR_COMPILATION_FAILED ;
return GDScriptCodeGenerator : : Address ( ) ;
2014-02-10 02:10:30 +01:00
}
} else {
2020-08-07 19:51:56 +02:00
r_error = ERR_COMPILATION_FAILED ;
return GDScriptCodeGenerator : : Address ( ) ;
2020-05-02 00:14:56 +02:00
}
}
2020-08-07 19:51:56 +02:00
}
2014-02-10 02:10:30 +01:00
2020-08-07 19:51:56 +02:00
for ( int i = 0 ; i < arguments . size ( ) ; i + + ) {
if ( arguments [ i ] . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
gen - > pop_temporary ( ) ;
2020-05-02 00:14:56 +02:00
}
}
2020-08-07 19:51:56 +02:00
return result ;
2020-05-02 00:14:56 +02:00
} break ;
case GDScriptParser : : Node : : GET_NODE : {
const GDScriptParser : : GetNodeNode * get_node = static_cast < const GDScriptParser : : GetNodeNode * > ( p_expression ) ;
2014-02-10 02:10:30 +01:00
2020-08-07 19:51:56 +02:00
Vector < GDScriptCodeGenerator : : Address > args ;
2022-05-26 17:56:39 +02:00
args . push_back ( codegen . add_constant ( NodePath ( get_node - > full_path ) ) ) ;
2020-08-07 19:51:56 +02:00
2022-11-08 12:51:20 +01:00
GDScriptCodeGenerator : : Address result = codegen . add_temporary ( _gdtype_from_datatype ( get_node - > get_datatype ( ) , codegen . script ) ) ;
2020-08-07 19:51:56 +02:00
MethodBind * get_node_method = ClassDB : : get_method ( " Node " , " get_node " ) ;
2023-07-25 17:42:07 +02:00
gen - > write_call_method_bind_validated ( result , GDScriptCodeGenerator : : Address ( GDScriptCodeGenerator : : Address : : SELF ) , get_node_method , args ) ;
2014-02-10 02:10:30 +01:00
2020-08-07 19:51:56 +02:00
return result ;
2020-05-02 00:14:56 +02:00
} break ;
case GDScriptParser : : Node : : PRELOAD : {
const GDScriptParser : : PreloadNode * preload = static_cast < const GDScriptParser : : PreloadNode * > ( p_expression ) ;
2014-09-15 16:33:30 +02:00
2020-05-02 00:14:56 +02:00
// Add resource as constant.
2020-08-07 19:51:56 +02:00
return codegen . add_constant ( preload - > resource ) ;
2020-05-02 00:14:56 +02:00
} break ;
case GDScriptParser : : Node : : AWAIT : {
const GDScriptParser : : AwaitNode * await = static_cast < const GDScriptParser : : AwaitNode * > ( p_expression ) ;
2014-09-15 16:33:30 +02:00
2022-11-08 12:51:20 +01:00
GDScriptCodeGenerator : : Address result = codegen . add_temporary ( _gdtype_from_datatype ( p_expression - > get_datatype ( ) , codegen . script ) ) ;
2023-03-01 07:47:09 +01:00
GDScriptParser : : ExpressionNode * previous_awaited_node = awaited_node ;
awaited_node = await - > to_await ;
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address argument = _parse_expression ( codegen , r_error , await - > to_await ) ;
2023-03-01 07:47:09 +01:00
awaited_node = previous_awaited_node ;
2020-08-07 19:51:56 +02:00
if ( r_error ) {
return GDScriptCodeGenerator : : Address ( ) ;
2020-05-02 00:14:56 +02:00
}
2020-08-07 19:51:56 +02:00
gen - > write_await ( result , argument ) ;
if ( argument . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
gen - > pop_temporary ( ) ;
2020-05-02 00:14:56 +02:00
}
2014-09-15 16:33:30 +02:00
2020-08-07 19:51:56 +02:00
return result ;
2020-05-02 00:14:56 +02:00
} break ;
2020-08-07 19:51:56 +02:00
// Indexing operator.
2020-05-02 00:14:56 +02:00
case GDScriptParser : : Node : : SUBSCRIPT : {
const GDScriptParser : : SubscriptNode * subscript = static_cast < const GDScriptParser : : SubscriptNode * > ( p_expression ) ;
2022-11-08 12:51:20 +01:00
GDScriptCodeGenerator : : Address result = codegen . add_temporary ( _gdtype_from_datatype ( subscript - > get_datatype ( ) , codegen . script ) ) ;
2014-02-10 02:10:30 +01:00
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address base = _parse_expression ( codegen , r_error , subscript - > base ) ;
if ( r_error ) {
return GDScriptCodeGenerator : : Address ( ) ;
2020-05-02 00:14:56 +02:00
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
bool named = subscript - > is_attribute ;
2020-08-07 19:51:56 +02:00
StringName name ;
GDScriptCodeGenerator : : Address index ;
2024-02-27 11:25:33 +01:00
if ( subscript - > is_attribute ) {
2020-06-01 21:41:05 +02:00
if ( subscript - > base - > type = = GDScriptParser : : Node : : SELF & & codegen . script ) {
2020-05-02 00:14:56 +02:00
GDScriptParser : : IdentifierNode * identifier = subscript - > attribute ;
2022-05-13 15:04:37 +02:00
HashMap < StringName , GDScript : : MemberInfo > : : Iterator MI = codegen . script - > member_indices . find ( identifier - > name ) ;
2016-06-25 15:59:39 +02:00
# ifdef DEBUG_ENABLED
2022-05-13 15:04:37 +02:00
if ( MI & & MI - > value . getter = = codegen . function_name ) {
2020-05-02 00:14:56 +02:00
String n = identifier - > name ;
_set_error ( " Must use ' " + n + " ' instead of 'self. " + n + " ' in getter. " , identifier ) ;
2020-08-07 19:51:56 +02:00
r_error = ERR_COMPILATION_FAILED ;
return GDScriptCodeGenerator : : Address ( ) ;
2020-05-02 00:14:56 +02:00
}
2015-04-18 22:55:04 +02:00
# endif
2016-06-25 15:59:39 +02:00
2022-05-13 15:04:37 +02:00
if ( MI & & MI - > value . getter = = " " ) {
2020-08-07 19:51:56 +02:00
// Remove result temp as we don't need it.
gen - > pop_temporary ( ) ;
// Faster than indexing self (as if no self. had been used).
2022-11-08 12:51:20 +01:00
return GDScriptCodeGenerator : : Address ( GDScriptCodeGenerator : : Address : : MEMBER , MI - > value . index , _gdtype_from_datatype ( subscript - > get_datatype ( ) , codegen . script ) ) ;
2020-05-02 00:14:56 +02:00
}
}
2014-02-10 02:10:30 +01:00
2020-08-07 19:51:56 +02:00
name = subscript - > attribute - > name ;
named = true ;
2020-05-02 00:14:56 +02:00
} else {
2021-04-23 20:42:33 +02:00
if ( subscript - > index - > is_constant & & subscript - > index - > reduced_value . get_type ( ) = = Variant : : STRING_NAME ) {
2020-08-07 19:51:56 +02:00
// Also, somehow, named (speed up anyway).
2021-04-23 20:42:33 +02:00
name = subscript - > index - > reduced_value ;
2020-05-02 00:14:56 +02:00
named = true ;
} else {
2020-08-07 19:51:56 +02:00
// Regular indexing.
index = _parse_expression ( codegen , r_error , subscript - > index ) ;
if ( r_error ) {
return GDScriptCodeGenerator : : Address ( ) ;
2020-05-02 00:14:56 +02:00
}
}
}
2014-02-10 02:10:30 +01:00
2020-08-07 19:51:56 +02:00
if ( named ) {
gen - > write_get_named ( result , name , base ) ;
} else {
gen - > write_get ( result , index , base ) ;
}
if ( index . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
gen - > pop_temporary ( ) ;
}
if ( base . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
gen - > pop_temporary ( ) ;
}
return result ;
2020-05-02 00:14:56 +02:00
} break ;
case GDScriptParser : : Node : : UNARY_OPERATOR : {
const GDScriptParser : : UnaryOpNode * unary = static_cast < const GDScriptParser : : UnaryOpNode * > ( p_expression ) ;
2020-08-07 19:51:56 +02:00
2022-11-08 12:51:20 +01:00
GDScriptCodeGenerator : : Address result = codegen . add_temporary ( _gdtype_from_datatype ( unary - > get_datatype ( ) , codegen . script ) ) ;
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address operand = _parse_expression ( codegen , r_error , unary - > operand ) ;
if ( r_error ) {
return GDScriptCodeGenerator : : Address ( ) ;
2020-05-02 00:14:56 +02:00
}
2020-08-07 19:51:56 +02:00
2020-11-26 18:41:55 +01:00
gen - > write_unary_operator ( result , unary - > variant_op , operand ) ;
2020-08-07 19:51:56 +02:00
if ( operand . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
gen - > pop_temporary ( ) ;
}
return result ;
2020-05-02 00:14:56 +02:00
}
case GDScriptParser : : Node : : BINARY_OPERATOR : {
const GDScriptParser : : BinaryOpNode * binary = static_cast < const GDScriptParser : : BinaryOpNode * > ( p_expression ) ;
2022-11-08 12:51:20 +01:00
GDScriptCodeGenerator : : Address result = codegen . add_temporary ( _gdtype_from_datatype ( binary - > get_datatype ( ) , codegen . script ) ) ;
2020-08-07 19:51:56 +02:00
2020-05-02 00:14:56 +02:00
switch ( binary - > operation ) {
case GDScriptParser : : BinaryOpNode : : OP_LOGIC_AND : {
2020-08-07 19:51:56 +02:00
// AND operator with early out on failure.
GDScriptCodeGenerator : : Address left_operand = _parse_expression ( codegen , r_error , binary - > left_operand ) ;
gen - > write_and_left_operand ( left_operand ) ;
GDScriptCodeGenerator : : Address right_operand = _parse_expression ( codegen , r_error , binary - > right_operand ) ;
gen - > write_and_right_operand ( right_operand ) ;
gen - > write_end_and ( result ) ;
2014-02-10 02:10:30 +01:00
2020-08-07 19:51:56 +02:00
if ( right_operand . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
gen - > pop_temporary ( ) ;
2020-05-14 16:41:43 +02:00
}
2020-08-07 19:51:56 +02:00
if ( left_operand . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
gen - > pop_temporary ( ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
} break ;
2020-05-02 00:14:56 +02:00
case GDScriptParser : : BinaryOpNode : : OP_LOGIC_OR : {
2020-08-07 19:51:56 +02:00
// OR operator with early out on success.
GDScriptCodeGenerator : : Address left_operand = _parse_expression ( codegen , r_error , binary - > left_operand ) ;
gen - > write_or_left_operand ( left_operand ) ;
GDScriptCodeGenerator : : Address right_operand = _parse_expression ( codegen , r_error , binary - > right_operand ) ;
gen - > write_or_right_operand ( right_operand ) ;
2014-02-10 02:10:30 +01:00
2020-08-07 19:51:56 +02:00
gen - > write_end_or ( result ) ;
if ( right_operand . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
gen - > pop_temporary ( ) ;
2020-05-14 16:41:43 +02:00
}
2020-08-07 19:51:56 +02:00
if ( left_operand . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
gen - > pop_temporary ( ) ;
2020-05-14 16:41:43 +02:00
}
2016-08-25 20:18:35 +02:00
} break ;
2020-08-07 19:51:56 +02:00
default : {
GDScriptCodeGenerator : : Address left_operand = _parse_expression ( codegen , r_error , binary - > left_operand ) ;
GDScriptCodeGenerator : : Address right_operand = _parse_expression ( codegen , r_error , binary - > right_operand ) ;
2017-03-05 16:44:50 +01:00
2020-11-26 18:41:55 +01:00
gen - > write_binary_operator ( result , binary - > variant_op , left_operand , right_operand ) ;
2016-08-25 20:18:35 +02:00
2020-08-07 19:51:56 +02:00
if ( right_operand . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
gen - > pop_temporary ( ) ;
2020-05-14 16:41:43 +02:00
}
2020-08-07 19:51:56 +02:00
if ( left_operand . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
gen - > pop_temporary ( ) ;
2020-05-14 16:41:43 +02:00
}
2020-08-07 19:51:56 +02:00
}
2020-05-02 00:14:56 +02:00
}
2020-08-07 19:51:56 +02:00
return result ;
2020-05-02 00:14:56 +02:00
} break ;
case GDScriptParser : : Node : : TERNARY_OPERATOR : {
2020-08-07 19:51:56 +02:00
// x IF a ELSE y operator with early out on failure.
2020-05-02 00:14:56 +02:00
const GDScriptParser : : TernaryOpNode * ternary = static_cast < const GDScriptParser : : TernaryOpNode * > ( p_expression ) ;
2022-11-08 12:51:20 +01:00
GDScriptCodeGenerator : : Address result = codegen . add_temporary ( _gdtype_from_datatype ( ternary - > get_datatype ( ) , codegen . script ) ) ;
2020-08-07 19:51:56 +02:00
gen - > write_start_ternary ( result ) ;
GDScriptCodeGenerator : : Address condition = _parse_expression ( codegen , r_error , ternary - > condition ) ;
if ( r_error ) {
return GDScriptCodeGenerator : : Address ( ) ;
2020-05-02 00:14:56 +02:00
}
2020-08-07 19:51:56 +02:00
gen - > write_ternary_condition ( condition ) ;
2020-05-02 00:14:56 +02:00
2020-08-07 19:51:56 +02:00
if ( condition . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
gen - > pop_temporary ( ) ;
2020-05-02 00:14:56 +02:00
}
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address true_expr = _parse_expression ( codegen , r_error , ternary - > true_expr ) ;
if ( r_error ) {
return GDScriptCodeGenerator : : Address ( ) ;
}
gen - > write_ternary_true_expr ( true_expr ) ;
if ( true_expr . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
gen - > pop_temporary ( ) ;
}
2020-05-02 00:14:56 +02:00
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address false_expr = _parse_expression ( codegen , r_error , ternary - > false_expr ) ;
if ( r_error ) {
return GDScriptCodeGenerator : : Address ( ) ;
}
gen - > write_ternary_false_expr ( false_expr ) ;
if ( false_expr . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
gen - > pop_temporary ( ) ;
}
2020-05-02 00:14:56 +02:00
2020-08-07 19:51:56 +02:00
gen - > write_end_ternary ( ) ;
2020-05-02 00:14:56 +02:00
2020-08-07 19:51:56 +02:00
return result ;
2020-05-02 00:14:56 +02:00
} break ;
2023-02-17 00:16:24 +01:00
case GDScriptParser : : Node : : TYPE_TEST : {
const GDScriptParser : : TypeTestNode * type_test = static_cast < const GDScriptParser : : TypeTestNode * > ( p_expression ) ;
GDScriptCodeGenerator : : Address result = codegen . add_temporary ( _gdtype_from_datatype ( type_test - > get_datatype ( ) , codegen . script ) ) ;
GDScriptCodeGenerator : : Address operand = _parse_expression ( codegen , r_error , type_test - > operand ) ;
2023-09-12 20:55:55 +02:00
GDScriptDataType test_type = _gdtype_from_datatype ( type_test - > test_datatype , codegen . script , false ) ;
2023-02-17 00:16:24 +01:00
if ( r_error ) {
return GDScriptCodeGenerator : : Address ( ) ;
}
if ( test_type . has_type ) {
gen - > write_type_test ( result , operand , test_type ) ;
} else {
gen - > write_assign_true ( result ) ;
}
if ( operand . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
gen - > pop_temporary ( ) ;
}
return result ;
} break ;
2020-05-02 00:14:56 +02:00
case GDScriptParser : : Node : : ASSIGNMENT : {
const GDScriptParser : : AssignmentNode * assignment = static_cast < const GDScriptParser : : AssignmentNode * > ( p_expression ) ;
if ( assignment - > assignee - > type = = GDScriptParser : : Node : : SUBSCRIPT ) {
// SET (chained) MODE!
const GDScriptParser : : SubscriptNode * subscript = static_cast < GDScriptParser : : SubscriptNode * > ( assignment - > assignee ) ;
2015-04-18 22:55:04 +02:00
# ifdef DEBUG_ENABLED
2020-08-07 19:51:56 +02:00
if ( subscript - > is_attribute & & subscript - > base - > type = = GDScriptParser : : Node : : SELF & & codegen . script ) {
2022-05-13 15:04:37 +02:00
HashMap < StringName , GDScript : : MemberInfo > : : Iterator MI = codegen . script - > member_indices . find ( subscript - > attribute - > name ) ;
if ( MI & & MI - > value . setter = = codegen . function_name ) {
2020-08-07 19:51:56 +02:00
String n = subscript - > attribute - > name ;
_set_error ( " Must use ' " + n + " ' instead of 'self. " + n + " ' in setter. " , subscript ) ;
r_error = ERR_COMPILATION_FAILED ;
return GDScriptCodeGenerator : : Address ( ) ;
2020-05-02 00:14:56 +02:00
}
}
2015-04-18 22:55:04 +02:00
# endif
2020-05-02 00:14:56 +02:00
/* Find chain of sets */
2014-02-10 02:10:30 +01:00
2021-12-28 00:52:44 +01:00
StringName assign_class_member_property ;
GDScriptCodeGenerator : : Address target_member_property ;
bool is_member_property = false ;
bool member_property_has_setter = false ;
bool member_property_is_in_setter = false ;
2023-04-19 16:10:35 +02:00
bool is_static = false ;
2023-05-16 12:03:53 +02:00
GDScriptCodeGenerator : : Address static_var_class ;
int static_var_index = 0 ;
GDScriptDataType static_var_data_type ;
StringName var_name ;
2021-12-28 00:52:44 +01:00
StringName member_property_setter_function ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
List < const GDScriptParser : : SubscriptNode * > chain ;
2017-01-04 21:37:45 +01:00
2020-05-02 00:14:56 +02:00
{
2020-08-07 19:51:56 +02:00
// Create get/set chain.
2020-05-02 00:14:56 +02:00
const GDScriptParser : : SubscriptNode * n = subscript ;
while ( true ) {
chain . push_back ( n ) ;
if ( n - > base - > type ! = GDScriptParser : : Node : : SUBSCRIPT ) {
2021-12-28 00:52:44 +01:00
// Check for a property.
2020-05-02 00:14:56 +02:00
if ( n - > base - > type = = GDScriptParser : : Node : : IDENTIFIER ) {
GDScriptParser : : IdentifierNode * identifier = static_cast < GDScriptParser : : IdentifierNode * > ( n - > base ) ;
2023-05-16 12:03:53 +02:00
var_name = identifier - > name ;
2021-12-28 00:52:44 +01:00
if ( _is_class_member_property ( codegen , var_name ) ) {
assign_class_member_property = var_name ;
2023-05-16 12:03:53 +02:00
} else if ( ! _is_local_or_parameter ( codegen , var_name ) ) {
if ( codegen . script - > member_indices . has ( var_name ) ) {
is_member_property = true ;
is_static = false ;
const GDScript : : MemberInfo & minfo = codegen . script - > member_indices [ var_name ] ;
member_property_setter_function = minfo . setter ;
member_property_has_setter = member_property_setter_function ! = StringName ( ) ;
member_property_is_in_setter = member_property_has_setter & & member_property_setter_function = = codegen . function_name ;
target_member_property . mode = GDScriptCodeGenerator : : Address : : MEMBER ;
target_member_property . address = minfo . index ;
target_member_property . type = minfo . data_type ;
} else {
// Try static variables.
GDScript * scr = codegen . script ;
while ( scr ) {
if ( scr - > static_variables_indices . has ( var_name ) ) {
is_member_property = true ;
is_static = true ;
const GDScript : : MemberInfo & minfo = scr - > static_variables_indices [ var_name ] ;
member_property_setter_function = minfo . setter ;
member_property_has_setter = member_property_setter_function ! = StringName ( ) ;
member_property_is_in_setter = member_property_has_setter & & member_property_setter_function = = codegen . function_name ;
static_var_class = codegen . add_constant ( scr ) ;
static_var_index = minfo . index ;
static_var_data_type = minfo . data_type ;
break ;
}
scr = scr - > _base ;
}
}
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
2020-05-02 00:14:56 +02:00
break ;
2014-02-10 02:10:30 +01:00
}
2020-05-02 00:14:56 +02:00
n = static_cast < const GDScriptParser : : SubscriptNode * > ( n - > base ) ;
}
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
/* Chain of gets */
2014-02-10 02:10:30 +01:00
2020-08-07 19:51:56 +02:00
// Get at (potential) root stack pos, so it can be returned.
GDScriptCodeGenerator : : Address base = _parse_expression ( codegen , r_error , chain . back ( ) - > get ( ) - > base ) ;
2024-07-23 21:23:38 +02:00
const bool base_known_type = base . type . has_type ;
const bool base_is_shared = Variant : : is_type_shared ( base . type . builtin_type ) ;
2024-07-09 17:45:07 +02:00
2020-08-07 19:51:56 +02:00
if ( r_error ) {
return GDScriptCodeGenerator : : Address ( ) ;
2020-05-02 00:14:56 +02:00
}
2014-02-10 02:10:30 +01:00
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address prev_base = base ;
2017-01-04 21:37:45 +01:00
2024-07-09 17:45:07 +02:00
// In case the base has a setter, don't use the address directly, as we want to call that setter.
// So use a temp value instead and call the setter at the end.
GDScriptCodeGenerator : : Address base_temp ;
2024-07-23 21:23:38 +02:00
if ( ( ! base_known_type | | ! base_is_shared ) & & base . mode = = GDScriptCodeGenerator : : Address : : MEMBER & & member_property_has_setter & & ! member_property_is_in_setter ) {
2024-07-09 17:45:07 +02:00
base_temp = codegen . add_temporary ( base . type ) ;
gen - > write_assign ( base_temp , base ) ;
prev_base = base_temp ;
}
2020-08-07 19:51:56 +02:00
struct ChainInfo {
bool is_named = false ;
GDScriptCodeGenerator : : Address base ;
GDScriptCodeGenerator : : Address key ;
StringName name ;
} ;
2014-02-10 02:10:30 +01:00
2020-08-07 19:51:56 +02:00
List < ChainInfo > set_chain ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
for ( List < const GDScriptParser : : SubscriptNode * > : : Element * E = chain . back ( ) ; E ; E = E - > prev ( ) ) {
2020-08-07 19:51:56 +02:00
if ( E = = chain . front ( ) ) {
// Skip the main subscript, since we'll assign to that.
2020-05-02 00:14:56 +02:00
break ;
}
const GDScriptParser : : SubscriptNode * subscript_elem = E - > get ( ) ;
2022-11-08 12:51:20 +01:00
GDScriptCodeGenerator : : Address value = codegen . add_temporary ( _gdtype_from_datatype ( subscript_elem - > get_datatype ( ) , codegen . script ) ) ;
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address key ;
StringName name ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
if ( subscript_elem - > is_attribute ) {
2020-08-07 19:51:56 +02:00
name = subscript_elem - > attribute - > name ;
gen - > write_get_named ( value , name , prev_base ) ;
2020-05-02 00:14:56 +02:00
} else {
2020-08-07 19:51:56 +02:00
key = _parse_expression ( codegen , r_error , subscript_elem - > index ) ;
if ( r_error ) {
return GDScriptCodeGenerator : : Address ( ) ;
2020-05-02 00:14:56 +02:00
}
2020-08-07 19:51:56 +02:00
gen - > write_get ( value , key , prev_base ) ;
2020-05-02 00:14:56 +02:00
}
2017-01-04 21:37:45 +01:00
2020-08-07 19:51:56 +02:00
// Store base and key for setting it back later.
set_chain . push_front ( { subscript_elem - > is_attribute , prev_base , key , name } ) ; // Push to front to invert the list.
prev_base = value ;
2020-05-02 00:14:56 +02:00
}
2014-02-10 02:10:30 +01:00
2020-08-07 19:51:56 +02:00
// Get value to assign.
GDScriptCodeGenerator : : Address assigned = _parse_expression ( codegen , r_error , assignment - > assigned_value ) ;
if ( r_error ) {
return GDScriptCodeGenerator : : Address ( ) ;
}
// Get the key if needed.
GDScriptCodeGenerator : : Address key ;
StringName name ;
2020-05-02 00:14:56 +02:00
if ( subscript - > is_attribute ) {
2020-08-07 19:51:56 +02:00
name = subscript - > attribute - > name ;
2020-05-02 00:14:56 +02:00
} else {
2020-08-07 19:51:56 +02:00
key = _parse_expression ( codegen , r_error , subscript - > index ) ;
if ( r_error ) {
return GDScriptCodeGenerator : : Address ( ) ;
}
2020-05-02 00:14:56 +02:00
}
2014-02-10 02:10:30 +01:00
2020-08-07 19:51:56 +02:00
// Perform operator if any.
if ( assignment - > operation ! = GDScriptParser : : AssignmentNode : : OP_NONE ) {
2022-11-08 12:51:20 +01:00
GDScriptCodeGenerator : : Address op_result = codegen . add_temporary ( _gdtype_from_datatype ( assignment - > get_datatype ( ) , codegen . script ) ) ;
GDScriptCodeGenerator : : Address value = codegen . add_temporary ( _gdtype_from_datatype ( subscript - > get_datatype ( ) , codegen . script ) ) ;
2020-08-07 19:51:56 +02:00
if ( subscript - > is_attribute ) {
gen - > write_get_named ( value , name , prev_base ) ;
} else {
gen - > write_get ( value , key , prev_base ) ;
}
2021-12-26 20:32:22 +01:00
gen - > write_binary_operator ( op_result , assignment - > variant_op , value , assigned ) ;
gen - > pop_temporary ( ) ;
2020-08-07 19:51:56 +02:00
if ( assigned . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
gen - > pop_temporary ( ) ;
}
2021-12-26 20:32:22 +01:00
assigned = op_result ;
2020-05-02 00:14:56 +02:00
}
2014-02-10 02:10:30 +01:00
2020-08-07 19:51:56 +02:00
// Perform assignment.
if ( subscript - > is_attribute ) {
gen - > write_set_named ( prev_base , name , assigned ) ;
} else {
gen - > write_set ( prev_base , key , assigned ) ;
2020-05-02 00:14:56 +02:00
}
2021-05-01 11:44:39 +02:00
if ( key . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
gen - > pop_temporary ( ) ;
}
2020-08-07 19:51:56 +02:00
if ( assigned . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
gen - > pop_temporary ( ) ;
2020-05-02 00:14:56 +02:00
}
2017-01-04 21:37:45 +01:00
2020-08-07 19:51:56 +02:00
assigned = prev_base ;
2017-01-04 21:37:45 +01:00
2020-08-07 19:51:56 +02:00
// Set back the values into their bases.
2021-07-24 15:46:25 +02:00
for ( const ChainInfo & info : set_chain ) {
2022-06-27 17:09:51 +02:00
bool known_type = assigned . type . has_type ;
bool is_shared = Variant : : is_type_shared ( assigned . type . builtin_type ) ;
2022-06-28 17:45:27 +02:00
if ( ! known_type | | ! is_shared ) {
if ( ! known_type ) {
// Jump shared values since they are already updated in-place.
gen - > write_jump_if_shared ( assigned ) ;
}
2022-06-27 17:09:51 +02:00
if ( ! info . is_named ) {
gen - > write_set ( info . base , info . key , assigned ) ;
} else {
gen - > write_set_named ( info . base , info . name , assigned ) ;
2020-08-07 19:51:56 +02:00
}
2022-06-28 17:45:27 +02:00
if ( ! known_type ) {
gen - > write_end_jump_if_shared ( ) ;
}
2022-06-27 17:09:51 +02:00
}
2022-06-28 17:45:27 +02:00
if ( ! info . is_named & & info . key . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
gen - > pop_temporary ( ) ;
2020-08-07 19:51:56 +02:00
}
if ( assigned . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
gen - > pop_temporary ( ) ;
}
assigned = info . base ;
2020-05-02 00:14:56 +02:00
}
2017-01-04 21:37:45 +01:00
2022-06-27 17:09:51 +02:00
bool known_type = assigned . type . has_type ;
bool is_shared = Variant : : is_type_shared ( assigned . type . builtin_type ) ;
if ( ! known_type | | ! is_shared ) {
// If this is a class member property, also assign to it.
// This allow things like: position.x += 2.0
if ( assign_class_member_property ! = StringName ( ) ) {
if ( ! known_type ) {
gen - > write_jump_if_shared ( assigned ) ;
}
gen - > write_set_member ( assigned , assign_class_member_property ) ;
if ( ! known_type ) {
gen - > write_end_jump_if_shared ( ) ;
}
} else if ( is_member_property ) {
// Same as above but for script members.
if ( ! known_type ) {
gen - > write_jump_if_shared ( assigned ) ;
}
if ( member_property_has_setter & & ! member_property_is_in_setter ) {
Vector < GDScriptCodeGenerator : : Address > args ;
args . push_back ( assigned ) ;
2023-05-16 12:03:53 +02:00
GDScriptCodeGenerator : : Address call_base = is_static ? GDScriptCodeGenerator : : Address ( GDScriptCodeGenerator : : Address : : CLASS ) : GDScriptCodeGenerator : : Address ( GDScriptCodeGenerator : : Address : : SELF ) ;
gen - > write_call ( GDScriptCodeGenerator : : Address ( ) , call_base , member_property_setter_function , args ) ;
} else if ( is_static ) {
GDScriptCodeGenerator : : Address temp = codegen . add_temporary ( static_var_data_type ) ;
gen - > write_assign ( temp , assigned ) ;
gen - > write_set_static_variable ( temp , static_var_class , static_var_index ) ;
gen - > pop_temporary ( ) ;
2022-06-27 17:09:51 +02:00
} else {
gen - > write_assign ( target_member_property , assigned ) ;
}
if ( ! known_type ) {
gen - > write_end_jump_if_shared ( ) ;
}
2021-12-28 00:52:44 +01:00
}
2024-07-09 17:45:07 +02:00
} else if ( base_temp . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
2024-07-23 21:23:38 +02:00
if ( ! base_known_type ) {
gen - > write_jump_if_shared ( base ) ;
}
2024-07-09 17:45:07 +02:00
// Save the temp value back to the base by calling its setter.
gen - > write_call ( GDScriptCodeGenerator : : Address ( ) , base , member_property_setter_function , { assigned } ) ;
2024-07-23 21:23:38 +02:00
if ( ! base_known_type ) {
gen - > write_end_jump_if_shared ( ) ;
}
2020-08-07 19:51:56 +02:00
}
2017-01-04 21:37:45 +01:00
2020-08-07 19:51:56 +02:00
if ( assigned . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
gen - > pop_temporary ( ) ;
}
2020-05-02 00:14:56 +02:00
} else if ( assignment - > assignee - > type = = GDScriptParser : : Node : : IDENTIFIER & & _is_class_member_property ( codegen , static_cast < GDScriptParser : : IdentifierNode * > ( assignment - > assignee ) - > name ) ) {
2020-08-07 19:51:56 +02:00
// Assignment to member property.
2021-09-21 21:23:42 +02:00
GDScriptCodeGenerator : : Address assigned_value = _parse_expression ( codegen , r_error , assignment - > assigned_value ) ;
2020-08-07 19:51:56 +02:00
if ( r_error ) {
return GDScriptCodeGenerator : : Address ( ) ;
2020-05-02 00:14:56 +02:00
}
2021-09-21 21:23:42 +02:00
GDScriptCodeGenerator : : Address to_assign = assigned_value ;
bool has_operation = assignment - > operation ! = GDScriptParser : : AssignmentNode : : OP_NONE ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
StringName name = static_cast < GDScriptParser : : IdentifierNode * > ( assignment - > assignee ) - > name ;
2014-02-10 02:10:30 +01:00
2021-09-21 21:23:42 +02:00
if ( has_operation ) {
2022-11-08 12:51:20 +01:00
GDScriptCodeGenerator : : Address op_result = codegen . add_temporary ( _gdtype_from_datatype ( assignment - > get_datatype ( ) , codegen . script ) ) ;
GDScriptCodeGenerator : : Address member = codegen . add_temporary ( _gdtype_from_datatype ( assignment - > assignee - > get_datatype ( ) , codegen . script ) ) ;
2020-08-07 19:51:56 +02:00
gen - > write_get_member ( member , name ) ;
2021-09-21 21:23:42 +02:00
gen - > write_binary_operator ( op_result , assignment - > variant_op , member , assigned_value ) ;
gen - > pop_temporary ( ) ; // Pop member temp.
to_assign = op_result ;
2020-08-07 19:51:56 +02:00
}
2014-02-10 02:10:30 +01:00
2021-09-21 21:23:42 +02:00
gen - > write_set_member ( to_assign , name ) ;
2014-02-10 02:10:30 +01:00
2021-09-21 21:23:42 +02:00
if ( to_assign . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
gen - > pop_temporary ( ) ; // Pop the assigned expression or the temp result if it has operation.
}
if ( has_operation & & assigned_value . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
gen - > pop_temporary ( ) ; // Pop the assigned expression if not done before.
2020-08-07 19:51:56 +02:00
}
} else {
// Regular assignment.
2021-12-28 00:51:18 +01:00
ERR_FAIL_COND_V_MSG ( assignment - > assignee - > type ! = GDScriptParser : : Node : : IDENTIFIER , GDScriptCodeGenerator : : Address ( ) , " Expected the assignee to be an identifier here. " ) ;
GDScriptCodeGenerator : : Address member ;
bool is_member = false ;
2020-06-01 21:41:05 +02:00
bool has_setter = false ;
bool is_in_setter = false ;
2023-04-19 16:10:35 +02:00
bool is_static = false ;
2023-05-16 12:03:53 +02:00
GDScriptCodeGenerator : : Address static_var_class ;
int static_var_index = 0 ;
GDScriptDataType static_var_data_type ;
StringName var_name ;
2020-06-01 21:41:05 +02:00
StringName setter_function ;
2023-05-16 12:03:53 +02:00
var_name = static_cast < const GDScriptParser : : IdentifierNode * > ( assignment - > assignee ) - > name ;
if ( ! _is_local_or_parameter ( codegen , var_name ) ) {
if ( codegen . script - > member_indices . has ( var_name ) ) {
is_member = true ;
is_static = false ;
GDScript : : MemberInfo & minfo = codegen . script - > member_indices [ var_name ] ;
setter_function = minfo . setter ;
has_setter = setter_function ! = StringName ( ) ;
is_in_setter = has_setter & & setter_function = = codegen . function_name ;
member . mode = GDScriptCodeGenerator : : Address : : MEMBER ;
member . address = minfo . index ;
member . type = minfo . data_type ;
} else {
// Try static variables.
GDScript * scr = codegen . script ;
while ( scr ) {
if ( scr - > static_variables_indices . has ( var_name ) ) {
is_member = true ;
is_static = true ;
GDScript : : MemberInfo & minfo = scr - > static_variables_indices [ var_name ] ;
setter_function = minfo . setter ;
has_setter = setter_function ! = StringName ( ) ;
is_in_setter = has_setter & & setter_function = = codegen . function_name ;
static_var_class = codegen . add_constant ( scr ) ;
static_var_index = minfo . index ;
static_var_data_type = minfo . data_type ;
break ;
}
scr = scr - > _base ;
}
}
2020-05-02 00:14:56 +02:00
}
2021-12-28 00:51:18 +01:00
GDScriptCodeGenerator : : Address target ;
if ( is_member ) {
target = member ; // _parse_expression could call its getter, but we want to know the actual address
2020-06-01 21:41:05 +02:00
} else {
2020-08-07 19:51:56 +02:00
target = _parse_expression ( codegen , r_error , assignment - > assignee ) ;
if ( r_error ) {
return GDScriptCodeGenerator : : Address ( ) ;
2020-06-01 21:41:05 +02:00
}
2020-08-07 19:51:56 +02:00
}
2020-06-01 21:41:05 +02:00
2021-09-29 15:53:47 +02:00
GDScriptCodeGenerator : : Address assigned_value = _parse_expression ( codegen , r_error , assignment - > assigned_value ) ;
2020-08-07 19:51:56 +02:00
if ( r_error ) {
return GDScriptCodeGenerator : : Address ( ) ;
2020-05-02 00:14:56 +02:00
}
2021-09-29 15:53:47 +02:00
GDScriptCodeGenerator : : Address to_assign ;
bool has_operation = assignment - > operation ! = GDScriptParser : : AssignmentNode : : OP_NONE ;
if ( has_operation ) {
2020-08-07 19:51:56 +02:00
// Perform operation.
2023-01-25 07:46:32 +01:00
GDScriptCodeGenerator : : Address op_result = codegen . add_temporary ( _gdtype_from_datatype ( assignment - > get_datatype ( ) , codegen . script ) ) ;
2023-04-10 12:03:18 +02:00
GDScriptCodeGenerator : : Address og_value = _parse_expression ( codegen , r_error , assignment - > assignee ) ;
2021-09-29 15:53:47 +02:00
gen - > write_binary_operator ( op_result , assignment - > variant_op , og_value , assigned_value ) ;
to_assign = op_result ;
if ( og_value . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
gen - > pop_temporary ( ) ;
}
2020-08-07 19:51:56 +02:00
} else {
2021-09-29 15:53:47 +02:00
to_assign = assigned_value ;
2020-05-02 00:14:56 +02:00
}
2020-06-01 21:41:05 +02:00
if ( has_setter & & ! is_in_setter ) {
// Call setter.
2020-08-07 19:51:56 +02:00
Vector < GDScriptCodeGenerator : : Address > args ;
2021-09-29 15:53:47 +02:00
args . push_back ( to_assign ) ;
2023-05-16 12:03:53 +02:00
GDScriptCodeGenerator : : Address call_base = is_static ? GDScriptCodeGenerator : : Address ( GDScriptCodeGenerator : : Address : : CLASS ) : GDScriptCodeGenerator : : Address ( GDScriptCodeGenerator : : Address : : SELF ) ;
gen - > write_call ( GDScriptCodeGenerator : : Address ( ) , call_base , setter_function , args ) ;
} else if ( is_static ) {
GDScriptCodeGenerator : : Address temp = codegen . add_temporary ( static_var_data_type ) ;
if ( assignment - > use_conversion_assign ) {
gen - > write_assign_with_conversion ( temp , to_assign ) ;
} else {
gen - > write_assign ( temp , to_assign ) ;
}
gen - > write_set_static_variable ( temp , static_var_class , static_var_index ) ;
gen - > pop_temporary ( ) ;
2020-08-07 19:51:56 +02:00
} else {
// Just assign.
2021-05-26 19:05:31 +02:00
if ( assignment - > use_conversion_assign ) {
2021-09-29 15:53:47 +02:00
gen - > write_assign_with_conversion ( target , to_assign ) ;
2021-05-26 19:05:31 +02:00
} else {
2021-09-29 15:53:47 +02:00
gen - > write_assign ( target , to_assign ) ;
2021-05-26 19:05:31 +02:00
}
2020-08-07 19:51:56 +02:00
}
2021-09-29 15:53:47 +02:00
if ( to_assign . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
gen - > pop_temporary ( ) ; // Pop assigned value or temp operation result.
2020-08-07 19:51:56 +02:00
}
2021-09-29 15:53:47 +02:00
if ( has_operation & & assigned_value . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
gen - > pop_temporary ( ) ; // Pop assigned value if not done before.
2020-08-07 19:51:56 +02:00
}
if ( target . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
2021-09-29 15:53:47 +02:00
gen - > pop_temporary ( ) ; // Pop the target to assignment.
2020-08-07 19:51:56 +02:00
}
2020-05-02 00:14:56 +02:00
}
2020-08-07 19:51:56 +02:00
return GDScriptCodeGenerator : : Address ( ) ; // Assignment does not return a value.
2020-05-02 00:14:56 +02:00
} break ;
2021-03-28 16:03:13 +02:00
case GDScriptParser : : Node : : LAMBDA : {
const GDScriptParser : : LambdaNode * lambda = static_cast < const GDScriptParser : : LambdaNode * > ( p_expression ) ;
2022-11-08 12:51:20 +01:00
GDScriptCodeGenerator : : Address result = codegen . add_temporary ( _gdtype_from_datatype ( lambda - > get_datatype ( ) , codegen . script ) ) ;
2021-03-28 16:03:13 +02:00
Vector < GDScriptCodeGenerator : : Address > captures ;
captures . resize ( lambda - > captures . size ( ) ) ;
for ( int i = 0 ; i < lambda - > captures . size ( ) ; i + + ) {
captures . write [ i ] = _parse_expression ( codegen , r_error , lambda - > captures [ i ] ) ;
if ( r_error ) {
return GDScriptCodeGenerator : : Address ( ) ;
}
}
GDScriptFunction * function = _parse_function ( r_error , codegen . script , codegen . class_node , lambda - > function , false , true ) ;
if ( r_error ) {
return GDScriptCodeGenerator : : Address ( ) ;
}
2024-01-03 01:14:50 +01:00
codegen . script - > lambda_info . insert ( function , { ( int ) lambda - > captures . size ( ) , lambda - > use_self } ) ;
2022-04-20 19:22:22 +02:00
gen - > write_lambda ( result , function , captures , lambda - > use_self ) ;
2021-03-28 16:03:13 +02:00
for ( int i = 0 ; i < captures . size ( ) ; i + + ) {
if ( captures [ i ] . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
gen - > pop_temporary ( ) ;
}
}
return result ;
} break ;
2020-05-02 00:14:56 +02:00
default : {
2020-08-07 19:51:56 +02:00
ERR_FAIL_V_MSG ( GDScriptCodeGenerator : : Address ( ) , " Bug in bytecode compiler, unexpected node in parse tree while parsing expression. " ) ; // Unreachable code.
2020-05-02 00:14:56 +02:00
} break ;
}
}
2014-02-10 02:10:30 +01:00
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address GDScriptCompiler : : _parse_match_pattern ( CodeGen & codegen , Error & r_error , const GDScriptParser : : PatternNode * p_pattern , const GDScriptCodeGenerator : : Address & p_value_addr , const GDScriptCodeGenerator : : Address & p_type_addr , const GDScriptCodeGenerator : : Address & p_previous_test , bool p_is_first , bool p_is_nested ) {
2020-05-02 00:14:56 +02:00
switch ( p_pattern - > pattern_type ) {
case GDScriptParser : : PatternNode : : PT_LITERAL : {
2020-08-07 19:51:56 +02:00
if ( p_is_nested ) {
codegen . generator - > write_and_left_operand ( p_previous_test ) ;
} else if ( ! p_is_first ) {
codegen . generator - > write_or_left_operand ( p_previous_test ) ;
}
2020-05-02 00:14:56 +02:00
// Get literal type into constant map.
2023-05-15 13:06:43 +02:00
Variant : : Type literal_type = p_pattern - > literal - > value . get_type ( ) ;
GDScriptCodeGenerator : : Address literal_type_addr = codegen . add_constant ( literal_type ) ;
2014-02-10 02:10:30 +01:00
2020-08-07 19:51:56 +02:00
// Equality is always a boolean.
GDScriptDataType equality_type ;
equality_type . has_type = true ;
equality_type . kind = GDScriptDataType : : BUILTIN ;
equality_type . builtin_type = Variant : : BOOL ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
// Check type equality.
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address type_equality_addr = codegen . add_temporary ( equality_type ) ;
2020-11-26 18:41:55 +01:00
codegen . generator - > write_binary_operator ( type_equality_addr , Variant : : OP_EQUAL , p_type_addr , literal_type_addr ) ;
2022-12-06 03:46:47 +01:00
2023-05-15 13:06:43 +02:00
if ( literal_type = = Variant : : STRING ) {
GDScriptCodeGenerator : : Address type_stringname_addr = codegen . add_constant ( Variant : : STRING_NAME ) ;
// Check StringName <-> String type equality.
GDScriptCodeGenerator : : Address tmp_comp_addr = codegen . add_temporary ( equality_type ) ;
codegen . generator - > write_binary_operator ( tmp_comp_addr , Variant : : OP_EQUAL , p_type_addr , type_stringname_addr ) ;
codegen . generator - > write_binary_operator ( type_equality_addr , Variant : : OP_OR , type_equality_addr , tmp_comp_addr ) ;
codegen . generator - > pop_temporary ( ) ; // Remove tmp_comp_addr from stack.
} else if ( literal_type = = Variant : : STRING_NAME ) {
GDScriptCodeGenerator : : Address type_string_addr = codegen . add_constant ( Variant : : STRING ) ;
2022-12-06 03:46:47 +01:00
2023-05-15 13:06:43 +02:00
// Check String <-> StringName type equality.
GDScriptCodeGenerator : : Address tmp_comp_addr = codegen . add_temporary ( equality_type ) ;
2022-12-06 03:46:47 +01:00
2023-05-15 13:06:43 +02:00
codegen . generator - > write_binary_operator ( tmp_comp_addr , Variant : : OP_EQUAL , p_type_addr , type_string_addr ) ;
codegen . generator - > write_binary_operator ( type_equality_addr , Variant : : OP_OR , type_equality_addr , tmp_comp_addr ) ;
2022-12-06 03:46:47 +01:00
2023-05-15 13:06:43 +02:00
codegen . generator - > pop_temporary ( ) ; // Remove tmp_comp_addr from stack.
}
2022-12-06 03:46:47 +01:00
2020-08-07 19:51:56 +02:00
codegen . generator - > write_and_left_operand ( type_equality_addr ) ;
2020-05-02 00:14:56 +02:00
// Get literal.
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address literal_addr = _parse_expression ( codegen , r_error , p_pattern - > literal ) ;
if ( r_error ) {
return GDScriptCodeGenerator : : Address ( ) ;
}
2020-05-02 00:14:56 +02:00
// Check value equality.
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address equality_addr = codegen . add_temporary ( equality_type ) ;
2020-11-26 18:41:55 +01:00
codegen . generator - > write_binary_operator ( equality_addr , Variant : : OP_EQUAL , p_value_addr , literal_addr ) ;
2020-08-07 19:51:56 +02:00
codegen . generator - > write_and_right_operand ( equality_addr ) ;
// AND both together (reuse temporary location).
codegen . generator - > write_end_and ( type_equality_addr ) ;
codegen . generator - > pop_temporary ( ) ; // Remove equality_addr from stack.
if ( literal_addr . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
codegen . generator - > pop_temporary ( ) ;
}
// If this isn't the first, we need to OR with the previous pattern. If it's nested, we use AND instead.
if ( p_is_nested ) {
// Use the previous value as target, since we only need one temporary variable.
codegen . generator - > write_and_right_operand ( type_equality_addr ) ;
codegen . generator - > write_end_and ( p_previous_test ) ;
} else if ( ! p_is_first ) {
// Use the previous value as target, since we only need one temporary variable.
codegen . generator - > write_or_right_operand ( type_equality_addr ) ;
codegen . generator - > write_end_or ( p_previous_test ) ;
} else {
// Just assign this value to the accumulator temporary.
codegen . generator - > write_assign ( p_previous_test , type_equality_addr ) ;
}
codegen . generator - > pop_temporary ( ) ; // Remove type_equality_addr.
return p_previous_test ;
2020-05-02 00:14:56 +02:00
} break ;
case GDScriptParser : : PatternNode : : PT_EXPRESSION : {
2020-08-07 19:51:56 +02:00
if ( p_is_nested ) {
codegen . generator - > write_and_left_operand ( p_previous_test ) ;
} else if ( ! p_is_first ) {
codegen . generator - > write_or_left_operand ( p_previous_test ) ;
}
2023-05-15 13:06:43 +02:00
GDScriptCodeGenerator : : Address type_string_addr = codegen . add_constant ( Variant : : STRING ) ;
GDScriptCodeGenerator : : Address type_stringname_addr = codegen . add_constant ( Variant : : STRING_NAME ) ;
// Equality is always a boolean.
GDScriptDataType equality_type ;
equality_type . has_type = true ;
equality_type . kind = GDScriptDataType : : BUILTIN ;
equality_type . builtin_type = Variant : : BOOL ;
2020-08-07 19:51:56 +02:00
// Create the result temps first since it's the last to go away.
2023-05-15 13:06:43 +02:00
GDScriptCodeGenerator : : Address result_addr = codegen . add_temporary ( equality_type ) ;
GDScriptCodeGenerator : : Address equality_test_addr = codegen . add_temporary ( equality_type ) ;
GDScriptCodeGenerator : : Address stringy_comp_addr = codegen . add_temporary ( equality_type ) ;
GDScriptCodeGenerator : : Address stringy_comp_addr_2 = codegen . add_temporary ( equality_type ) ;
GDScriptCodeGenerator : : Address expr_type_addr = codegen . add_temporary ( ) ;
2020-08-07 19:51:56 +02:00
2020-05-02 00:14:56 +02:00
// Evaluate expression.
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address expr_addr ;
expr_addr = _parse_expression ( codegen , r_error , p_pattern - > expression ) ;
if ( r_error ) {
return GDScriptCodeGenerator : : Address ( ) ;
2020-05-02 00:14:56 +02:00
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
// Evaluate expression type.
2020-08-07 19:51:56 +02:00
Vector < GDScriptCodeGenerator : : Address > typeof_args ;
typeof_args . push_back ( expr_addr ) ;
2023-05-15 13:06:43 +02:00
codegen . generator - > write_call_utility ( expr_type_addr , " typeof " , typeof_args ) ;
2020-05-02 00:14:56 +02:00
// Check type equality.
2023-05-15 13:06:43 +02:00
codegen . generator - > write_binary_operator ( result_addr , Variant : : OP_EQUAL , p_type_addr , expr_type_addr ) ;
// Check for String <-> StringName comparison.
codegen . generator - > write_binary_operator ( stringy_comp_addr , Variant : : OP_EQUAL , p_type_addr , type_string_addr ) ;
codegen . generator - > write_binary_operator ( stringy_comp_addr_2 , Variant : : OP_EQUAL , expr_type_addr , type_stringname_addr ) ;
codegen . generator - > write_binary_operator ( stringy_comp_addr , Variant : : OP_AND , stringy_comp_addr , stringy_comp_addr_2 ) ;
codegen . generator - > write_binary_operator ( result_addr , Variant : : OP_OR , result_addr , stringy_comp_addr ) ;
// Check for StringName <-> String comparison.
codegen . generator - > write_binary_operator ( stringy_comp_addr , Variant : : OP_EQUAL , p_type_addr , type_stringname_addr ) ;
codegen . generator - > write_binary_operator ( stringy_comp_addr_2 , Variant : : OP_EQUAL , expr_type_addr , type_string_addr ) ;
codegen . generator - > write_binary_operator ( stringy_comp_addr , Variant : : OP_AND , stringy_comp_addr , stringy_comp_addr_2 ) ;
codegen . generator - > write_binary_operator ( result_addr , Variant : : OP_OR , result_addr , stringy_comp_addr ) ;
codegen . generator - > pop_temporary ( ) ; // Remove expr_type_addr from stack.
codegen . generator - > pop_temporary ( ) ; // Remove stringy_comp_addr_2 from stack.
codegen . generator - > pop_temporary ( ) ; // Remove stringy_comp_addr from stack.
2020-08-07 19:51:56 +02:00
codegen . generator - > write_and_left_operand ( result_addr ) ;
2020-05-02 00:14:56 +02:00
// Check value equality.
2021-03-02 19:18:29 +01:00
codegen . generator - > write_binary_operator ( equality_test_addr , Variant : : OP_EQUAL , p_value_addr , expr_addr ) ;
2020-08-07 19:51:56 +02:00
codegen . generator - > write_and_right_operand ( equality_test_addr ) ;
2014-02-10 02:10:30 +01:00
2020-08-07 19:51:56 +02:00
// AND both type and value equality.
codegen . generator - > write_end_and ( result_addr ) ;
// We don't need the expression temporary anymore.
if ( expr_addr . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
codegen . generator - > pop_temporary ( ) ;
}
2023-05-15 13:06:43 +02:00
codegen . generator - > pop_temporary ( ) ; // Remove equality_test_addr from stack.
2020-08-07 19:51:56 +02:00
// If this isn't the first, we need to OR with the previous pattern. If it's nested, we use AND instead.
if ( p_is_nested ) {
// Use the previous value as target, since we only need one temporary variable.
codegen . generator - > write_and_right_operand ( result_addr ) ;
codegen . generator - > write_end_and ( p_previous_test ) ;
} else if ( ! p_is_first ) {
// Use the previous value as target, since we only need one temporary variable.
codegen . generator - > write_or_right_operand ( result_addr ) ;
codegen . generator - > write_end_or ( p_previous_test ) ;
} else {
// Just assign this value to the accumulator temporary.
codegen . generator - > write_assign ( p_previous_test , result_addr ) ;
}
codegen . generator - > pop_temporary ( ) ; // Remove temp result addr.
return p_previous_test ;
2020-05-02 00:14:56 +02:00
} break ;
case GDScriptParser : : PatternNode : : PT_ARRAY : {
2020-08-07 19:51:56 +02:00
if ( p_is_nested ) {
codegen . generator - > write_and_left_operand ( p_previous_test ) ;
} else if ( ! p_is_first ) {
codegen . generator - > write_or_left_operand ( p_previous_test ) ;
}
2020-05-02 00:14:56 +02:00
// Get array type into constant map.
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address array_type_addr = codegen . add_constant ( ( int ) Variant : : ARRAY ) ;
// Equality is always a boolean.
GDScriptDataType temp_type ;
temp_type . has_type = true ;
temp_type . kind = GDScriptDataType : : BUILTIN ;
temp_type . builtin_type = Variant : : BOOL ;
2020-05-02 00:14:56 +02:00
// Check type equality.
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address result_addr = codegen . add_temporary ( temp_type ) ;
2020-11-26 18:41:55 +01:00
codegen . generator - > write_binary_operator ( result_addr , Variant : : OP_EQUAL , p_type_addr , array_type_addr ) ;
2020-08-07 19:51:56 +02:00
codegen . generator - > write_and_left_operand ( result_addr ) ;
2020-05-02 00:14:56 +02:00
// Store pattern length in constant map.
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address array_length_addr = codegen . add_constant ( p_pattern - > rest_used ? p_pattern - > array . size ( ) - 1 : p_pattern - > array . size ( ) ) ;
2020-05-02 00:14:56 +02:00
// Get value length.
2020-08-07 19:51:56 +02:00
temp_type . builtin_type = Variant : : INT ;
GDScriptCodeGenerator : : Address value_length_addr = codegen . add_temporary ( temp_type ) ;
Vector < GDScriptCodeGenerator : : Address > len_args ;
len_args . push_back ( p_value_addr ) ;
2022-12-29 14:47:53 +01:00
codegen . generator - > write_call_gdscript_utility ( value_length_addr , " len " , len_args ) ;
2020-05-02 00:14:56 +02:00
// Test length compatibility.
2020-08-07 19:51:56 +02:00
temp_type . builtin_type = Variant : : BOOL ;
GDScriptCodeGenerator : : Address length_compat_addr = codegen . add_temporary ( temp_type ) ;
2020-11-26 18:41:55 +01:00
codegen . generator - > write_binary_operator ( length_compat_addr , p_pattern - > rest_used ? Variant : : OP_GREATER_EQUAL : Variant : : OP_EQUAL , value_length_addr , array_length_addr ) ;
2020-08-07 19:51:56 +02:00
codegen . generator - > write_and_right_operand ( length_compat_addr ) ;
// AND type and length check.
codegen . generator - > write_end_and ( result_addr ) ;
// Remove length temporaries.
codegen . generator - > pop_temporary ( ) ;
codegen . generator - > pop_temporary ( ) ;
// Create temporaries outside the loop so they can be reused.
GDScriptCodeGenerator : : Address element_addr = codegen . add_temporary ( ) ;
GDScriptCodeGenerator : : Address element_type_addr = codegen . add_temporary ( ) ;
2020-05-02 00:14:56 +02:00
// Evaluate element by element.
for ( int i = 0 ; i < p_pattern - > array . size ( ) ; i + + ) {
if ( p_pattern - > array [ i ] - > pattern_type = = GDScriptParser : : PatternNode : : PT_REST ) {
// Don't want to access an extra element of the user array.
break ;
}
2018-08-26 18:31:23 +02:00
2020-08-07 19:51:56 +02:00
// Use AND here too, as we don't want to be checking elements if previous test failed (which means this might be an invalid get).
2022-03-02 12:05:18 +01:00
codegen . generator - > write_and_left_operand ( result_addr ) ;
2020-08-07 19:51:56 +02:00
2020-05-02 00:14:56 +02:00
// Add index to constant map.
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address index_addr = codegen . add_constant ( i ) ;
2020-05-02 00:14:56 +02:00
// Get the actual element from the user-sent array.
2020-08-07 19:51:56 +02:00
codegen . generator - > write_get ( element_addr , index_addr , p_value_addr ) ;
2020-05-02 00:14:56 +02:00
// Also get type of element.
2020-08-07 19:51:56 +02:00
Vector < GDScriptCodeGenerator : : Address > typeof_args ;
typeof_args . push_back ( element_addr ) ;
2020-11-26 15:56:32 +01:00
codegen . generator - > write_call_utility ( element_type_addr , " typeof " , typeof_args ) ;
2020-05-02 00:14:56 +02:00
// Try the pattern inside the element.
2022-03-02 12:05:18 +01:00
result_addr = _parse_match_pattern ( codegen , r_error , p_pattern - > array [ i ] , element_addr , element_type_addr , result_addr , false , true ) ;
2020-08-07 19:51:56 +02:00
if ( r_error ! = OK ) {
return GDScriptCodeGenerator : : Address ( ) ;
2020-05-02 00:14:56 +02:00
}
2018-08-26 18:31:23 +02:00
2022-03-02 12:05:18 +01:00
codegen . generator - > write_and_right_operand ( result_addr ) ;
codegen . generator - > write_end_and ( result_addr ) ;
2020-05-02 00:14:56 +02:00
}
2020-08-07 19:51:56 +02:00
// Remove element temporaries.
codegen . generator - > pop_temporary ( ) ;
codegen . generator - > pop_temporary ( ) ;
2018-08-26 18:31:23 +02:00
2022-03-02 12:05:18 +01:00
// If this isn't the first, we need to OR with the previous pattern. If it's nested, we use AND instead.
if ( p_is_nested ) {
// Use the previous value as target, since we only need one temporary variable.
codegen . generator - > write_and_right_operand ( result_addr ) ;
codegen . generator - > write_end_and ( p_previous_test ) ;
} else if ( ! p_is_first ) {
// Use the previous value as target, since we only need one temporary variable.
codegen . generator - > write_or_right_operand ( result_addr ) ;
codegen . generator - > write_end_or ( p_previous_test ) ;
} else {
// Just assign this value to the accumulator temporary.
codegen . generator - > write_assign ( p_previous_test , result_addr ) ;
}
codegen . generator - > pop_temporary ( ) ; // Remove temp result addr.
return p_previous_test ;
2020-05-02 00:14:56 +02:00
} break ;
case GDScriptParser : : PatternNode : : PT_DICTIONARY : {
2020-08-07 19:51:56 +02:00
if ( p_is_nested ) {
codegen . generator - > write_and_left_operand ( p_previous_test ) ;
} else if ( ! p_is_first ) {
codegen . generator - > write_or_left_operand ( p_previous_test ) ;
}
2020-05-02 00:14:56 +02:00
// Get dictionary type into constant map.
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address dict_type_addr = codegen . add_constant ( ( int ) Variant : : DICTIONARY ) ;
// Equality is always a boolean.
GDScriptDataType temp_type ;
temp_type . has_type = true ;
temp_type . kind = GDScriptDataType : : BUILTIN ;
temp_type . builtin_type = Variant : : BOOL ;
2020-05-02 00:14:56 +02:00
// Check type equality.
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address result_addr = codegen . add_temporary ( temp_type ) ;
2020-11-26 18:41:55 +01:00
codegen . generator - > write_binary_operator ( result_addr , Variant : : OP_EQUAL , p_type_addr , dict_type_addr ) ;
2020-08-07 19:51:56 +02:00
codegen . generator - > write_and_left_operand ( result_addr ) ;
2020-05-02 00:14:56 +02:00
// Store pattern length in constant map.
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address dict_length_addr = codegen . add_constant ( p_pattern - > rest_used ? p_pattern - > dictionary . size ( ) - 1 : p_pattern - > dictionary . size ( ) ) ;
2020-05-02 00:14:56 +02:00
// Get user's dictionary length.
2020-08-07 19:51:56 +02:00
temp_type . builtin_type = Variant : : INT ;
GDScriptCodeGenerator : : Address value_length_addr = codegen . add_temporary ( temp_type ) ;
Vector < GDScriptCodeGenerator : : Address > func_args ;
func_args . push_back ( p_value_addr ) ;
2022-12-29 14:47:53 +01:00
codegen . generator - > write_call_gdscript_utility ( value_length_addr , " len " , func_args ) ;
2020-05-02 00:14:56 +02:00
// Test length compatibility.
2020-08-07 19:51:56 +02:00
temp_type . builtin_type = Variant : : BOOL ;
GDScriptCodeGenerator : : Address length_compat_addr = codegen . add_temporary ( temp_type ) ;
2020-11-26 18:41:55 +01:00
codegen . generator - > write_binary_operator ( length_compat_addr , p_pattern - > rest_used ? Variant : : OP_GREATER_EQUAL : Variant : : OP_EQUAL , value_length_addr , dict_length_addr ) ;
2020-08-07 19:51:56 +02:00
codegen . generator - > write_and_right_operand ( length_compat_addr ) ;
// AND type and length check.
codegen . generator - > write_end_and ( result_addr ) ;
// Remove length temporaries.
codegen . generator - > pop_temporary ( ) ;
codegen . generator - > pop_temporary ( ) ;
// Create temporaries outside the loop so they can be reused.
GDScriptCodeGenerator : : Address element_addr = codegen . add_temporary ( ) ;
GDScriptCodeGenerator : : Address element_type_addr = codegen . add_temporary ( ) ;
2020-05-02 00:14:56 +02:00
// Evaluate element by element.
for ( int i = 0 ; i < p_pattern - > dictionary . size ( ) ; i + + ) {
const GDScriptParser : : PatternNode : : Pair & element = p_pattern - > dictionary [ i ] ;
2020-07-16 03:02:44 +02:00
if ( element . value_pattern & & element . value_pattern - > pattern_type = = GDScriptParser : : PatternNode : : PT_REST ) {
2020-05-02 00:14:56 +02:00
// Ignore rest pattern.
2020-08-07 19:51:56 +02:00
break ;
2020-05-02 00:14:56 +02:00
}
2020-08-07 19:51:56 +02:00
// Use AND here too, as we don't want to be checking elements if previous test failed (which means this might be an invalid get).
2022-03-02 12:05:18 +01:00
codegen . generator - > write_and_left_operand ( result_addr ) ;
2018-08-26 18:31:23 +02:00
2020-05-02 00:14:56 +02:00
// Get the pattern key.
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address pattern_key_addr = _parse_expression ( codegen , r_error , element . key ) ;
if ( r_error ) {
return GDScriptCodeGenerator : : Address ( ) ;
2020-05-02 00:14:56 +02:00
}
2014-02-10 02:10:30 +01:00
2020-08-07 19:51:56 +02:00
// Check if pattern key exists in user's dictionary. This will be AND-ed with next result.
func_args . clear ( ) ;
func_args . push_back ( pattern_key_addr ) ;
2022-03-02 12:05:18 +01:00
codegen . generator - > write_call ( result_addr , p_value_addr , " has " , func_args ) ;
2020-05-02 00:14:56 +02:00
2020-07-16 03:02:44 +02:00
if ( element . value_pattern ! = nullptr ) {
2020-08-07 19:51:56 +02:00
// Use AND here too, as we don't want to be checking elements if previous test failed (which means this might be an invalid get).
2022-03-02 12:05:18 +01:00
codegen . generator - > write_and_left_operand ( result_addr ) ;
2020-08-07 19:51:56 +02:00
2020-07-16 03:02:44 +02:00
// Get actual value from user dictionary.
2020-08-07 19:51:56 +02:00
codegen . generator - > write_get ( element_addr , pattern_key_addr , p_value_addr ) ;
2020-07-16 03:02:44 +02:00
// Also get type of value.
2020-08-07 19:51:56 +02:00
func_args . clear ( ) ;
func_args . push_back ( element_addr ) ;
2020-11-26 15:56:32 +01:00
codegen . generator - > write_call_utility ( element_type_addr , " typeof " , func_args ) ;
2020-07-16 03:02:44 +02:00
// Try the pattern inside the value.
2022-03-02 12:05:18 +01:00
result_addr = _parse_match_pattern ( codegen , r_error , element . value_pattern , element_addr , element_type_addr , result_addr , false , true ) ;
2020-08-07 19:51:56 +02:00
if ( r_error ! = OK ) {
return GDScriptCodeGenerator : : Address ( ) ;
2020-07-16 03:02:44 +02:00
}
2022-03-02 12:05:18 +01:00
codegen . generator - > write_and_right_operand ( result_addr ) ;
codegen . generator - > write_end_and ( result_addr ) ;
2020-05-02 00:14:56 +02:00
}
2022-03-02 12:05:18 +01:00
codegen . generator - > write_and_right_operand ( result_addr ) ;
codegen . generator - > write_end_and ( result_addr ) ;
2020-08-07 19:51:56 +02:00
// Remove pattern key temporary.
if ( pattern_key_addr . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
codegen . generator - > pop_temporary ( ) ;
2020-05-02 00:14:56 +02:00
}
2014-02-10 02:10:30 +01:00
}
2020-08-07 19:51:56 +02:00
// Remove element temporaries.
codegen . generator - > pop_temporary ( ) ;
codegen . generator - > pop_temporary ( ) ;
2020-05-02 00:14:56 +02:00
2022-03-02 12:05:18 +01:00
// If this isn't the first, we need to OR with the previous pattern. If it's nested, we use AND instead.
if ( p_is_nested ) {
// Use the previous value as target, since we only need one temporary variable.
codegen . generator - > write_and_right_operand ( result_addr ) ;
codegen . generator - > write_end_and ( p_previous_test ) ;
} else if ( ! p_is_first ) {
// Use the previous value as target, since we only need one temporary variable.
codegen . generator - > write_or_right_operand ( result_addr ) ;
codegen . generator - > write_end_or ( p_previous_test ) ;
} else {
// Just assign this value to the accumulator temporary.
codegen . generator - > write_assign ( p_previous_test , result_addr ) ;
}
codegen . generator - > pop_temporary ( ) ; // Remove temp result addr.
return p_previous_test ;
2014-02-10 02:10:30 +01:00
} break ;
2020-05-02 00:14:56 +02:00
case GDScriptParser : : PatternNode : : PT_REST :
// Do nothing.
2020-08-07 19:51:56 +02:00
return p_previous_test ;
2020-05-02 00:14:56 +02:00
break ;
2020-08-07 19:51:56 +02:00
case GDScriptParser : : PatternNode : : PT_BIND : {
if ( p_is_nested ) {
codegen . generator - > write_and_left_operand ( p_previous_test ) ;
} else if ( ! p_is_first ) {
codegen . generator - > write_or_left_operand ( p_previous_test ) ;
}
// Get the bind address.
GDScriptCodeGenerator : : Address bind = codegen . locals [ p_pattern - > bind - > name ] ;
// Assign value to bound variable.
codegen . generator - > write_assign ( bind , p_value_addr ) ;
}
[[fallthrough]] ; // Act like matching anything too.
2020-05-02 00:14:56 +02:00
case GDScriptParser : : PatternNode : : PT_WILDCARD :
2020-08-07 19:51:56 +02:00
// If this is a fall through we don't want to do this again.
if ( p_pattern - > pattern_type ! = GDScriptParser : : PatternNode : : PT_BIND ) {
if ( p_is_nested ) {
codegen . generator - > write_and_left_operand ( p_previous_test ) ;
} else if ( ! p_is_first ) {
codegen . generator - > write_or_left_operand ( p_previous_test ) ;
}
}
// This matches anything so just do the same as `if(true)`.
// If this isn't the first, we need to OR with the previous pattern. If it's nested, we use AND instead.
if ( p_is_nested ) {
// Use the operator with the `true` constant so it works as always matching.
GDScriptCodeGenerator : : Address constant = codegen . add_constant ( true ) ;
codegen . generator - > write_and_right_operand ( constant ) ;
codegen . generator - > write_end_and ( p_previous_test ) ;
} else if ( ! p_is_first ) {
// Use the operator with the `true` constant so it works as always matching.
GDScriptCodeGenerator : : Address constant = codegen . add_constant ( true ) ;
codegen . generator - > write_or_right_operand ( constant ) ;
codegen . generator - > write_end_or ( p_previous_test ) ;
} else {
// Just assign this value to the accumulator temporary.
codegen . generator - > write_assign_true ( p_previous_test ) ;
}
return p_previous_test ;
2014-02-10 02:10:30 +01:00
}
2020-08-07 19:51:56 +02:00
ERR_FAIL_V_MSG ( p_previous_test , " Reaching the end of pattern compilation without matching a pattern. " ) ;
2014-02-10 02:10:30 +01:00
}
2024-03-28 19:57:56 +01:00
List < GDScriptCodeGenerator : : Address > GDScriptCompiler : : _add_block_locals ( CodeGen & codegen , const GDScriptParser : : SuiteNode * p_block ) {
2023-06-01 20:46:37 +02:00
List < GDScriptCodeGenerator : : Address > addresses ;
2020-08-07 19:51:56 +02:00
for ( int i = 0 ; i < p_block - > locals . size ( ) ; i + + ) {
if ( p_block - > locals [ i ] . type = = GDScriptParser : : SuiteNode : : Local : : PARAMETER | | p_block - > locals [ i ] . type = = GDScriptParser : : SuiteNode : : Local : : FOR_VARIABLE ) {
// Parameters are added directly from function and loop variables are declared explicitly.
continue ;
}
2023-06-01 20:46:37 +02:00
addresses . push_back ( codegen . add_local ( p_block - > locals [ i ] . name , _gdtype_from_datatype ( p_block - > locals [ i ] . get_datatype ( ) , codegen . script ) ) ) ;
2020-08-07 19:51:56 +02:00
}
2023-06-01 20:46:37 +02:00
return addresses ;
2020-08-07 19:51:56 +02:00
}
2024-03-28 19:57:56 +01:00
// Avoid keeping in the stack long-lived references to objects, which may prevent `RefCounted` objects from being freed.
void GDScriptCompiler : : _clear_block_locals ( CodeGen & codegen , const List < GDScriptCodeGenerator : : Address > & p_locals ) {
for ( const GDScriptCodeGenerator : : Address & local : p_locals ) {
if ( local . type . can_contain_object ( ) ) {
codegen . generator - > clear_address ( local ) ;
2023-06-01 20:46:37 +02:00
}
}
}
2024-03-28 19:57:56 +01:00
Error GDScriptCompiler : : _parse_block ( CodeGen & codegen , const GDScriptParser : : SuiteNode * p_block , bool p_add_locals , bool p_clear_locals ) {
2022-09-29 11:53:28 +02:00
Error err = OK ;
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator * gen = codegen . generator ;
2023-06-01 20:46:37 +02:00
List < GDScriptCodeGenerator : : Address > block_locals ;
2020-08-07 19:51:56 +02:00
2024-03-28 19:57:56 +01:00
gen - > clear_temporaries ( ) ;
2020-08-07 19:51:56 +02:00
codegen . start_block ( ) ;
if ( p_add_locals ) {
2024-03-28 19:57:56 +01:00
block_locals = _add_block_locals ( codegen , p_block ) ;
2020-08-07 19:51:56 +02:00
}
2014-02-10 02:10:30 +01:00
for ( int i = 0 ; i < p_block - > statements . size ( ) ; i + + ) {
2017-11-16 18:38:18 +01:00
const GDScriptParser : : Node * s = p_block - > statements [ i ] ;
2014-02-10 02:10:30 +01:00
2016-09-20 13:54:17 +02:00
# ifdef DEBUG_ENABLED
2020-05-02 00:14:56 +02:00
// Add a newline before each statement, since the debugger needs those.
2020-08-07 19:51:56 +02:00
gen - > write_newline ( s - > start_line ) ;
2016-09-20 13:54:17 +02:00
# endif
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
switch ( s - > type ) {
case GDScriptParser : : Node : : MATCH : {
const GDScriptParser : : MatchNode * match = static_cast < const GDScriptParser : : MatchNode * > ( s ) ;
2014-02-10 02:10:30 +01:00
2024-07-25 10:56:30 +02:00
codegen . start_block ( ) ; // Add an extra block, since @special locals belong to the match scope.
2017-03-05 16:44:50 +01:00
2020-08-07 19:51:56 +02:00
// Evaluate the match expression.
2022-11-08 12:51:20 +01:00
GDScriptCodeGenerator : : Address value = codegen . add_local ( " @match_value " , _gdtype_from_datatype ( match - > test - > get_datatype ( ) , codegen . script ) ) ;
2022-09-29 11:53:28 +02:00
GDScriptCodeGenerator : : Address value_expr = _parse_expression ( codegen , err , match - > test ) ;
if ( err ) {
return err ;
2020-05-02 00:14:56 +02:00
}
2017-03-05 16:44:50 +01:00
2020-11-22 16:24:40 +01:00
// Assign to local.
// TODO: This can be improved by passing the target to parse_expression().
2021-03-12 15:04:45 +01:00
gen - > write_assign ( value , value_expr ) ;
2020-11-22 16:24:40 +01:00
2021-03-12 15:04:45 +01:00
if ( value_expr . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
2020-11-22 16:24:40 +01:00
codegen . generator - > pop_temporary ( ) ;
}
2020-05-02 00:14:56 +02:00
// Then, let's save the type of the value in the stack too, so we can reuse for later comparisons.
2020-11-22 16:24:40 +01:00
GDScriptDataType typeof_type ;
typeof_type . has_type = true ;
typeof_type . kind = GDScriptDataType : : BUILTIN ;
typeof_type . builtin_type = Variant : : INT ;
GDScriptCodeGenerator : : Address type = codegen . add_local ( " @match_type " , typeof_type ) ;
2020-08-07 19:51:56 +02:00
Vector < GDScriptCodeGenerator : : Address > typeof_args ;
typeof_args . push_back ( value ) ;
2020-11-26 15:56:32 +01:00
gen - > write_call_utility ( type , " typeof " , typeof_args ) ;
2017-03-05 16:44:50 +01:00
2020-05-02 00:14:56 +02:00
// Now we can actually start testing.
// For each branch.
for ( int j = 0 ; j < match - > branches . size ( ) ; j + + ) {
2020-08-07 19:51:56 +02:00
if ( j > 0 ) {
// Use `else` to not check the next branch after matching.
gen - > write_else ( ) ;
}
2020-05-02 00:14:56 +02:00
const GDScriptParser : : MatchBranchNode * branch = match - > branches [ j ] ;
2017-03-05 16:44:50 +01:00
2024-07-25 10:56:30 +02:00
codegen . start_block ( ) ; // Add an extra block, since binds belong to the match branch scope.
2020-08-07 19:51:56 +02:00
// Add locals in block before patterns, so temporaries don't use the stack address for binds.
2024-03-28 19:57:56 +01:00
List < GDScriptCodeGenerator : : Address > branch_locals = _add_block_locals ( codegen , branch - > block ) ;
2017-03-05 16:44:50 +01:00
2020-05-02 00:14:56 +02:00
# ifdef DEBUG_ENABLED
// Add a newline before each branch, since the debugger needs those.
2020-08-07 19:51:56 +02:00
gen - > write_newline ( branch - > start_line ) ;
2020-05-02 00:14:56 +02:00
# endif
// For each pattern in branch.
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address pattern_result = codegen . add_temporary ( ) ;
2020-05-02 00:14:56 +02:00
for ( int k = 0 ; k < branch - > patterns . size ( ) ; k + + ) {
2022-09-29 11:53:28 +02:00
pattern_result = _parse_match_pattern ( codegen , err , branch - > patterns [ k ] , value , type , pattern_result , k = = 0 , false ) ;
if ( err ! = OK ) {
return err ;
2016-10-16 13:20:28 +02:00
}
2020-05-02 00:14:56 +02:00
}
2017-03-05 16:44:50 +01:00
2023-07-31 12:47:26 +02:00
// If there's a guard, check its condition too.
if ( branch - > guard_body ! = nullptr ) {
// Do this first so the guard does not run unless the pattern matched.
gen - > write_and_left_operand ( pattern_result ) ;
// Don't actually use the block for the guard.
// The binds are already in the locals and we don't want to clear the result of the guard condition before we check the actual match.
GDScriptCodeGenerator : : Address guard_result = _parse_expression ( codegen , err , static_cast < GDScriptParser : : ExpressionNode * > ( branch - > guard_body - > statements [ 0 ] ) ) ;
if ( err ) {
return err ;
}
gen - > write_and_right_operand ( guard_result ) ;
gen - > write_end_and ( pattern_result ) ;
if ( guard_result . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
codegen . generator - > pop_temporary ( ) ;
}
}
2020-08-07 19:51:56 +02:00
// Check if pattern did match.
gen - > write_if ( pattern_result ) ;
2018-08-26 03:19:02 +02:00
2020-08-07 19:51:56 +02:00
// Remove the result from stack.
gen - > pop_temporary ( ) ;
2017-03-05 16:44:50 +01:00
2020-08-07 19:51:56 +02:00
// Parse the branch block.
2022-09-29 11:53:28 +02:00
err = _parse_block ( codegen , branch - > block , false ) ; // Don't add locals again.
if ( err ) {
return err ;
2020-05-02 00:14:56 +02:00
}
2014-02-10 02:10:30 +01:00
2024-03-28 19:57:56 +01:00
_clear_block_locals ( codegen , branch_locals ) ;
2023-06-01 20:46:37 +02:00
2024-07-25 10:56:30 +02:00
codegen . end_block ( ) ; // Get out of extra block for binds.
2020-05-02 00:14:56 +02:00
}
2020-08-07 19:51:56 +02:00
// End all nested `if`s.
for ( int j = 0 ; j < match - > branches . size ( ) ; j + + ) {
gen - > write_endif ( ) ;
2020-05-02 00:14:56 +02:00
}
2024-07-25 10:56:30 +02:00
codegen . end_block ( ) ; // Get out of extra block for match's @special locals.
2020-08-07 19:51:56 +02:00
} break ;
2020-05-02 00:14:56 +02:00
case GDScriptParser : : Node : : IF : {
const GDScriptParser : : IfNode * if_n = static_cast < const GDScriptParser : : IfNode * > ( s ) ;
2022-09-29 11:53:28 +02:00
GDScriptCodeGenerator : : Address condition = _parse_expression ( codegen , err , if_n - > condition ) ;
if ( err ) {
return err ;
2020-05-02 00:14:56 +02:00
}
2014-02-10 02:10:30 +01:00
2020-08-07 19:51:56 +02:00
gen - > write_if ( condition ) ;
2014-02-10 02:10:30 +01:00
2020-08-07 19:51:56 +02:00
if ( condition . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
codegen . generator - > pop_temporary ( ) ;
}
2022-09-29 11:53:28 +02:00
err = _parse_block ( codegen , if_n - > true_block ) ;
if ( err ) {
return err ;
2020-05-02 00:14:56 +02:00
}
2020-04-21 06:35:11 +02:00
2020-05-02 00:14:56 +02:00
if ( if_n - > false_block ) {
2020-08-07 19:51:56 +02:00
gen - > write_else ( ) ;
2014-02-10 02:10:30 +01:00
2022-09-29 11:53:28 +02:00
err = _parse_block ( codegen , if_n - > false_block ) ;
if ( err ) {
return err ;
2020-08-07 19:51:56 +02:00
}
2020-05-02 00:14:56 +02:00
}
2014-02-10 02:10:30 +01:00
2020-08-07 19:51:56 +02:00
gen - > write_endif ( ) ;
2020-05-02 00:14:56 +02:00
} break ;
case GDScriptParser : : Node : : FOR : {
const GDScriptParser : : ForNode * for_n = static_cast < const GDScriptParser : : ForNode * > ( s ) ;
2014-02-10 02:10:30 +01:00
2024-07-25 10:56:30 +02:00
// Add an extra block, since the iterator and @special locals belong to the loop scope.
// Also we use custom logic to clear block locals.
codegen . start_block ( ) ;
2024-03-28 19:57:56 +01:00
2022-11-08 12:51:20 +01:00
GDScriptCodeGenerator : : Address iterator = codegen . add_local ( for_n - > variable - > name , _gdtype_from_datatype ( for_n - > variable - > get_datatype ( ) , codegen . script ) ) ;
2020-08-07 19:51:56 +02:00
2022-11-08 12:51:20 +01:00
gen - > start_for ( iterator . type , _gdtype_from_datatype ( for_n - > list - > get_datatype ( ) , codegen . script ) ) ;
2020-11-22 16:24:40 +01:00
2022-09-29 11:53:28 +02:00
GDScriptCodeGenerator : : Address list = _parse_expression ( codegen , err , for_n - > list ) ;
if ( err ) {
return err ;
2020-08-07 19:51:56 +02:00
}
2023-08-04 11:19:11 +02:00
gen - > write_for_assignment ( list ) ;
2020-11-22 16:24:40 +01:00
if ( list . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
codegen . generator - > pop_temporary ( ) ;
}
2023-08-04 11:19:11 +02:00
gen - > write_for ( iterator , for_n - > use_conversion_assign ) ;
2020-08-07 19:51:56 +02:00
2024-03-28 19:57:56 +01:00
// Loop variables must be cleared even when `break`/`continue` is used.
List < GDScriptCodeGenerator : : Address > loop_locals = _add_block_locals ( codegen , for_n - > loop ) ;
//_clear_block_locals(codegen, loop_locals); // Inside loop, before block - for `continue`. // TODO
err = _parse_block ( codegen , for_n - > loop , false ) ; // Don't add locals again.
2022-09-29 11:53:28 +02:00
if ( err ) {
return err ;
2020-08-07 19:51:56 +02:00
}
gen - > write_endfor ( ) ;
2024-03-28 19:57:56 +01:00
_clear_block_locals ( codegen , loop_locals ) ; // Outside loop, after block - for `break` and normal exit.
2024-07-25 10:56:30 +02:00
codegen . end_block ( ) ; // Get out of extra block for loop iterator, @special locals, and custom locals clearing.
2020-05-02 00:14:56 +02:00
} break ;
case GDScriptParser : : Node : : WHILE : {
const GDScriptParser : : WhileNode * while_n = static_cast < const GDScriptParser : : WhileNode * > ( s ) ;
2020-08-07 19:51:56 +02:00
2024-07-25 10:56:30 +02:00
codegen . start_block ( ) ; // Add an extra block, since we use custom logic to clear block locals.
2020-08-07 19:51:56 +02:00
gen - > start_while_condition ( ) ;
2022-09-29 11:53:28 +02:00
GDScriptCodeGenerator : : Address condition = _parse_expression ( codegen , err , while_n - > condition ) ;
if ( err ) {
return err ;
2020-05-02 00:14:56 +02:00
}
2020-08-07 19:51:56 +02:00
gen - > write_while ( condition ) ;
if ( condition . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
codegen . generator - > pop_temporary ( ) ;
2020-05-02 00:14:56 +02:00
}
2014-02-10 02:10:30 +01:00
2024-03-28 19:57:56 +01:00
// Loop variables must be cleared even when `break`/`continue` is used.
List < GDScriptCodeGenerator : : Address > loop_locals = _add_block_locals ( codegen , while_n - > loop ) ;
//_clear_block_locals(codegen, loop_locals); // Inside loop, before block - for `continue`. // TODO
err = _parse_block ( codegen , while_n - > loop , false ) ; // Don't add locals again.
2022-09-29 11:53:28 +02:00
if ( err ) {
return err ;
2020-08-07 19:51:56 +02:00
}
2014-02-10 02:10:30 +01:00
2020-08-07 19:51:56 +02:00
gen - > write_endwhile ( ) ;
2024-03-28 19:57:56 +01:00
_clear_block_locals ( codegen , loop_locals ) ; // Outside loop, after block - for `break` and normal exit.
2024-07-25 10:56:30 +02:00
codegen . end_block ( ) ; // Get out of extra block for custom locals clearing.
2020-05-02 00:14:56 +02:00
} break ;
case GDScriptParser : : Node : : BREAK : {
2020-08-07 19:51:56 +02:00
gen - > write_break ( ) ;
2020-05-02 00:14:56 +02:00
} break ;
case GDScriptParser : : Node : : CONTINUE : {
2023-01-21 17:33:05 +01:00
gen - > write_continue ( ) ;
2020-05-02 00:14:56 +02:00
} break ;
case GDScriptParser : : Node : : RETURN : {
const GDScriptParser : : ReturnNode * return_n = static_cast < const GDScriptParser : : ReturnNode * > ( s ) ;
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address return_value ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
if ( return_n - > return_value ! = nullptr ) {
2022-09-29 11:53:28 +02:00
return_value = _parse_expression ( codegen , err , return_n - > return_value ) ;
if ( err ) {
return err ;
2020-05-02 00:14:56 +02:00
}
2014-02-10 02:10:30 +01:00
}
2020-05-02 00:14:56 +02:00
2023-01-27 21:54:07 +01:00
if ( return_n - > void_return ) {
// Always return "null", even if the expression is a call to a void function.
gen - > write_return ( codegen . add_constant ( Variant ( ) ) ) ;
} else {
gen - > write_return ( return_value ) ;
}
2020-08-07 19:51:56 +02:00
if ( return_value . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
codegen . generator - > pop_temporary ( ) ;
}
2014-02-10 02:10:30 +01:00
} break ;
2020-05-02 00:14:56 +02:00
case GDScriptParser : : Node : : ASSERT : {
2017-03-13 00:25:29 +01:00
# ifdef DEBUG_ENABLED
2017-11-16 18:38:18 +01:00
const GDScriptParser : : AssertNode * as = static_cast < const GDScriptParser : : AssertNode * > ( s ) ;
2014-02-10 02:10:30 +01:00
2022-09-29 11:53:28 +02:00
GDScriptCodeGenerator : : Address condition = _parse_expression ( codegen , err , as - > condition ) ;
if ( err ) {
return err ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address message ;
2019-08-06 13:28:22 +02:00
if ( as - > message ) {
2022-09-29 11:53:28 +02:00
message = _parse_expression ( codegen , err , as - > message ) ;
if ( err ) {
return err ;
2020-05-14 16:41:43 +02:00
}
2019-08-06 13:28:22 +02:00
}
2020-08-07 19:51:56 +02:00
gen - > write_assert ( condition , message ) ;
2019-08-06 13:28:22 +02:00
2020-08-07 19:51:56 +02:00
if ( condition . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
codegen . generator - > pop_temporary ( ) ;
}
if ( message . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
codegen . generator - > pop_temporary ( ) ;
}
2017-03-13 00:25:29 +01:00
# endif
2014-02-10 02:10:30 +01:00
} break ;
2020-05-02 00:14:56 +02:00
case GDScriptParser : : Node : : BREAKPOINT : {
2016-09-20 13:54:17 +02:00
# ifdef DEBUG_ENABLED
2020-08-07 19:51:56 +02:00
gen - > write_breakpoint ( ) ;
2016-09-20 13:54:17 +02:00
# endif
2015-12-29 16:11:21 +01:00
} break ;
2020-05-02 00:14:56 +02:00
case GDScriptParser : : Node : : VARIABLE : {
const GDScriptParser : : VariableNode * lv = static_cast < const GDScriptParser : : VariableNode * > ( s ) ;
2020-08-07 19:51:56 +02:00
// Should be already in stack when the block began.
GDScriptCodeGenerator : : Address local = codegen . locals [ lv - > identifier - > name ] ;
2022-11-08 12:51:20 +01:00
GDScriptDataType local_type = _gdtype_from_datatype ( lv - > get_datatype ( ) , codegen . script ) ;
2014-02-10 02:10:30 +01:00
2022-11-28 17:29:02 +01:00
bool initialized = false ;
2020-05-02 00:14:56 +02:00
if ( lv - > initializer ! = nullptr ) {
2022-09-29 11:53:28 +02:00
GDScriptCodeGenerator : : Address src_address = _parse_expression ( codegen , err , lv - > initializer ) ;
if ( err ) {
return err ;
2020-05-02 00:14:56 +02:00
}
2021-05-26 19:05:31 +02:00
if ( lv - > use_conversion_assign ) {
gen - > write_assign_with_conversion ( local , src_address ) ;
} else {
gen - > write_assign ( local , src_address ) ;
}
2020-08-07 19:51:56 +02:00
if ( src_address . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
codegen . generator - > pop_temporary ( ) ;
2020-07-16 03:02:44 +02:00
}
2022-11-28 17:29:02 +01:00
initialized = true ;
2024-03-28 19:57:56 +01:00
} else if ( ( local_type . has_type & & local_type . kind = = GDScriptDataType : : BUILTIN ) | | codegen . generator - > is_local_dirty ( local ) ) {
// Initialize with default for the type. Built-in types must always be cleared (they cannot be `null`).
// Objects and untyped variables are assigned to `null` only if the stack address has been re-used and not cleared.
codegen . generator - > clear_address ( local ) ;
initialized = true ;
2020-05-02 00:14:56 +02:00
}
2022-11-28 17:29:02 +01:00
2024-03-28 19:57:56 +01:00
// Don't check `is_local_dirty()` since the variable must be assigned to `null` **on each iteration**.
2023-06-01 20:46:37 +02:00
if ( ! initialized & & p_block - > is_in_loop ) {
2024-03-28 19:57:56 +01:00
codegen . generator - > clear_address ( local ) ;
2022-11-28 17:29:02 +01:00
}
2014-02-10 02:10:30 +01:00
} break ;
2020-05-02 00:14:56 +02:00
case GDScriptParser : : Node : : CONSTANT : {
// Local constants.
const GDScriptParser : : ConstantNode * lc = static_cast < const GDScriptParser : : ConstantNode * > ( s ) ;
2020-07-16 03:02:44 +02:00
if ( ! lc - > initializer - > is_constant ) {
_set_error ( " Local constant must have a constant value as initializer. " , lc - > initializer ) ;
2020-05-02 00:14:56 +02:00
return ERR_PARSE_ERROR ;
}
2020-08-07 19:51:56 +02:00
codegen . add_local_constant ( lc - > identifier - > name , lc - > initializer - > reduced_value ) ;
2020-05-02 00:14:56 +02:00
} break ;
case GDScriptParser : : Node : : PASS :
// Nothing to do.
break ;
2014-02-10 02:10:30 +01:00
default : {
2020-08-07 19:51:56 +02:00
// Expression.
2020-05-02 00:14:56 +02:00
if ( s - > is_expression ( ) ) {
2022-09-29 11:53:28 +02:00
GDScriptCodeGenerator : : Address expr = _parse_expression ( codegen , err , static_cast < const GDScriptParser : : ExpressionNode * > ( s ) , true ) ;
if ( err ) {
return err ;
2020-08-07 19:51:56 +02:00
}
if ( expr . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
codegen . generator - > pop_temporary ( ) ;
2020-05-02 00:14:56 +02:00
}
} else {
2020-08-07 19:51:56 +02:00
ERR_FAIL_V_MSG ( ERR_INVALID_DATA , " Bug in bytecode compiler, unexpected node in parse tree while parsing statement. " ) ; // Unreachable code.
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
} break ;
}
2023-04-11 17:20:03 +02:00
2024-03-28 19:57:56 +01:00
gen - > clear_temporaries ( ) ;
2014-02-10 02:10:30 +01:00
}
2020-05-02 00:14:56 +02:00
2024-03-28 19:57:56 +01:00
if ( p_add_locals & & p_clear_locals ) {
_clear_block_locals ( codegen , block_locals ) ;
2023-06-01 20:46:37 +02:00
}
2020-08-07 19:51:56 +02:00
codegen . end_block ( ) ;
2014-02-10 02:10:30 +01:00
return OK ;
}
2021-03-28 16:03:13 +02:00
GDScriptFunction * GDScriptCompiler : : _parse_function ( Error & r_error , GDScript * p_script , const GDScriptParser : : ClassNode * p_class , const GDScriptParser : : FunctionNode * p_func , bool p_for_ready , bool p_for_lambda ) {
r_error = OK ;
2014-02-10 02:10:30 +01:00
CodeGen codegen ;
2020-08-07 19:51:56 +02:00
codegen . generator = memnew ( GDScriptByteCodeGenerator ) ;
2014-02-10 02:10:30 +01:00
codegen . class_node = p_class ;
2018-11-24 23:46:13 +01:00
codegen . script = p_script ;
2014-02-10 02:10:30 +01:00
codegen . function_node = p_func ;
2020-08-07 19:51:56 +02:00
StringName func_name ;
bool is_static = false ;
2022-07-12 23:12:42 +02:00
Variant rpc_config ;
2020-08-07 19:51:56 +02:00
GDScriptDataType return_type ;
return_type . has_type = true ;
return_type . kind = GDScriptDataType : : BUILTIN ;
return_type . builtin_type = Variant : : NIL ;
if ( p_func ) {
2021-03-28 16:03:13 +02:00
if ( p_func - > identifier ) {
func_name = p_func - > identifier - > name ;
} else {
func_name = " <anonymous lambda> " ;
}
2020-08-07 19:51:56 +02:00
is_static = p_func - > is_static ;
2021-06-24 10:28:15 +02:00
rpc_config = p_func - > rpc_config ;
2020-09-10 01:26:07 +02:00
return_type = _gdtype_from_datatype ( p_func - > get_datatype ( ) , p_script ) ;
2020-08-07 19:51:56 +02:00
} else {
if ( p_for_ready ) {
2024-05-13 16:56:03 +02:00
func_name = SceneStringName ( _ready ) ;
2020-08-07 19:51:56 +02:00
} else {
func_name = " @implicit_new " ;
}
}
2023-08-28 18:20:10 +02:00
MethodInfo method_info ;
2020-08-07 19:51:56 +02:00
codegen . function_name = func_name ;
2023-08-28 18:20:10 +02:00
method_info . name = func_name ;
2023-04-19 16:10:35 +02:00
codegen . is_static = is_static ;
2023-08-28 18:20:10 +02:00
if ( is_static ) {
method_info . flags | = METHOD_FLAG_STATIC ;
}
2021-06-24 10:28:15 +02:00
codegen . generator - > write_start ( p_script , func_name , is_static , rpc_config , return_type ) ;
2020-08-07 19:51:56 +02:00
2020-05-02 00:14:56 +02:00
int optional_parameters = 0 ;
2014-02-10 02:10:30 +01:00
if ( p_func ) {
2020-05-02 00:14:56 +02:00
for ( int i = 0 ; i < p_func - > parameters . size ( ) ; i + + ) {
2020-08-07 19:51:56 +02:00
const GDScriptParser : : ParameterNode * parameter = p_func - > parameters [ i ] ;
2020-09-10 01:26:07 +02:00
GDScriptDataType par_type = _gdtype_from_datatype ( parameter - > get_datatype ( ) , p_script ) ;
2022-12-22 21:43:36 +01:00
uint32_t par_addr = codegen . generator - > add_parameter ( parameter - > identifier - > name , parameter - > initializer ! = nullptr , par_type ) ;
2020-08-07 19:51:56 +02:00
codegen . parameters [ parameter - > identifier - > name ] = GDScriptCodeGenerator : : Address ( GDScriptCodeGenerator : : Address : : FUNCTION_PARAMETER , par_addr , par_type ) ;
2023-08-28 18:20:10 +02:00
method_info . arguments . push_back ( parameter - > get_datatype ( ) . to_property_info ( parameter - > identifier - > name ) ) ;
2023-01-06 05:25:55 +01:00
if ( parameter - > initializer ! = nullptr ) {
2020-05-02 00:14:56 +02:00
optional_parameters + + ;
}
2014-02-10 02:10:30 +01:00
}
2023-08-28 18:20:10 +02:00
method_info . default_arguments . append_array ( p_func - > default_arg_values ) ;
2014-02-10 02:10:30 +01:00
}
2020-08-07 19:51:56 +02:00
// Parse initializer if applies.
2021-03-28 16:03:13 +02:00
bool is_implicit_initializer = ! p_for_ready & & ! p_func & & ! p_for_lambda ;
2022-06-20 18:05:11 +02:00
bool is_initializer = p_func & & ! p_for_lambda & & p_func - > identifier - > name = = GDScriptLanguage : : get_singleton ( ) - > strings . _init ;
bool is_implicit_ready = ! p_func & & p_for_ready ;
2014-02-10 02:10:30 +01:00
2023-02-25 01:39:10 +01:00
if ( ! p_for_lambda & & is_implicit_initializer ) {
2023-04-19 16:10:35 +02:00
// Initialize the default values for typed variables before anything.
2023-02-25 01:39:10 +01:00
// This avoids crashes if they are accessed with validated calls before being properly initialized.
// It may happen with out-of-order access or with `@onready` variables.
for ( const GDScriptParser : : ClassNode : : Member & member : p_class - > members ) {
if ( member . type ! = GDScriptParser : : ClassNode : : Member : : VARIABLE ) {
continue ;
}
const GDScriptParser : : VariableNode * field = member . variable ;
2023-04-19 16:10:35 +02:00
if ( field - > is_static ) {
continue ;
}
2023-02-25 01:39:10 +01:00
GDScriptDataType field_type = _gdtype_from_datatype ( field - > get_datatype ( ) , codegen . script ) ;
if ( field_type . has_type ) {
codegen . generator - > write_newline ( field - > start_line ) ;
2023-05-16 12:03:53 +02:00
GDScriptCodeGenerator : : Address dst_address ( GDScriptCodeGenerator : : Address : : MEMBER , codegen . script - > member_indices [ field - > identifier - > name ] . index , field_type ) ;
2023-06-24 20:03:28 +02:00
if ( field_type . builtin_type = = Variant : : ARRAY & & field_type . has_container_element_type ( 0 ) ) {
2023-09-14 20:31:07 +02:00
codegen . generator - > write_construct_typed_array ( dst_address , field_type . get_container_element_type ( 0 ) , Vector < GDScriptCodeGenerator : : Address > ( ) ) ;
2023-06-24 20:03:28 +02:00
} else if ( field_type . builtin_type = = Variant : : DICTIONARY & & field_type . has_container_element_types ( ) ) {
codegen . generator - > write_construct_typed_dictionary ( dst_address , field_type . get_container_element_type_or_variant ( 0 ) ,
field_type . get_container_element_type_or_variant ( 1 ) , Vector < GDScriptCodeGenerator : : Address > ( ) ) ;
2023-02-25 01:39:10 +01:00
} else if ( field_type . kind = = GDScriptDataType : : BUILTIN ) {
codegen . generator - > write_construct ( dst_address , field_type . builtin_type , Vector < GDScriptCodeGenerator : : Address > ( ) ) ;
}
// The `else` branch is for objects, in such case we leave it as `null`.
}
}
}
2022-06-20 18:05:11 +02:00
if ( ! p_for_lambda & & ( is_implicit_initializer | | is_implicit_ready ) ) {
2020-05-02 00:14:56 +02:00
// Initialize class fields.
for ( int i = 0 ; i < p_class - > members . size ( ) ; i + + ) {
if ( p_class - > members [ i ] . type ! = GDScriptParser : : ClassNode : : Member : : VARIABLE ) {
continue ;
}
const GDScriptParser : : VariableNode * field = p_class - > members [ i ] . variable ;
2023-04-19 16:10:35 +02:00
if ( field - > is_static ) {
continue ;
}
2022-06-20 18:05:11 +02:00
if ( field - > onready ! = is_implicit_ready ) {
// Only initialize in @implicit_ready.
2020-05-02 00:14:56 +02:00
continue ;
}
if ( field - > initializer ) {
// Emit proper line change.
2020-08-07 19:51:56 +02:00
codegen . generator - > write_newline ( field - > initializer - > start_line ) ;
2020-05-02 00:14:56 +02:00
2021-03-28 16:03:13 +02:00
GDScriptCodeGenerator : : Address src_address = _parse_expression ( codegen , r_error , field - > initializer , false , true ) ;
if ( r_error ) {
2020-09-02 21:05:31 +02:00
memdelete ( codegen . generator ) ;
2021-03-28 16:03:13 +02:00
return nullptr ;
2020-05-02 00:14:56 +02:00
}
2023-05-16 12:03:53 +02:00
GDScriptDataType field_type = _gdtype_from_datatype ( field - > get_datatype ( ) , codegen . script ) ;
GDScriptCodeGenerator : : Address dst_address ( GDScriptCodeGenerator : : Address : : MEMBER , codegen . script - > member_indices [ field - > identifier - > name ] . index , field_type ) ;
2021-05-26 19:05:31 +02:00
if ( field - > use_conversion_assign ) {
codegen . generator - > write_assign_with_conversion ( dst_address , src_address ) ;
} else {
codegen . generator - > write_assign ( dst_address , src_address ) ;
}
2020-08-07 19:51:56 +02:00
if ( src_address . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
codegen . generator - > pop_temporary ( ) ;
2020-07-16 03:02:44 +02:00
}
2020-05-02 00:14:56 +02:00
}
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
2020-08-07 19:51:56 +02:00
// Parse default argument code if applies.
2014-02-10 02:10:30 +01:00
if ( p_func ) {
2020-05-02 00:14:56 +02:00
if ( optional_parameters > 0 ) {
2020-08-07 19:51:56 +02:00
codegen . generator - > start_parameters ( ) ;
2020-05-02 00:14:56 +02:00
for ( int i = p_func - > parameters . size ( ) - optional_parameters ; i < p_func - > parameters . size ( ) ; i + + ) {
2020-08-07 19:51:56 +02:00
const GDScriptParser : : ParameterNode * parameter = p_func - > parameters [ i ] ;
2022-12-22 21:43:36 +01:00
GDScriptCodeGenerator : : Address src_addr = _parse_expression ( codegen , r_error , parameter - > initializer ) ;
2021-03-28 16:03:13 +02:00
if ( r_error ) {
2020-09-02 21:05:31 +02:00
memdelete ( codegen . generator ) ;
2021-03-28 16:03:13 +02:00
return nullptr ;
2020-05-02 00:14:56 +02:00
}
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address dst_addr = codegen . parameters [ parameter - > identifier - > name ] ;
2023-01-06 10:49:06 +01:00
codegen . generator - > write_assign_default_parameter ( dst_addr , src_addr , parameter - > use_conversion_assign ) ;
2020-08-07 19:51:56 +02:00
if ( src_addr . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
codegen . generator - > pop_temporary ( ) ;
2020-07-16 03:02:44 +02:00
}
2014-02-10 02:10:30 +01:00
}
2020-08-07 19:51:56 +02:00
codegen . generator - > end_parameters ( ) ;
2014-02-10 02:10:30 +01:00
}
2023-06-01 20:46:37 +02:00
// No need to reset locals at the end of the function, the stack will be cleared anyway.
r_error = _parse_block ( codegen , p_func - > body , true , false ) ;
2021-03-28 16:03:13 +02:00
if ( r_error ) {
2020-09-02 21:05:31 +02:00
memdelete ( codegen . generator ) ;
2021-03-28 16:03:13 +02:00
return nullptr ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
2016-05-22 02:18:16 +02:00
# ifdef DEBUG_ENABLED
2020-02-27 03:30:20 +01:00
if ( EngineDebugger : : is_active ( ) ) {
2016-05-22 02:18:16 +02:00
String signature ;
2020-08-07 19:51:56 +02:00
// Path.
2022-12-01 11:20:42 +01:00
if ( ! p_script - > get_script_path ( ) . is_empty ( ) ) {
signature + = p_script - > get_script_path ( ) ;
2020-05-14 16:41:43 +02:00
}
2020-08-07 19:51:56 +02:00
// Location.
2016-05-22 02:18:16 +02:00
if ( p_func ) {
2020-05-02 00:14:56 +02:00
signature + = " :: " + itos ( p_func - > body - > start_line ) ;
2016-05-22 02:18:16 +02:00
} else {
signature + = " ::0 " ;
}
2020-08-07 19:51:56 +02:00
// Function and class.
2016-05-22 02:18:16 +02:00
2020-05-02 00:14:56 +02:00
if ( p_class - > identifier ) {
signature + = " :: " + String ( p_class - > identifier - > name ) + " . " + String ( func_name ) ;
2016-05-22 02:18:16 +02:00
} else {
signature + = " :: " + String ( func_name ) ;
}
2021-03-28 16:03:13 +02:00
if ( p_for_lambda ) {
signature + = " (lambda) " ;
}
2020-08-07 19:51:56 +02:00
codegen . generator - > set_signature ( signature ) ;
2016-05-22 02:18:16 +02:00
}
# endif
2014-05-24 06:35:47 +02:00
2014-02-10 02:10:30 +01:00
if ( p_func ) {
2020-08-07 19:51:56 +02:00
codegen . generator - > set_initial_line ( p_func - > start_line ) ;
2014-02-10 02:10:30 +01:00
} else {
2020-08-07 19:51:56 +02:00
codegen . generator - > set_initial_line ( 0 ) ;
2014-02-10 02:10:30 +01:00
}
2020-08-07 19:51:56 +02:00
GDScriptFunction * gd_function = codegen . generator - > write_end ( ) ;
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( is_initializer ) {
2020-08-07 19:51:56 +02:00
p_script - > initializer = gd_function ;
} else if ( is_implicit_initializer ) {
p_script - > implicit_initializer = gd_function ;
2022-06-20 18:05:11 +02:00
} else if ( is_implicit_ready ) {
p_script - > implicit_ready = gd_function ;
2020-05-02 00:14:56 +02:00
}
2014-02-10 02:10:30 +01:00
2020-11-29 03:37:57 +01:00
if ( p_func ) {
2023-08-28 18:20:10 +02:00
// If no `return` statement, then return type is `void`, not `Variant`.
2020-11-29 03:37:57 +01:00
if ( p_func - > body - > has_return ) {
2022-01-09 19:14:48 +01:00
gd_function - > return_type = _gdtype_from_datatype ( p_func - > get_datatype ( ) , p_script ) ;
2023-08-28 18:20:10 +02:00
method_info . return_val = p_func - > get_datatype ( ) . to_property_info ( String ( ) ) ;
2020-11-29 03:37:57 +01:00
} else {
gd_function - > return_type = GDScriptDataType ( ) ;
gd_function - > return_type . has_type = true ;
gd_function - > return_type . kind = GDScriptDataType : : BUILTIN ;
gd_function - > return_type . builtin_type = Variant : : NIL ;
}
}
2023-08-28 18:20:10 +02:00
gd_function - > method_info = method_info ;
2022-06-20 18:05:11 +02:00
if ( ! is_implicit_initializer & & ! is_implicit_ready & & ! p_for_lambda ) {
2021-03-28 16:03:13 +02:00
p_script - > member_functions [ func_name ] = gd_function ;
}
2020-08-07 19:51:56 +02:00
2020-09-02 21:05:31 +02:00
memdelete ( codegen . generator ) ;
2021-03-28 16:03:13 +02:00
return gd_function ;
2014-02-10 02:10:30 +01:00
}
2023-04-19 16:10:35 +02:00
GDScriptFunction * GDScriptCompiler : : _make_static_initializer ( Error & r_error , GDScript * p_script , const GDScriptParser : : ClassNode * p_class ) {
r_error = OK ;
CodeGen codegen ;
codegen . generator = memnew ( GDScriptByteCodeGenerator ) ;
codegen . class_node = p_class ;
codegen . script = p_script ;
StringName func_name = SNAME ( " @static_initializer " ) ;
bool is_static = true ;
Variant rpc_config ;
GDScriptDataType return_type ;
return_type . has_type = true ;
return_type . kind = GDScriptDataType : : BUILTIN ;
return_type . builtin_type = Variant : : NIL ;
codegen . function_name = func_name ;
codegen . is_static = is_static ;
codegen . generator - > write_start ( p_script , func_name , is_static , rpc_config , return_type ) ;
2023-05-16 12:03:53 +02:00
// The static initializer is always called on the same class where the static variables are defined,
// so the CLASS address (current class) can be used instead of `codegen.add_constant(p_script)`.
2023-04-19 16:10:35 +02:00
GDScriptCodeGenerator : : Address class_addr ( GDScriptCodeGenerator : : Address : : CLASS ) ;
// Initialize the default values for typed variables before anything.
// This avoids crashes if they are accessed with validated calls before being properly initialized.
// It may happen with out-of-order access or with `@onready` variables.
for ( const GDScriptParser : : ClassNode : : Member & member : p_class - > members ) {
if ( member . type ! = GDScriptParser : : ClassNode : : Member : : VARIABLE ) {
continue ;
}
const GDScriptParser : : VariableNode * field = member . variable ;
if ( ! field - > is_static ) {
continue ;
}
GDScriptDataType field_type = _gdtype_from_datatype ( field - > get_datatype ( ) , codegen . script ) ;
if ( field_type . has_type ) {
codegen . generator - > write_newline ( field - > start_line ) ;
2023-06-24 20:03:28 +02:00
if ( field_type . builtin_type = = Variant : : ARRAY & & field_type . has_container_element_type ( 0 ) ) {
2023-04-19 16:10:35 +02:00
GDScriptCodeGenerator : : Address temp = codegen . add_temporary ( field_type ) ;
2023-09-14 20:31:07 +02:00
codegen . generator - > write_construct_typed_array ( temp , field_type . get_container_element_type ( 0 ) , Vector < GDScriptCodeGenerator : : Address > ( ) ) ;
2023-05-16 12:03:53 +02:00
codegen . generator - > write_set_static_variable ( temp , class_addr , p_script - > static_variables_indices [ field - > identifier - > name ] . index ) ;
2023-04-19 16:10:35 +02:00
codegen . generator - > pop_temporary ( ) ;
2023-06-24 20:03:28 +02:00
} else if ( field_type . builtin_type = = Variant : : DICTIONARY & & field_type . has_container_element_types ( ) ) {
GDScriptCodeGenerator : : Address temp = codegen . add_temporary ( field_type ) ;
codegen . generator - > write_construct_typed_dictionary ( temp , field_type . get_container_element_type_or_variant ( 0 ) ,
field_type . get_container_element_type_or_variant ( 1 ) , Vector < GDScriptCodeGenerator : : Address > ( ) ) ;
codegen . generator - > write_set_static_variable ( temp , class_addr , p_script - > static_variables_indices [ field - > identifier - > name ] . index ) ;
codegen . generator - > pop_temporary ( ) ;
2023-04-19 16:10:35 +02:00
} else if ( field_type . kind = = GDScriptDataType : : BUILTIN ) {
GDScriptCodeGenerator : : Address temp = codegen . add_temporary ( field_type ) ;
codegen . generator - > write_construct ( temp , field_type . builtin_type , Vector < GDScriptCodeGenerator : : Address > ( ) ) ;
2023-05-16 12:03:53 +02:00
codegen . generator - > write_set_static_variable ( temp , class_addr , p_script - > static_variables_indices [ field - > identifier - > name ] . index ) ;
2023-04-19 16:10:35 +02:00
codegen . generator - > pop_temporary ( ) ;
}
// The `else` branch is for objects, in such case we leave it as `null`.
}
}
for ( int i = 0 ; i < p_class - > members . size ( ) ; i + + ) {
// Initialize static fields.
if ( p_class - > members [ i ] . type ! = GDScriptParser : : ClassNode : : Member : : VARIABLE ) {
continue ;
}
const GDScriptParser : : VariableNode * field = p_class - > members [ i ] . variable ;
if ( ! field - > is_static ) {
continue ;
}
if ( field - > initializer ) {
// Emit proper line change.
codegen . generator - > write_newline ( field - > initializer - > start_line ) ;
GDScriptCodeGenerator : : Address src_address = _parse_expression ( codegen , r_error , field - > initializer , false , true ) ;
if ( r_error ) {
memdelete ( codegen . generator ) ;
return nullptr ;
}
2023-05-16 12:03:53 +02:00
GDScriptDataType field_type = _gdtype_from_datatype ( field - > get_datatype ( ) , codegen . script ) ;
2023-04-19 16:10:35 +02:00
GDScriptCodeGenerator : : Address temp = codegen . add_temporary ( field_type ) ;
2023-05-16 12:03:53 +02:00
2023-04-19 16:10:35 +02:00
if ( field - > use_conversion_assign ) {
codegen . generator - > write_assign_with_conversion ( temp , src_address ) ;
} else {
codegen . generator - > write_assign ( temp , src_address ) ;
}
if ( src_address . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
codegen . generator - > pop_temporary ( ) ;
}
2023-05-16 12:03:53 +02:00
codegen . generator - > write_set_static_variable ( temp , class_addr , p_script - > static_variables_indices [ field - > identifier - > name ] . index ) ;
2023-04-19 16:10:35 +02:00
codegen . generator - > pop_temporary ( ) ;
}
}
if ( p_script - > has_method ( GDScriptLanguage : : get_singleton ( ) - > strings . _static_init ) ) {
codegen . generator - > write_newline ( p_class - > start_line ) ;
codegen . generator - > write_call ( GDScriptCodeGenerator : : Address ( ) , class_addr , GDScriptLanguage : : get_singleton ( ) - > strings . _static_init , Vector < GDScriptCodeGenerator : : Address > ( ) ) ;
}
# ifdef DEBUG_ENABLED
if ( EngineDebugger : : is_active ( ) ) {
String signature ;
// Path.
if ( ! p_script - > get_script_path ( ) . is_empty ( ) ) {
signature + = p_script - > get_script_path ( ) ;
}
// Location.
signature + = " ::0 " ;
// Function and class.
if ( p_class - > identifier ) {
signature + = " :: " + String ( p_class - > identifier - > name ) + " . " + String ( func_name ) ;
} else {
signature + = " :: " + String ( func_name ) ;
}
codegen . generator - > set_signature ( signature ) ;
}
# endif
codegen . generator - > set_initial_line ( p_class - > start_line ) ;
GDScriptFunction * gd_function = codegen . generator - > write_end ( ) ;
memdelete ( codegen . generator ) ;
return gd_function ;
}
2020-06-01 21:41:05 +02:00
Error GDScriptCompiler : : _parse_setter_getter ( GDScript * p_script , const GDScriptParser : : ClassNode * p_class , const GDScriptParser : : VariableNode * p_variable , bool p_is_setter ) {
2022-09-29 11:53:28 +02:00
Error err = OK ;
2020-06-01 21:41:05 +02:00
2021-09-06 07:04:43 +02:00
GDScriptParser : : FunctionNode * function ;
2021-04-06 19:00:08 +02:00
2020-08-07 19:51:56 +02:00
if ( p_is_setter ) {
2021-09-06 07:04:43 +02:00
function = p_variable - > setter ;
2020-08-07 19:51:56 +02:00
} else {
2021-09-06 07:04:43 +02:00
function = p_variable - > getter ;
2020-06-01 21:41:05 +02:00
}
2022-09-29 11:53:28 +02:00
_parse_function ( err , p_script , p_class , function ) ;
2020-06-01 21:41:05 +02:00
2022-09-29 11:53:28 +02:00
return err ;
2020-06-01 21:41:05 +02:00
}
2023-08-31 19:49:04 +02:00
// Prepares given script, and inner class scripts, for compilation. It populates class members and initializes method
// RPC info for its base classes first, then for itself, then for inner classes.
// Warning: this function cannot initiate compilation of other classes, or it will result in cyclic dependency issues.
Error GDScriptCompiler : : _prepare_compilation ( GDScript * p_script , const GDScriptParser : : ClassNode * p_class , bool p_keep_state ) {
2022-11-08 12:51:20 +01:00
if ( parsed_classes . has ( p_script ) ) {
return OK ;
}
2014-02-10 02:10:30 +01:00
2022-11-08 12:51:20 +01:00
if ( parsing_classes . has ( p_script ) ) {
String class_name = p_class - > identifier ? String ( p_class - > identifier - > name ) : p_class - > fqcn ;
_set_error ( vformat ( R " (Cyclic class reference for " % s " .) " , class_name ) , p_class ) ;
return ERR_PARSE_ERROR ;
2016-06-02 01:22:02 +02:00
}
2014-02-10 02:10:30 +01:00
2022-11-08 12:51:20 +01:00
parsing_classes . insert ( p_script ) ;
2022-10-09 18:41:28 +02:00
p_script - > clearing = true ;
2020-11-29 03:37:57 +01:00
2017-11-16 18:38:18 +01:00
p_script - > native = Ref < GDScriptNativeClass > ( ) ;
2014-02-10 02:10:30 +01:00
p_script - > base = Ref < GDScript > ( ) ;
2020-04-02 01:20:12 +02:00
p_script - > _base = nullptr ;
2014-02-10 02:10:30 +01:00
p_script - > members . clear ( ) ;
2022-10-09 18:41:28 +02:00
// This makes possible to clear script constants and member_functions without heap-use-after-free errors.
HashMap < StringName , Variant > constants ;
for ( const KeyValue < StringName , Variant > & E : p_script - > constants ) {
constants . insert ( E . key , E . value ) ;
}
2014-02-10 02:10:30 +01:00
p_script - > constants . clear ( ) ;
2022-10-09 18:41:28 +02:00
constants . clear ( ) ;
HashMap < StringName , GDScriptFunction * > member_functions ;
2021-08-09 22:13:42 +02:00
for ( const KeyValue < StringName , GDScriptFunction * > & E : p_script - > member_functions ) {
2022-10-09 18:41:28 +02:00
member_functions . insert ( E . key , E . value ) ;
}
p_script - > member_functions . clear ( ) ;
for ( const KeyValue < StringName , GDScriptFunction * > & E : member_functions ) {
2021-08-09 22:13:42 +02:00
memdelete ( E . value ) ;
2016-05-22 02:18:16 +02:00
}
2022-10-09 18:41:28 +02:00
member_functions . clear ( ) ;
2023-04-19 16:10:35 +02:00
p_script - > static_variables . clear ( ) ;
2022-06-17 19:48:07 +02:00
if ( p_script - > implicit_initializer ) {
memdelete ( p_script - > implicit_initializer ) ;
}
2022-06-20 18:05:11 +02:00
if ( p_script - > implicit_ready ) {
memdelete ( p_script - > implicit_ready ) ;
}
2023-04-19 16:10:35 +02:00
if ( p_script - > static_initializer ) {
memdelete ( p_script - > static_initializer ) ;
}
2014-02-10 02:10:30 +01:00
p_script - > member_functions . clear ( ) ;
p_script - > member_indices . clear ( ) ;
2023-04-19 16:10:35 +02:00
p_script - > static_variables_indices . clear ( ) ;
p_script - > static_variables . clear ( ) ;
2016-07-17 18:49:22 +02:00
p_script - > _signals . clear ( ) ;
2020-04-02 01:20:12 +02:00
p_script - > initializer = nullptr ;
2022-06-17 19:48:07 +02:00
p_script - > implicit_initializer = nullptr ;
2022-06-20 18:05:11 +02:00
p_script - > implicit_ready = nullptr ;
2023-04-19 16:10:35 +02:00
p_script - > static_initializer = nullptr ;
2023-08-31 19:49:04 +02:00
p_script - > rpc_config . clear ( ) ;
2023-09-13 09:40:48 +02:00
p_script - > lambda_info . clear ( ) ;
2016-06-02 01:22:02 +02:00
2022-10-09 18:41:28 +02:00
p_script - > clearing = false ;
2020-05-02 00:14:56 +02:00
p_script - > tool = parser - > is_tool ( ) ;
2014-02-10 02:10:30 +01:00
2023-08-28 18:20:10 +02:00
if ( p_script - > local_name ! = StringName ( ) ) {
if ( ClassDB : : class_exists ( p_script - > local_name ) & & ClassDB : : is_class_exposed ( p_script - > local_name ) ) {
_set_error ( vformat ( R " (The class " % s " shadows a native class) " , p_script - > local_name ) , p_class ) ;
2021-08-24 18:49:03 +02:00
return ERR_ALREADY_EXISTS ;
}
}
2023-09-12 20:55:55 +02:00
GDScriptDataType base_type = _gdtype_from_datatype ( p_class - > base_type , p_script , false ) ;
2014-02-10 02:10:30 +01:00
2024-05-07 08:05:23 +02:00
ERR_FAIL_COND_V_MSG ( base_type . native_type = = StringName ( ) , ERR_BUG , vformat ( R " (Failed to get base class for " % s " ) " , p_script - > path ) ) ;
2023-02-21 18:09:10 +01:00
int native_idx = GDScriptLanguage : : get_singleton ( ) - > get_global_map ( ) [ base_type . native_type ] ;
2024-05-07 08:05:23 +02:00
2023-02-21 18:09:10 +01:00
p_script - > native = GDScriptLanguage : : get_singleton ( ) - > get_global_array ( ) [ native_idx ] ;
ERR_FAIL_COND_V ( p_script - > native . is_null ( ) , ERR_BUG ) ;
2018-05-30 04:16:52 +02:00
// Inheritance
2019-03-16 17:49:29 +01:00
switch ( base_type . kind ) {
2023-02-21 18:09:10 +01:00
case GDScriptDataType : : NATIVE :
// Nothing more to do.
break ;
2019-03-16 17:49:29 +01:00
case GDScriptDataType : : GDSCRIPT : {
2020-09-10 01:26:07 +02:00
Ref < GDScript > base = Ref < GDScript > ( base_type . script_type ) ;
2022-11-08 12:51:20 +01:00
if ( base . is_null ( ) ) {
return ERR_COMPILATION_FAILED ;
}
2019-04-09 21:21:20 +02:00
2022-12-12 13:35:55 +01:00
if ( main_script - > has_class ( base . ptr ( ) ) ) {
2023-08-31 19:49:04 +02:00
Error err = _prepare_compilation ( base . ptr ( ) , p_class - > base_type . class_type , p_keep_state ) ;
2022-11-08 12:51:20 +01:00
if ( err ) {
return err ;
}
} else if ( ! base - > is_valid ( ) ) {
Error err = OK ;
2023-07-08 15:20:09 +02:00
Ref < GDScript > base_root = GDScriptCache : : get_shallow_script ( base - > path , err , p_script - > path ) ;
2022-11-08 12:51:20 +01:00
if ( err ) {
2023-07-08 15:20:09 +02:00
_set_error ( vformat ( R " (Could not parse base class " % s " from " % s " : %s) " , base - > fully_qualified_name , base - > path , error_names [ err ] ) , nullptr ) ;
2022-11-08 12:51:20 +01:00
return err ;
2019-04-09 21:21:20 +02:00
}
2022-11-08 12:51:20 +01:00
if ( base_root . is_valid ( ) ) {
base = Ref < GDScript > ( base_root - > find_class ( base - > fully_qualified_name ) ) ;
}
if ( base . is_null ( ) ) {
_set_error ( vformat ( R " (Could not find class " % s " in " % s " .) " , base - > fully_qualified_name , base - > path ) , nullptr ) ;
return ERR_COMPILATION_FAILED ;
}
2023-07-08 15:20:09 +02:00
2023-08-31 19:49:04 +02:00
err = _prepare_compilation ( base . ptr ( ) , p_class - > base_type . class_type , p_keep_state ) ;
2023-07-08 15:20:09 +02:00
if ( err ) {
_set_error ( vformat ( R " (Could not populate class members of base class " % s " in " % s " .) " , base - > fully_qualified_name , base - > path ) , nullptr ) ;
return err ;
}
2019-04-09 21:21:20 +02:00
}
2020-07-16 03:02:44 +02:00
2022-11-08 12:51:20 +01:00
p_script - > base = base ;
p_script - > _base = base . ptr ( ) ;
2020-07-16 03:02:44 +02:00
p_script - > member_indices = base - > member_indices ;
2019-03-16 17:49:29 +01:00
} break ;
2018-05-30 04:16:52 +02:00
default : {
2022-11-08 12:51:20 +01:00
_set_error ( " Parser bug: invalid inheritance. " , nullptr ) ;
2018-05-30 04:16:52 +02:00
return ERR_BUG ;
} break ;
2014-02-10 02:10:30 +01:00
}
2023-08-31 19:49:04 +02:00
// Duplicate RPC information from base GDScript
// Base script isn't valid because it should not have been compiled yet, but the reference contains relevant info.
if ( base_type . kind = = GDScriptDataType : : GDSCRIPT & & p_script - > base . is_valid ( ) ) {
p_script - > rpc_config = p_script - > base - > rpc_config . duplicate ( ) ;
}
2020-05-02 00:14:56 +02:00
for ( int i = 0 ; i < p_class - > members . size ( ) ; i + + ) {
const GDScriptParser : : ClassNode : : Member & member = p_class - > members [ i ] ;
switch ( member . type ) {
case GDScriptParser : : ClassNode : : Member : : VARIABLE : {
const GDScriptParser : : VariableNode * variable = member . variable ;
StringName name = variable - > identifier - > name ;
GDScript : : MemberInfo minfo ;
2020-06-01 21:41:05 +02:00
switch ( variable - > property ) {
case GDScriptParser : : VariableNode : : PROP_NONE :
break ; // Nothing to do.
case GDScriptParser : : VariableNode : : PROP_SETGET :
if ( variable - > setter_pointer ! = nullptr ) {
minfo . setter = variable - > setter_pointer - > name ;
}
if ( variable - > getter_pointer ! = nullptr ) {
minfo . getter = variable - > getter_pointer - > name ;
}
break ;
case GDScriptParser : : VariableNode : : PROP_INLINE :
if ( variable - > setter ! = nullptr ) {
minfo . setter = " @ " + variable - > identifier - > name + " _setter " ;
}
if ( variable - > getter ! = nullptr ) {
minfo . getter = " @ " + variable - > identifier - > name + " _getter " ;
}
break ;
}
2020-09-10 01:26:07 +02:00
minfo . data_type = _gdtype_from_datatype ( variable - > get_datatype ( ) , p_script ) ;
2020-05-02 00:14:56 +02:00
2023-08-28 18:20:10 +02:00
PropertyInfo prop_info = variable - > get_datatype ( ) . to_property_info ( name ) ;
2020-05-02 00:14:56 +02:00
PropertyInfo export_info = variable - > export_info ;
if ( variable - > exported ) {
if ( ! minfo . data_type . has_type ) {
prop_info . type = export_info . type ;
prop_info . class_name = export_info . class_name ;
}
prop_info . hint = export_info . hint ;
prop_info . hint_string = export_info . hint_string ;
2023-08-28 18:20:10 +02:00
prop_info . usage = export_info . usage ;
2020-05-02 00:14:56 +02:00
}
2023-08-28 18:20:10 +02:00
prop_info . usage | = PROPERTY_USAGE_SCRIPT_VARIABLE ;
minfo . property_info = prop_info ;
2014-02-10 02:10:30 +01:00
2023-04-19 16:10:35 +02:00
if ( variable - > is_static ) {
minfo . index = p_script - > static_variables_indices . size ( ) ;
p_script - > static_variables_indices [ name ] = minfo ;
} else {
2023-08-28 18:20:10 +02:00
minfo . index = p_script - > member_indices . size ( ) ;
2023-04-19 16:10:35 +02:00
p_script - > member_indices [ name ] = minfo ;
p_script - > members . insert ( name ) ;
}
2014-02-10 02:10:30 +01:00
2016-09-12 15:52:29 +02:00
# ifdef TOOLS_ENABLED
2020-11-29 03:37:57 +01:00
if ( variable - > initializer ! = nullptr & & variable - > initializer - > is_constant ) {
p_script - > member_default_values [ name ] = variable - > initializer - > reduced_value ;
2022-11-30 22:14:24 +01:00
GDScriptCompiler : : convert_to_initializer_type ( p_script - > member_default_values [ name ] , variable ) ;
2020-11-29 03:37:57 +01:00
} else {
p_script - > member_default_values . erase ( name ) ;
}
2016-09-12 15:52:29 +02:00
# endif
2020-05-02 00:14:56 +02:00
} break ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
case GDScriptParser : : ClassNode : : Member : : CONSTANT : {
const GDScriptParser : : ConstantNode * constant = member . constant ;
StringName name = constant - > identifier - > name ;
2018-05-30 04:16:52 +02:00
2020-08-24 14:15:33 +02:00
p_script - > constants . insert ( name , constant - > initializer - > reduced_value ) ;
2020-05-02 00:14:56 +02:00
} break ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
case GDScriptParser : : ClassNode : : Member : : ENUM_VALUE : {
const GDScriptParser : : EnumNode : : Value & enum_value = member . enum_value ;
StringName name = enum_value . identifier - > name ;
2015-06-24 18:29:23 +02:00
2020-05-02 00:14:56 +02:00
p_script - > constants . insert ( name , enum_value . value ) ;
} break ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
case GDScriptParser : : ClassNode : : Member : : SIGNAL : {
const GDScriptParser : : SignalNode * signal = member . signal ;
StringName name = signal - > identifier - > name ;
2015-06-24 18:29:23 +02:00
2023-08-28 18:20:10 +02:00
p_script - > _signals [ name ] = signal - > method_info ;
2020-05-02 00:14:56 +02:00
} break ;
case GDScriptParser : : ClassNode : : Member : : ENUM : {
const GDScriptParser : : EnumNode * enum_n = member . m_enum ;
2022-12-27 04:06:11 +01:00
StringName name = enum_n - > identifier - > name ;
2020-05-02 00:14:56 +02:00
2022-12-27 04:06:11 +01:00
p_script - > constants . insert ( name , enum_n - > dictionary ) ;
2020-05-02 00:14:56 +02:00
} break ;
2022-07-03 21:30:08 +02:00
case GDScriptParser : : ClassNode : : Member : : GROUP : {
const GDScriptParser : : AnnotationNode * annotation = member . annotation ;
2023-06-15 07:02:42 +02:00
// Avoid name conflict. See GH-78252.
StringName name = vformat ( " @group_%d_%s " , p_script - > members . size ( ) , annotation - > export_info . name ) ;
2022-07-03 21:30:08 +02:00
// This is not a normal member, but we need this to keep indices in order.
GDScript : : MemberInfo minfo ;
minfo . index = p_script - > member_indices . size ( ) ;
PropertyInfo prop_info ;
2023-06-15 07:02:42 +02:00
prop_info . name = annotation - > export_info . name ;
2022-07-03 21:30:08 +02:00
prop_info . usage = annotation - > export_info . usage ;
prop_info . hint_string = annotation - > export_info . hint_string ;
2023-08-28 18:20:10 +02:00
minfo . property_info = prop_info ;
2022-07-03 21:30:08 +02:00
p_script - > member_indices [ name ] = minfo ;
2023-09-23 14:19:43 +02:00
p_script - > members . insert ( name ) ;
2022-07-03 21:30:08 +02:00
} break ;
2023-08-31 19:49:04 +02:00
case GDScriptParser : : ClassNode : : Member : : FUNCTION : {
const GDScriptParser : : FunctionNode * function_n = member . function ;
Variant config = function_n - > rpc_config ;
if ( config . get_type ( ) ! = Variant : : NIL ) {
p_script - > rpc_config [ function_n - > identifier - > name ] = config ;
}
} break ;
2020-05-02 00:14:56 +02:00
default :
break ; // Nothing to do here.
}
2015-06-24 18:29:23 +02:00
}
2018-05-30 04:16:52 +02:00
2023-04-19 16:10:35 +02:00
p_script - > static_variables . resize ( p_script - > static_variables_indices . size ( ) ) ;
2019-03-16 17:49:29 +01:00
parsed_classes . insert ( p_script ) ;
parsing_classes . erase ( p_script ) ;
2018-05-30 04:16:52 +02:00
2023-08-31 19:49:04 +02:00
// Populate inner classes.
2020-05-02 00:14:56 +02:00
for ( int i = 0 ; i < p_class - > members . size ( ) ; i + + ) {
const GDScriptParser : : ClassNode : : Member & member = p_class - > members [ i ] ;
if ( member . type ! = member . CLASS ) {
continue ;
}
const GDScriptParser : : ClassNode * inner_class = member . m_class ;
StringName name = inner_class - > identifier - > name ;
2019-06-02 13:07:46 +02:00
Ref < GDScript > & subclass = p_script - > subclasses [ name ] ;
GDScript * subclass_ptr = subclass . ptr ( ) ;
2016-06-02 01:22:02 +02:00
2018-05-30 04:16:52 +02:00
// Subclass might still be parsing, just skip it
2022-11-08 12:51:20 +01:00
if ( ! parsing_classes . has ( subclass_ptr ) ) {
2023-08-31 19:49:04 +02:00
Error err = _prepare_compilation ( subclass_ptr , inner_class , p_keep_state ) ;
2020-05-14 16:41:43 +02:00
if ( err ) {
2018-05-30 04:16:52 +02:00
return err ;
2020-05-14 16:41:43 +02:00
}
2016-06-02 01:22:02 +02:00
}
2014-02-10 02:10:30 +01:00
2014-05-02 17:40:34 +02:00
p_script - > constants . insert ( name , subclass ) ; //once parsed, goes to the list of constants
2014-02-10 02:10:30 +01:00
}
2018-05-30 04:16:52 +02:00
return OK ;
}
2022-11-08 12:51:20 +01:00
Error GDScriptCompiler : : _compile_class ( GDScript * p_script , const GDScriptParser : : ClassNode * p_class , bool p_keep_state ) {
// Compile member functions, getters, and setters.
2020-05-02 00:14:56 +02:00
for ( int i = 0 ; i < p_class - > members . size ( ) ; i + + ) {
const GDScriptParser : : ClassNode : : Member & member = p_class - > members [ i ] ;
2020-06-01 21:41:05 +02:00
if ( member . type = = member . FUNCTION ) {
const GDScriptParser : : FunctionNode * function = member . function ;
2021-03-28 16:03:13 +02:00
Error err = OK ;
_parse_function ( err , p_script , p_class , function ) ;
2020-06-01 21:41:05 +02:00
if ( err ) {
return err ;
}
} else if ( member . type = = member . VARIABLE ) {
const GDScriptParser : : VariableNode * variable = member . variable ;
if ( variable - > property = = GDScriptParser : : VariableNode : : PROP_INLINE ) {
if ( variable - > setter ! = nullptr ) {
Error err = _parse_setter_getter ( p_script , p_class , variable , true ) ;
if ( err ) {
return err ;
}
}
if ( variable - > getter ! = nullptr ) {
Error err = _parse_setter_getter ( p_script , p_class , variable , false ) ;
if ( err ) {
return err ;
}
}
}
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
2020-05-02 00:14:56 +02:00
{
// Create an implicit constructor in any case.
2021-03-28 16:03:13 +02:00
Error err = OK ;
_parse_function ( err , p_script , p_class , nullptr ) ;
2020-05-14 16:41:43 +02:00
if ( err ) {
2014-02-10 02:10:30 +01:00
return err ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
2022-06-20 18:05:11 +02:00
if ( p_class - > onready_used ) {
// Create an implicit_ready constructor.
2021-03-28 16:03:13 +02:00
Error err = OK ;
_parse_function ( err , p_script , p_class , nullptr , true ) ;
2020-05-14 16:41:43 +02:00
if ( err ) {
2015-12-28 23:31:52 +01:00
return err ;
2020-05-14 16:41:43 +02:00
}
2015-12-28 23:31:52 +01:00
}
2023-04-19 16:10:35 +02:00
if ( p_class - > has_static_data ) {
Error err = OK ;
GDScriptFunction * func = _make_static_initializer ( err , p_script , p_class ) ;
p_script - > static_initializer = func ;
if ( err ) {
return err ;
}
}
2015-01-03 04:57:02 +01:00
# ifdef DEBUG_ENABLED
2016-06-02 01:22:02 +02:00
//validate instances if keeping state
if ( p_keep_state ) {
2022-05-13 15:04:37 +02:00
for ( RBSet < Object * > : : Element * E = p_script - > instances . front ( ) ; E ; ) {
RBSet < Object * > : : Element * N = E - > next ( ) ;
2016-06-02 01:22:02 +02:00
ScriptInstance * si = E - > get ( ) - > get_script_instance ( ) ;
if ( si - > is_placeholder ( ) ) {
2016-06-02 01:44:34 +02:00
# ifdef TOOLS_ENABLED
2016-06-02 01:22:02 +02:00
PlaceHolderScriptInstance * psi = static_cast < PlaceHolderScriptInstance * > ( si ) ;
if ( p_script - > is_tool ( ) ) {
//re-create as an instance
p_script - > placeholders . erase ( psi ) ; //remove placeholder
2017-11-16 18:38:18 +01:00
GDScriptInstance * instance = memnew ( GDScriptInstance ) ;
2021-06-04 18:03:15 +02:00
instance - > base_ref_counted = Object : : cast_to < RefCounted > ( E - > get ( ) ) ;
2016-06-02 01:22:02 +02:00
instance - > members . resize ( p_script - > member_indices . size ( ) ) ;
instance - > script = Ref < GDScript > ( p_script ) ;
instance - > owner = E - > get ( ) ;
//needed for hot reloading
2021-08-09 22:13:42 +02:00
for ( const KeyValue < StringName , GDScript : : MemberInfo > & F : p_script - > member_indices ) {
instance - > member_indices_cache [ F . key ] = F . value . index ;
2016-06-02 01:22:02 +02:00
}
instance - > owner - > set_script_instance ( instance ) ;
Fix misc. source comment typos
Found using `codespell -q 3 -S ./thirdparty,*.po -L ang,ba,cas,dof,doubleclick,fave,hist,leapyear,lod,nd,numer,ois,paket,seeked,sinc,switchs,te,uint -D ~/Projects/codespell/codespell_lib/data/dictionary.txt `
2019-09-19 20:36:39 +02:00
/* STEP 2, INITIALIZE AND CONSTRUCT */
2016-06-02 01:22:02 +02:00
2020-02-19 20:27:19 +01:00
Callable : : CallError ce ;
2020-04-02 01:20:12 +02:00
p_script - > initializer - > call ( instance , nullptr , 0 , ce ) ;
2016-06-02 01:22:02 +02:00
2020-02-19 20:27:19 +01:00
if ( ce . error ! = Callable : : CallError : : CALL_OK ) {
2021-03-12 14:35:16 +01:00
//well, tough luck, not gonna do anything here
2016-06-02 01:22:02 +02:00
}
}
2023-04-21 15:32:26 +02:00
# endif // TOOLS_ENABLED
2016-06-02 01:22:02 +02:00
} else {
2017-11-16 18:38:18 +01:00
GDScriptInstance * gi = static_cast < GDScriptInstance * > ( si ) ;
2016-06-02 01:22:02 +02:00
gi - > reload_members ( ) ;
}
E = N ;
}
}
2023-04-21 15:32:26 +02:00
# endif //DEBUG_ENABLED
2016-02-29 13:56:36 +01:00
2023-04-19 16:10:35 +02:00
has_static_data = p_class - > has_static_data ;
2020-05-02 00:14:56 +02:00
for ( int i = 0 ; i < p_class - > members . size ( ) ; i + + ) {
if ( p_class - > members [ i ] . type ! = GDScriptParser : : ClassNode : : Member : : CLASS ) {
continue ;
}
const GDScriptParser : : ClassNode * inner_class = p_class - > members [ i ] . m_class ;
StringName name = inner_class - > identifier - > name ;
2019-03-16 17:49:29 +01:00
GDScript * subclass = p_script - > subclasses [ name ] . ptr ( ) ;
2018-05-30 04:16:52 +02:00
2022-11-08 12:51:20 +01:00
Error err = _compile_class ( subclass , inner_class , p_keep_state ) ;
2018-05-30 04:16:52 +02:00
if ( err ) {
return err ;
}
2023-04-19 16:10:35 +02:00
has_static_data = has_static_data | | inner_class - > has_static_data ;
2018-05-30 04:16:52 +02:00
}
2024-06-02 11:07:47 +02:00
p_script - > _static_default_init ( ) ;
2018-05-30 04:16:56 +02:00
p_script - > valid = true ;
2014-02-10 02:10:30 +01:00
return OK ;
}
2022-11-30 22:14:24 +01:00
void GDScriptCompiler : : convert_to_initializer_type ( Variant & p_variant , const GDScriptParser : : VariableNode * p_node ) {
// Set p_variant to the value of p_node's initializer, with the type of p_node's variable.
GDScriptParser : : DataType member_t = p_node - > datatype ;
GDScriptParser : : DataType init_t = p_node - > initializer - > datatype ;
if ( member_t . is_hard_type ( ) & & init_t . is_hard_type ( ) & &
member_t . kind = = GDScriptParser : : DataType : : BUILTIN & & init_t . kind = = GDScriptParser : : DataType : : BUILTIN ) {
if ( Variant : : can_convert_strict ( init_t . builtin_type , member_t . builtin_type ) ) {
Variant * v = & p_node - > initializer - > reduced_value ;
Callable : : CallError ce ;
Variant : : construct ( member_t . builtin_type , p_variant , const_cast < const Variant * * > ( & v ) , 1 , ce ) ;
}
}
}
2022-11-08 12:51:20 +01:00
void GDScriptCompiler : : make_scripts ( GDScript * p_script , const GDScriptParser : : ClassNode * p_class , bool p_keep_state ) {
p_script - > fully_qualified_name = p_class - > fqcn ;
2023-08-28 18:20:10 +02:00
p_script - > local_name = p_class - > identifier ? p_class - > identifier - > name : StringName ( ) ;
p_script - > global_name = p_class - > get_global_name ( ) ;
2023-08-24 12:49:20 +02:00
p_script - > simplified_icon_path = p_class - > simplified_icon_path ;
2022-11-08 12:51:20 +01:00
2022-05-13 15:04:37 +02:00
HashMap < StringName , Ref < GDScript > > old_subclasses ;
2018-05-30 04:16:52 +02:00
if ( p_keep_state ) {
old_subclasses = p_script - > subclasses ;
}
2019-03-16 17:49:29 +01:00
p_script - > subclasses . clear ( ) ;
2020-05-02 00:14:56 +02:00
for ( int i = 0 ; i < p_class - > members . size ( ) ; i + + ) {
if ( p_class - > members [ i ] . type ! = GDScriptParser : : ClassNode : : Member : : CLASS ) {
continue ;
}
const GDScriptParser : : ClassNode * inner_class = p_class - > members [ i ] . m_class ;
StringName name = inner_class - > identifier - > name ;
2018-05-30 04:16:52 +02:00
Ref < GDScript > subclass ;
if ( old_subclasses . has ( name ) ) {
subclass = old_subclasses [ name ] ;
} else {
2022-11-08 12:51:20 +01:00
subclass = GDScriptLanguage : : get_singleton ( ) - > get_orphan_subclass ( inner_class - > fqcn ) ;
}
if ( subclass . is_null ( ) ) {
subclass . instantiate ( ) ;
2018-05-30 04:16:52 +02:00
}
2019-03-16 17:49:29 +01:00
subclass - > _owner = p_script ;
2022-11-08 12:51:20 +01:00
subclass - > path = p_script - > path ;
2019-03-16 17:49:29 +01:00
p_script - > subclasses . insert ( name , subclass ) ;
2018-05-30 04:16:52 +02:00
2022-11-08 12:51:20 +01:00
make_scripts ( subclass . ptr ( ) , inner_class , p_keep_state ) ;
2018-05-30 04:16:52 +02:00
}
}
2023-09-13 09:40:48 +02:00
GDScriptCompiler : : FunctionLambdaInfo GDScriptCompiler : : _get_function_replacement_info ( GDScriptFunction * p_func , int p_index , int p_depth , GDScriptFunction * p_parent_func ) {
FunctionLambdaInfo info ;
info . function = p_func ;
info . parent = p_parent_func ;
2023-12-27 21:05:20 +01:00
info . script = p_func - > get_script ( ) ;
2023-09-13 09:40:48 +02:00
info . name = p_func - > get_name ( ) ;
info . line = p_func - > _initial_line ;
info . index = p_index ;
info . depth = p_depth ;
info . capture_count = 0 ;
info . use_self = false ;
info . arg_count = p_func - > _argument_count ;
info . default_arg_count = p_func - > _default_arg_count ;
info . sublambdas = _get_function_lambda_replacement_info ( p_func , p_depth , p_parent_func ) ;
2023-12-27 21:05:20 +01:00
ERR_FAIL_NULL_V ( info . script , info ) ;
GDScript : : LambdaInfo * extra_info = info . script - > lambda_info . getptr ( p_func ) ;
2023-09-13 09:40:48 +02:00
if ( extra_info ! = nullptr ) {
info . capture_count = extra_info - > capture_count ;
info . use_self = extra_info - > use_self ;
2023-12-27 21:05:20 +01:00
} else {
info . capture_count = 0 ;
info . use_self = false ;
2023-09-13 09:40:48 +02:00
}
return info ;
}
Vector < GDScriptCompiler : : FunctionLambdaInfo > GDScriptCompiler : : _get_function_lambda_replacement_info ( GDScriptFunction * p_func , int p_depth , GDScriptFunction * p_parent_func ) {
Vector < FunctionLambdaInfo > result ;
// Only scrape the lambdas inside p_func.
for ( int i = 0 ; i < p_func - > lambdas . size ( ) ; + + i ) {
result . push_back ( _get_function_replacement_info ( p_func - > lambdas [ i ] , i , p_depth + 1 , p_func ) ) ;
}
return result ;
}
GDScriptCompiler : : ScriptLambdaInfo GDScriptCompiler : : _get_script_lambda_replacement_info ( GDScript * p_script ) {
ScriptLambdaInfo info ;
if ( p_script - > implicit_initializer ) {
info . implicit_initializer_info = _get_function_lambda_replacement_info ( p_script - > implicit_initializer ) ;
}
if ( p_script - > implicit_ready ) {
info . implicit_ready_info = _get_function_lambda_replacement_info ( p_script - > implicit_ready ) ;
}
if ( p_script - > static_initializer ) {
info . static_initializer_info = _get_function_lambda_replacement_info ( p_script - > static_initializer ) ;
}
for ( const KeyValue < StringName , GDScriptFunction * > & E : p_script - > member_functions ) {
info . member_function_infos . insert ( E . key , _get_function_lambda_replacement_info ( E . value ) ) ;
}
for ( const KeyValue < StringName , Ref < GDScript > > & KV : p_script - > get_subclasses ( ) ) {
info . subclass_info . insert ( KV . key , _get_script_lambda_replacement_info ( KV . value . ptr ( ) ) ) ;
}
return info ;
}
bool GDScriptCompiler : : _do_function_infos_match ( const FunctionLambdaInfo & p_old_info , const FunctionLambdaInfo * p_new_info ) {
if ( p_new_info = = nullptr ) {
return false ;
}
if ( p_new_info - > capture_count ! = p_old_info . capture_count | | p_new_info - > use_self ! = p_old_info . use_self ) {
return false ;
}
int old_required_arg_count = p_old_info . arg_count - p_old_info . default_arg_count ;
int new_required_arg_count = p_new_info - > arg_count - p_new_info - > default_arg_count ;
if ( new_required_arg_count > old_required_arg_count | | p_new_info - > arg_count < old_required_arg_count ) {
return false ;
}
return true ;
}
void GDScriptCompiler : : _get_function_ptr_replacements ( HashMap < GDScriptFunction * , GDScriptFunction * > & r_replacements , const FunctionLambdaInfo & p_old_info , const FunctionLambdaInfo * p_new_info ) {
ERR_FAIL_COND ( r_replacements . has ( p_old_info . function ) ) ;
if ( ! _do_function_infos_match ( p_old_info , p_new_info ) ) {
p_new_info = nullptr ;
}
r_replacements . insert ( p_old_info . function , p_new_info ! = nullptr ? p_new_info - > function : nullptr ) ;
_get_function_ptr_replacements ( r_replacements , p_old_info . sublambdas , p_new_info ! = nullptr ? & p_new_info - > sublambdas : nullptr ) ;
}
void GDScriptCompiler : : _get_function_ptr_replacements ( HashMap < GDScriptFunction * , GDScriptFunction * > & r_replacements , const Vector < FunctionLambdaInfo > & p_old_infos , const Vector < FunctionLambdaInfo > * p_new_infos ) {
for ( int i = 0 ; i < p_old_infos . size ( ) ; + + i ) {
const FunctionLambdaInfo & old_info = p_old_infos [ i ] ;
const FunctionLambdaInfo * new_info = nullptr ;
if ( p_new_infos ! = nullptr & & p_new_infos - > size ( ) = = p_old_infos . size ( ) ) {
// For now only attempt if the size is the same.
new_info = & p_new_infos - > get ( i ) ;
}
_get_function_ptr_replacements ( r_replacements , old_info , new_info ) ;
}
}
void GDScriptCompiler : : _get_function_ptr_replacements ( HashMap < GDScriptFunction * , GDScriptFunction * > & r_replacements , const ScriptLambdaInfo & p_old_info , const ScriptLambdaInfo * p_new_info ) {
_get_function_ptr_replacements ( r_replacements , p_old_info . implicit_initializer_info , p_new_info ! = nullptr ? & p_new_info - > implicit_initializer_info : nullptr ) ;
_get_function_ptr_replacements ( r_replacements , p_old_info . implicit_ready_info , p_new_info ! = nullptr ? & p_new_info - > implicit_ready_info : nullptr ) ;
_get_function_ptr_replacements ( r_replacements , p_old_info . static_initializer_info , p_new_info ! = nullptr ? & p_new_info - > static_initializer_info : nullptr ) ;
for ( const KeyValue < StringName , Vector < FunctionLambdaInfo > > & old_kv : p_old_info . member_function_infos ) {
_get_function_ptr_replacements ( r_replacements , old_kv . value , p_new_info ! = nullptr ? p_new_info - > member_function_infos . getptr ( old_kv . key ) : nullptr ) ;
}
for ( int i = 0 ; i < p_old_info . other_function_infos . size ( ) ; + + i ) {
const FunctionLambdaInfo & old_other_info = p_old_info . other_function_infos [ i ] ;
const FunctionLambdaInfo * new_other_info = nullptr ;
if ( p_new_info ! = nullptr & & p_new_info - > other_function_infos . size ( ) = = p_old_info . other_function_infos . size ( ) ) {
// For now only attempt if the size is the same.
new_other_info = & p_new_info - > other_function_infos [ i ] ;
}
// Needs to be called on all old lambdas, even if there's no replacement.
_get_function_ptr_replacements ( r_replacements , old_other_info , new_other_info ) ;
}
for ( const KeyValue < StringName , ScriptLambdaInfo > & old_kv : p_old_info . subclass_info ) {
const ScriptLambdaInfo & old_subinfo = old_kv . value ;
const ScriptLambdaInfo * new_subinfo = p_new_info ! = nullptr ? p_new_info - > subclass_info . getptr ( old_kv . key ) : nullptr ;
_get_function_ptr_replacements ( r_replacements , old_subinfo , new_subinfo ) ;
}
}
2018-11-24 23:46:13 +01:00
Error GDScriptCompiler : : compile ( const GDScriptParser * p_parser , GDScript * p_script , bool p_keep_state ) {
2014-02-10 02:10:30 +01:00
err_line = - 1 ;
err_column = - 1 ;
error = " " ;
parser = p_parser ;
2018-11-24 23:46:13 +01:00
main_script = p_script ;
2020-05-02 00:14:56 +02:00
const GDScriptParser : : ClassNode * root = parser - > get_tree ( ) ;
2014-02-10 02:10:30 +01:00
source = p_script - > get_path ( ) ;
2023-09-13 09:40:48 +02:00
ScriptLambdaInfo old_lambda_info = _get_script_lambda_replacement_info ( p_script ) ;
2018-05-30 04:16:52 +02:00
// Create scripts for subclasses beforehand so they can be referenced
2022-11-08 12:51:20 +01:00
make_scripts ( p_script , root , p_keep_state ) ;
2018-05-30 04:16:52 +02:00
2022-11-08 12:51:20 +01:00
main_script - > _owner = nullptr ;
2023-08-31 19:49:04 +02:00
Error err = _prepare_compilation ( main_script , parser - > get_tree ( ) , p_keep_state ) ;
2018-05-30 04:16:52 +02:00
2020-05-14 16:41:43 +02:00
if ( err ) {
2018-05-30 04:16:52 +02:00
return err ;
2020-05-14 16:41:43 +02:00
}
2018-05-30 04:16:52 +02:00
2022-11-08 12:51:20 +01:00
err = _compile_class ( main_script , root , p_keep_state ) ;
2020-05-14 16:41:43 +02:00
if ( err ) {
2014-02-10 02:10:30 +01:00
return err ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2023-09-13 09:40:48 +02:00
ScriptLambdaInfo new_lambda_info = _get_script_lambda_replacement_info ( p_script ) ;
HashMap < GDScriptFunction * , GDScriptFunction * > func_ptr_replacements ;
_get_function_ptr_replacements ( func_ptr_replacements , old_lambda_info , & new_lambda_info ) ;
2023-12-27 21:05:20 +01:00
main_script - > _recurse_replace_function_ptrs ( func_ptr_replacements ) ;
2023-09-13 09:40:48 +02:00
2023-04-19 16:10:35 +02:00
if ( has_static_data & & ! root - > annotated_static_unload ) {
GDScriptCache : : add_static_script ( p_script ) ;
}
2024-06-02 11:07:47 +02:00
return GDScriptCache : : finish_compiling ( main_script - > path ) ;
2014-02-10 02:10:30 +01:00
}
2017-11-16 18:38:18 +01:00
String GDScriptCompiler : : get_error ( ) const {
2014-02-10 02:10:30 +01:00
return error ;
}
2020-05-14 14:29:06 +02:00
2017-11-16 18:38:18 +01:00
int GDScriptCompiler : : get_error_line ( ) const {
2014-02-10 02:10:30 +01:00
return err_line ;
}
2020-05-14 14:29:06 +02:00
2017-11-16 18:38:18 +01:00
int GDScriptCompiler : : get_error_column ( ) const {
2014-02-10 02:10:30 +01:00
return err_column ;
}
2017-11-16 18:38:18 +01:00
GDScriptCompiler : : GDScriptCompiler ( ) {
2014-02-10 02:10:30 +01:00
}