2022-03-15 13:29:32 +01:00
/*************************************************************************/
/* gdscript_function.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
# include "gdscript_function.h"
# include "core/os/os.h"
# include "gdscript.h"
# include "gdscript_functions.h"
Variant * GDScriptFunction : : _get_variant ( int p_address , GDScriptInstance * p_instance , GDScript * p_script , Variant & self , Variant & static_ref , Variant * p_stack , String & r_error ) const {
int address = p_address & ADDR_MASK ;
//sequential table (jump table generated by compiler)
switch ( ( p_address & ADDR_TYPE_MASK ) > > ADDR_BITS ) {
case ADDR_TYPE_SELF : {
# ifdef DEBUG_ENABLED
if ( unlikely ( ! p_instance ) ) {
r_error = " Cannot access self without instance. " ;
return nullptr ;
}
# endif
return & self ;
} break ;
case ADDR_TYPE_CLASS : {
return & static_ref ;
} break ;
case ADDR_TYPE_MEMBER : {
# ifdef DEBUG_ENABLED
if ( unlikely ( ! p_instance ) ) {
r_error = " Cannot access member without instance. " ;
return nullptr ;
}
# endif
//member indexing is O(1)
return & p_instance - > members . write [ address ] ;
} break ;
case ADDR_TYPE_CLASS_CONSTANT : {
//todo change to index!
GDScript * s = p_script ;
# ifdef DEBUG_ENABLED
ERR_FAIL_INDEX_V ( address , _global_names_count , nullptr ) ;
# endif
const StringName * sn = & _global_names_ptr [ address ] ;
while ( s ) {
GDScript * o = s ;
while ( o ) {
Map < StringName , Variant > : : Element * E = o - > constants . find ( * sn ) ;
if ( E ) {
return & E - > get ( ) ;
}
o = o - > _owner ;
}
s = s - > _base ;
}
ERR_FAIL_V_MSG ( nullptr , " GDScriptCompiler bug. " ) ;
} break ;
case ADDR_TYPE_LOCAL_CONSTANT : {
# ifdef DEBUG_ENABLED
ERR_FAIL_INDEX_V ( address , _constant_count , nullptr ) ;
# endif
return & _constants_ptr [ address ] ;
} break ;
case ADDR_TYPE_STACK :
case ADDR_TYPE_STACK_VARIABLE : {
# ifdef DEBUG_ENABLED
ERR_FAIL_INDEX_V ( address , _stack_size , nullptr ) ;
# endif
return & p_stack [ address ] ;
} break ;
case ADDR_TYPE_GLOBAL : {
# ifdef DEBUG_ENABLED
ERR_FAIL_INDEX_V ( address , GDScriptLanguage : : get_singleton ( ) - > get_global_array_size ( ) , nullptr ) ;
# endif
return & GDScriptLanguage : : get_singleton ( ) - > get_global_array ( ) [ address ] ;
} break ;
# ifdef TOOLS_ENABLED
case ADDR_TYPE_NAMED_GLOBAL : {
# ifdef DEBUG_ENABLED
ERR_FAIL_INDEX_V ( address , _named_globals_count , nullptr ) ;
# endif
StringName id = _named_globals_ptr [ address ] ;
if ( GDScriptLanguage : : get_singleton ( ) - > get_named_globals_map ( ) . has ( id ) ) {
return ( Variant * ) & GDScriptLanguage : : get_singleton ( ) - > get_named_globals_map ( ) [ id ] ;
} else {
r_error = " Autoload singleton ' " + String ( id ) + " ' has been removed. " ;
return nullptr ;
}
} break ;
# endif
case ADDR_TYPE_NIL : {
return & nil ;
} break ;
}
ERR_FAIL_V_MSG ( nullptr , " Bad code! (unknown addressing mode). " ) ;
return nullptr ;
}
# ifdef DEBUG_ENABLED
static String _get_var_type ( const Variant * p_var ) {
String basestr ;
if ( p_var - > get_type ( ) = = Variant : : OBJECT ) {
Object * bobj = * p_var ;
if ( ! bobj ) {
if ( p_var - > is_invalid_object ( ) ) {
basestr = " previously freed instance " ;
} else {
basestr = " null instance " ;
}
} else {
if ( bobj - > get_script_instance ( ) ) {
basestr = bobj - > get_class ( ) + " ( " + bobj - > get_script_instance ( ) - > get_script ( ) - > get_path ( ) . get_file ( ) + " ) " ;
} else {
basestr = bobj - > get_class ( ) ;
}
}
} else {
basestr = Variant : : get_type_name ( p_var - > get_type ( ) ) ;
}
return basestr ;
}
# endif // DEBUG_ENABLED
String GDScriptFunction : : _get_call_error ( const Variant : : CallError & p_err , const String & p_where , const Variant * * argptrs ) const {
String err_text ;
if ( p_err . error = = Variant : : CallError : : CALL_ERROR_INVALID_ARGUMENT ) {
int errorarg = p_err . argument ;
// Handle the Object to Object case separately as we don't have further class details.
# ifdef DEBUG_ENABLED
if ( p_err . expected = = Variant : : OBJECT & & argptrs [ errorarg ] - > get_type ( ) = = p_err . expected ) {
err_text = " Invalid type in " + p_where + " . The Object-derived class of argument " + itos ( errorarg + 1 ) + " ( " + _get_var_type ( argptrs [ errorarg ] ) + " ) is not a subclass of the expected argument class. " ;
} else
# endif // DEBUG_ENABLED
{
err_text = " Invalid type in " + p_where + " . Cannot convert argument " + itos ( errorarg + 1 ) + " from " + Variant : : get_type_name ( argptrs [ errorarg ] - > get_type ( ) ) + " to " + Variant : : get_type_name ( p_err . expected ) + " . " ;
}
} else if ( p_err . error = = Variant : : CallError : : CALL_ERROR_TOO_MANY_ARGUMENTS ) {
err_text = " Invalid call to " + p_where + " . Expected " + itos ( p_err . argument ) + " arguments. " ;
} else if ( p_err . error = = Variant : : CallError : : CALL_ERROR_TOO_FEW_ARGUMENTS ) {
err_text = " Invalid call to " + p_where + " . Expected " + itos ( p_err . argument ) + " arguments. " ;
} else if ( p_err . error = = Variant : : CallError : : CALL_ERROR_INVALID_METHOD ) {
err_text = " Invalid call. Nonexistent " + p_where + " . " ;
} else if ( p_err . error = = Variant : : CallError : : CALL_ERROR_INSTANCE_IS_NULL ) {
err_text = " Attempt to call " + p_where + " on a null instance. " ;
} else {
err_text = " Bug, call error: # " + itos ( p_err . error ) ;
}
return err_text ;
}
# if defined(__GNUC__)
# define OPCODES_TABLE \
static const void * switch_table_ops [ ] = { \
& & OPCODE_OPERATOR , \
& & OPCODE_EXTENDS_TEST , \
& & OPCODE_IS_BUILTIN , \
& & OPCODE_SET , \
& & OPCODE_GET , \
& & OPCODE_SET_NAMED , \
& & OPCODE_GET_NAMED , \
& & OPCODE_SET_MEMBER , \
& & OPCODE_GET_MEMBER , \
& & OPCODE_ASSIGN , \
& & OPCODE_ASSIGN_TRUE , \
& & OPCODE_ASSIGN_FALSE , \
& & OPCODE_ASSIGN_TYPED_BUILTIN , \
& & OPCODE_ASSIGN_TYPED_NATIVE , \
& & OPCODE_ASSIGN_TYPED_SCRIPT , \
& & OPCODE_CAST_TO_BUILTIN , \
& & OPCODE_CAST_TO_NATIVE , \
& & OPCODE_CAST_TO_SCRIPT , \
& & OPCODE_CONSTRUCT , \
& & OPCODE_CONSTRUCT_ARRAY , \
& & OPCODE_CONSTRUCT_DICTIONARY , \
& & OPCODE_CALL , \
& & OPCODE_CALL_RETURN , \
& & OPCODE_CALL_BUILT_IN , \
& & OPCODE_CALL_SELF , \
& & OPCODE_CALL_SELF_BASE , \
& & OPCODE_YIELD , \
& & OPCODE_YIELD_SIGNAL , \
& & OPCODE_YIELD_RESUME , \
& & OPCODE_JUMP , \
& & OPCODE_JUMP_IF , \
& & OPCODE_JUMP_IF_NOT , \
& & OPCODE_JUMP_TO_DEF_ARGUMENT , \
& & OPCODE_RETURN , \
& & OPCODE_ITERATE_BEGIN , \
& & OPCODE_ITERATE , \
& & OPCODE_ASSERT , \
& & OPCODE_BREAKPOINT , \
& & OPCODE_LINE , \
& & OPCODE_END \
} ;
# define OPCODE(m_op) \
m_op :
# define OPCODE_WHILE(m_test)
# define OPCODES_END \
OPSEXIT :
# define OPCODES_OUT \
OPSOUT :
# define DISPATCH_OPCODE goto *switch_table_ops[_code_ptr[ip]]
# define OPCODE_SWITCH(m_test) DISPATCH_OPCODE;
# define OPCODE_BREAK goto OPSEXIT
# define OPCODE_OUT goto OPSOUT
# else
# define OPCODES_TABLE
# define OPCODE(m_op) case m_op:
# define OPCODE_WHILE(m_test) while (m_test)
# define OPCODES_END
# define OPCODES_OUT
# define DISPATCH_OPCODE continue
# define OPCODE_SWITCH(m_test) switch (m_test)
# define OPCODE_BREAK break
# define OPCODE_OUT break
# endif
Variant GDScriptFunction : : call ( GDScriptInstance * p_instance , const Variant * * p_args , int p_argcount , Variant : : CallError & r_err , CallState * p_state ) {
OPCODES_TABLE ;
if ( ! _code_ptr ) {
return Variant ( ) ;
}
r_err . error = Variant : : CallError : : CALL_OK ;
Variant self ;
Variant static_ref ;
Variant retvalue ;
Variant * stack = nullptr ;
Variant * * call_args ;
int defarg = 0 ;
# ifdef DEBUG_ENABLED
//GDScriptLanguage::get_singleton()->calls++;
# endif
uint32_t alloca_size = 0 ;
GDScript * script ;
int ip = 0 ;
int line = _initial_line ;
if ( p_state ) {
//use existing (supplied) state (yielded)
stack = ( Variant * ) p_state - > stack . ptr ( ) ;
call_args = ( Variant * * ) & p_state - > stack . ptr ( ) [ sizeof ( Variant ) * p_state - > stack_size ] ; //ptr() to avoid bounds check
line = p_state - > line ;
ip = p_state - > ip ;
alloca_size = p_state - > stack . size ( ) ;
script = p_state - > script ;
p_instance = p_state - > instance ;
defarg = p_state - > defarg ;
self = p_state - > self ;
//stack[p_state->result_pos]=p_state->result; //assign stack with result
} else {
if ( p_argcount ! = _argument_count ) {
if ( p_argcount > _argument_count ) {
r_err . error = Variant : : CallError : : CALL_ERROR_TOO_MANY_ARGUMENTS ;
r_err . argument = _argument_count ;
return Variant ( ) ;
} else if ( p_argcount < _argument_count - _default_arg_count ) {
r_err . error = Variant : : CallError : : CALL_ERROR_TOO_FEW_ARGUMENTS ;
r_err . argument = _argument_count - _default_arg_count ;
return Variant ( ) ;
} else {
defarg = _argument_count - p_argcount ;
}
}
alloca_size = sizeof ( Variant * ) * _call_size + sizeof ( Variant ) * _stack_size ;
if ( alloca_size ) {
uint8_t * aptr = ( uint8_t * ) alloca ( alloca_size ) ;
if ( _stack_size ) {
stack = ( Variant * ) aptr ;
for ( int i = 0 ; i < p_argcount ; i + + ) {
if ( ! argument_types [ i ] . has_type ) {
memnew_placement ( & stack [ i ] , Variant ( * p_args [ i ] ) ) ;
continue ;
}
if ( ! argument_types [ i ] . is_type ( * p_args [ i ] , true ) ) {
if ( argument_types [ i ] . is_type ( Variant ( ) , true ) ) {
memnew_placement ( & stack [ i ] , Variant ) ;
continue ;
} else {
r_err . error = Variant : : CallError : : CALL_ERROR_INVALID_ARGUMENT ;
r_err . argument = i ;
r_err . expected = argument_types [ i ] . kind = = GDScriptDataType : : BUILTIN ? argument_types [ i ] . builtin_type : Variant : : OBJECT ;
return Variant ( ) ;
}
}
if ( argument_types [ i ] . kind = = GDScriptDataType : : BUILTIN ) {
Variant arg = Variant : : construct ( argument_types [ i ] . builtin_type , & p_args [ i ] , 1 , r_err ) ;
memnew_placement ( & stack [ i ] , Variant ( arg ) ) ;
} else {
memnew_placement ( & stack [ i ] , Variant ( * p_args [ i ] ) ) ;
}
}
for ( int i = p_argcount ; i < _stack_size ; i + + ) {
memnew_placement ( & stack [ i ] , Variant ) ;
}
} else {
stack = nullptr ;
}
if ( _call_size ) {
call_args = ( Variant * * ) & aptr [ sizeof ( Variant ) * _stack_size ] ;
} else {
call_args = nullptr ;
}
} else {
stack = nullptr ;
call_args = nullptr ;
}
if ( p_instance ) {
self = p_instance - > owner ;
script = p_instance - > script . ptr ( ) ;
} else {
script = _script ;
}
}
# ifdef DEBUG_ENABLED
Variant instance = p_instance ;
# endif
static_ref = script ;
String err_text ;
# ifdef DEBUG_ENABLED
if ( ScriptDebugger : : get_singleton ( ) ) {
GDScriptLanguage : : get_singleton ( ) - > enter_function ( p_instance , this , stack , & ip , & line ) ;
}
# define GD_ERR_BREAK(m_cond) \
{ \
if ( unlikely ( m_cond ) ) { \
_err_print_error ( FUNCTION_STR , __FILE__ , __LINE__ , " Condition ' " _STR ( m_cond ) " ' is true. Breaking..: " ) ; \
OPCODE_BREAK ; \
} \
}
# define CHECK_SPACE(m_space) \
GD_ERR_BREAK ( ( ip + m_space ) > _code_size )
# define GET_VARIANT_PTR(m_v, m_code_ofs) \
Variant * m_v ; \
m_v = _get_variant ( _code_ptr [ ip + m_code_ofs ] , p_instance , script , self , static_ref , stack , err_text ) ; \
if ( unlikely ( ! m_v ) ) \
OPCODE_BREAK ;
# else
# define GD_ERR_BREAK(m_cond)
# define CHECK_SPACE(m_space)
# define GET_VARIANT_PTR(m_v, m_code_ofs) \
Variant * m_v ; \
m_v = _get_variant ( _code_ptr [ ip + m_code_ofs ] , p_instance , script , self , static_ref , stack , err_text ) ;
# endif
# ifdef DEBUG_ENABLED
uint64_t function_start_time = 0 ;
uint64_t function_call_time = 0 ;
if ( GDScriptLanguage : : get_singleton ( ) - > profiling ) {
function_start_time = OS : : get_singleton ( ) - > get_ticks_usec ( ) ;
function_call_time = 0 ;
profile . call_count + + ;
profile . frame_call_count + + ;
}
bool exit_ok = false ;
bool yielded = false ;
# endif
# ifdef DEBUG_ENABLED
OPCODE_WHILE ( ip < _code_size ) {
int last_opcode = _code_ptr [ ip ] ;
# else
OPCODE_WHILE ( true ) {
# endif
OPCODE_SWITCH ( _code_ptr [ ip ] ) {
OPCODE ( OPCODE_OPERATOR ) {
CHECK_SPACE ( 5 ) ;
bool valid ;
Variant : : Operator op = ( Variant : : Operator ) _code_ptr [ ip + 1 ] ;
GD_ERR_BREAK ( op > = Variant : : OP_MAX ) ;
GET_VARIANT_PTR ( a , 2 ) ;
GET_VARIANT_PTR ( b , 3 ) ;
GET_VARIANT_PTR ( dst , 4 ) ;
# ifdef DEBUG_ENABLED
Variant ret ;
Variant : : evaluate ( op , * a , * b , ret , valid ) ;
# else
Variant : : evaluate ( op , * a , * b , * dst , valid ) ;
# endif
# ifdef DEBUG_ENABLED
if ( ! valid ) {
if ( ret . get_type ( ) = = Variant : : STRING ) {
//return a string when invalid with the error
err_text = ret ;
err_text + = " in operator ' " + Variant : : get_operator_name ( op ) + " '. " ;
} else {
err_text = " Invalid operands ' " + Variant : : get_type_name ( a - > get_type ( ) ) + " ' and ' " + Variant : : get_type_name ( b - > get_type ( ) ) + " ' in operator ' " + Variant : : get_operator_name ( op ) + " '. " ;
}
OPCODE_BREAK ;
}
* dst = ret ;
# endif
ip + = 5 ;
}
DISPATCH_OPCODE ;
OPCODE ( OPCODE_EXTENDS_TEST ) {
CHECK_SPACE ( 4 ) ;
GET_VARIANT_PTR ( a , 1 ) ;
GET_VARIANT_PTR ( b , 2 ) ;
GET_VARIANT_PTR ( dst , 3 ) ;
# ifdef DEBUG_ENABLED
if ( a - > is_invalid_object ( ) ) {
err_text = " Left operand of 'is' was already freed. " ;
OPCODE_BREAK ;
}
if ( b - > get_type ( ) ! = Variant : : OBJECT | | b - > is_invalid_object ( ) ) {
err_text = " Right operand of 'is' is not a class. " ;
OPCODE_BREAK ;
}
# endif
bool extends_ok = false ;
if ( a - > get_type ( ) = = Variant : : OBJECT & & a - > operator Object * ( ) ! = nullptr ) {
Object * obj_A = * a ;
Object * obj_B = * b ;
GDScript * scr_B = Object : : cast_to < GDScript > ( obj_B ) ;
if ( scr_B ) {
//if B is a script, the only valid condition is that A has an instance which inherits from the script
//in other situation, this shoul return false.
if ( obj_A - > get_script_instance ( ) & & obj_A - > get_script_instance ( ) - > get_language ( ) = = GDScriptLanguage : : get_singleton ( ) ) {
GDScript * cmp = static_cast < GDScript * > ( obj_A - > get_script_instance ( ) - > get_script ( ) . ptr ( ) ) ;
//bool found=false;
while ( cmp ) {
if ( cmp = = scr_B ) {
//inherits from script, all ok
extends_ok = true ;
break ;
}
cmp = cmp - > _base ;
}
}
} else {
GDScriptNativeClass * nc = Object : : cast_to < GDScriptNativeClass > ( obj_B ) ;
# ifdef DEBUG_ENABLED
if ( ! nc ) {
if ( obj_B ) {
err_text = " Right operand of 'is' is not a class (type: ' " + obj_B - > get_class ( ) + " '). " ;
} else {
err_text = " Right operand of 'is' is null. " ;
}
OPCODE_BREAK ;
}
# endif
extends_ok = ClassDB : : is_parent_class ( obj_A - > get_class_name ( ) , nc - > get_name ( ) ) ;
}
}
* dst = extends_ok ;
ip + = 4 ;
}
DISPATCH_OPCODE ;
OPCODE ( OPCODE_IS_BUILTIN ) {
CHECK_SPACE ( 4 ) ;
GET_VARIANT_PTR ( value , 1 ) ;
Variant : : Type var_type = ( Variant : : Type ) _code_ptr [ ip + 2 ] ;
GET_VARIANT_PTR ( dst , 3 ) ;
GD_ERR_BREAK ( var_type < 0 | | var_type > = Variant : : VARIANT_MAX ) ;
* dst = value - > get_type ( ) = = var_type ;
ip + = 4 ;
}
DISPATCH_OPCODE ;
OPCODE ( OPCODE_SET ) {
CHECK_SPACE ( 3 ) ;
GET_VARIANT_PTR ( dst , 1 ) ;
GET_VARIANT_PTR ( index , 2 ) ;
GET_VARIANT_PTR ( value , 3 ) ;
bool valid ;
dst - > set ( * index , * value , & valid ) ;
# ifdef DEBUG_ENABLED
if ( ! valid ) {
String v = index - > operator String ( ) ;
if ( v ! = " " ) {
v = " ' " + v + " ' " ;
} else {
v = " of type ' " + _get_var_type ( index ) + " ' " ;
}
err_text = " Invalid set index " + v + " (on base: ' " + _get_var_type ( dst ) + " ') with value of type ' " + _get_var_type ( value ) + " ' " ;
OPCODE_BREAK ;
}
# endif
ip + = 4 ;
}
DISPATCH_OPCODE ;
OPCODE ( OPCODE_GET ) {
CHECK_SPACE ( 3 ) ;
GET_VARIANT_PTR ( src , 1 ) ;
GET_VARIANT_PTR ( index , 2 ) ;
GET_VARIANT_PTR ( dst , 3 ) ;
bool valid ;
# ifdef DEBUG_ENABLED
//allow better error message in cases where src and dst are the same stack position
Variant ret = src - > get ( * index , & valid ) ;
# else
* dst = src - > get ( * index , & valid ) ;
# endif
# ifdef DEBUG_ENABLED
if ( ! valid ) {
String v = index - > operator String ( ) ;
if ( v ! = " " ) {
v = " ' " + v + " ' " ;
} else {
v = " of type ' " + _get_var_type ( index ) + " ' " ;
}
err_text = " Invalid get index " + v + " (on base: ' " + _get_var_type ( src ) + " '). " ;
2022-03-21 21:29:06 +01:00
2022-03-15 13:29:32 +01:00
OPCODE_BREAK ;
}
* dst = ret ;
# endif
ip + = 4 ;
}
DISPATCH_OPCODE ;
OPCODE ( OPCODE_SET_NAMED ) {
CHECK_SPACE ( 3 ) ;
GET_VARIANT_PTR ( dst , 1 ) ;
GET_VARIANT_PTR ( value , 3 ) ;
int indexname = _code_ptr [ ip + 2 ] ;
GD_ERR_BREAK ( indexname < 0 | | indexname > = _global_names_count ) ;
const StringName * index = & _global_names_ptr [ indexname ] ;
bool valid ;
dst - > set_named ( * index , * value , & valid ) ;
# ifdef DEBUG_ENABLED
if ( ! valid ) {
String err_type ;
err_text = " Invalid set index ' " + String ( * index ) + " ' (on base: ' " + _get_var_type ( dst ) + " ') with value of type ' " + _get_var_type ( value ) + " '. " ;
OPCODE_BREAK ;
}
# endif
ip + = 4 ;
}
DISPATCH_OPCODE ;
OPCODE ( OPCODE_GET_NAMED ) {
CHECK_SPACE ( 4 ) ;
GET_VARIANT_PTR ( src , 1 ) ;
GET_VARIANT_PTR ( dst , 3 ) ;
int indexname = _code_ptr [ ip + 2 ] ;
GD_ERR_BREAK ( indexname < 0 | | indexname > = _global_names_count ) ;
const StringName * index = & _global_names_ptr [ indexname ] ;
bool valid ;
# ifdef DEBUG_ENABLED
//allow better error message in cases where src and dst are the same stack position
Variant ret = src - > get_named ( * index , & valid ) ;
# else
* dst = src - > get_named ( * index , & valid ) ;
# endif
# ifdef DEBUG_ENABLED
if ( ! valid ) {
if ( src - > has_method ( * index ) ) {
err_text = " Invalid get index ' " + index - > operator String ( ) + " ' (on base: ' " + _get_var_type ( src ) + " '). Did you mean '. " + index - > operator String ( ) + " ()' or funcref(obj, \" " + index - > operator String ( ) + " \" ) ? " ;
} else {
err_text = " Invalid get index ' " + index - > operator String ( ) + " ' (on base: ' " + _get_var_type ( src ) + " '). " ;
}
OPCODE_BREAK ;
}
* dst = ret ;
# endif
ip + = 4 ;
}
DISPATCH_OPCODE ;
OPCODE ( OPCODE_SET_MEMBER ) {
CHECK_SPACE ( 3 ) ;
int indexname = _code_ptr [ ip + 1 ] ;
GD_ERR_BREAK ( indexname < 0 | | indexname > = _global_names_count ) ;
const StringName * index = & _global_names_ptr [ indexname ] ;
GET_VARIANT_PTR ( src , 2 ) ;
bool valid ;
# ifndef DEBUG_ENABLED
ClassDB : : set_property ( p_instance - > owner , * index , * src , & valid ) ;
# else
bool ok = ClassDB : : set_property ( p_instance - > owner , * index , * src , & valid ) ;
if ( ! ok ) {
err_text = " Internal error setting property: " + String ( * index ) ;
OPCODE_BREAK ;
} else if ( ! valid ) {
err_text = " Error setting property ' " + String ( * index ) + " ' with value of type " + Variant : : get_type_name ( src - > get_type ( ) ) + " . " ;
OPCODE_BREAK ;
}
# endif
ip + = 3 ;
}
DISPATCH_OPCODE ;
OPCODE ( OPCODE_GET_MEMBER ) {
CHECK_SPACE ( 3 ) ;
int indexname = _code_ptr [ ip + 1 ] ;
GD_ERR_BREAK ( indexname < 0 | | indexname > = _global_names_count ) ;
const StringName * index = & _global_names_ptr [ indexname ] ;
GET_VARIANT_PTR ( dst , 2 ) ;
# ifndef DEBUG_ENABLED
ClassDB : : get_property ( p_instance - > owner , * index , * dst ) ;
# else
bool ok = ClassDB : : get_property ( p_instance - > owner , * index , * dst ) ;
if ( ! ok ) {
err_text = " Internal error getting property: " + String ( * index ) ;
OPCODE_BREAK ;
}
# endif
ip + = 3 ;
}
DISPATCH_OPCODE ;
OPCODE ( OPCODE_ASSIGN ) {
CHECK_SPACE ( 3 ) ;
GET_VARIANT_PTR ( dst , 1 ) ;
GET_VARIANT_PTR ( src , 2 ) ;
* dst = * src ;
ip + = 3 ;
}
DISPATCH_OPCODE ;
OPCODE ( OPCODE_ASSIGN_TRUE ) {
CHECK_SPACE ( 2 ) ;
GET_VARIANT_PTR ( dst , 1 ) ;
* dst = true ;
ip + = 2 ;
}
DISPATCH_OPCODE ;
OPCODE ( OPCODE_ASSIGN_FALSE ) {
CHECK_SPACE ( 2 ) ;
GET_VARIANT_PTR ( dst , 1 ) ;
* dst = false ;
ip + = 2 ;
}
DISPATCH_OPCODE ;
OPCODE ( OPCODE_ASSIGN_TYPED_BUILTIN ) {
CHECK_SPACE ( 4 ) ;
GET_VARIANT_PTR ( dst , 2 ) ;
GET_VARIANT_PTR ( src , 3 ) ;
Variant : : Type var_type = ( Variant : : Type ) _code_ptr [ ip + 1 ] ;
GD_ERR_BREAK ( var_type < 0 | | var_type > = Variant : : VARIANT_MAX ) ;
if ( src - > get_type ( ) ! = var_type ) {
# ifdef DEBUG_ENABLED
if ( Variant : : can_convert_strict ( src - > get_type ( ) , var_type ) ) {
# endif // DEBUG_ENABLED
Variant : : CallError ce ;
* dst = Variant : : construct ( var_type , const_cast < const Variant * * > ( & src ) , 1 , ce ) ;
} else {
# ifdef DEBUG_ENABLED
err_text = " Trying to assign value of type ' " + Variant : : get_type_name ( src - > get_type ( ) ) +
" ' to a variable of type ' " + Variant : : get_type_name ( var_type ) + " '. " ;
OPCODE_BREAK ;
}
} else {
# endif // DEBUG_ENABLED
* dst = * src ;
}
ip + = 4 ;
}
DISPATCH_OPCODE ;
OPCODE ( OPCODE_ASSIGN_TYPED_NATIVE ) {
CHECK_SPACE ( 4 ) ;
GET_VARIANT_PTR ( dst , 2 ) ;
GET_VARIANT_PTR ( src , 3 ) ;
# ifdef DEBUG_ENABLED
GET_VARIANT_PTR ( type , 1 ) ;
GDScriptNativeClass * nc = Object : : cast_to < GDScriptNativeClass > ( type - > operator Object * ( ) ) ;
GD_ERR_BREAK ( ! nc ) ;
if ( src - > get_type ( ) ! = Variant : : OBJECT & & src - > get_type ( ) ! = Variant : : NIL ) {
err_text = " Trying to assign value of type ' " + Variant : : get_type_name ( src - > get_type ( ) ) +
" ' to a variable of type ' " + nc - > get_name ( ) + " '. " ;
OPCODE_BREAK ;
}
Object * src_obj = src - > operator Object * ( ) ;
if ( src_obj & & ! ClassDB : : is_parent_class ( src_obj - > get_class_name ( ) , nc - > get_name ( ) ) ) {
err_text = " Trying to assign value of type ' " + src_obj - > get_class_name ( ) +
" ' to a variable of type ' " + nc - > get_name ( ) + " '. " ;
OPCODE_BREAK ;
}
# endif // DEBUG_ENABLED
* dst = * src ;
ip + = 4 ;
}
DISPATCH_OPCODE ;
OPCODE ( OPCODE_ASSIGN_TYPED_SCRIPT ) {
CHECK_SPACE ( 4 ) ;
GET_VARIANT_PTR ( dst , 2 ) ;
GET_VARIANT_PTR ( src , 3 ) ;
# ifdef DEBUG_ENABLED
GET_VARIANT_PTR ( type , 1 ) ;
Script * base_type = Object : : cast_to < Script > ( type - > operator Object * ( ) ) ;
GD_ERR_BREAK ( ! base_type ) ;
if ( src - > get_type ( ) ! = Variant : : OBJECT & & src - > get_type ( ) ! = Variant : : NIL ) {
err_text = " Trying to assign a non-object value to a variable of type ' " + base_type - > get_path ( ) . get_file ( ) + " '. " ;
OPCODE_BREAK ;
}
if ( src - > get_type ( ) ! = Variant : : NIL & & src - > operator Object * ( ) ! = nullptr ) {
ScriptInstance * scr_inst = src - > operator Object * ( ) - > get_script_instance ( ) ;
if ( ! scr_inst ) {
err_text = " Trying to assign value of type ' " + src - > operator Object * ( ) - > get_class_name ( ) +
" ' to a variable of type ' " + base_type - > get_path ( ) . get_file ( ) + " '. " ;
OPCODE_BREAK ;
}
Script * src_type = src - > operator Object * ( ) - > get_script_instance ( ) - > get_script ( ) . ptr ( ) ;
bool valid = false ;
while ( src_type ) {
if ( src_type = = base_type ) {
valid = true ;
break ;
}
src_type = src_type - > get_base_script ( ) . ptr ( ) ;
}
if ( ! valid ) {
err_text = " Trying to assign value of type ' " + src - > operator Object * ( ) - > get_script_instance ( ) - > get_script ( ) - > get_path ( ) . get_file ( ) +
" ' to a variable of type ' " + base_type - > get_path ( ) . get_file ( ) + " '. " ;
OPCODE_BREAK ;
}
}
# endif // DEBUG_ENABLED
* dst = * src ;
ip + = 4 ;
}
DISPATCH_OPCODE ;
OPCODE ( OPCODE_CAST_TO_BUILTIN ) {
CHECK_SPACE ( 4 ) ;
Variant : : Type to_type = ( Variant : : Type ) _code_ptr [ ip + 1 ] ;
GET_VARIANT_PTR ( src , 2 ) ;
GET_VARIANT_PTR ( dst , 3 ) ;
GD_ERR_BREAK ( to_type < 0 | | to_type > = Variant : : VARIANT_MAX ) ;
Variant : : CallError err ;
* dst = Variant : : construct ( to_type , ( const Variant * * ) & src , 1 , err ) ;
# ifdef DEBUG_ENABLED
if ( src - > is_invalid_object ( ) ) {
err_text = " Trying to cast a deleted object. " ;
OPCODE_BREAK ;
}
if ( err . error ! = Variant : : CallError : : CALL_OK ) {
err_text = " Invalid cast: could not convert value to ' " + Variant : : get_type_name ( to_type ) + " '. " ;
OPCODE_BREAK ;
}
# endif
ip + = 4 ;
}
DISPATCH_OPCODE ;
OPCODE ( OPCODE_CAST_TO_NATIVE ) {
CHECK_SPACE ( 4 ) ;
GET_VARIANT_PTR ( to_type , 1 ) ;
GET_VARIANT_PTR ( src , 2 ) ;
GET_VARIANT_PTR ( dst , 3 ) ;
GDScriptNativeClass * nc = Object : : cast_to < GDScriptNativeClass > ( to_type - > operator Object * ( ) ) ;
GD_ERR_BREAK ( ! nc ) ;
# ifdef DEBUG_ENABLED
if ( src - > is_invalid_object ( ) ) {
err_text = " Trying to cast a deleted object. " ;
OPCODE_BREAK ;
}
if ( src - > get_type ( ) ! = Variant : : OBJECT & & src - > get_type ( ) ! = Variant : : NIL ) {
err_text = " Invalid cast: can't convert a non-object value to an object type. " ;
OPCODE_BREAK ;
}
# endif
Object * src_obj = src - > operator Object * ( ) ;
if ( src_obj & & ! ClassDB : : is_parent_class ( src_obj - > get_class_name ( ) , nc - > get_name ( ) ) ) {
* dst = Variant ( ) ; // invalid cast, assign NULL
} else {
* dst = * src ;
}
ip + = 4 ;
}
DISPATCH_OPCODE ;
OPCODE ( OPCODE_CAST_TO_SCRIPT ) {
CHECK_SPACE ( 4 ) ;
GET_VARIANT_PTR ( to_type , 1 ) ;
GET_VARIANT_PTR ( src , 2 ) ;
GET_VARIANT_PTR ( dst , 3 ) ;
Script * base_type = Object : : cast_to < Script > ( to_type - > operator Object * ( ) ) ;
GD_ERR_BREAK ( ! base_type ) ;
# ifdef DEBUG_ENABLED
if ( src - > is_invalid_object ( ) ) {
err_text = " Trying to cast a deleted object. " ;
OPCODE_BREAK ;
}
if ( src - > get_type ( ) ! = Variant : : OBJECT & & src - > get_type ( ) ! = Variant : : NIL ) {
err_text = " Trying to assign a non-object value to a variable of type ' " + base_type - > get_path ( ) . get_file ( ) + " '. " ;
OPCODE_BREAK ;
}
# endif
bool valid = false ;
if ( src - > get_type ( ) ! = Variant : : NIL & & src - > operator Object * ( ) ! = nullptr ) {
ScriptInstance * scr_inst = src - > operator Object * ( ) - > get_script_instance ( ) ;
if ( scr_inst ) {
Script * src_type = src - > operator Object * ( ) - > get_script_instance ( ) - > get_script ( ) . ptr ( ) ;
while ( src_type ) {
if ( src_type = = base_type ) {
valid = true ;
break ;
}
src_type = src_type - > get_base_script ( ) . ptr ( ) ;
}
}
}
if ( valid ) {
* dst = * src ; // Valid cast, copy the source object
} else {
* dst = Variant ( ) ; // invalid cast, assign NULL
}
ip + = 4 ;
}
DISPATCH_OPCODE ;
OPCODE ( OPCODE_CONSTRUCT ) {
CHECK_SPACE ( 2 ) ;
Variant : : Type t = Variant : : Type ( _code_ptr [ ip + 1 ] ) ;
int argc = _code_ptr [ ip + 2 ] ;
CHECK_SPACE ( argc + 2 ) ;
Variant * * argptrs = call_args ;
for ( int i = 0 ; i < argc ; i + + ) {
GET_VARIANT_PTR ( v , 3 + i ) ;
argptrs [ i ] = v ;
}
GET_VARIANT_PTR ( dst , 3 + argc ) ;
Variant : : CallError err ;
* dst = Variant : : construct ( t , ( const Variant * * ) argptrs , argc , err ) ;
# ifdef DEBUG_ENABLED
if ( err . error ! = Variant : : CallError : : CALL_OK ) {
err_text = _get_call_error ( err , " ' " + Variant : : get_type_name ( t ) + " ' constructor " , ( const Variant * * ) argptrs ) ;
OPCODE_BREAK ;
}
# endif
ip + = 4 + argc ;
//construct a basic type
}
DISPATCH_OPCODE ;
OPCODE ( OPCODE_CONSTRUCT_ARRAY ) {
CHECK_SPACE ( 1 ) ;
int argc = _code_ptr [ ip + 1 ] ;
Array array ; //arrays are always shared
array . resize ( argc ) ;
CHECK_SPACE ( argc + 2 ) ;
for ( int i = 0 ; i < argc ; i + + ) {
GET_VARIANT_PTR ( v , 2 + i ) ;
array [ i ] = * v ;
}
GET_VARIANT_PTR ( dst , 2 + argc ) ;
* dst = array ;
ip + = 3 + argc ;
}
DISPATCH_OPCODE ;
OPCODE ( OPCODE_CONSTRUCT_DICTIONARY ) {
CHECK_SPACE ( 1 ) ;
int argc = _code_ptr [ ip + 1 ] ;
Dictionary dict ; //arrays are always shared
CHECK_SPACE ( argc * 2 + 2 ) ;
for ( int i = 0 ; i < argc ; i + + ) {
GET_VARIANT_PTR ( k , 2 + i * 2 + 0 ) ;
GET_VARIANT_PTR ( v , 2 + i * 2 + 1 ) ;
dict [ * k ] = * v ;
}
GET_VARIANT_PTR ( dst , 2 + argc * 2 ) ;
* dst = dict ;
ip + = 3 + argc * 2 ;
}
DISPATCH_OPCODE ;
OPCODE ( OPCODE_CALL_RETURN )
OPCODE ( OPCODE_CALL ) {
CHECK_SPACE ( 4 ) ;
bool call_ret = _code_ptr [ ip ] = = OPCODE_CALL_RETURN ;
int argc = _code_ptr [ ip + 1 ] ;
GET_VARIANT_PTR ( base , 2 ) ;
int nameg = _code_ptr [ ip + 3 ] ;
GD_ERR_BREAK ( nameg < 0 | | nameg > = _global_names_count ) ;
const StringName * methodname = & _global_names_ptr [ nameg ] ;
GD_ERR_BREAK ( argc < 0 ) ;
ip + = 4 ;
CHECK_SPACE ( argc + 1 ) ;
Variant * * argptrs = call_args ;
for ( int i = 0 ; i < argc ; i + + ) {
GET_VARIANT_PTR ( v , i ) ;
argptrs [ i ] = v ;
}
# ifdef DEBUG_ENABLED
uint64_t call_time = 0 ;
if ( GDScriptLanguage : : get_singleton ( ) - > profiling ) {
call_time = OS : : get_singleton ( ) - > get_ticks_usec ( ) ;
}
# endif
Variant : : CallError err ;
if ( call_ret ) {
GET_VARIANT_PTR ( ret , argc ) ;
base - > call_ptr ( * methodname , ( const Variant * * ) argptrs , argc , ret , err ) ;
} else {
base - > call_ptr ( * methodname , ( const Variant * * ) argptrs , argc , nullptr , err ) ;
}
# ifdef DEBUG_ENABLED
if ( GDScriptLanguage : : get_singleton ( ) - > profiling ) {
function_call_time + = OS : : get_singleton ( ) - > get_ticks_usec ( ) - call_time ;
}
if ( err . error ! = Variant : : CallError : : CALL_OK ) {
String methodstr = * methodname ;
String basestr = _get_var_type ( base ) ;
if ( methodstr = = " call " ) {
if ( argc > = 1 ) {
methodstr = String ( * argptrs [ 0 ] ) + " (via call) " ;
if ( err . error = = Variant : : CallError : : CALL_ERROR_INVALID_ARGUMENT ) {
err . argument + = 1 ;
}
}
} else if ( methodstr = = " free " ) {
if ( err . error = = Variant : : CallError : : CALL_ERROR_INVALID_METHOD ) {
if ( base - > is_ref ( ) ) {
err_text = " Attempted to free a reference. " ;
OPCODE_BREAK ;
} else if ( base - > get_type ( ) = = Variant : : OBJECT ) {
err_text = " Attempted to free a locked object (calling or emitting). " ;
OPCODE_BREAK ;
}
}
} else if ( methodstr = = " call_recursive " & & basestr = = " TreeItem " ) {
if ( argc > = 1 ) {
methodstr = String ( * argptrs [ 0 ] ) + " (via TreeItem.call_recursive) " ;
if ( err . error = = Variant : : CallError : : CALL_ERROR_INVALID_ARGUMENT ) {
err . argument + = 1 ;
}
}
}
err_text = _get_call_error ( err , " function ' " + methodstr + " ' in base ' " + basestr + " ' " , ( const Variant * * ) argptrs ) ;
OPCODE_BREAK ;
}
# endif
//_call_func(NULL,base,*methodname,ip,argc,p_instance,stack);
ip + = argc + 1 ;
}
DISPATCH_OPCODE ;
OPCODE ( OPCODE_CALL_BUILT_IN ) {
CHECK_SPACE ( 4 ) ;
GDScriptFunctions : : Function func = GDScriptFunctions : : Function ( _code_ptr [ ip + 1 ] ) ;
int argc = _code_ptr [ ip + 2 ] ;
GD_ERR_BREAK ( argc < 0 ) ;
ip + = 3 ;
CHECK_SPACE ( argc + 1 ) ;
Variant * * argptrs = call_args ;
for ( int i = 0 ; i < argc ; i + + ) {
GET_VARIANT_PTR ( v , i ) ;
argptrs [ i ] = v ;
}
GET_VARIANT_PTR ( dst , argc ) ;
Variant : : CallError err ;
GDScriptFunctions : : call ( func , ( const Variant * * ) argptrs , argc , * dst , err ) ;
# ifdef DEBUG_ENABLED
if ( err . error ! = Variant : : CallError : : CALL_OK ) {
String methodstr = GDScriptFunctions : : get_func_name ( func ) ;
if ( dst - > get_type ( ) = = Variant : : STRING ) {
//call provided error string
err_text = " Error calling built-in function ' " + methodstr + " ': " + String ( * dst ) ;
} else {
err_text = _get_call_error ( err , " built-in function ' " + methodstr + " ' " , ( const Variant * * ) argptrs ) ;
}
OPCODE_BREAK ;
}
# endif
ip + = argc + 1 ;
}
DISPATCH_OPCODE ;
OPCODE ( OPCODE_CALL_SELF ) {
OPCODE_BREAK ;
}
OPCODE ( OPCODE_CALL_SELF_BASE ) {
CHECK_SPACE ( 2 ) ;
int self_fun = _code_ptr [ ip + 1 ] ;
# ifdef DEBUG_ENABLED
if ( self_fun < 0 | | self_fun > = _global_names_count ) {
err_text = " compiler bug, function name not found " ;
OPCODE_BREAK ;
}
# endif
const StringName * methodname = & _global_names_ptr [ self_fun ] ;
int argc = _code_ptr [ ip + 2 ] ;
CHECK_SPACE ( 2 + argc + 1 ) ;
Variant * * argptrs = call_args ;
for ( int i = 0 ; i < argc ; i + + ) {
GET_VARIANT_PTR ( v , i + 3 ) ;
argptrs [ i ] = v ;
}
GET_VARIANT_PTR ( dst , argc + 3 ) ;
const GDScript * gds = _script ;
const Map < StringName , GDScriptFunction * > : : Element * E = nullptr ;
while ( gds - > base . ptr ( ) ) {
gds = gds - > base . ptr ( ) ;
E = gds - > member_functions . find ( * methodname ) ;
if ( E ) {
break ;
}
}
Variant : : CallError err ;
if ( E ) {
* dst = E - > get ( ) - > call ( p_instance , ( const Variant * * ) argptrs , argc , err ) ;
} else if ( gds - > native . ptr ( ) ) {
if ( * methodname ! = GDScriptLanguage : : get_singleton ( ) - > strings . _init ) {
MethodBind * mb = ClassDB : : get_method ( gds - > native - > get_name ( ) , * methodname ) ;
if ( ! mb ) {
err . error = Variant : : CallError : : CALL_ERROR_INVALID_METHOD ;
} else {
* dst = mb - > call ( p_instance - > owner , ( const Variant * * ) argptrs , argc , err ) ;
}
} else {
err . error = Variant : : CallError : : CALL_OK ;
}
} else {
if ( * methodname ! = GDScriptLanguage : : get_singleton ( ) - > strings . _init ) {
err . error = Variant : : CallError : : CALL_ERROR_INVALID_METHOD ;
} else {
err . error = Variant : : CallError : : CALL_OK ;
}
}
if ( err . error ! = Variant : : CallError : : CALL_OK ) {
String methodstr = * methodname ;
err_text = _get_call_error ( err , " function ' " + methodstr + " ' " , ( const Variant * * ) argptrs ) ;
OPCODE_BREAK ;
}
ip + = 4 + argc ;
}
DISPATCH_OPCODE ;
OPCODE ( OPCODE_YIELD )
OPCODE ( OPCODE_YIELD_SIGNAL ) {
int ipofs = 1 ;
if ( _code_ptr [ ip ] = = OPCODE_YIELD_SIGNAL ) {
CHECK_SPACE ( 4 ) ;
ipofs + = 2 ;
} else {
CHECK_SPACE ( 2 ) ;
}
Ref < GDScriptFunctionState > gdfs = memnew ( GDScriptFunctionState ) ;
gdfs - > function = this ;
gdfs - > state . stack . resize ( alloca_size ) ;
//copy variant stack
for ( int i = 0 ; i < _stack_size ; i + + ) {
memnew_placement ( & gdfs - > state . stack . write [ sizeof ( Variant ) * i ] , Variant ( stack [ i ] ) ) ;
}
gdfs - > state . stack_size = _stack_size ;
gdfs - > state . self = self ;
gdfs - > state . alloca_size = alloca_size ;
gdfs - > state . ip = ip + ipofs ;
gdfs - > state . line = line ;
gdfs - > state . script = _script ;
GDScriptLanguage : : singleton - > lock . lock ( ) ;
_script - > pending_func_states . add ( & gdfs - > scripts_list ) ;
if ( p_instance ) {
gdfs - > state . instance = p_instance ;
p_instance - > pending_func_states . add ( & gdfs - > instances_list ) ;
} else {
gdfs - > state . instance = nullptr ;
}
GDScriptLanguage : : singleton - > lock . unlock ( ) ;
# ifdef DEBUG_ENABLED
gdfs - > state . function_name = name ;
gdfs - > state . script_path = _script - > get_path ( ) ;
# endif
//gdfs->state.result_pos=ip+ipofs-1;
gdfs - > state . defarg = defarg ;
gdfs - > function = this ;
retvalue = gdfs ;
if ( _code_ptr [ ip ] = = OPCODE_YIELD_SIGNAL ) {
//do the oneshot connect
GET_VARIANT_PTR ( argobj , 1 ) ;
GET_VARIANT_PTR ( argname , 2 ) ;
# ifdef DEBUG_ENABLED
if ( argobj - > get_type ( ) ! = Variant : : OBJECT ) {
err_text = " First argument of yield() not of type object. " ;
OPCODE_BREAK ;
}
if ( argname - > get_type ( ) ! = Variant : : STRING ) {
err_text = " Second argument of yield() not a string (for signal name). " ;
OPCODE_BREAK ;
}
# endif
Object * obj = argobj - > operator Object * ( ) ;
String signal = argname - > operator String ( ) ;
if ( argobj - > is_invalid_object ( ) ) {
err_text = " First argument of yield() is a previously freed instance. " ;
OPCODE_BREAK ;
}
# ifdef DEBUG_ENABLED
if ( ! obj ) {
err_text = " First argument of yield() is null. " ;
OPCODE_BREAK ;
}
if ( signal . length ( ) = = 0 ) {
err_text = " Second argument of yield() is an empty string (for signal name). " ;
OPCODE_BREAK ;
}
Error err = obj - > connect ( signal , gdfs . ptr ( ) , " _signal_callback " , varray ( gdfs ) , Object : : CONNECT_ONESHOT ) ;
if ( err ! = OK ) {
err_text = " Error connecting to signal: " + signal + " during yield(). " ;
OPCODE_BREAK ;
}
# else
obj - > connect ( signal , gdfs . ptr ( ) , " _signal_callback " , varray ( gdfs ) , Object : : CONNECT_ONESHOT ) ;
# endif
}
# ifdef DEBUG_ENABLED
exit_ok = true ;
yielded = true ;
# endif
OPCODE_BREAK ;
}
OPCODE ( OPCODE_YIELD_RESUME ) {
CHECK_SPACE ( 2 ) ;
# ifdef DEBUG_ENABLED
if ( ! p_state ) {
err_text = ( " Invalid Resume (bug?) " ) ;
OPCODE_BREAK ;
}
# endif
GET_VARIANT_PTR ( result , 1 ) ;
* result = p_state - > result ;
ip + = 2 ;
}
DISPATCH_OPCODE ;
OPCODE ( OPCODE_JUMP ) {
CHECK_SPACE ( 2 ) ;
int to = _code_ptr [ ip + 1 ] ;
GD_ERR_BREAK ( to < 0 | | to > _code_size ) ;
ip = to ;
}
DISPATCH_OPCODE ;
OPCODE ( OPCODE_JUMP_IF ) {
CHECK_SPACE ( 3 ) ;
GET_VARIANT_PTR ( test , 1 ) ;
bool result = test - > booleanize ( ) ;
if ( result ) {
int to = _code_ptr [ ip + 2 ] ;
GD_ERR_BREAK ( to < 0 | | to > _code_size ) ;
ip = to ;
} else {
ip + = 3 ;
}
}
DISPATCH_OPCODE ;
OPCODE ( OPCODE_JUMP_IF_NOT ) {
CHECK_SPACE ( 3 ) ;
GET_VARIANT_PTR ( test , 1 ) ;
bool result = test - > booleanize ( ) ;
if ( ! result ) {
int to = _code_ptr [ ip + 2 ] ;
GD_ERR_BREAK ( to < 0 | | to > _code_size ) ;
ip = to ;
} else {
ip + = 3 ;
}
}
DISPATCH_OPCODE ;
OPCODE ( OPCODE_JUMP_TO_DEF_ARGUMENT ) {
CHECK_SPACE ( 2 ) ;
ip = _default_arg_ptr [ defarg ] ;
}
DISPATCH_OPCODE ;
OPCODE ( OPCODE_RETURN ) {
CHECK_SPACE ( 2 ) ;
GET_VARIANT_PTR ( r , 1 ) ;
retvalue = * r ;
# ifdef DEBUG_ENABLED
exit_ok = true ;
# endif
OPCODE_BREAK ;
}
OPCODE ( OPCODE_ITERATE_BEGIN ) {
CHECK_SPACE ( 8 ) ; //space for this a regular iterate
GET_VARIANT_PTR ( counter , 1 ) ;
GET_VARIANT_PTR ( container , 2 ) ;
bool valid ;
if ( ! container - > iter_init ( * counter , valid ) ) {
# ifdef DEBUG_ENABLED
if ( ! valid ) {
err_text = " Unable to iterate on object of type ' " + Variant : : get_type_name ( container - > get_type ( ) ) + " '. " ;
OPCODE_BREAK ;
}
# endif
int jumpto = _code_ptr [ ip + 3 ] ;
GD_ERR_BREAK ( jumpto < 0 | | jumpto > _code_size ) ;
ip = jumpto ;
} else {
GET_VARIANT_PTR ( iterator , 4 ) ;
* iterator = container - > iter_get ( * counter , valid ) ;
# ifdef DEBUG_ENABLED
if ( ! valid ) {
err_text = " Unable to obtain iterator object of type ' " + Variant : : get_type_name ( container - > get_type ( ) ) + " '. " ;
OPCODE_BREAK ;
}
# endif
ip + = 5 ; //skip regular iterate which is always next
}
}
DISPATCH_OPCODE ;
OPCODE ( OPCODE_ITERATE ) {
CHECK_SPACE ( 4 ) ;
GET_VARIANT_PTR ( counter , 1 ) ;
GET_VARIANT_PTR ( container , 2 ) ;
bool valid ;
if ( ! container - > iter_next ( * counter , valid ) ) {
# ifdef DEBUG_ENABLED
if ( ! valid ) {
err_text = " Unable to iterate on object of type ' " + Variant : : get_type_name ( container - > get_type ( ) ) + " ' (type changed since first iteration?). " ;
OPCODE_BREAK ;
}
# endif
int jumpto = _code_ptr [ ip + 3 ] ;
GD_ERR_BREAK ( jumpto < 0 | | jumpto > _code_size ) ;
ip = jumpto ;
} else {
GET_VARIANT_PTR ( iterator , 4 ) ;
* iterator = container - > iter_get ( * counter , valid ) ;
# ifdef DEBUG_ENABLED
if ( ! valid ) {
err_text = " Unable to obtain iterator object of type ' " + Variant : : get_type_name ( container - > get_type ( ) ) + " ' (but was obtained on first iteration?). " ;
OPCODE_BREAK ;
}
# endif
ip + = 5 ; //loop again
}
}
DISPATCH_OPCODE ;
OPCODE ( OPCODE_ASSERT ) {
CHECK_SPACE ( 3 ) ;
# ifdef DEBUG_ENABLED
GET_VARIANT_PTR ( test , 1 ) ;
bool result = test - > booleanize ( ) ;
if ( ! result ) {
String message_str ;
if ( _code_ptr [ ip + 2 ] ! = 0 ) {
GET_VARIANT_PTR ( message , 2 ) ;
message_str = * message ;
}
if ( message_str . empty ( ) ) {
err_text = " Assertion failed. " ;
} else {
err_text = " Assertion failed: " + message_str ;
}
OPCODE_BREAK ;
}
# endif
ip + = 3 ;
}
DISPATCH_OPCODE ;
OPCODE ( OPCODE_BREAKPOINT ) {
# ifdef DEBUG_ENABLED
if ( ScriptDebugger : : get_singleton ( ) ) {
GDScriptLanguage : : get_singleton ( ) - > debug_break ( " Breakpoint Statement " , true ) ;
}
# endif
ip + = 1 ;
}
DISPATCH_OPCODE ;
OPCODE ( OPCODE_LINE ) {
CHECK_SPACE ( 2 ) ;
line = _code_ptr [ ip + 1 ] ;
ip + = 2 ;
if ( ScriptDebugger : : get_singleton ( ) ) {
// line
bool do_break = false ;
if ( ScriptDebugger : : get_singleton ( ) - > get_lines_left ( ) > 0 ) {
if ( ScriptDebugger : : get_singleton ( ) - > get_depth ( ) < = 0 ) {
ScriptDebugger : : get_singleton ( ) - > set_lines_left ( ScriptDebugger : : get_singleton ( ) - > get_lines_left ( ) - 1 ) ;
}
if ( ScriptDebugger : : get_singleton ( ) - > get_lines_left ( ) < = 0 ) {
do_break = true ;
}
}
if ( ScriptDebugger : : get_singleton ( ) - > is_breakpoint ( line , source ) ) {
do_break = true ;
}
if ( do_break ) {
GDScriptLanguage : : get_singleton ( ) - > debug_break ( " Breakpoint " , true ) ;
}
ScriptDebugger : : get_singleton ( ) - > line_poll ( ) ;
}
}
DISPATCH_OPCODE ;
OPCODE ( OPCODE_END ) {
# ifdef DEBUG_ENABLED
exit_ok = true ;
# endif
OPCODE_BREAK ;
}
#if 0 // Enable for debugging.
default : {
err_text = " Illegal opcode " + itos ( _code_ptr [ ip ] ) + " at address " + itos ( ip ) ;
OPCODE_BREAK ;
}
# endif
}
OPCODES_END
# ifdef DEBUG_ENABLED
if ( exit_ok ) {
OPCODE_OUT ;
}
//error
// function, file, line, error, explanation
String err_file ;
if ( p_instance & & ! instance . is_invalid_object ( ) & & p_instance - > script - > is_valid ( ) & & p_instance - > script - > path ! = " " ) {
err_file = p_instance - > script - > path ;
} else if ( script ) {
err_file = script - > path ;
}
if ( err_file = = " " ) {
err_file = " <built-in> " ;
}
String err_func = name ;
if ( p_instance & & ! instance . is_invalid_object ( ) & & p_instance - > script - > is_valid ( ) & & p_instance - > script - > name ! = " " ) {
err_func = p_instance - > script - > name + " . " + err_func ;
}
int err_line = line ;
if ( err_text = = " " ) {
err_text = " Internal script error! Opcode: " + itos ( last_opcode ) + " (please report). " ;
}
if ( ! GDScriptLanguage : : get_singleton ( ) - > debug_break ( err_text , false ) ) {
// debugger break did not happen
_err_print_error ( err_func . utf8 ( ) . get_data ( ) , err_file . utf8 ( ) . get_data ( ) , err_line , err_text . utf8 ( ) . get_data ( ) , ERR_HANDLER_SCRIPT ) ;
}
# endif
OPCODE_OUT ;
}
OPCODES_OUT
# ifdef DEBUG_ENABLED
if ( GDScriptLanguage : : get_singleton ( ) - > profiling ) {
uint64_t time_taken = OS : : get_singleton ( ) - > get_ticks_usec ( ) - function_start_time ;
profile . total_time + = time_taken ;
profile . self_time + = time_taken - function_call_time ;
profile . frame_total_time + = time_taken ;
profile . frame_self_time + = time_taken - function_call_time ;
GDScriptLanguage : : get_singleton ( ) - > script_frame_time + = time_taken - function_call_time ;
}
// Check if this is the last time the function is resuming from yield
// Will be true if never yielded as well
// When it's the last resume it will postpone the exit from stack,
// so the debugger knows which function triggered the resume of the next function (if any)
if ( ! p_state | | yielded ) {
if ( ScriptDebugger : : get_singleton ( ) ) {
GDScriptLanguage : : get_singleton ( ) - > exit_function ( ) ;
}
# endif
if ( _stack_size ) {
//free stack
for ( int i = 0 ; i < _stack_size ; i + + ) {
stack [ i ] . ~ Variant ( ) ;
}
}
# ifdef DEBUG_ENABLED
}
# endif
return retvalue ;
}
const int * GDScriptFunction : : get_code ( ) const {
return _code_ptr ;
}
int GDScriptFunction : : get_code_size ( ) const {
return _code_size ;
}
Variant GDScriptFunction : : get_constant ( int p_idx ) const {
ERR_FAIL_INDEX_V ( p_idx , constants . size ( ) , " <errconst> " ) ;
return constants [ p_idx ] ;
}
StringName GDScriptFunction : : get_global_name ( int p_idx ) const {
ERR_FAIL_INDEX_V ( p_idx , global_names . size ( ) , " <errgname> " ) ;
return global_names [ p_idx ] ;
}
int GDScriptFunction : : get_default_argument_count ( ) const {
return _default_arg_count ;
}
int GDScriptFunction : : get_default_argument_addr ( int p_idx ) const {
ERR_FAIL_INDEX_V ( p_idx , default_arguments . size ( ) , - 1 ) ;
return default_arguments [ p_idx ] ;
}
GDScriptDataType GDScriptFunction : : get_return_type ( ) const {
return return_type ;
}
GDScriptDataType GDScriptFunction : : get_argument_type ( int p_idx ) const {
ERR_FAIL_INDEX_V ( p_idx , argument_types . size ( ) , GDScriptDataType ( ) ) ;
return argument_types [ p_idx ] ;
}
StringName GDScriptFunction : : get_name ( ) const {
return name ;
}
int GDScriptFunction : : get_max_stack_size ( ) const {
return _stack_size ;
}
struct _GDFKC {
int order ;
List < int > pos ;
} ;
struct _GDFKCS {
int order ;
StringName id ;
int pos ;
bool operator < ( const _GDFKCS & p_r ) const {
return order < p_r . order ;
}
} ;
void GDScriptFunction : : debug_get_stack_member_state ( int p_line , List < Pair < StringName , int > > * r_stackvars ) const {
int oc = 0 ;
Map < StringName , _GDFKC > sdmap ;
for ( const List < StackDebug > : : Element * E = stack_debug . front ( ) ; E ; E = E - > next ( ) ) {
const StackDebug & sd = E - > get ( ) ;
if ( sd . line > p_line ) {
break ;
}
if ( sd . added ) {
if ( ! sdmap . has ( sd . identifier ) ) {
_GDFKC d ;
d . order = oc + + ;
d . pos . push_back ( sd . pos ) ;
sdmap [ sd . identifier ] = d ;
} else {
sdmap [ sd . identifier ] . pos . push_back ( sd . pos ) ;
}
} else {
ERR_CONTINUE ( ! sdmap . has ( sd . identifier ) ) ;
sdmap [ sd . identifier ] . pos . pop_back ( ) ;
if ( sdmap [ sd . identifier ] . pos . empty ( ) ) {
sdmap . erase ( sd . identifier ) ;
}
}
}
List < _GDFKCS > stackpositions ;
for ( Map < StringName , _GDFKC > : : Element * E = sdmap . front ( ) ; E ; E = E - > next ( ) ) {
_GDFKCS spp ;
spp . id = E - > key ( ) ;
spp . order = E - > get ( ) . order ;
spp . pos = E - > get ( ) . pos . back ( ) - > get ( ) ;
stackpositions . push_back ( spp ) ;
}
stackpositions . sort ( ) ;
for ( List < _GDFKCS > : : Element * E = stackpositions . front ( ) ; E ; E = E - > next ( ) ) {
Pair < StringName , int > p ;
p . first = E - > get ( ) . id ;
p . second = E - > get ( ) . pos ;
r_stackvars - > push_back ( p ) ;
}
}
GDScriptFunction : : GDScriptFunction ( ) :
function_list ( this ) {
_stack_size = 0 ;
_call_size = 0 ;
rpc_mode = MultiplayerAPI : : RPC_MODE_DISABLED ;
name = " <anonymous> " ;
# ifdef DEBUG_ENABLED
_func_cname = nullptr ;
GDScriptLanguage : : get_singleton ( ) - > lock . lock ( ) ;
GDScriptLanguage : : get_singleton ( ) - > function_list . add ( & function_list ) ;
GDScriptLanguage : : get_singleton ( ) - > lock . unlock ( ) ;
profile . call_count = 0 ;
profile . self_time = 0 ;
profile . total_time = 0 ;
profile . frame_call_count = 0 ;
profile . frame_self_time = 0 ;
profile . frame_total_time = 0 ;
profile . last_frame_call_count = 0 ;
profile . last_frame_self_time = 0 ;
profile . last_frame_total_time = 0 ;
# endif
}
GDScriptFunction : : ~ GDScriptFunction ( ) {
# ifdef DEBUG_ENABLED
GDScriptLanguage : : get_singleton ( ) - > lock . lock ( ) ;
GDScriptLanguage : : get_singleton ( ) - > function_list . remove ( & function_list ) ;
GDScriptLanguage : : get_singleton ( ) - > lock . unlock ( ) ;
# endif
}
/////////////////////
Variant GDScriptFunctionState : : _signal_callback ( const Variant * * p_args , int p_argcount , Variant : : CallError & r_error ) {
Variant arg ;
r_error . error = Variant : : CallError : : CALL_OK ;
if ( p_argcount = = 0 ) {
r_error . error = Variant : : CallError : : CALL_ERROR_TOO_FEW_ARGUMENTS ;
r_error . argument = 1 ;
return Variant ( ) ;
} else if ( p_argcount = = 1 ) {
//noooneee
} else if ( p_argcount = = 2 ) {
arg = * p_args [ 0 ] ;
} else {
Array extra_args ;
for ( int i = 0 ; i < p_argcount - 1 ; i + + ) {
extra_args . push_back ( * p_args [ i ] ) ;
}
arg = extra_args ;
}
Ref < GDScriptFunctionState > self = * p_args [ p_argcount - 1 ] ;
if ( self . is_null ( ) ) {
r_error . error = Variant : : CallError : : CALL_ERROR_INVALID_ARGUMENT ;
r_error . argument = p_argcount - 1 ;
r_error . expected = Variant : : OBJECT ;
return Variant ( ) ;
}
return resume ( arg ) ;
}
bool GDScriptFunctionState : : is_valid ( bool p_extended_check ) const {
if ( function = = nullptr ) {
return false ;
}
if ( p_extended_check ) {
# ifndef NO_THREADS
MutexLock lock ( GDScriptLanguage : : get_singleton ( ) - > lock ) ;
# endif
// Script gone?
if ( ! scripts_list . in_list ( ) ) {
return false ;
}
// Class instance gone? (if not static function)
if ( state . instance & & ! instances_list . in_list ( ) ) {
return false ;
}
}
return true ;
}
Variant GDScriptFunctionState : : resume ( const Variant & p_arg ) {
ERR_FAIL_COND_V ( ! function , Variant ( ) ) ;
{
# ifndef NO_THREADS
MutexLock lock ( GDScriptLanguage : : singleton - > lock ) ;
# endif
if ( ! scripts_list . in_list ( ) ) {
# ifdef DEBUG_ENABLED
ERR_FAIL_V_MSG ( Variant ( ) , " Resumed function ' " + state . function_name + " ()' after yield, but script is gone. At script: " + state . script_path + " : " + itos ( state . line ) ) ;
# else
return Variant ( ) ;
# endif
}
if ( state . instance & & ! instances_list . in_list ( ) ) {
# ifdef DEBUG_ENABLED
ERR_FAIL_V_MSG ( Variant ( ) , " Resumed function ' " + state . function_name + " ()' after yield, but class instance is gone. At script: " + state . script_path + " : " + itos ( state . line ) ) ;
# else
return Variant ( ) ;
# endif
}
// Do these now to avoid locking again after the call
scripts_list . remove_from_list ( ) ;
instances_list . remove_from_list ( ) ;
}
state . result = p_arg ;
Variant : : CallError err ;
Variant ret = function - > call ( nullptr , nullptr , 0 , err , & state ) ;
bool completed = true ;
// If the return value is a GDScriptFunctionState reference,
// then the function did yield again after resuming.
if ( ret . is_ref ( ) ) {
GDScriptFunctionState * gdfs = Object : : cast_to < GDScriptFunctionState > ( ret ) ;
if ( gdfs & & gdfs - > function = = function ) {
completed = false ;
gdfs - > first_state = first_state . is_valid ( ) ? first_state : Ref < GDScriptFunctionState > ( this ) ;
}
}
function = nullptr ; //cleaned up;
state . result = Variant ( ) ;
if ( completed ) {
if ( first_state . is_valid ( ) ) {
first_state - > emit_signal ( " completed " , ret ) ;
} else {
emit_signal ( " completed " , ret ) ;
}
# ifdef DEBUG_ENABLED
if ( ScriptDebugger : : get_singleton ( ) ) {
GDScriptLanguage : : get_singleton ( ) - > exit_function ( ) ;
}
# endif
}
return ret ;
}
void GDScriptFunctionState : : _clear_stack ( ) {
if ( state . stack_size ) {
Variant * stack = ( Variant * ) state . stack . ptr ( ) ;
for ( int i = 0 ; i < state . stack_size ; i + + ) {
stack [ i ] . ~ Variant ( ) ;
}
state . stack_size = 0 ;
}
}
void GDScriptFunctionState : : _bind_methods ( ) {
ClassDB : : bind_method ( D_METHOD ( " resume " , " arg " ) , & GDScriptFunctionState : : resume , DEFVAL ( Variant ( ) ) ) ;
ClassDB : : bind_method ( D_METHOD ( " is_valid " , " extended_check " ) , & GDScriptFunctionState : : is_valid , DEFVAL ( false ) ) ;
ClassDB : : bind_vararg_method ( METHOD_FLAGS_DEFAULT , " _signal_callback " , & GDScriptFunctionState : : _signal_callback , MethodInfo ( " _signal_callback " ) ) ;
ADD_SIGNAL ( MethodInfo ( " completed " , PropertyInfo ( Variant : : NIL , " result " , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_NIL_IS_VARIANT ) ) ) ;
}
GDScriptFunctionState : : GDScriptFunctionState ( ) :
scripts_list ( this ) ,
instances_list ( this ) {
function = nullptr ;
}
GDScriptFunctionState : : ~ GDScriptFunctionState ( ) {
_clear_stack ( ) ;
GDScriptLanguage : : singleton - > lock . lock ( ) ;
scripts_list . remove_from_list ( ) ;
instances_list . remove_from_list ( ) ;
GDScriptLanguage : : singleton - > lock . unlock ( ) ;
}