2020-11-12 14:14:48 +01:00
/*************************************************************************/
/* gdscript_vm.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 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 , _global_names_count , nullptr ) ;
# endif
StringName id = _global_names_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 ) {
bool was_freed ;
Object * bobj = p_var - > get_validated_object_with_check ( was_freed ) ;
if ( ! bobj ) {
if ( was_freed ) {
basestr = " null instance " ;
} else {
basestr = " previously freed " ;
}
} 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 Callable : : CallError & p_err , const String & p_where , const Variant * * argptrs ) const {
String err_text ;
if ( p_err . error = = Callable : : 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 ( Variant : : Type ( p_err . expected ) ) + " . " ;
}
} else if ( p_err . error = = Callable : : CallError : : CALL_ERROR_TOO_MANY_ARGUMENTS ) {
err_text = " Invalid call to " + p_where + " . Expected " + itos ( p_err . argument ) + " arguments. " ;
} else if ( p_err . error = = Callable : : CallError : : CALL_ERROR_TOO_FEW_ARGUMENTS ) {
err_text = " Invalid call to " + p_where + " . Expected " + itos ( p_err . argument ) + " arguments. " ;
} else if ( p_err . error = = Callable : : CallError : : CALL_ERROR_INVALID_METHOD ) {
err_text = " Invalid call. Nonexistent " + p_where + " . " ;
} else if ( p_err . error = = Callable : : 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__)
2020-11-17 14:44:52 +01:00
# define OPCODES_TABLE \
static const void * switch_table_ops [ ] = { \
& & OPCODE_OPERATOR , \
& & OPCODE_OPERATOR_VALIDATED , \
& & OPCODE_EXTENDS_TEST , \
& & OPCODE_IS_BUILTIN , \
& & OPCODE_SET_KEYED , \
& & OPCODE_SET_KEYED_VALIDATED , \
& & OPCODE_SET_INDEXED_VALIDATED , \
& & OPCODE_GET_KEYED , \
& & OPCODE_GET_KEYED_VALIDATED , \
& & OPCODE_GET_INDEXED_VALIDATED , \
& & OPCODE_SET_NAMED , \
& & OPCODE_SET_NAMED_VALIDATED , \
& & OPCODE_GET_NAMED , \
& & OPCODE_GET_NAMED_VALIDATED , \
& & 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_ASYNC , \
& & OPCODE_CALL_BUILT_IN , \
& & OPCODE_CALL_SELF_BASE , \
& & OPCODE_CALL_METHOD_BIND , \
& & OPCODE_CALL_METHOD_BIND_RET , \
& & OPCODE_CALL_PTRCALL_NO_RETURN , \
& & OPCODE_CALL_PTRCALL_BOOL , \
& & OPCODE_CALL_PTRCALL_INT , \
& & OPCODE_CALL_PTRCALL_FLOAT , \
& & OPCODE_CALL_PTRCALL_STRING , \
& & OPCODE_CALL_PTRCALL_VECTOR2 , \
& & OPCODE_CALL_PTRCALL_VECTOR2I , \
& & OPCODE_CALL_PTRCALL_RECT2 , \
& & OPCODE_CALL_PTRCALL_RECT2I , \
& & OPCODE_CALL_PTRCALL_VECTOR3 , \
& & OPCODE_CALL_PTRCALL_VECTOR3I , \
& & OPCODE_CALL_PTRCALL_TRANSFORM2D , \
& & OPCODE_CALL_PTRCALL_PLANE , \
& & OPCODE_CALL_PTRCALL_QUAT , \
& & OPCODE_CALL_PTRCALL_AABB , \
& & OPCODE_CALL_PTRCALL_BASIS , \
& & OPCODE_CALL_PTRCALL_TRANSFORM , \
& & OPCODE_CALL_PTRCALL_COLOR , \
& & OPCODE_CALL_PTRCALL_STRING_NAME , \
& & OPCODE_CALL_PTRCALL_NODE_PATH , \
& & OPCODE_CALL_PTRCALL_RID , \
& & OPCODE_CALL_PTRCALL_OBJECT , \
& & OPCODE_CALL_PTRCALL_CALLABLE , \
& & OPCODE_CALL_PTRCALL_SIGNAL , \
& & OPCODE_CALL_PTRCALL_DICTIONARY , \
& & OPCODE_CALL_PTRCALL_ARRAY , \
& & OPCODE_CALL_PTRCALL_PACKED_BYTE_ARRAY , \
& & OPCODE_CALL_PTRCALL_PACKED_INT32_ARRAY , \
& & OPCODE_CALL_PTRCALL_PACKED_INT64_ARRAY , \
& & OPCODE_CALL_PTRCALL_PACKED_FLOAT32_ARRAY , \
& & OPCODE_CALL_PTRCALL_PACKED_FLOAT64_ARRAY , \
& & OPCODE_CALL_PTRCALL_PACKED_STRING_ARRAY , \
& & OPCODE_CALL_PTRCALL_PACKED_VECTOR2_ARRAY , \
& & OPCODE_CALL_PTRCALL_PACKED_VECTOR3_ARRAY , \
& & OPCODE_CALL_PTRCALL_PACKED_COLOR_ARRAY , \
& & OPCODE_AWAIT , \
& & OPCODE_AWAIT_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 \
} ; \
2020-11-12 14:14:48 +01:00
static_assert ( ( sizeof ( switch_table_ops ) / sizeof ( switch_table_ops [ 0 ] ) = = ( OPCODE_END + 1 ) ) , " Opcodes in jump table aren't the same as opcodes in enum. " ) ;
# define OPCODE(m_op) \
m_op :
2020-11-13 14:31:14 +01:00
# define OPCODE_WHILE(m_test) \
OPSWHILE :
2020-11-12 14:14:48 +01:00
# define OPCODES_END \
OPSEXIT :
# define OPCODES_OUT \
OPSOUT :
2020-11-13 14:31:14 +01:00
# define DISPATCH_OPCODE goto OPSWHILE
# define OPCODE_SWITCH(m_test) goto *switch_table_ops[m_test];
2020-11-12 14:14:48 +01:00
# 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
2020-11-17 14:44:52 +01:00
// Helpers for VariantInternal methods in macros.
# define OP_GET_BOOL get_bool
# define OP_GET_INT get_int
# define OP_GET_FLOAT get_float
# define OP_GET_VECTOR2 get_vector2
# define OP_GET_VECTOR2I get_vector2i
# define OP_GET_VECTOR3 get_vector3
# define OP_GET_VECTOR3I get_vector3i
# define OP_GET_RECT2 get_rect2
# define OP_GET_RECT2I get_rect2i
# define OP_GET_QUAT get_quat
# define OP_GET_COLOR get_color
# define OP_GET_STRING get_string
# define OP_GET_STRING_NAME get_string_name
# define OP_GET_NODE_PATH get_node_path
# define OP_GET_CALLABLE get_callable
# define OP_GET_SIGNAL get_signal
# define OP_GET_ARRAY get_array
# define OP_GET_DICTIONARY get_dictionary
# define OP_GET_PACKED_BYTE_ARRAY get_byte_array
# define OP_GET_PACKED_INT32_ARRAY get_int32_array
# define OP_GET_PACKED_INT64_ARRAY get_int64_array
# define OP_GET_PACKED_FLOAT32_ARRAY get_float32_array
# define OP_GET_PACKED_FLOAT64_ARRAY get_float64_array
# define OP_GET_PACKED_STRING_ARRAY get_string_array
# define OP_GET_PACKED_VECTOR2_ARRAY get_vector2_array
# define OP_GET_PACKED_VECTOR3_ARRAY get_vector3_array
# define OP_GET_PACKED_COLOR_ARRAY get_color_array
# define OP_GET_TRANSFORM get_transform
# define OP_GET_TRANSFORM2D get_transform2d
# define OP_GET_PLANE get_plane
# define OP_GET_AABB get_aabb
# define OP_GET_BASIS get_basis
# define OP_GET_RID get_rid
2020-11-12 14:14:48 +01:00
Variant GDScriptFunction : : call ( GDScriptInstance * p_instance , const Variant * * p_args , int p_argcount , Callable : : CallError & r_err , CallState * p_state ) {
OPCODES_TABLE ;
if ( ! _code_ptr ) {
return Variant ( ) ;
}
r_err . error = Callable : : CallError : : CALL_OK ;
Variant self ;
Variant static_ref ;
Variant retvalue ;
Variant * stack = nullptr ;
2020-11-13 14:31:14 +01:00
Variant * * instruction_args ;
2020-11-17 14:44:52 +01:00
const void * * call_args_ptr = nullptr ;
2020-11-12 14:14:48 +01:00
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 (awaited)
stack = ( Variant * ) p_state - > stack . ptr ( ) ;
2020-11-13 14:31:14 +01:00
instruction_args = ( Variant * * ) & p_state - > stack . ptr ( ) [ sizeof ( Variant ) * p_state - > stack_size ] ; //ptr() to avoid bounds check
2020-11-12 14:14:48 +01:00
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 ;
} else {
if ( p_argcount ! = _argument_count ) {
if ( p_argcount > _argument_count ) {
r_err . error = Callable : : 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 = Callable : : CallError : : CALL_ERROR_TOO_FEW_ARGUMENTS ;
r_err . argument = _argument_count - _default_arg_count ;
return Variant ( ) ;
} else {
defarg = _argument_count - p_argcount ;
}
}
2020-11-13 14:31:14 +01:00
alloca_size = sizeof ( Variant * ) * _instruction_args_size + sizeof ( Variant ) * _stack_size ;
2020-11-12 14:14:48 +01:00
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 ) ) {
r_err . error = Callable : : 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 , arg , & 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 ;
}
2020-11-13 14:31:14 +01:00
if ( _instruction_args_size ) {
instruction_args = ( Variant * * ) & aptr [ sizeof ( Variant ) * _stack_size ] ;
2020-11-12 14:14:48 +01:00
} else {
2020-11-13 14:31:14 +01:00
instruction_args = nullptr ;
2020-11-12 14:14:48 +01:00
}
} else {
stack = nullptr ;
2020-11-13 14:31:14 +01:00
instruction_args = nullptr ;
2020-11-12 14:14:48 +01:00
}
if ( p_instance ) {
if ( p_instance - > base_ref & & static_cast < Reference * > ( p_instance - > owner ) - > is_referenced ( ) ) {
self = REF ( static_cast < Reference * > ( p_instance - > owner ) ) ;
} else {
self = p_instance - > owner ;
}
script = p_instance - > script . ptr ( ) ;
} else {
script = _script ;
}
}
2020-11-17 14:44:52 +01:00
if ( _ptrcall_args_size ) {
call_args_ptr = ( const void * * ) alloca ( _ptrcall_args_size * sizeof ( void * ) ) ;
} else {
call_args_ptr = nullptr ;
}
2020-11-12 14:14:48 +01:00
static_ref = script ;
String err_text ;
# ifdef DEBUG_ENABLED
if ( EngineDebugger : : is_active ( ) ) {
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
2020-11-13 14:31:14 +01:00
# define GET_INSTRUCTION_ARG(m_v, m_idx) \
Variant * m_v = instruction_args [ m_idx ]
2020-11-12 14:14:48 +01:00
# 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 awaited = false ;
# endif
# ifdef DEBUG_ENABLED
OPCODE_WHILE ( ip < _code_size ) {
int last_opcode = _code_ptr [ ip ] ;
# else
OPCODE_WHILE ( true ) {
# endif
2020-11-13 14:31:14 +01:00
// Load arguments for the instruction before each instruction.
int instr_arg_count = ( ( _code_ptr [ ip ] ) & INSTR_ARGS_MASK ) > > INSTR_BITS ;
for ( int i = 0 ; i < instr_arg_count ; i + + ) {
GET_VARIANT_PTR ( v , i + 1 ) ;
instruction_args [ i ] = v ;
}
2020-11-12 14:14:48 +01:00
2020-11-13 14:31:14 +01:00
OPCODE_SWITCH ( _code_ptr [ ip ] & INSTR_MASK ) {
2020-11-12 14:14:48 +01:00
OPCODE ( OPCODE_OPERATOR ) {
CHECK_SPACE ( 5 ) ;
bool valid ;
2020-11-13 14:31:14 +01:00
Variant : : Operator op = ( Variant : : Operator ) _code_ptr [ ip + 4 ] ;
2020-11-12 14:14:48 +01:00
GD_ERR_BREAK ( op > = Variant : : OP_MAX ) ;
2020-11-13 14:31:14 +01:00
GET_INSTRUCTION_ARG ( a , 0 ) ;
GET_INSTRUCTION_ARG ( b , 1 ) ;
GET_INSTRUCTION_ARG ( dst , 2 ) ;
2020-11-12 14:14:48 +01:00
# 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 ;
2020-11-13 20:47:45 +01:00
OPCODE ( OPCODE_OPERATOR_VALIDATED ) {
CHECK_SPACE ( 5 ) ;
int operator_idx = _code_ptr [ ip + 4 ] ;
GD_ERR_BREAK ( operator_idx < 0 | | operator_idx > = _operator_funcs_count ) ;
Variant : : ValidatedOperatorEvaluator operator_func = _operator_funcs_ptr [ operator_idx ] ;
GET_INSTRUCTION_ARG ( a , 0 ) ;
GET_INSTRUCTION_ARG ( b , 1 ) ;
GET_INSTRUCTION_ARG ( dst , 2 ) ;
operator_func ( a , b , dst ) ;
ip + = 5 ;
}
DISPATCH_OPCODE ;
2020-11-12 14:14:48 +01:00
OPCODE ( OPCODE_EXTENDS_TEST ) {
CHECK_SPACE ( 4 ) ;
2020-11-13 14:31:14 +01:00
GET_INSTRUCTION_ARG ( a , 0 ) ;
GET_INSTRUCTION_ARG ( b , 1 ) ;
GET_INSTRUCTION_ARG ( dst , 2 ) ;
2020-11-12 14:14:48 +01:00
# ifdef DEBUG_ENABLED
if ( b - > get_type ( ) ! = Variant : : OBJECT | | b - > operator Object * ( ) = = nullptr ) {
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 ) {
# ifdef DEBUG_ENABLED
bool was_freed ;
Object * obj_A = a - > get_validated_object_with_check ( was_freed ) ;
if ( was_freed ) {
err_text = " Left operand of 'is' is a previously freed instance. " ;
OPCODE_BREAK ;
}
Object * obj_B = b - > get_validated_object_with_check ( was_freed ) ;
if ( was_freed ) {
err_text = " Right operand of 'is' is a previously freed instance. " ;
OPCODE_BREAK ;
}
# else
Object * obj_A = * a ;
Object * obj_B = * b ;
# endif // DEBUG_ENABLED
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 ) {
err_text = " Right operand of 'is' is not a class (type: ' " + obj_B - > get_class ( ) + " '). " ;
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 ) ;
2020-11-13 14:31:14 +01:00
GET_INSTRUCTION_ARG ( value , 0 ) ;
GET_INSTRUCTION_ARG ( dst , 1 ) ;
Variant : : Type var_type = ( Variant : : Type ) _code_ptr [ ip + 3 ] ;
2020-11-12 14:14:48 +01:00
GD_ERR_BREAK ( var_type < 0 | | var_type > = Variant : : VARIANT_MAX ) ;
* dst = value - > get_type ( ) = = var_type ;
ip + = 4 ;
}
DISPATCH_OPCODE ;
2020-11-16 16:59:53 +01:00
OPCODE ( OPCODE_SET_KEYED ) {
2020-11-12 14:14:48 +01:00
CHECK_SPACE ( 3 ) ;
2020-11-13 14:31:14 +01:00
GET_INSTRUCTION_ARG ( dst , 0 ) ;
GET_INSTRUCTION_ARG ( index , 1 ) ;
GET_INSTRUCTION_ARG ( value , 2 ) ;
2020-11-12 14:14:48 +01:00
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 ;
2020-11-16 16:59:53 +01:00
OPCODE ( OPCODE_SET_KEYED_VALIDATED ) {
CHECK_SPACE ( 4 ) ;
GET_INSTRUCTION_ARG ( dst , 0 ) ;
GET_INSTRUCTION_ARG ( index , 1 ) ;
GET_INSTRUCTION_ARG ( value , 2 ) ;
int index_setter = _code_ptr [ ip + 4 ] ;
GD_ERR_BREAK ( index_setter < 0 | | index_setter > = _keyed_setters_count ) ;
const Variant : : ValidatedKeyedSetter setter = _keyed_setters_ptr [ index_setter ] ;
bool valid ;
setter ( dst , 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 + = 5 ;
}
DISPATCH_OPCODE ;
OPCODE ( OPCODE_SET_INDEXED_VALIDATED ) {
CHECK_SPACE ( 4 ) ;
GET_INSTRUCTION_ARG ( dst , 0 ) ;
GET_INSTRUCTION_ARG ( index , 1 ) ;
GET_INSTRUCTION_ARG ( value , 2 ) ;
int index_setter = _code_ptr [ ip + 4 ] ;
GD_ERR_BREAK ( index_setter < 0 | | index_setter > = _indexed_setters_count ) ;
const Variant : : ValidatedIndexedSetter setter = _indexed_setters_ptr [ index_setter ] ;
int64_t int_index = * VariantInternal : : get_int ( index ) ;
bool oob ;
setter ( dst , int_index , value , oob ) ;
# ifdef DEBUG_ENABLED
if ( oob ) {
String v = index - > operator String ( ) ;
if ( v ! = " " ) {
v = " ' " + v + " ' " ;
} else {
v = " of type ' " + _get_var_type ( index ) + " ' " ;
}
err_text = " Out of bounds set index " + v + " (on base: ' " + _get_var_type ( dst ) + " ') " ;
OPCODE_BREAK ;
}
# endif
ip + = 5 ;
}
DISPATCH_OPCODE ;
OPCODE ( OPCODE_GET_KEYED ) {
2020-11-12 14:14:48 +01:00
CHECK_SPACE ( 3 ) ;
2020-11-13 14:31:14 +01:00
GET_INSTRUCTION_ARG ( src , 0 ) ;
GET_INSTRUCTION_ARG ( index , 1 ) ;
GET_INSTRUCTION_ARG ( dst , 2 ) ;
2020-11-12 14:14:48 +01:00
bool valid ;
# ifdef DEBUG_ENABLED
2020-11-16 16:59:53 +01:00
// Allow better error message in cases where src and dst are the same stack position.
2020-11-12 14:14:48 +01:00
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 ) + " '). " ;
OPCODE_BREAK ;
}
* dst = ret ;
# endif
ip + = 4 ;
}
DISPATCH_OPCODE ;
2020-11-16 16:59:53 +01:00
OPCODE ( OPCODE_GET_KEYED_VALIDATED ) {
CHECK_SPACE ( 4 ) ;
GET_INSTRUCTION_ARG ( src , 0 ) ;
GET_INSTRUCTION_ARG ( key , 1 ) ;
GET_INSTRUCTION_ARG ( dst , 2 ) ;
int index_getter = _code_ptr [ ip + 4 ] ;
GD_ERR_BREAK ( index_getter < 0 | | index_getter > = _keyed_getters_count ) ;
const Variant : : ValidatedKeyedGetter getter = _keyed_getters_ptr [ index_getter ] ;
bool valid ;
# ifdef DEBUG_ENABLED
// Allow better error message in cases where src and dst are the same stack position.
Variant ret ;
getter ( src , key , & ret , valid ) ;
# else
getter ( src , key , dst , valid ) ;
# endif
# ifdef DEBUG_ENABLED
if ( ! valid ) {
String v = key - > operator String ( ) ;
if ( v ! = " " ) {
v = " ' " + v + " ' " ;
} else {
v = " of type ' " + _get_var_type ( key ) + " ' " ;
}
err_text = " Invalid get index " + v + " (on base: ' " + _get_var_type ( src ) + " '). " ;
OPCODE_BREAK ;
}
* dst = ret ;
# endif
ip + = 5 ;
}
DISPATCH_OPCODE ;
OPCODE ( OPCODE_GET_INDEXED_VALIDATED ) {
CHECK_SPACE ( 4 ) ;
GET_INSTRUCTION_ARG ( src , 0 ) ;
GET_INSTRUCTION_ARG ( index , 1 ) ;
GET_INSTRUCTION_ARG ( dst , 2 ) ;
int index_getter = _code_ptr [ ip + 4 ] ;
GD_ERR_BREAK ( index_getter < 0 | | index_getter > = _indexed_getters_count ) ;
const Variant : : ValidatedIndexedGetter getter = _indexed_getters_ptr [ index_getter ] ;
int64_t int_index = * VariantInternal : : get_int ( index ) ;
bool oob ;
getter ( src , int_index , dst , oob ) ;
# ifdef DEBUG_ENABLED
if ( oob ) {
String v = index - > operator String ( ) ;
if ( v ! = " " ) {
v = " ' " + v + " ' " ;
} else {
v = " of type ' " + _get_var_type ( index ) + " ' " ;
}
err_text = " Out of bounds get index " + v + " (on base: ' " + _get_var_type ( src ) + " ') " ;
OPCODE_BREAK ;
}
# endif
ip + = 5 ;
}
DISPATCH_OPCODE ;
2020-11-12 14:14:48 +01:00
OPCODE ( OPCODE_SET_NAMED ) {
CHECK_SPACE ( 3 ) ;
2020-11-13 14:31:14 +01:00
GET_INSTRUCTION_ARG ( dst , 0 ) ;
GET_INSTRUCTION_ARG ( value , 1 ) ;
2020-11-12 14:14:48 +01:00
2020-11-13 14:31:14 +01:00
int indexname = _code_ptr [ ip + 3 ] ;
2020-11-12 14:14:48 +01:00
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 ;
2020-11-16 16:59:53 +01:00
OPCODE ( OPCODE_SET_NAMED_VALIDATED ) {
CHECK_SPACE ( 3 ) ;
GET_INSTRUCTION_ARG ( dst , 0 ) ;
GET_INSTRUCTION_ARG ( value , 1 ) ;
int index_setter = _code_ptr [ ip + 3 ] ;
GD_ERR_BREAK ( index_setter < 0 | | index_setter > = _setters_count ) ;
const Variant : : ValidatedSetter setter = _setters_ptr [ index_setter ] ;
setter ( dst , value ) ;
ip + = 4 ;
}
DISPATCH_OPCODE ;
2020-11-12 14:14:48 +01:00
OPCODE ( OPCODE_GET_NAMED ) {
CHECK_SPACE ( 4 ) ;
2020-11-13 14:31:14 +01:00
GET_INSTRUCTION_ARG ( src , 0 ) ;
GET_INSTRUCTION_ARG ( dst , 1 ) ;
2020-11-12 14:14:48 +01:00
2020-11-13 14:31:14 +01:00
int indexname = _code_ptr [ ip + 3 ] ;
2020-11-12 14:14:48 +01:00
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 ;
2020-11-16 16:59:53 +01:00
OPCODE ( OPCODE_GET_NAMED_VALIDATED ) {
CHECK_SPACE ( 3 ) ;
GET_INSTRUCTION_ARG ( src , 0 ) ;
GET_INSTRUCTION_ARG ( dst , 1 ) ;
int index_getter = _code_ptr [ ip + 3 ] ;
GD_ERR_BREAK ( index_getter < 0 | | index_getter > = _getters_count ) ;
const Variant : : ValidatedGetter getter = _getters_ptr [ index_getter ] ;
getter ( src , dst ) ;
ip + = 4 ;
}
DISPATCH_OPCODE ;
2020-11-12 14:14:48 +01:00
OPCODE ( OPCODE_SET_MEMBER ) {
CHECK_SPACE ( 3 ) ;
2020-11-13 14:31:14 +01:00
GET_INSTRUCTION_ARG ( src , 0 ) ;
int indexname = _code_ptr [ ip + 2 ] ;
2020-11-12 14:14:48 +01:00
GD_ERR_BREAK ( indexname < 0 | | indexname > = _global_names_count ) ;
const StringName * index = & _global_names_ptr [ indexname ] ;
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 ) ;
2020-11-13 14:31:14 +01:00
GET_INSTRUCTION_ARG ( dst , 0 ) ;
int indexname = _code_ptr [ ip + 2 ] ;
2020-11-12 14:14:48 +01:00
GD_ERR_BREAK ( indexname < 0 | | indexname > = _global_names_count ) ;
const StringName * index = & _global_names_ptr [ indexname ] ;
# 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 ) ;
2020-11-13 14:31:14 +01:00
GET_INSTRUCTION_ARG ( dst , 0 ) ;
GET_INSTRUCTION_ARG ( src , 1 ) ;
2020-11-12 14:14:48 +01:00
* dst = * src ;
ip + = 3 ;
}
DISPATCH_OPCODE ;
OPCODE ( OPCODE_ASSIGN_TRUE ) {
CHECK_SPACE ( 2 ) ;
2020-11-13 14:31:14 +01:00
GET_INSTRUCTION_ARG ( dst , 0 ) ;
2020-11-12 14:14:48 +01:00
* dst = true ;
ip + = 2 ;
}
DISPATCH_OPCODE ;
OPCODE ( OPCODE_ASSIGN_FALSE ) {
CHECK_SPACE ( 2 ) ;
2020-11-13 14:31:14 +01:00
GET_INSTRUCTION_ARG ( dst , 0 ) ;
2020-11-12 14:14:48 +01:00
* dst = false ;
ip + = 2 ;
}
DISPATCH_OPCODE ;
OPCODE ( OPCODE_ASSIGN_TYPED_BUILTIN ) {
CHECK_SPACE ( 4 ) ;
2020-11-13 14:31:14 +01:00
GET_INSTRUCTION_ARG ( dst , 0 ) ;
GET_INSTRUCTION_ARG ( src , 1 ) ;
2020-11-12 14:14:48 +01:00
2020-11-13 14:31:14 +01:00
Variant : : Type var_type = ( Variant : : Type ) _code_ptr [ ip + 3 ] ;
2020-11-12 14:14:48 +01:00
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
Callable : : CallError ce ;
Variant : : construct ( var_type , * dst , 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 ) ;
2020-11-13 14:31:14 +01:00
GET_INSTRUCTION_ARG ( dst , 0 ) ;
GET_INSTRUCTION_ARG ( src , 1 ) ;
2020-11-12 14:14:48 +01:00
# ifdef DEBUG_ENABLED
2020-11-13 14:31:14 +01:00
GET_INSTRUCTION_ARG ( type , 2 ) ;
2020-11-12 14:14:48 +01:00
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 ) ;
2020-11-13 14:31:14 +01:00
GET_INSTRUCTION_ARG ( dst , 0 ) ;
GET_INSTRUCTION_ARG ( src , 1 ) ;
2020-11-12 14:14:48 +01:00
# ifdef DEBUG_ENABLED
2020-11-13 14:31:14 +01:00
GET_INSTRUCTION_ARG ( type , 2 ) ;
2020-11-12 14:14:48 +01:00
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 ) ;
2020-11-13 14:31:14 +01:00
GET_INSTRUCTION_ARG ( src , 0 ) ;
GET_INSTRUCTION_ARG ( dst , 1 ) ;
Variant : : Type to_type = ( Variant : : Type ) _code_ptr [ ip + 3 ] ;
2020-11-12 14:14:48 +01:00
GD_ERR_BREAK ( to_type < 0 | | to_type > = Variant : : VARIANT_MAX ) ;
Callable : : CallError err ;
Variant : : construct ( to_type , * dst , ( const Variant * * ) & src , 1 , err ) ;
# ifdef DEBUG_ENABLED
if ( err . error ! = Callable : : 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 ) ;
2020-11-13 14:31:14 +01:00
GET_INSTRUCTION_ARG ( src , 0 ) ;
GET_INSTRUCTION_ARG ( dst , 1 ) ;
GET_INSTRUCTION_ARG ( to_type , 2 ) ;
2020-11-12 14:14:48 +01:00
GDScriptNativeClass * nc = Object : : cast_to < GDScriptNativeClass > ( to_type - > operator Object * ( ) ) ;
GD_ERR_BREAK ( ! nc ) ;
# ifdef DEBUG_ENABLED
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 ) ;
2020-11-13 14:31:14 +01:00
GET_INSTRUCTION_ARG ( src , 0 ) ;
GET_INSTRUCTION_ARG ( dst , 1 ) ;
GET_INSTRUCTION_ARG ( to_type , 2 ) ;
2020-11-12 14:14:48 +01:00
Script * base_type = Object : : cast_to < Script > ( to_type - > operator Object * ( ) ) ;
GD_ERR_BREAK ( ! base_type ) ;
# ifdef DEBUG_ENABLED
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 ) {
2020-11-13 14:31:14 +01:00
CHECK_SPACE ( 2 + instr_arg_count ) ;
ip + = instr_arg_count ;
int argc = _code_ptr [ ip + 1 ] ;
Variant : : Type t = Variant : : Type ( _code_ptr [ ip + 2 ] ) ;
Variant * * argptrs = instruction_args ;
GET_INSTRUCTION_ARG ( dst , argc ) ;
2020-11-12 14:14:48 +01:00
Callable : : CallError err ;
Variant : : construct ( t , * dst , ( const Variant * * ) argptrs , argc , err ) ;
# ifdef DEBUG_ENABLED
if ( err . error ! = Callable : : CallError : : CALL_OK ) {
err_text = _get_call_error ( err , " ' " + Variant : : get_type_name ( t ) + " ' constructor " , ( const Variant * * ) argptrs ) ;
OPCODE_BREAK ;
}
# endif
2020-11-13 14:31:14 +01:00
ip + = 3 ;
2020-11-12 14:14:48 +01:00
}
DISPATCH_OPCODE ;
OPCODE ( OPCODE_CONSTRUCT_ARRAY ) {
2020-11-13 14:31:14 +01:00
CHECK_SPACE ( 1 + instr_arg_count ) ;
ip + = instr_arg_count ;
2020-11-12 14:14:48 +01:00
int argc = _code_ptr [ ip + 1 ] ;
2020-11-13 14:31:14 +01:00
Array array ;
2020-11-12 14:14:48 +01:00
array . resize ( argc ) ;
for ( int i = 0 ; i < argc ; i + + ) {
2020-11-13 14:31:14 +01:00
array [ i ] = * ( instruction_args [ i ] ) ;
2020-11-12 14:14:48 +01:00
}
2020-11-13 14:31:14 +01:00
GET_INSTRUCTION_ARG ( dst , argc ) ;
2020-11-12 14:14:48 +01:00
* dst = array ;
2020-11-13 14:31:14 +01:00
ip + = 2 ;
2020-11-12 14:14:48 +01:00
}
DISPATCH_OPCODE ;
OPCODE ( OPCODE_CONSTRUCT_DICTIONARY ) {
2020-11-13 14:31:14 +01:00
CHECK_SPACE ( 2 + instr_arg_count ) ;
2020-11-12 14:14:48 +01:00
2020-11-13 14:31:14 +01:00
ip + = instr_arg_count ;
int argc = _code_ptr [ ip + 1 ] ;
Dictionary dict ;
2020-11-12 14:14:48 +01:00
for ( int i = 0 ; i < argc ; i + + ) {
2020-11-13 14:31:14 +01:00
GET_INSTRUCTION_ARG ( k , i * 2 + 0 ) ;
GET_INSTRUCTION_ARG ( v , i * 2 + 1 ) ;
2020-11-12 14:14:48 +01:00
dict [ * k ] = * v ;
}
2020-11-13 14:31:14 +01:00
GET_INSTRUCTION_ARG ( dst , argc * 2 ) ;
2020-11-12 14:14:48 +01:00
* dst = dict ;
2020-11-13 14:31:14 +01:00
ip + = 2 ;
2020-11-12 14:14:48 +01:00
}
DISPATCH_OPCODE ;
OPCODE ( OPCODE_CALL_ASYNC )
OPCODE ( OPCODE_CALL_RETURN )
OPCODE ( OPCODE_CALL ) {
2020-11-13 14:31:14 +01:00
CHECK_SPACE ( 3 + instr_arg_count ) ;
bool call_ret = ( _code_ptr [ ip ] & INSTR_MASK ) ! = OPCODE_CALL ;
2020-11-12 14:14:48 +01:00
# ifdef DEBUG_ENABLED
2020-11-13 14:31:14 +01:00
bool call_async = ( _code_ptr [ ip ] & INSTR_MASK ) = = OPCODE_CALL_ASYNC ;
2020-11-12 14:14:48 +01:00
# endif
2020-11-13 14:31:14 +01:00
ip + = instr_arg_count ;
2020-11-12 14:14:48 +01:00
2020-11-13 14:31:14 +01:00
int argc = _code_ptr [ ip + 1 ] ;
2020-11-12 14:14:48 +01:00
GD_ERR_BREAK ( argc < 0 ) ;
2020-11-13 14:31:14 +01:00
int methodname_idx = _code_ptr [ ip + 2 ] ;
GD_ERR_BREAK ( methodname_idx < 0 | | methodname_idx > = _global_names_count ) ;
const StringName * methodname = & _global_names_ptr [ methodname_idx ] ;
GET_INSTRUCTION_ARG ( base , argc ) ;
Variant * * argptrs = instruction_args ;
2020-11-12 14:14:48 +01:00
# ifdef DEBUG_ENABLED
uint64_t call_time = 0 ;
if ( GDScriptLanguage : : get_singleton ( ) - > profiling ) {
call_time = OS : : get_singleton ( ) - > get_ticks_usec ( ) ;
}
# endif
Callable : : CallError err ;
if ( call_ret ) {
2020-11-13 14:31:14 +01:00
GET_INSTRUCTION_ARG ( ret , argc + 1 ) ;
2020-11-12 14:14:48 +01:00
base - > call ( * methodname , ( const Variant * * ) argptrs , argc , * ret , err ) ;
# ifdef DEBUG_ENABLED
if ( ! call_async & & ret - > get_type ( ) = = Variant : : OBJECT ) {
// Check if getting a function state without await.
bool was_freed = false ;
Object * obj = ret - > get_validated_object_with_check ( was_freed ) ;
if ( was_freed ) {
err_text = " Got a freed object as a result of the call. " ;
OPCODE_BREAK ;
}
if ( obj & & obj - > is_class_ptr ( GDScriptFunctionState : : get_class_ptr_static ( ) ) ) {
err_text = R " (Trying to call an async function without " await " .) " ;
OPCODE_BREAK ;
}
}
# endif
} else {
Variant ret ;
base - > call ( * methodname , ( const Variant * * ) argptrs , argc , ret , err ) ;
}
# ifdef DEBUG_ENABLED
if ( GDScriptLanguage : : get_singleton ( ) - > profiling ) {
function_call_time + = OS : : get_singleton ( ) - > get_ticks_usec ( ) - call_time ;
}
if ( err . error ! = Callable : : 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 = = Callable : : CallError : : CALL_ERROR_INVALID_ARGUMENT ) {
err . argument + = 1 ;
}
}
} else if ( methodstr = = " free " ) {
if ( err . error = = Callable : : 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 = = Callable : : 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
2020-11-13 14:31:14 +01:00
ip + = 3 ;
2020-11-12 14:14:48 +01:00
}
DISPATCH_OPCODE ;
2020-11-17 14:44:52 +01:00
OPCODE ( OPCODE_CALL_METHOD_BIND )
OPCODE ( OPCODE_CALL_METHOD_BIND_RET ) {
CHECK_SPACE ( 3 + instr_arg_count ) ;
bool call_ret = ( _code_ptr [ ip ] & INSTR_MASK ) = = OPCODE_CALL_METHOD_BIND_RET ;
ip + = instr_arg_count ;
int argc = _code_ptr [ ip + 1 ] ;
GD_ERR_BREAK ( argc < 0 ) ;
GD_ERR_BREAK ( _code_ptr [ ip + 2 ] < 0 | | _code_ptr [ ip + 2 ] > = _methods_count ) ;
MethodBind * method = _methods_ptr [ _code_ptr [ ip + 2 ] ] ;
GET_INSTRUCTION_ARG ( base , argc ) ;
# ifdef DEBUG_ENABLED
bool freed = false ;
Object * base_obj = base - > get_validated_object_with_check ( freed ) ;
if ( freed ) {
err_text = " Trying to call a function on a previously freed instance. " ;
OPCODE_BREAK ;
} else if ( ! base_obj ) {
err_text = " Trying to call a function on a null value. " ;
OPCODE_BREAK ;
}
# else
Object * base_obj = base - > operator Object * ( ) ;
# endif
Variant * * argptrs = instruction_args ;
# ifdef DEBUG_ENABLED
uint64_t call_time = 0 ;
if ( GDScriptLanguage : : get_singleton ( ) - > profiling ) {
call_time = OS : : get_singleton ( ) - > get_ticks_usec ( ) ;
}
# endif
Callable : : CallError err ;
if ( call_ret ) {
GET_INSTRUCTION_ARG ( ret , argc + 1 ) ;
* ret = method - > call ( base_obj , ( const Variant * * ) argptrs , argc , err ) ;
} else {
method - > call ( base_obj , ( const Variant * * ) argptrs , argc , err ) ;
}
# ifdef DEBUG_ENABLED
if ( GDScriptLanguage : : get_singleton ( ) - > profiling ) {
function_call_time + = OS : : get_singleton ( ) - > get_ticks_usec ( ) - call_time ;
}
if ( err . error ! = Callable : : CallError : : CALL_OK ) {
String methodstr = method - > get_name ( ) ;
String basestr = _get_var_type ( base ) ;
if ( methodstr = = " call " ) {
if ( argc > = 1 ) {
methodstr = String ( * argptrs [ 0 ] ) + " (via call) " ;
if ( err . error = = Callable : : CallError : : CALL_ERROR_INVALID_ARGUMENT ) {
err . argument + = 1 ;
}
}
} else if ( methodstr = = " free " ) {
if ( err . error = = Callable : : 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 ;
}
}
}
err_text = _get_call_error ( err , " function ' " + methodstr + " ' in base ' " + basestr + " ' " , ( const Variant * * ) argptrs ) ;
OPCODE_BREAK ;
}
# endif
ip + = 3 ;
}
DISPATCH_OPCODE ;
# ifdef DEBUG_ENABLED
# define OPCODE_CALL_PTR(m_type) \
OPCODE ( OPCODE_CALL_PTRCALL_ # # m_type ) { \
CHECK_SPACE ( 3 + instr_arg_count ) ; \
ip + = instr_arg_count ; \
int argc = _code_ptr [ ip + 1 ] ; \
GD_ERR_BREAK ( argc < 0 ) ; \
GET_INSTRUCTION_ARG ( base , argc ) ; \
GD_ERR_BREAK ( _code_ptr [ ip + 2 ] < 0 | | _code_ptr [ ip + 2 ] > = _methods_count ) ; \
MethodBind * method = _methods_ptr [ _code_ptr [ ip + 2 ] ] ; \
bool freed = false ; \
Object * base_obj = base - > get_validated_object_with_check ( freed ) ; \
if ( freed ) { \
err_text = " Trying to call a function on a previously freed instance. " ; \
OPCODE_BREAK ; \
} else if ( ! base_obj ) { \
err_text = " Trying to call a function on a null value. " ; \
OPCODE_BREAK ; \
} \
const void * * argptrs = call_args_ptr ; \
for ( int i = 0 ; i < argc ; i + + ) { \
GET_INSTRUCTION_ARG ( v , i ) ; \
argptrs [ i ] = VariantInternal : : get_opaque_pointer ( ( const Variant * ) v ) ; \
} \
uint64_t call_time = 0 ; \
if ( GDScriptLanguage : : get_singleton ( ) - > profiling ) { \
call_time = OS : : get_singleton ( ) - > get_ticks_usec ( ) ; \
} \
GET_INSTRUCTION_ARG ( ret , argc + 1 ) ; \
VariantInternal : : initialize ( ret , Variant : : m_type ) ; \
void * ret_opaque = VariantInternal : : OP_GET_ # # m_type ( ret ) ; \
method - > ptrcall ( base_obj , argptrs , ret_opaque ) ; \
if ( GDScriptLanguage : : get_singleton ( ) - > profiling ) { \
function_call_time + = OS : : get_singleton ( ) - > get_ticks_usec ( ) - call_time ; \
} \
ip + = 3 ; \
} \
DISPATCH_OPCODE
# else
# define OPCODE_CALL_PTR(m_type) \
OPCODE ( OPCODE_CALL_PTRCALL_ # # m_type ) { \
CHECK_SPACE ( 3 + instr_arg_count ) ; \
int argc = _code_ptr [ ip + 1 ] ; \
GET_INSTRUCTION_ARG ( base , argc ) ; \
MethodBind * method = _methods_ptr [ _code_ptr [ ip + 2 ] ] ; \
Object * base_obj = * VariantInternal : : get_object ( base ) ; \
const void * * argptrs = call_args_ptr ; \
for ( int i = 0 ; i < argc ; i + + ) { \
GET_INSTRUCTION_ARG ( v , i ) ; \
argptrs [ i ] = VariantInternal : : get_opaque_pointer ( ( const Variant * ) v ) ; \
} \
GET_INSTRUCTION_ARG ( ret , argc + 1 ) ; \
VariantInternal : : initialize ( ret , Variant : : m_type ) ; \
void * ret_opaque = VariantInternal : : OP_GET_ # # m_type ( ret ) ; \
method - > ptrcall ( base_obj , argptrs , ret_opaque ) ; \
ip + = 3 ; \
} \
DISPATCH_OPCODE
# endif
OPCODE_CALL_PTR ( BOOL ) ;
OPCODE_CALL_PTR ( INT ) ;
OPCODE_CALL_PTR ( FLOAT ) ;
OPCODE_CALL_PTR ( STRING ) ;
OPCODE_CALL_PTR ( VECTOR2 ) ;
OPCODE_CALL_PTR ( VECTOR2I ) ;
OPCODE_CALL_PTR ( RECT2 ) ;
OPCODE_CALL_PTR ( RECT2I ) ;
OPCODE_CALL_PTR ( VECTOR3 ) ;
OPCODE_CALL_PTR ( VECTOR3I ) ;
OPCODE_CALL_PTR ( TRANSFORM2D ) ;
OPCODE_CALL_PTR ( PLANE ) ;
OPCODE_CALL_PTR ( QUAT ) ;
OPCODE_CALL_PTR ( AABB ) ;
OPCODE_CALL_PTR ( BASIS ) ;
OPCODE_CALL_PTR ( TRANSFORM ) ;
OPCODE_CALL_PTR ( COLOR ) ;
OPCODE_CALL_PTR ( STRING_NAME ) ;
OPCODE_CALL_PTR ( NODE_PATH ) ;
OPCODE_CALL_PTR ( RID ) ;
OPCODE_CALL_PTR ( CALLABLE ) ;
OPCODE_CALL_PTR ( SIGNAL ) ;
OPCODE_CALL_PTR ( DICTIONARY ) ;
OPCODE_CALL_PTR ( ARRAY ) ;
OPCODE_CALL_PTR ( PACKED_BYTE_ARRAY ) ;
OPCODE_CALL_PTR ( PACKED_INT32_ARRAY ) ;
OPCODE_CALL_PTR ( PACKED_INT64_ARRAY ) ;
OPCODE_CALL_PTR ( PACKED_FLOAT32_ARRAY ) ;
OPCODE_CALL_PTR ( PACKED_FLOAT64_ARRAY ) ;
OPCODE_CALL_PTR ( PACKED_STRING_ARRAY ) ;
OPCODE_CALL_PTR ( PACKED_VECTOR2_ARRAY ) ;
OPCODE_CALL_PTR ( PACKED_VECTOR3_ARRAY ) ;
OPCODE_CALL_PTR ( PACKED_COLOR_ARRAY ) ;
OPCODE ( OPCODE_CALL_PTRCALL_OBJECT ) {
CHECK_SPACE ( 3 + instr_arg_count ) ;
ip + = instr_arg_count ;
int argc = _code_ptr [ ip + 1 ] ;
GD_ERR_BREAK ( argc < 0 ) ;
GD_ERR_BREAK ( _code_ptr [ ip + 2 ] < 0 | | _code_ptr [ ip + 2 ] > = _methods_count ) ;
MethodBind * method = _methods_ptr [ _code_ptr [ ip + 2 ] ] ;
GET_INSTRUCTION_ARG ( base , argc ) ;
# ifdef DEBUG_ENABLED
bool freed = false ;
Object * base_obj = base - > get_validated_object_with_check ( freed ) ;
if ( freed ) {
err_text = " Trying to call a function on a previously freed instance. " ;
OPCODE_BREAK ;
} else if ( ! base_obj ) {
err_text = " Trying to call a function on a null value. " ;
OPCODE_BREAK ;
}
# else
Object * base_obj = * VariantInternal : : get_object ( base ) ;
# endif
const void * * argptrs = call_args_ptr ;
for ( int i = 0 ; i < argc ; i + + ) {
GET_INSTRUCTION_ARG ( v , i ) ;
argptrs [ i ] = VariantInternal : : get_opaque_pointer ( ( const Variant * ) v ) ;
}
# ifdef DEBUG_ENABLED
uint64_t call_time = 0 ;
if ( GDScriptLanguage : : get_singleton ( ) - > profiling ) {
call_time = OS : : get_singleton ( ) - > get_ticks_usec ( ) ;
}
# endif
GET_INSTRUCTION_ARG ( ret , argc + 1 ) ;
VariantInternal : : initialize ( ret , Variant : : OBJECT ) ;
Object * * ret_opaque = VariantInternal : : get_object ( ret ) ;
method - > ptrcall ( base_obj , argptrs , ret_opaque ) ;
VariantInternal : : set_object ( ret , * ret_opaque ) ;
# ifdef DEBUG_ENABLED
if ( GDScriptLanguage : : get_singleton ( ) - > profiling ) {
function_call_time + = OS : : get_singleton ( ) - > get_ticks_usec ( ) - call_time ;
}
# endif
ip + = 3 ;
}
DISPATCH_OPCODE ;
OPCODE ( OPCODE_CALL_PTRCALL_NO_RETURN ) {
CHECK_SPACE ( 3 + instr_arg_count ) ;
ip + = instr_arg_count ;
int argc = _code_ptr [ ip + 1 ] ;
GD_ERR_BREAK ( argc < 0 ) ;
GD_ERR_BREAK ( _code_ptr [ ip + 2 ] < 0 | | _code_ptr [ ip + 2 ] > = _methods_count ) ;
MethodBind * method = _methods_ptr [ _code_ptr [ ip + 2 ] ] ;
GET_INSTRUCTION_ARG ( base , argc ) ;
# ifdef DEBUG_ENABLED
bool freed = false ;
Object * base_obj = base - > get_validated_object_with_check ( freed ) ;
if ( freed ) {
err_text = " Trying to call a function on a previously freed instance. " ;
OPCODE_BREAK ;
} else if ( ! base_obj ) {
err_text = " Trying to call a function on a null value. " ;
OPCODE_BREAK ;
}
# else
Object * base_obj = * VariantInternal : : get_object ( base ) ;
# endif
const void * * argptrs = call_args_ptr ;
for ( int i = 0 ; i < argc ; i + + ) {
GET_INSTRUCTION_ARG ( v , i ) ;
argptrs [ i ] = VariantInternal : : get_opaque_pointer ( ( const Variant * ) v ) ;
}
# ifdef DEBUG_ENABLED
uint64_t call_time = 0 ;
if ( GDScriptLanguage : : get_singleton ( ) - > profiling ) {
call_time = OS : : get_singleton ( ) - > get_ticks_usec ( ) ;
}
# endif
GET_INSTRUCTION_ARG ( ret , argc + 1 ) ;
VariantInternal : : initialize ( ret , Variant : : NIL ) ;
method - > ptrcall ( base_obj , argptrs , nullptr ) ;
# ifdef DEBUG_ENABLED
if ( GDScriptLanguage : : get_singleton ( ) - > profiling ) {
function_call_time + = OS : : get_singleton ( ) - > get_ticks_usec ( ) - call_time ;
}
# endif
ip + = 3 ;
}
DISPATCH_OPCODE ;
2020-11-12 14:14:48 +01:00
OPCODE ( OPCODE_CALL_BUILT_IN ) {
2020-11-13 14:31:14 +01:00
CHECK_SPACE ( 3 + instr_arg_count ) ;
2020-11-12 14:14:48 +01:00
2020-11-13 14:31:14 +01:00
ip + = instr_arg_count ;
2020-11-12 14:14:48 +01:00
2020-11-13 14:31:14 +01:00
int argc = _code_ptr [ ip + 1 ] ;
GD_ERR_BREAK ( argc < 0 ) ;
2020-11-12 14:14:48 +01:00
2020-11-13 14:31:14 +01:00
GDScriptFunctions : : Function func = GDScriptFunctions : : Function ( _code_ptr [ ip + 2 ] ) ;
Variant * * argptrs = instruction_args ;
2020-11-12 14:14:48 +01:00
2020-11-13 14:31:14 +01:00
GET_INSTRUCTION_ARG ( dst , argc ) ;
2020-11-12 14:14:48 +01:00
Callable : : CallError err ;
GDScriptFunctions : : call ( func , ( const Variant * * ) argptrs , argc , * dst , err ) ;
# ifdef DEBUG_ENABLED
if ( err . error ! = Callable : : 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
2020-11-13 14:31:14 +01:00
ip + = 3 ;
2020-11-12 14:14:48 +01:00
}
DISPATCH_OPCODE ;
OPCODE ( OPCODE_CALL_SELF_BASE ) {
2020-11-13 14:31:14 +01:00
CHECK_SPACE ( 3 + instr_arg_count ) ;
ip + = instr_arg_count ;
2020-11-12 14:14:48 +01:00
2020-11-13 14:31:14 +01:00
int self_fun = _code_ptr [ ip + 1 ] ;
2020-11-12 14:14:48 +01:00
# 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 ] ;
2020-11-13 14:31:14 +01:00
GD_ERR_BREAK ( argc < 0 ) ;
2020-11-12 14:14:48 +01:00
2020-11-13 14:31:14 +01:00
Variant * * argptrs = instruction_args ;
2020-11-12 14:14:48 +01:00
2020-11-13 14:31:14 +01:00
GET_INSTRUCTION_ARG ( dst , argc ) ;
2020-11-12 14:14:48 +01:00
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 ;
}
}
Callable : : 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 = Callable : : CallError : : CALL_ERROR_INVALID_METHOD ;
} else {
* dst = mb - > call ( p_instance - > owner , ( const Variant * * ) argptrs , argc , err ) ;
}
} else {
err . error = Callable : : CallError : : CALL_OK ;
}
} else {
if ( * methodname ! = GDScriptLanguage : : get_singleton ( ) - > strings . _init ) {
err . error = Callable : : CallError : : CALL_ERROR_INVALID_METHOD ;
} else {
err . error = Callable : : CallError : : CALL_OK ;
}
}
if ( err . error ! = Callable : : CallError : : CALL_OK ) {
String methodstr = * methodname ;
err_text = _get_call_error ( err , " function ' " + methodstr + " ' " , ( const Variant * * ) argptrs ) ;
OPCODE_BREAK ;
}
2020-11-13 14:31:14 +01:00
ip + = 3 ;
2020-11-12 14:14:48 +01:00
}
DISPATCH_OPCODE ;
OPCODE ( OPCODE_AWAIT ) {
CHECK_SPACE ( 2 ) ;
2020-11-13 14:31:14 +01:00
// Do the oneshot connect.
GET_INSTRUCTION_ARG ( argobj , 0 ) ;
2020-11-12 14:14:48 +01:00
Signal sig ;
bool is_signal = true ;
{
Variant result = * argobj ;
if ( argobj - > get_type ( ) = = Variant : : OBJECT ) {
bool was_freed = false ;
Object * obj = argobj - > get_validated_object_with_check ( was_freed ) ;
if ( was_freed ) {
err_text = " Trying to await on a freed object. " ;
OPCODE_BREAK ;
}
// Is this even possible to be null at this point?
if ( obj ) {
if ( obj - > is_class_ptr ( GDScriptFunctionState : : get_class_ptr_static ( ) ) ) {
static StringName completed = _scs_create ( " completed " ) ;
result = Signal ( obj , completed ) ;
}
}
}
if ( result . get_type ( ) ! = Variant : : SIGNAL ) {
ip + = 4 ; // Skip OPCODE_AWAIT_RESUME and its data.
// The stack pointer should be the same, so we don't need to set a return value.
is_signal = false ;
} else {
sig = result ;
}
}
if ( is_signal ) {
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 + 2 ;
gdfs - > state . line = line ;
gdfs - > state . script = _script ;
{
MutexLock lock ( GDScriptLanguage : : get_singleton ( ) - > 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 ;
}
}
# ifdef DEBUG_ENABLED
gdfs - > state . function_name = name ;
gdfs - > state . script_path = _script - > get_path ( ) ;
# endif
gdfs - > state . defarg = defarg ;
gdfs - > function = this ;
retvalue = gdfs ;
Error err = sig . connect ( Callable ( gdfs . ptr ( ) , " _signal_callback " ) , varray ( gdfs ) , Object : : CONNECT_ONESHOT ) ;
if ( err ! = OK ) {
err_text = " Error connecting to signal: " + sig . get_name ( ) + " during await. " ;
OPCODE_BREAK ;
}
# ifdef DEBUG_ENABLED
exit_ok = true ;
awaited = true ;
# endif
OPCODE_BREAK ;
}
}
DISPATCH_OPCODE ; // Needed for synchronous calls (when result is immediately available).
OPCODE ( OPCODE_AWAIT_RESUME ) {
CHECK_SPACE ( 2 ) ;
# ifdef DEBUG_ENABLED
if ( ! p_state ) {
err_text = ( " Invalid Resume (bug?) " ) ;
OPCODE_BREAK ;
}
# endif
2020-11-13 14:31:14 +01:00
GET_INSTRUCTION_ARG ( result , 0 ) ;
2020-11-12 14:14:48 +01:00
* 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 ) ;
2020-11-13 14:31:14 +01:00
GET_INSTRUCTION_ARG ( test , 0 ) ;
2020-11-12 14:14:48 +01:00
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 ) ;
2020-11-13 14:31:14 +01:00
GET_INSTRUCTION_ARG ( test , 0 ) ;
2020-11-12 14:14:48 +01:00
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 ) ;
2020-11-13 14:31:14 +01:00
GET_INSTRUCTION_ARG ( r , 0 ) ;
2020-11-12 14:14:48 +01:00
retvalue = * r ;
# ifdef DEBUG_ENABLED
exit_ok = true ;
# endif
OPCODE_BREAK ;
}
OPCODE ( OPCODE_ITERATE_BEGIN ) {
2020-11-13 14:31:14 +01:00
CHECK_SPACE ( 8 ) ; // Space for this and a regular iterate.
2020-11-12 14:14:48 +01:00
2020-11-13 14:31:14 +01:00
GET_INSTRUCTION_ARG ( counter , 0 ) ;
GET_INSTRUCTION_ARG ( container , 1 ) ;
2020-11-12 14:14:48 +01:00
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
2020-11-13 14:31:14 +01:00
int jumpto = _code_ptr [ ip + 4 ] ;
2020-11-12 14:14:48 +01:00
GD_ERR_BREAK ( jumpto < 0 | | jumpto > _code_size ) ;
ip = jumpto ;
} else {
2020-11-13 14:31:14 +01:00
GET_INSTRUCTION_ARG ( iterator , 2 ) ;
2020-11-12 14:14:48 +01:00
* 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
2020-11-13 14:31:14 +01:00
ip + = 5 ; // Skip regular iterate which is always next.
2020-11-12 14:14:48 +01:00
}
}
DISPATCH_OPCODE ;
OPCODE ( OPCODE_ITERATE ) {
CHECK_SPACE ( 4 ) ;
2020-11-13 14:31:14 +01:00
GET_INSTRUCTION_ARG ( counter , 0 ) ;
GET_INSTRUCTION_ARG ( container , 1 ) ;
2020-11-12 14:14:48 +01:00
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
2020-11-13 14:31:14 +01:00
int jumpto = _code_ptr [ ip + 4 ] ;
2020-11-12 14:14:48 +01:00
GD_ERR_BREAK ( jumpto < 0 | | jumpto > _code_size ) ;
ip = jumpto ;
} else {
2020-11-13 14:31:14 +01:00
GET_INSTRUCTION_ARG ( iterator , 2 ) ;
2020-11-12 14:14:48 +01:00
* 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
2020-11-13 14:31:14 +01:00
GET_INSTRUCTION_ARG ( test , 0 ) ;
2020-11-12 14:14:48 +01:00
bool result = test - > booleanize ( ) ;
if ( ! result ) {
String message_str ;
if ( _code_ptr [ ip + 2 ] ! = 0 ) {
2020-11-13 14:31:14 +01:00
GET_INSTRUCTION_ARG ( message , 1 ) ;
2020-11-12 14:14:48 +01:00
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 ( EngineDebugger : : is_active ( ) ) {
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 ( EngineDebugger : : is_active ( ) ) {
// line
bool do_break = false ;
if ( EngineDebugger : : get_script_debugger ( ) - > get_lines_left ( ) > 0 ) {
if ( EngineDebugger : : get_script_debugger ( ) - > get_depth ( ) < = 0 ) {
EngineDebugger : : get_script_debugger ( ) - > set_lines_left ( EngineDebugger : : get_script_debugger ( ) - > get_lines_left ( ) - 1 ) ;
}
if ( EngineDebugger : : get_script_debugger ( ) - > get_lines_left ( ) < = 0 ) {
do_break = true ;
}
}
if ( EngineDebugger : : get_script_debugger ( ) - > is_breakpoint ( line , source ) ) {
do_break = true ;
}
if ( do_break ) {
GDScriptLanguage : : get_singleton ( ) - > debug_break ( " Breakpoint " , true ) ;
}
EngineDebugger : : 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 & & ObjectDB : : get_instance ( p_instance - > owner_id ) ! = nullptr & & 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 & & ObjectDB : : get_instance ( p_instance - > owner_id ) ! = nullptr & & 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 ) + " (report please). " ;
}
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 await
// Will be true if never awaited 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 | | awaited ) {
if ( EngineDebugger : : is_active ( ) ) {
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 ;
}