2014-02-10 02:10:30 +01:00
/*************************************************************************/
2017-11-16 18:38:18 +01:00
/* gdscript.cpp */
2014-02-10 02:10:30 +01:00
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
2017-08-27 14:16:55 +02:00
/* https://godotengine.org */
2014-02-10 02:10:30 +01:00
/*************************************************************************/
2019-01-01 12:53:14 +01:00
/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
2014-02-10 02:10:30 +01:00
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
2018-01-05 00:50:27 +01:00
2017-11-16 18:38:18 +01:00
# include "gdscript.h"
2017-08-27 21:07:15 +02:00
2019-04-10 07:07:40 +02:00
# include "core/core_string_names.h"
2018-09-11 18:13:45 +02:00
# include "core/engine.h"
# include "core/global_constants.h"
# include "core/io/file_access_encrypted.h"
# include "core/os/file_access.h"
# include "core/os/os.h"
# include "core/project_settings.h"
2017-11-16 18:38:18 +01:00
# include "gdscript_compiler.h"
2014-02-10 02:10:30 +01:00
2014-09-15 16:33:30 +02:00
///////////////////////////
2017-11-16 18:38:18 +01:00
GDScriptNativeClass : : GDScriptNativeClass ( const StringName & p_name ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
name = p_name ;
2014-02-10 02:10:30 +01:00
}
2017-11-16 18:38:18 +01:00
bool GDScriptNativeClass : : _get ( const StringName & p_name , Variant & r_ret ) const {
2014-02-10 02:10:30 +01:00
bool ok ;
2017-01-03 03:03:46 +01:00
int v = ClassDB : : get_integer_constant ( name , p_name , & ok ) ;
2014-02-10 02:10:30 +01:00
if ( ok ) {
2017-03-05 16:44:50 +01:00
r_ret = v ;
2014-02-10 02:10:30 +01:00
return true ;
} else {
return false ;
}
}
2017-11-16 18:38:18 +01:00
void GDScriptNativeClass : : _bind_methods ( ) {
2014-02-10 02:10:30 +01:00
2017-11-16 18:38:18 +01:00
ClassDB : : bind_method ( D_METHOD ( " new " ) , & GDScriptNativeClass : : _new ) ;
2014-02-10 02:10:30 +01:00
}
2017-11-16 18:38:18 +01:00
Variant GDScriptNativeClass : : _new ( ) {
2014-02-10 02:10:30 +01:00
Object * o = instance ( ) ;
2019-08-09 06:49:33 +02:00
ERR_FAIL_COND_V_MSG ( ! o , Variant ( ) , " Class type: ' " + String ( name ) + " ' is not instantiable. " ) ;
2014-02-10 02:10:30 +01:00
2017-08-24 22:58:51 +02:00
Reference * ref = Object : : cast_to < Reference > ( o ) ;
2014-02-10 02:10:30 +01:00
if ( ref ) {
return REF ( ref ) ;
} else {
return o ;
}
}
2017-11-16 18:38:18 +01:00
Object * GDScriptNativeClass : : instance ( ) {
2014-02-10 02:10:30 +01:00
2017-01-03 03:03:46 +01:00
return ClassDB : : instance ( name ) ;
2014-02-10 02:10:30 +01:00
}
2017-11-16 18:38:18 +01:00
GDScriptInstance * GDScript : : _create_instance ( const Variant * * p_args , int p_argcount , Object * p_owner , bool p_isref , Variant : : CallError & r_error ) {
2014-02-10 02:10:30 +01:00
/* STEP 1, CREATE */
2017-11-16 18:38:18 +01:00
GDScriptInstance * instance = memnew ( GDScriptInstance ) ;
2017-03-05 16:44:50 +01:00
instance - > base_ref = p_isref ;
2014-02-10 02:10:30 +01:00
instance - > members . resize ( member_indices . size ( ) ) ;
2017-03-05 16:44:50 +01:00
instance - > script = Ref < GDScript > ( this ) ;
instance - > owner = p_owner ;
2016-06-02 01:22:02 +02:00
# ifdef DEBUG_ENABLED
//needed for hot reloading
2017-03-05 16:44:50 +01:00
for ( Map < StringName , MemberInfo > : : Element * E = member_indices . front ( ) ; E ; E = E - > next ( ) ) {
instance - > member_indices_cache [ E - > key ( ) ] = E - > get ( ) . index ;
2016-06-02 01:22:02 +02:00
}
# endif
2014-02-10 02:10:30 +01:00
instance - > owner - > set_script_instance ( instance ) ;
Fix misc. source comment typos
Found using `codespell -q 3 -S ./thirdparty,*.po -L ang,ba,cas,dof,doubleclick,fave,hist,leapyear,lod,nd,numer,ois,paket,seeked,sinc,switchs,te,uint -D ~/Projects/codespell/codespell_lib/data/dictionary.txt `
2019-09-19 20:36:39 +02:00
/* STEP 2, INITIALIZE AND CONSTRUCT */
2014-02-10 02:10:30 +01:00
2017-01-10 23:02:52 +01:00
# ifndef NO_THREADS
GDScriptLanguage : : singleton - > lock - > lock ( ) ;
# endif
2014-02-10 02:10:30 +01:00
instances . insert ( instance - > owner ) ;
2017-01-10 23:02:52 +01:00
# ifndef NO_THREADS
GDScriptLanguage : : singleton - > lock - > unlock ( ) ;
# endif
2017-03-05 16:44:50 +01:00
initializer - > call ( instance , p_args , p_argcount , r_error ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( r_error . error ! = Variant : : CallError : : CALL_OK ) {
instance - > script = Ref < GDScript > ( ) ;
2014-12-18 05:50:47 +01:00
instance - > owner - > set_script_instance ( NULL ) ;
2017-01-10 23:02:52 +01:00
# ifndef NO_THREADS
GDScriptLanguage : : singleton - > lock - > lock ( ) ;
# endif
2014-02-10 02:10:30 +01:00
instances . erase ( p_owner ) ;
2017-01-10 23:02:52 +01:00
# ifndef NO_THREADS
GDScriptLanguage : : singleton - > lock - > unlock ( ) ;
# endif
2018-09-19 15:37:54 +02:00
ERR_FAIL_COND_V ( r_error . error ! = Variant : : CallError : : CALL_OK , NULL ) ; //error constructing
2014-02-10 02:10:30 +01:00
}
//@TODO make thread safe
return instance ;
}
2017-03-05 16:44:50 +01:00
Variant GDScript : : _new ( const Variant * * p_args , int p_argcount , Variant : : CallError & r_error ) {
2014-02-10 02:10:30 +01:00
/* STEP 1, CREATE */
2016-02-28 03:10:44 +01:00
if ( ! valid ) {
2017-03-05 16:44:50 +01:00
r_error . error = Variant : : CallError : : CALL_ERROR_INVALID_METHOD ;
2016-02-28 03:10:44 +01:00
return Variant ( ) ;
}
2017-03-05 16:44:50 +01:00
r_error . error = Variant : : CallError : : CALL_OK ;
2014-02-10 02:10:30 +01:00
REF ref ;
2017-03-05 16:44:50 +01:00
Object * owner = NULL ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
GDScript * _baseptr = this ;
2014-02-10 02:10:30 +01:00
while ( _baseptr - > _base ) {
2017-03-05 16:44:50 +01:00
_baseptr = _baseptr - > _base ;
2014-02-10 02:10:30 +01:00
}
2017-01-12 20:00:14 +01:00
ERR_FAIL_COND_V ( _baseptr - > native . is_null ( ) , Variant ( ) ) ;
2016-06-07 04:40:50 +02:00
if ( _baseptr - > native . ptr ( ) ) {
2017-03-05 16:44:50 +01:00
owner = _baseptr - > native - > instance ( ) ;
2016-06-07 04:40:50 +02:00
} else {
2017-03-05 16:44:50 +01:00
owner = memnew ( Reference ) ; //by default, no base means use reference
2016-06-07 04:40:50 +02:00
}
2019-08-09 06:49:33 +02:00
ERR_FAIL_COND_V_MSG ( ! owner , Variant ( ) , " Can't inherit from a virtual class. " ) ;
2014-02-10 02:10:30 +01:00
2017-08-24 22:58:51 +02:00
Reference * r = Object : : cast_to < Reference > ( owner ) ;
2014-02-10 02:10:30 +01:00
if ( r ) {
2017-03-05 16:44:50 +01:00
ref = REF ( r ) ;
2014-02-10 02:10:30 +01:00
}
2017-11-16 18:38:18 +01:00
GDScriptInstance * instance = _create_instance ( p_args , p_argcount , owner , r ! = NULL , r_error ) ;
2014-02-10 02:10:30 +01:00
if ( ! instance ) {
if ( ref . is_null ( ) ) {
memdelete ( owner ) ; //no owner, sorry
}
return Variant ( ) ;
}
2016-03-09 00:00:52 +01:00
if ( ref . is_valid ( ) ) {
2014-02-10 02:10:30 +01:00
return ref ;
} else {
return owner ;
}
}
bool GDScript : : can_instance ( ) const {
2018-07-29 22:40:09 +02:00
# ifdef TOOLS_ENABLED
return valid & & ( tool | | ScriptServer : : is_scripting_enabled ( ) ) ;
# else
return valid ;
# endif
2014-02-10 02:10:30 +01:00
}
2016-08-25 22:45:20 +02:00
Ref < Script > GDScript : : get_base_script ( ) const {
if ( _base ) {
2017-03-05 16:44:50 +01:00
return Ref < GDScript > ( _base ) ;
2016-08-25 22:45:20 +02:00
} else {
return Ref < Script > ( ) ;
}
}
2014-02-10 02:10:30 +01:00
StringName GDScript : : get_instance_base_type ( ) const {
if ( native . is_valid ( ) )
return native - > get_name ( ) ;
if ( base . is_valid ( ) )
return base - > get_instance_base_type ( ) ;
return StringName ( ) ;
}
struct _GDScriptMemberSort {
int index ;
StringName name ;
2017-03-05 16:44:50 +01:00
_FORCE_INLINE_ bool operator < ( const _GDScriptMemberSort & p_member ) const { return index < p_member . index ; }
2014-02-10 02:10:30 +01:00
} ;
# ifdef TOOLS_ENABLED
void GDScript : : _placeholder_erased ( PlaceHolderScriptInstance * p_placeholder ) {
placeholders . erase ( p_placeholder ) ;
}
# endif
2016-01-03 00:17:31 +01:00
2016-08-19 21:48:08 +02:00
void GDScript : : get_script_method_list ( List < MethodInfo > * p_list ) const {
2016-08-03 00:11:05 +02:00
2019-01-25 14:09:44 +01:00
const GDScript * current = this ;
while ( current ) {
2019-05-02 16:13:45 +02:00
for ( const Map < StringName , GDScriptFunction * > : : Element * E = current - > member_functions . front ( ) ; E ; E = E - > next ( ) ) {
2019-01-25 14:09:44 +01:00
GDScriptFunction * func = E - > get ( ) ;
MethodInfo mi ;
mi . name = E - > key ( ) ;
for ( int i = 0 ; i < func - > get_argument_count ( ) ; i + + ) {
mi . arguments . push_back ( func - > get_argument_type ( i ) ) ;
}
mi . return_val = func - > get_return_type ( ) ;
p_list - > push_back ( mi ) ;
2016-08-03 00:11:05 +02:00
}
2019-01-25 14:09:44 +01:00
current = current - > _base ;
2016-08-03 00:11:05 +02:00
}
}
2016-08-08 06:21:22 +02:00
2016-08-24 00:29:07 +02:00
void GDScript : : get_script_property_list ( List < PropertyInfo > * p_list ) const {
2017-03-05 16:44:50 +01:00
const GDScript * sptr = this ;
2016-08-24 00:29:07 +02:00
List < PropertyInfo > props ;
2017-03-05 16:44:50 +01:00
while ( sptr ) {
2016-08-24 00:29:07 +02:00
Vector < _GDScriptMemberSort > msort ;
2017-03-05 16:44:50 +01:00
for ( Map < StringName , PropertyInfo > : : Element * E = sptr - > member_info . front ( ) ; E ; E = E - > next ( ) ) {
2016-08-24 00:29:07 +02:00
_GDScriptMemberSort ms ;
ERR_CONTINUE ( ! sptr - > member_indices . has ( E - > key ( ) ) ) ;
2017-03-05 16:44:50 +01:00
ms . index = sptr - > member_indices [ E - > key ( ) ] . index ;
ms . name = E - > key ( ) ;
2016-08-24 00:29:07 +02:00
msort . push_back ( ms ) ;
}
msort . sort ( ) ;
msort . invert ( ) ;
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < msort . size ( ) ; i + + ) {
2016-08-24 00:29:07 +02:00
props . push_front ( sptr - > member_info [ msort [ i ] . name ] ) ;
}
sptr = sptr - > _base ;
}
2017-03-05 16:44:50 +01:00
for ( List < PropertyInfo > : : Element * E = props . front ( ) ; E ; E = E - > next ( ) ) {
2016-08-24 00:29:07 +02:00
p_list - > push_back ( E - > get ( ) ) ;
}
}
2017-03-05 16:44:50 +01:00
bool GDScript : : has_method ( const StringName & p_method ) const {
2016-08-08 06:21:22 +02:00
return member_functions . has ( p_method ) ;
}
2017-03-05 16:44:50 +01:00
MethodInfo GDScript : : get_method_info ( const StringName & p_method ) const {
2016-08-08 06:21:22 +02:00
2017-11-16 18:38:18 +01:00
const Map < StringName , GDScriptFunction * > : : Element * E = member_functions . find ( p_method ) ;
2016-08-08 06:21:22 +02:00
if ( ! E )
return MethodInfo ( ) ;
2018-05-30 04:16:57 +02:00
GDScriptFunction * func = E - > get ( ) ;
2016-08-08 06:21:22 +02:00
MethodInfo mi ;
2017-03-05 16:44:50 +01:00
mi . name = E - > key ( ) ;
2018-05-30 04:16:57 +02:00
for ( int i = 0 ; i < func - > get_argument_count ( ) ; i + + ) {
mi . arguments . push_back ( func - > get_argument_type ( i ) ) ;
2016-08-08 06:21:22 +02:00
}
2018-05-30 04:16:57 +02:00
mi . return_val = func - > get_return_type ( ) ;
2016-08-08 06:21:22 +02:00
return mi ;
}
2017-03-05 16:44:50 +01:00
bool GDScript : : get_property_default_value ( const StringName & p_property , Variant & r_value ) const {
2016-01-03 00:17:31 +01:00
# ifdef TOOLS_ENABLED
2017-03-05 16:44:50 +01:00
const Map < StringName , Variant > : : Element * E = member_default_values_cache . find ( p_property ) ;
2016-01-03 00:17:31 +01:00
if ( E ) {
2017-03-05 16:44:50 +01:00
r_value = E - > get ( ) ;
2016-01-03 00:17:31 +01:00
return true ;
}
if ( base_cache . is_valid ( ) ) {
2017-03-05 16:44:50 +01:00
return base_cache - > get_property_default_value ( p_property , r_value ) ;
2016-01-03 00:17:31 +01:00
}
# endif
return false ;
}
2017-03-05 16:44:50 +01:00
ScriptInstance * GDScript : : instance_create ( Object * p_this ) {
2014-09-22 05:50:48 +02:00
2017-03-05 16:44:50 +01:00
GDScript * top = this ;
while ( top - > _base )
top = top - > _base ;
2014-02-10 02:10:30 +01:00
if ( top - > native . is_valid ( ) ) {
2017-03-05 16:44:50 +01:00
if ( ! ClassDB : : is_parent_class ( p_this - > get_class_name ( ) , top - > native - > get_name ( ) ) ) {
2014-02-10 02:10:30 +01:00
if ( ScriptDebugger : : get_singleton ( ) ) {
2017-03-05 16:44:50 +01:00
GDScriptLanguage : : get_singleton ( ) - > debug_break_parse ( get_path ( ) , 0 , " Script inherits from native type ' " + String ( top - > native - > get_name ( ) ) + " ', so it can't be instanced in object of type: ' " + p_this - > get_class ( ) + " ' " ) ;
2014-02-10 02:10:30 +01:00
}
2019-08-09 06:49:33 +02:00
ERR_FAIL_V_MSG ( NULL , " Script inherits from native type ' " + String ( top - > native - > get_name ( ) ) + " ', so it can't be instanced in object of type ' " + p_this - > get_class ( ) + " ' " + " . " ) ;
2014-02-10 02:10:30 +01:00
}
}
2015-11-28 19:53:05 +01:00
Variant : : CallError unchecked_error ;
2019-07-01 12:59:42 +02:00
return _create_instance ( NULL , 0 , p_this , Object : : cast_to < Reference > ( p_this ) ! = NULL , unchecked_error ) ;
2014-02-10 02:10:30 +01:00
}
2018-07-29 22:40:09 +02:00
PlaceHolderScriptInstance * GDScript : : placeholder_instance_create ( Object * p_this ) {
# ifdef TOOLS_ENABLED
PlaceHolderScriptInstance * si = memnew ( PlaceHolderScriptInstance ( GDScriptLanguage : : get_singleton ( ) , Ref < Script > ( this ) , p_this ) ) ;
placeholders . insert ( si ) ;
_update_exports ( ) ;
return si ;
# else
return NULL ;
# endif
}
2014-02-10 02:10:30 +01:00
bool GDScript : : instance_has ( const Object * p_this ) const {
2017-01-10 23:02:52 +01:00
# ifndef NO_THREADS
GDScriptLanguage : : singleton - > lock - > lock ( ) ;
# endif
2017-03-05 16:44:50 +01:00
bool hasit = instances . has ( ( Object * ) p_this ) ;
2017-01-10 23:02:52 +01:00
# ifndef NO_THREADS
GDScriptLanguage : : singleton - > lock - > unlock ( ) ;
# endif
return hasit ;
2014-02-10 02:10:30 +01:00
}
bool GDScript : : has_source_code ( ) const {
2017-03-05 16:44:50 +01:00
return source ! = " " ;
2014-02-10 02:10:30 +01:00
}
String GDScript : : get_source_code ( ) const {
return source ;
}
2017-03-05 16:44:50 +01:00
void GDScript : : set_source_code ( const String & p_code ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( source = = p_code )
2014-12-07 06:04:20 +01:00
return ;
2017-03-05 16:44:50 +01:00
source = p_code ;
2014-12-07 06:04:20 +01:00
# ifdef TOOLS_ENABLED
2017-03-05 16:44:50 +01:00
source_changed_cache = true ;
2014-12-07 06:04:20 +01:00
# endif
2014-09-22 05:50:48 +02:00
}
2014-12-07 06:04:20 +01:00
# ifdef TOOLS_ENABLED
2017-03-05 16:44:50 +01:00
void GDScript : : _update_exports_values ( Map < StringName , Variant > & values , List < PropertyInfo > & propnames ) {
2014-12-07 06:04:20 +01:00
if ( base_cache . is_valid ( ) ) {
2017-03-05 16:44:50 +01:00
base_cache - > _update_exports_values ( values , propnames ) ;
2014-12-07 06:04:20 +01:00
}
2017-03-05 16:44:50 +01:00
for ( Map < StringName , Variant > : : Element * E = member_default_values_cache . front ( ) ; E ; E = E - > next ( ) ) {
values [ E - > key ( ) ] = E - > get ( ) ;
2014-12-07 06:04:20 +01:00
}
2017-03-05 16:44:50 +01:00
for ( List < PropertyInfo > : : Element * E = members_cache . front ( ) ; E ; E = E - > next ( ) ) {
2014-12-07 06:04:20 +01:00
propnames . push_back ( E - > get ( ) ) ;
}
}
# endif
2014-10-12 07:13:22 +02:00
2014-12-07 06:04:20 +01:00
bool GDScript : : _update_exports ( ) {
2014-09-22 05:50:48 +02:00
# ifdef TOOLS_ENABLED
2014-10-12 07:13:22 +02:00
2017-03-05 16:44:50 +01:00
bool changed = false ;
2014-09-22 05:50:48 +02:00
2014-12-07 06:04:20 +01:00
if ( source_changed_cache ) {
2017-03-05 16:44:50 +01:00
source_changed_cache = false ;
changed = true ;
2014-09-22 05:50:48 +02:00
2017-03-05 16:44:50 +01:00
String basedir = path ;
2014-09-22 05:50:48 +02:00
2017-03-05 16:44:50 +01:00
if ( basedir = = " " )
basedir = get_path ( ) ;
2014-09-22 05:50:48 +02:00
2017-03-05 16:44:50 +01:00
if ( basedir ! = " " )
basedir = basedir . get_base_dir ( ) ;
2014-09-22 05:50:48 +02:00
2017-11-16 18:38:18 +01:00
GDScriptParser parser ;
2017-03-05 16:44:50 +01:00
Error err = parser . parse ( source , basedir , true , path ) ;
2014-10-12 07:13:22 +02:00
2017-03-05 16:44:50 +01:00
if ( err = = OK ) {
2014-10-12 07:13:22 +02:00
2017-11-16 18:38:18 +01:00
const GDScriptParser : : Node * root = parser . get_parse_tree ( ) ;
ERR_FAIL_COND_V ( root - > type ! = GDScriptParser : : Node : : TYPE_CLASS , false ) ;
2014-09-22 05:50:48 +02:00
2017-11-16 18:38:18 +01:00
const GDScriptParser : : ClassNode * c = static_cast < const GDScriptParser : : ClassNode * > ( root ) ;
2014-10-12 07:13:22 +02:00
2014-12-07 06:04:20 +01:00
if ( base_cache . is_valid ( ) ) {
2017-08-07 12:17:31 +02:00
base_cache - > inheriters_cache . erase ( get_instance_id ( ) ) ;
2017-03-05 16:44:50 +01:00
base_cache = Ref < GDScript > ( ) ;
2014-12-07 06:04:20 +01:00
}
2014-10-12 07:13:22 +02:00
2018-10-12 01:08:14 +02:00
if ( c - > extends_used ) {
String path = " " ;
if ( String ( c - > extends_file ) ! = " " & & String ( c - > extends_file ) ! = get_path ( ) ) {
path = c - > extends_file ;
if ( path . is_rel_path ( ) ) {
String base = get_path ( ) ;
if ( base = = " " | | base . is_rel_path ( ) ) {
ERR_PRINT ( ( " Could not resolve relative path for parent class: " + path ) . utf8 ( ) . get_data ( ) ) ;
} else {
path = base . get_base_dir ( ) . plus_file ( path ) ;
}
2014-12-07 06:04:20 +01:00
}
2018-10-12 01:08:14 +02:00
} else if ( c - > extends_class . size ( ) ! = 0 ) {
String base = c - > extends_class [ 0 ] ;
if ( ScriptServer : : is_global_class ( base ) )
path = ScriptServer : : get_global_class_path ( base ) ;
2014-12-07 06:04:20 +01:00
}
2018-10-12 01:08:14 +02:00
if ( path ! = " " ) {
if ( path ! = get_path ( ) ) {
2016-01-02 17:56:58 +01:00
2018-10-12 01:08:14 +02:00
Ref < GDScript > bf = ResourceLoader : : load ( path ) ;
2014-12-07 06:04:20 +01:00
2018-10-12 01:08:14 +02:00
if ( bf . is_valid ( ) ) {
2014-12-07 06:04:20 +01:00
2018-10-12 01:08:14 +02:00
base_cache = bf ;
bf - > inheriters_cache . insert ( get_instance_id ( ) ) ;
}
} else {
ERR_PRINT ( ( " Path extending itself in " + path ) . utf8 ( ) . get_data ( ) ) ;
2016-01-02 17:56:58 +01:00
}
2014-12-07 06:04:20 +01:00
}
}
2017-01-14 18:03:38 +01:00
members_cache . clear ( ) ;
2014-12-07 06:04:20 +01:00
member_default_values_cache . clear ( ) ;
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < c - > variables . size ( ) ; i + + ) {
if ( c - > variables [ i ] . _export . type = = Variant : : NIL )
2014-12-07 06:04:20 +01:00
continue ;
members_cache . push_back ( c - > variables [ i ] . _export ) ;
2017-03-05 16:44:50 +01:00
member_default_values_cache [ c - > variables [ i ] . identifier ] = c - > variables [ i ] . default_value ;
2014-12-07 06:04:20 +01:00
}
2015-06-24 18:29:23 +02:00
_signals . clear ( ) ;
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < c - > _signals . size ( ) ; i + + ) {
_signals [ c - > _signals [ i ] . name ] = c - > _signals [ i ] . arguments ;
2015-06-24 18:29:23 +02:00
}
2018-07-29 22:40:09 +02:00
} else {
2019-01-10 00:26:00 +01:00
placeholder_fallback_enabled = true ;
2018-11-10 20:06:17 +01:00
return false ;
}
2019-02-14 02:36:19 +01:00
} else if ( placeholder_fallback_enabled ) {
2019-01-10 00:26:00 +01:00
return false ;
2014-09-22 05:50:48 +02:00
}
2019-01-10 00:26:00 +01:00
placeholder_fallback_enabled = false ;
2014-12-07 06:04:20 +01:00
if ( base_cache . is_valid ( ) ) {
if ( base_cache - > _update_exports ( ) ) {
changed = true ;
}
}
2018-08-24 09:35:07 +02:00
if ( placeholders . size ( ) ) { //hm :(
2014-12-07 06:04:20 +01:00
2018-08-24 09:35:07 +02:00
// update placeholders if any
2017-03-05 16:44:50 +01:00
Map < StringName , Variant > values ;
2014-12-07 06:04:20 +01:00
List < PropertyInfo > propnames ;
2017-03-05 16:44:50 +01:00
_update_exports_values ( values , propnames ) ;
2014-09-22 05:50:48 +02:00
2017-03-05 16:44:50 +01:00
for ( Set < PlaceHolderScriptInstance * > : : Element * E = placeholders . front ( ) ; E ; E = E - > next ( ) ) {
E - > get ( ) - > update ( propnames , values ) ;
2014-12-07 06:04:20 +01:00
}
2014-09-22 05:50:48 +02:00
}
2014-12-07 06:04:20 +01:00
return changed ;
2017-08-22 19:01:57 +02:00
# else
2014-12-20 19:30:06 +01:00
return false ;
2017-08-22 19:01:57 +02:00
# endif
2014-10-12 07:13:22 +02:00
}
void GDScript : : update_exports ( ) {
# ifdef TOOLS_ENABLED
2014-12-07 06:04:20 +01:00
_update_exports ( ) ;
2014-10-12 07:13:22 +02:00
2017-03-05 16:44:50 +01:00
Set < ObjectID > copy = inheriters_cache ; //might get modified
2014-12-07 06:04:20 +01:00
2017-03-05 16:44:50 +01:00
for ( Set < ObjectID > : : Element * E = copy . front ( ) ; E ; E = E - > next ( ) ) {
Object * id = ObjectDB : : get_instance ( E - > get ( ) ) ;
2017-08-24 22:58:51 +02:00
GDScript * s = Object : : cast_to < GDScript > ( id ) ;
2014-12-07 06:04:20 +01:00
if ( ! s )
continue ;
s - > update_exports ( ) ;
}
2014-09-22 05:50:48 +02:00
# endif
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
void GDScript : : _set_subclass_path ( Ref < GDScript > & p_sc , const String & p_path ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
p_sc - > path = p_path ;
for ( Map < StringName , Ref < GDScript > > : : Element * E = p_sc - > subclasses . front ( ) ; E ; E = E - > next ( ) ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
_set_subclass_path ( E - > get ( ) , p_path ) ;
2014-02-10 02:10:30 +01:00
}
}
2016-06-02 01:22:02 +02:00
Error GDScript : : reload ( bool p_keep_state ) {
2014-02-10 02:10:30 +01:00
2017-01-10 23:02:52 +01:00
# ifndef NO_THREADS
GDScriptLanguage : : singleton - > lock - > lock ( ) ;
# endif
bool has_instances = instances . size ( ) ;
# ifndef NO_THREADS
GDScriptLanguage : : singleton - > lock - > unlock ( ) ;
# endif
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
ERR_FAIL_COND_V ( ! p_keep_state & & has_instances , ERR_ALREADY_IN_USE ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
String basedir = path ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( basedir = = " " )
basedir = get_path ( ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( basedir ! = " " )
basedir = basedir . get_base_dir ( ) ;
2014-02-10 02:10:30 +01:00
2017-08-24 22:07:56 +02:00
if ( source . find ( " %BASE% " ) ! = - 1 ) {
2017-06-13 22:03:08 +02:00
//loading a template, don't parse
return OK ;
}
2017-03-05 16:44:50 +01:00
valid = false ;
2017-11-16 18:38:18 +01:00
GDScriptParser parser ;
2017-03-05 16:44:50 +01:00
Error err = parser . parse ( source , basedir , false , path ) ;
2014-02-10 02:10:30 +01:00
if ( err ) {
if ( ScriptDebugger : : get_singleton ( ) ) {
2017-03-05 16:44:50 +01:00
GDScriptLanguage : : get_singleton ( ) - > debug_break_parse ( get_path ( ) , parser . get_error_line ( ) , " Parser Error: " + parser . get_error ( ) ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
_err_print_error ( " GDScript::reload " , path . empty ( ) ? " built-in " : ( const char * ) path . utf8 ( ) . get_data ( ) , parser . get_error_line ( ) , ( " Parse Error: " + parser . get_error ( ) ) . utf8 ( ) . get_data ( ) , ERR_HANDLER_SCRIPT ) ;
2014-02-10 02:10:30 +01:00
ERR_FAIL_V ( ERR_PARSE_ERROR ) ;
}
2016-01-23 19:36:03 +01:00
bool can_run = ScriptServer : : is_scripting_enabled ( ) | | parser . is_tool_script ( ) ;
2017-11-16 18:38:18 +01:00
GDScriptCompiler compiler ;
2017-03-05 16:44:50 +01:00
err = compiler . compile ( & parser , this , p_keep_state ) ;
2014-02-10 02:10:30 +01:00
if ( err ) {
2016-01-23 19:36:03 +01:00
if ( can_run ) {
if ( ScriptDebugger : : get_singleton ( ) ) {
2017-03-05 16:44:50 +01:00
GDScriptLanguage : : get_singleton ( ) - > debug_break_parse ( get_path ( ) , compiler . get_error_line ( ) , " Parser Error: " + compiler . get_error ( ) ) ;
2016-01-23 19:36:03 +01:00
}
2017-03-05 16:44:50 +01:00
_err_print_error ( " GDScript::reload " , path . empty ( ) ? " built-in " : ( const char * ) path . utf8 ( ) . get_data ( ) , compiler . get_error_line ( ) , ( " Compile Error: " + compiler . get_error ( ) ) . utf8 ( ) . get_data ( ) , ERR_HANDLER_SCRIPT ) ;
2016-01-23 19:36:03 +01:00
ERR_FAIL_V ( ERR_COMPILATION_FAILED ) ;
} else {
return err ;
2014-02-10 02:10:30 +01:00
}
}
2019-04-05 23:20:20 +02:00
# ifdef DEBUG_ENABLED
2018-07-01 18:17:40 +02:00
for ( const List < GDScriptWarning > : : Element * E = parser . get_warnings ( ) . front ( ) ; E ; E = E - > next ( ) ) {
2018-08-21 20:21:51 +02:00
const GDScriptWarning & warning = E - > get ( ) ;
if ( ScriptDebugger : : get_singleton ( ) ) {
Vector < ScriptLanguage : : StackInfo > si ;
ScriptDebugger : : get_singleton ( ) - > send_error ( " " , get_path ( ) , warning . line , warning . get_name ( ) , warning . get_message ( ) , ERR_HANDLER_WARNING , si ) ;
}
2018-07-01 18:17:40 +02:00
}
# endif
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
valid = true ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
for ( Map < StringName , Ref < GDScript > > : : Element * E = subclasses . front ( ) ; E ; E = E - > next ( ) ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
_set_subclass_path ( E - > get ( ) , path ) ;
2014-02-10 02:10:30 +01:00
}
return OK ;
}
ScriptLanguage * GDScript : : get_language ( ) const {
return GDScriptLanguage : : get_singleton ( ) ;
}
2017-10-17 16:35:49 +02:00
void GDScript : : get_constants ( Map < StringName , Variant > * p_constants ) {
if ( p_constants ) {
for ( Map < StringName , Variant > : : Element * E = constants . front ( ) ; E ; E = E - > next ( ) ) {
( * p_constants ) [ E - > key ( ) ] = E - > value ( ) ;
}
}
}
void GDScript : : get_members ( Set < StringName > * p_members ) {
if ( p_members ) {
for ( Set < StringName > : : Element * E = members . front ( ) ; E ; E = E - > next ( ) ) {
p_members - > insert ( E - > get ( ) ) ;
}
}
}
2017-03-05 16:44:50 +01:00
Variant GDScript : : call ( const StringName & p_method , const Variant * * p_args , int p_argcount , Variant : : CallError & r_error ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
GDScript * top = this ;
while ( top ) {
2014-02-10 02:10:30 +01:00
2017-11-16 18:38:18 +01:00
Map < StringName , GDScriptFunction * > : : Element * E = top - > member_functions . find ( p_method ) ;
2014-02-10 02:10:30 +01:00
if ( E ) {
2019-08-09 06:49:33 +02:00
ERR_FAIL_COND_V_MSG ( ! E - > get ( ) - > is_static ( ) , Variant ( ) , " Can't call non-static function ' " + String ( p_method ) + " ' in script. " ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
return E - > get ( ) - > call ( NULL , p_args , p_argcount , r_error ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
top = top - > _base ;
2014-02-10 02:10:30 +01:00
}
//none found, regular
2017-03-05 16:44:50 +01:00
return Script : : call ( p_method , p_args , p_argcount , r_error ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
bool GDScript : : _get ( const StringName & p_name , Variant & r_ret ) const {
2014-02-10 02:10:30 +01:00
{
2017-03-05 16:44:50 +01:00
const GDScript * top = this ;
while ( top ) {
2014-02-10 02:10:30 +01:00
{
2017-03-05 16:44:50 +01:00
const Map < StringName , Variant > : : Element * E = top - > constants . find ( p_name ) ;
2014-02-10 02:10:30 +01:00
if ( E ) {
2017-03-05 16:44:50 +01:00
r_ret = E - > get ( ) ;
2014-02-10 02:10:30 +01:00
return true ;
}
}
{
2017-03-05 16:44:50 +01:00
const Map < StringName , Ref < GDScript > > : : Element * E = subclasses . find ( p_name ) ;
2014-02-10 02:10:30 +01:00
if ( E ) {
2017-03-05 16:44:50 +01:00
r_ret = E - > get ( ) ;
2014-02-10 02:10:30 +01:00
return true ;
}
}
2017-03-05 16:44:50 +01:00
top = top - > _base ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
if ( p_name = = GDScriptLanguage : : get_singleton ( ) - > strings . _script_source ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
r_ret = get_source_code ( ) ;
2014-02-16 01:16:33 +01:00
return true ;
}
}
2014-02-10 02:10:30 +01:00
2014-02-16 01:16:33 +01:00
return false ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
bool GDScript : : _set ( const StringName & p_name , const Variant & p_value ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( p_name = = GDScriptLanguage : : get_singleton ( ) - > strings . _script_source ) {
2014-02-10 02:10:30 +01:00
set_source_code ( p_value ) ;
reload ( ) ;
} else
return false ;
return true ;
}
void GDScript : : _get_property_list ( List < PropertyInfo > * p_properties ) const {
2018-01-11 23:35:12 +01:00
p_properties - > push_back ( PropertyInfo ( Variant : : STRING , " script/source " , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL ) ) ;
2014-02-10 02:10:30 +01:00
}
void GDScript : : _bind_methods ( ) {
2019-08-26 18:36:51 +02:00
ClassDB : : bind_vararg_method ( METHOD_FLAGS_DEFAULT , " new " , & GDScript : : _new , MethodInfo ( " new " ) ) ;
2015-06-30 16:28:43 +02:00
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " get_as_byte_code " ) , & GDScript : : get_as_byte_code ) ;
2014-02-10 02:10:30 +01:00
}
2015-06-30 16:28:43 +02:00
Vector < uint8_t > GDScript : : get_as_byte_code ( ) const {
2017-11-16 18:38:18 +01:00
GDScriptTokenizerBuffer tokenizer ;
2015-06-30 16:28:43 +02:00
return tokenizer . parse_code_string ( source ) ;
} ;
2017-03-05 16:44:50 +01:00
Error GDScript : : load_byte_code ( const String & p_path ) {
2014-02-25 13:31:47 +01:00
2014-06-11 15:41:03 +02:00
Vector < uint8_t > bytecode ;
if ( p_path . ends_with ( " gde " ) ) {
2017-03-05 16:44:50 +01:00
FileAccess * fa = FileAccess : : open ( p_path , FileAccess : : READ ) ;
ERR_FAIL_COND_V ( ! fa , ERR_CANT_OPEN ) ;
2018-08-26 03:19:02 +02:00
2017-03-05 16:44:50 +01:00
FileAccessEncrypted * fae = memnew ( FileAccessEncrypted ) ;
ERR_FAIL_COND_V ( ! fae , ERR_CANT_OPEN ) ;
2018-08-26 03:19:02 +02:00
2014-06-11 15:41:03 +02:00
Vector < uint8_t > key ;
key . resize ( 32 ) ;
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < key . size ( ) ; i + + ) {
2018-07-25 03:11:03 +02:00
key . write [ i ] = script_encryption_key [ i ] ;
2014-06-11 15:41:03 +02:00
}
2018-08-26 03:19:02 +02:00
2017-03-05 16:44:50 +01:00
Error err = fae - > open_and_parse ( fa , key , FileAccessEncrypted : : MODE_READ ) ;
2018-08-26 03:19:02 +02:00
if ( err ) {
fa - > close ( ) ;
memdelete ( fa ) ;
memdelete ( fae ) ;
ERR_FAIL_COND_V ( err , err ) ;
}
2014-06-11 15:41:03 +02:00
bytecode . resize ( fae - > get_len ( ) ) ;
2017-11-25 04:07:54 +01:00
fae - > get_buffer ( bytecode . ptrw ( ) , bytecode . size ( ) ) ;
2018-08-26 03:19:02 +02:00
fae - > close ( ) ;
2014-06-11 15:41:03 +02:00
memdelete ( fae ) ;
2018-08-26 03:19:02 +02:00
2014-06-11 15:41:03 +02:00
} else {
bytecode = FileAccess : : get_file_as_array ( p_path ) ;
}
2018-08-26 03:19:02 +02:00
2017-03-05 16:44:50 +01:00
ERR_FAIL_COND_V ( bytecode . size ( ) = = 0 , ERR_PARSE_ERROR ) ;
path = p_path ;
2014-02-25 13:31:47 +01:00
2017-03-05 16:44:50 +01:00
String basedir = path ;
2014-02-25 13:31:47 +01:00
2017-03-05 16:44:50 +01:00
if ( basedir = = " " )
basedir = get_path ( ) ;
2014-02-25 13:31:47 +01:00
2017-03-05 16:44:50 +01:00
if ( basedir ! = " " )
basedir = basedir . get_base_dir ( ) ;
2014-02-25 13:31:47 +01:00
2017-03-05 16:44:50 +01:00
valid = false ;
2017-11-16 18:38:18 +01:00
GDScriptParser parser ;
2017-03-05 16:44:50 +01:00
Error err = parser . parse_bytecode ( bytecode , basedir , get_path ( ) ) ;
2014-02-25 13:31:47 +01:00
if ( err ) {
2017-03-05 16:44:50 +01:00
_err_print_error ( " GDScript::load_byte_code " , path . empty ( ) ? " built-in " : ( const char * ) path . utf8 ( ) . get_data ( ) , parser . get_error_line ( ) , ( " Parse Error: " + parser . get_error ( ) ) . utf8 ( ) . get_data ( ) , ERR_HANDLER_SCRIPT ) ;
2014-02-25 13:31:47 +01:00
ERR_FAIL_V ( ERR_PARSE_ERROR ) ;
}
2017-11-16 18:38:18 +01:00
GDScriptCompiler compiler ;
2017-03-05 16:44:50 +01:00
err = compiler . compile ( & parser , this ) ;
2014-02-25 13:31:47 +01:00
if ( err ) {
2017-03-05 16:44:50 +01:00
_err_print_error ( " GDScript::load_byte_code " , path . empty ( ) ? " built-in " : ( const char * ) path . utf8 ( ) . get_data ( ) , compiler . get_error_line ( ) , ( " Compile Error: " + compiler . get_error ( ) ) . utf8 ( ) . get_data ( ) , ERR_HANDLER_SCRIPT ) ;
2014-02-25 13:31:47 +01:00
ERR_FAIL_V ( ERR_COMPILATION_FAILED ) ;
}
2017-03-05 16:44:50 +01:00
valid = true ;
2014-02-25 13:31:47 +01:00
2017-03-05 16:44:50 +01:00
for ( Map < StringName , Ref < GDScript > > : : Element * E = subclasses . front ( ) ; E ; E = E - > next ( ) ) {
2014-02-25 13:31:47 +01:00
2017-03-05 16:44:50 +01:00
_set_subclass_path ( E - > get ( ) , path ) ;
2014-02-25 13:31:47 +01:00
}
return OK ;
}
2017-03-05 16:44:50 +01:00
Error GDScript : : load_source_code ( const String & p_path ) {
2014-02-10 02:10:30 +01:00
2017-01-07 22:25:37 +01:00
PoolVector < uint8_t > sourcef ;
2014-02-10 02:10:30 +01:00
Error err ;
2017-03-05 16:44:50 +01:00
FileAccess * f = FileAccess : : open ( p_path , FileAccess : : READ , & err ) ;
2014-02-10 02:10:30 +01:00
if ( err ) {
2017-03-05 16:44:50 +01:00
ERR_FAIL_COND_V ( err , err ) ;
2014-02-10 02:10:30 +01:00
}
int len = f - > get_len ( ) ;
2017-03-05 16:44:50 +01:00
sourcef . resize ( len + 1 ) ;
2017-01-07 22:25:37 +01:00
PoolVector < uint8_t > : : Write w = sourcef . write ( ) ;
2017-03-05 16:44:50 +01:00
int r = f - > get_buffer ( w . ptr ( ) , len ) ;
2014-02-10 02:10:30 +01:00
f - > close ( ) ;
memdelete ( f ) ;
2017-03-05 16:44:50 +01:00
ERR_FAIL_COND_V ( r ! = len , ERR_CANT_OPEN ) ;
w [ len ] = 0 ;
2014-02-10 02:10:30 +01:00
String s ;
2017-03-05 16:44:50 +01:00
if ( s . parse_utf8 ( ( const char * ) w . ptr ( ) ) ) {
2014-02-10 02:10:30 +01:00
2019-08-09 06:49:33 +02:00
ERR_FAIL_V_MSG ( ERR_INVALID_DATA , " Script ' " + p_path + " ' contains invalid unicode (UTF-8), so it was not loaded. Please ensure that scripts are saved in valid UTF-8 unicode. " ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
source = s ;
2014-12-07 06:39:51 +01:00
# ifdef TOOLS_ENABLED
2017-03-05 16:44:50 +01:00
source_changed_cache = true ;
2014-12-07 06:39:51 +01:00
# endif
2017-03-05 16:44:50 +01:00
path = p_path ;
2014-02-10 02:10:30 +01:00
return OK ;
}
2017-11-16 18:38:18 +01:00
const Map < StringName , GDScriptFunction * > & GDScript : : debug_get_member_functions ( ) const {
2014-02-10 02:10:30 +01:00
return member_functions ;
}
StringName GDScript : : debug_get_member_by_index ( int p_idx ) const {
2017-03-05 16:44:50 +01:00
for ( const Map < StringName , MemberInfo > : : Element * E = member_indices . front ( ) ; E ; E = E - > next ( ) ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( E - > get ( ) . index = = p_idx )
2014-02-10 02:10:30 +01:00
return E - > key ( ) ;
}
return " <error> " ;
}
Ref < GDScript > GDScript : : get_base ( ) const {
return base ;
}
2017-03-05 16:44:50 +01:00
bool GDScript : : has_script_signal ( const StringName & p_signal ) const {
2015-06-24 18:29:23 +02:00
if ( _signals . has ( p_signal ) )
return true ;
if ( base . is_valid ( ) ) {
return base - > has_script_signal ( p_signal ) ;
}
# ifdef TOOLS_ENABLED
2017-03-05 16:44:50 +01:00
else if ( base_cache . is_valid ( ) ) {
2015-06-24 18:29:23 +02:00
return base_cache - > has_script_signal ( p_signal ) ;
}
# endif
return false ;
}
void GDScript : : get_script_signal_list ( List < MethodInfo > * r_signals ) const {
2017-03-05 16:44:50 +01:00
for ( const Map < StringName , Vector < StringName > > : : Element * E = _signals . front ( ) ; E ; E = E - > next ( ) ) {
2015-06-24 18:29:23 +02:00
MethodInfo mi ;
2017-03-05 16:44:50 +01:00
mi . name = E - > key ( ) ;
for ( int i = 0 ; i < E - > get ( ) . size ( ) ; i + + ) {
2015-06-24 18:29:23 +02:00
PropertyInfo arg ;
2017-03-05 16:44:50 +01:00
arg . name = E - > get ( ) [ i ] ;
2015-06-24 18:29:23 +02:00
mi . arguments . push_back ( arg ) ;
}
r_signals - > push_back ( mi ) ;
}
if ( base . is_valid ( ) ) {
base - > get_script_signal_list ( r_signals ) ;
}
# ifdef TOOLS_ENABLED
2017-03-05 16:44:50 +01:00
else if ( base_cache . is_valid ( ) ) {
2015-06-24 18:29:23 +02:00
base_cache - > get_script_signal_list ( r_signals ) ;
}
# endif
}
2017-12-06 21:36:34 +01:00
GDScript : : GDScript ( ) :
script_list ( this ) {
2015-06-24 18:29:23 +02:00
2017-03-05 16:44:50 +01:00
_static_ref = this ;
valid = false ;
subclass_count = 0 ;
initializer = NULL ;
_base = NULL ;
_owner = NULL ;
tool = false ;
2014-12-07 06:04:20 +01:00
# ifdef TOOLS_ENABLED
2017-03-05 16:44:50 +01:00
source_changed_cache = false ;
2019-01-10 00:26:00 +01:00
placeholder_fallback_enabled = false ;
2014-12-07 06:04:20 +01:00
# endif
2014-06-16 15:22:26 +02:00
2016-06-02 01:22:02 +02:00
# ifdef DEBUG_ENABLED
if ( GDScriptLanguage : : get_singleton ( ) - > lock ) {
GDScriptLanguage : : get_singleton ( ) - > lock - > lock ( ) ;
}
GDScriptLanguage : : get_singleton ( ) - > script_list . add ( & script_list ) ;
if ( GDScriptLanguage : : get_singleton ( ) - > lock ) {
GDScriptLanguage : : get_singleton ( ) - > lock - > unlock ( ) ;
}
# endif
2014-02-10 02:10:30 +01:00
}
2016-05-22 02:18:16 +02:00
GDScript : : ~ GDScript ( ) {
2017-11-16 18:38:18 +01:00
for ( Map < StringName , GDScriptFunction * > : : Element * E = member_functions . front ( ) ; E ; E = E - > next ( ) ) {
2017-03-05 16:44:50 +01:00
memdelete ( E - > get ( ) ) ;
2016-05-22 02:18:16 +02:00
}
2016-06-02 01:22:02 +02:00
2017-03-05 16:44:50 +01:00
for ( Map < StringName , Ref < GDScript > > : : Element * E = subclasses . front ( ) ; E ; E = E - > next ( ) ) {
E - > get ( ) - > _owner = NULL ; //bye, you are no longer owned cause I died
2016-06-28 15:44:38 +02:00
}
2016-06-02 01:22:02 +02:00
# ifdef DEBUG_ENABLED
if ( GDScriptLanguage : : get_singleton ( ) - > lock ) {
GDScriptLanguage : : get_singleton ( ) - > lock - > lock ( ) ;
}
GDScriptLanguage : : get_singleton ( ) - > script_list . remove ( & script_list ) ;
if ( GDScriptLanguage : : get_singleton ( ) - > lock ) {
GDScriptLanguage : : get_singleton ( ) - > lock - > unlock ( ) ;
}
# endif
2016-05-22 02:18:16 +02:00
}
2014-02-10 02:10:30 +01:00
//////////////////////////////
// INSTANCE //
//////////////////////////////
2017-11-16 18:38:18 +01:00
bool GDScriptInstance : : set ( const StringName & p_name , const Variant & p_value ) {
2014-02-10 02:10:30 +01:00
//member
{
2017-03-05 16:44:50 +01:00
const Map < StringName , GDScript : : MemberInfo > : : Element * E = script - > member_indices . find ( p_name ) ;
2014-02-10 02:10:30 +01:00
if ( E ) {
2014-10-28 02:54:32 +01:00
if ( E - > get ( ) . setter ) {
2017-03-05 16:44:50 +01:00
const Variant * val = & p_value ;
2014-10-28 02:54:32 +01:00
Variant : : CallError err ;
2017-03-05 16:44:50 +01:00
call ( E - > get ( ) . setter , & val , 1 , err ) ;
if ( err . error = = Variant : : CallError : : CALL_OK ) {
2014-10-28 02:54:32 +01:00
return true ; //function exists, call was successful
}
2018-05-30 04:16:56 +02:00
} else {
if ( ! E - > get ( ) . data_type . is_type ( p_value ) ) {
return false ; // Type mismatch
}
2018-07-25 03:11:03 +02:00
members . write [ E - > get ( ) . index ] = p_value ;
2018-05-30 04:16:56 +02:00
}
2014-02-10 02:10:30 +01:00
return true ;
}
}
2017-03-05 16:44:50 +01:00
GDScript * sptr = script . ptr ( ) ;
while ( sptr ) {
2014-02-10 02:10:30 +01:00
2017-11-16 18:38:18 +01:00
Map < StringName , GDScriptFunction * > : : Element * E = sptr - > member_functions . find ( GDScriptLanguage : : get_singleton ( ) - > strings . _set ) ;
2014-02-10 02:10:30 +01:00
if ( E ) {
2017-03-05 16:44:50 +01:00
Variant name = p_name ;
const Variant * args [ 2 ] = { & name , & p_value } ;
2014-02-10 02:10:30 +01:00
Variant : : CallError err ;
2017-03-05 16:44:50 +01:00
Variant ret = E - > get ( ) - > call ( this , ( const Variant * * ) args , 2 , err ) ;
if ( err . error = = Variant : : CallError : : CALL_OK & & ret . get_type ( ) = = Variant : : BOOL & & ret . operator bool ( ) )
2014-02-10 02:10:30 +01:00
return true ;
}
sptr = sptr - > _base ;
}
return false ;
}
2017-11-16 18:38:18 +01:00
bool GDScriptInstance : : get ( const StringName & p_name , Variant & r_ret ) const {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
const GDScript * sptr = script . ptr ( ) ;
while ( sptr ) {
2014-02-10 02:10:30 +01:00
{
2017-03-05 16:44:50 +01:00
const Map < StringName , GDScript : : MemberInfo > : : Element * E = script - > member_indices . find ( p_name ) ;
2014-02-10 02:10:30 +01:00
if ( E ) {
2014-10-28 02:54:32 +01:00
if ( E - > get ( ) . getter ) {
Variant : : CallError err ;
2017-11-16 18:38:18 +01:00
r_ret = const_cast < GDScriptInstance * > ( this ) - > call ( E - > get ( ) . getter , NULL , 0 , err ) ;
2017-03-05 16:44:50 +01:00
if ( err . error = = Variant : : CallError : : CALL_OK ) {
2014-10-28 02:54:32 +01:00
return true ;
}
}
2017-03-05 16:44:50 +01:00
r_ret = members [ E - > get ( ) . index ] ;
2014-02-10 02:10:30 +01:00
return true ; //index found
}
}
{
2016-06-28 16:15:55 +02:00
const GDScript * sl = sptr ;
2017-03-05 16:44:50 +01:00
while ( sl ) {
const Map < StringName , Variant > : : Element * E = sl - > constants . find ( p_name ) ;
2016-06-28 16:15:55 +02:00
if ( E ) {
2017-03-05 16:44:50 +01:00
r_ret = E - > get ( ) ;
2016-06-28 16:15:55 +02:00
return true ; //index found
}
2017-03-05 16:44:50 +01:00
sl = sl - > _base ;
2014-02-10 02:10:30 +01:00
}
}
{
2017-11-16 18:38:18 +01:00
const Map < StringName , GDScriptFunction * > : : Element * E = sptr - > member_functions . find ( GDScriptLanguage : : get_singleton ( ) - > strings . _get ) ;
2014-02-10 02:10:30 +01:00
if ( E ) {
2017-03-05 16:44:50 +01:00
Variant name = p_name ;
const Variant * args [ 1 ] = { & name } ;
2014-02-10 02:10:30 +01:00
Variant : : CallError err ;
2017-11-16 18:38:18 +01:00
Variant ret = const_cast < GDScriptFunction * > ( E - > get ( ) ) - > call ( const_cast < GDScriptInstance * > ( this ) , ( const Variant * * ) args , 1 , err ) ;
2017-03-05 16:44:50 +01:00
if ( err . error = = Variant : : CallError : : CALL_OK & & ret . get_type ( ) ! = Variant : : NIL ) {
r_ret = ret ;
2014-02-10 02:10:30 +01:00
return true ;
}
}
}
sptr = sptr - > _base ;
}
return false ;
}
2015-12-05 18:18:22 +01:00
2017-11-16 18:38:18 +01:00
Variant : : Type GDScriptInstance : : get_property_type ( const StringName & p_name , bool * r_is_valid ) const {
2015-12-05 18:18:22 +01:00
2017-03-05 16:44:50 +01:00
const GDScript * sptr = script . ptr ( ) ;
while ( sptr ) {
2015-12-05 18:18:22 +01:00
if ( sptr - > member_info . has ( p_name ) ) {
if ( r_is_valid )
2017-03-05 16:44:50 +01:00
* r_is_valid = true ;
2015-12-05 18:18:22 +01:00
return sptr - > member_info [ p_name ] . type ;
}
sptr = sptr - > _base ;
}
if ( r_is_valid )
2017-03-05 16:44:50 +01:00
* r_is_valid = false ;
2015-12-05 18:18:22 +01:00
return Variant : : NIL ;
}
2017-11-16 18:38:18 +01:00
void GDScriptInstance : : get_property_list ( List < PropertyInfo > * p_properties ) const {
2019-05-19 12:34:40 +02:00
// exported members, not done yet!
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
const GDScript * sptr = script . ptr ( ) ;
2014-02-10 02:10:30 +01:00
List < PropertyInfo > props ;
2017-03-05 16:44:50 +01:00
while ( sptr ) {
2014-02-10 02:10:30 +01:00
2017-11-16 18:38:18 +01:00
const Map < StringName , GDScriptFunction * > : : Element * E = sptr - > member_functions . find ( GDScriptLanguage : : get_singleton ( ) - > strings . _get_property_list ) ;
2014-02-10 02:10:30 +01:00
if ( E ) {
Variant : : CallError err ;
2017-11-16 18:38:18 +01:00
Variant ret = const_cast < GDScriptFunction * > ( E - > get ( ) ) - > call ( const_cast < GDScriptInstance * > ( this ) , NULL , 0 , err ) ;
2017-03-05 16:44:50 +01:00
if ( err . error = = Variant : : CallError : : CALL_OK ) {
2014-02-10 02:10:30 +01:00
2019-08-09 06:49:33 +02:00
ERR_FAIL_COND_MSG ( ret . get_type ( ) ! = Variant : : ARRAY , " Wrong type for _get_property_list, must be an array of dictionaries. " ) ;
2014-02-10 02:10:30 +01:00
Array arr = ret ;
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < arr . size ( ) ; i + + ) {
2014-02-10 02:10:30 +01:00
Dictionary d = arr [ i ] ;
ERR_CONTINUE ( ! d . has ( " name " ) ) ;
ERR_CONTINUE ( ! d . has ( " type " ) ) ;
PropertyInfo pinfo ;
2017-03-05 16:44:50 +01:00
pinfo . type = Variant : : Type ( d [ " type " ] . operator int ( ) ) ;
ERR_CONTINUE ( pinfo . type < 0 | | pinfo . type > = Variant : : VARIANT_MAX ) ;
2014-02-10 02:10:30 +01:00
pinfo . name = d [ " name " ] ;
2017-03-05 16:44:50 +01:00
ERR_CONTINUE ( pinfo . name = = " " ) ;
2014-02-10 02:10:30 +01:00
if ( d . has ( " hint " ) )
2017-03-05 16:44:50 +01:00
pinfo . hint = PropertyHint ( d [ " hint " ] . operator int ( ) ) ;
2014-02-10 02:10:30 +01:00
if ( d . has ( " hint_string " ) )
2017-03-05 16:44:50 +01:00
pinfo . hint_string = d [ " hint_string " ] ;
2014-02-10 02:10:30 +01:00
if ( d . has ( " usage " ) )
2017-03-05 16:44:50 +01:00
pinfo . usage = d [ " usage " ] ;
2014-02-10 02:10:30 +01:00
props . push_back ( pinfo ) ;
}
}
}
//instance a fake script for editing the values
Vector < _GDScriptMemberSort > msort ;
2019-02-12 21:10:08 +01:00
for ( Map < StringName , PropertyInfo > : : Element * F = sptr - > member_info . front ( ) ; F ; F = F - > next ( ) ) {
2014-02-10 02:10:30 +01:00
_GDScriptMemberSort ms ;
2019-02-12 21:10:08 +01:00
ERR_CONTINUE ( ! sptr - > member_indices . has ( F - > key ( ) ) ) ;
ms . index = sptr - > member_indices [ F - > key ( ) ] . index ;
ms . name = F - > key ( ) ;
2014-02-10 02:10:30 +01:00
msort . push_back ( ms ) ;
}
msort . sort ( ) ;
msort . invert ( ) ;
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < msort . size ( ) ; i + + ) {
2014-02-10 02:10:30 +01:00
props . push_front ( sptr - > member_info [ msort [ i ] . name ] ) ;
}
sptr = sptr - > _base ;
}
2017-03-05 16:44:50 +01:00
for ( List < PropertyInfo > : : Element * E = props . front ( ) ; E ; E = E - > next ( ) ) {
2014-02-10 02:10:30 +01:00
p_properties - > push_back ( E - > get ( ) ) ;
}
}
2017-11-16 18:38:18 +01:00
void GDScriptInstance : : get_method_list ( List < MethodInfo > * p_list ) const {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
const GDScript * sptr = script . ptr ( ) ;
while ( sptr ) {
2014-02-10 02:10:30 +01:00
2017-11-16 18:38:18 +01:00
for ( Map < StringName , GDScriptFunction * > : : Element * E = sptr - > member_functions . front ( ) ; E ; E = E - > next ( ) ) {
2014-02-10 02:10:30 +01:00
MethodInfo mi ;
2017-03-05 16:44:50 +01:00
mi . name = E - > key ( ) ;
mi . flags | = METHOD_FLAG_FROM_SCRIPT ;
for ( int i = 0 ; i < E - > get ( ) - > get_argument_count ( ) ; i + + )
mi . arguments . push_back ( PropertyInfo ( Variant : : NIL , " arg " + itos ( i ) ) ) ;
2014-02-10 02:10:30 +01:00
p_list - > push_back ( mi ) ;
}
sptr = sptr - > _base ;
}
}
2017-11-16 18:38:18 +01:00
bool GDScriptInstance : : has_method ( const StringName & p_method ) const {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
const GDScript * sptr = script . ptr ( ) ;
while ( sptr ) {
2017-11-16 18:38:18 +01:00
const Map < StringName , GDScriptFunction * > : : Element * E = sptr - > member_functions . find ( p_method ) ;
2014-02-10 02:10:30 +01:00
if ( E )
return true ;
sptr = sptr - > _base ;
}
return false ;
}
2017-11-16 18:38:18 +01:00
Variant GDScriptInstance : : call ( const StringName & p_method , const Variant * * p_args , int p_argcount , Variant : : CallError & r_error ) {
2014-02-10 02:10:30 +01:00
//printf("calling %ls:%i method %ls\n", script->get_path().c_str(), -1, String(p_method).c_str());
2017-03-05 16:44:50 +01:00
GDScript * sptr = script . ptr ( ) ;
while ( sptr ) {
2017-11-16 18:38:18 +01:00
Map < StringName , GDScriptFunction * > : : Element * E = sptr - > member_functions . find ( p_method ) ;
2014-02-10 02:10:30 +01:00
if ( E ) {
2017-03-05 16:44:50 +01:00
return E - > get ( ) - > call ( this , p_args , p_argcount , r_error ) ;
2014-02-10 02:10:30 +01:00
}
sptr = sptr - > _base ;
}
2017-03-05 16:44:50 +01:00
r_error . error = Variant : : CallError : : CALL_ERROR_INVALID_METHOD ;
2014-02-10 02:10:30 +01:00
return Variant ( ) ;
}
2017-11-16 18:38:18 +01:00
void GDScriptInstance : : call_multilevel ( const StringName & p_method , const Variant * * p_args , int p_argcount ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
GDScript * sptr = script . ptr ( ) ;
2014-02-10 02:10:30 +01:00
Variant : : CallError ce ;
2017-03-05 16:44:50 +01:00
while ( sptr ) {
2017-11-16 18:38:18 +01:00
Map < StringName , GDScriptFunction * > : : Element * E = sptr - > member_functions . find ( p_method ) ;
2014-02-10 02:10:30 +01:00
if ( E ) {
2017-03-05 16:44:50 +01:00
E - > get ( ) - > call ( this , p_args , p_argcount , ce ) ;
2014-02-10 02:10:30 +01:00
}
sptr = sptr - > _base ;
}
}
2017-11-16 18:38:18 +01:00
void GDScriptInstance : : _ml_call_reversed ( GDScript * sptr , const StringName & p_method , const Variant * * p_args , int p_argcount ) {
2014-02-10 02:10:30 +01:00
if ( sptr - > _base )
2017-03-05 16:44:50 +01:00
_ml_call_reversed ( sptr - > _base , p_method , p_args , p_argcount ) ;
2014-02-10 02:10:30 +01:00
Variant : : CallError ce ;
2017-11-16 18:38:18 +01:00
Map < StringName , GDScriptFunction * > : : Element * E = sptr - > member_functions . find ( p_method ) ;
2014-02-10 02:10:30 +01:00
if ( E ) {
2017-03-05 16:44:50 +01:00
E - > get ( ) - > call ( this , p_args , p_argcount , ce ) ;
2014-02-10 02:10:30 +01:00
}
}
2017-11-16 18:38:18 +01:00
void GDScriptInstance : : call_multilevel_reversed ( const StringName & p_method , const Variant * * p_args , int p_argcount ) {
2014-02-10 02:10:30 +01:00
if ( script . ptr ( ) ) {
2017-03-05 16:44:50 +01:00
_ml_call_reversed ( script . ptr ( ) , p_method , p_args , p_argcount ) ;
2014-02-10 02:10:30 +01:00
}
}
2017-11-16 18:38:18 +01:00
void GDScriptInstance : : notification ( int p_notification ) {
2014-02-10 02:10:30 +01:00
2017-03-24 21:45:31 +01:00
//notification is not virtual, it gets called at ALL levels just like in C.
2017-03-05 16:44:50 +01:00
Variant value = p_notification ;
const Variant * args [ 1 ] = { & value } ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
GDScript * sptr = script . ptr ( ) ;
while ( sptr ) {
2017-11-16 18:38:18 +01:00
Map < StringName , GDScriptFunction * > : : Element * E = sptr - > member_functions . find ( GDScriptLanguage : : get_singleton ( ) - > strings . _notification ) ;
2014-02-10 02:10:30 +01:00
if ( E ) {
Variant : : CallError err ;
2017-03-05 16:44:50 +01:00
E - > get ( ) - > call ( this , args , 1 , err ) ;
if ( err . error ! = Variant : : CallError : : CALL_OK ) {
2014-02-10 02:10:30 +01:00
//print error about notification call
}
}
sptr = sptr - > _base ;
}
}
2019-04-10 07:07:40 +02:00
String GDScriptInstance : : to_string ( bool * r_valid ) {
if ( has_method ( CoreStringNames : : get_singleton ( ) - > _to_string ) ) {
Variant : : CallError ce ;
Variant ret = call ( CoreStringNames : : get_singleton ( ) - > _to_string , NULL , 0 , ce ) ;
if ( ce . error = = Variant : : CallError : : CALL_OK ) {
if ( ret . get_type ( ) ! = Variant : : STRING ) {
if ( r_valid )
* r_valid = false ;
2019-08-09 06:49:33 +02:00
ERR_FAIL_V_MSG ( String ( ) , " Wrong type for " + CoreStringNames : : get_singleton ( ) - > _to_string + " , must be a String. " ) ;
2019-04-10 07:07:40 +02:00
}
if ( r_valid )
* r_valid = true ;
return ret . operator String ( ) ;
}
}
if ( r_valid )
* r_valid = false ;
return String ( ) ;
}
2017-11-16 18:38:18 +01:00
Ref < Script > GDScriptInstance : : get_script ( ) const {
2014-02-10 02:10:30 +01:00
return script ;
}
2017-11-16 18:38:18 +01:00
ScriptLanguage * GDScriptInstance : : get_language ( ) {
2014-02-10 02:10:30 +01:00
return GDScriptLanguage : : get_singleton ( ) ;
}
2018-05-13 07:07:56 +02:00
MultiplayerAPI : : RPCMode GDScriptInstance : : get_rpc_mode ( const StringName & p_method ) const {
2016-08-19 21:48:08 +02:00
const GDScript * cscript = script . ptr ( ) ;
2017-03-05 16:44:50 +01:00
while ( cscript ) {
2017-11-16 18:38:18 +01:00
const Map < StringName , GDScriptFunction * > : : Element * E = cscript - > member_functions . find ( p_method ) ;
2016-08-19 21:48:08 +02:00
if ( E ) {
2018-05-13 07:07:56 +02:00
if ( E - > get ( ) - > get_rpc_mode ( ) ! = MultiplayerAPI : : RPC_MODE_DISABLED ) {
2016-08-19 21:48:08 +02:00
return E - > get ( ) - > get_rpc_mode ( ) ;
}
}
2017-03-05 16:44:50 +01:00
cscript = cscript - > _base ;
2016-08-19 21:48:08 +02:00
}
2018-05-13 07:07:56 +02:00
return MultiplayerAPI : : RPC_MODE_DISABLED ;
2016-08-19 21:48:08 +02:00
}
2018-05-13 07:07:56 +02:00
MultiplayerAPI : : RPCMode GDScriptInstance : : get_rset_mode ( const StringName & p_variable ) const {
2016-08-19 21:48:08 +02:00
const GDScript * cscript = script . ptr ( ) ;
2017-03-05 16:44:50 +01:00
while ( cscript ) {
const Map < StringName , GDScript : : MemberInfo > : : Element * E = cscript - > member_indices . find ( p_variable ) ;
2016-08-19 21:48:08 +02:00
if ( E ) {
if ( E - > get ( ) . rpc_mode ) {
return E - > get ( ) . rpc_mode ;
}
}
2017-03-05 16:44:50 +01:00
cscript = cscript - > _base ;
2016-08-19 21:48:08 +02:00
}
2018-05-13 07:07:56 +02:00
return MultiplayerAPI : : RPC_MODE_DISABLED ;
2016-08-19 21:48:08 +02:00
}
2017-11-16 18:38:18 +01:00
void GDScriptInstance : : reload_members ( ) {
2016-06-02 01:22:02 +02:00
# ifdef DEBUG_ENABLED
members . resize ( script - > member_indices . size ( ) ) ; //resize
Vector < Variant > new_members ;
new_members . resize ( script - > member_indices . size ( ) ) ;
//pass the values to the new indices
2017-03-05 16:44:50 +01:00
for ( Map < StringName , GDScript : : MemberInfo > : : Element * E = script - > member_indices . front ( ) ; E ; E = E - > next ( ) ) {
2016-06-02 01:22:02 +02:00
if ( member_indices_cache . has ( E - > key ( ) ) ) {
Variant value = members [ member_indices_cache [ E - > key ( ) ] ] ;
2018-07-25 03:11:03 +02:00
new_members . write [ E - > get ( ) . index ] = value ;
2016-06-02 01:22:02 +02:00
}
}
//apply
2017-03-05 16:44:50 +01:00
members = new_members ;
2016-06-02 01:22:02 +02:00
//pass the values to the new indices
member_indices_cache . clear ( ) ;
2017-03-05 16:44:50 +01:00
for ( Map < StringName , GDScript : : MemberInfo > : : Element * E = script - > member_indices . front ( ) ; E ; E = E - > next ( ) ) {
2016-06-02 01:22:02 +02:00
2017-03-05 16:44:50 +01:00
member_indices_cache [ E - > key ( ) ] = E - > get ( ) . index ;
2016-06-02 01:22:02 +02:00
}
# endif
}
2014-02-10 02:10:30 +01:00
2017-11-16 18:38:18 +01:00
GDScriptInstance : : GDScriptInstance ( ) {
2017-03-05 16:44:50 +01:00
owner = NULL ;
base_ref = false ;
2014-02-10 02:10:30 +01:00
}
2017-11-16 18:38:18 +01:00
GDScriptInstance : : ~ GDScriptInstance ( ) {
2014-02-10 02:10:30 +01:00
if ( script . is_valid ( ) & & owner ) {
2017-01-10 23:02:52 +01:00
# ifndef NO_THREADS
2017-03-05 16:44:50 +01:00
GDScriptLanguage : : singleton - > lock - > lock ( ) ;
2017-01-10 23:02:52 +01:00
# endif
2017-03-05 16:44:50 +01:00
script - > instances . erase ( owner ) ;
2017-01-10 23:02:52 +01:00
# ifndef NO_THREADS
2017-03-05 16:44:50 +01:00
GDScriptLanguage : : singleton - > lock - > unlock ( ) ;
2017-01-10 23:02:52 +01:00
# endif
2014-02-10 02:10:30 +01:00
}
}
/************* SCRIPT LANGUAGE **************/
2017-03-05 16:44:50 +01:00
GDScriptLanguage * GDScriptLanguage : : singleton = NULL ;
2014-02-10 02:10:30 +01:00
String GDScriptLanguage : : get_name ( ) const {
return " GDScript " ;
}
/* LANGUAGE FUNCTIONS */
2017-03-05 16:44:50 +01:00
void GDScriptLanguage : : _add_global ( const StringName & p_name , const Variant & p_value ) {
2014-02-10 02:10:30 +01:00
if ( globals . has ( p_name ) ) {
//overwrite existing
2018-07-25 03:11:03 +02:00
global_array . write [ globals [ p_name ] ] = p_value ;
2014-02-10 02:10:30 +01:00
return ;
}
2017-03-05 16:44:50 +01:00
globals [ p_name ] = global_array . size ( ) ;
2014-02-10 02:10:30 +01:00
global_array . push_back ( p_value ) ;
2017-11-25 04:07:54 +01:00
_global_array = global_array . ptrw ( ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
void GDScriptLanguage : : add_global_constant ( const StringName & p_variable , const Variant & p_value ) {
2015-12-28 19:59:20 +01:00
2017-03-05 16:44:50 +01:00
_add_global ( p_variable , p_value ) ;
2015-12-28 19:59:20 +01:00
}
2018-05-01 16:06:23 +02:00
void GDScriptLanguage : : add_named_global_constant ( const StringName & p_name , const Variant & p_value ) {
named_globals [ p_name ] = p_value ;
}
void GDScriptLanguage : : remove_named_global_constant ( const StringName & p_name ) {
ERR_FAIL_COND ( ! named_globals . has ( p_name ) ) ;
named_globals . erase ( p_name ) ;
}
2014-02-10 02:10:30 +01:00
void GDScriptLanguage : : init ( ) {
//populate global constants
2017-03-05 16:44:50 +01:00
int gcc = GlobalConstants : : get_global_constant_count ( ) ;
for ( int i = 0 ; i < gcc ; i + + ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
_add_global ( StaticCString : : create ( GlobalConstants : : get_global_constant_name ( i ) ) , GlobalConstants : : get_global_constant_value ( i ) ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
_add_global ( StaticCString : : create ( " PI " ) , Math_PI ) ;
2017-11-04 10:34:27 +01:00
_add_global ( StaticCString : : create ( " TAU " ) , Math_TAU ) ;
2017-03-05 16:44:50 +01:00
_add_global ( StaticCString : : create ( " INF " ) , Math_INF ) ;
_add_global ( StaticCString : : create ( " NAN " ) , Math_NAN ) ;
2014-02-10 02:10:30 +01:00
//populate native classes
2015-06-29 05:29:49 +02:00
List < StringName > class_list ;
2017-01-03 03:03:46 +01:00
ClassDB : : get_class_list ( & class_list ) ;
2017-03-05 16:44:50 +01:00
for ( List < StringName > : : Element * E = class_list . front ( ) ; E ; E = E - > next ( ) ) {
2014-02-10 02:10:30 +01:00
StringName n = E - > get ( ) ;
String s = String ( n ) ;
if ( s . begins_with ( " _ " ) )
2017-03-05 16:44:50 +01:00
n = s . substr ( 1 , s . length ( ) ) ;
2014-02-10 02:10:30 +01:00
if ( globals . has ( n ) )
continue ;
2017-11-16 18:38:18 +01:00
Ref < GDScriptNativeClass > nc = memnew ( GDScriptNativeClass ( E - > get ( ) ) ) ;
2017-03-05 16:44:50 +01:00
_add_global ( n , nc ) ;
2014-02-10 02:10:30 +01:00
}
//populate singletons
2017-11-13 21:46:57 +01:00
List < Engine : : Singleton > singletons ;
Engine : : get_singleton ( ) - > get_singletons ( & singletons ) ;
for ( List < Engine : : Singleton > : : Element * E = singletons . front ( ) ; E ; E = E - > next ( ) ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
_add_global ( E - > get ( ) . name , E - > get ( ) . ptr ) ;
2014-02-10 02:10:30 +01:00
}
}
String GDScriptLanguage : : get_type ( ) const {
return " GDScript " ;
}
String GDScriptLanguage : : get_extension ( ) const {
return " gd " ;
}
2017-03-05 16:44:50 +01:00
Error GDScriptLanguage : : execute_file ( const String & p_path ) {
2014-02-10 02:10:30 +01:00
// ??
return OK ;
}
2017-03-05 16:44:50 +01:00
void GDScriptLanguage : : finish ( ) {
2014-02-10 02:10:30 +01:00
}
2016-05-22 02:18:16 +02:00
void GDScriptLanguage : : profiling_start ( ) {
# ifdef DEBUG_ENABLED
if ( lock ) {
lock - > lock ( ) ;
}
2017-11-16 18:38:18 +01:00
SelfList < GDScriptFunction > * elem = function_list . first ( ) ;
2017-03-05 16:44:50 +01:00
while ( elem ) {
elem - > self ( ) - > profile . call_count = 0 ;
elem - > self ( ) - > profile . self_time = 0 ;
elem - > self ( ) - > profile . total_time = 0 ;
elem - > self ( ) - > profile . frame_call_count = 0 ;
elem - > self ( ) - > profile . frame_self_time = 0 ;
elem - > self ( ) - > profile . frame_total_time = 0 ;
elem - > self ( ) - > profile . last_frame_call_count = 0 ;
elem - > self ( ) - > profile . last_frame_self_time = 0 ;
elem - > self ( ) - > profile . last_frame_total_time = 0 ;
elem = elem - > next ( ) ;
2016-05-22 02:18:16 +02:00
}
2017-03-05 16:44:50 +01:00
profiling = true ;
2016-05-22 02:18:16 +02:00
if ( lock ) {
lock - > unlock ( ) ;
}
# endif
}
void GDScriptLanguage : : profiling_stop ( ) {
# ifdef DEBUG_ENABLED
if ( lock ) {
lock - > lock ( ) ;
}
2017-03-05 16:44:50 +01:00
profiling = false ;
2016-05-22 02:18:16 +02:00
if ( lock ) {
lock - > unlock ( ) ;
}
# endif
}
2017-03-05 16:44:50 +01:00
int GDScriptLanguage : : profiling_get_accumulated_data ( ProfilingInfo * p_info_arr , int p_info_max ) {
2016-05-22 02:18:16 +02:00
2017-03-05 16:44:50 +01:00
int current = 0 ;
2016-05-22 02:18:16 +02:00
# ifdef DEBUG_ENABLED
if ( lock ) {
lock - > lock ( ) ;
}
2017-11-16 18:38:18 +01:00
SelfList < GDScriptFunction > * elem = function_list . first ( ) ;
2017-03-05 16:44:50 +01:00
while ( elem ) {
if ( current > = p_info_max )
2016-05-22 02:18:16 +02:00
break ;
2017-03-05 16:44:50 +01:00
p_info_arr [ current ] . call_count = elem - > self ( ) - > profile . call_count ;
p_info_arr [ current ] . self_time = elem - > self ( ) - > profile . self_time ;
p_info_arr [ current ] . total_time = elem - > self ( ) - > profile . total_time ;
p_info_arr [ current ] . signature = elem - > self ( ) - > profile . signature ;
elem = elem - > next ( ) ;
2016-05-22 02:18:16 +02:00
current + + ;
}
if ( lock ) {
lock - > unlock ( ) ;
}
# endif
return current ;
}
2017-03-05 16:44:50 +01:00
int GDScriptLanguage : : profiling_get_frame_data ( ProfilingInfo * p_info_arr , int p_info_max ) {
2016-05-22 02:18:16 +02:00
2017-03-05 16:44:50 +01:00
int current = 0 ;
2016-05-22 02:18:16 +02:00
# ifdef DEBUG_ENABLED
if ( lock ) {
lock - > lock ( ) ;
}
2017-11-16 18:38:18 +01:00
SelfList < GDScriptFunction > * elem = function_list . first ( ) ;
2017-03-05 16:44:50 +01:00
while ( elem ) {
if ( current > = p_info_max )
2016-05-22 02:18:16 +02:00
break ;
2017-03-05 16:44:50 +01:00
if ( elem - > self ( ) - > profile . last_frame_call_count > 0 ) {
p_info_arr [ current ] . call_count = elem - > self ( ) - > profile . last_frame_call_count ;
p_info_arr [ current ] . self_time = elem - > self ( ) - > profile . last_frame_self_time ;
p_info_arr [ current ] . total_time = elem - > self ( ) - > profile . last_frame_total_time ;
p_info_arr [ current ] . signature = elem - > self ( ) - > profile . signature ;
2016-05-22 02:18:16 +02:00
current + + ;
}
2017-03-05 16:44:50 +01:00
elem = elem - > next ( ) ;
2016-05-22 02:18:16 +02:00
}
if ( lock ) {
lock - > unlock ( ) ;
}
# endif
return current ;
}
2016-06-02 01:22:02 +02:00
struct GDScriptDepSort {
//must support sorting so inheritance works properly (parent must be reloaded first)
2017-03-05 16:44:50 +01:00
bool operator ( ) ( const Ref < GDScript > & A , const Ref < GDScript > & B ) const {
2016-06-02 01:22:02 +02:00
2017-03-05 16:44:50 +01:00
if ( A = = B )
2016-06-02 01:22:02 +02:00
return false ; //shouldn't happen but..
2017-03-05 16:44:50 +01:00
const GDScript * I = B - > get_base ( ) . ptr ( ) ;
while ( I ) {
if ( I = = A . ptr ( ) ) {
2016-06-02 01:22:02 +02:00
// A is a base of B
return true ;
}
2017-03-05 16:44:50 +01:00
I = I - > get_base ( ) . ptr ( ) ;
2016-06-02 01:22:02 +02:00
}
return false ; //not a base
}
} ;
void GDScriptLanguage : : reload_all_scripts ( ) {
# ifdef DEBUG_ENABLED
2018-08-24 09:35:07 +02:00
print_verbose ( " GDScript: Reloading all scripts " ) ;
2016-06-02 01:22:02 +02:00
if ( lock ) {
lock - > lock ( ) ;
}
List < Ref < GDScript > > scripts ;
2017-03-05 16:44:50 +01:00
SelfList < GDScript > * elem = script_list . first ( ) ;
while ( elem ) {
2016-06-02 01:22:02 +02:00
if ( elem - > self ( ) - > get_path ( ) . is_resource_file ( ) ) {
2018-08-24 09:35:07 +02:00
print_verbose ( " GDScript: Found: " + elem - > self ( ) - > get_path ( ) ) ;
2016-06-02 01:22:02 +02:00
scripts . push_back ( Ref < GDScript > ( elem - > self ( ) ) ) ; //cast to gdscript to avoid being erased by accident
}
2017-03-05 16:44:50 +01:00
elem = elem - > next ( ) ;
2016-06-02 01:22:02 +02:00
}
if ( lock ) {
lock - > unlock ( ) ;
}
//as scripts are going to be reloaded, must proceed without locking here
scripts . sort_custom < GDScriptDepSort > ( ) ; //update in inheritance dependency order
2017-03-05 16:44:50 +01:00
for ( List < Ref < GDScript > > : : Element * E = scripts . front ( ) ; E ; E = E - > next ( ) ) {
2016-06-02 01:22:02 +02:00
2018-08-24 09:35:07 +02:00
print_verbose ( " GDScript: Reloading: " + E - > get ( ) - > get_path ( ) ) ;
2016-06-02 01:22:02 +02:00
E - > get ( ) - > load_source_code ( E - > get ( ) - > get_path ( ) ) ;
E - > get ( ) - > reload ( true ) ;
}
# endif
}
2017-03-05 16:44:50 +01:00
void GDScriptLanguage : : reload_tool_script ( const Ref < Script > & p_script , bool p_soft_reload ) {
2016-06-09 01:00:02 +02:00
# ifdef DEBUG_ENABLED
if ( lock ) {
lock - > lock ( ) ;
}
List < Ref < GDScript > > scripts ;
2017-03-05 16:44:50 +01:00
SelfList < GDScript > * elem = script_list . first ( ) ;
while ( elem ) {
2016-06-09 01:00:02 +02:00
if ( elem - > self ( ) - > get_path ( ) . is_resource_file ( ) ) {
scripts . push_back ( Ref < GDScript > ( elem - > self ( ) ) ) ; //cast to gdscript to avoid being erased by accident
}
2017-03-05 16:44:50 +01:00
elem = elem - > next ( ) ;
2016-06-09 01:00:02 +02:00
}
if ( lock ) {
lock - > unlock ( ) ;
}
//when someone asks you why dynamically typed languages are easier to write....
2017-03-05 16:44:50 +01:00
Map < Ref < GDScript > , Map < ObjectID , List < Pair < StringName , Variant > > > > to_reload ;
2016-06-09 01:00:02 +02:00
//as scripts are going to be reloaded, must proceed without locking here
scripts . sort_custom < GDScriptDepSort > ( ) ; //update in inheritance dependency order
2017-03-05 16:44:50 +01:00
for ( List < Ref < GDScript > > : : Element * E = scripts . front ( ) ; E ; E = E - > next ( ) ) {
2016-06-09 01:00:02 +02:00
2017-03-05 16:44:50 +01:00
bool reload = E - > get ( ) = = p_script | | to_reload . has ( E - > get ( ) - > get_base ( ) ) ;
2016-06-09 01:00:02 +02:00
if ( ! reload )
continue ;
2017-03-05 16:44:50 +01:00
to_reload . insert ( E - > get ( ) , Map < ObjectID , List < Pair < StringName , Variant > > > ( ) ) ;
2016-06-09 01:00:02 +02:00
if ( ! p_soft_reload ) {
//save state and remove script from instances
2017-03-05 16:44:50 +01:00
Map < ObjectID , List < Pair < StringName , Variant > > > & map = to_reload [ E - > get ( ) ] ;
2016-06-09 01:00:02 +02:00
2017-03-05 16:44:50 +01:00
while ( E - > get ( ) - > instances . front ( ) ) {
2016-06-09 01:00:02 +02:00
Object * obj = E - > get ( ) - > instances . front ( ) - > get ( ) ;
//save instance info
2017-03-05 16:44:50 +01:00
List < Pair < StringName , Variant > > state ;
2016-06-09 01:00:02 +02:00
if ( obj - > get_script_instance ( ) ) {
obj - > get_script_instance ( ) - > get_property_state ( state ) ;
2017-08-07 12:17:31 +02:00
map [ obj - > get_instance_id ( ) ] = state ;
2016-06-09 01:00:02 +02:00
obj - > set_script ( RefPtr ( ) ) ;
}
}
2017-03-05 16:44:50 +01:00
//same thing for placeholders
2016-06-09 01:00:02 +02:00
# ifdef TOOLS_ENABLED
2017-04-24 18:51:39 +02:00
while ( E - > get ( ) - > placeholders . size ( ) ) {
Object * obj = E - > get ( ) - > placeholders . front ( ) - > get ( ) - > get_owner ( ) ;
2016-06-09 01:00:02 +02:00
//save instance info
if ( obj - > get_script_instance ( ) ) {
2017-11-15 14:41:31 +01:00
map . insert ( obj - > get_instance_id ( ) , List < Pair < StringName , Variant > > ( ) ) ;
List < Pair < StringName , Variant > > & state = map [ obj - > get_instance_id ( ) ] ;
2016-06-09 01:00:02 +02:00
obj - > get_script_instance ( ) - > get_property_state ( state ) ;
obj - > set_script ( RefPtr ( ) ) ;
2017-04-24 18:51:39 +02:00
} else {
// no instance found. Let's remove it so we don't loop forever
E - > get ( ) - > placeholders . erase ( E - > get ( ) - > placeholders . front ( ) - > get ( ) ) ;
2016-06-09 01:00:02 +02:00
}
}
2017-11-15 14:41:31 +01:00
2016-06-09 01:00:02 +02:00
# endif
2017-03-05 16:44:50 +01:00
for ( Map < ObjectID , List < Pair < StringName , Variant > > > : : Element * F = E - > get ( ) - > pending_reload_state . front ( ) ; F ; F = F - > next ( ) ) {
map [ F - > key ( ) ] = F - > get ( ) ; //pending to reload, use this one instead
2016-07-21 03:37:48 +02:00
}
2016-06-09 01:00:02 +02:00
}
}
2017-03-05 16:44:50 +01:00
for ( Map < Ref < GDScript > , Map < ObjectID , List < Pair < StringName , Variant > > > > : : Element * E = to_reload . front ( ) ; E ; E = E - > next ( ) ) {
2016-06-09 01:00:02 +02:00
Ref < GDScript > scr = E - > key ( ) ;
2016-07-21 03:37:48 +02:00
scr - > reload ( p_soft_reload ) ;
2016-06-09 01:00:02 +02:00
//restore state if saved
2017-03-05 16:44:50 +01:00
for ( Map < ObjectID , List < Pair < StringName , Variant > > > : : Element * F = E - > get ( ) . front ( ) ; F ; F = F - > next ( ) ) {
2016-06-09 01:00:02 +02:00
2019-01-10 00:26:00 +01:00
List < Pair < StringName , Variant > > & saved_state = F - > get ( ) ;
2016-06-09 01:00:02 +02:00
Object * obj = ObjectDB : : get_instance ( F - > key ( ) ) ;
if ( ! obj )
continue ;
2016-07-21 03:37:48 +02:00
if ( ! p_soft_reload ) {
//clear it just in case (may be a pending reload state)
obj - > set_script ( RefPtr ( ) ) ;
}
2016-06-09 01:00:02 +02:00
obj - > set_script ( scr . get_ref_ptr ( ) ) ;
2019-01-10 00:26:00 +01:00
ScriptInstance * script_instance = obj - > get_script_instance ( ) ;
if ( ! script_instance ) {
2016-07-21 03:37:48 +02:00
//failed, save reload state for next time if not saved
2017-08-07 12:17:31 +02:00
if ( ! scr - > pending_reload_state . has ( obj - > get_instance_id ( ) ) ) {
2019-01-10 00:26:00 +01:00
scr - > pending_reload_state [ obj - > get_instance_id ( ) ] = saved_state ;
2016-07-21 03:37:48 +02:00
}
2016-06-09 01:00:02 +02:00
continue ;
2016-07-21 03:37:48 +02:00
}
2016-06-09 01:00:02 +02:00
2019-01-10 00:26:00 +01:00
if ( script_instance - > is_placeholder ( ) & & scr - > is_placeholder_fallback_enabled ( ) ) {
PlaceHolderScriptInstance * placeholder = static_cast < PlaceHolderScriptInstance * > ( script_instance ) ;
for ( List < Pair < StringName , Variant > > : : Element * G = saved_state . front ( ) ; G ; G = G - > next ( ) ) {
placeholder - > property_set_fallback ( G - > get ( ) . first , G - > get ( ) . second ) ;
}
} else {
for ( List < Pair < StringName , Variant > > : : Element * G = saved_state . front ( ) ; G ; G = G - > next ( ) ) {
script_instance - > set ( G - > get ( ) . first , G - > get ( ) . second ) ;
}
2016-06-09 01:00:02 +02:00
}
2016-07-21 03:37:48 +02:00
2017-08-07 12:17:31 +02:00
scr - > pending_reload_state . erase ( obj - > get_instance_id ( ) ) ; //as it reloaded, remove pending state
2016-06-09 01:00:02 +02:00
}
//if instance states were saved, set them!
}
# endif
}
2014-02-10 02:10:30 +01:00
void GDScriptLanguage : : frame ( ) {
2017-03-05 16:44:50 +01:00
calls = 0 ;
2016-05-22 02:18:16 +02:00
# ifdef DEBUG_ENABLED
if ( profiling ) {
if ( lock ) {
lock - > lock ( ) ;
}
2017-11-16 18:38:18 +01:00
SelfList < GDScriptFunction > * elem = function_list . first ( ) ;
2017-03-05 16:44:50 +01:00
while ( elem ) {
elem - > self ( ) - > profile . last_frame_call_count = elem - > self ( ) - > profile . frame_call_count ;
elem - > self ( ) - > profile . last_frame_self_time = elem - > self ( ) - > profile . frame_self_time ;
elem - > self ( ) - > profile . last_frame_total_time = elem - > self ( ) - > profile . frame_total_time ;
elem - > self ( ) - > profile . frame_call_count = 0 ;
elem - > self ( ) - > profile . frame_self_time = 0 ;
elem - > self ( ) - > profile . frame_total_time = 0 ;
elem = elem - > next ( ) ;
2016-05-22 02:18:16 +02:00
}
if ( lock ) {
lock - > unlock ( ) ;
}
}
# endif
2014-02-10 02:10:30 +01:00
}
/* EDITOR FUNCTIONS */
2017-03-05 16:44:50 +01:00
void GDScriptLanguage : : get_reserved_words ( List < String > * p_words ) const {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
static const char * _reserved_words [ ] = {
2016-02-04 10:17:23 +01:00
// operators
2014-02-10 02:10:30 +01:00
" and " ,
2016-02-04 10:17:23 +01:00
" in " ,
" not " ,
2014-02-10 02:10:30 +01:00
" or " ,
2016-02-04 10:17:23 +01:00
// types and values
" false " ,
" float " ,
" int " ,
2016-06-24 17:45:59 +02:00
" bool " ,
2016-02-04 10:17:23 +01:00
" null " ,
2016-02-04 10:26:10 +01:00
" PI " ,
2017-11-04 10:34:27 +01:00
" TAU " ,
2017-02-06 23:44:22 +01:00
" INF " ,
" NAN " ,
2016-02-04 10:17:23 +01:00
" self " ,
" true " ,
2018-05-30 04:16:51 +02:00
" void " ,
2016-02-04 10:17:23 +01:00
// functions
2018-05-30 04:16:51 +02:00
" as " ,
2014-05-14 06:22:15 +02:00
" assert " ,
2015-12-29 16:11:21 +01:00
" breakpoint " ,
2016-02-04 10:17:23 +01:00
" class " ,
2018-07-16 00:29:00 +02:00
" class_name " ,
2016-02-04 10:17:23 +01:00
" extends " ,
2017-05-26 19:45:39 +02:00
" is " ,
2016-02-04 10:17:23 +01:00
" func " ,
2016-02-04 18:56:41 +01:00
" preload " ,
2016-02-04 10:17:23 +01:00
" setget " ,
" signal " ,
" tool " ,
2014-09-15 16:33:30 +02:00
" yield " ,
2016-02-04 10:17:23 +01:00
// var
" const " ,
" enum " ,
" export " ,
" onready " ,
2014-09-15 16:33:30 +02:00
" static " ,
2016-02-04 10:17:23 +01:00
" var " ,
// control flow
" break " ,
" continue " ,
" if " ,
" elif " ,
" else " ,
" for " ,
" pass " ,
" return " ,
2016-10-16 13:20:28 +02:00
" match " ,
2016-02-04 10:17:23 +01:00
" while " ,
2016-08-19 21:48:08 +02:00
" remote " ,
" sync " ,
" master " ,
2018-09-14 21:59:47 +02:00
" puppet " ,
2016-08-19 21:48:08 +02:00
" slave " ,
2018-05-26 10:30:36 +02:00
" remotesync " ,
" mastersync " ,
2018-09-14 21:59:47 +02:00
" puppetsync " ,
2017-03-05 16:44:50 +01:00
0
} ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
const char * * w = _reserved_words ;
2014-02-10 02:10:30 +01:00
while ( * w ) {
p_words - > push_back ( * w ) ;
w + + ;
}
2017-11-16 18:38:18 +01:00
for ( int i = 0 ; i < GDScriptFunctions : : FUNC_MAX ; i + + ) {
p_words - > push_back ( GDScriptFunctions : : get_func_name ( GDScriptFunctions : : Function ( i ) ) ) ;
2014-02-10 02:10:30 +01:00
}
}
2018-07-16 00:29:00 +02:00
bool GDScriptLanguage : : handles_global_class_type ( const String & p_type ) const {
return p_type = = " GDScript " ;
}
2018-07-29 05:36:43 +02:00
String GDScriptLanguage : : get_global_class_name ( const String & p_path , String * r_base_type , String * r_icon_path ) const {
2018-07-16 00:29:00 +02:00
PoolVector < uint8_t > sourcef ;
Error err ;
2019-03-04 13:52:15 +01:00
FileAccessRef f = FileAccess : : open ( p_path , FileAccess : : READ , & err ) ;
2018-07-16 00:29:00 +02:00
if ( err ) {
return String ( ) ;
}
2019-03-03 20:36:42 +01:00
String source = f - > get_as_utf8_string ( ) ;
2018-07-16 00:29:00 +02:00
GDScriptParser parser ;
2019-03-03 20:36:42 +01:00
parser . parse ( source , p_path . get_base_dir ( ) , true , p_path , false , NULL , true ) ;
2018-07-16 00:29:00 +02:00
if ( parser . get_parse_tree ( ) & & parser . get_parse_tree ( ) - > type = = GDScriptParser : : Node : : TYPE_CLASS ) {
const GDScriptParser : : ClassNode * c = static_cast < const GDScriptParser : : ClassNode * > ( parser . get_parse_tree ( ) ) ;
2019-03-03 20:36:42 +01:00
if ( r_icon_path ) {
if ( c - > icon_path . empty ( ) | | c - > icon_path . is_abs_path ( ) )
* r_icon_path = c - > icon_path ;
else if ( c - > icon_path . is_rel_path ( ) )
* r_icon_path = p_path . get_base_dir ( ) . plus_file ( c - > icon_path ) . simplify_path ( ) ;
}
2018-05-30 04:16:52 +02:00
if ( r_base_type ) {
2019-03-03 20:36:42 +01:00
const GDScriptParser : : ClassNode * subclass = c ;
String path = p_path ;
GDScriptParser subparser ;
while ( subclass ) {
if ( subclass - > extends_used ) {
if ( subclass - > extends_file ) {
if ( subclass - > extends_class . size ( ) = = 0 ) {
get_global_class_name ( subclass - > extends_file , r_base_type ) ;
subclass = NULL ;
break ;
} else {
Vector < StringName > extend_classes = subclass - > extends_class ;
2019-03-04 13:52:15 +01:00
FileAccessRef subfile = FileAccess : : open ( subclass - > extends_file , FileAccess : : READ ) ;
2019-03-03 20:36:42 +01:00
if ( ! subfile ) {
break ;
}
String subsource = subfile - > get_as_utf8_string ( ) ;
2019-03-04 13:52:15 +01:00
2019-03-03 20:36:42 +01:00
if ( subsource . empty ( ) ) {
break ;
}
String subpath = subclass - > extends_file ;
if ( subpath . is_rel_path ( ) ) {
subpath = path . get_base_dir ( ) . plus_file ( subpath ) . simplify_path ( ) ;
}
if ( OK ! = subparser . parse ( subsource , subpath . get_base_dir ( ) , true , subpath , false , NULL , true ) ) {
break ;
2018-05-30 04:16:52 +02:00
}
2019-03-03 20:36:42 +01:00
path = subpath ;
if ( ! subparser . get_parse_tree ( ) | | subparser . get_parse_tree ( ) - > type ! = GDScriptParser : : Node : : TYPE_CLASS ) {
break ;
}
subclass = static_cast < const GDScriptParser : : ClassNode * > ( subparser . get_parse_tree ( ) ) ;
while ( extend_classes . size ( ) > 0 ) {
bool found = false ;
for ( int i = 0 ; i < subclass - > subclasses . size ( ) ; i + + ) {
const GDScriptParser : : ClassNode * inner_class = subclass - > subclasses [ i ] ;
if ( inner_class - > name = = extend_classes [ 0 ] ) {
extend_classes . remove ( 0 ) ;
found = true ;
subclass = inner_class ;
break ;
}
}
if ( ! found ) {
subclass = NULL ;
break ;
}
}
}
} else if ( subclass - > extends_class . size ( ) = = 1 ) {
* r_base_type = subclass - > extends_class [ 0 ] ;
subclass = NULL ;
} else {
break ;
2018-05-30 04:16:52 +02:00
}
2019-03-03 20:36:42 +01:00
} else {
2018-05-30 04:16:52 +02:00
* r_base_type = " Reference " ;
2019-03-03 20:36:42 +01:00
subclass = NULL ;
2018-05-30 04:16:52 +02:00
}
}
2018-07-16 00:29:00 +02:00
}
return c - > name ;
}
return String ( ) ;
}
2018-07-01 18:17:40 +02:00
# ifdef DEBUG_ENABLED
String GDScriptWarning : : get_message ( ) const {
# define CHECK_SYMBOLS(m_amount) ERR_FAIL_COND_V(symbols.size() < m_amount, String());
switch ( code ) {
case UNASSIGNED_VARIABLE_OP_ASSIGN : {
CHECK_SYMBOLS ( 1 ) ;
return " Using assignment with operation but the variable ' " + symbols [ 0 ] + " ' was not previously assigned a value. " ;
} break ;
case UNASSIGNED_VARIABLE : {
CHECK_SYMBOLS ( 1 ) ;
return " The variable ' " + symbols [ 0 ] + " ' was used but never assigned a value. " ;
} break ;
case UNUSED_VARIABLE : {
CHECK_SYMBOLS ( 1 ) ;
return " The local variable ' " + symbols [ 0 ] + " ' is declared but never used in the block. " ;
} break ;
2019-04-15 15:51:52 +02:00
case SHADOWED_VARIABLE : {
CHECK_SYMBOLS ( 2 ) ;
return " The local variable ' " + symbols [ 0 ] + " ' is shadowing an already defined variable at line " + symbols [ 1 ] + " . " ;
} break ;
2018-07-01 18:17:40 +02:00
case UNUSED_CLASS_VARIABLE : {
CHECK_SYMBOLS ( 1 ) ;
return " The class variable ' " + symbols [ 0 ] + " ' is declared but never used in the script. " ;
} break ;
case UNUSED_ARGUMENT : {
CHECK_SYMBOLS ( 2 ) ;
return " The argument ' " + symbols [ 1 ] + " ' is never used in the function ' " + symbols [ 0 ] + " '. " ;
} break ;
case UNREACHABLE_CODE : {
CHECK_SYMBOLS ( 1 ) ;
return " Unreachable code (statement after return) in function ' " + symbols [ 0 ] + " ()'. " ;
} break ;
case STANDALONE_EXPRESSION : {
return " Standalone expression (the line has no effect). " ;
} break ;
case VOID_ASSIGNMENT : {
CHECK_SYMBOLS ( 1 ) ;
return " Assignment operation, but the function ' " + symbols [ 0 ] + " ()' returns void. " ;
} break ;
case NARROWING_CONVERSION : {
2019-03-08 14:28:33 +01:00
return " Narrowing conversion (float is converted to int and loses precision). " ;
2018-07-01 18:17:40 +02:00
} break ;
case FUNCTION_MAY_YIELD : {
CHECK_SYMBOLS ( 1 ) ;
return " Assigned variable is typed but the function ' " + symbols [ 0 ] + " ()' may yield and return a GDScriptFunctionState instead. " ;
} break ;
case VARIABLE_CONFLICTS_FUNCTION : {
CHECK_SYMBOLS ( 1 ) ;
return " Variable declaration of ' " + symbols [ 0 ] + " ' conflicts with a function of the same name. " ;
} break ;
case FUNCTION_CONFLICTS_VARIABLE : {
CHECK_SYMBOLS ( 1 ) ;
return " Function declaration of ' " + symbols [ 0 ] + " ()' conflicts with a variable of the same name. " ;
} break ;
case FUNCTION_CONFLICTS_CONSTANT : {
CHECK_SYMBOLS ( 1 ) ;
return " Function declaration of ' " + symbols [ 0 ] + " ()' conflicts with a constant of the same name. " ;
} break ;
case INCOMPATIBLE_TERNARY : {
return " Values of the ternary conditional are not mutually compatible. " ;
} break ;
case UNUSED_SIGNAL : {
CHECK_SYMBOLS ( 1 ) ;
return " The signal ' " + symbols [ 0 ] + " ' is declared but never emitted. " ;
} break ;
case RETURN_VALUE_DISCARDED : {
CHECK_SYMBOLS ( 1 ) ;
return " The function ' " + symbols [ 0 ] + " ()' returns a value, but this value is never used. " ;
} break ;
case PROPERTY_USED_AS_FUNCTION : {
CHECK_SYMBOLS ( 2 ) ;
return " The method ' " + symbols [ 0 ] + " ()' was not found in base ' " + symbols [ 1 ] + " ' but there's a property with the same name. Did you mean to access it? " ;
} break ;
case CONSTANT_USED_AS_FUNCTION : {
CHECK_SYMBOLS ( 2 ) ;
return " The method ' " + symbols [ 0 ] + " ()' was not found in base ' " + symbols [ 1 ] + " ' but there's a constant with the same name. Did you mean to access it? " ;
} break ;
case FUNCTION_USED_AS_PROPERTY : {
CHECK_SYMBOLS ( 2 ) ;
return " The property ' " + symbols [ 0 ] + " ' was not found in base ' " + symbols [ 1 ] + " ' but there's a method with the same name. Did you mean to call it? " ;
} break ;
case INTEGER_DIVISION : {
return " Integer division, decimal part will be discarded. " ;
} break ;
case UNSAFE_PROPERTY_ACCESS : {
CHECK_SYMBOLS ( 2 ) ;
return " The property ' " + symbols [ 0 ] + " ' is not present on the inferred type ' " + symbols [ 1 ] + " ' (but may be present on a subtype). " ;
} break ;
case UNSAFE_METHOD_ACCESS : {
CHECK_SYMBOLS ( 2 ) ;
return " The method ' " + symbols [ 0 ] + " ' is not present on the inferred type ' " + symbols [ 1 ] + " ' (but may be present on a subtype). " ;
} break ;
case UNSAFE_CAST : {
CHECK_SYMBOLS ( 1 ) ;
2018-09-13 03:38:39 +02:00
return " The value is cast to ' " + symbols [ 0 ] + " ' but has an unknown type. " ;
2018-07-01 18:17:40 +02:00
} break ;
case UNSAFE_CALL_ARGUMENT : {
CHECK_SYMBOLS ( 4 ) ;
return " The argument ' " + symbols [ 0 ] + " ' of the function ' " + symbols [ 1 ] + " ' requires a the subtype ' " + symbols [ 2 ] + " ' but the supertype ' " + symbols [ 3 ] + " ' was provided " ;
} break ;
2018-09-15 01:45:43 +02:00
case DEPRECATED_KEYWORD : {
CHECK_SYMBOLS ( 2 ) ;
return " The ' " + symbols [ 0 ] + " ' keyword is deprecated and will be removed in a future release, please replace its uses by ' " + symbols [ 1 ] + " '. " ;
} break ;
2018-09-26 13:13:56 +02:00
case WARNING_MAX : break ; // Can't happen, but silences warning
2018-07-01 18:17:40 +02:00
}
2019-08-09 06:49:33 +02:00
ERR_FAIL_V_MSG ( String ( ) , " Invalid GDScript warning code: " + get_name_from_code ( code ) + " . " ) ;
2018-07-01 18:17:40 +02:00
# undef CHECK_SYMBOLS
}
String GDScriptWarning : : get_name ( ) const {
return get_name_from_code ( code ) ;
}
String GDScriptWarning : : get_name_from_code ( Code p_code ) {
ERR_FAIL_COND_V ( p_code < 0 | | p_code > = WARNING_MAX , String ( ) ) ;
static const char * names [ ] = {
" UNASSIGNED_VARIABLE " ,
" UNASSIGNED_VARIABLE_OP_ASSIGN " ,
" UNUSED_VARIABLE " ,
2019-04-15 15:51:52 +02:00
" SHADOWED_VARIABLE " ,
2018-07-01 18:17:40 +02:00
" UNUSED_CLASS_VARIABLE " ,
" UNUSED_ARGUMENT " ,
" UNREACHABLE_CODE " ,
" STANDALONE_EXPRESSION " ,
" VOID_ASSIGNMENT " ,
" NARROWING_CONVERSION " ,
" FUNCTION_MAY_YIELD " ,
" VARIABLE_CONFLICTS_FUNCTION " ,
" FUNCTION_CONFLICTS_VARIABLE " ,
" FUNCTION_CONFLICTS_CONSTANT " ,
" INCOMPATIBLE_TERNARY " ,
" UNUSED_SIGNAL " ,
" RETURN_VALUE_DISCARDED " ,
" PROPERTY_USED_AS_FUNCTION " ,
" CONSTANT_USED_AS_FUNCTION " ,
" FUNCTION_USED_AS_PROPERTY " ,
" INTEGER_DIVISION " ,
" UNSAFE_PROPERTY_ACCESS " ,
" UNSAFE_METHOD_ACCESS " ,
" UNSAFE_CAST " ,
" UNSAFE_CALL_ARGUMENT " ,
2018-09-15 01:45:43 +02:00
" DEPRECATED_KEYWORD " ,
2018-07-01 18:17:40 +02:00
NULL
} ;
return names [ ( int ) p_code ] ;
}
GDScriptWarning : : Code GDScriptWarning : : get_code_from_name ( const String & p_name ) {
for ( int i = 0 ; i < WARNING_MAX ; i + + ) {
if ( get_name_from_code ( ( Code ) i ) = = p_name ) {
return ( Code ) i ;
}
}
2019-08-09 06:49:33 +02:00
ERR_FAIL_V_MSG ( WARNING_MAX , " Invalid GDScript warning name: " + p_name ) ;
2018-07-01 18:17:40 +02:00
}
# endif // DEBUG_ENABLED
2014-02-10 02:10:30 +01:00
GDScriptLanguage : : GDScriptLanguage ( ) {
2017-03-05 16:44:50 +01:00
calls = 0 ;
2014-02-10 02:10:30 +01:00
ERR_FAIL_COND ( singleton ) ;
2017-03-05 16:44:50 +01:00
singleton = this ;
2014-02-10 02:10:30 +01:00
strings . _init = StaticCString : : create ( " _init " ) ;
strings . _notification = StaticCString : : create ( " _notification " ) ;
2017-03-05 16:44:50 +01:00
strings . _set = StaticCString : : create ( " _set " ) ;
strings . _get = StaticCString : : create ( " _get " ) ;
strings . _get_property_list = StaticCString : : create ( " _get_property_list " ) ;
strings . _script_source = StaticCString : : create ( " script/source " ) ;
_debug_parse_err_line = - 1 ;
_debug_parse_err_file = " " ;
2014-02-10 02:10:30 +01:00
2016-05-22 02:18:16 +02:00
# ifdef NO_THREADS
2017-03-05 16:44:50 +01:00
lock = NULL ;
2016-05-22 02:18:16 +02:00
# else
lock = Mutex : : create ( ) ;
# endif
2017-03-05 16:44:50 +01:00
profiling = false ;
script_frame_time = 0 ;
2016-05-22 02:18:16 +02:00
2017-03-05 16:44:50 +01:00
_debug_call_stack_pos = 0 ;
2017-07-18 02:05:38 +02:00
int dmcs = GLOBAL_DEF ( " debug/settings/gdscript/max_call_stack " , 1024 ) ;
2018-10-05 18:43:53 +02:00
ProjectSettings : : get_singleton ( ) - > set_custom_property_info ( " debug/settings/gdscript/max_call_stack " , PropertyInfo ( Variant : : INT , " debug/settings/gdscript/max_call_stack " , PROPERTY_HINT_RANGE , " 1024,4096,1,or_greater " ) ) ; //minimum is 1024
2016-05-22 02:18:16 +02:00
if ( ScriptDebugger : : get_singleton ( ) ) {
//debugging enabled!
2014-02-10 02:10:30 +01:00
2016-05-22 02:18:16 +02:00
_debug_max_call_stack = dmcs ;
2017-03-05 16:44:50 +01:00
_call_stack = memnew_arr ( CallLevel , _debug_max_call_stack + 1 ) ;
2014-02-10 02:10:30 +01:00
2016-05-22 02:18:16 +02:00
} else {
2017-03-05 16:44:50 +01:00
_debug_max_call_stack = 0 ;
_call_stack = NULL ;
2016-05-22 02:18:16 +02:00
}
2018-07-01 18:17:40 +02:00
# ifdef DEBUG_ENABLED
GLOBAL_DEF ( " debug/gdscript/warnings/enable " , true ) ;
GLOBAL_DEF ( " debug/gdscript/warnings/treat_warnings_as_errors " , false ) ;
2018-10-22 20:32:58 +02:00
GLOBAL_DEF ( " debug/gdscript/completion/autocomplete_setters_and_getters " , false ) ;
2018-07-01 18:17:40 +02:00
for ( int i = 0 ; i < ( int ) GDScriptWarning : : WARNING_MAX ; i + + ) {
String warning = GDScriptWarning : : get_name_from_code ( ( GDScriptWarning : : Code ) i ) . to_lower ( ) ;
GLOBAL_DEF ( " debug/gdscript/warnings/ " + warning , ! warning . begins_with ( " unsafe_ " ) ) ;
}
# endif // DEBUG_ENABLED
2014-02-10 02:10:30 +01:00
}
GDScriptLanguage : : ~ GDScriptLanguage ( ) {
2016-05-22 02:18:16 +02:00
if ( lock ) {
memdelete ( lock ) ;
2017-03-05 16:44:50 +01:00
lock = NULL ;
2016-05-22 02:18:16 +02:00
}
2017-03-05 16:44:50 +01:00
if ( _call_stack ) {
2016-05-22 02:18:16 +02:00
memdelete_arr ( _call_stack ) ;
}
2017-03-05 16:44:50 +01:00
singleton = NULL ;
2014-02-10 02:10:30 +01:00
}
/*************** RESOURCE ***************/
2017-03-05 16:44:50 +01:00
RES ResourceFormatLoaderGDScript : : load ( const String & p_path , const String & p_original_path , Error * r_error ) {
2015-08-24 01:15:56 +02:00
if ( r_error )
2017-03-05 16:44:50 +01:00
* r_error = ERR_FILE_CANT_OPEN ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
GDScript * script = memnew ( GDScript ) ;
2014-02-10 02:10:30 +01:00
Ref < GDScript > scriptres ( script ) ;
2014-06-11 15:41:03 +02:00
if ( p_path . ends_with ( " .gde " ) | | p_path . ends_with ( " .gdc " ) ) {
2014-02-10 02:10:30 +01:00
2014-02-25 13:31:47 +01:00
script - > set_script_path ( p_original_path ) ; // script needs this.
script - > set_path ( p_original_path ) ;
Error err = script - > load_byte_code ( p_path ) ;
2018-09-10 17:24:57 +02:00
ERR_FAIL_COND_V ( err ! = OK , RES ( ) ) ;
2014-02-25 13:31:47 +01:00
} else {
Error err = script - > load_source_code ( p_path ) ;
2018-09-10 17:24:57 +02:00
ERR_FAIL_COND_V ( err ! = OK , RES ( ) ) ;
2014-02-25 13:31:47 +01:00
script - > set_script_path ( p_original_path ) ; // script needs this.
script - > set_path ( p_original_path ) ;
script - > reload ( ) ;
}
2015-08-24 01:15:56 +02:00
if ( r_error )
2017-03-05 16:44:50 +01:00
* r_error = OK ;
2014-02-10 02:10:30 +01:00
return scriptres ;
}
2018-08-26 03:19:02 +02:00
2014-02-10 02:10:30 +01:00
void ResourceFormatLoaderGDScript : : get_recognized_extensions ( List < String > * p_extensions ) const {
p_extensions - > push_back ( " gd " ) ;
2014-02-25 13:31:47 +01:00
p_extensions - > push_back ( " gdc " ) ;
2014-06-11 15:41:03 +02:00
p_extensions - > push_back ( " gde " ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
bool ResourceFormatLoaderGDScript : : handles_type ( const String & p_type ) const {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
return ( p_type = = " Script " | | p_type = = " GDScript " ) ;
2014-02-10 02:10:30 +01:00
}
String ResourceFormatLoaderGDScript : : get_resource_type ( const String & p_path ) const {
2017-01-14 04:51:09 +01:00
String el = p_path . get_extension ( ) . to_lower ( ) ;
2017-03-05 16:44:50 +01:00
if ( el = = " gd " | | el = = " gdc " | | el = = " gde " )
2014-02-10 02:10:30 +01:00
return " GDScript " ;
return " " ;
}
2019-03-03 20:36:42 +01:00
void ResourceFormatLoaderGDScript : : get_dependencies ( const String & p_path , List < String > * p_dependencies , bool p_add_types ) {
2019-03-04 13:52:15 +01:00
FileAccessRef file = FileAccess : : open ( p_path , FileAccess : : READ ) ;
2019-03-03 20:36:42 +01:00
ERR_FAIL_COND ( ! file ) ;
String source = file - > get_as_utf8_string ( ) ;
if ( source . empty ( ) ) {
return ;
}
GDScriptParser parser ;
if ( OK ! = parser . parse ( source , p_path . get_base_dir ( ) , true , p_path , false , NULL , true ) ) {
return ;
}
for ( const List < String > : : Element * E = parser . get_dependencies ( ) . front ( ) ; E ; E = E - > next ( ) ) {
p_dependencies - > push_back ( E - > get ( ) ) ;
}
}
2017-03-05 16:44:50 +01:00
Error ResourceFormatSaverGDScript : : save ( const String & p_path , const RES & p_resource , uint32_t p_flags ) {
2014-02-10 02:10:30 +01:00
Ref < GDScript > sqscr = p_resource ;
2017-03-05 16:44:50 +01:00
ERR_FAIL_COND_V ( sqscr . is_null ( ) , ERR_INVALID_PARAMETER ) ;
2014-02-10 02:10:30 +01:00
String source = sqscr - > get_source_code ( ) ;
Error err ;
2017-03-05 16:44:50 +01:00
FileAccess * file = FileAccess : : open ( p_path , FileAccess : : WRITE , & err ) ;
2014-02-10 02:10:30 +01:00
if ( err ) {
2017-03-05 16:44:50 +01:00
ERR_FAIL_COND_V ( err , err ) ;
2014-02-10 02:10:30 +01:00
}
file - > store_string ( source ) ;
2017-03-05 16:44:50 +01:00
if ( file - > get_error ( ) ! = OK & & file - > get_error ( ) ! = ERR_FILE_EOF ) {
2015-03-02 04:54:10 +01:00
memdelete ( file ) ;
return ERR_CANT_CREATE ;
}
2014-02-10 02:10:30 +01:00
file - > close ( ) ;
memdelete ( file ) ;
2016-06-13 15:58:32 +02:00
if ( ScriptServer : : is_reload_scripts_on_save_enabled ( ) ) {
2017-03-05 16:44:50 +01:00
GDScriptLanguage : : get_singleton ( ) - > reload_tool_script ( p_resource , false ) ;
2016-06-13 15:58:32 +02:00
}
2014-02-10 02:10:30 +01:00
return OK ;
}
2017-03-05 16:44:50 +01:00
void ResourceFormatSaverGDScript : : get_recognized_extensions ( const RES & p_resource , List < String > * p_extensions ) const {
2014-02-10 02:10:30 +01:00
2017-08-24 22:58:51 +02:00
if ( Object : : cast_to < GDScript > ( * p_resource ) ) {
2014-02-10 02:10:30 +01:00
p_extensions - > push_back ( " gd " ) ;
}
}
2017-03-05 16:44:50 +01:00
bool ResourceFormatSaverGDScript : : recognize ( const RES & p_resource ) const {
2014-02-10 02:10:30 +01:00
2017-08-24 22:58:51 +02:00
return Object : : cast_to < GDScript > ( * p_resource ) ! = NULL ;
2014-02-10 02:10:30 +01:00
}