diff options
Diffstat (limited to 'modules/gdscript/gd_script.cpp')
-rw-r--r-- | modules/gdscript/gd_script.cpp | 2281 |
1 files changed, 2281 insertions, 0 deletions
diff --git a/modules/gdscript/gd_script.cpp b/modules/gdscript/gd_script.cpp new file mode 100644 index 0000000000..29857e6be6 --- /dev/null +++ b/modules/gdscript/gd_script.cpp @@ -0,0 +1,2281 @@ +/*************************************************************************/ +/* gd_script.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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" + +/* 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; + 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! + GDScript *s=p_script; + ERR_FAIL_INDEX_V(address,_global_names_count,NULL); + const StringName *sn = &_global_names_ptr[address]; + + while(s) { + Map<StringName,Variant>::Element *E=s->constants.find(*sn); + if (E) { + return &E->get(); + } + s=s->_base; + } + + + 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) { + err_text="Invalid call. Unexisting "+p_where+"."; + } else if (p_err.error==Variant::CallError::CALL_ERROR_INSTANCE_IS_NULL) { + err_text="Attempt to call "+p_where+" on a null instance."; + } else { + err_text="Bug, call error: #"+itos(p_err.error); + } + + return err_text; + +} + +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; + +} + +Variant GDFunction::call(GDInstance *p_instance,const Variant **p_args, int p_argcount,Variant::CallError& r_err) { + + + 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 + + if (p_argcount!=_argument_count) { + + if (p_argcount>_argument_count) { + + r_err.error=Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; + r_err.argument=_argument_count; + + return Variant(); + } else if (p_argcount < _argument_count - _default_arg_count) { + + r_err.error=Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_err.argument=_argument_count - _default_arg_count; + return Variant(); + } else { + + defarg=_argument_count-p_argcount; + } + } + + uint32_t alloca_size = sizeof(Variant*)*_call_size + sizeof(Variant)*_stack_size; + + if (alloca_size) { + + uint8_t *aptr = (uint8_t*)alloca(alloca_size); + + if (_stack_size) { + + stack=(Variant*)aptr; + for(int i=0;i<p_argcount;i++) + 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; + } + + if (_call_size) { + + call_args = (Variant**)&aptr[sizeof(Variant)*_stack_size]; + } else { + + call_args=NULL; + } + + + } else { + stack=NULL; + call_args=NULL; + } + + + GDScript *_class; + + if (p_instance) { + if (p_instance->base_ref && static_cast<Reference*>(p_instance->owner)->is_referenced()) { + + self=REF(static_cast<Reference*>(p_instance->owner)); + } else { + self=p_instance->owner; + } + _class=p_instance->script.ptr(); + } else { + _class=_script; + } + + int ip=0; + int line=_initial_line; + 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); + + Variant::evaluate(op,*a,*b,*dst,valid); + if (!valid) { + if (false && dst->get_type()==Variant::STRING) { + //return a string when invalid with the error + err_text=*dst; + } 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)+"'."; + } + break; + } + + 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; + } + + } + } 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; + *dst = src->get(*index,&valid); + + 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; + } + 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; + *dst = src->get_named(*index,&valid); + + if (!valid) { + err_text="Invalid get index '"+index->operator String()+"' (on base: '"+_get_var_type(src)+"')."; + break; + } + + 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; + } + } + } + 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; + + } 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; + 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>"; + +} + +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); +} + + + +GDInstance* GDScript::_create_instance(const Variant** p_args,int p_argcount,Object *p_owner,bool p_isref) { + + + /* 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); + + Variant::CallError err; + initializer->call(instance,p_args,p_argcount,err); + + if (err.error!=Variant::CallError::CALL_OK) { + instance->script=Ref<GDScript>(); + instances.erase(p_owner); + memdelete(instance); + ERR_FAIL_COND_V(err.error!=Variant::CallError::CALL_OK, NULL); //error consrtucting + } + + //@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); + } + + + GDInstance* instance = _create_instance(p_args,p_argcount,owner,r!=NULL); + 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 { + + return valid; //any script in GDscript can instance +} + +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); +} + +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())); + ms.index=scr->member_indices[E->key()]; + 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); + +} +#endif +ScriptInstance* GDScript::instance_create(Object *p_this) { + + if (!tool && !ScriptServer::is_scripting_enabled()) { + +#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); + _update_placeholder(si); + 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); + + } + } + + return _create_instance(NULL,0,p_this,p_this->cast_to<Reference>()); + +} +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) { + + source=p_code; + +} + +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=="") + basedir=get_path(); + + if (basedir!="") + basedir=basedir.get_base_dir(); + + + + valid=false; + GDParser parser; + Error err = parser.parse(source,basedir); + 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 + for (Set<PlaceHolderScriptInstance*>::Element *E=placeholders.front();E;E=E->next()) { + + _update_placeholder(E->get()); + } +#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; + } + + if (p_name==GDScriptLanguage::get_singleton()->strings._script_source) { + + r_ret=get_source_code(); + return true; + } + } + + + + return false; + +} +bool GDScript::_set(const StringName& p_name, const Variant& p_value) { + + if (p_name==GDScriptLanguage::get_singleton()->strings._script_source) { + + 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")); + +} + + + +Error GDScript::load_byte_code(const String& p_path) { + + Vector<uint8_t> bytecode = FileAccess::get_file_as_array(p_path); + 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; + Error err = parser.parse_bytecode(bytecode,basedir); + 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; +} + + +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; + 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 { + + + for(const Map<StringName,int>::Element *E=member_indices.front();E;E=E->next()) { + + if (E->get()==p_idx) + return E->key(); + } + + return "<error>"; +} + + +Ref<GDScript> GDScript::get_base() const { + + return base; +} + +GDScript::GDScript() { + + + valid=false; + subclass_count=0; + initializer=NULL; + _base=NULL; + _owner=NULL; + tool=false; +} + + + + + +////////////////////////////// +// INSTANCE // +////////////////////////////// + + +bool GDInstance::set(const StringName& p_name, const Variant& p_value) { + + //member + { + const Map<StringName,int>::Element *E = script->member_indices.find(p_name); + if (E) { + members[E->get()]=p_value; + 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) { + + { + const Map<StringName,int>::Element *E = script->member_indices.find(p_name); + if (E) { + r_ret=members[E->get()]; + 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; + +} +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())); + ms.index=sptr->member_indices[E->key()]; + 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(); +} + +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 + + List<String> class_list; + ObjectTypeDB::get_type_list(&class_list); + for(List<String>::Element *E=class_list.front();E;E=E->next()) { + + 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" , + "for" , + "func" , + "if" , + "in" , + "varl", + "null" , + "return" , + "self" , + "while" , + "true" , + "false" , + "tool", + "var", + "pass", + "and", + "or", + "export", + 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"); + strings._script_source=StaticCString::create("script/source"); + _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 ***************/ + +RES ResourceFormatLoaderGDScript::load(const String &p_path,const String& p_original_path) { + + GDScript *script = memnew( GDScript ); + + Ref<GDScript> scriptres(script); + + if (p_path.ends_with(".gdc")) { + + script->set_script_path(p_original_path); // script needs this. + script->set_path(p_original_path); + Error err = script->load_byte_code(p_path); + + + if (err!=OK) { + + ERR_FAIL_COND_V(err!=OK, RES()); + } + + } else { + Error err = script->load_source_code(p_path); + + 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(); + } + + return scriptres; +} +void ResourceFormatLoaderGDScript::get_recognized_extensions(List<String> *p_extensions) const { + + p_extensions->push_back("gd"); + p_extensions->push_back("gdc"); +} + +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 { + + String el = p_path.extension().to_lower(); + if (el=="gd" || el=="gdc") + 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); + + 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; +} |