2014-02-10 02:10:30 +01:00
/*************************************************************************/
/* gd_script.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
2015-04-18 19:38:54 +02:00
/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
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. */
/*************************************************************************/
# include "gd_script.h"
# include "globals.h"
# include "global_constants.h"
# include "gd_compiler.h"
# include "os/file_access.h"
2014-06-11 15:41:03 +02:00
# include "io/file_access_encrypted.h"
2014-02-10 02:10:30 +01:00
/* TODO:
* populate globals
* do checks as close to debugger as possible ( but don ' t do debugger )
* const check plz
* check arguments and default arguments in GDFunction
- get property list in instance ?
* missing opcodes
- const checks
- make thread safe
*/
Variant * GDFunction : : _get_variant ( int p_address , GDInstance * p_instance , GDScript * p_script , Variant & self , Variant * p_stack , String & r_error ) const {
int address = p_address & ADDR_MASK ;
//sequential table (jump table generated by compiler)
switch ( ( p_address & ADDR_TYPE_MASK ) > > ADDR_BITS ) {
case ADDR_TYPE_SELF : {
if ( ! p_instance ) {
r_error = " Cannot access self without instance. " ;
return NULL ;
}
return & self ;
} break ;
2014-05-01 16:34:10 +02:00
case ADDR_TYPE_CLASS : {
return & p_script - > _static_ref ;
} break ;
2014-02-10 02:10:30 +01:00
case ADDR_TYPE_MEMBER : {
//member indexing is O(1)
if ( ! p_instance ) {
r_error = " Cannot access member without instance. " ;
return NULL ;
}
return & p_instance - > members [ address ] ;
} break ;
case ADDR_TYPE_CLASS_CONSTANT : {
//todo change to index!
2014-05-02 17:40:34 +02:00
GDScript * o = p_script ;
2014-02-10 02:10:30 +01:00
ERR_FAIL_INDEX_V ( address , _global_names_count , NULL ) ;
const StringName * sn = & _global_names_ptr [ address ] ;
2014-05-02 17:40:34 +02:00
while ( o ) {
GDScript * s = o ;
while ( s ) {
Map < StringName , Variant > : : Element * E = s - > constants . find ( * sn ) ;
if ( E ) {
return & E - > get ( ) ;
}
s = s - > _base ;
2014-02-10 02:10:30 +01:00
}
2014-05-02 17:40:34 +02:00
o = o - > _owner ;
2014-02-10 02:10:30 +01:00
}
ERR_EXPLAIN ( " GDCompiler bug.. " ) ;
ERR_FAIL_V ( NULL ) ;
} break ;
case ADDR_TYPE_LOCAL_CONSTANT : {
ERR_FAIL_INDEX_V ( address , _constant_count , NULL ) ;
return & _constants_ptr [ address ] ;
} break ;
case ADDR_TYPE_STACK :
case ADDR_TYPE_STACK_VARIABLE : {
ERR_FAIL_INDEX_V ( address , _stack_size , NULL ) ;
return & p_stack [ address ] ;
} break ;
case ADDR_TYPE_GLOBAL : {
ERR_FAIL_INDEX_V ( address , GDScriptLanguage : : get_singleton ( ) - > get_global_array_size ( ) , NULL ) ;
return & GDScriptLanguage : : get_singleton ( ) - > get_global_array ( ) [ address ] ;
} break ;
case ADDR_TYPE_NIL : {
return & nil ;
} break ;
}
ERR_EXPLAIN ( " Bad Code! (Addressing Mode) " ) ;
ERR_FAIL_V ( NULL ) ;
return NULL ;
}
String GDFunction : : _get_call_error ( const Variant : : CallError & p_err , const String & p_where , const Variant * * argptrs ) const {
String err_text ;
if ( p_err . error = = Variant : : CallError : : CALL_ERROR_INVALID_ARGUMENT ) {
int errorarg = p_err . argument ;
err_text = " Invalid type in " + p_where + " . Cannot convert argument " + itos ( errorarg + 1 ) + " from " + Variant : : get_type_name ( argptrs [ errorarg ] - > get_type ( ) ) + " to " + Variant : : get_type_name ( p_err . expected ) + " . " ;
} else if ( p_err . error = = Variant : : CallError : : CALL_ERROR_TOO_MANY_ARGUMENTS ) {
err_text = " Invalid call to " + p_where + " . Expected " + itos ( p_err . argument ) + " arguments. " ;
} else if ( p_err . error = = Variant : : CallError : : CALL_ERROR_TOO_FEW_ARGUMENTS ) {
err_text = " Invalid call to " + p_where + " . Expected " + itos ( p_err . argument ) + " arguments. " ;
} else if ( p_err . error = = Variant : : CallError : : CALL_ERROR_INVALID_METHOD ) {
2015-05-18 15:20:54 +02:00
err_text = " Invalid call. Nonexistent " + p_where + " . " ;
2014-02-10 02:10:30 +01:00
} else if ( p_err . error = = Variant : : CallError : : CALL_ERROR_INSTANCE_IS_NULL ) {
err_text = " Attempt to call " + p_where + " on a null instance. " ;
} else {
err_text = " Bug, call error: # " + itos ( p_err . error ) ;
}
return err_text ;
}
static String _get_var_type ( const Variant * p_type ) {
String basestr ;
if ( p_type - > get_type ( ) = = Variant : : OBJECT ) {
Object * bobj = * p_type ;
if ( ! bobj ) {
basestr = " null instance " ;
} else {
# ifdef DEBUG_ENABLED
if ( ObjectDB : : instance_validate ( bobj ) ) {
if ( bobj - > get_script_instance ( ) )
basestr = bobj - > get_type ( ) + " ( " + bobj - > get_script_instance ( ) - > get_script ( ) - > get_path ( ) . get_file ( ) + " ) " ;
else
basestr = bobj - > get_type ( ) ;
} else {
basestr = " previously freed instance " ;
}
# else
basestr = " Object " ;
# endif
}
} else {
basestr = Variant : : get_type_name ( p_type - > get_type ( ) ) ;
}
return basestr ;
}
2014-09-15 16:33:30 +02:00
Variant GDFunction : : call ( GDInstance * p_instance , const Variant * * p_args , int p_argcount , Variant : : CallError & r_err , CallState * p_state ) {
2014-02-10 02:10:30 +01:00
if ( ! _code_ptr ) {
return Variant ( ) ;
}
r_err . error = Variant : : CallError : : CALL_OK ;
Variant self ;
Variant retvalue ;
Variant * stack = NULL ;
Variant * * call_args ;
int defarg = 0 ;
# ifdef DEBUG_ENABLED
//GDScriptLanguage::get_singleton()->calls++;
# endif
2014-09-15 16:33:30 +02:00
uint32_t alloca_size = 0 ;
GDScript * _class ;
int ip = 0 ;
int line = _initial_line ;
2014-02-10 02:10:30 +01:00
2014-09-15 16:33:30 +02:00
if ( p_state ) {
//use existing (supplied) state (yielded)
stack = ( Variant * ) p_state - > stack . ptr ( ) ;
call_args = ( Variant * * ) & p_state - > stack [ sizeof ( Variant ) * p_state - > stack_size ] ;
line = p_state - > line ;
ip = p_state - > ip ;
alloca_size = p_state - > stack . size ( ) ;
_class = p_state - > _class ;
p_instance = p_state - > instance ;
defarg = p_state - > defarg ;
self = p_state - > self ;
//stack[p_state->result_pos]=p_state->result; //assign stack with result
2014-02-10 02:10:30 +01:00
2014-09-15 16:33:30 +02:00
} else {
2014-02-10 02:10:30 +01:00
2014-09-15 16:33:30 +02:00
if ( p_argcount ! = _argument_count ) {
2014-02-10 02:10:30 +01:00
2014-09-15 16:33:30 +02:00
if ( p_argcount > _argument_count ) {
2014-02-10 02:10:30 +01:00
2014-09-15 16:33:30 +02:00
r_err . error = Variant : : CallError : : CALL_ERROR_TOO_MANY_ARGUMENTS ;
r_err . argument = _argument_count ;
2014-02-10 02:10:30 +01:00
2014-09-15 16:33:30 +02:00
return Variant ( ) ;
} else if ( p_argcount < _argument_count - _default_arg_count ) {
2014-02-10 02:10:30 +01:00
2014-09-15 16:33:30 +02:00
r_err . error = Variant : : CallError : : CALL_ERROR_TOO_FEW_ARGUMENTS ;
r_err . argument = _argument_count - _default_arg_count ;
return Variant ( ) ;
} else {
2014-02-10 02:10:30 +01:00
2014-09-15 16:33:30 +02:00
defarg = _argument_count - p_argcount ;
}
}
2014-02-10 02:10:30 +01:00
2014-09-15 16:33:30 +02:00
alloca_size = sizeof ( Variant * ) * _call_size + sizeof ( Variant ) * _stack_size ;
2014-02-10 02:10:30 +01:00
2014-09-15 16:33:30 +02:00
if ( alloca_size ) {
2014-02-10 02:10:30 +01:00
2014-09-15 16:33:30 +02:00
uint8_t * aptr = ( uint8_t * ) alloca ( alloca_size ) ;
2014-02-10 02:10:30 +01:00
2014-09-15 16:33:30 +02:00
if ( _stack_size ) {
2014-02-10 02:10:30 +01:00
2014-09-15 16:33:30 +02:00
stack = ( Variant * ) aptr ;
for ( int i = 0 ; i < p_argcount ; i + + )
memnew_placement ( & stack [ i ] , Variant ( * p_args [ i ] ) ) ;
for ( int i = p_argcount ; i < _stack_size ; i + + )
memnew_placement ( & stack [ i ] , Variant ) ;
} else {
stack = NULL ;
}
2014-02-10 02:10:30 +01:00
2014-09-15 16:33:30 +02:00
if ( _call_size ) {
2014-02-10 02:10:30 +01:00
2014-09-15 16:33:30 +02:00
call_args = ( Variant * * ) & aptr [ sizeof ( Variant ) * _stack_size ] ;
} else {
call_args = NULL ;
}
2014-02-10 02:10:30 +01:00
2014-09-15 16:33:30 +02:00
} else {
stack = NULL ;
call_args = NULL ;
}
2014-02-10 02:10:30 +01:00
2014-09-15 16:33:30 +02:00
if ( p_instance ) {
if ( p_instance - > base_ref & & static_cast < Reference * > ( p_instance - > owner ) - > is_referenced ( ) ) {
2014-02-10 02:10:30 +01:00
2014-09-15 16:33:30 +02:00
self = REF ( static_cast < Reference * > ( p_instance - > owner ) ) ;
} else {
self = p_instance - > owner ;
}
_class = p_instance - > script . ptr ( ) ;
2014-02-10 02:10:30 +01:00
} else {
2014-09-15 16:33:30 +02:00
_class = _script ;
2014-02-10 02:10:30 +01:00
}
}
String err_text ;
# ifdef DEBUG_ENABLED
if ( ScriptDebugger : : get_singleton ( ) )
GDScriptLanguage : : get_singleton ( ) - > enter_function ( p_instance , this , stack , & ip , & line ) ;
# define CHECK_SPACE(m_space)\
ERR_BREAK ( ( ip + m_space ) > _code_size )
# define GET_VARIANT_PTR(m_v,m_code_ofs) \
Variant * m_v ; \
m_v = _get_variant ( _code_ptr [ ip + m_code_ofs ] , p_instance , _class , self , stack , err_text ) ; \
if ( ! m_v ) \
break ;
# else
# define CHECK_SPACE(m_space)
# define GET_VARIANT_PTR(m_v,m_code_ofs) \
Variant * m_v ; \
m_v = _get_variant ( _code_ptr [ ip + m_code_ofs ] , p_instance , _class , self , stack , err_text ) ;
# endif
bool exit_ok = false ;
while ( ip < _code_size ) {
int last_opcode = _code_ptr [ ip ] ;
switch ( _code_ptr [ ip ] ) {
case OPCODE_OPERATOR : {
CHECK_SPACE ( 5 ) ;
bool valid ;
Variant : : Operator op = ( Variant : : Operator ) _code_ptr [ ip + 1 ] ;
ERR_BREAK ( op > = Variant : : OP_MAX ) ;
GET_VARIANT_PTR ( a , 2 ) ;
GET_VARIANT_PTR ( b , 3 ) ;
GET_VARIANT_PTR ( dst , 4 ) ;
2015-05-16 21:32:46 +02:00
# ifdef DEBUG_ENABLED
Variant ret ;
Variant : : evaluate ( op , * a , * b , ret , valid ) ;
# else
2014-02-10 02:10:30 +01:00
Variant : : evaluate ( op , * a , * b , * dst , valid ) ;
2015-05-16 21:32:46 +02:00
# endif
2014-02-10 02:10:30 +01:00
if ( ! valid ) {
2015-05-17 18:11:55 +02:00
# ifdef DEBUG_ENABLED
2015-05-16 21:32:46 +02:00
if ( ret . get_type ( ) = = Variant : : STRING ) {
2014-02-10 02:10:30 +01:00
//return a string when invalid with the error
2015-05-16 21:32:46 +02:00
err_text = ret ;
2015-02-19 17:59:37 +01:00
err_text + = " in operator ' " + Variant : : get_operator_name ( op ) + " '. " ;
2014-02-10 02:10:30 +01:00
} else {
err_text = " Invalid operands ' " + Variant : : get_type_name ( a - > get_type ( ) ) + " ' and ' " + Variant : : get_type_name ( b - > get_type ( ) ) + " ' in operator ' " + Variant : : get_operator_name ( op ) + " '. " ;
}
2015-05-17 18:11:55 +02:00
# endif
2014-02-10 02:10:30 +01:00
break ;
2015-05-17 18:11:55 +02:00
2014-02-10 02:10:30 +01:00
}
2015-05-16 21:32:46 +02:00
# ifdef DEBUG_ENABLED
* dst = ret ;
# endif
2014-02-10 02:10:30 +01:00
ip + = 5 ;
} continue ;
case OPCODE_EXTENDS_TEST : {
CHECK_SPACE ( 4 ) ;
GET_VARIANT_PTR ( a , 1 ) ;
GET_VARIANT_PTR ( b , 2 ) ;
GET_VARIANT_PTR ( dst , 3 ) ;
# ifdef DEBUG_ENABLED
if ( a - > get_type ( ) ! = Variant : : OBJECT | | a - > operator Object * ( ) = = NULL ) {
err_text = " Left operand of 'extends' is not an instance of anything. " ;
break ;
}
if ( b - > get_type ( ) ! = Variant : : OBJECT | | b - > operator Object * ( ) = = NULL ) {
err_text = " Right operand of 'extends' is not a class. " ;
break ;
}
# endif
Object * obj_A = * a ;
Object * obj_B = * b ;
GDScript * scr_B = obj_B - > cast_to < GDScript > ( ) ;
bool extends_ok = false ;
if ( scr_B ) {
//if B is a script, the only valid condition is that A has an instance which inherits from the script
//in other situation, this shoul return false.
if ( obj_A - > get_script_instance ( ) & & obj_A - > get_script_instance ( ) - > get_language ( ) = = GDScriptLanguage : : get_singleton ( ) ) {
GDInstance * ins = static_cast < GDInstance * > ( obj_A - > get_script_instance ( ) ) ;
GDScript * cmp = ins - > script . ptr ( ) ;
//bool found=false;
while ( cmp ) {
if ( cmp = = scr_B ) {
//inherits from script, all ok
extends_ok = true ;
break ;
}
cmp = cmp - > _base ;
}
}
2014-05-24 18:25:56 +02:00
2014-02-10 02:10:30 +01:00
} else {
GDNativeClass * nc = obj_B - > cast_to < GDNativeClass > ( ) ;
if ( ! nc ) {
err_text = " Right operand of 'extends' is not a class (type: ' " + obj_B - > get_type ( ) + " '). " ;
break ;
}
extends_ok = ObjectTypeDB : : is_type ( obj_A - > get_type_name ( ) , nc - > get_name ( ) ) ;
}
* dst = extends_ok ;
ip + = 4 ;
} continue ;
case OPCODE_SET : {
CHECK_SPACE ( 3 ) ;
GET_VARIANT_PTR ( dst , 1 ) ;
GET_VARIANT_PTR ( index , 2 ) ;
GET_VARIANT_PTR ( value , 3 ) ;
bool valid ;
dst - > set ( * index , * value , & valid ) ;
if ( ! valid ) {
String v = index - > operator String ( ) ;
if ( v ! = " " ) {
v = " ' " + v + " ' " ;
} else {
v = " of type ' " + _get_var_type ( index ) + " ' " ;
}
err_text = " Invalid set index " + v + " (on base: ' " + _get_var_type ( dst ) + " '). " ;
break ;
}
ip + = 4 ;
} continue ;
case OPCODE_GET : {
CHECK_SPACE ( 3 ) ;
GET_VARIANT_PTR ( src , 1 ) ;
GET_VARIANT_PTR ( index , 2 ) ;
GET_VARIANT_PTR ( dst , 3 ) ;
bool valid ;
2015-05-16 21:32:46 +02:00
# ifdef DEBUG_ENABLED
//allow better error message in cases where src and dst are the same stack position
Variant ret = src - > get ( * index , & valid ) ;
# else
2014-02-10 02:10:30 +01:00
* dst = src - > get ( * index , & valid ) ;
2015-05-16 21:32:46 +02:00
# endif
2014-02-10 02:10:30 +01:00
if ( ! valid ) {
String v = index - > operator String ( ) ;
if ( v ! = " " ) {
v = " ' " + v + " ' " ;
} else {
v = " of type ' " + _get_var_type ( index ) + " ' " ;
}
err_text = " Invalid get index " + v + " (on base: ' " + _get_var_type ( src ) + " '). " ;
break ;
}
2015-05-16 21:32:46 +02:00
# ifdef DEBUG_ENABLED
* dst = ret ;
# endif
2014-02-10 02:10:30 +01:00
ip + = 4 ;
} continue ;
case OPCODE_SET_NAMED : {
CHECK_SPACE ( 3 ) ;
GET_VARIANT_PTR ( dst , 1 ) ;
GET_VARIANT_PTR ( value , 3 ) ;
int indexname = _code_ptr [ ip + 2 ] ;
ERR_BREAK ( indexname < 0 | | indexname > = _global_names_count ) ;
const StringName * index = & _global_names_ptr [ indexname ] ;
bool valid ;
dst - > set_named ( * index , * value , & valid ) ;
if ( ! valid ) {
String err_type ;
err_text = " Invalid set index ' " + String ( * index ) + " ' (on base: ' " + _get_var_type ( dst ) + " '). " ;
break ;
}
ip + = 4 ;
} continue ;
case OPCODE_GET_NAMED : {
CHECK_SPACE ( 3 ) ;
GET_VARIANT_PTR ( src , 1 ) ;
GET_VARIANT_PTR ( dst , 3 ) ;
int indexname = _code_ptr [ ip + 2 ] ;
ERR_BREAK ( indexname < 0 | | indexname > = _global_names_count ) ;
const StringName * index = & _global_names_ptr [ indexname ] ;
bool valid ;
2015-05-16 21:32:46 +02:00
# ifdef DEBUG_ENABLED
//allow better error message in cases where src and dst are the same stack position
Variant ret = src - > get_named ( * index , & valid ) ;
# else
2014-02-10 02:10:30 +01:00
* dst = src - > get_named ( * index , & valid ) ;
2015-05-16 21:32:46 +02:00
# endif
2014-02-10 02:10:30 +01:00
if ( ! valid ) {
2014-09-20 02:01:41 +02:00
if ( src - > has_method ( * index ) ) {
err_text = " Invalid get index ' " + index - > operator String ( ) + " ' (on base: ' " + _get_var_type ( src ) + " '). Did you mean '. " + index - > operator String ( ) + " ()' ? " ;
} else {
err_text = " Invalid get index ' " + index - > operator String ( ) + " ' (on base: ' " + _get_var_type ( src ) + " '). " ;
}
2014-02-10 02:10:30 +01:00
break ;
}
2015-05-16 21:32:46 +02:00
# ifdef DEBUG_ENABLED
* dst = ret ;
# endif
2014-02-10 02:10:30 +01:00
ip + = 4 ;
} continue ;
case OPCODE_ASSIGN : {
CHECK_SPACE ( 3 ) ;
GET_VARIANT_PTR ( dst , 1 ) ;
GET_VARIANT_PTR ( src , 2 ) ;
* dst = * src ;
ip + = 3 ;
} continue ;
case OPCODE_ASSIGN_TRUE : {
CHECK_SPACE ( 2 ) ;
GET_VARIANT_PTR ( dst , 1 ) ;
* dst = true ;
ip + = 2 ;
} continue ;
case OPCODE_ASSIGN_FALSE : {
CHECK_SPACE ( 2 ) ;
GET_VARIANT_PTR ( dst , 1 ) ;
* dst = false ;
ip + = 2 ;
} continue ;
case OPCODE_CONSTRUCT : {
CHECK_SPACE ( 2 ) ;
Variant : : Type t = Variant : : Type ( _code_ptr [ ip + 1 ] ) ;
int argc = _code_ptr [ ip + 2 ] ;
CHECK_SPACE ( argc + 2 ) ;
Variant * * argptrs = call_args ;
for ( int i = 0 ; i < argc ; i + + ) {
GET_VARIANT_PTR ( v , 3 + i ) ;
argptrs [ i ] = v ;
}
GET_VARIANT_PTR ( dst , 3 + argc ) ;
Variant : : CallError err ;
* dst = Variant : : construct ( t , ( const Variant * * ) argptrs , argc , err ) ;
if ( err . error ! = Variant : : CallError : : CALL_OK ) {
err_text = _get_call_error ( err , " ' " + Variant : : get_type_name ( t ) + " ' constructor " , ( const Variant * * ) argptrs ) ;
break ;
}
ip + = 4 + argc ;
//construct a basic type
} continue ;
case OPCODE_CONSTRUCT_ARRAY : {
CHECK_SPACE ( 1 ) ;
int argc = _code_ptr [ ip + 1 ] ;
Array array ( true ) ; //arrays are always shared
array . resize ( argc ) ;
CHECK_SPACE ( argc + 2 ) ;
for ( int i = 0 ; i < argc ; i + + ) {
GET_VARIANT_PTR ( v , 2 + i ) ;
array [ i ] = * v ;
}
GET_VARIANT_PTR ( dst , 2 + argc ) ;
* dst = array ;
ip + = 3 + argc ;
} continue ;
case OPCODE_CONSTRUCT_DICTIONARY : {
CHECK_SPACE ( 1 ) ;
int argc = _code_ptr [ ip + 1 ] ;
Dictionary dict ( true ) ; //arrays are always shared
CHECK_SPACE ( argc * 2 + 2 ) ;
for ( int i = 0 ; i < argc ; i + + ) {
GET_VARIANT_PTR ( k , 2 + i * 2 + 0 ) ;
GET_VARIANT_PTR ( v , 2 + i * 2 + 1 ) ;
dict [ * k ] = * v ;
}
GET_VARIANT_PTR ( dst , 2 + argc * 2 ) ;
* dst = dict ;
ip + = 3 + argc * 2 ;
} continue ;
case OPCODE_CALL_RETURN :
case OPCODE_CALL : {
CHECK_SPACE ( 4 ) ;
bool call_ret = _code_ptr [ ip ] = = OPCODE_CALL_RETURN ;
int argc = _code_ptr [ ip + 1 ] ;
GET_VARIANT_PTR ( base , 2 ) ;
int nameg = _code_ptr [ ip + 3 ] ;
ERR_BREAK ( nameg < 0 | | nameg > = _global_names_count ) ;
const StringName * methodname = & _global_names_ptr [ nameg ] ;
ERR_BREAK ( argc < 0 ) ;
ip + = 4 ;
CHECK_SPACE ( argc + 1 ) ;
Variant * * argptrs = call_args ;
for ( int i = 0 ; i < argc ; i + + ) {
GET_VARIANT_PTR ( v , i ) ;
argptrs [ i ] = v ;
}
Variant : : CallError err ;
if ( call_ret ) {
GET_VARIANT_PTR ( ret , argc ) ;
* ret = base - > call ( * methodname , ( const Variant * * ) argptrs , argc , err ) ;
} else {
base - > call ( * methodname , ( const Variant * * ) argptrs , argc , err ) ;
}
if ( err . error ! = Variant : : CallError : : CALL_OK ) {
String methodstr = * methodname ;
String basestr = _get_var_type ( base ) ;
if ( methodstr = = " call " ) {
if ( argc > = 1 ) {
methodstr = String ( * argptrs [ 0 ] ) + " (via call) " ;
if ( err . error = = Variant : : CallError : : CALL_ERROR_INVALID_ARGUMENT ) {
err . argument - = 1 ;
}
}
2014-04-05 17:39:30 +02:00
} if ( methodstr = = " free " ) {
if ( err . error = = Variant : : CallError : : CALL_ERROR_INVALID_METHOD ) {
if ( base - > is_ref ( ) ) {
err_text = " Attempted to free a reference. " ;
break ;
} else if ( base - > get_type ( ) = = Variant : : OBJECT ) {
err_text = " Attempted to free a locked object (calling or emitting). " ;
break ;
}
}
2014-02-10 02:10:30 +01:00
}
err_text = _get_call_error ( err , " function ' " + methodstr + " ' in base ' " + basestr + " ' " , ( const Variant * * ) argptrs ) ;
break ;
}
//_call_func(NULL,base,*methodname,ip,argc,p_instance,stack);
ip + = argc + 1 ;
} continue ;
case OPCODE_CALL_BUILT_IN : {
CHECK_SPACE ( 4 ) ;
GDFunctions : : Function func = GDFunctions : : Function ( _code_ptr [ ip + 1 ] ) ;
int argc = _code_ptr [ ip + 2 ] ;
ERR_BREAK ( argc < 0 ) ;
ip + = 3 ;
CHECK_SPACE ( argc + 1 ) ;
Variant * * argptrs = call_args ;
for ( int i = 0 ; i < argc ; i + + ) {
GET_VARIANT_PTR ( v , i ) ;
argptrs [ i ] = v ;
}
GET_VARIANT_PTR ( dst , argc ) ;
Variant : : CallError err ;
GDFunctions : : call ( func , ( const Variant * * ) argptrs , argc , * dst , err ) ;
if ( err . error ! = Variant : : CallError : : CALL_OK ) {
String methodstr = GDFunctions : : get_func_name ( func ) ;
err_text = _get_call_error ( err , " built-in function ' " + methodstr + " ' " , ( const Variant * * ) argptrs ) ;
break ;
}
ip + = argc + 1 ;
} continue ;
case OPCODE_CALL_SELF : {
} break ;
case OPCODE_CALL_SELF_BASE : {
CHECK_SPACE ( 2 ) ;
int self_fun = _code_ptr [ ip + 1 ] ;
# ifdef DEBUG_ENABLED
if ( self_fun < 0 | | self_fun > = _global_names_count ) {
err_text = " compiler bug, function name not found " ;
break ;
}
# endif
const StringName * methodname = & _global_names_ptr [ self_fun ] ;
int argc = _code_ptr [ ip + 2 ] ;
CHECK_SPACE ( 2 + argc + 1 ) ;
Variant * * argptrs = call_args ;
for ( int i = 0 ; i < argc ; i + + ) {
GET_VARIANT_PTR ( v , i + 3 ) ;
argptrs [ i ] = v ;
}
GET_VARIANT_PTR ( dst , argc + 3 ) ;
const GDScript * gds = _script ;
const Map < StringName , GDFunction > : : Element * E = NULL ;
while ( gds - > base . ptr ( ) ) {
gds = gds - > base . ptr ( ) ;
E = gds - > member_functions . find ( * methodname ) ;
if ( E )
break ;
}
Variant : : CallError err ;
if ( E ) {
* dst = ( ( GDFunction * ) & E - > get ( ) ) - > call ( p_instance , ( const Variant * * ) argptrs , argc , err ) ;
} else if ( gds - > native . ptr ( ) ) {
if ( * methodname ! = GDScriptLanguage : : get_singleton ( ) - > strings . _init ) {
MethodBind * mb = ObjectTypeDB : : get_method ( gds - > native - > get_name ( ) , * methodname ) ;
if ( ! mb ) {
err . error = Variant : : CallError : : CALL_ERROR_INVALID_METHOD ;
} else {
* dst = mb - > call ( p_instance - > owner , ( const Variant * * ) argptrs , argc , err ) ;
}
} else {
err . error = Variant : : CallError : : CALL_OK ;
}
} else {
if ( * methodname ! = GDScriptLanguage : : get_singleton ( ) - > strings . _init ) {
err . error = Variant : : CallError : : CALL_ERROR_INVALID_METHOD ;
} else {
err . error = Variant : : CallError : : CALL_OK ;
}
}
if ( err . error ! = Variant : : CallError : : CALL_OK ) {
String methodstr = * methodname ;
err_text = _get_call_error ( err , " function ' " + methodstr + " ' " , ( const Variant * * ) argptrs ) ;
break ;
}
ip + = 4 + argc ;
2014-09-15 16:33:30 +02:00
} continue ;
case OPCODE_YIELD :
case OPCODE_YIELD_SIGNAL : {
int ipofs = 1 ;
if ( _code_ptr [ ip ] = = OPCODE_YIELD_SIGNAL ) {
CHECK_SPACE ( 4 ) ;
ipofs + = 2 ;
} else {
CHECK_SPACE ( 2 ) ;
}
Ref < GDFunctionState > gdfs = memnew ( GDFunctionState ) ;
gdfs - > function = this ;
gdfs - > state . stack . resize ( alloca_size ) ;
//copy variant stack
for ( int i = 0 ; i < _stack_size ; i + + ) {
2015-01-06 04:39:35 +01:00
memnew_placement ( & gdfs - > state . stack [ sizeof ( Variant ) * i ] , Variant ( stack [ i ] ) ) ;
2014-09-15 16:33:30 +02:00
}
gdfs - > state . stack_size = _stack_size ;
gdfs - > state . self = self ;
gdfs - > state . alloca_size = alloca_size ;
gdfs - > state . _class = _class ;
gdfs - > state . ip = ip + ipofs ;
gdfs - > state . line = line ;
//gdfs->state.result_pos=ip+ipofs-1;
gdfs - > state . defarg = defarg ;
gdfs - > state . instance = p_instance ;
gdfs - > function = this ;
retvalue = gdfs ;
if ( _code_ptr [ ip ] = = OPCODE_YIELD_SIGNAL ) {
GET_VARIANT_PTR ( argobj , 1 ) ;
GET_VARIANT_PTR ( argname , 2 ) ;
//do the oneshot connect
if ( argobj - > get_type ( ) ! = Variant : : OBJECT ) {
err_text = " First argument of yield() not of type object. " ;
break ;
}
if ( argname - > get_type ( ) ! = Variant : : STRING ) {
err_text = " Second argument of yield() not a string (for signal name). " ;
break ;
}
Object * obj = argobj - > operator Object * ( ) ;
String signal = argname - > operator String ( ) ;
# ifdef DEBUG_ENABLED
if ( ! obj ) {
err_text = " First argument of yield() is null. " ;
break ;
}
if ( ScriptDebugger : : get_singleton ( ) ) {
if ( ! ObjectDB : : instance_validate ( obj ) ) {
err_text = " First argument of yield() is a previously freed instance. " ;
break ;
}
}
if ( signal . length ( ) = = 0 ) {
err_text = " Second argument of yield() is an empty string (for signal name). " ;
break ;
}
# endif
Error err = obj - > connect ( signal , gdfs . ptr ( ) , " _signal_callback " , varray ( gdfs ) , Object : : CONNECT_ONESHOT ) ;
if ( err ! = OK ) {
err_text = " Error connecting to signal: " + signal + " during yield(). " ;
break ;
}
}
exit_ok = true ;
} break ;
case OPCODE_YIELD_RESUME : {
CHECK_SPACE ( 2 ) ;
if ( ! p_state ) {
err_text = ( " Invalid Resume (bug?) " ) ;
break ;
}
GET_VARIANT_PTR ( result , 1 ) ;
* result = p_state - > result ;
ip + = 2 ;
2014-02-10 02:10:30 +01:00
} continue ;
case OPCODE_JUMP : {
CHECK_SPACE ( 2 ) ;
int to = _code_ptr [ ip + 1 ] ;
ERR_BREAK ( to < 0 | | to > _code_size ) ;
ip = to ;
} continue ;
case OPCODE_JUMP_IF : {
CHECK_SPACE ( 3 ) ;
GET_VARIANT_PTR ( test , 1 ) ;
bool valid ;
bool result = test - > booleanize ( valid ) ;
# ifdef DEBUG_ENABLED
if ( ! valid ) {
err_text = " cannot evaluate conditional expression of type: " + Variant : : get_type_name ( test - > get_type ( ) ) ;
break ;
}
# endif
if ( result ) {
int to = _code_ptr [ ip + 2 ] ;
ERR_BREAK ( to < 0 | | to > _code_size ) ;
ip = to ;
continue ;
}
ip + = 3 ;
} continue ;
case OPCODE_JUMP_IF_NOT : {
CHECK_SPACE ( 3 ) ;
GET_VARIANT_PTR ( test , 1 ) ;
bool valid ;
bool result = test - > booleanize ( valid ) ;
# ifdef DEBUG_ENABLED
if ( ! valid ) {
err_text = " cannot evaluate conditional expression of type: " + Variant : : get_type_name ( test - > get_type ( ) ) ;
break ;
}
# endif
if ( ! result ) {
int to = _code_ptr [ ip + 2 ] ;
ERR_BREAK ( to < 0 | | to > _code_size ) ;
ip = to ;
continue ;
}
ip + = 3 ;
} continue ;
case OPCODE_JUMP_TO_DEF_ARGUMENT : {
CHECK_SPACE ( 2 ) ;
ip = _default_arg_ptr [ defarg ] ;
} continue ;
case OPCODE_RETURN : {
CHECK_SPACE ( 2 ) ;
GET_VARIANT_PTR ( r , 1 ) ;
retvalue = * r ;
exit_ok = true ;
} break ;
case OPCODE_ITERATE_BEGIN : {
CHECK_SPACE ( 8 ) ; //space for this an regular iterate
GET_VARIANT_PTR ( counter , 1 ) ;
GET_VARIANT_PTR ( container , 2 ) ;
bool valid ;
if ( ! container - > iter_init ( * counter , valid ) ) {
if ( ! valid ) {
err_text = " Unable to iterate on object of type " + Variant : : get_type_name ( container - > get_type ( ) ) + " '. " ;
break ;
}
int jumpto = _code_ptr [ ip + 3 ] ;
ERR_BREAK ( jumpto < 0 | | jumpto > _code_size ) ;
ip = jumpto ;
continue ;
}
GET_VARIANT_PTR ( iterator , 4 ) ;
* iterator = container - > iter_get ( * counter , valid ) ;
if ( ! valid ) {
err_text = " Unable to obtain iterator object of type " + Variant : : get_type_name ( container - > get_type ( ) ) + " '. " ;
break ;
}
ip + = 5 ; //skip regular iterate which is always next
} continue ;
case OPCODE_ITERATE : {
CHECK_SPACE ( 4 ) ;
GET_VARIANT_PTR ( counter , 1 ) ;
GET_VARIANT_PTR ( container , 2 ) ;
bool valid ;
if ( ! container - > iter_next ( * counter , valid ) ) {
if ( ! valid ) {
err_text = " Unable to iterate on object of type " + Variant : : get_type_name ( container - > get_type ( ) ) + " ' (type changed since first iteration?). " ;
break ;
}
int jumpto = _code_ptr [ ip + 3 ] ;
ERR_BREAK ( jumpto < 0 | | jumpto > _code_size ) ;
ip = jumpto ;
continue ;
}
GET_VARIANT_PTR ( iterator , 4 ) ;
* iterator = container - > iter_get ( * counter , valid ) ;
if ( ! valid ) {
err_text = " Unable to obtain iterator object of type " + Variant : : get_type_name ( container - > get_type ( ) ) + " ' (but was obtained on first iteration?). " ;
break ;
}
ip + = 5 ; //loop again
} continue ;
case OPCODE_ASSERT : {
CHECK_SPACE ( 2 ) ;
GET_VARIANT_PTR ( test , 1 ) ;
# ifdef DEBUG_ENABLED
bool valid ;
bool result = test - > booleanize ( valid ) ;
if ( ! valid ) {
err_text = " cannot evaluate conditional expression of type: " + Variant : : get_type_name ( test - > get_type ( ) ) ;
break ;
}
if ( ! result ) {
err_text = " Assertion failed. " ;
break ;
}
# endif
ip + = 2 ;
} continue ;
2015-12-29 16:11:21 +01:00
case OPCODE_BREAKPOINT : {
# ifdef DEBUG_ENABLED
if ( ScriptDebugger : : get_singleton ( ) ) {
GDScriptLanguage : : get_singleton ( ) - > debug_break ( " Breakpoint Statement " , true ) ;
}
# endif
ip + = 1 ;
} continue ;
2014-02-10 02:10:30 +01:00
case OPCODE_LINE : {
CHECK_SPACE ( 2 ) ;
line = _code_ptr [ ip + 1 ] ;
ip + = 2 ;
if ( ScriptDebugger : : get_singleton ( ) ) {
// line
bool do_break = false ;
if ( ScriptDebugger : : get_singleton ( ) - > get_lines_left ( ) > 0 ) {
if ( ScriptDebugger : : get_singleton ( ) - > get_depth ( ) < = 0 )
ScriptDebugger : : get_singleton ( ) - > set_lines_left ( ScriptDebugger : : get_singleton ( ) - > get_lines_left ( ) - 1 ) ;
if ( ScriptDebugger : : get_singleton ( ) - > get_lines_left ( ) < = 0 )
do_break = true ;
}
if ( ScriptDebugger : : get_singleton ( ) - > is_breakpoint ( line , source ) )
do_break = true ;
if ( do_break ) {
GDScriptLanguage : : get_singleton ( ) - > debug_break ( " Breakpoint " , true ) ;
}
ScriptDebugger : : get_singleton ( ) - > line_poll ( ) ;
}
} continue ;
case OPCODE_END : {
exit_ok = true ;
break ;
} break ;
default : {
err_text = " Illegal opcode " + itos ( _code_ptr [ ip ] ) + " at address " + itos ( ip ) ;
} break ;
}
if ( exit_ok )
break ;
//error
// function, file, line, error, explanation
String err_file ;
if ( p_instance )
err_file = p_instance - > script - > path ;
else if ( _class )
err_file = _class - > path ;
if ( err_file = = " " )
err_file = " <built-in> " ;
String err_func = name ;
if ( p_instance & & p_instance - > script - > name ! = " " )
err_func = p_instance - > script - > name + " . " + err_func ;
int err_line = line ;
if ( err_text = = " " ) {
err_text = " Internal Script Error! - opcode # " + itos ( last_opcode ) + " (report please). " ;
}
if ( ! GDScriptLanguage : : get_singleton ( ) - > debug_break ( err_text , false ) ) {
// debugger break did not happen
_err_print_error ( err_func . utf8 ( ) . get_data ( ) , err_file . utf8 ( ) . get_data ( ) , err_line , err_text . utf8 ( ) . get_data ( ) ) ;
}
break ;
}
if ( ScriptDebugger : : get_singleton ( ) )
GDScriptLanguage : : get_singleton ( ) - > exit_function ( ) ;
if ( _stack_size ) {
//free stack
for ( int i = 0 ; i < _stack_size ; i + + )
stack [ i ] . ~ Variant ( ) ;
}
return retvalue ;
}
const int * GDFunction : : get_code ( ) const {
return _code_ptr ;
}
int GDFunction : : get_code_size ( ) const {
return _code_size ;
}
Variant GDFunction : : get_constant ( int p_idx ) const {
ERR_FAIL_INDEX_V ( p_idx , constants . size ( ) , " <errconst> " ) ;
return constants [ p_idx ] ;
}
StringName GDFunction : : get_global_name ( int p_idx ) const {
ERR_FAIL_INDEX_V ( p_idx , global_names . size ( ) , " <errgname> " ) ;
return global_names [ p_idx ] ;
}
int GDFunction : : get_default_argument_count ( ) const {
return default_arguments . size ( ) ;
}
int GDFunction : : get_default_argument_addr ( int p_arg ) const {
ERR_FAIL_INDEX_V ( p_arg , default_arguments . size ( ) , - 1 ) ;
return default_arguments [ p_arg ] ;
}
StringName GDFunction : : get_name ( ) const {
return name ;
}
int GDFunction : : get_max_stack_size ( ) const {
return _stack_size ;
}
struct _GDFKC {
int order ;
List < int > pos ;
} ;
struct _GDFKCS {
int order ;
StringName id ;
int pos ;
bool operator < ( const _GDFKCS & p_r ) const {
return order < p_r . order ;
}
} ;
void GDFunction : : debug_get_stack_member_state ( int p_line , List < Pair < StringName , int > > * r_stackvars ) const {
int oc = 0 ;
Map < StringName , _GDFKC > sdmap ;
for ( const List < StackDebug > : : Element * E = stack_debug . front ( ) ; E ; E = E - > next ( ) ) {
const StackDebug & sd = E - > get ( ) ;
if ( sd . line > p_line )
break ;
if ( sd . added ) {
if ( ! sdmap . has ( sd . identifier ) ) {
_GDFKC d ;
d . order = oc + + ;
d . pos . push_back ( sd . pos ) ;
sdmap [ sd . identifier ] = d ;
} else {
sdmap [ sd . identifier ] . pos . push_back ( sd . pos ) ;
}
} else {
ERR_CONTINUE ( ! sdmap . has ( sd . identifier ) ) ;
sdmap [ sd . identifier ] . pos . pop_back ( ) ;
if ( sdmap [ sd . identifier ] . pos . empty ( ) )
sdmap . erase ( sd . identifier ) ;
}
}
List < _GDFKCS > stackpositions ;
for ( Map < StringName , _GDFKC > : : Element * E = sdmap . front ( ) ; E ; E = E - > next ( ) ) {
_GDFKCS spp ;
spp . id = E - > key ( ) ;
spp . order = E - > get ( ) . order ;
spp . pos = E - > get ( ) . pos . back ( ) - > get ( ) ;
stackpositions . push_back ( spp ) ;
}
stackpositions . sort ( ) ;
for ( List < _GDFKCS > : : Element * E = stackpositions . front ( ) ; E ; E = E - > next ( ) ) {
Pair < StringName , int > p ;
p . first = E - > get ( ) . id ;
p . second = E - > get ( ) . pos ;
r_stackvars - > push_back ( p ) ;
}
}
#if 0
void GDFunction : : clear ( ) {
name = StringName ( ) ;
constants . clear ( ) ;
_stack_size = 0 ;
code . clear ( ) ;
_constants_ptr = NULL ;
_constant_count = 0 ;
_global_names_ptr = NULL ;
_global_names_count = 0 ;
_code_ptr = NULL ;
_code_size = 0 ;
}
# endif
GDFunction : : GDFunction ( ) {
_stack_size = 0 ;
_call_size = 0 ;
name = " <anonymous> " ;
2014-05-24 06:35:47 +02:00
# ifdef DEBUG_ENABLED
_func_cname = NULL ;
# endif
2014-02-10 02:10:30 +01:00
}
2014-09-15 16:33:30 +02:00
/////////////////////
Variant GDFunctionState : : _signal_callback ( const Variant * * p_args , int p_argcount , Variant : : CallError & r_error ) {
Variant arg ;
r_error . error = Variant : : CallError : : CALL_OK ;
ERR_FAIL_COND_V ( ! function , Variant ( ) ) ;
if ( p_argcount = = 0 ) {
r_error . error = Variant : : CallError : : CALL_ERROR_TOO_FEW_ARGUMENTS ;
r_error . argument = 1 ;
return Variant ( ) ;
} else if ( p_argcount = = 1 ) {
//noooneee
} else if ( p_argcount = = 2 ) {
arg = * p_args [ 0 ] ;
} else {
Array extra_args ;
for ( int i = 0 ; i < p_argcount - 1 ; i + + ) {
extra_args . push_back ( * p_args [ i ] ) ;
}
arg = extra_args ;
}
Ref < GDFunctionState > self = * p_args [ p_argcount - 1 ] ;
if ( self . is_null ( ) ) {
r_error . error = Variant : : CallError : : CALL_ERROR_INVALID_ARGUMENT ;
r_error . argument = p_argcount - 1 ;
r_error . expected = Variant : : OBJECT ;
return Variant ( ) ;
}
state . result = arg ;
Variant ret = function - > call ( NULL , NULL , 0 , r_error , & state ) ;
function = NULL ; //cleaned up;
state . result = Variant ( ) ;
return ret ;
}
bool GDFunctionState : : is_valid ( ) const {
return function ! = NULL ;
}
Variant GDFunctionState : : resume ( const Variant & p_arg ) {
ERR_FAIL_COND_V ( ! function , Variant ( ) ) ;
state . result = p_arg ;
Variant : : CallError err ;
Variant ret = function - > call ( NULL , NULL , 0 , err , & state ) ;
function = NULL ; //cleaned up;
state . result = Variant ( ) ;
return ret ;
}
void GDFunctionState : : _bind_methods ( ) {
2015-12-14 12:28:01 +01:00
ObjectTypeDB : : bind_method ( _MD ( " resume:Variant " , " arg " ) , & GDFunctionState : : resume , DEFVAL ( Variant ( ) ) ) ;
2014-09-15 16:33:30 +02:00
ObjectTypeDB : : bind_method ( _MD ( " is_valid " ) , & GDFunctionState : : is_valid ) ;
ObjectTypeDB : : bind_native_method ( METHOD_FLAGS_DEFAULT , " _signal_callback " , & GDFunctionState : : _signal_callback , MethodInfo ( " _signal_callback " ) ) ;
}
GDFunctionState : : GDFunctionState ( ) {
function = NULL ;
}
GDFunctionState : : ~ GDFunctionState ( ) {
if ( function ! = NULL ) {
//never called, deinitialize stack
for ( int i = 0 ; i < state . stack_size ; i + + ) {
Variant * v = ( Variant * ) & state . stack [ sizeof ( Variant ) * i ] ;
v - > ~ Variant ( ) ;
}
}
}
///////////////////////////
2014-02-10 02:10:30 +01:00
GDNativeClass : : GDNativeClass ( const StringName & p_name ) {
name = p_name ;
}
/*void GDNativeClass::call_multilevel(const StringName& p_method,const Variant** p_args,int p_argcount){
} */
bool GDNativeClass : : _get ( const StringName & p_name , Variant & r_ret ) const {
bool ok ;
int v = ObjectTypeDB : : get_integer_constant ( name , p_name , & ok ) ;
if ( ok ) {
r_ret = v ;
return true ;
} else {
return false ;
}
}
void GDNativeClass : : _bind_methods ( ) {
ObjectTypeDB : : bind_method ( _MD ( " new " ) , & GDNativeClass : : _new ) ;
}
Variant GDNativeClass : : _new ( ) {
Object * o = instance ( ) ;
if ( ! o ) {
ERR_EXPLAIN ( " Class type: ' " + String ( name ) + " ' is not instantiable. " ) ;
ERR_FAIL_COND_V ( ! o , Variant ( ) ) ;
}
Reference * ref = o - > cast_to < Reference > ( ) ;
if ( ref ) {
return REF ( ref ) ;
} else {
return o ;
}
}
Object * GDNativeClass : : instance ( ) {
return ObjectTypeDB : : instance ( name ) ;
}
2015-11-28 19:53:05 +01:00
GDInstance * 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 */
GDInstance * instance = memnew ( GDInstance ) ;
instance - > base_ref = p_isref ;
instance - > members . resize ( member_indices . size ( ) ) ;
instance - > script = Ref < GDScript > ( this ) ;
instance - > owner = p_owner ;
instance - > owner - > set_script_instance ( instance ) ;
/* STEP 2, INITIALIZE AND CONSRTUCT */
instances . insert ( instance - > owner ) ;
2015-11-28 19:53:05 +01:00
initializer - > call ( instance , p_args , p_argcount , r_error ) ;
2014-02-10 02:10:30 +01:00
2015-11-28 19:53:05 +01:00
if ( r_error . error ! = Variant : : CallError : : CALL_OK ) {
2014-02-10 02:10:30 +01:00
instance - > script = Ref < GDScript > ( ) ;
2014-12-18 05:50:47 +01:00
instance - > owner - > set_script_instance ( NULL ) ;
2014-02-10 02:10:30 +01:00
instances . erase ( p_owner ) ;
2015-11-28 19:53:05 +01: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 ;
}
Variant GDScript : : _new ( const Variant * * p_args , int p_argcount , Variant : : CallError & r_error ) {
/* STEP 1, CREATE */
r_error . error = Variant : : CallError : : CALL_OK ;
REF ref ;
Object * owner = NULL ;
GDScript * _baseptr = this ;
while ( _baseptr - > _base ) {
_baseptr = _baseptr - > _base ;
}
if ( _baseptr - > native . ptr ( ) ) {
owner = _baseptr - > native - > instance ( ) ;
} else {
owner = memnew ( Reference ) ; //by default, no base means use reference
}
Reference * r = owner - > cast_to < Reference > ( ) ;
if ( r ) {
ref = REF ( r ) ;
}
2015-11-28 19:53:05 +01:00
GDInstance * 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 ( ) ;
}
if ( ref . is_valid ( ) ) {
return ref ;
} else {
return owner ;
}
}
bool GDScript : : can_instance ( ) const {
2014-09-22 05:50:48 +02:00
//return valid; //any script in GDscript can instance
return valid | | ( ! tool & & ! ScriptServer : : is_scripting_enabled ( ) ) ;
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 ;
_FORCE_INLINE_ bool operator < ( const _GDScriptMemberSort & p_member ) const { return index < p_member . index ; }
} ;
# ifdef TOOLS_ENABLED
void GDScript : : _placeholder_erased ( PlaceHolderScriptInstance * p_placeholder ) {
placeholders . erase ( p_placeholder ) ;
}
2014-12-07 06:04:20 +01:00
/*
2014-02-10 02:10:30 +01:00
void GDScript : : _update_placeholder ( PlaceHolderScriptInstance * p_placeholder ) {
List < PropertyInfo > plist ;
GDScript * scr = this ;
Map < StringName , Variant > default_values ;
while ( scr ) {
Vector < _GDScriptMemberSort > msort ;
for ( Map < StringName , PropertyInfo > : : Element * E = scr - > member_info . front ( ) ; E ; E = E - > next ( ) ) {
_GDScriptMemberSort ms ;
ERR_CONTINUE ( ! scr - > member_indices . has ( E - > key ( ) ) ) ;
2014-10-28 02:54:32 +01:00
ms . index = scr - > member_indices [ E - > key ( ) ] . index ;
2014-02-10 02:10:30 +01:00
ms . name = E - > key ( ) ;
msort . push_back ( ms ) ;
}
msort . sort ( ) ;
msort . invert ( ) ;
for ( int i = 0 ; i < msort . size ( ) ; i + + ) {
plist . push_front ( scr - > member_info [ msort [ i ] . name ] ) ;
if ( scr - > member_default_values . has ( msort [ i ] . name ) )
default_values [ msort [ i ] . name ] = scr - > member_default_values [ msort [ i ] . name ] ;
else {
Variant : : CallError err ;
default_values [ msort [ i ] . name ] = Variant : : construct ( scr - > member_info [ msort [ i ] . name ] . type , NULL , 0 , err ) ;
}
}
scr = scr - > _base ;
}
p_placeholder - > update ( plist , default_values ) ;
2014-12-07 06:04:20 +01:00
} */
2014-02-10 02:10:30 +01:00
# endif
ScriptInstance * GDScript : : instance_create ( Object * p_this ) {
2014-09-22 05:50:48 +02:00
2014-02-10 02:10:30 +01:00
if ( ! tool & & ! ScriptServer : : is_scripting_enabled ( ) ) {
2014-09-22 05:50:48 +02:00
2014-02-10 02:10:30 +01:00
# ifdef TOOLS_ENABLED
//instance a fake script for editing the values
//plist.invert();
/*print_line("CREATING PLACEHOLDER");
for ( List < PropertyInfo > : : Element * E = plist . front ( ) ; E ; E = E - > next ( ) ) {
print_line ( E - > get ( ) . name ) ;
} */
PlaceHolderScriptInstance * si = memnew ( PlaceHolderScriptInstance ( GDScriptLanguage : : get_singleton ( ) , Ref < Script > ( this ) , p_this ) ) ;
placeholders . insert ( si ) ;
2014-12-07 06:04:20 +01:00
//_update_placeholder(si);
_update_exports ( ) ;
2014-02-10 02:10:30 +01:00
return si ;
# else
return NULL ;
# endif
}
GDScript * top = this ;
while ( top - > _base )
top = top - > _base ;
if ( top - > native . is_valid ( ) ) {
if ( ! ObjectTypeDB : : is_type ( p_this - > get_type_name ( ) , top - > native - > get_name ( ) ) ) {
if ( ScriptDebugger : : get_singleton ( ) ) {
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_type ( ) + " ' " ) ;
}
ERR_EXPLAIN ( " Script inherits from native type ' " + String ( top - > native - > get_name ( ) ) + " ', so it can't be instanced in object of type: ' " + p_this - > get_type ( ) + " ' " ) ;
ERR_FAIL_V ( NULL ) ;
}
}
2015-11-28 19:53:05 +01:00
Variant : : CallError unchecked_error ;
return _create_instance ( NULL , 0 , p_this , p_this - > cast_to < Reference > ( ) , unchecked_error ) ;
2014-02-10 02:10:30 +01:00
}
bool GDScript : : instance_has ( const Object * p_this ) const {
return instances . has ( ( Object * ) p_this ) ;
}
bool GDScript : : has_source_code ( ) const {
return source ! = " " ;
}
String GDScript : : get_source_code ( ) const {
return source ;
}
void GDScript : : set_source_code ( const String & p_code ) {
2014-12-07 06:04:20 +01:00
if ( source = = p_code )
return ;
2014-02-10 02:10:30 +01:00
source = p_code ;
2014-12-07 06:04:20 +01:00
# ifdef TOOLS_ENABLED
source_changed_cache = true ;
//print_line("SC CHANGED "+get_path());
# endif
2014-09-22 05:50:48 +02:00
}
2014-12-07 06:04:20 +01:00
# ifdef TOOLS_ENABLED
void GDScript : : _update_exports_values ( Map < StringName , Variant > & values , List < PropertyInfo > & propnames ) {
if ( base_cache . is_valid ( ) ) {
base_cache - > _update_exports_values ( values , propnames ) ;
}
for ( Map < StringName , Variant > : : Element * E = member_default_values_cache . front ( ) ; E ; E = E - > next ( ) ) {
values [ E - > key ( ) ] = E - > get ( ) ;
}
for ( List < PropertyInfo > : : Element * E = members_cache . front ( ) ; E ; E = E - > next ( ) ) {
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
2014-12-07 06:04:20 +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 ) {
//print_line("updating source for "+get_path());
source_changed_cache = false ;
changed = true ;
2014-09-22 05:50:48 +02:00
2014-12-07 06:04:20 +01:00
String basedir = path ;
2014-09-22 05:50:48 +02:00
2014-12-07 06:04:20 +01:00
if ( basedir = = " " )
basedir = get_path ( ) ;
2014-09-22 05:50:48 +02:00
2014-12-07 06:04:20 +01:00
if ( basedir ! = " " )
basedir = basedir . get_base_dir ( ) ;
2014-09-22 05:50:48 +02:00
2014-12-07 06:04:20 +01:00
GDParser parser ;
Error err = parser . parse ( source , basedir , true , path ) ;
2014-10-12 07:13:22 +02:00
2014-12-07 06:04:20 +01:00
if ( err = = OK ) {
2014-10-12 07:13:22 +02:00
2014-12-07 06:04:20 +01:00
const GDParser : : Node * root = parser . get_parse_tree ( ) ;
ERR_FAIL_COND_V ( root - > type ! = GDParser : : Node : : TYPE_CLASS , false ) ;
2014-09-22 05:50:48 +02:00
2014-12-07 06:04:20 +01:00
const GDParser : : ClassNode * c = static_cast < const GDParser : : ClassNode * > ( root ) ;
2014-10-12 07:13:22 +02:00
2014-12-07 06:04:20 +01:00
if ( base_cache . is_valid ( ) ) {
base_cache - > inheriters_cache . erase ( get_instance_ID ( ) ) ;
base_cache = Ref < GDScript > ( ) ;
}
2014-10-12 07:13:22 +02:00
2015-03-31 21:34:50 +02:00
if ( c - > extends_used & & String ( c - > extends_file ) ! = " " & & String ( c - > extends_file ) ! = get_path ( ) ) {
2014-10-12 07:13:22 +02:00
2014-12-07 06:04:20 +01:00
String path = c - > extends_file ;
if ( path . is_rel_path ( ) ) {
2014-09-22 05:50:48 +02:00
2014-12-07 06:04:20 +01:00
String base = get_path ( ) ;
if ( base = = " " | | base . is_rel_path ( ) ) {
2014-09-22 05:50:48 +02:00
2014-12-07 06:04:20 +01:00
ERR_PRINT ( ( " Could not resolve relative path for parent class: " + path ) . utf8 ( ) . get_data ( ) ) ;
} else {
path = base . get_base_dir ( ) . plus_file ( path ) ;
}
}
Ref < GDScript > bf = ResourceLoader : : load ( path ) ;
if ( bf . is_valid ( ) ) {
//print_line("parent is: "+bf->get_path());
base_cache = bf ;
bf - > inheriters_cache . insert ( get_instance_ID ( ) ) ;
//bf->_update_exports(p_instances,true,false);
}
}
members_cache . clear ( ) ; ;
member_default_values_cache . clear ( ) ;
for ( int i = 0 ; i < c - > variables . size ( ) ; i + + ) {
if ( c - > variables [ i ] . _export . type = = Variant : : NIL )
continue ;
members_cache . push_back ( c - > variables [ i ] . _export ) ;
//print_line("found "+c->variables[i]._export.name);
member_default_values_cache [ c - > variables [ i ] . identifier ] = c - > variables [ i ] . default_value ;
}
2015-06-24 18:29:23 +02:00
_signals . clear ( ) ;
for ( int i = 0 ; i < c - > _signals . size ( ) ; i + + ) {
_signals [ c - > _signals [ i ] . name ] = c - > _signals [ i ] . arguments ;
}
2014-12-07 06:04:20 +01:00
}
} else {
//print_line("unchaged is "+get_path());
2014-02-10 02:10:30 +01:00
2014-09-22 05:50:48 +02:00
}
2014-12-07 06:04:20 +01:00
if ( base_cache . is_valid ( ) ) {
if ( base_cache - > _update_exports ( ) ) {
changed = true ;
}
}
if ( /*changed &&*/ placeholders . size ( ) ) { //hm :(
//print_line("updating placeholders for "+get_path());
//update placeholders if any
Map < StringName , Variant > values ;
List < PropertyInfo > propnames ;
_update_exports_values ( values , propnames ) ;
2014-09-22 05:50:48 +02:00
2014-12-07 06:04:20 +01:00
for ( Set < PlaceHolderScriptInstance * > : : Element * E = placeholders . front ( ) ; E ; E = E - > next ( ) ) {
2014-09-22 05:50:48 +02:00
2014-12-07 06:04:20 +01:00
E - > get ( ) - > update ( propnames , values ) ;
}
2014-09-22 05:50:48 +02:00
}
2014-12-07 06:04:20 +01:00
return changed ;
2014-10-12 07:13:22 +02:00
# endif
2014-12-20 19:30:06 +01:00
return false ;
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
2014-12-07 06:04:20 +01:00
Set < ObjectID > copy = inheriters_cache ; //might get modified
//print_line("update exports for "+get_path()+" ic: "+itos(copy.size()));
for ( Set < ObjectID > : : Element * E = copy . front ( ) ; E ; E = E - > next ( ) ) {
Object * id = ObjectDB : : get_instance ( E - > get ( ) ) ;
if ( ! id )
continue ;
GDScript * s = id - > cast_to < GDScript > ( ) ;
if ( ! s )
continue ;
s - > update_exports ( ) ;
}
2014-09-22 05:50:48 +02:00
# endif
2014-02-10 02:10:30 +01:00
}
void GDScript : : _set_subclass_path ( Ref < GDScript > & p_sc , const String & p_path ) {
p_sc - > path = p_path ;
for ( Map < StringName , Ref < GDScript > > : : Element * E = p_sc - > subclasses . front ( ) ; E ; E = E - > next ( ) ) {
_set_subclass_path ( E - > get ( ) , p_path ) ;
}
}
Error GDScript : : reload ( ) {
ERR_FAIL_COND_V ( instances . size ( ) , ERR_ALREADY_IN_USE ) ;
String basedir = path ;
if ( basedir = = " " )
2014-02-25 13:31:47 +01:00
basedir = get_path ( ) ;
2014-02-10 02:10:30 +01:00
if ( basedir ! = " " )
basedir = basedir . get_base_dir ( ) ;
2014-06-16 15:22:26 +02:00
2014-02-10 02:10:30 +01:00
valid = false ;
GDParser parser ;
2014-12-07 06:04:20 +01:00
Error err = parser . parse ( source , basedir , false , path ) ;
2014-02-10 02:10:30 +01:00
if ( err ) {
if ( ScriptDebugger : : get_singleton ( ) ) {
GDScriptLanguage : : get_singleton ( ) - > debug_break_parse ( get_path ( ) , parser . get_error_line ( ) , " Parser Error: " + parser . get_error ( ) ) ;
}
_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_FAIL_V ( ERR_PARSE_ERROR ) ;
}
GDCompiler compiler ;
err = compiler . compile ( & parser , this ) ;
if ( err ) {
if ( ScriptDebugger : : get_singleton ( ) ) {
GDScriptLanguage : : get_singleton ( ) - > debug_break_parse ( get_path ( ) , compiler . get_error_line ( ) , " Parser Error: " + compiler . get_error ( ) ) ;
}
_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_FAIL_V ( ERR_COMPILATION_FAILED ) ;
}
valid = true ;
for ( Map < StringName , Ref < GDScript > > : : Element * E = subclasses . front ( ) ; E ; E = E - > next ( ) ) {
_set_subclass_path ( E - > get ( ) , path ) ;
}
# ifdef TOOLS_ENABLED
2014-12-07 06:04:20 +01:00
/*for (Set<PlaceHolderScriptInstance*>::Element *E=placeholders.front();E;E=E->next()) {
2014-02-10 02:10:30 +01:00
_update_placeholder ( E - > get ( ) ) ;
2014-12-07 06:04:20 +01:00
} */
2014-02-10 02:10:30 +01:00
# endif
return OK ;
}
String GDScript : : get_node_type ( ) const {
return " " ; // ?
}
ScriptLanguage * GDScript : : get_language ( ) const {
return GDScriptLanguage : : get_singleton ( ) ;
}
Variant GDScript : : call ( const StringName & p_method , const Variant * * p_args , int p_argcount , Variant : : CallError & r_error ) {
GDScript * top = this ;
while ( top ) {
Map < StringName , GDFunction > : : Element * E = top - > member_functions . find ( p_method ) ;
if ( E ) {
if ( ! E - > get ( ) . is_static ( ) ) {
WARN_PRINT ( String ( " Can't call non-static function: ' " + String ( p_method ) + " ' in script. " ) . utf8 ( ) . get_data ( ) ) ;
}
return E - > get ( ) . call ( NULL , p_args , p_argcount , r_error ) ;
}
top = top - > _base ;
}
//none found, regular
return Script : : call ( p_method , p_args , p_argcount , r_error ) ;
}
bool GDScript : : _get ( const StringName & p_name , Variant & r_ret ) const {
{
const GDScript * top = this ;
while ( top ) {
{
const Map < StringName , Variant > : : Element * E = top - > constants . find ( p_name ) ;
if ( E ) {
r_ret = E - > get ( ) ;
return true ;
}
}
{
const Map < StringName , Ref < GDScript > > : : Element * E = subclasses . find ( p_name ) ;
if ( E ) {
r_ret = E - > get ( ) ;
return true ;
}
}
top = top - > _base ;
}
2014-02-16 01:16:33 +01:00
if ( p_name = = GDScriptLanguage : : get_singleton ( ) - > strings . _script_source ) {
2014-02-10 02:10:30 +01:00
2014-02-16 01:16:33 +01:00
r_ret = get_source_code ( ) ;
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
}
bool GDScript : : _set ( const StringName & p_name , const Variant & p_value ) {
2014-02-16 01:16:33 +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 {
p_properties - > push_back ( PropertyInfo ( Variant : : STRING , " script/source " , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_NOEDITOR ) ) ;
}
void GDScript : : _bind_methods ( ) {
ObjectTypeDB : : bind_native_method ( METHOD_FLAGS_DEFAULT , " new " , & GDScript : : _new , MethodInfo ( " new " ) ) ;
2015-06-30 16:28:43 +02:00
ObjectTypeDB : : bind_method ( _MD ( " get_as_byte_code " ) , & GDScript : : get_as_byte_code ) ;
2014-02-10 02:10:30 +01:00
}
2014-02-25 13:31:47 +01:00
2015-06-30 16:28:43 +02:00
Vector < uint8_t > GDScript : : get_as_byte_code ( ) const {
GDTokenizerBuffer tokenizer ;
return tokenizer . parse_code_string ( source ) ;
} ;
2014-02-25 13:31:47 +01:00
Error GDScript : : load_byte_code ( const String & p_path ) {
2014-06-11 15:41:03 +02:00
Vector < uint8_t > bytecode ;
if ( p_path . ends_with ( " gde " ) ) {
FileAccess * fa = FileAccess : : open ( p_path , FileAccess : : READ ) ;
ERR_FAIL_COND_V ( ! fa , ERR_CANT_OPEN ) ;
FileAccessEncrypted * fae = memnew ( FileAccessEncrypted ) ;
ERR_FAIL_COND_V ( ! fae , ERR_CANT_OPEN ) ;
Vector < uint8_t > key ;
key . resize ( 32 ) ;
for ( int i = 0 ; i < key . size ( ) ; i + + ) {
key [ i ] = script_encryption_key [ i ] ;
}
Error err = fae - > open_and_parse ( fa , key , FileAccessEncrypted : : MODE_READ ) ;
ERR_FAIL_COND_V ( err , err ) ;
bytecode . resize ( fae - > get_len ( ) ) ;
fae - > get_buffer ( bytecode . ptr ( ) , bytecode . size ( ) ) ;
memdelete ( fae ) ;
} else {
bytecode = FileAccess : : get_file_as_array ( p_path ) ;
}
2014-02-25 13:31:47 +01:00
ERR_FAIL_COND_V ( bytecode . size ( ) = = 0 , ERR_PARSE_ERROR ) ;
path = p_path ;
String basedir = path ;
if ( basedir = = " " )
basedir = get_path ( ) ;
if ( basedir ! = " " )
basedir = basedir . get_base_dir ( ) ;
valid = false ;
GDParser parser ;
2014-12-07 06:04:20 +01:00
Error err = parser . parse_bytecode ( bytecode , basedir , get_path ( ) ) ;
2014-02-25 13:31:47 +01:00
if ( err ) {
_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_FAIL_V ( ERR_PARSE_ERROR ) ;
}
GDCompiler compiler ;
err = compiler . compile ( & parser , this ) ;
if ( err ) {
_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_FAIL_V ( ERR_COMPILATION_FAILED ) ;
}
valid = true ;
for ( Map < StringName , Ref < GDScript > > : : Element * E = subclasses . front ( ) ; E ; E = E - > next ( ) ) {
_set_subclass_path ( E - > get ( ) , path ) ;
}
return OK ;
}
2014-02-10 02:10:30 +01:00
Error GDScript : : load_source_code ( const String & p_path ) {
DVector < uint8_t > sourcef ;
Error err ;
FileAccess * f = FileAccess : : open ( p_path , FileAccess : : READ , & err ) ;
if ( err ) {
ERR_FAIL_COND_V ( err , err ) ;
}
int len = f - > get_len ( ) ;
sourcef . resize ( len + 1 ) ;
DVector < uint8_t > : : Write w = sourcef . write ( ) ;
int r = f - > get_buffer ( w . ptr ( ) , len ) ;
f - > close ( ) ;
memdelete ( f ) ;
ERR_FAIL_COND_V ( r ! = len , ERR_CANT_OPEN ) ;
w [ len ] = 0 ;
String s ;
if ( s . parse_utf8 ( ( const char * ) w . ptr ( ) ) ) {
ERR_EXPLAIN ( " Script ' " + p_path + " ' contains invalid unicode (utf-8), so it was not loaded. Please ensure that scripts are saved in valid utf-8 unicode. " ) ;
ERR_FAIL_V ( ERR_INVALID_DATA ) ;
}
source = s ;
2014-12-07 06:39:51 +01:00
# ifdef TOOLS_ENABLED
2014-12-07 06:04:20 +01:00
source_changed_cache = true ;
2014-12-07 06:39:51 +01:00
# endif
2014-12-07 06:04:20 +01:00
//print_line("LSC :"+get_path());
2014-02-10 02:10:30 +01:00
path = p_path ;
return OK ;
}
const Map < StringName , GDFunction > & GDScript : : debug_get_member_functions ( ) const {
return member_functions ;
}
StringName GDScript : : debug_get_member_by_index ( int p_idx ) const {
2014-10-28 02:54:32 +01:00
for ( const Map < StringName , MemberInfo > : : Element * E = member_indices . front ( ) ; E ; E = E - > next ( ) ) {
2014-02-10 02:10:30 +01:00
2014-10-28 02:54:32 +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 ;
}
2015-06-24 18:29:23 +02:00
bool GDScript : : has_script_signal ( const StringName & p_signal ) const {
if ( _signals . has ( p_signal ) )
return true ;
if ( base . is_valid ( ) ) {
return base - > has_script_signal ( p_signal ) ;
}
# ifdef TOOLS_ENABLED
else if ( base_cache . is_valid ( ) ) {
return base_cache - > has_script_signal ( p_signal ) ;
}
# endif
return false ;
}
void GDScript : : get_script_signal_list ( List < MethodInfo > * r_signals ) const {
for ( const Map < StringName , Vector < StringName > > : : Element * E = _signals . front ( ) ; E ; E = E - > next ( ) ) {
MethodInfo mi ;
mi . name = E - > key ( ) ;
for ( int i = 0 ; i < E - > get ( ) . size ( ) ; i + + ) {
PropertyInfo arg ;
arg . name = E - > get ( ) [ i ] ;
mi . arguments . push_back ( arg ) ;
}
r_signals - > push_back ( mi ) ;
}
if ( base . is_valid ( ) ) {
base - > get_script_signal_list ( r_signals ) ;
}
# ifdef TOOLS_ENABLED
else if ( base_cache . is_valid ( ) ) {
base_cache - > get_script_signal_list ( r_signals ) ;
}
# endif
}
2014-02-10 02:10:30 +01:00
GDScript : : GDScript ( ) {
2014-05-01 16:34:10 +02:00
_static_ref = this ;
2014-02-10 02:10:30 +01:00
valid = false ;
subclass_count = 0 ;
initializer = NULL ;
_base = NULL ;
_owner = NULL ;
tool = false ;
2014-12-07 06:04:20 +01:00
# ifdef TOOLS_ENABLED
source_changed_cache = false ;
# endif
2014-06-16 15:22:26 +02:00
2014-02-10 02:10:30 +01:00
}
//////////////////////////////
// INSTANCE //
//////////////////////////////
bool GDInstance : : set ( const StringName & p_name , const Variant & p_value ) {
//member
{
2014-10-28 02:54:32 +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 ) {
const Variant * val = & p_value ;
Variant : : CallError err ;
call ( E - > get ( ) . setter , & val , 1 , err ) ;
if ( err . error = = Variant : : CallError : : CALL_OK ) {
return true ; //function exists, call was successful
}
}
2015-06-13 20:10:06 +02:00
else
members [ E - > get ( ) . index ] = p_value ;
2014-02-10 02:10:30 +01:00
return true ;
}
}
GDScript * sptr = script . ptr ( ) ;
while ( sptr ) {
Map < StringName , GDFunction > : : Element * E = sptr - > member_functions . find ( GDScriptLanguage : : get_singleton ( ) - > strings . _set ) ;
if ( E ) {
Variant name = p_name ;
const Variant * args [ 2 ] = { & name , & p_value } ;
Variant : : CallError err ;
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 ( ) )
return true ;
}
sptr = sptr - > _base ;
}
return false ;
}
bool GDInstance : : get ( const StringName & p_name , Variant & r_ret ) const {
const GDScript * sptr = script . ptr ( ) ;
while ( sptr ) {
{
2014-10-28 02:54:32 +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 ;
r_ret = const_cast < GDInstance * > ( this ) - > call ( E - > get ( ) . getter , NULL , 0 , err ) ;
if ( err . error = = Variant : : CallError : : CALL_OK ) {
return true ;
}
}
r_ret = members [ E - > get ( ) . index ] ;
2014-02-10 02:10:30 +01:00
return true ; //index found
}
}
{
const Map < StringName , Variant > : : Element * E = script - > constants . find ( p_name ) ;
if ( E ) {
r_ret = E - > get ( ) ;
return true ; //index found
}
}
{
const Map < StringName , GDFunction > : : Element * E = sptr - > member_functions . find ( GDScriptLanguage : : get_singleton ( ) - > strings . _get ) ;
if ( E ) {
Variant name = p_name ;
const Variant * args [ 1 ] = { & name } ;
Variant : : CallError err ;
Variant ret = const_cast < GDFunction * > ( & E - > get ( ) ) - > call ( const_cast < GDInstance * > ( this ) , ( const Variant * * ) args , 1 , err ) ;
if ( err . error = = Variant : : CallError : : CALL_OK & & ret . get_type ( ) ! = Variant : : NIL ) {
r_ret = ret ;
return true ;
}
}
}
sptr = sptr - > _base ;
}
return false ;
}
2015-12-05 18:18:22 +01:00
Variant : : Type GDInstance : : get_property_type ( const StringName & p_name , bool * r_is_valid ) const {
const GDScript * sptr = script . ptr ( ) ;
while ( sptr ) {
if ( sptr - > member_info . has ( p_name ) ) {
if ( r_is_valid )
* r_is_valid = true ;
return sptr - > member_info [ p_name ] . type ;
}
sptr = sptr - > _base ;
}
if ( r_is_valid )
* r_is_valid = false ;
return Variant : : NIL ;
}
2014-02-10 02:10:30 +01:00
void GDInstance : : get_property_list ( List < PropertyInfo > * p_properties ) const {
// exported members, not doen yet!
const GDScript * sptr = script . ptr ( ) ;
List < PropertyInfo > props ;
while ( sptr ) {
const Map < StringName , GDFunction > : : Element * E = sptr - > member_functions . find ( GDScriptLanguage : : get_singleton ( ) - > strings . _get_property_list ) ;
if ( E ) {
Variant : : CallError err ;
Variant ret = const_cast < GDFunction * > ( & E - > get ( ) ) - > call ( const_cast < GDInstance * > ( this ) , NULL , 0 , err ) ;
if ( err . error = = Variant : : CallError : : CALL_OK ) {
if ( ret . get_type ( ) ! = Variant : : ARRAY ) {
ERR_EXPLAIN ( " Wrong type for _get_property list, must be an array of dictionaries. " ) ;
ERR_FAIL ( ) ;
}
Array arr = ret ;
for ( int i = 0 ; i < arr . size ( ) ; i + + ) {
Dictionary d = arr [ i ] ;
ERR_CONTINUE ( ! d . has ( " name " ) ) ;
ERR_CONTINUE ( ! d . has ( " type " ) ) ;
PropertyInfo pinfo ;
pinfo . type = Variant : : Type ( d [ " type " ] . operator int ( ) ) ;
ERR_CONTINUE ( pinfo . type < 0 | | pinfo . type > = Variant : : VARIANT_MAX ) ;
pinfo . name = d [ " name " ] ;
ERR_CONTINUE ( pinfo . name = = " " ) ;
if ( d . has ( " hint " ) )
pinfo . hint = PropertyHint ( d [ " hint " ] . operator int ( ) ) ;
if ( d . has ( " hint_string " ) )
pinfo . hint_string = d [ " hint_string " ] ;
if ( d . has ( " usage " ) )
pinfo . usage = d [ " usage " ] ;
props . push_back ( pinfo ) ;
}
}
}
//instance a fake script for editing the values
Vector < _GDScriptMemberSort > msort ;
for ( Map < StringName , PropertyInfo > : : Element * E = sptr - > member_info . front ( ) ; E ; E = E - > next ( ) ) {
_GDScriptMemberSort ms ;
ERR_CONTINUE ( ! sptr - > member_indices . has ( E - > key ( ) ) ) ;
2014-10-28 02:54:32 +01:00
ms . index = sptr - > member_indices [ E - > key ( ) ] . index ;
2014-02-10 02:10:30 +01:00
ms . name = E - > key ( ) ;
msort . push_back ( ms ) ;
}
msort . sort ( ) ;
msort . invert ( ) ;
for ( int i = 0 ; i < msort . size ( ) ; i + + ) {
props . push_front ( sptr - > member_info [ msort [ i ] . name ] ) ;
}
#if 0
if ( sptr - > member_functions . has ( " _get_property_list " ) ) {
Variant : : CallError err ;
GDFunction * f = const_cast < GDFunction * > ( & sptr - > member_functions [ " _get_property_list " ] ) ;
Variant plv = f - > call ( const_cast < GDInstance * > ( this ) , NULL , 0 , err ) ;
if ( plv . get_type ( ) ! = Variant : : ARRAY ) {
ERR_PRINT ( " _get_property_list: expected array returned " ) ;
} else {
Array pl = plv ;
for ( int i = 0 ; i < pl . size ( ) ; i + + ) {
Dictionary p = pl [ i ] ;
PropertyInfo pinfo ;
if ( ! p . has ( " name " ) ) {
ERR_PRINT ( " _get_property_list: expected 'name' key of type string. " )
continue ;
}
if ( ! p . has ( " type " ) ) {
ERR_PRINT ( " _get_property_list: expected 'type' key of type integer. " )
continue ;
}
pinfo . name = p [ " name " ] ;
pinfo . type = Variant : : Type ( int ( p [ " type " ] ) ) ;
if ( p . has ( " hint " ) )
pinfo . hint = PropertyHint ( int ( p [ " hint " ] ) ) ;
if ( p . has ( " hint_string " ) )
pinfo . hint_string = p [ " hint_string " ] ;
if ( p . has ( " usage " ) )
pinfo . usage = p [ " usage " ] ;
props . push_back ( pinfo ) ;
}
}
}
# endif
sptr = sptr - > _base ;
}
//props.invert();
for ( List < PropertyInfo > : : Element * E = props . front ( ) ; E ; E = E - > next ( ) ) {
p_properties - > push_back ( E - > get ( ) ) ;
}
}
void GDInstance : : get_method_list ( List < MethodInfo > * p_list ) const {
const GDScript * sptr = script . ptr ( ) ;
while ( sptr ) {
for ( Map < StringName , GDFunction > : : Element * E = sptr - > member_functions . front ( ) ; E ; E = E - > next ( ) ) {
MethodInfo mi ;
mi . name = E - > key ( ) ;
for ( int i = 0 ; i < E - > get ( ) . get_argument_count ( ) ; i + + )
mi . arguments . push_back ( PropertyInfo ( Variant : : NIL , " arg " + itos ( i ) ) ) ;
p_list - > push_back ( mi ) ;
}
sptr = sptr - > _base ;
}
}
bool GDInstance : : has_method ( const StringName & p_method ) const {
const GDScript * sptr = script . ptr ( ) ;
while ( sptr ) {
const Map < StringName , GDFunction > : : Element * E = sptr - > member_functions . find ( p_method ) ;
if ( E )
return true ;
sptr = sptr - > _base ;
}
return false ;
}
Variant GDInstance : : call ( const StringName & p_method , const Variant * * p_args , int p_argcount , Variant : : CallError & r_error ) {
//printf("calling %ls:%i method %ls\n", script->get_path().c_str(), -1, String(p_method).c_str());
GDScript * sptr = script . ptr ( ) ;
while ( sptr ) {
Map < StringName , GDFunction > : : Element * E = sptr - > member_functions . find ( p_method ) ;
if ( E ) {
return E - > get ( ) . call ( this , p_args , p_argcount , r_error ) ;
}
sptr = sptr - > _base ;
}
r_error . error = Variant : : CallError : : CALL_ERROR_INVALID_METHOD ;
return Variant ( ) ;
}
void GDInstance : : call_multilevel ( const StringName & p_method , const Variant * * p_args , int p_argcount ) {
GDScript * sptr = script . ptr ( ) ;
Variant : : CallError ce ;
while ( sptr ) {
Map < StringName , GDFunction > : : Element * E = sptr - > member_functions . find ( p_method ) ;
if ( E ) {
E - > get ( ) . call ( this , p_args , p_argcount , ce ) ;
}
sptr = sptr - > _base ;
}
}
void GDInstance : : _ml_call_reversed ( GDScript * sptr , const StringName & p_method , const Variant * * p_args , int p_argcount ) {
if ( sptr - > _base )
_ml_call_reversed ( sptr - > _base , p_method , p_args , p_argcount ) ;
Variant : : CallError ce ;
Map < StringName , GDFunction > : : Element * E = sptr - > member_functions . find ( p_method ) ;
if ( E ) {
E - > get ( ) . call ( this , p_args , p_argcount , ce ) ;
}
}
void GDInstance : : call_multilevel_reversed ( const StringName & p_method , const Variant * * p_args , int p_argcount ) {
if ( script . ptr ( ) ) {
_ml_call_reversed ( script . ptr ( ) , p_method , p_args , p_argcount ) ;
}
}
void GDInstance : : notification ( int p_notification ) {
//notification is not virutal, it gets called at ALL levels just like in C.
Variant value = p_notification ;
const Variant * args [ 1 ] = { & value } ;
GDScript * sptr = script . ptr ( ) ;
while ( sptr ) {
Map < StringName , GDFunction > : : Element * E = sptr - > member_functions . find ( GDScriptLanguage : : get_singleton ( ) - > strings . _notification ) ;
if ( E ) {
Variant : : CallError err ;
E - > get ( ) . call ( this , args , 1 , err ) ;
if ( err . error ! = Variant : : CallError : : CALL_OK ) {
//print error about notification call
}
}
sptr = sptr - > _base ;
}
}
Ref < Script > GDInstance : : get_script ( ) const {
return script ;
}
ScriptLanguage * GDInstance : : get_language ( ) {
return GDScriptLanguage : : get_singleton ( ) ;
}
GDInstance : : GDInstance ( ) {
owner = NULL ;
base_ref = false ;
}
GDInstance : : ~ GDInstance ( ) {
if ( script . is_valid ( ) & & owner ) {
script - > instances . erase ( owner ) ;
}
}
/************* SCRIPT LANGUAGE **************/
/************* SCRIPT LANGUAGE **************/
/************* SCRIPT LANGUAGE **************/
/************* SCRIPT LANGUAGE **************/
/************* SCRIPT LANGUAGE **************/
GDScriptLanguage * GDScriptLanguage : : singleton = NULL ;
String GDScriptLanguage : : get_name ( ) const {
return " GDScript " ;
}
/* LANGUAGE FUNCTIONS */
void GDScriptLanguage : : _add_global ( const StringName & p_name , const Variant & p_value ) {
if ( globals . has ( p_name ) ) {
//overwrite existing
global_array [ globals [ p_name ] ] = p_value ;
return ;
}
globals [ p_name ] = global_array . size ( ) ;
global_array . push_back ( p_value ) ;
_global_array = global_array . ptr ( ) ;
}
2015-12-28 19:59:20 +01:00
void GDScriptLanguage : : add_global_constant ( const StringName & p_variable , const Variant & p_value ) {
_add_global ( p_variable , p_value ) ;
}
2014-02-10 02:10:30 +01:00
void GDScriptLanguage : : init ( ) {
//populate global constants
int gcc = GlobalConstants : : get_global_constant_count ( ) ;
for ( int i = 0 ; i < gcc ; i + + ) {
_add_global ( StaticCString : : create ( GlobalConstants : : get_global_constant_name ( i ) ) , GlobalConstants : : get_global_constant_value ( i ) ) ;
}
_add_global ( StaticCString : : create ( " PI " ) , Math_PI ) ;
//populate native classes
2015-06-29 05:29:49 +02:00
List < StringName > class_list ;
2014-02-10 02:10:30 +01:00
ObjectTypeDB : : get_type_list ( & class_list ) ;
2015-06-29 05:29:49 +02: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 ( " _ " ) )
n = s . substr ( 1 , s . length ( ) ) ;
if ( globals . has ( n ) )
continue ;
Ref < GDNativeClass > nc = memnew ( GDNativeClass ( E - > get ( ) ) ) ;
_add_global ( n , nc ) ;
}
//populate singletons
List < Globals : : Singleton > singletons ;
Globals : : get_singleton ( ) - > get_singletons ( & singletons ) ;
for ( List < Globals : : Singleton > : : Element * E = singletons . front ( ) ; E ; E = E - > next ( ) ) {
_add_global ( E - > get ( ) . name , E - > get ( ) . ptr ) ;
}
}
String GDScriptLanguage : : get_type ( ) const {
return " GDScript " ;
}
String GDScriptLanguage : : get_extension ( ) const {
return " gd " ;
}
Error GDScriptLanguage : : execute_file ( const String & p_path ) {
// ??
return OK ;
}
void GDScriptLanguage : : finish ( ) {
}
void GDScriptLanguage : : frame ( ) {
// print_line("calls: "+itos(calls));
calls = 0 ;
}
/* EDITOR FUNCTIONS */
void GDScriptLanguage : : get_reserved_words ( List < String > * p_words ) const {
static const char * _reserved_words [ ] = {
" break " ,
" class " ,
" continue " ,
" const " ,
" else " ,
" elif " ,
" enum " ,
" extends " ,
2015-12-28 23:31:52 +01:00
" onready " ,
2014-02-10 02:10:30 +01:00
" for " ,
" func " ,
" if " ,
" in " ,
" null " ,
2014-11-02 15:31:01 +01:00
" not " ,
2014-02-10 02:10:30 +01:00
" return " ,
" self " ,
" while " ,
" true " ,
" false " ,
" tool " ,
" var " ,
2014-10-28 02:54:32 +01:00
" setget " ,
2014-02-10 02:10:30 +01:00
" pass " ,
" and " ,
" or " ,
" export " ,
2014-05-14 06:22:15 +02:00
" assert " ,
2015-12-29 16:11:21 +01:00
" breakpoint " ,
2014-09-15 16:33:30 +02:00
" yield " ,
" static " ,
2014-11-19 15:33:15 +01:00
" float " ,
" int " ,
2015-06-24 18:29:23 +02:00
" signal " ,
2014-02-10 02:10:30 +01:00
0 } ;
const char * * w = _reserved_words ;
while ( * w ) {
p_words - > push_back ( * w ) ;
w + + ;
}
for ( int i = 0 ; i < GDFunctions : : FUNC_MAX ; i + + ) {
p_words - > push_back ( GDFunctions : : get_func_name ( GDFunctions : : Function ( i ) ) ) ;
}
}
GDScriptLanguage : : GDScriptLanguage ( ) {
calls = 0 ;
ERR_FAIL_COND ( singleton ) ;
singleton = this ;
strings . _init = StaticCString : : create ( " _init " ) ;
strings . _notification = StaticCString : : create ( " _notification " ) ;
strings . _set = StaticCString : : create ( " _set " ) ;
strings . _get = StaticCString : : create ( " _get " ) ;
strings . _get_property_list = StaticCString : : create ( " _get_property_list " ) ;
2014-02-16 01:16:33 +01:00
strings . _script_source = StaticCString : : create ( " script/source " ) ;
2014-02-10 02:10:30 +01:00
_debug_parse_err_line = - 1 ;
_debug_parse_err_file = " " ;
_debug_call_stack_pos = 0 ;
int dmcs = GLOBAL_DEF ( " debug/script_max_call_stack " , 1024 ) ;
if ( ScriptDebugger : : get_singleton ( ) ) {
//debugging enabled!
_debug_max_call_stack = dmcs ;
if ( _debug_max_call_stack < 1024 )
_debug_max_call_stack = 1024 ;
_call_stack = memnew_arr ( CallLevel , _debug_max_call_stack + 1 ) ;
} else {
_debug_max_call_stack = 0 ;
_call_stack = NULL ;
}
}
GDScriptLanguage : : ~ GDScriptLanguage ( ) {
if ( _call_stack ) {
memdelete_arr ( _call_stack ) ;
}
singleton = NULL ;
}
/*************** RESOURCE ***************/
2015-08-24 01:15:56 +02:00
RES ResourceFormatLoaderGDScript : : load ( const String & p_path , const String & p_original_path , Error * r_error ) {
if ( r_error )
* r_error = ERR_FILE_CANT_OPEN ;
2014-02-10 02:10:30 +01:00
GDScript * script = memnew ( GDScript ) ;
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 ) ;
2014-02-10 02:10:30 +01:00
2014-02-25 13:31:47 +01:00
if ( err ! = OK ) {
ERR_FAIL_COND_V ( err ! = OK , RES ( ) ) ;
}
} else {
Error err = script - > load_source_code ( p_path ) ;
2014-02-10 02:10:30 +01:00
2014-02-25 13:31:47 +01:00
if ( err ! = OK ) {
ERR_FAIL_COND_V ( err ! = OK , RES ( ) ) ;
}
script - > set_script_path ( p_original_path ) ; // script needs this.
script - > set_path ( p_original_path ) ;
//script->set_name(p_path.get_file());
script - > reload ( ) ;
}
2015-08-24 01:15:56 +02:00
if ( r_error )
* r_error = OK ;
2014-02-10 02:10:30 +01:00
return scriptres ;
}
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
}
bool ResourceFormatLoaderGDScript : : handles_type ( const String & p_type ) const {
return ( p_type = = " Script " | | p_type = = " GDScript " ) ;
}
String ResourceFormatLoaderGDScript : : get_resource_type ( const String & p_path ) const {
2014-02-25 13:31:47 +01:00
String el = p_path . extension ( ) . to_lower ( ) ;
2014-06-11 15:41:03 +02:00
if ( el = = " gd " | | el = = " gdc " | | el = = " gde " )
2014-02-10 02:10:30 +01:00
return " GDScript " ;
return " " ;
}
Error ResourceFormatSaverGDScript : : save ( const String & p_path , const RES & p_resource , uint32_t p_flags ) {
Ref < GDScript > sqscr = p_resource ;
ERR_FAIL_COND_V ( sqscr . is_null ( ) , ERR_INVALID_PARAMETER ) ;
String source = sqscr - > get_source_code ( ) ;
Error err ;
FileAccess * file = FileAccess : : open ( p_path , FileAccess : : WRITE , & err ) ;
if ( err ) {
ERR_FAIL_COND_V ( err , err ) ;
}
file - > store_string ( source ) ;
2015-03-02 04:54:10 +01:00
if ( file - > get_error ( ) ! = OK & & file - > get_error ( ) ! = ERR_FILE_EOF ) {
memdelete ( file ) ;
return ERR_CANT_CREATE ;
}
2014-02-10 02:10:30 +01:00
file - > close ( ) ;
memdelete ( file ) ;
return OK ;
}
void ResourceFormatSaverGDScript : : get_recognized_extensions ( const RES & p_resource , List < String > * p_extensions ) const {
if ( p_resource - > cast_to < GDScript > ( ) ) {
p_extensions - > push_back ( " gd " ) ;
}
}
bool ResourceFormatSaverGDScript : : recognize ( const RES & p_resource ) const {
return p_resource - > cast_to < GDScript > ( ) ! = NULL ;
}