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 */
2017-08-27 14:16:55 +02:00
/* https://godotengine.org */
2014-02-10 02:10:30 +01:00
/*************************************************************************/
2021-01-01 20:13:46 +01:00
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
2014-02-10 02:10:30 +01:00
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
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
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
2020-08-07 19:51:56 +02:00
if ( codegen . locals . has ( 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-03-05 16:44:50 +01:00
return _is_class_member_property ( codegen . script , p_name ) ;
2017-01-04 21:37:45 +01:00
}
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-03-05 16:44:50 +01:00
while ( scr ) {
2020-05-14 16:41:43 +02:00
if ( scr - > native . is_valid ( ) ) {
2017-03-05 16:44:50 +01:00
nc = scr - > native . ptr ( ) ;
2020-05-14 16:41:43 +02:00
}
2017-03-05 16:44:50 +01:00
scr = scr - > _base ;
2017-01-04 21:37:45 +01:00
}
2017-03-05 16:44:50 +01:00
ERR_FAIL_COND_V ( ! nc , false ) ;
2017-01-04 21:37:45 +01:00
2017-03-05 16:44:50 +01:00
return ClassDB : : has_property ( nc - > get_name ( ) , p_name ) ;
2017-01-04 21:37:45 +01:00
}
2017-11-16 18:38:18 +01:00
void GDScriptCompiler : : _set_error ( const String & p_error , const GDScriptParser : : Node * p_node ) {
2020-05-14 16:41:43 +02:00
if ( error ! = " " ) {
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
2017-03-05 16:44:50 +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 {
2017-03-05 16:44:50 +01:00
err_line = 0 ;
err_column = 0 ;
2015-01-03 04:57:02 +01:00
}
2014-02-10 02:10:30 +01:00
}
2020-09-10 01:26:07 +02:00
GDScriptDataType GDScriptCompiler : : _gdtype_from_datatype ( const GDScriptParser : : DataType & p_datatype , GDScript * p_owner ) const {
2020-07-16 03:02:44 +02:00
if ( ! p_datatype . is_set ( ) | | ! p_datatype . is_hard_type ( ) ) {
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 : {
result . kind = GDScriptDataType : : NATIVE ;
result . native_type = p_datatype . native_type ;
} break ;
case GDScriptParser : : DataType : : SCRIPT : {
result . kind = GDScriptDataType : : SCRIPT ;
2020-12-04 18:58:59 +01:00
result . script_type_ref = Ref < Script > ( p_datatype . script_type ) ;
result . script_type = result . script_type_ref . ptr ( ) ;
2018-05-30 04:16:57 +02:00
result . native_type = result . script_type - > get_instance_base_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 : {
2019-03-16 17:49:29 +01:00
// Locate class by constructing the path to it and following that path
2020-06-11 00:53:25 +02:00
GDScriptParser : : ClassNode * class_type = p_datatype . class_type ;
2020-05-02 00:14:56 +02:00
if ( class_type ) {
2020-12-15 13:04:21 +01:00
if ( class_type - > fqcn . begins_with ( main_script - > path ) | | ( ! main_script - > name . is_empty ( ) & & class_type - > fqcn . begins_with ( main_script - > name ) ) ) {
2020-07-16 03:02:44 +02:00
// Local class.
List < StringName > names ;
while ( class_type - > outer ) {
names . push_back ( class_type - > identifier - > name ) ;
class_type = class_type - > outer ;
}
Ref < GDScript > script = Ref < GDScript > ( main_script ) ;
while ( names . back ( ) ) {
if ( ! script - > subclasses . has ( names . back ( ) - > get ( ) ) ) {
ERR_PRINT ( " Parser bug: Cannot locate datatype class. " ) ;
result . has_type = false ;
return GDScriptDataType ( ) ;
}
script = script - > subclasses [ names . back ( ) - > get ( ) ] ;
names . pop_back ( ) ;
2020-05-02 00:14:56 +02:00
}
2020-07-16 03:02:44 +02:00
result . kind = GDScriptDataType : : GDSCRIPT ;
2020-12-04 18:58:59 +01:00
result . script_type_ref = script ;
result . script_type = result . script_type_ref . ptr ( ) ;
2020-07-16 03:02:44 +02:00
result . native_type = script - > get_instance_base_type ( ) ;
} else {
result . kind = GDScriptDataType : : GDSCRIPT ;
2020-12-04 18:58:59 +01:00
result . script_type_ref = GDScriptCache : : get_shallow_script ( p_datatype . script_path , main_script - > path ) ;
result . script_type = result . script_type_ref . ptr ( ) ;
2020-07-16 03:02:44 +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 ;
2020-07-16 03:02:44 +02:00
case GDScriptParser : : DataType : : ENUM_VALUE :
result . has_type = true ;
result . kind = GDScriptDataType : : BUILTIN ;
result . builtin_type = Variant : : INT ;
break ;
case GDScriptParser : : DataType : : ENUM :
result . has_type = true ;
result . kind = GDScriptDataType : : BUILTIN ;
result . builtin_type = Variant : : DICTIONARY ;
break ;
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
}
2021-03-09 16:32:35 +01:00
if ( p_datatype . has_container_element_type ( ) ) {
result . set_container_element_type ( _gdtype_from_datatype ( p_datatype . get_container_element_type ( ) ) ) ;
}
2020-09-10 01:26:07 +02:00
// Only hold strong reference to the script if it's not the owner of the
// element qualified with this type, to avoid cyclic references (leaks).
2020-12-04 18:58:59 +01:00
if ( result . script_type & & result . script_type = = p_owner ) {
result . script_type_ref = Ref < Script > ( ) ;
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 ;
}
}
static bool _have_exact_arguments ( const MethodBind * p_method , const Vector < GDScriptCodeGenerator : : Address > & p_arguments ) {
if ( p_method - > get_argument_count ( ) ! = p_arguments . size ( ) ) {
// ptrcall won't work with default arguments.
return false ;
}
MethodInfo info ;
ClassDB : : get_method_info ( p_method - > get_instance_class ( ) , p_method - > get_name ( ) , & info ) ;
for ( int i = 0 ; i < p_arguments . size ( ) ; i + + ) {
const PropertyInfo & prop = info . arguments [ i ] ;
if ( ! _is_exact_type ( prop , p_arguments [ i ] . type ) ) {
return false ;
}
}
return true ;
}
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address GDScriptCompiler : : _parse_expression ( CodeGen & codegen , Error & r_error , const GDScriptParser : : ExpressionNode * p_expression , bool p_root , bool p_initializer , const GDScriptCodeGenerator : : Address & p_index_addr ) {
2020-06-11 00:53:25 +02:00
if ( p_expression - > is_constant ) {
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 ;
2017-03-05 16:44:50 +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 ;
2020-08-07 19:51:56 +02:00
// Try function parameters.
if ( codegen . parameters . has ( identifier ) ) {
return codegen . parameters [ identifier ] ;
2018-01-20 21:41:05 +01:00
}
2020-08-07 19:51:56 +02:00
// Try local variables and constants.
if ( ! p_initializer & & codegen . locals . has ( identifier ) ) {
return codegen . locals [ identifier ] ;
2020-05-02 00:14:56 +02:00
}
2020-08-07 19:51:56 +02:00
// Try class members.
2017-03-05 16:44:50 +01:00
if ( _is_class_member_property ( codegen , identifier ) ) {
2020-08-07 19:51:56 +02:00
// Get property.
GDScriptCodeGenerator : : Address temp = codegen . add_temporary ( ) ; // TODO: Could get the type of the class member here.
gen - > write_get_member ( temp , identifier ) ;
return temp ;
2017-01-04 21:37:45 +01:00
}
2020-08-07 19:51:56 +02:00
// Try members.
2020-05-02 00:14:56 +02:00
if ( ! codegen . function_node | | ! codegen . function_node - > is_static ) {
2020-08-07 19:51:56 +02:00
// Try member variables.
2014-02-10 02:10:30 +01:00
if ( codegen . script - > member_indices . has ( identifier ) ) {
2020-06-01 21:41:05 +02:00
if ( codegen . script - > member_indices [ identifier ] . getter ! = StringName ( ) & & codegen . script - > member_indices [ identifier ] . getter ! = codegen . function_name ) {
// Perform getter.
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address temp = codegen . add_temporary ( ) ;
Vector < GDScriptCodeGenerator : : Address > args ; // No argument needed.
gen - > write_call_self ( temp , codegen . script - > member_indices [ identifier ] . getter , args ) ;
return temp ;
2020-06-01 21:41:05 +02:00
} else {
2020-08-07 19:51:56 +02:00
// No getter or inside getter: direct member access.,
2020-06-01 21:41:05 +02:00
int idx = codegen . script - > member_indices [ identifier ] . index ;
2020-08-07 19:51:56 +02:00
return GDScriptCodeGenerator : : Address ( GDScriptCodeGenerator : : Address : : MEMBER , idx , codegen . script - > get_member_type ( identifier ) ) ;
2020-06-01 21:41:05 +02:00
}
2014-02-10 02:10:30 +01:00
}
}
2020-08-07 19:51:56 +02:00
// Try class constants.
2021-02-24 15:02:33 +01:00
{
GDScript * owner = codegen . script ;
while ( owner ) {
GDScript * scr = owner ;
GDScriptNativeClass * nc = nullptr ;
while ( scr ) {
if ( scr - > constants . has ( identifier ) ) {
return GDScriptCodeGenerator : : Address ( GDScriptCodeGenerator : : Address : : CLASS_CONSTANT , gen - > add_or_get_name ( identifier ) ) ; // TODO: Get type here.
}
if ( scr - > native . is_valid ( ) ) {
nc = scr - > native . ptr ( ) ;
}
scr = scr - > _base ;
2014-05-02 17:40:34 +02:00
}
2021-02-24 15:02:33 +01:00
// Class C++ integer constant.
if ( nc ) {
bool success = false ;
int constant = ClassDB : : get_integer_constant ( nc - > get_name ( ) , identifier , & success ) ;
if ( success ) {
return codegen . add_constant ( constant ) ;
}
2020-05-14 16:41:43 +02:00
}
2021-02-24 15:02:33 +01:00
owner = owner - > _owner ;
2014-02-10 02:10:30 +01:00
}
2021-02-24 15:02:33 +01:00
}
2014-02-10 02:10:30 +01:00
2021-02-24 15:02:33 +01:00
// Try signals and methods (can be made callables).
{
if ( codegen . class_node - > members_indices . has ( identifier ) ) {
const GDScriptParser : : ClassNode : : Member & member = codegen . class_node - > members [ codegen . class_node - > members_indices [ 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.
GDScriptCodeGenerator : : Address self ( GDScriptCodeGenerator : : Address : : SELF ) ;
gen - > write_get_named ( temp , identifier , self ) ;
return temp ;
2014-02-10 02:10:30 +01:00
}
}
2021-02-24 15:02:33 +01: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 ;
}
2014-02-10 02:10:30 +01:00
2021-02-24 15:02:33 +01:00
if ( nc & & ( ClassDB : : has_signal ( nc - > get_name ( ) , identifier ) | | ClassDB : : has_method ( nc - > get_name ( ) , identifier ) ) ) {
2020-05-02 00:14:56 +02:00
// Get like it was a property.
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address temp = codegen . add_temporary ( ) ; // TODO: Get type here.
GDScriptCodeGenerator : : Address self ( GDScriptCodeGenerator : : Address : : SELF ) ;
gen - > write_get_named ( temp , identifier , self ) ;
return temp ;
2020-05-02 00:14:56 +02:00
}
}
2014-02-10 02:10:30 +01:00
if ( GDScriptLanguage : : get_singleton ( ) - > get_global_map ( ) . has ( identifier ) ) {
int idx = GDScriptLanguage : : get_singleton ( ) - > get_global_map ( ) [ identifier ] ;
2020-08-07 19:51:56 +02:00
return GDScriptCodeGenerator : : Address ( GDScriptCodeGenerator : : Address : : GLOBAL , idx ) ; // TODO: Get type.
2014-02-10 02:10:30 +01:00
}
2020-08-07 19:51:56 +02:00
// Try global classes.
2018-07-16 00:29:00 +02:00
if ( ScriptServer : : is_global_class ( identifier ) ) {
const GDScriptParser : : ClassNode * class_node = codegen . class_node ;
2020-05-02 00:14:56 +02:00
while ( class_node - > outer ) {
class_node = class_node - > outer ;
2018-07-16 00:29:00 +02:00
}
2020-07-16 03:02:44 +02:00
RES res ;
2018-07-16 00:29:00 +02:00
2020-07-16 03:02:44 +02:00
if ( class_node - > identifier & & class_node - > identifier - > name = = identifier ) {
res = Ref < GDScript > ( main_script ) ;
} else {
res = ResourceLoader : : load ( ScriptServer : : get_global_class_path ( identifier ) ) ;
if ( res . is_null ( ) ) {
_set_error ( " Can't load global class " + String ( identifier ) + " , cyclic reference? " , p_expression ) ;
2020-08-07 19:51:56 +02:00
r_error = ERR_COMPILATION_FAILED ;
return GDScriptCodeGenerator : : Address ( ) ;
2020-07-16 03:02:44 +02:00
}
2018-07-16 00:29:00 +02:00
}
2020-08-07 19:51:56 +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
if ( GDScriptLanguage : : get_singleton ( ) - > get_named_globals_map ( ) . has ( identifier ) ) {
2020-08-07 19:51:56 +02:00
return GDScriptCodeGenerator : : Address ( GDScriptCodeGenerator : : Address : : NAMED_GLOBAL , gen - > add_or_get_name ( identifier ) ) ; // TODO: Get type.
2018-05-01 16:06:23 +02:00
}
# endif
2020-08-07 19:51:56 +02:00
// Not found, error.
2017-03-05 16:44:50 +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 ) {
2017-03-05 16:44:50 +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.
2021-03-09 16:32:35 +01:00
GDScriptDataType array_type = _gdtype_from_datatype ( an - > get_datatype ( ) ) ;
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address result = codegen . add_temporary ( array_type ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +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
}
2021-03-09 16:32:35 +01:00
if ( array_type . has_container_element_type ( ) ) {
gen - > write_construct_typed_array ( result , array_type . get_container_element_type ( ) , values ) ;
} 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.
GDScriptDataType dict_type ;
dict_type . has_type = true ;
dict_type . kind = GDScriptDataType : : BUILTIN ;
dict_type . builtin_type = Variant : : DICTIONARY ;
GDScriptCodeGenerator : : Address result = codegen . add_temporary ( dict_type ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +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 :
// Lua-style: key is an identifier interpreted as string.
String key = static_cast < const GDScriptParser : : IdentifierNode * > ( dn - > elements [ i ] . key ) - > name ;
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
}
2020-08-07 19:51:56 +02:00
gen - > write_construct_dictionary ( result , elements ) ;
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 ) ;
2020-08-07 19:51:56 +02:00
GDScriptDataType cast_type = _gdtype_from_datatype ( cn - > cast_type - > get_datatype ( ) ) ;
2018-05-30 04:16:56 +02:00
2020-08-07 19:51:56 +02:00
// Create temporary for result first since it will be deleted last.
GDScriptCodeGenerator : : Address result = codegen . add_temporary ( cast_type ) ;
2018-05-30 04:16:56 +02:00
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address source = _parse_expression ( codegen , r_error , cn - > operand ) ;
2019-03-16 17:49:29 +01:00
2020-08-07 19:51:56 +02:00
gen - > write_cast ( result , source , cast_type ) ;
2018-05-30 04:16:56 +02:00
2020-08-07 19:51:56 +02:00
if ( source . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
gen - > pop_temporary ( ) ;
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 ) ;
2020-08-07 19:51:56 +02:00
GDScriptDataType type = _gdtype_from_datatype ( call - > get_datatype ( ) ) ;
GDScriptCodeGenerator : : Address 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
2020-11-26 15:56:32 +01:00
if ( ! call - > is_super & & call - > callee - > type = = GDScriptParser : : Node : : IDENTIFIER & & GDScriptParser : : get_builtin_type ( call - > function_name ) ! = Variant : : VARIANT_MAX ) {
2020-08-07 19:51:56 +02:00
// Construct a built-in type.
Variant : : Type vtype = GDScriptParser : : get_builtin_type ( static_cast < GDScriptParser : : IdentifierNode * > ( call - > callee ) - > name ) ;
2014-02-10 02:10:30 +01:00
2020-08-07 19:51:56 +02:00
gen - > write_construct ( result , vtype , 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.
gen - > write_call_utility ( result , call - > function_name , arguments ) ;
} else if ( ! call - > is_super & & call - > callee - > type = = GDScriptParser : : Node : : IDENTIFIER & & GDScriptUtilityFunctions : : function_exists ( call - > function_name ) ) {
// GDScript utility function.
gen - > write_call_gdscript_utility ( result , GDScriptUtilityFunctions : : get_function ( 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.
2020-08-07 19:51:56 +02: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 ) ;
if ( _have_exact_arguments ( method , arguments ) ) {
// Exact arguments, use ptrcall.
gen - > write_call_ptrcall ( result , self , method , arguments ) ;
} else {
// Not exact arguments, but still can use method bind call.
gen - > write_call_method_bind ( result , self , method , arguments ) ;
}
} else if ( ( 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 ;
2020-12-05 00:42:45 +01:00
if ( within_await ) {
gen - > write_call_async ( result , self , call - > function_name , arguments ) ;
} else {
gen - > write_call ( result , self , call - > function_name , arguments ) ;
}
2020-05-02 00:14:56 +02:00
} else {
2020-12-05 00:42:45 +01:00
if ( within_await ) {
gen - > write_call_self_async ( result , call - > function_name , arguments ) ;
} else {
gen - > write_call_self ( result , call - > function_name , arguments ) ;
}
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 ) ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
if ( subscript - > is_attribute ) {
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address base = _parse_expression ( codegen , r_error , subscript - > base ) ;
if ( r_error ) {
return GDScriptCodeGenerator : : Address ( ) ;
}
if ( within_await ) {
gen - > write_call_async ( result , base , call - > function_name , arguments ) ;
2020-11-17 14:44:52 +01: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 ;
} else {
class_name = base . type . native_type = = StringName ( ) ? base . type . script_type - > get_instance_base_type ( ) : base . type . native_type ;
}
if ( ClassDB : : class_exists ( class_name ) & & ClassDB : : has_method ( class_name , call - > function_name ) ) {
MethodBind * method = ClassDB : : get_method ( class_name , call - > function_name ) ;
if ( _have_exact_arguments ( method , arguments ) ) {
// Exact arguments, use ptrcall.
gen - > write_call_ptrcall ( result , base , method , arguments ) ;
} else {
// Not exact arguments, but still can use method bind call.
gen - > write_call_method_bind ( result , base , method , arguments ) ;
}
} else {
gen - > write_call ( result , base , call - > function_name , arguments ) ;
}
2020-11-18 14:32:28 +01:00
} else if ( base . type . has_type & & base . type . kind = = GDScriptDataType : : BUILTIN ) {
gen - > write_call_builtin_type ( result , base , base . type . builtin_type , call - > function_name , arguments ) ;
2020-08-07 19:51:56 +02:00
} else {
gen - > write_call ( result , base , call - > function_name , arguments ) ;
2020-05-14 16:41:43 +02:00
}
2020-08-07 19:51:56 +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-05-02 00:14:56 +02:00
String node_name ;
if ( get_node - > string ! = nullptr ) {
node_name + = String ( get_node - > string - > value ) ;
} else {
for ( int i = 0 ; i < get_node - > chain . size ( ) ; i + + ) {
if ( i > 0 ) {
node_name + = " / " ;
}
node_name + = get_node - > chain [ i ] - > name ;
}
}
2014-02-10 02:10:30 +01:00
2020-08-07 19:51:56 +02:00
Vector < GDScriptCodeGenerator : : Address > args ;
args . push_back ( codegen . add_constant ( NodePath ( node_name ) ) ) ;
GDScriptCodeGenerator : : Address result = codegen . add_temporary ( _gdtype_from_datatype ( get_node - > get_datatype ( ) ) ) ;
MethodBind * get_node_method = ClassDB : : get_method ( " Node " , " get_node " ) ;
2020-11-17 14:44:52 +01:00
gen - > write_call_ptrcall ( 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
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address result = codegen . add_temporary ( _gdtype_from_datatype ( p_expression - > get_datatype ( ) ) ) ;
2020-05-02 00:14:56 +02:00
within_await = true ;
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address argument = _parse_expression ( codegen , r_error , await - > to_await ) ;
2020-05-02 00:14:56 +02:00
within_await = false ;
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 ) ;
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address result = codegen . add_temporary ( _gdtype_from_datatype ( subscript - > get_datatype ( ) ) ) ;
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 ;
if ( p_index_addr . mode ! = GDScriptCodeGenerator : : Address : : NIL ) {
2020-05-02 00:14:56 +02:00
index = p_index_addr ;
} else 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 ;
const Map < StringName , GDScript : : MemberInfo > : : Element * MI = codegen . script - > member_indices . find ( identifier - > name ) ;
2016-06-25 15:59:39 +02:00
# ifdef DEBUG_ENABLED
2020-06-01 21:41:05 +02:00
if ( MI & & MI - > get ( ) . 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
2020-05-02 00:14:56 +02:00
if ( MI & & MI - > get ( ) . 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).
return GDScriptCodeGenerator : : Address ( GDScriptCodeGenerator : : Address : : MEMBER , MI - > get ( ) . index , _gdtype_from_datatype ( subscript - > get_datatype ( ) ) ) ;
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 {
if ( subscript - > index - > type = = GDScriptParser : : Node : : LITERAL & & static_cast < const GDScriptParser : : LiteralNode * > ( subscript - > index ) - > value . get_type ( ) = = Variant : : STRING ) {
2020-08-07 19:51:56 +02:00
// Also, somehow, named (speed up anyway).
name = static_cast < const GDScriptParser : : LiteralNode * > ( subscript - > index ) - > 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
GDScriptCodeGenerator : : Address result = codegen . add_temporary ( ) ;
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 ) ;
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address result = codegen . add_temporary ( ) ;
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-05-02 00:14:56 +02:00
case GDScriptParser : : BinaryOpNode : : OP_TYPE_TEST : {
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address operand = _parse_expression ( codegen , r_error , binary - > left_operand ) ;
2017-03-05 16:44:50 +01:00
2020-05-02 00:14:56 +02:00
if ( binary - > right_operand - > type = = GDScriptParser : : Node : : IDENTIFIER & & GDScriptParser : : get_builtin_type ( static_cast < const GDScriptParser : : IdentifierNode * > ( binary - > right_operand ) - > name ) ! = Variant : : VARIANT_MAX ) {
2020-08-07 19:51:56 +02:00
// `is` with builtin type)
Variant : : Type type = GDScriptParser : : get_builtin_type ( static_cast < const GDScriptParser : : IdentifierNode * > ( binary - > right_operand ) - > name ) ;
gen - > write_type_test_builtin ( result , operand , type ) ;
2020-05-02 00:14:56 +02:00
} else {
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address type = _parse_expression ( codegen , r_error , binary - > right_operand ) ;
if ( r_error ) {
return GDScriptCodeGenerator : : Address ( ) ;
}
gen - > write_type_test ( result , operand , type ) ;
if ( type . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
gen - > pop_temporary ( ) ;
2020-05-02 00:14:56 +02:00
}
2020-05-14 16:41:43 +02:00
}
2020-08-07 19:51:56 +02:00
} break ;
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 ) ;
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address result = codegen . add_temporary ( _gdtype_from_datatype ( ternary - > get_datatype ( ) ) ) ;
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 ;
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 ) {
const Map < StringName , GDScript : : MemberInfo > : : Element * MI = codegen . script - > member_indices . find ( subscript - > attribute - > name ) ;
if ( MI & & MI - > get ( ) . setter = = codegen . function_name ) {
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
2020-05-02 00:14:56 +02:00
StringName assign_property ;
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 ) {
2020-08-07 19:51:56 +02:00
// Check for a built-in 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 ) ;
if ( _is_class_member_property ( codegen , identifier - > name ) ) {
assign_property = identifier - > name ;
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 ) ;
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
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 ( ) ;
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address value = codegen . add_temporary ( _gdtype_from_datatype ( subscript_elem - > get_datatype ( ) ) ) ;
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 ) {
GDScriptCodeGenerator : : Address value = codegen . add_temporary ( ) ;
if ( subscript - > is_attribute ) {
gen - > write_get_named ( value , name , prev_base ) ;
} else {
gen - > write_get ( value , key , prev_base ) ;
}
2020-11-26 18:41:55 +01:00
gen - > write_binary_operator ( value , assignment - > variant_op , value , assigned ) ;
2020-08-07 19:51:56 +02:00
if ( assigned . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
gen - > pop_temporary ( ) ;
}
assigned = 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
// 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
}
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.
for ( List < ChainInfo > : : Element * E = set_chain . front ( ) ; E ; E = E - > next ( ) ) {
const ChainInfo & info = E - > get ( ) ;
if ( ! info . is_named ) {
gen - > write_set ( info . base , info . key , assigned ) ;
if ( info . key . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
gen - > pop_temporary ( ) ;
}
} else {
gen - > write_set_named ( info . base , info . name , assigned ) ;
}
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
2020-08-07 19:51:56 +02:00
// If this is a local member, also assign to it.
// This allow things like: position.x += 2.0
if ( assign_property ! = StringName ( ) ) {
gen - > write_set_member ( assigned , assign_property ) ;
}
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.
GDScriptCodeGenerator : : Address assigned = _parse_expression ( codegen , r_error , assignment - > assigned_value ) ;
if ( r_error ) {
return GDScriptCodeGenerator : : Address ( ) ;
2020-05-02 00:14:56 +02:00
}
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address assign_temp = assigned ;
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
2020-08-07 19:51:56 +02:00
if ( assignment - > operation ! = GDScriptParser : : AssignmentNode : : OP_NONE ) {
GDScriptCodeGenerator : : Address member = codegen . add_temporary ( ) ;
gen - > write_get_member ( member , name ) ;
2020-11-26 18:41:55 +01:00
gen - > write_binary_operator ( assigned , assignment - > variant_op , member , assigned ) ;
2020-08-07 19:51:56 +02:00
gen - > pop_temporary ( ) ;
}
2014-02-10 02:10:30 +01:00
2020-08-07 19:51:56 +02:00
gen - > write_set_member ( assigned , name ) ;
2014-02-10 02:10:30 +01:00
2020-08-07 19:51:56 +02:00
if ( assign_temp . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
gen - > pop_temporary ( ) ;
}
} else {
// Regular assignment.
GDScriptCodeGenerator : : Address target ;
2020-06-01 21:41:05 +02:00
bool has_setter = false ;
bool is_in_setter = false ;
StringName setter_function ;
if ( assignment - > assignee - > type = = GDScriptParser : : Node : : IDENTIFIER ) {
StringName var_name = static_cast < const GDScriptParser : : IdentifierNode * > ( assignment - > assignee ) - > name ;
2020-08-07 19:51:56 +02:00
if ( ! codegen . locals . has ( var_name ) & & codegen . script - > member_indices . has ( var_name ) ) {
2020-06-01 21:41:05 +02:00
setter_function = codegen . script - > member_indices [ var_name ] . setter ;
if ( setter_function ! = StringName ( ) ) {
has_setter = true ;
is_in_setter = setter_function = = codegen . function_name ;
2020-08-07 19:51:56 +02:00
target . mode = GDScriptCodeGenerator : : Address : : MEMBER ;
target . address = codegen . script - > member_indices [ var_name ] . index ;
2020-06-01 21:41:05 +02:00
}
}
2020-05-02 00:14:56 +02:00
}
2020-06-01 21:41:05 +02:00
if ( has_setter ) {
2020-08-07 19:51:56 +02:00
if ( ! is_in_setter ) {
2020-06-01 21:41:05 +02:00
// Store stack slot for the temp value.
2020-08-07 19:51:56 +02:00
target = codegen . add_temporary ( _gdtype_from_datatype ( assignment - > assignee - > get_datatype ( ) ) ) ;
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
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address assigned = _parse_expression ( codegen , r_error , assignment - > assigned_value ) ;
GDScriptCodeGenerator : : Address op_result ;
if ( r_error ) {
return GDScriptCodeGenerator : : Address ( ) ;
2020-05-02 00:14:56 +02:00
}
2020-08-07 19:51:56 +02:00
if ( assignment - > operation ! = GDScriptParser : : AssignmentNode : : OP_NONE ) {
// Perform operation.
op_result = codegen . add_temporary ( ) ;
2020-11-26 18:41:55 +01:00
gen - > write_binary_operator ( op_result , assignment - > variant_op , target , assigned ) ;
2020-08-07 19:51:56 +02:00
} else {
op_result = assigned ;
assigned = GDScriptCodeGenerator : : Address ( ) ;
2020-05-02 00:14:56 +02:00
}
2020-07-06 17:24:24 +02:00
GDScriptDataType assign_type = _gdtype_from_datatype ( assignment - > assignee - > get_datatype ( ) ) ;
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 ;
args . push_back ( op_result ) ;
gen - > write_call ( GDScriptCodeGenerator : : Address ( ) , GDScriptCodeGenerator : : Address ( GDScriptCodeGenerator : : Address : : SELF ) , setter_function , args ) ;
} else {
// Just assign.
gen - > write_assign ( target , op_result ) ;
}
if ( op_result . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
gen - > pop_temporary ( ) ;
}
if ( assigned . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
gen - > pop_temporary ( ) ;
}
if ( target . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
gen - > pop_temporary ( ) ;
}
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 ;
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.
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address literal_type_addr = codegen . add_constant ( ( int ) p_pattern - > literal - > value . get_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 ) ;
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 ) ;
}
// Create the result temps first since it's the last to go away.
GDScriptCodeGenerator : : Address result_addr = codegen . add_temporary ( ) ;
GDScriptCodeGenerator : : Address equality_test_addr = codegen . add_temporary ( ) ;
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 ) ;
2020-11-26 15:56:32 +01:00
codegen . generator - > write_call_utility ( result_addr , " typeof " , typeof_args ) ;
2020-05-02 00:14:56 +02:00
// Check type equality.
2020-11-26 18:41:55 +01:00
codegen . generator - > write_binary_operator ( result_addr , Variant : : OP_EQUAL , p_type_addr , result_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
// 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 ( ) ;
}
codegen . generator - > pop_temporary ( ) ; // Remove type equality 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 ( 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 ) ;
2020-11-26 15:56:32 +01:00
codegen . generator - > write_call_gdscript_utility ( value_length_addr , GDScriptUtilityFunctions : : get_function ( " 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 ( ) ;
// 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.
// 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 ( ) ;
GDScriptCodeGenerator : : Address test_addr = p_previous_test ;
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).
codegen . generator - > write_and_left_operand ( test_addr ) ;
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.
2020-08-07 19:51:56 +02:00
test_addr = _parse_match_pattern ( codegen , r_error , p_pattern - > array [ i ] , element_addr , element_type_addr , p_previous_test , false , true ) ;
if ( r_error ! = OK ) {
return GDScriptCodeGenerator : : Address ( ) ;
2020-05-02 00:14:56 +02:00
}
2018-08-26 18:31:23 +02:00
2020-08-07 19:51:56 +02:00
codegen . generator - > write_and_right_operand ( test_addr ) ;
codegen . generator - > write_end_and ( test_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
2020-08-07 19:51:56 +02:00
return test_addr ;
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 ) ;
2020-11-26 15:56:32 +01:00
codegen . generator - > write_call_gdscript_utility ( value_length_addr , GDScriptUtilityFunctions : : get_function ( " 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 ( ) ;
// 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.
// Create temporaries outside the loop so they can be reused.
temp_type . builtin_type = Variant : : BOOL ;
GDScriptCodeGenerator : : Address test_result = codegen . add_temporary ( temp_type ) ;
GDScriptCodeGenerator : : Address element_addr = codegen . add_temporary ( ) ;
GDScriptCodeGenerator : : Address element_type_addr = codegen . add_temporary ( ) ;
GDScriptCodeGenerator : : Address test_addr = p_previous_test ;
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).
codegen . generator - > write_and_left_operand ( test_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 ) ;
codegen . generator - > write_call ( test_result , 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).
codegen . generator - > write_and_left_operand ( test_result ) ;
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.
2020-08-07 19:51:56 +02:00
test_addr = _parse_match_pattern ( codegen , r_error , element . value_pattern , element_addr , element_type_addr , test_addr , false , true ) ;
if ( r_error ! = OK ) {
return GDScriptCodeGenerator : : Address ( ) ;
2020-07-16 03:02:44 +02:00
}
2020-08-07 19:51:56 +02:00
codegen . generator - > write_and_right_operand ( test_addr ) ;
codegen . generator - > write_end_and ( test_addr ) ;
2020-05-02 00:14:56 +02:00
}
2020-08-07 19:51:56 +02:00
codegen . generator - > write_and_right_operand ( test_addr ) ;
codegen . generator - > write_end_and ( test_addr ) ;
// 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 ( ) ;
codegen . generator - > pop_temporary ( ) ;
2020-05-02 00:14:56 +02:00
2020-08-07 19:51:56 +02:00
return test_addr ;
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
}
2020-08-07 19:51:56 +02:00
void GDScriptCompiler : : _add_locals_in_block ( CodeGen & codegen , const GDScriptParser : : SuiteNode * p_block ) {
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 ;
}
codegen . add_local ( p_block - > locals [ i ] . name , _gdtype_from_datatype ( p_block - > locals [ i ] . get_datatype ( ) ) ) ;
}
}
Error GDScriptCompiler : : _parse_block ( CodeGen & codegen , const GDScriptParser : : SuiteNode * p_block , bool p_add_locals ) {
Error error = OK ;
GDScriptCodeGenerator * gen = codegen . generator ;
codegen . start_block ( ) ;
if ( p_add_locals ) {
_add_locals_in_block ( codegen , p_block ) ;
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +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
2020-08-07 19:51:56 +02:00
gen - > start_match ( ) ;
codegen . start_block ( ) ;
2017-03-05 16:44:50 +01:00
2020-08-07 19:51:56 +02:00
// Evaluate the match expression.
2021-03-12 15:04:45 +01:00
GDScriptCodeGenerator : : Address value = codegen . add_local ( " @match_value " , _gdtype_from_datatype ( match - > test - > get_datatype ( ) ) ) ;
GDScriptCodeGenerator : : Address value_expr = _parse_expression ( codegen , error , match - > test ) ;
2020-08-07 19:51:56 +02:00
if ( error ) {
return error ;
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
2020-08-07 19:51:56 +02:00
gen - > start_match_branch ( ) ; // Need so lower level code can patch 'continue' jumps.
codegen . start_block ( ) ; // Create an extra block around for binds.
// Add locals in block before patterns, so temporaries don't use the stack address for binds.
_add_locals_in_block ( 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 + + ) {
2020-08-07 19:51:56 +02:00
pattern_result = _parse_match_pattern ( codegen , error , branch - > patterns [ k ] , value , type , pattern_result , k = = 0 , false ) ;
if ( error ! = OK ) {
return error ;
2016-10-16 13:20:28 +02:00
}
2020-05-02 00:14:56 +02:00
}
2017-03-05 16:44:50 +01:00
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.
error = _parse_block ( codegen , branch - > block , false ) ; // Don't add locals again.
if ( error ) {
return error ;
2020-05-02 00:14:56 +02:00
}
2014-02-10 02:10:30 +01:00
2020-08-07 19:51:56 +02:00
codegen . end_block ( ) ; // Get out of extra block.
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
}
2014-02-10 02:10:30 +01:00
2020-08-07 19:51:56 +02:00
gen - > end_match ( ) ;
} break ;
2020-05-02 00:14:56 +02:00
case GDScriptParser : : Node : : IF : {
const GDScriptParser : : IfNode * if_n = static_cast < const GDScriptParser : : IfNode * > ( s ) ;
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address condition = _parse_expression ( codegen , error , if_n - > condition ) ;
if ( error ) {
return error ;
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 ( ) ;
}
error = _parse_block ( codegen , if_n - > true_block ) ;
if ( error ) {
return error ;
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
2020-08-07 19:51:56 +02:00
error = _parse_block ( codegen , if_n - > false_block ) ;
if ( error ) {
return error ;
}
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
2020-08-07 19:51:56 +02:00
codegen . start_block ( ) ;
GDScriptCodeGenerator : : Address iterator = codegen . add_local ( for_n - > variable - > name , _gdtype_from_datatype ( for_n - > variable - > get_datatype ( ) ) ) ;
2020-11-22 16:24:40 +01:00
gen - > start_for ( iterator . type , _gdtype_from_datatype ( for_n - > list - > get_datatype ( ) ) ) ;
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address list = _parse_expression ( codegen , error , for_n - > list ) ;
if ( error ) {
return error ;
}
2020-11-22 16:24:40 +01:00
gen - > write_for_assignment ( iterator , list ) ;
if ( list . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
codegen . generator - > pop_temporary ( ) ;
}
gen - > write_for ( ) ;
2020-08-07 19:51:56 +02:00
error = _parse_block ( codegen , for_n - > loop ) ;
if ( error ) {
return error ;
}
gen - > write_endfor ( ) ;
codegen . end_block ( ) ;
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
gen - > start_while_condition ( ) ;
GDScriptCodeGenerator : : Address condition = _parse_expression ( codegen , error , while_n - > condition ) ;
if ( error ) {
return error ;
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
2020-08-07 19:51:56 +02:00
error = _parse_block ( codegen , while_n - > loop ) ;
if ( error ) {
return error ;
}
2014-02-10 02:10:30 +01:00
2020-08-07 19:51:56 +02:00
gen - > write_endwhile ( ) ;
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 : {
2020-08-07 19:51:56 +02:00
const GDScriptParser : : ContinueNode * cont = static_cast < const GDScriptParser : : ContinueNode * > ( s ) ;
if ( cont - > is_for_match ) {
gen - > write_continue_match ( ) ;
} else {
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 ) {
2020-08-07 19:51:56 +02:00
return_value = _parse_expression ( codegen , error , return_n - > return_value ) ;
if ( error ) {
return error ;
2020-05-02 00:14:56 +02:00
}
2014-02-10 02:10:30 +01:00
}
2020-05-02 00:14:56 +02:00
2020-08-07 19:51:56 +02:00
gen - > write_return ( return_value ) ;
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
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address condition = _parse_expression ( codegen , error , as - > condition ) ;
if ( error ) {
return error ;
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 ) {
2020-08-07 19:51:56 +02:00
message = _parse_expression ( codegen , error , as - > message ) ;
if ( error ) {
return error ;
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 ] ;
2021-03-09 16:32:35 +01:00
GDScriptParser : : DataType local_type = lv - > get_datatype ( ) ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
if ( lv - > initializer ! = nullptr ) {
2021-03-09 16:32:35 +01:00
// For typed arrays we need to make sure this is already initialized correctly so typed assignment work.
if ( local_type . is_hard_type ( ) & & local_type . builtin_type = = Variant : : ARRAY ) {
if ( local_type . has_container_element_type ( ) ) {
codegen . generator - > write_construct_typed_array ( local , _gdtype_from_datatype ( local_type . get_container_element_type ( ) , codegen . script ) , Vector < GDScriptCodeGenerator : : Address > ( ) ) ;
} else {
codegen . generator - > write_construct_array ( local , Vector < GDScriptCodeGenerator : : Address > ( ) ) ;
}
}
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address src_address = _parse_expression ( codegen , error , lv - > initializer ) ;
if ( error ) {
return error ;
2020-05-02 00:14:56 +02:00
}
2020-08-07 19:51:56 +02:00
gen - > write_assign ( local , src_address ) ;
if ( src_address . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
codegen . generator - > pop_temporary ( ) ;
2020-07-16 03:02:44 +02:00
}
2021-03-09 16:32:35 +01:00
} else if ( lv - > get_datatype ( ) . is_hard_type ( ) ) {
// Initialize with default for type.
if ( local_type . has_container_element_type ( ) ) {
codegen . generator - > write_construct_typed_array ( local , _gdtype_from_datatype ( local_type . get_container_element_type ( ) , codegen . script ) , Vector < GDScriptCodeGenerator : : Address > ( ) ) ;
} else if ( local_type . kind = = GDScriptParser : : DataType : : BUILTIN ) {
codegen . generator - > write_construct ( local , local_type . builtin_type , Vector < GDScriptCodeGenerator : : Address > ( ) ) ;
}
// The `else` branch is for objects, in such case we leave it as `null`.
2020-05-02 00:14:56 +02: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 ( ) ) {
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address expr = _parse_expression ( codegen , error , static_cast < const GDScriptParser : : ExpressionNode * > ( s ) , true ) ;
if ( error ) {
return error ;
}
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 ;
}
}
2020-05-02 00:14:56 +02:00
2020-08-07 19:51:56 +02:00
codegen . end_block ( ) ;
2014-02-10 02:10:30 +01:00
return OK ;
}
2018-11-24 23:46:13 +01:00
Error GDScriptCompiler : : _parse_function ( GDScript * p_script , const GDScriptParser : : ClassNode * p_class , const GDScriptParser : : FunctionNode * p_func , bool p_for_ready ) {
2020-08-07 19:51:56 +02:00
Error 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
2017-03-05 16:44:50 +01:00
codegen . class_node = p_class ;
2018-11-24 23:46:13 +01:00
codegen . script = p_script ;
2017-03-05 16:44:50 +01:00
codegen . function_node = p_func ;
2014-02-10 02:10:30 +01:00
2020-08-07 19:51:56 +02:00
StringName func_name ;
bool is_static = false ;
MultiplayerAPI : : RPCMode rpc_mode = MultiplayerAPI : : RPC_MODE_DISABLED ;
GDScriptDataType return_type ;
return_type . has_type = true ;
return_type . kind = GDScriptDataType : : BUILTIN ;
return_type . builtin_type = Variant : : NIL ;
if ( p_func ) {
func_name = p_func - > identifier - > name ;
is_static = p_func - > is_static ;
rpc_mode = p_func - > rpc_mode ;
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 ) {
func_name = " _ready " ;
} else {
func_name = " @implicit_new " ;
}
}
codegen . function_name = func_name ;
codegen . generator - > write_start ( p_script , func_name , is_static , rpc_mode , return_type ) ;
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 ) ;
2020-08-07 19:51:56 +02:00
uint32_t par_addr = codegen . generator - > add_parameter ( parameter - > identifier - > name , parameter - > default_value ! = nullptr , par_type ) ;
codegen . parameters [ parameter - > identifier - > name ] = GDScriptCodeGenerator : : Address ( GDScriptCodeGenerator : : Address : : FUNCTION_PARAMETER , par_addr , par_type ) ;
2020-05-02 00:14:56 +02:00
if ( p_func - > parameters [ i ] - > default_value ! = nullptr ) {
optional_parameters + + ;
}
2014-02-10 02:10:30 +01:00
}
}
2020-08-07 19:51:56 +02:00
// Parse initializer if applies.
2020-05-02 00:14:56 +02:00
bool is_implicit_initializer = ! p_for_ready & & ! p_func ;
bool is_initializer = p_func & & String ( p_func - > identifier - > name ) = = GDScriptLanguage : : get_singleton ( ) - > strings . _init ;
2020-08-07 19:51:56 +02:00
bool is_for_ready = p_for_ready | | ( p_func & & String ( p_func - > identifier - > name ) = = " _ready " ) ;
2014-02-10 02:10:30 +01:00
2020-08-07 19:51:56 +02:00
if ( is_implicit_initializer | | is_for_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 ;
2020-08-07 19:51:56 +02:00
if ( field - > onready ! = is_for_ready ) {
2020-05-02 00:14:56 +02:00
// Only initialize in _ready.
continue ;
}
2021-03-09 16:32:35 +01:00
GDScriptParser : : DataType field_type = field - > get_datatype ( ) ;
GDScriptCodeGenerator : : Address dst_address ( GDScriptCodeGenerator : : Address : : MEMBER , codegen . script - > member_indices [ field - > identifier - > name ] . index , _gdtype_from_datatype ( field - > get_datatype ( ) ) ) ;
2020-05-02 00:14:56 +02:00
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-09 16:32:35 +01:00
// For typed arrays we need to make sure this is already initialized correctly so typed assignment work.
if ( field_type . is_hard_type ( ) & & field_type . builtin_type = = Variant : : ARRAY & & field_type . has_container_element_type ( ) ) {
if ( field_type . has_container_element_type ( ) ) {
codegen . generator - > write_construct_typed_array ( dst_address , _gdtype_from_datatype ( field_type . get_container_element_type ( ) , codegen . script ) , Vector < GDScriptCodeGenerator : : Address > ( ) ) ;
} else {
codegen . generator - > write_construct_array ( dst_address , Vector < GDScriptCodeGenerator : : Address > ( ) ) ;
}
}
2020-08-07 19:51:56 +02:00
GDScriptCodeGenerator : : Address src_address = _parse_expression ( codegen , error , field - > initializer , false , true ) ;
if ( error ) {
2020-09-02 21:05:31 +02:00
memdelete ( codegen . generator ) ;
2020-08-07 19:51:56 +02:00
return error ;
2020-05-02 00:14:56 +02:00
}
2020-08-07 19:51:56 +02:00
codegen . generator - > write_assign ( dst_address , src_address ) ;
if ( src_address . mode = = GDScriptCodeGenerator : : Address : : TEMPORARY ) {
codegen . generator - > pop_temporary ( ) ;
2020-07-16 03:02:44 +02:00
}
2021-03-09 16:32:35 +01:00
} else if ( field - > get_datatype ( ) . is_hard_type ( ) ) {
codegen . generator - > write_newline ( field - > start_line ) ;
// Initialize with default for type.
if ( field_type . has_container_element_type ( ) ) {
codegen . generator - > write_construct_typed_array ( dst_address , _gdtype_from_datatype ( field_type . get_container_element_type ( ) , codegen . script ) , Vector < GDScriptCodeGenerator : : Address > ( ) ) ;
} else if ( field_type . kind = = GDScriptParser : : DataType : : 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`.
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 ] ;
GDScriptCodeGenerator : : Address src_addr = _parse_expression ( codegen , error , parameter - > default_value , true ) ;
if ( error ) {
2020-09-02 21:05:31 +02:00
memdelete ( codegen . generator ) ;
2020-08-07 19:51:56 +02:00
return error ;
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 ] ;
2020-09-04 23:46:23 +02:00
codegen . generator - > write_assign_default_parameter ( dst_addr , src_addr ) ;
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
}
2020-08-07 19:51:56 +02:00
Error err = _parse_block ( codegen , p_func - > body ) ;
2020-05-14 16:41:43 +02:00
if ( err ) {
2020-09-02 21:05:31 +02:00
memdelete ( codegen . generator ) ;
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
}
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.
2020-05-14 16:41:43 +02:00
if ( p_script - > get_path ( ) ! = String ( ) ) {
2017-03-05 16:44:50 +01:00
signature + = p_script - > get_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 {
2017-03-05 16:44:50 +01:00
signature + = " ::0 " ;
2016-05-22 02:18:16 +02:00
}
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 {
2017-03-05 16:44:50 +01:00
signature + = " :: " + String ( func_name ) ;
2016-05-22 02:18:16 +02:00
}
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 ) ;
2016-09-12 15:52:29 +02:00
# ifdef TOOLS_ENABLED
2020-05-02 00:14:56 +02:00
p_script - > member_lines [ func_name ] = p_func - > start_line ;
2020-11-29 03:37:57 +01:00
p_script - > doc_functions [ func_name ] = p_func - > doc_description ;
2016-09-12 15:52:29 +02:00
# endif
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 ;
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 ) {
// if no return statement -> return type is void not unresolved Variant
if ( p_func - > body - > has_return ) {
gd_function - > return_type = _gdtype_from_datatype ( p_func - > get_datatype ( ) ) ;
} 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 ;
}
# ifdef TOOLS_ENABLED
gd_function - > default_arg_values = p_func - > default_arg_values ;
# endif
}
2020-08-07 19:51:56 +02:00
p_script - > member_functions [ func_name ] = gd_function ;
2020-09-02 21:05:31 +02:00
memdelete ( codegen . generator ) ;
2014-02-10 02:10:30 +01:00
return OK ;
}
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 ) {
2020-08-07 19:51:56 +02:00
Error error = OK ;
2020-06-01 21:41:05 +02:00
CodeGen codegen ;
2020-08-07 19:51:56 +02:00
codegen . generator = memnew ( GDScriptByteCodeGenerator ) ;
2020-06-01 21:41:05 +02:00
codegen . class_node = p_class ;
codegen . script = p_script ;
StringName func_name ;
if ( p_is_setter ) {
func_name = " @ " + p_variable - > identifier - > name + " _setter " ;
} else {
func_name = " @ " + p_variable - > identifier - > name + " _getter " ;
}
2020-08-07 19:51:56 +02:00
GDScriptDataType return_type ;
if ( p_is_setter ) {
return_type . has_type = true ;
return_type . kind = GDScriptDataType : : BUILTIN ;
return_type . builtin_type = Variant : : NIL ;
} else {
2020-09-10 01:26:07 +02:00
return_type = _gdtype_from_datatype ( p_variable - > get_datatype ( ) , p_script ) ;
2020-06-01 21:41:05 +02:00
}
2020-08-07 19:51:56 +02:00
codegen . generator - > write_start ( p_script , func_name , false , p_variable - > rpc_mode , return_type ) ;
2020-06-01 21:41:05 +02:00
2020-08-07 19:51:56 +02:00
if ( p_is_setter ) {
uint32_t par_addr = codegen . generator - > add_parameter ( p_variable - > setter_parameter - > name , false , _gdtype_from_datatype ( p_variable - > get_datatype ( ) ) ) ;
codegen . parameters [ p_variable - > setter_parameter - > name ] = GDScriptCodeGenerator : : Address ( GDScriptCodeGenerator : : Address : : FUNCTION_PARAMETER , par_addr , _gdtype_from_datatype ( p_variable - > get_datatype ( ) ) ) ;
2020-06-01 21:41:05 +02:00
}
2020-08-07 19:51:56 +02:00
error = _parse_block ( codegen , p_is_setter ? p_variable - > setter : p_variable - > getter ) ;
if ( error ) {
2020-09-02 21:05:31 +02:00
memdelete ( codegen . generator ) ;
2020-08-07 19:51:56 +02:00
return error ;
2020-06-01 21:41:05 +02:00
}
2020-08-07 19:51:56 +02:00
GDScriptFunction * gd_function = codegen . generator - > write_end ( ) ;
p_script - > member_functions [ func_name ] = gd_function ;
2020-06-01 21:41:05 +02:00
# ifdef DEBUG_ENABLED
if ( EngineDebugger : : is_active ( ) ) {
String signature ;
//path
if ( p_script - > get_path ( ) ! = String ( ) ) {
signature + = p_script - > get_path ( ) ;
}
//loc
signature + = " :: " + itos ( p_is_setter ? p_variable - > setter - > start_line : p_variable - > getter - > start_line ) ;
//function and class
if ( p_class - > identifier ) {
signature + = " :: " + String ( p_class - > identifier - > name ) + " . " + String ( func_name ) ;
} else {
signature + = " :: " + String ( func_name ) ;
}
2020-08-07 19:51:56 +02:00
codegen . generator - > set_signature ( signature ) ;
2020-06-01 21:41:05 +02:00
}
# endif
2020-08-07 19:51:56 +02:00
codegen . generator - > set_initial_line ( p_is_setter ? p_variable - > setter - > start_line : p_variable - > getter - > start_line ) ;
2020-06-01 21:41:05 +02:00
# ifdef TOOLS_ENABLED
2020-08-07 19:51:56 +02:00
p_script - > member_lines [ func_name ] = p_is_setter ? p_variable - > setter - > start_line : p_variable - > getter - > start_line ;
2020-06-01 21:41:05 +02:00
# endif
2020-09-02 21:05:31 +02:00
memdelete ( codegen . generator ) ;
2020-06-01 21:41:05 +02:00
return OK ;
}
2019-03-16 17:49:29 +01:00
Error GDScriptCompiler : : _parse_class_level ( GDScript * p_script , const GDScriptParser : : ClassNode * p_class , bool p_keep_state ) {
parsing_classes . insert ( p_script ) ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
if ( p_class - > outer & & p_class - > outer - > outer ) {
2018-05-30 04:16:52 +02:00
// Owner is not root
2019-03-16 17:49:29 +01:00
if ( ! parsed_classes . has ( p_script - > _owner ) ) {
if ( parsing_classes . has ( p_script - > _owner ) ) {
2020-05-02 00:14:56 +02:00
_set_error ( " Cyclic class reference for ' " + String ( p_class - > identifier - > name ) + " '. " , p_class ) ;
2018-05-30 04:16:52 +02:00
return ERR_PARSE_ERROR ;
}
2020-05-02 00:14:56 +02:00
Error err = _parse_class_level ( p_script - > _owner , p_class - > outer , p_keep_state ) ;
2018-05-30 04:16:52 +02:00
if ( err ) {
return err ;
}
}
2016-06-02 01:22:02 +02:00
}
2014-02-10 02:10:30 +01:00
2020-11-29 04:42:06 +01:00
# ifdef TOOLS_ENABLED
2020-11-29 03:37:57 +01:00
p_script - > doc_functions . clear ( ) ;
p_script - > doc_variables . clear ( ) ;
p_script - > doc_constants . clear ( ) ;
p_script - > doc_enums . clear ( ) ;
p_script - > doc_signals . clear ( ) ;
p_script - > doc_tutorials . clear ( ) ;
p_script - > doc_brief_description = p_class - > doc_brief_description ;
p_script - > doc_description = p_class - > doc_description ;
for ( int i = 0 ; i < p_class - > doc_tutorials . size ( ) ; i + + ) {
DocData : : TutorialDoc td ;
td . title = p_class - > doc_tutorials [ i ] . first ;
td . link = p_class - > doc_tutorials [ i ] . second ;
p_script - > doc_tutorials . append ( td ) ;
}
# endif
2017-11-16 18:38:18 +01:00
p_script - > native = Ref < GDScriptNativeClass > ( ) ;
2017-03-05 16:44:50 +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 ( ) ;
p_script - > constants . clear ( ) ;
2017-11-16 18:38:18 +01:00
for ( Map < StringName , GDScriptFunction * > : : Element * E = p_script - > member_functions . front ( ) ; E ; E = E - > next ( ) ) {
2016-05-22 02:18:16 +02:00
memdelete ( E - > get ( ) ) ;
}
2014-02-10 02:10:30 +01:00
p_script - > member_functions . clear ( ) ;
p_script - > member_indices . clear ( ) ;
p_script - > member_info . 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 ;
2016-06-02 01:22:02 +02:00
2020-05-02 00:14:56 +02:00
p_script - > tool = parser - > is_tool ( ) ;
p_script - > name = p_class - > identifier ? p_class - > identifier - > name : " " ;
2014-02-10 02:10:30 +01:00
2017-11-16 18:38:18 +01:00
Ref < GDScriptNativeClass > native ;
2014-02-10 02:10:30 +01:00
2019-03-16 17:49:29 +01:00
GDScriptDataType base_type = _gdtype_from_datatype ( p_class - > base_type ) ;
2018-05-30 04:16:52 +02:00
// Inheritance
2019-03-16 17:49:29 +01:00
switch ( base_type . kind ) {
case GDScriptDataType : : NATIVE : {
int native_idx = GDScriptLanguage : : get_singleton ( ) - > get_global_map ( ) [ base_type . native_type ] ;
2018-05-30 04:16:52 +02:00
native = GDScriptLanguage : : get_singleton ( ) - > get_global_array ( ) [ native_idx ] ;
ERR_FAIL_COND_V ( native . is_null ( ) , ERR_BUG ) ;
2017-03-05 16:44:50 +01:00
p_script - > native = native ;
2018-05-30 04:16:52 +02:00
} 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 ) ;
2019-03-16 17:49:29 +01:00
p_script - > base = base ;
p_script - > _base = base . ptr ( ) ;
2019-04-09 21:21:20 +02:00
2020-06-11 00:53:25 +02:00
if ( p_class - > base_type . kind = = GDScriptParser : : DataType : : CLASS & & p_class - > base_type . class_type ! = nullptr ) {
2020-08-26 19:50:27 +02:00
if ( p_class - > base_type . script_path = = main_script - > path ) {
if ( ! parsed_classes . has ( p_script - > _base ) ) {
if ( parsing_classes . has ( p_script - > _base ) ) {
String class_name = p_class - > identifier ? p_class - > identifier - > name : " <main> " ;
_set_error ( " Cyclic class reference for ' " + class_name + " '. " , p_class ) ;
return ERR_PARSE_ERROR ;
}
Error err = _parse_class_level ( p_script - > _base , p_class - > base_type . class_type , p_keep_state ) ;
if ( err ) {
return err ;
}
2019-04-09 21:21:20 +02:00
}
2020-08-26 19:50:27 +02:00
} else {
Error err = OK ;
base = GDScriptCache : : get_full_script ( p_class - > base_type . script_path , err , main_script - > path ) ;
2019-04-09 21:21:20 +02:00
if ( err ) {
return err ;
}
2020-08-26 19:50:27 +02:00
if ( base . is_null ( ) & & ! base - > is_valid ( ) ) {
return ERR_COMPILATION_FAILED ;
}
2019-04-09 21:21:20 +02:00
}
}
2020-07-16 03:02:44 +02:00
p_script - > member_indices = base - > member_indices ;
2020-10-26 14:06:44 +01:00
native = base - > native ;
p_script - > native = native ;
2019-03-16 17:49:29 +01:00
} break ;
2018-05-30 04:16:52 +02:00
default : {
_set_error ( " Parser bug: invalid inheritance. " , p_class ) ;
return ERR_BUG ;
} break ;
2014-02-10 02:10:30 +01:00
}
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 ;
minfo . index = p_script - > member_indices . size ( ) ;
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-05-02 00:14:56 +02:00
minfo . rpc_mode = variable - > rpc_mode ;
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
PropertyInfo prop_info = minfo . data_type ;
prop_info . name = name ;
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 ;
prop_info . usage = export_info . usage ;
} else {
prop_info . usage = PROPERTY_USAGE_SCRIPT_VARIABLE ;
}
2020-11-29 04:42:06 +01:00
# ifdef TOOLS_ENABLED
2020-11-29 03:37:57 +01:00
p_script - > doc_variables [ name ] = variable - > doc_description ;
# endif
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
p_script - > member_info [ name ] = prop_info ;
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 ;
} else {
p_script - > member_default_values . erase ( name ) ;
}
2020-05-02 00:14:56 +02:00
p_script - > member_lines [ name ] = variable - > start_line ;
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 ) ;
2016-09-12 15:52:29 +02:00
# ifdef TOOLS_ENABLED
2020-05-02 00:14:56 +02:00
p_script - > member_lines [ name ] = constant - > start_line ;
2020-11-29 03:37:57 +01:00
if ( constant - > doc_description ! = String ( ) ) {
p_script - > doc_constants [ name ] = constant - > doc_description ;
}
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 : : 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 ) ;
# ifdef TOOLS_ENABLED
p_script - > member_lines [ name ] = enum_value . identifier - > start_line ;
2020-11-29 03:37:57 +01:00
if ( ! p_script - > doc_enums . has ( " @unnamed_enums " ) ) {
p_script - > doc_enums [ " @unnamed_enums " ] = DocData : : EnumDoc ( ) ;
p_script - > doc_enums [ " @unnamed_enums " ] . name = " @unnamed_enums " ;
}
DocData : : ConstantDoc const_doc ;
const_doc . name = enum_value . identifier - > name ;
const_doc . value = Variant ( enum_value . value ) . operator String ( ) ; // TODO-DOC: enum value currently is int.
const_doc . description = enum_value . doc_description ;
p_script - > doc_enums [ " @unnamed_enums " ] . values . push_back ( const_doc ) ;
2020-05-02 00:14:56 +02:00
# endif
} 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
2020-05-02 00:14:56 +02:00
GDScript * c = p_script ;
2015-06-24 18:29:23 +02:00
2020-05-02 00:14:56 +02:00
while ( c ) {
if ( c - > _signals . has ( name ) ) {
_set_error ( " Signal ' " + name + " ' redefined (in current or parent class) " , p_class ) ;
return ERR_ALREADY_EXISTS ;
}
if ( c - > base . is_valid ( ) ) {
c = c - > base . ptr ( ) ;
} else {
c = nullptr ;
}
}
if ( native . is_valid ( ) ) {
if ( ClassDB : : has_signal ( native - > get_name ( ) , name ) ) {
_set_error ( " Signal ' " + name + " ' redefined (original in native class ' " + String ( native - > get_name ( ) ) + " ') " , p_class ) ;
return ERR_ALREADY_EXISTS ;
}
}
Vector < StringName > parameters_names ;
parameters_names . resize ( signal - > parameters . size ( ) ) ;
for ( int j = 0 ; j < signal - > parameters . size ( ) ; j + + ) {
parameters_names . write [ j ] = signal - > parameters [ j ] - > identifier - > name ;
}
p_script - > _signals [ name ] = parameters_names ;
2020-11-29 04:42:06 +01:00
# ifdef TOOLS_ENABLED
2020-12-15 13:04:21 +01:00
if ( ! signal - > doc_description . is_empty ( ) ) {
2020-11-29 03:37:57 +01:00
p_script - > doc_signals [ name ] = signal - > doc_description ;
}
# endif
2020-05-02 00:14:56 +02:00
} break ;
case GDScriptParser : : ClassNode : : Member : : ENUM : {
const GDScriptParser : : EnumNode * enum_n = member . m_enum ;
// TODO: Make enums not be just a dictionary?
Dictionary new_enum ;
for ( int j = 0 ; j < enum_n - > values . size ( ) ; j + + ) {
int value = enum_n - > values [ j ] . value ;
// Needs to be string because Variant::get will convert to String.
new_enum [ String ( enum_n - > values [ j ] . identifier - > name ) ] = value ;
}
2015-06-24 18:29:23 +02:00
2020-05-02 00:14:56 +02:00
p_script - > constants . insert ( enum_n - > identifier - > name , new_enum ) ;
# ifdef TOOLS_ENABLED
p_script - > member_lines [ enum_n - > identifier - > name ] = enum_n - > start_line ;
2020-11-29 03:37:57 +01:00
p_script - > doc_enums [ enum_n - > identifier - > name ] = DocData : : EnumDoc ( ) ;
p_script - > doc_enums [ enum_n - > identifier - > name ] . name = enum_n - > identifier - > name ;
p_script - > doc_enums [ enum_n - > identifier - > name ] . description = enum_n - > doc_description ;
for ( int j = 0 ; j < enum_n - > values . size ( ) ; j + + ) {
DocData : : ConstantDoc const_doc ;
const_doc . name = enum_n - > values [ j ] . identifier - > name ;
const_doc . value = Variant ( enum_n - > values [ j ] . value ) . operator String ( ) ;
const_doc . description = enum_n - > values [ j ] . doc_description ;
p_script - > doc_enums [ enum_n - > identifier - > name ] . values . push_back ( const_doc ) ;
}
2020-05-02 00:14:56 +02:00
# endif
} break ;
default :
break ; // Nothing to do here.
}
2015-06-24 18:29:23 +02:00
}
2018-05-30 04:16:52 +02:00
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
2014-02-10 02:10:30 +01:00
//parse sub-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
2019-06-02 13:07:46 +02:00
if ( ! parsed_classes . has ( subclass_ptr ) & & ! parsing_classes . has ( subclass_ptr ) ) {
2020-05-02 00:14:56 +02:00
Error err = _parse_class_level ( 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
2016-09-12 15:52:29 +02:00
# ifdef TOOLS_ENABLED
2020-05-02 00:14:56 +02:00
p_script - > member_lines [ name ] = inner_class - > start_line ;
2016-09-12 15:52:29 +02:00
# endif
2016-06-28 15:44:38 +02:00
2017-03-05 16:44:50 +01: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 ;
}
2018-11-24 23:46:13 +01:00
Error GDScriptCompiler : : _parse_class_blocks ( GDScript * p_script , const GDScriptParser : : ClassNode * p_class , bool p_keep_state ) {
2014-02-10 02:10:30 +01:00
//parse methods
2017-03-05 16:44:50 +01:00
bool has_ready = false ;
2015-12-28 23:31:52 +01:00
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 ;
if ( ! has_ready & & function - > identifier - > name = = " _ready " ) {
has_ready = true ;
}
Error err = _parse_function ( p_script , p_class , function ) ;
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.
2020-04-02 01:20:12 +02:00
Error err = _parse_function ( 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
}
2020-05-02 00:14:56 +02:00
if ( ! has_ready & & p_class - > onready_used ) {
//create a _ready constructor
2020-04-02 01:20:12 +02:00
Error err = _parse_function ( 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
}
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 ) {
2017-03-05 16:44:50 +01:00
for ( Set < Object * > : : Element * E = p_script - > instances . front ( ) ; E ; ) {
Set < 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
2017-03-05 16:44:50 +01:00
PlaceHolderScriptInstance * psi = static_cast < PlaceHolderScriptInstance * > ( si ) ;
2016-06-02 01:22:02 +02:00
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 ) ;
2017-08-24 22:58:51 +02:00
instance - > base_ref = Object : : cast_to < Reference > ( E - > get ( ) ) ;
2016-06-02 01:22:02 +02:00
instance - > members . resize ( p_script - > member_indices . size ( ) ) ;
2017-03-05 16:44:50 +01:00
instance - > script = Ref < GDScript > ( p_script ) ;
instance - > owner = E - > get ( ) ;
2016-06-02 01:22:02 +02:00
//needed for hot reloading
2019-02-12 21:10:08 +01:00
for ( Map < StringName , GDScript : : MemberInfo > : : Element * F = p_script - > member_indices . front ( ) ; F ; F = F - > next ( ) ) {
instance - > member_indices_cache [ F - > key ( ) ] = F - > get ( ) . 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
}
}
2016-06-02 01:44:34 +02:00
# endif
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 ( ) ;
}
2017-03-05 16:44:50 +01:00
E = N ;
2016-06-02 01:22:02 +02:00
}
}
2015-01-03 04:57:02 +01:00
# endif
2016-02-29 13:56:36 +01:00
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
2020-05-02 00:14:56 +02:00
Error err = _parse_class_blocks ( subclass , inner_class , p_keep_state ) ;
2018-05-30 04:16:52 +02:00
if ( err ) {
return err ;
}
}
2018-05-30 04:16:56 +02:00
p_script - > valid = true ;
2014-02-10 02:10:30 +01:00
return OK ;
}
2019-03-16 17:49:29 +01:00
void GDScriptCompiler : : _make_scripts ( GDScript * p_script , const GDScriptParser : : ClassNode * p_class , bool p_keep_state ) {
2020-03-17 07:33:00 +01:00
Map < 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 ;
2020-01-14 00:19:37 +01:00
String fully_qualified_name = p_script - > fully_qualified_name + " :: " + name ;
2018-05-30 04:16:52 +02:00
if ( old_subclasses . has ( name ) ) {
subclass = old_subclasses [ name ] ;
} else {
2020-01-14 00:19:37 +01:00
Ref < GDScript > orphan_subclass = GDScriptLanguage : : get_singleton ( ) - > get_orphan_subclass ( fully_qualified_name ) ;
if ( orphan_subclass . is_valid ( ) ) {
subclass = orphan_subclass ;
} else {
subclass . instance ( ) ;
}
2018-05-30 04:16:52 +02:00
}
2019-03-16 17:49:29 +01:00
subclass - > _owner = p_script ;
2020-01-14 00:19:37 +01:00
subclass - > fully_qualified_name = fully_qualified_name ;
2019-03-16 17:49:29 +01:00
p_script - > subclasses . insert ( name , subclass ) ;
2018-05-30 04:16:52 +02:00
2020-05-02 00:14:56 +02:00
_make_scripts ( subclass . ptr ( ) , inner_class , false ) ;
2018-05-30 04:16:52 +02:00
}
}
2018-11-24 23:46:13 +01:00
Error GDScriptCompiler : : compile ( const GDScriptParser * p_parser , GDScript * p_script , bool p_keep_state ) {
2017-03-05 16:44:50 +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
2017-03-05 16:44:50 +01:00
source = p_script - > get_path ( ) ;
2014-02-10 02:10:30 +01:00
2020-01-14 16:33:33 +01:00
// The best fully qualified name for a base level script is its file path
p_script - > fully_qualified_name = p_script - > path ;
2018-05-30 04:16:52 +02:00
// Create scripts for subclasses beforehand so they can be referenced
2020-05-02 00:14:56 +02:00
_make_scripts ( p_script , root , p_keep_state ) ;
2018-05-30 04:16:52 +02:00
2020-04-02 01:20:12 +02:00
p_script - > _owner = nullptr ;
2020-05-02 00:14:56 +02:00
Error err = _parse_class_level ( p_script , root , 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
2020-05-02 00:14:56 +02:00
err = _parse_class_blocks ( p_script , root , p_keep_state ) ;
2014-02-10 02:10:30 +01:00
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
2020-07-16 03:02:44 +02:00
return GDScriptCache : : finish_compiling ( p_script - > get_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
}