summaryrefslogtreecommitdiff
path: root/modules/gdscript
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gdscript')
-rw-r--r--modules/gdscript/SCsub4
-rw-r--r--modules/gdscript/config.py7
-rw-r--r--modules/gdscript/gd_compiler.cpp313
-rw-r--r--modules/gdscript/gd_compiler.h10
-rw-r--r--modules/gdscript/gd_editor.cpp790
-rw-r--r--modules/gdscript/gd_function.cpp1499
-rw-r--r--modules/gdscript/gd_function.h225
-rw-r--r--modules/gdscript/gd_functions.cpp253
-rw-r--r--modules/gdscript/gd_functions.h8
-rw-r--r--modules/gdscript/gd_parser.cpp660
-rw-r--r--modules/gdscript/gd_parser.h23
-rw-r--r--modules/gdscript/gd_pretty_print.cpp34
-rw-r--r--modules/gdscript/gd_pretty_print.h40
-rw-r--r--modules/gdscript/gd_script.cpp1919
-rw-r--r--modules/gdscript/gd_script.h366
-rw-r--r--modules/gdscript/gd_tokenizer.cpp84
-rw-r--r--modules/gdscript/gd_tokenizer.h8
-rw-r--r--modules/gdscript/register_types.cpp51
-rw-r--r--modules/gdscript/register_types.h2
19 files changed, 4184 insertions, 2112 deletions
diff --git a/modules/gdscript/SCsub b/modules/gdscript/SCsub
index 403fe68f66..0882406761 100644
--- a/modules/gdscript/SCsub
+++ b/modules/gdscript/SCsub
@@ -1,5 +1,7 @@
+#!/usr/bin/env python
+
Import('env')
-env.add_source_files(env.modules_sources,"*.cpp")
+env.add_source_files(env.modules_sources, "*.cpp")
Export('env')
diff --git a/modules/gdscript/config.py b/modules/gdscript/config.py
index ea7e83378a..5698a37295 100644
--- a/modules/gdscript/config.py
+++ b/modules/gdscript/config.py
@@ -1,11 +1,8 @@
def can_build(platform):
- return True
+ return True
def configure(env):
- pass
-
-
-
+ pass
diff --git a/modules/gdscript/gd_compiler.cpp b/modules/gdscript/gd_compiler.cpp
index 5a6299bcf8..8e9e94cc48 100644
--- a/modules/gdscript/gd_compiler.cpp
+++ b/modules/gdscript/gd_compiler.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2017 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 */
@@ -29,6 +29,31 @@
#include "gd_compiler.h"
#include "gd_script.h"
+bool GDCompiler::_is_class_member_property(CodeGen & codegen, const StringName & p_name) {
+
+ if (!codegen.function_node || codegen.function_node->_static)
+ return false;
+
+ return _is_class_member_property(codegen.script,p_name);
+}
+
+bool GDCompiler::_is_class_member_property(GDScript *owner, const StringName & p_name) {
+
+
+ GDScript *scr = owner;
+ GDNativeClass *nc=NULL;
+ while(scr) {
+
+ if (scr->native.is_valid())
+ nc=scr->native.ptr();
+ scr=scr->_base;
+ }
+
+ ERR_FAIL_COND_V(!nc,false);
+
+ return ClassDB::has_property(nc->get_name(),p_name);
+}
+
void GDCompiler::_set_error(const String& p_error,const GDParser::Node *p_node) {
@@ -164,6 +189,17 @@ int GDCompiler::_parse_expression(CodeGen& codegen,const GDParser::Node *p_expre
StringName identifier = in->name;
+
+ if (_is_class_member_property(codegen,identifier)) {
+ //get property
+ codegen.opcodes.push_back(GDFunction::OPCODE_GET_MEMBER); // perform operator
+ codegen.opcodes.push_back(codegen.get_name_map_pos(identifier)); // argument 2 (unary only takes one parameter)
+ int dst_addr=(p_stack_level)|(GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS);
+ codegen.opcodes.push_back(dst_addr); // append the stack level as destination address of the opcode
+ codegen.alloc_stack(p_stack_level);
+ return dst_addr;
+ }
+
// TRY STACK!
if (!p_initializer && codegen.stack_identifiers.has(identifier)) {
@@ -208,7 +244,7 @@ int GDCompiler::_parse_expression(CodeGen& codegen,const GDParser::Node *p_expre
if (nc) {
bool success=false;
- int constant = ObjectTypeDB::get_integer_constant(nc->get_name(),identifier,&success);
+ int constant = ClassDB::get_integer_constant(nc->get_name(),identifier,&success);
if (success) {
Variant key=constant;
int idx;
@@ -460,7 +496,6 @@ int GDCompiler::_parse_expression(CodeGen& codegen,const GDParser::Node *p_expre
const GDParser::Node *instance = on->arguments[0];
- bool in_static=false;
if (instance->type==GDParser::Node::TYPE_SELF) {
//room for optimization
@@ -550,17 +585,25 @@ int GDCompiler::_parse_expression(CodeGen& codegen,const GDParser::Node *p_expre
int index;
if (named) {
-#ifdef DEBUG_ENABLED
if (on->arguments[0]->type==GDParser::Node::TYPE_SELF && codegen.script && codegen.function_node && !codegen.function_node->_static) {
- const Map<StringName,GDScript::MemberInfo>::Element *MI = codegen.script->member_indices.find(static_cast<GDParser::IdentifierNode*>(on->arguments[1])->name);
+ GDParser::IdentifierNode* identifier = static_cast<GDParser::IdentifierNode*>(on->arguments[1]);
+ const Map<StringName,GDScript::MemberInfo>::Element *MI = codegen.script->member_indices.find(identifier->name);
+
+#ifdef DEBUG_ENABLED
if (MI && MI->get().getter==codegen.function_node->name) {
String n = static_cast<GDParser::IdentifierNode*>(on->arguments[1])->name;
_set_error("Must use '"+n+"' instead of 'self."+n+"' in getter.",on);
return -1;
}
- }
#endif
+
+ if (MI && MI->get().getter=="") {
+ // Faster than indexing self (as if no self. had been used)
+ return (MI->get().index)|(GDFunction::ADDR_TYPE_MEMBER<<GDFunction::ADDR_BITS);
+ }
+ }
+
index=codegen.get_name_map_pos(static_cast<GDParser::IdentifierNode*>(on->arguments[1])->name);
} else {
@@ -655,6 +698,46 @@ int GDCompiler::_parse_expression(CodeGen& codegen,const GDParser::Node *p_expre
return p_stack_level|GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS;
} break;
+ // ternary operators
+ case GDParser::OperatorNode::OP_TERNARY_IF: {
+
+ // x IF a ELSE y operator with early out on failure
+
+ int res = _parse_expression(codegen,on->arguments[0],p_stack_level);
+ if (res<0)
+ return res;
+ codegen.opcodes.push_back(GDFunction::OPCODE_JUMP_IF_NOT);
+ codegen.opcodes.push_back(res);
+ int jump_fail_pos=codegen.opcodes.size();
+ codegen.opcodes.push_back(0);
+
+
+ res = _parse_expression(codegen,on->arguments[1],p_stack_level);
+ if (res<0)
+ return res;
+
+ codegen.alloc_stack(p_stack_level); //it will be used..
+ codegen.opcodes.push_back(GDFunction::OPCODE_ASSIGN);
+ codegen.opcodes.push_back(p_stack_level|GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS);
+ codegen.opcodes.push_back(res);
+ codegen.opcodes.push_back(GDFunction::OPCODE_JUMP);
+ int jump_past_pos=codegen.opcodes.size();
+ codegen.opcodes.push_back(0);
+
+ codegen.opcodes[jump_fail_pos]=codegen.opcodes.size();
+ res = _parse_expression(codegen,on->arguments[2],p_stack_level);
+ if (res<0)
+ return res;
+
+ codegen.opcodes.push_back(GDFunction::OPCODE_ASSIGN);
+ codegen.opcodes.push_back(p_stack_level|GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS);
+ codegen.opcodes.push_back(res);
+
+ codegen.opcodes[jump_past_pos]=codegen.opcodes.size();
+
+ return p_stack_level|GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS;
+
+ } break;
//unary operators
case GDParser::OperatorNode::OP_NEG: { if (!_create_unary_operator(codegen,on,Variant::OP_NEGATE,p_stack_level)) return -1;} break;
case GDParser::OperatorNode::OP_NOT: { if (!_create_unary_operator(codegen,on,Variant::OP_NOT,p_stack_level)) return -1;} break;
@@ -729,6 +812,8 @@ int GDCompiler::_parse_expression(CodeGen& codegen,const GDParser::Node *p_expre
/* Find chain of sets */
+ StringName assign_property;
+
List<GDParser::OperatorNode*> chain;
{
@@ -737,8 +822,20 @@ int GDCompiler::_parse_expression(CodeGen& codegen,const GDParser::Node *p_expre
while(true) {
chain.push_back(n);
- if (n->arguments[0]->type!=GDParser::Node::TYPE_OPERATOR)
+ if (n->arguments[0]->type!=GDParser::Node::TYPE_OPERATOR) {
+
+ //check for a built-in property
+ if (n->arguments[0]->type==GDParser::Node::TYPE_IDENTIFIER) {
+
+ GDParser::IdentifierNode *identifier = static_cast<GDParser::IdentifierNode*>(n->arguments[0]);
+ if (_is_class_member_property(codegen,identifier->name)) {
+ assign_property = identifier->name;
+
+ }
+
+ }
break;
+ }
n = static_cast<GDParser::OperatorNode*>(n->arguments[0]);
if (n->op!=GDParser::OperatorNode::OP_INDEX && n->op!=GDParser::OperatorNode::OP_INDEX_NAMED)
break;
@@ -763,7 +860,16 @@ int GDCompiler::_parse_expression(CodeGen& codegen,const GDParser::Node *p_expre
Vector<int> setchain;
- int prev_key_idx=-1;
+
+ if (assign_property!=StringName()) {
+
+ // recover and assign at the end, this allows stuff like
+ // position.x+=2.0
+ // in Node2D
+ setchain.push_back(prev_pos);
+ setchain.push_back(codegen.get_name_map_pos(assign_property));
+ setchain.push_back(GDFunction::OPCODE_SET_MEMBER);
+ }
for(List<GDParser::OperatorNode*>::Element *E=chain.back();E;E=E->prev()) {
@@ -795,7 +901,7 @@ int GDCompiler::_parse_expression(CodeGen& codegen,const GDParser::Node *p_expre
}
- if (key_idx<0)
+ if (key_idx<0) //error
return key_idx;
codegen.opcodes.push_back(named ? GDFunction::OPCODE_GET_NAMED : GDFunction::OPCODE_GET);
@@ -807,14 +913,16 @@ int GDCompiler::_parse_expression(CodeGen& codegen,const GDParser::Node *p_expre
codegen.opcodes.push_back(dst_pos);
+
//add in reverse order, since it will be reverted
+
+
setchain.push_back(dst_pos);
setchain.push_back(key_idx);
setchain.push_back(prev_pos);
setchain.push_back(named ? GDFunction::OPCODE_SET_NAMED : GDFunction::OPCODE_SET);
prev_pos=dst_pos;
- prev_key_idx=key_idx;
}
@@ -837,7 +945,7 @@ int GDCompiler::_parse_expression(CodeGen& codegen,const GDParser::Node *p_expre
}
- if (set_index<0)
+ if (set_index<0) //error
return set_index;
if (set_index&GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS) {
@@ -847,7 +955,7 @@ int GDCompiler::_parse_expression(CodeGen& codegen,const GDParser::Node *p_expre
int set_value = _parse_assign_right_expression(codegen,on,slevel+1);
- if (set_value<0)
+ if (set_value<0) //error
return set_value;
codegen.opcodes.push_back(named?GDFunction::OPCODE_SET_NAMED:GDFunction::OPCODE_SET);
@@ -855,20 +963,36 @@ int GDCompiler::_parse_expression(CodeGen& codegen,const GDParser::Node *p_expre
codegen.opcodes.push_back(set_index);
codegen.opcodes.push_back(set_value);
- for(int i=0;i<setchain.size();i+=4) {
+ for(int i=0;i<setchain.size();i++) {
- codegen.opcodes.push_back(setchain[i+0]);
- codegen.opcodes.push_back(setchain[i+1]);
- codegen.opcodes.push_back(setchain[i+2]);
- codegen.opcodes.push_back(setchain[i+3]);
+ codegen.opcodes.push_back(setchain[i]);
}
return retval;
+ } else if (on->arguments[0]->type==GDParser::Node::TYPE_IDENTIFIER && _is_class_member_property(codegen,static_cast<GDParser::IdentifierNode*>(on->arguments[0])->name)) {
+ //assignment to member property
+
+ int slevel = p_stack_level;
+
+ int src_address = _parse_assign_right_expression(codegen,on,slevel);
+ if (src_address<0)
+ return -1;
+
+ StringName name = static_cast<GDParser::IdentifierNode*>(on->arguments[0])->name;
+
+ codegen.opcodes.push_back(GDFunction::OPCODE_SET_MEMBER);
+ codegen.opcodes.push_back(codegen.get_name_map_pos(name));
+ codegen.opcodes.push_back(src_address);
+
+ return GDFunction::ADDR_TYPE_NIL<<GDFunction::ADDR_BITS;
} else {
- //ASSIGNMENT MODE!!
+
+
+
+ //REGULAR ASSIGNMENT MODE!!
int slevel = p_stack_level;
@@ -961,12 +1085,12 @@ Error GDCompiler::_parse_block(CodeGen& codegen,const GDParser::BlockNode *p_blo
switch(s->type) {
case GDParser::Node::TYPE_NEWLINE: {
-
+#ifdef DEBUG_ENABLED
const GDParser::NewLineNode *nl = static_cast<const GDParser::NewLineNode*>(s);
codegen.opcodes.push_back(GDFunction::OPCODE_LINE);
codegen.opcodes.push_back(nl->line);
codegen.current_line=nl->line;
-
+#endif
} break;
case GDParser::Node::TYPE_CONTROL_FLOW: {
// try subblocks
@@ -1157,14 +1281,21 @@ Error GDCompiler::_parse_block(CodeGen& codegen,const GDParser::BlockNode *p_blo
codegen.opcodes.push_back(ret);
} break;
case GDParser::Node::TYPE_BREAKPOINT: {
+#ifdef DEBUG_ENABLED
// try subblocks
codegen.opcodes.push_back(GDFunction::OPCODE_BREAKPOINT);
+#endif
} break;
case GDParser::Node::TYPE_LOCAL_VAR: {
const GDParser::LocalVarNode *lv = static_cast<const GDParser::LocalVarNode*>(s);
+ if (_is_class_member_property(codegen,lv->name)) {
+ _set_error("Name for local variable '"+String(lv->name)+"' can't shadow class property of the same name.",lv);
+ return ERR_ALREADY_EXISTS;
+ }
+
codegen.add_stack_identifier(lv->name,p_stack_level++);
codegen.alloc_stack(p_stack_level);
new_identifiers++;
@@ -1203,7 +1334,10 @@ Error GDCompiler::_parse_function(GDScript *p_script,const GDParser::ClassNode *
if (p_func) {
for(int i=0;i<p_func->arguments.size();i++) {
- int idx = i;
+ if (_is_class_member_property(p_script,p_func->arguments[i])) {
+ _set_error("Name for argument '"+String(p_func->arguments[i])+"' can't shadow class property of the same name.",p_func);
+ return ERR_ALREADY_EXISTS;
+ }
codegen.add_stack_identifier(p_func->arguments[i],i);
#ifdef TOOLS_ENABLED
argnames.push_back(p_func->arguments[i]);
@@ -1286,16 +1420,19 @@ Error GDCompiler::_parse_function(GDScript *p_script,const GDParser::ClassNode *
GDFunction *gdfunc=NULL;
- //if (String(p_func->name)=="") { //initializer func
- // gdfunc = &p_script->initializer;
-
+ /*
+ if (String(p_func->name)=="") { //initializer func
+ gdfunc = &p_script->initializer;
+ */
//} else { //regular func
p_script->member_functions[func_name]=memnew(GDFunction);
gdfunc = p_script->member_functions[func_name];
//}
- if (p_func)
+ if (p_func) {
gdfunc->_static=p_func->_static;
+ gdfunc->rpc_mode=p_func->rpc_mode;
+ }
#ifdef TOOLS_ENABLED
gdfunc->arg_names=argnames;
@@ -1398,6 +1535,10 @@ Error GDCompiler::_parse_function(GDScript *p_script,const GDParser::ClassNode *
#endif
if (p_func) {
gdfunc->_initial_line=p_func->line;
+#ifdef TOOLS_ENABLED
+
+ p_script->member_lines[func_name]=p_func->line;
+#endif
} else {
gdfunc->_initial_line=0;
}
@@ -1414,8 +1555,13 @@ Error GDCompiler::_parse_function(GDScript *p_script,const GDParser::ClassNode *
-Error GDCompiler::_parse_class(GDScript *p_script,GDScript *p_owner,const GDParser::ClassNode *p_class) {
+Error GDCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, const GDParser::ClassNode *p_class, bool p_keep_state) {
+ Map<StringName,Ref<GDScript> > old_subclasses;
+
+ if (p_keep_state) {
+ old_subclasses=p_script->subclasses;
+ }
p_script->native=Ref<GDNativeClass>();
p_script->base=Ref<GDScript>();
@@ -1428,14 +1574,15 @@ Error GDCompiler::_parse_class(GDScript *p_script,GDScript *p_owner,const GDPars
p_script->member_functions.clear();
p_script->member_indices.clear();
p_script->member_info.clear();
+ p_script->_signals.clear();
p_script->initializer=NULL;
+
p_script->subclasses.clear();
p_script->_owner=p_owner;
p_script->tool=p_class->tool;
p_script->name=p_class->name;
- int index_from=0;
Ref<GDNativeClass> native;
if (p_class->extends_used) {
@@ -1491,7 +1638,8 @@ Error GDCompiler::_parse_class(GDScript *p_script,GDScript *p_owner,const GDPars
String sub = p_class->extends_class[i];
if (script->subclasses.has(sub)) {
- script=script->subclasses[sub];
+ Ref<Script> subclass = script->subclasses[sub]; //avoid reference from dissapearing
+ script=subclass;
} else {
_set_error("Could not find subclass: "+sub,p_class);
@@ -1582,9 +1730,14 @@ Error GDCompiler::_parse_class(GDScript *p_script,GDScript *p_owner,const GDPars
}
+ }else {
+ // without extends, implicitly extend Reference
+ int native_idx = GDScriptLanguage::get_singleton()->get_global_map()["Reference"];
+ native = GDScriptLanguage::get_singleton()->get_global_array()[native_idx];
+ ERR_FAIL_COND_V(native.is_null(), ERR_BUG);
+ p_script->native=native;
}
-
//print_line("Script: "+p_script->get_path()+" indices: "+itos(p_script->member_indices.size()));
@@ -1595,6 +1748,10 @@ Error GDCompiler::_parse_class(GDScript *p_script,GDScript *p_owner,const GDPars
_set_error("Member '"+name+"' already exists (in current or parent class)",p_class);
return ERR_ALREADY_EXISTS;
}
+ if (_is_class_member_property(p_script,name)) {
+ _set_error("Member '"+name+"' already exists as a class property.",p_class);
+ return ERR_ALREADY_EXISTS;
+ }
if (p_class->variables[i]._export.type!=Variant::NIL) {
@@ -1605,6 +1762,9 @@ Error GDCompiler::_parse_class(GDScript *p_script,GDScript *p_owner,const GDPars
p_script->member_default_values[name]=p_class->variables[i].default_value;
}
#endif
+ } else {
+
+ p_script->member_info[name]=PropertyInfo(Variant::NIL,name,PROPERTY_HINT_NONE,"",PROPERTY_USAGE_SCRIPT_VARIABLE);
}
//int new_idx = p_script->member_indices.size();
@@ -1612,9 +1772,17 @@ Error GDCompiler::_parse_class(GDScript *p_script,GDScript *p_owner,const GDPars
minfo.index = p_script->member_indices.size();
minfo.setter = p_class->variables[i].setter;
minfo.getter = p_class->variables[i].getter;
+ minfo.rpc_mode=p_class->variables[i].rpc_mode;
+
p_script->member_indices[name]=minfo;
p_script->members.insert(name);
+#ifdef TOOLS_ENABLED
+
+ p_script->member_lines[name]=p_class->variables[i].line;
+#endif
+
+
}
for(int i=0;i<p_class->constant_expressions.size();i++) {
@@ -1622,10 +1790,20 @@ Error GDCompiler::_parse_class(GDScript *p_script,GDScript *p_owner,const GDPars
StringName name = p_class->constant_expressions[i].identifier;
ERR_CONTINUE( p_class->constant_expressions[i].expression->type!=GDParser::Node::TYPE_CONSTANT );
+ if (_is_class_member_property(p_script,name)) {
+ _set_error("Member '"+name+"' already exists as a class property.",p_class);
+ return ERR_ALREADY_EXISTS;
+ }
+
GDParser::ConstantNode *constant = static_cast<GDParser::ConstantNode*>(p_class->constant_expressions[i].expression);
p_script->constants.insert(name,constant->value);
//p_script->constants[constant->value].make_const();
+#ifdef TOOLS_ENABLED
+
+ p_script->member_lines[name]=p_class->constant_expressions[i].expression->line;
+#endif
+
}
for(int i=0;i<p_class->_signals.size();i++) {
@@ -1649,7 +1827,7 @@ Error GDCompiler::_parse_class(GDScript *p_script,GDScript *p_owner,const GDPars
}
if (native.is_valid()) {
- if (ObjectTypeDB::has_signal(native->get_name(),name)) {
+ if (ClassDB::has_signal(native->get_name(),name)) {
_set_error("Signal '"+name+"' redefined (original in native class '"+String(native->get_name())+"')",p_class);
return ERR_ALREADY_EXISTS;
}
@@ -1662,12 +1840,23 @@ Error GDCompiler::_parse_class(GDScript *p_script,GDScript *p_owner,const GDPars
for(int i=0;i<p_class->subclasses.size();i++) {
StringName name = p_class->subclasses[i]->name;
- Ref<GDScript> subclass = memnew( GDScript );
+ Ref<GDScript> subclass;
- Error err = _parse_class(subclass.ptr(),p_script,p_class->subclasses[i]);
+ if (old_subclasses.has(name)) {
+ subclass=old_subclasses[name];
+ } else {
+ subclass.instance();
+ }
+
+ Error err = _parse_class(subclass.ptr(),p_script,p_class->subclasses[i],p_keep_state);
if (err)
return err;
+#ifdef TOOLS_ENABLED
+
+ p_script->member_lines[name]=p_class->subclasses[i]->line;
+#endif
+
p_script->constants.insert(name,subclass); //once parsed, goes to the list of constants
p_script->subclasses.insert(name,subclass);
@@ -1755,13 +1944,67 @@ Error GDCompiler::_parse_class(GDScript *p_script,GDScript *p_owner,const GDPars
}
}
+
+ //validate instances if keeping state
+
+ if (p_keep_state) {
+
+ print_line("RELOAD KEEP "+p_script->path);
+ for (Set<Object*>::Element *E=p_script->instances.front();E;) {
+
+ Set<Object*>::Element *N = E->next();
+
+ ScriptInstance *si = E->get()->get_script_instance();
+ if (si->is_placeholder()) {
+#ifdef TOOLS_ENABLED
+ PlaceHolderScriptInstance *psi = static_cast<PlaceHolderScriptInstance*>(si);
+
+ if (p_script->is_tool()) {
+ //re-create as an instance
+ p_script->placeholders.erase(psi); //remove placeholder
+
+ GDInstance* instance = memnew( GDInstance );
+ instance->base_ref=E->get()->cast_to<Reference>();
+ instance->members.resize(p_script->member_indices.size());
+ instance->script=Ref<GDScript>(p_script);
+ instance->owner=E->get();
+
+ //needed for hot reloading
+ for(Map<StringName,GDScript::MemberInfo>::Element *E=p_script->member_indices.front();E;E=E->next()) {
+ instance->member_indices_cache[E->key()]=E->get().index;
+ }
+ instance->owner->set_script_instance(instance);
+
+
+ /* STEP 2, INITIALIZE AND CONSRTUCT */
+
+ Variant::CallError ce;
+ p_script->initializer->call(instance,NULL,0,ce);
+
+ if (ce.error!=Variant::CallError::CALL_OK) {
+ //well, tough luck, not goinna do anything here
+ }
+ }
+#endif
+ } else {
+
+ GDInstance *gi = static_cast<GDInstance*>(si);
+ gi->reload_members();
+ }
+
+ E=N;
+
+ }
+
+
+ }
#endif
p_script->valid=true;
return OK;
}
-Error GDCompiler::compile(const GDParser *p_parser,GDScript *p_script) {
+Error GDCompiler::compile(const GDParser *p_parser,GDScript *p_script,bool p_keep_state) {
err_line=-1;
err_column=-1;
@@ -1772,9 +2015,7 @@ Error GDCompiler::compile(const GDParser *p_parser,GDScript *p_script) {
source=p_script->get_path();
-
-
- Error err = _parse_class(p_script,NULL,static_cast<const GDParser::ClassNode*>(root));
+ Error err = _parse_class(p_script,NULL,static_cast<const GDParser::ClassNode*>(root),p_keep_state);
if (err)
return err;
diff --git a/modules/gdscript/gd_compiler.h b/modules/gdscript/gd_compiler.h
index 32e18c6dcf..dd211a852c 100644
--- a/modules/gdscript/gd_compiler.h
+++ b/modules/gdscript/gd_compiler.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2017 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 */
@@ -37,6 +37,7 @@ class GDCompiler {
const GDParser *parser;
struct CodeGen {
+
GDScript *script;
const GDParser::ClassNode *class_node;
const GDParser::FunctionNode *function_node;
@@ -134,6 +135,9 @@ class GDCompiler {
Ref<GDScript> _parse_class(GDParser::ClassNode *p_class);
#endif
+ bool _is_class_member_property(CodeGen & codegen, const StringName & p_name);
+ bool _is_class_member_property(GDScript *owner, const StringName & p_name);
+
void _set_error(const String& p_error,const GDParser::Node *p_node);
bool _create_unary_operator(CodeGen& codegen,const GDParser::OperatorNode *on,Variant::Operator op, int p_stack_level);
@@ -144,7 +148,7 @@ class GDCompiler {
int _parse_expression(CodeGen& codegen,const GDParser::Node *p_expression, int p_stack_level,bool p_root=false,bool p_initializer=false);
Error _parse_block(CodeGen& codegen,const GDParser::BlockNode *p_block,int p_stack_level=0,int p_break_addr=-1,int p_continue_addr=-1);
Error _parse_function(GDScript *p_script,const GDParser::ClassNode *p_class,const GDParser::FunctionNode *p_func,bool p_for_ready=false);
- Error _parse_class(GDScript *p_script,GDScript *p_owner,const GDParser::ClassNode *p_class);
+ Error _parse_class(GDScript *p_script,GDScript *p_owner,const GDParser::ClassNode *p_class,bool p_keep_state);
int err_line;
int err_column;
StringName source;
@@ -152,7 +156,7 @@ class GDCompiler {
public:
- Error compile(const GDParser *p_parser,GDScript *p_script);
+ Error compile(const GDParser *p_parser, GDScript *p_script, bool p_keep_state=false);
String get_error() const;
int get_error_line() const;
diff --git a/modules/gdscript/gd_editor.cpp b/modules/gdscript/gd_editor.cpp
index 7e5ff620c9..19472d3d46 100644
--- a/modules/gdscript/gd_editor.cpp
+++ b/modules/gdscript/gd_editor.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2017 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 */
@@ -44,21 +44,26 @@ void GDScriptLanguage::get_string_delimiters(List<String> *p_delimiters) const {
}
-String GDScriptLanguage::get_template(const String& p_class_name, const String& p_base_class_name) const {
+Ref<Script> GDScriptLanguage::get_template(const String& p_class_name, const String& p_base_class_name) const {
String _template = String()+
- "\nextends %BASE%\n\n"+
- "# member variables here, example:\n"+
- "# var a=2\n"+
- "# var b=\"textvar\"\n\n"+
+ "extends %BASE%\n\n"+
+ "# class member variables go here, for example:\n"+
+ "# var a = 2\n"+
+ "# var b = \"textvar\"\n\n"+
"func _ready():\n"+
"\t# Called every time the node is added to the scene.\n"+
"\t# Initialization here\n"+
- "\tpass\n"+
- "\n"+
- "\n";
+ "\tpass\n";
+
+ _template = _template.replace("%BASE%",p_base_class_name);
+
+ Ref<GDScript> script;
+ script.instance();
+ script->set_source_code(_template);
+
+ return script;
- return _template.replace("%BASE%",p_base_class_name);
}
@@ -212,7 +217,7 @@ String GDScriptLanguage::debug_get_stack_level_source(int p_level) const {
ERR_FAIL_INDEX_V(p_level,_debug_call_stack_pos,"");
int l = _debug_call_stack_pos - p_level -1;
- return _call_stack[l].function->get_script()->get_path();
+ return _call_stack[l].function->get_source();
}
void GDScriptLanguage::debug_get_stack_level_locals(int p_level,List<String> *p_locals, List<Variant> *p_values, int p_max_subitems,int p_max_depth) {
@@ -297,7 +302,7 @@ void GDScriptLanguage::get_public_functions(List<MethodInfo> *p_functions) const
}
{
MethodInfo mi;
- mi.name="yield";
+ mi.name="yield:GDFunctionState";
mi.arguments.push_back(PropertyInfo(Variant::OBJECT,"object"));
mi.arguments.push_back(PropertyInfo(Variant::STRING,"signal"));
mi.default_arguments.push_back(Variant::NIL);
@@ -320,7 +325,7 @@ void GDScriptLanguage::get_public_constants(List<Pair<String,Variant> > *p_const
p_constants->push_back(pi);
}
-String GDScriptLanguage::make_function(const String& p_class,const String& p_name,const StringArray& p_args) const {
+String GDScriptLanguage::make_function(const String& p_class,const String& p_name,const PoolStringArray& p_args) const {
String s="func "+p_name+"(";
if (p_args.size()) {
@@ -328,7 +333,7 @@ String GDScriptLanguage::make_function(const String& p_class,const String& p_nam
for(int i=0;i<p_args.size();i++) {
if (i>0)
s+=", ";
- s+=p_args[i];
+ s+=p_args[i].get_slice(":",0);
}
s+=" ";
}
@@ -346,6 +351,7 @@ struct GDCompletionIdentifier {
Ref<GDScript> script;
Variant::Type type;
Variant value; //im case there is a value, also return it
+
};
@@ -358,11 +364,13 @@ static GDCompletionIdentifier _get_type_from_variant(const Variant& p_variant) {
if (p_variant.get_type()==Variant::OBJECT) {
Object *obj = p_variant;
if (obj) {
- //if (obj->cast_to<GDNativeClass>()) {
- // t.obj_type=obj->cast_to<GDNativeClass>()->get_name();
- // t.value=Variant();
- //} else {
- t.obj_type=obj->get_type();
+ /*
+ if (obj->cast_to<GDNativeClass>()) {
+ t.obj_type=obj->cast_to<GDNativeClass>()->get_name();
+ t.value=Variant();
+ } else {
+ */
+ t.obj_type=obj->get_class();
//}
}
}
@@ -449,62 +457,21 @@ static Ref<Reference> _get_parent_class(GDCompletionContext& context) {
}
String base=context._class->extends_class[0];
- const GDParser::ClassNode *p = context._class->owner;
- Ref<GDScript> base_class;
-#if 0
- while(p) {
- if (p->subclasses.has(base)) {
+ if (context._class->extends_class.size()>1) {
- base_class=p->subclasses[base];
- break;
- }
- p=p->_owner;
- }
-#endif
- if (base_class.is_valid()) {
-#if 0
- for(int i=1;i<context._class->extends_class.size();i++) {
-
- String subclass=context._class->extends_class[i];
-
- if (base_class->subclasses.has(subclass)) {
-
- base_class=base_class->subclasses[subclass];
- } else {
-
- //print_line("Could not find subclass: "+subclass);
- return _get_type_from_class(context); //fail please
- }
- }
-
- script=base_class;
-#endif
-
- } else {
-
- if (context._class->extends_class.size()>1) {
-
- return REF();
-
-
- }
- //if not found, try engine classes
- if (!GDScriptLanguage::get_singleton()->get_global_map().has(base)) {
-
- return REF();
- }
-
- int base_idx = GDScriptLanguage::get_singleton()->get_global_map()[base];
- native = GDScriptLanguage::get_singleton()->get_global_array()[base_idx];
- if (!native.is_valid()) {
+ return REF();
- print_line("Global not a class: '"+base+"'");
+ }
+ //if not found, try engine classes
+ if (!GDScriptLanguage::get_singleton()->get_global_map().has(base)) {
- }
- return native;
+ return REF();
}
+ int base_idx = GDScriptLanguage::get_singleton()->get_global_map()[base];
+ native = GDScriptLanguage::get_singleton()->get_global_array()[base_idx];
+ return native;
}
@@ -649,10 +616,10 @@ static bool _guess_expression_type(GDCompletionContext& context,const GDParser::
}
}
- if (ObjectTypeDB::has_method(base.obj_type,id)) {
+ if (ClassDB::has_method(base.obj_type,id)) {
#ifdef TOOLS_ENABLED
- MethodBind *mb = ObjectTypeDB::get_method(base.obj_type,id);
+ MethodBind *mb = ClassDB::get_method(base.obj_type,id);
PropertyInfo pi = mb->get_argument_info(-1);
//try calling the function if constant and all args are constant, should not crash..
@@ -678,14 +645,14 @@ static bool _guess_expression_type(GDCompletionContext& context,const GDParser::
}
}
- if (all_valid && String(id)=="get_node" && ObjectTypeDB::is_type(base.obj_type,"Node") && args.size()) {
+ if (all_valid && String(id)=="get_node" && ClassDB::is_parent_class(base.obj_type,"Node") && args.size()) {
String arg1=args[0];
if (arg1.begins_with("/root/")) {
String which = arg1.get_slice("/",2);
if (which!="") {
List<PropertyInfo> props;
- Globals::get_singleton()->get_property_list(&props);
+ GlobalConfig::get_singleton()->get_property_list(&props);
//print_line("find singleton");
for(List<PropertyInfo>::Element *E=props.front();E;E=E->next()) {
@@ -697,7 +664,7 @@ static bool _guess_expression_type(GDCompletionContext& context,const GDParser::
String name = s.get_slice("/",1);
//print_line("name: "+name+", which: "+which);
if (name==which) {
- String script = Globals::get_singleton()->get(s);
+ String script = GlobalConfig::get_singleton()->get(s);
if (!script.begins_with("res://")) {
script="res://"+script;
@@ -706,7 +673,7 @@ static bool _guess_expression_type(GDCompletionContext& context,const GDParser::
if (!script.ends_with(".gd")) {
//not a script, try find the script anyway,
//may have some success
- script=script.basename()+".gd";
+ script=script.get_basename()+".gd";
}
if (FileAccess::exists(script)) {
@@ -971,9 +938,19 @@ static bool _guess_expression_type(GDCompletionContext& context,const GDParser::
return false;
}
+
static bool _guess_identifier_type_in_block(GDCompletionContext& context,int p_line,const StringName& p_identifier,GDCompletionIdentifier &r_type) {
+ GDCompletionIdentifier gdi = _get_native_class(context);
+ if (gdi.obj_type!=StringName()) {
+ bool valid;
+ Variant::Type t = ClassDB::get_property_type(gdi.obj_type,p_identifier,&valid);
+ if (t!=Variant::NIL && valid) {
+ r_type.type=t;
+ return true;
+ }
+ }
const GDParser::Node *last_assign=NULL;
int last_assign_line=-1;
@@ -1024,7 +1001,7 @@ static bool _guess_identifier_type_in_block(GDCompletionContext& context,int p_l
}
-static bool _guess_identifier_from_assignment_in_function(GDCompletionContext& context,const StringName& p_identifier, const StringName& p_function,GDCompletionIdentifier &r_type) {
+static bool _guess_identifier_from_assignment_in_function(GDCompletionContext& context, int p_src_line, const StringName& p_identifier, const StringName& p_function,GDCompletionIdentifier &r_type) {
const GDParser::FunctionNode* func=NULL;
for(int i=0;i<context._class->functions.size();i++) {
@@ -1039,7 +1016,9 @@ static bool _guess_identifier_from_assignment_in_function(GDCompletionContext& c
for(int i=0;i<func->body->statements.size();i++) {
-
+ if (func->body->statements[i]->line == p_src_line) {
+ break;
+ }
if (func->body->statements[i]->type==GDParser::BlockNode::TYPE_OPERATOR) {
const GDParser::OperatorNode *op = static_cast<const GDParser::OperatorNode *>(func->body->statements[i]);
@@ -1100,7 +1079,7 @@ static bool _guess_identifier_type(GDCompletionContext& context,int p_line,const
//this kinda sucks but meh
List<MethodInfo> vmethods;
- ObjectTypeDB::get_virtual_methods(id.obj_type,&vmethods);
+ ClassDB::get_virtual_methods(id.obj_type,&vmethods);
for (List<MethodInfo>::Element *E=vmethods.front();E;E=E->next()) {
@@ -1160,11 +1139,11 @@ static bool _guess_identifier_type(GDCompletionContext& context,int p_line,const
}
//try to guess from assignment in construtor or _ready
- if (_guess_identifier_from_assignment_in_function(context,p_identifier,"_ready",r_type))
+ if (_guess_identifier_from_assignment_in_function(context,p_line+1,p_identifier,"_ready",r_type))
return true;
- if (_guess_identifier_from_assignment_in_function(context,p_identifier,"_enter_tree",r_type))
+ if (_guess_identifier_from_assignment_in_function(context,p_line+1,p_identifier,"_enter_tree",r_type))
return true;
- if (_guess_identifier_from_assignment_in_function(context,p_identifier,"_init",r_type))
+ if (_guess_identifier_from_assignment_in_function(context,p_line+1,p_identifier,"_init",r_type))
return true;
return false;
@@ -1174,7 +1153,7 @@ static bool _guess_identifier_type(GDCompletionContext& context,int p_line,const
//autoloads as singletons
List<PropertyInfo> props;
- Globals::get_singleton()->get_property_list(&props);
+ GlobalConfig::get_singleton()->get_property_list(&props);
for(List<PropertyInfo>::Element *E=props.front();E;E=E->next()) {
@@ -1184,14 +1163,14 @@ static bool _guess_identifier_type(GDCompletionContext& context,int p_line,const
String name = s.get_slice("/",1);
if (name==String(p_identifier)) {
- String path = Globals::get_singleton()->get(s);
+ String path = GlobalConfig::get_singleton()->get(s);
if (path.begins_with("*")) {
String script =path.substr(1,path.length());
if (!script.ends_with(".gd")) {
//not a script, try find the script anyway,
//may have some success
- script=script.basename()+".gd";
+ script=script.get_basename()+".gd";
}
if (FileAccess::exists(script)) {
@@ -1330,26 +1309,43 @@ static void _find_identifiers_in_class(GDCompletionContext& context,bool p_stati
base=script->get_native();
} else if (nc.is_valid()) {
+ StringName type = nc->get_name();
+
if (!p_only_functions) {
- StringName type = nc->get_name();
+
List<String> constants;
- ObjectTypeDB::get_integer_constant_list(type,&constants);
+ ClassDB::get_integer_constant_list(type,&constants);
for(List<String>::Element *E=constants.front();E;E=E->next()) {
result.insert(E->get());
}
- List<MethodInfo> methods;
- ObjectTypeDB::get_method_list(type,&methods);
- for(List<MethodInfo>::Element *E=methods.front();E;E=E->next()) {
- if (E->get().name.begins_with("_"))
+ List<PropertyInfo> pinfo;
+
+ ClassDB::get_property_list(type,&pinfo);
+
+ for (List<PropertyInfo>::Element *E=pinfo.front();E;E=E->next()) {
+ if (E->get().usage&(PROPERTY_USAGE_GROUP|PROPERTY_USAGE_CATEGORY))
continue;
- if (E->get().arguments.size())
- result.insert(E->get().name+"(");
- else
- result.insert(E->get().name+"()");
+ if (E->get().name.find("/")!=-1)
+ continue;
+ result.insert(E->get().name);
}
+
+ }
+ List<MethodInfo> methods;
+ ClassDB::get_method_list(type,&methods);
+ for(List<MethodInfo>::Element *E=methods.front();E;E=E->next()) {
+ if (E->get().name.begins_with("_"))
+ continue;
+ if (E->get().arguments.size())
+ result.insert(E->get().name+"(");
+ else
+ result.insert(E->get().name+"()");
}
+
+
+
break;
} else
break;
@@ -1399,7 +1395,7 @@ static void _find_identifiers(GDCompletionContext& context,int p_line,bool p_onl
}
static const char*_type_names[Variant::VARIANT_MAX]={
- "null","bool","int","float","String","Vector2","Rect2","Vector3","Matrix32","Plane","Quat","AABB","Matrix3","Transform",
+ "null","bool","int","float","String","Vector2","Rect2","Vector3","Transform2D","Plane","Quat","AABB","Basis","Transform",
"Color","Image","NodePath","RID","Object","InputEvent","Dictionary","Array","RawArray","IntArray","FloatArray","StringArray",
"Vector2Array","Vector3Array","ColorArray"};
@@ -1409,7 +1405,7 @@ static void _find_identifiers(GDCompletionContext& context,int p_line,bool p_onl
//autoload singletons
List<PropertyInfo> props;
- Globals::get_singleton()->get_property_list(&props);
+ GlobalConfig::get_singleton()->get_property_list(&props);
for(List<PropertyInfo>::Element *E=props.front();E;E=E->next()) {
@@ -1417,7 +1413,7 @@ static void _find_identifiers(GDCompletionContext& context,int p_line,bool p_onl
if (!s.begins_with("autoload/"))
continue;
String name = s.get_slice("/",1);
- String path = Globals::get_singleton()->get(s);
+ String path = GlobalConfig::get_singleton()->get(s);
if (path.begins_with("*")) {
result.insert(name);
}
@@ -1495,14 +1491,14 @@ static void _make_function_hint(const GDParser::FunctionNode* p_func,int p_argid
}
-static void _find_type_arguments(const GDParser::Node*p_node,int p_line,const StringName& p_method,const GDCompletionIdentifier& id, int p_argidx, Set<String>& result, String& arghint) {
+static void _find_type_arguments(GDCompletionContext& context,const GDParser::Node*p_node,int p_line,const StringName& p_method,const GDCompletionIdentifier& id, int p_argidx, Set<String>& result, String& arghint) {
//print_line("find type arguments?");
if (id.type==Variant::INPUT_EVENT && String(p_method)=="is_action" && p_argidx==0) {
List<PropertyInfo> pinfo;
- Globals::get_singleton()->get_property_list(&pinfo);
+ GlobalConfig::get_singleton()->get_property_list(&pinfo);
for(List<PropertyInfo>::Element *E=pinfo.front();E;E=E->next()) {
const PropertyInfo &pi=E->get();
@@ -1518,7 +1514,7 @@ static void _find_type_arguments(const GDParser::Node*p_node,int p_line,const St
} else if (id.type==Variant::OBJECT && id.obj_type!=StringName()) {
- MethodBind *m = ObjectTypeDB::get_method(id.obj_type,p_method);
+ MethodBind *m = ClassDB::get_method(id.obj_type,p_method);
if (!m) {
//not in static method, see script
@@ -1731,10 +1727,32 @@ static void _find_type_arguments(const GDParser::Node*p_node,int p_line,const St
if (p_argidx==0) {
List<MethodInfo> sigs;
- ObjectTypeDB::get_signal_list(id.obj_type,&sigs);
+ ClassDB::get_signal_list(id.obj_type,&sigs);
+
+ if (id.script.is_valid()) {
+ id.script->get_script_signal_list(&sigs);
+ } else if (id.value.get_type()==Variant::OBJECT) {
+ Object *obj = id.value;
+ if (obj && !obj->get_script().is_null()) {
+ Ref<Script> scr=obj->get_script();
+ if (scr.is_valid()) {
+ scr->get_script_signal_list(&sigs);
+ }
+ }
+ }
+
for (List<MethodInfo>::Element *E=sigs.front();E;E=E->next()) {
result.insert("\""+E->get().name+"\"");
}
+
+ } else if (p_argidx==2){
+
+
+ if (context._class) {
+ for(int i=0;i<context._class->functions.size();i++) {
+ result.insert("\""+context._class->functions[i]->name+"\"");
+ }
+ }
}
/*if (p_argidx==2) {
@@ -1745,17 +1763,17 @@ static void _find_type_arguments(const GDParser::Node*p_node,int p_line,const St
}*/
} else {
- if (p_argidx==0 && (String(p_method)=="get_node" || String(p_method)=="has_node") && ObjectTypeDB::is_type(id.obj_type,"Node")) {
+ if (p_argidx==0 && (String(p_method)=="get_node" || String(p_method)=="has_node") && ClassDB::is_parent_class(id.obj_type,"Node")) {
List<PropertyInfo> props;
- Globals::get_singleton()->get_property_list(&props);
+ GlobalConfig::get_singleton()->get_property_list(&props);
for(List<PropertyInfo>::Element *E=props.front();E;E=E->next()) {
String s = E->get().name;
if (!s.begins_with("autoload/"))
continue;
- // print_line("found "+s);
+ //print_line("found "+s);
String name = s.get_slice("/",1);
result.insert("\"/root/"+name+"\"");
}
@@ -1976,16 +1994,16 @@ static void _find_call_arguments(GDCompletionContext& context,const GDParser::No
if (!context._class->owner)
ci.value=context.base;
- _find_type_arguments(p_node,p_line,id->name,ci,p_argidx,result,arghint);
+ _find_type_arguments(context,p_node,p_line,id->name,ci,p_argidx,result,arghint);
//guess type..
/*
List<MethodInfo> methods;
- ObjectTypeDB::get_method_list(type,&methods);
+ ClassDB::get_method_list(type,&methods);
for(List<MethodInfo>::Element *E=methods.front();E;E=E->next()) {
- //if (E->get().arguments.size())
- // result.insert(E->get().name+"(");
- //else
- // result.insert(E->get().name+"()");
+ if (E->get().arguments.size())
+ result.insert(E->get().name+"(");
+ else
+ result.insert(E->get().name+"()");
}*/
}
break;
@@ -1999,7 +2017,7 @@ static void _find_call_arguments(GDCompletionContext& context,const GDParser::No
GDCompletionIdentifier ci;
if (_guess_expression_type(context,op->arguments[0],p_line,ci)) {
- _find_type_arguments(p_node,p_line,id->name,ci,p_argidx,result,arghint);
+ _find_type_arguments(context,p_node,p_line,id->name,ci,p_argidx,result,arghint);
return;
}
@@ -2073,13 +2091,13 @@ static void _find_call_arguments(GDCompletionContext& context,const GDParser::No
StringName type = nc->get_name();
List<String> constants;
- ObjectTypeDB::get_integer_constant_list(type,&constants);
+ ClassDB::get_integer_constant_list(type,&constants);
for(List<String>::Element *E=constants.front();E;E=E->next()) {
result.insert(E->get());
}
List<MethodInfo> methods;
- ObjectTypeDB::get_method_list(type,&methods);
+ ClassDB::get_method_list(type,&methods);
for(List<MethodInfo>::Element *E=methods.front();E;E=E->next()) {
if (E->get().arguments.size())
result.insert(E->get().name+"(");
@@ -2103,12 +2121,10 @@ static void _find_call_arguments(GDCompletionContext& context,const GDParser::No
}
Error GDScriptLanguage::complete_code(const String& p_code, const String& p_base_path, Object*p_owner, List<String>* r_options, String &r_call_hint) {
- //print_line( p_code.replace(String::chr(0xFFFF),"<cursor>"));
GDParser p;
- //Error parse(const String& p_code, const String& p_base_path="", bool p_just_validate=false,const String& p_self_path="",bool p_for_completion=false);
- Error err = p.parse(p_code,p_base_path,false,"",true);
+ p.parse(p_code,p_base_path,false,"",true);
bool isfunction=false;
Set<String> options;
@@ -2122,10 +2138,8 @@ Error GDScriptLanguage::complete_code(const String& p_code, const String& p_base
switch(p.get_completion_type()) {
case GDParser::COMPLETION_NONE: {
- print_line("No completion");
} break;
case GDParser::COMPLETION_BUILT_IN_TYPE_CONSTANT: {
- print_line("Built in type constant");
List<StringName> constants;
Variant::get_numeric_constants_for_type(p.get_completion_built_in_constant(),&constants);
for(List<StringName>::Element *E=constants.front();E;E=E->next()) {
@@ -2141,9 +2155,29 @@ Error GDScriptLanguage::complete_code(const String& p_code, const String& p_base
_find_identifiers(context,p.get_completion_line(),isfunction,options);
} break;
case GDParser::COMPLETION_PARENT_FUNCTION: {
- print_line("parent function");
} break;
+ case GDParser::COMPLETION_GET_NODE: {
+
+ if (p_owner) {
+ List<String> opts;
+ p_owner->get_argument_options("get_node",0,&opts);
+
+ for (List<String>::Element *E=opts.front();E;E=E->next()) {
+
+ String opt = E->get().strip_edges();
+ if (opt.begins_with("\"") && opt.ends_with("\"")) {
+ String idopt=opt.substr(1,opt.length()-2);
+ if (idopt.replace("/","_").is_valid_identifier()) {
+ options.insert(idopt);
+ } else {
+ options.insert(opt);
+ }
+ }
+ }
+
+ }
+ } break;
case GDParser::COMPLETION_METHOD:
isfunction=true;
case GDParser::COMPLETION_INDEX: {
@@ -2158,7 +2192,29 @@ Error GDScriptLanguage::complete_code(const String& p_code, const String& p_base
GDCompletionIdentifier t;
if (_guess_expression_type(context,static_cast<const GDParser::OperatorNode *>(node)->arguments[0],p.get_completion_line(),t)) {
- if (t.type==Variant::OBJECT && t.obj_type!=StringName()) {
+ if (t.type==Variant::OBJECT && t.obj_type=="GDNativeClass") {
+ //native enum
+ Ref<GDNativeClass> gdn = t.value;
+ if (gdn.is_valid()) {
+ StringName cn = gdn->get_name();
+ List<String> cnames;
+ ClassDB::get_integer_constant_list(cn,&cnames);
+ for (List<String>::Element *E=cnames.front();E;E=E->next()) {
+ options.insert(E->get());
+ }
+
+ List<PropertyInfo> pinfo;
+ ClassDB::get_property_list(cn,&pinfo);
+
+ for (List<PropertyInfo>::Element *E=pinfo.front();E;E=E->next()) {
+ if (E->get().usage&(PROPERTY_USAGE_GROUP|PROPERTY_USAGE_CATEGORY))
+ continue;
+ if (E->get().name.find("/")!=-1)
+ continue;
+ options.insert(E->get().name);
+ }
+ }
+ } else if (t.type==Variant::OBJECT && t.obj_type!=StringName()) {
Ref<GDScript> on_script;
@@ -2210,7 +2266,6 @@ Error GDScriptLanguage::complete_code(const String& p_code, const String& p_base
if (code!="") {
//if there is code, parse it. This way is slower but updates in real-time
GDParser p;
- //Error parse(const String& p_code, const String& p_base_path="", bool p_just_validate=false,const String& p_self_path="",bool p_for_completion=false);
Error err = p.parse(scr->get_source_code(),scr->get_path().get_base_dir(),true,"",false);
@@ -2293,10 +2348,23 @@ Error GDScriptLanguage::complete_code(const String& p_code, const String& p_base
if (!isfunction) {
- ObjectTypeDB::get_integer_constant_list(t.obj_type,r_options);
+ ClassDB::get_integer_constant_list(t.obj_type,r_options);
+
+ List<PropertyInfo> pinfo;
+ ClassDB::get_property_list(t.obj_type,&pinfo);
+
+ for (List<PropertyInfo>::Element *E=pinfo.front();E;E=E->next()) {
+ if (E->get().usage&(PROPERTY_USAGE_GROUP|PROPERTY_USAGE_CATEGORY))
+ continue;
+ if (E->get().name.find("/")!=-1)
+ continue;
+ r_options->push_back(E->get().name);
+ }
}
+
+
List<MethodInfo> mi;
- ObjectTypeDB::get_method_list(t.obj_type,&mi);
+ ClassDB::get_method_list(t.obj_type,&mi);
for (List<MethodInfo>::Element *E=mi.front();E;E=E->next()) {
if (E->get().name.begins_with("_"))
@@ -2325,8 +2393,8 @@ Error GDScriptLanguage::complete_code(const String& p_code, const String& p_base
"# Key",
"# MouseMotion",
"# MouseButton",
- "# JoystickMotion",
- "# JoystickButton",
+ "# JoypadMotion",
+ "# JoypadButton",
"# ScreenTouch",
"# ScreenDrag",
"# Action"
@@ -2402,7 +2470,7 @@ Error GDScriptLanguage::complete_code(const String& p_code, const String& p_base
if (cid.obj_type!=StringName()) {
List<MethodInfo> vm;
- ObjectTypeDB::get_virtual_methods(cid.obj_type,&vm);
+ ClassDB::get_virtual_methods(cid.obj_type,&vm);
for(List<MethodInfo>::Element *E=vm.front();E;E=E->next()) {
MethodInfo &mi=E->get();
@@ -2427,7 +2495,24 @@ Error GDScriptLanguage::complete_code(const String& p_code, const String& p_base
}
}
} break;
+ case GDParser::COMPLETION_YIELD: {
+ const GDParser::Node *node = p.get_completion_node();
+
+ GDCompletionIdentifier t;
+ if (!_guess_expression_type(context,node,p.get_completion_line(),t))
+ break;
+
+ if (t.type==Variant::OBJECT && t.obj_type!=StringName()) {
+
+ List<MethodInfo> sigs;
+ ClassDB::get_signal_list(t.obj_type,&sigs);
+ for (List<MethodInfo>::Element *E=sigs.front();E;E=E->next()) {
+ options.insert("\""+E->get().name+"\"");
+ }
+ }
+
+ } break;
}
@@ -2512,3 +2597,456 @@ void GDScriptLanguage::auto_indent_code(String& p_code,int p_from_line,int p_to_
}
}
+
+#ifdef TOOLS_ENABLED
+
+Error GDScriptLanguage::lookup_code(const String& p_code, const String& p_symbol,const String& p_base_path, Object*p_owner,LookupResult& r_result) {
+
+
+ //before parsing, try the usual stuff
+ if (ClassDB::class_exists(p_symbol)) {
+ r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS;
+ r_result.class_name=p_symbol;
+ return OK;
+ }
+
+ for(int i=0;i<Variant::VARIANT_MAX;i++) {
+ Variant::Type t = Variant::Type(i);
+ if (Variant::get_type_name(t)==p_symbol) {
+ r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS;
+ r_result.class_name=Variant::get_type_name(t);
+ return OK;
+ }
+ }
+
+ for(int i=0;i<GDFunctions::FUNC_MAX;i++) {
+ if (GDFunctions::get_func_name(GDFunctions::Function(i))==p_symbol) {
+ r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS_METHOD;
+ r_result.class_name="@GDScript";
+ r_result.class_member=p_symbol;
+ return OK;
+ }
+ }
+
+ GDParser p;
+ p.parse(p_code,p_base_path,false,"",true);
+
+ if (p.get_completion_type()==GDParser::COMPLETION_NONE)
+ return ERR_CANT_RESOLVE;
+
+ GDCompletionContext context;
+
+ context._class=p.get_completion_class();
+ context.block=p.get_completion_block();
+ context.function=p.get_completion_function();
+ context.base=p_owner;
+ context.base_path=p_base_path;
+ bool isfunction=false;
+
+ switch(p.get_completion_type()) {
+
+ case GDParser::COMPLETION_NONE: {
+ } break;
+ case GDParser::COMPLETION_BUILT_IN_TYPE_CONSTANT: {
+
+ r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT;
+ r_result.class_name=Variant::get_type_name(p.get_completion_built_in_constant());
+ r_result.class_member=p_symbol;
+ return OK;
+
+ } break;
+ case GDParser::COMPLETION_FUNCTION: {
+
+
+ if (context._class && context._class->functions.size()) {
+ for(int i=0;i<context._class->functions.size();i++) {
+ if (context._class->functions[i]->name==p_symbol) {
+ r_result.type=ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
+ r_result.location=context._class->functions[i]->line;
+ return OK;
+ }
+ }
+ }
+
+ Ref<GDScript> parent = _get_parent_class(context);
+ while(parent.is_valid()) {
+ int line = parent->get_member_line(p_symbol);
+ if (line>=0) {
+ r_result.type=ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
+ r_result.location=line;
+ r_result.script=parent;
+ return OK;
+
+ }
+
+ parent=parent->get_base();
+ }
+
+ GDCompletionIdentifier identifier = _get_native_class(context);
+ print_line("identifier: "+String(identifier.obj_type));
+
+ if (ClassDB::has_method(identifier.obj_type,p_symbol)) {
+
+ r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS_METHOD;
+ r_result.class_name=identifier.obj_type;
+ r_result.class_member=p_symbol;
+ return OK;
+ }
+
+
+ } break;
+ case GDParser::COMPLETION_IDENTIFIER: {
+
+ //check if a function
+ if (p.get_completion_identifier_is_function()) {
+ if (context._class && context._class->functions.size()) {
+ for(int i=0;i<context._class->functions.size();i++) {
+ if (context._class->functions[i]->name==p_symbol) {
+ r_result.type=ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
+ r_result.location=context._class->functions[i]->line;
+ return OK;
+ }
+ }
+ }
+
+ Ref<GDScript> parent = _get_parent_class(context);
+ while(parent.is_valid()) {
+ int line = parent->get_member_line(p_symbol);
+ if (line>=0) {
+ r_result.type=ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
+ r_result.location=line;
+ r_result.script=parent;
+ return OK;
+
+ }
+
+ parent=parent->get_base();
+ }
+
+ GDCompletionIdentifier identifier = _get_native_class(context);
+
+
+ if (ClassDB::has_method(identifier.obj_type,p_symbol)) {
+
+ r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS_METHOD;
+ r_result.class_name=identifier.obj_type;
+ r_result.class_member=p_symbol;
+ return OK;
+ }
+ } else {
+
+
+ GDCompletionIdentifier gdi = _get_native_class(context);
+ if (gdi.obj_type!=StringName()) {
+ bool valid;
+ Variant::Type t = ClassDB::get_property_type(gdi.obj_type,p_symbol,&valid);
+ if (t!=Variant::NIL && valid) {
+ r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS_PROPERTY;
+ r_result.class_name=gdi.obj_type;
+ r_result.class_member=p_symbol;
+ return OK;
+
+ }
+ }
+
+ const GDParser::BlockNode *block=context.block;
+ //search in blocks going up (local var?)
+ while(block) {
+
+
+
+ for (int i=0;i<block->statements.size();i++) {
+
+ if (block->statements[i]->line>p.get_completion_line())
+ continue;
+
+
+ if (block->statements[i]->type==GDParser::BlockNode::TYPE_LOCAL_VAR) {
+
+ const GDParser::LocalVarNode *lv=static_cast<const GDParser::LocalVarNode *>(block->statements[i]);
+
+ if (lv->assign && lv->name==p_symbol) {
+
+ r_result.type=ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
+ r_result.location=block->statements[i]->line;
+ return OK;
+ }
+ }
+ }
+ block=block->parent_block;
+ }
+
+ //guess from function arguments
+ if (context.function && context.function->name!=StringName()) {
+
+ for(int i=0;i<context.function->arguments.size();i++) {
+
+ if (context.function->arguments[i]==p_symbol) {
+ r_result.type=ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
+ r_result.location=context.function->line;
+ return OK;
+ }
+
+ }
+ }
+
+ //guess in class constants
+
+ for(int i=0;i<context._class->constant_expressions.size();i++) {
+
+ if (context._class->constant_expressions[i].identifier==p_symbol) {
+ r_result.type=ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
+ r_result.location=context._class->constant_expressions[i].expression->line;
+ return OK;
+ }
+ }
+
+ //guess in class variables
+ if (!(context.function && context.function->_static)) {
+
+ for(int i=0;i<context._class->variables.size();i++) {
+
+ if (context._class->variables[i].identifier==p_symbol) {
+
+ r_result.type=ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
+ r_result.location=context._class->variables[i].line;
+ return OK;
+ }
+ }
+ }
+
+ //guess in autoloads as singletons
+ List<PropertyInfo> props;
+ GlobalConfig::get_singleton()->get_property_list(&props);
+
+ for(List<PropertyInfo>::Element *E=props.front();E;E=E->next()) {
+
+ String s = E->get().name;
+ if (!s.begins_with("autoload/"))
+ continue;
+ String name = s.get_slice("/",1);
+ if (name==String(p_symbol)) {
+
+ String path = GlobalConfig::get_singleton()->get(s);
+ if (path.begins_with("*")) {
+ String script =path.substr(1,path.length());
+
+ if (!script.ends_with(".gd")) {
+ //not a script, try find the script anyway,
+ //may have some success
+ script=script.get_basename()+".gd";
+ }
+
+ if (FileAccess::exists(script)) {
+
+ r_result.type=ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
+ r_result.location=0;
+ r_result.script=ResourceLoader::load(script);
+ return OK;
+ }
+ }
+ }
+ }
+
+ //global
+ for(Map<StringName,int>::Element *E=GDScriptLanguage::get_singleton()->get_global_map().front();E;E=E->next()) {
+ if (E->key()==p_symbol) {
+
+ Variant value = GDScriptLanguage::get_singleton()->get_global_array()[E->get()];
+ if (value.get_type()==Variant::OBJECT) {
+ Object *obj = value;
+ if (obj) {
+
+ if (obj->cast_to<GDNativeClass>()) {
+ r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS;
+ r_result.class_name=obj->cast_to<GDNativeClass>()->get_name();
+
+ } else {
+ r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS;
+ r_result.class_name=obj->get_class();
+ }
+ return OK;
+ }
+ } else {
+
+ r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT;
+ r_result.class_name="@Global Scope";
+ r_result.class_member=p_symbol;
+ return OK;
+ }
+ }
+
+ }
+#if 0
+ GDCompletionIdentifier identifier;
+ if (_guess_identifier_type(context,p.get_completion_line(),p_symbol,identifier)) {
+
+ print_line("var type: "+Variant::get_type_name(identifier.type));
+ if (identifier.script.is_valid()) {
+ print_line("var script: "+identifier.script->get_path());
+ }
+ print_line("obj type: "+String(identifier.obj_type));
+ print_line("value: "+String(identifier.value));
+ }
+#endif
+ }
+
+ } break;
+ case GDParser::COMPLETION_PARENT_FUNCTION: {
+
+ } break;
+ case GDParser::COMPLETION_METHOD:
+ isfunction=true;
+ case GDParser::COMPLETION_INDEX: {
+
+ const GDParser::Node *node = p.get_completion_node();
+ if (node->type!=GDParser::Node::TYPE_OPERATOR)
+ break;
+
+
+
+
+ GDCompletionIdentifier t;
+ if (_guess_expression_type(context,static_cast<const GDParser::OperatorNode *>(node)->arguments[0],p.get_completion_line(),t)) {
+
+ if (t.type==Variant::OBJECT && t.obj_type=="GDNativeClass") {
+ //native enum
+ Ref<GDNativeClass> gdn = t.value;
+ if (gdn.is_valid()) {
+ r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT;
+ r_result.class_name=gdn->get_name();;
+ r_result.class_member=p_symbol;
+ return OK;
+
+ }
+ } else if (t.type==Variant::OBJECT && t.obj_type!=StringName()) {
+
+ Ref<GDScript> on_script;
+
+ if (t.value.get_type()) {
+ Object *obj=t.value;
+
+
+ if (obj) {
+
+
+ on_script=obj->get_script();
+
+ if (on_script.is_valid()) {
+ int loc = on_script->get_member_line(p_symbol);
+ if (loc>=0) {
+ r_result.script=on_script;
+ r_result.type=ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
+ r_result.location=loc;
+ return OK;
+ }
+ }
+ }
+ }
+
+ if (ClassDB::has_method(t.obj_type,p_symbol)) {
+
+ r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS_METHOD;
+ r_result.class_name=t.obj_type;
+ r_result.class_member=p_symbol;
+ return OK;
+
+ }
+
+ bool success;
+ ClassDB::get_integer_constant(t.obj_type,p_symbol,&success);
+ if (success) {
+ r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT;
+ r_result.class_name=t.obj_type;
+ r_result.class_member=p_symbol;
+ return OK;
+ }
+
+
+ ClassDB::get_property_type(t.obj_type,p_symbol,&success);
+
+ if (success) {
+ r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS_PROPERTY;
+ r_result.class_name=t.obj_type;
+ r_result.class_member=p_symbol;
+ return OK;
+ }
+
+
+ } else {
+
+ Variant::CallError ce;
+ Variant v = Variant::construct(t.type,NULL,0,ce);
+
+ bool valid;
+ v.get_numeric_constant_value(t.type,p_symbol,&valid);
+ if (valid) {
+ r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT;
+ r_result.class_name=Variant::get_type_name(t.type);
+ r_result.class_member=p_symbol;
+ return OK;
+ }
+
+ //todo check all inputevent types for property
+
+ v.get(p_symbol,&valid);
+
+ if (valid) {
+ r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS_PROPERTY;
+ r_result.class_name=Variant::get_type_name(t.type);
+ r_result.class_member=p_symbol;
+ return OK;
+ }
+
+ if (v.has_method(p_symbol)) {
+
+ r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS_METHOD;
+ r_result.class_name=Variant::get_type_name(t.type);
+ r_result.class_member=p_symbol;
+ return OK;
+
+ }
+
+
+ }
+ }
+
+
+ } break;
+ case GDParser::COMPLETION_CALL_ARGUMENTS: {
+
+ return ERR_CANT_RESOLVE;
+ } break;
+ case GDParser::COMPLETION_VIRTUAL_FUNC: {
+
+ GDCompletionIdentifier cid = _get_native_class(context);
+
+ if (cid.obj_type!=StringName()) {
+ List<MethodInfo> vm;
+ ClassDB::get_virtual_methods(cid.obj_type,&vm);
+ for(List<MethodInfo>::Element *E=vm.front();E;E=E->next()) {
+
+ if (p_symbol==E->get().name) {
+
+ r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS_METHOD;
+ r_result.class_name=cid.obj_type;
+ r_result.class_member=p_symbol;
+ return OK;
+
+ }
+ }
+ }
+ } break;
+ case GDParser::COMPLETION_YIELD: {
+
+ return ERR_CANT_RESOLVE;
+
+ } break;
+
+ }
+
+
+ return ERR_CANT_RESOLVE;
+}
+
+#endif
diff --git a/modules/gdscript/gd_function.cpp b/modules/gdscript/gd_function.cpp
new file mode 100644
index 0000000000..6659988602
--- /dev/null
+++ b/modules/gdscript/gd_function.cpp
@@ -0,0 +1,1499 @@
+#include "gd_function.h"
+#include "gd_script.h"
+#include "os/os.h"
+#include "gd_functions.h"
+
+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_CLASS: {
+
+ return &p_script->_static_ref;
+ } 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 *o=p_script;
+ ERR_FAIL_INDEX_V(address,_global_names_count,NULL);
+ const StringName *sn = &_global_names_ptr[address];
+
+ while(o) {
+ GDScript *s=o;
+ while(s) {
+
+ Map<StringName,Variant>::Element *E=s->constants.find(*sn);
+ if (E) {
+ return &E->get();
+ }
+ s=s->_base;
+ }
+ o=o->_owner;
+ }
+
+
+ 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. Nonexistent "+p_where+".";
+ } else if (p_err.error==Variant::CallError::CALL_ERROR_INSTANCE_IS_NULL) {
+ err_text="Attempt to call "+p_where+" on a null instance.";
+ } else {
+ err_text="Bug, call error: #"+itos(p_err.error);
+ }
+
+ return err_text;
+
+}
+
+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_class()+" ("+bobj->get_script_instance()->get_script()->get_path().get_file()+")";
+ else
+ basestr = bobj->get_class();
+ } 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, CallState *p_state) {
+
+
+ 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
+
+ uint32_t alloca_size=0;
+ GDScript *_class;
+ int ip=0;
+ int line=_initial_line;
+
+
+
+ if (p_state) {
+ //use existing (supplied) state (yielded)
+ stack=(Variant*)p_state->stack.ptr();
+ call_args=(Variant**)&p_state->stack[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
+
+ } else {
+
+ if (p_argcount!=_argument_count) {
+
+ if (p_argcount>_argument_count) {
+
+ r_err.error=Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
+ r_err.argument=_argument_count;
+
+
+ return Variant();
+ } else if (p_argcount < _argument_count - _default_arg_count) {
+
+ r_err.error=Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_err.argument=_argument_count - _default_arg_count;
+ return Variant();
+ } else {
+
+ defarg=_argument_count-p_argcount;
+ }
+ }
+
+ alloca_size = sizeof(Variant*)*_call_size + sizeof(Variant)*_stack_size;
+
+ if (alloca_size) {
+
+ uint8_t *aptr = (uint8_t*)alloca(alloca_size);
+
+ if (_stack_size) {
+
+ stack=(Variant*)aptr;
+ for(int i=0;i<p_argcount;i++)
+ 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;
+ }
+
+ 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;
+ }
+ }
+
+ 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
+
+
+#ifdef DEBUG_ENABLED
+
+ uint64_t function_start_time;
+ uint64_t function_call_time;
+
+ if (GDScriptLanguage::get_singleton()->profiling) {
+ function_start_time=OS::get_singleton()->get_ticks_usec();
+ function_call_time=0;
+ profile.call_count++;
+ profile.frame_call_count++;
+ }
+#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);
+
+#ifdef DEBUG_ENABLED
+ Variant ret;
+ Variant::evaluate(op,*a,*b,ret,valid);
+#else
+ Variant::evaluate(op,*a,*b,*dst,valid);
+#endif
+
+ if (!valid) {
+#ifdef DEBUG_ENABLED
+
+ if (ret.get_type()==Variant::STRING) {
+ //return a string when invalid with the error
+ err_text=ret;
+ err_text += " in operator '"+Variant::get_operator_name(op)+"'.";
+ } else {
+ err_text="Invalid operands '"+Variant::get_type_name(a->get_type())+"' and '"+Variant::get_type_name(b->get_type())+"' in operator '"+Variant::get_operator_name(op)+"'.";
+ }
+#endif
+ break;
+
+ }
+#ifdef DEBUG_ENABLED
+ *dst=ret;
+#endif
+
+ 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()) {
+
+ GDScript *cmp = static_cast<GDScript*>(obj_A->get_script_instance()->get_script().ptr());
+ //bool found=false;
+ while(cmp) {
+
+ if (cmp==scr_B) {
+ //inherits from script, all ok
+ extends_ok=true;
+ break;
+
+ }
+
+ cmp=cmp->_base;
+ }
+
+ }
+
+
+ } else {
+
+ GDNativeClass *nc= obj_B->cast_to<GDNativeClass>();
+
+ if (!nc) {
+
+ err_text="Right operand of 'extends' is not a class (type: '"+obj_B->get_class()+"').";
+ break;
+ }
+
+ extends_ok=ClassDB::is_parent_class(obj_A->get_class_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;
+#ifdef DEBUG_ENABLED
+ //allow better error message in cases where src and dst are the same stack position
+ Variant ret = src->get(*index,&valid);
+#else
+ *dst = src->get(*index,&valid);
+
+#endif
+ 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;
+ }
+#ifdef DEBUG_ENABLED
+ *dst=ret;
+#endif
+ 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(4);
+
+ 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;
+#ifdef DEBUG_ENABLED
+ //allow better error message in cases where src and dst are the same stack position
+ Variant ret = src->get_named(*index,&valid);
+
+#else
+ *dst = src->get_named(*index,&valid);
+#endif
+
+ if (!valid) {
+ if (src->has_method(*index)) {
+ err_text="Invalid get index '"+index->operator String()+"' (on base: '"+_get_var_type(src)+"'). Did you mean '."+index->operator String()+"()' ?";
+ } else {
+ err_text="Invalid get index '"+index->operator String()+"' (on base: '"+_get_var_type(src)+"').";
+ }
+ break;
+ }
+#ifdef DEBUG_ENABLED
+ *dst=ret;
+#endif
+ ip+=4;
+ } continue;
+ case OPCODE_SET_MEMBER: {
+
+ CHECK_SPACE(3);
+ int indexname = _code_ptr[ip+1];
+ ERR_BREAK(indexname<0 || indexname>=_global_names_count);
+ const StringName *index = &_global_names_ptr[indexname];
+ GET_VARIANT_PTR(src,2);
+
+ bool valid;
+ bool ok = ClassDB::set_property(p_instance->owner,*index,*src,&valid);
+#ifdef DEBUG_ENABLED
+ if (!ok) {
+ err_text="Internal error setting property: "+String(*index);
+ break;
+ } else if (!valid) {
+ err_text="Error setting property '"+String(*index)+"' with value of type "+Variant::get_type_name(src->get_type())+".";
+ break;
+
+ }
+#endif
+ ip+=3;
+ } continue;
+ case OPCODE_GET_MEMBER: {
+
+ CHECK_SPACE(3);
+ int indexname = _code_ptr[ip+1];
+ ERR_BREAK(indexname<0 || indexname>=_global_names_count);
+ const StringName *index = &_global_names_ptr[indexname];
+ GET_VARIANT_PTR(dst,2);
+ bool ok = ClassDB::get_property(p_instance->owner,*index,*dst);
+
+#ifdef DEBUG_ENABLED
+ if (!ok) {
+ err_text="Internal error getting property: "+String(*index);
+ break;
+ }
+#endif
+ ip+=3;
+
+ } 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; //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; //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;
+ }
+
+#ifdef DEBUG_ENABLED
+ uint64_t call_time;
+
+ if (GDScriptLanguage::get_singleton()->profiling) {
+ call_time=OS::get_singleton()->get_ticks_usec();
+ }
+
+#endif
+ Variant::CallError err;
+ if (call_ret) {
+
+ GET_VARIANT_PTR(ret,argc);
+ base->call_ptr(*methodname,(const Variant**)argptrs,argc,ret,err);
+ } else {
+
+ base->call_ptr(*methodname,(const Variant**)argptrs,argc,NULL,err);
+ }
+#ifdef DEBUG_ENABLED
+ if (GDScriptLanguage::get_singleton()->profiling) {
+ function_call_time+=OS::get_singleton()->get_ticks_usec() - call_time;
+ }
+#endif
+
+ 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;
+ }
+ }
+ } 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;
+ }
+ }
+ }
+ 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);
+ if (dst->get_type()==Variant::STRING) {
+ //call provided error string
+ err_text="Error calling built-in function '"+methodstr+"': "+String(*dst);
+ } else {
+ err_text=_get_call_error(err,"built-in function '"+methodstr+"'",(const Variant**)argptrs);
+ }
+ 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=E->get()->call(p_instance,(const Variant**)argptrs,argc,err);
+ } else if (gds->native.ptr()) {
+
+ if (*methodname!=GDScriptLanguage::get_singleton()->strings._init) {
+
+ MethodBind *mb = ClassDB::get_method(gds->native->get_name(),*methodname);
+ if (!mb) {
+ err.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ } else {
+ *dst=mb->call(p_instance->owner,(const Variant**)argptrs,argc,err);
+ }
+ } else {
+ err.error=Variant::CallError::CALL_OK;
+ }
+ } else {
+
+ if (*methodname!=GDScriptLanguage::get_singleton()->strings._init) {
+ err.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ } else {
+ err.error=Variant::CallError::CALL_OK;
+ }
+ }
+
+
+ if (err.error!=Variant::CallError::CALL_OK) {
+
+
+ String methodstr = *methodname;
+ err_text=_get_call_error(err,"function '"+methodstr+"'",(const Variant**)argptrs);
+
+ break;
+ }
+
+ ip+=4+argc;
+
+ } 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++) {
+ memnew_placement(&gdfs->state.stack[sizeof(Variant)*i],Variant(stack[i]));
+ }
+ 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.instance_id=(p_instance && p_instance->get_owner())?p_instance->get_owner()->get_instance_ID():0;
+ gdfs->state.script_id=_class->get_instance_ID();
+ //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;
+
+ } 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_BREAKPOINT: {
+#ifdef DEBUG_ENABLED
+ if (ScriptDebugger::get_singleton()) {
+ GDScriptLanguage::get_singleton()->debug_break("Breakpoint Statement",true);
+ }
+#endif
+ ip+=1;
+ } 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(),ERR_HANDLER_SCRIPT);
+ }
+
+
+ break;
+ }
+
+#ifdef DEBUG_ENABLED
+ if (GDScriptLanguage::get_singleton()->profiling) {
+ uint64_t time_taken = OS::get_singleton()->get_ticks_usec() - function_start_time;
+ profile.total_time+=time_taken;
+ profile.self_time+=time_taken-function_call_time;
+ profile.frame_total_time+=time_taken;
+ profile.frame_self_time+=time_taken-function_call_time;
+ GDScriptLanguage::get_singleton()->script_frame_time+=time_taken-function_call_time;
+
+ }
+
+#endif
+ 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() : function_list(this) {
+
+ _stack_size=0;
+ _call_size=0;
+ rpc_mode=ScriptInstance::RPC_MODE_DISABLED;
+ name="<anonymous>";
+#ifdef DEBUG_ENABLED
+ _func_cname=NULL;
+
+ if (GDScriptLanguage::get_singleton()->lock) {
+ GDScriptLanguage::get_singleton()->lock->lock();
+ }
+ GDScriptLanguage::get_singleton()->function_list.add(&function_list);
+
+ if (GDScriptLanguage::get_singleton()->lock) {
+ GDScriptLanguage::get_singleton()->lock->unlock();
+ }
+
+ profile.call_count=0;
+ profile.self_time=0;
+ profile.total_time=0;
+ profile.frame_call_count=0;
+ profile.frame_self_time=0;
+ profile.frame_total_time=0;
+ profile.last_frame_call_count=0;
+ profile.last_frame_self_time=0;
+ profile.last_frame_total_time=0;
+
+#endif
+}
+
+GDFunction::~GDFunction() {
+#ifdef DEBUG_ENABLED
+ if (GDScriptLanguage::get_singleton()->lock) {
+ GDScriptLanguage::get_singleton()->lock->lock();
+ }
+ GDScriptLanguage::get_singleton()->function_list.remove(&function_list);
+
+ if (GDScriptLanguage::get_singleton()->lock) {
+ GDScriptLanguage::get_singleton()->lock->unlock();
+ }
+#endif
+}
+
+/////////////////////
+
+
+Variant GDFunctionState::_signal_callback(const Variant** p_args, int p_argcount, Variant::CallError& r_error) {
+
+#ifdef DEBUG_ENABLED
+ if (state.instance_id && !ObjectDB::get_instance(state.instance_id)) {
+ ERR_EXPLAIN("Resumed after yield, but class instance is gone");
+ ERR_FAIL_V(Variant());
+ }
+
+ if (state.script_id && !ObjectDB::get_instance(state.script_id)) {
+ ERR_EXPLAIN("Resumed after yield, but script is gone");
+ ERR_FAIL_V(Variant());
+ }
+#endif
+
+ 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());
+#ifdef DEBUG_ENABLED
+ if (state.instance_id && !ObjectDB::get_instance(state.instance_id)) {
+ ERR_EXPLAIN("Resumed after yield, but class instance is gone");
+ ERR_FAIL_V(Variant());
+ }
+
+ if (state.script_id && !ObjectDB::get_instance(state.script_id)) {
+ ERR_EXPLAIN("Resumed after yield, but script is gone");
+ ERR_FAIL_V(Variant());
+ }
+#endif
+
+ 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() {
+
+ ClassDB::bind_method(_MD("resume:Variant","arg"),&GDFunctionState::resume,DEFVAL(Variant()));
+ ClassDB::bind_method(_MD("is_valid"),&GDFunctionState::is_valid);
+ ClassDB::bind_vararg_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();
+ }
+ }
+}
+
diff --git a/modules/gdscript/gd_function.h b/modules/gdscript/gd_function.h
new file mode 100644
index 0000000000..e5262e8ad7
--- /dev/null
+++ b/modules/gdscript/gd_function.h
@@ -0,0 +1,225 @@
+#ifndef GD_FUNCTION_H
+#define GD_FUNCTION_H
+
+#include "self_list.h"
+#include "os/thread.h"
+#include "pair.h"
+#include "variant.h"
+#include "string_db.h"
+#include "reference.h"
+#include "script_language.h"
+
+class GDInstance;
+class GDScript;
+
+
+class GDFunction {
+public:
+
+ enum Opcode {
+ OPCODE_OPERATOR,
+ OPCODE_EXTENDS_TEST,
+ OPCODE_SET,
+ OPCODE_GET,
+ OPCODE_SET_NAMED,
+ OPCODE_GET_NAMED,
+ OPCODE_SET_MEMBER,
+ OPCODE_GET_MEMBER,
+ OPCODE_ASSIGN,
+ OPCODE_ASSIGN_TRUE,
+ OPCODE_ASSIGN_FALSE,
+ OPCODE_CONSTRUCT, //only for basic types!!
+ OPCODE_CONSTRUCT_ARRAY,
+ OPCODE_CONSTRUCT_DICTIONARY,
+ OPCODE_CALL,
+ OPCODE_CALL_RETURN,
+ OPCODE_CALL_BUILT_IN,
+ OPCODE_CALL_SELF,
+ OPCODE_CALL_SELF_BASE,
+ OPCODE_YIELD,
+ OPCODE_YIELD_SIGNAL,
+ OPCODE_YIELD_RESUME,
+ OPCODE_JUMP,
+ OPCODE_JUMP_IF,
+ OPCODE_JUMP_IF_NOT,
+ OPCODE_JUMP_TO_DEF_ARGUMENT,
+ OPCODE_RETURN,
+ OPCODE_ITERATE_BEGIN,
+ OPCODE_ITERATE,
+ OPCODE_ASSERT,
+ OPCODE_BREAKPOINT,
+ OPCODE_LINE,
+ OPCODE_END
+ };
+
+ enum Address {
+ ADDR_BITS=24,
+ ADDR_MASK=((1<<ADDR_BITS)-1),
+ ADDR_TYPE_MASK=~ADDR_MASK,
+ ADDR_TYPE_SELF=0,
+ ADDR_TYPE_CLASS=1,
+ ADDR_TYPE_MEMBER=2,
+ ADDR_TYPE_CLASS_CONSTANT=3,
+ ADDR_TYPE_LOCAL_CONSTANT=4,
+ ADDR_TYPE_STACK=5,
+ ADDR_TYPE_STACK_VARIABLE=6,
+ ADDR_TYPE_GLOBAL=7,
+ ADDR_TYPE_NIL=8
+ };
+
+ enum RPCMode {
+ RPC_DISABLED,
+ RPC_ENABLED,
+ RPC_SYNC,
+ RPC_SYNC_MASTER,
+ RPC_SYNC_SLAVE
+ };
+
+ struct StackDebug {
+
+ int line;
+ int pos;
+ bool added;
+ StringName identifier;
+ };
+
+private:
+friend class GDCompiler;
+
+ StringName source;
+
+ mutable Variant nil;
+ mutable Variant *_constants_ptr;
+ int _constant_count;
+ const StringName *_global_names_ptr;
+ int _global_names_count;
+ const int *_default_arg_ptr;
+ int _default_arg_count;
+ const int *_code_ptr;
+ int _code_size;
+ int _argument_count;
+ int _stack_size;
+ int _call_size;
+ int _initial_line;
+ bool _static;
+ ScriptInstance::RPCMode rpc_mode;
+
+ GDScript *_script;
+
+ StringName name;
+ Vector<Variant> constants;
+ Vector<StringName> global_names;
+ Vector<int> default_arguments;
+ Vector<int> code;
+
+#ifdef TOOLS_ENABLED
+ Vector<StringName> arg_names;
+#endif
+
+ List<StackDebug> stack_debug;
+
+ _FORCE_INLINE_ Variant *_get_variant(int p_address,GDInstance *p_instance,GDScript *p_script,Variant &self,Variant *p_stack,String& r_error) const;
+ _FORCE_INLINE_ String _get_call_error(const Variant::CallError& p_err, const String& p_where,const Variant**argptrs) const;
+
+friend class GDScriptLanguage;
+
+ SelfList<GDFunction> function_list;
+#ifdef DEBUG_ENABLED
+ CharString func_cname;
+ const char*_func_cname;
+
+ struct Profile {
+ StringName signature;
+ uint64_t call_count;
+ uint64_t self_time;
+ uint64_t total_time;
+ uint64_t frame_call_count;
+ uint64_t frame_self_time;
+ uint64_t frame_total_time;
+ uint64_t last_frame_call_count;
+ uint64_t last_frame_self_time;
+ uint64_t last_frame_total_time;
+ } profile;
+
+#endif
+
+public:
+
+
+
+ struct CallState {
+
+ ObjectID instance_id; //by debug only
+ ObjectID script_id;
+
+ GDInstance *instance;
+ Vector<uint8_t> stack;
+ int stack_size;
+ Variant self;
+ uint32_t alloca_size;
+ GDScript *_class;
+ int ip;
+ int line;
+ int defarg;
+ Variant result;
+
+ };
+
+ _FORCE_INLINE_ bool is_static() const { return _static; }
+
+ const int* get_code() const; //used for debug
+ int get_code_size() const;
+ Variant get_constant(int p_idx) const;
+ StringName get_global_name(int p_idx) const;
+ StringName get_name() const;
+ int get_max_stack_size() const;
+ int get_default_argument_count() const;
+ int get_default_argument_addr(int p_idx) const;
+ GDScript *get_script() const { return _script; }
+ StringName get_source() const { return source; }
+
+ void debug_get_stack_member_state(int p_line,List<Pair<StringName,int> > *r_stackvars) const;
+
+ _FORCE_INLINE_ bool is_empty() const { return _code_size==0; }
+
+ int get_argument_count() const { return _argument_count; }
+ StringName get_argument_name(int p_idx) const {
+#ifdef TOOLS_ENABLED
+ ERR_FAIL_INDEX_V(p_idx,arg_names.size(),StringName());
+ return arg_names[p_idx];
+#endif
+ return StringName();
+
+ }
+ Variant get_default_argument(int p_idx) const {
+ ERR_FAIL_INDEX_V(p_idx,default_arguments.size(),Variant());
+ return default_arguments[p_idx];
+ }
+
+ Variant call(GDInstance *p_instance,const Variant **p_args, int p_argcount,Variant::CallError& r_err,CallState *p_state=NULL);
+
+ _FORCE_INLINE_ ScriptInstance::RPCMode get_rpc_mode() const { return rpc_mode; }
+ GDFunction();
+ ~GDFunction();
+};
+
+
+class GDFunctionState : public Reference {
+
+ GDCLASS(GDFunctionState,Reference);
+friend class GDFunction;
+ GDFunction *function;
+ GDFunction::CallState state;
+ Variant _signal_callback(const Variant** p_args, int p_argcount, Variant::CallError& r_error);
+protected:
+ static void _bind_methods();
+public:
+
+ bool is_valid() const;
+ Variant resume(const Variant& p_arg=Variant());
+ GDFunctionState();
+ ~GDFunctionState();
+};
+
+
+#endif // GD_FUNCTION_H
diff --git a/modules/gdscript/gd_functions.cpp b/modules/gdscript/gd_functions.cpp
index 1c05a71d01..1c41b2e73b 100644
--- a/modules/gdscript/gd_functions.cpp
+++ b/modules/gdscript/gd_functions.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2017 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 */
@@ -35,6 +35,7 @@
#include "os/os.h"
#include "variant_parser.h"
#include "io/marshalls.h"
+#include "io/json.h"
const char *GDFunctions::get_func_name(Function p_func) {
@@ -87,6 +88,8 @@ const char *GDFunctions::get_func_name(Function p_func) {
"funcref",
"convert",
"typeof",
+ "type_exists",
+ "char",
"str",
"print",
"printt",
@@ -101,8 +104,12 @@ const char *GDFunctions::get_func_name(Function p_func) {
"load",
"inst2dict",
"dict2inst",
+ "validate_json",
+ "parse_json",
+ "to_json",
"hash",
"Color8",
+ "ColorN",
"print_stack",
"instance_from_id",
};
@@ -120,11 +127,13 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
if (p_arg_count<m_count) {\
r_error.error=Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;\
r_error.argument=m_count;\
+ r_ret=Variant();\
return;\
}\
if (p_arg_count>m_count) {\
r_error.error=Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;\
r_error.argument=m_count;\
+ r_ret=Variant();\
return;\
}
@@ -133,6 +142,7 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;\
r_error.argument=m_arg;\
r_error.expected=Variant::REAL;\
+ r_ret=Variant();\
return;\
}
@@ -244,6 +254,7 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument=0;
r_error.expected=Variant::REAL;
+ r_ret=Variant();
}
} break;
case MATH_SIGN: {
@@ -261,6 +272,7 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument=0;
r_error.expected=Variant::REAL;
+ r_ret=Variant();
}
} break;
case MATH_POW: {
@@ -298,7 +310,7 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
case MATH_DECIMALS: {
VALIDATE_ARG_COUNT(1);
VALIDATE_ARG_NUM(0);
- r_ret=Math::decimals(*p_args[0]);
+ r_ret=Math::step_decimals(*p_args[0]);
} break;
case MATH_STEPIFY: {
VALIDATE_ARG_COUNT(2);
@@ -442,6 +454,7 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument=0;
r_error.expected=Variant::OBJECT;
+ r_ret=Variant();
return;
}
@@ -479,7 +492,7 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument=0;
r_error.expected=Variant::OBJECT;
- r_ret=Variant();
+ r_ret=Variant();
return;
}
@@ -495,7 +508,6 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
Ref<FuncRef> fr = memnew( FuncRef);
- Object *obj = *p_args[0];
fr->set_instance(*p_args[0]);
fr->set_function(*p_args[1]);
@@ -508,8 +520,11 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
int type=*p_args[1];
if (type<0 || type>=Variant::VARIANT_MAX) {
- ERR_PRINT("Invalid type argument to convert()");
- r_ret=Variant::NIL;
+ r_ret=RTR("Invalid type argument to convert(), use TYPE_* constants.");
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=0;
+ r_error.expected=Variant::INT;
+ return;
} else {
@@ -523,6 +538,18 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
r_ret = p_args[0]->get_type();
} break;
+ case TYPE_EXISTS: {
+
+ VALIDATE_ARG_COUNT(1);
+ r_ret = ClassDB::class_exists(*p_args[0]);
+
+ } break;
+ case TEXT_CHAR: {
+ VALIDATE_ARG_COUNT(1);
+ VALIDATE_ARG_NUM(0);
+ CharType result[2] = {*p_args[0], 0};
+ r_ret=String(result);
+ } break;
case TEXT_STR: {
String str;
@@ -638,27 +665,28 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument=0;
r_error.expected=Variant::STRING;
- r_ret=Variant();
+ r_ret="Parse error at line "+itos(line)+": "+errs;
+ return;
}
} break;
case VAR_TO_BYTES: {
VALIDATE_ARG_COUNT(1);
- ByteArray barr;
+ PoolByteArray barr;
int len;
Error err = encode_variant(*p_args[0],NULL,len);
if (err) {
r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument=0;
r_error.expected=Variant::NIL;
- r_ret=Variant();
+ r_ret="Unexpected error encoding variable to bytes, likely unserializable type found (Object or RID).";
return;
}
barr.resize(len);
{
- ByteArray::Write w = barr.write();
+ PoolByteArray::Write w = barr.write();
encode_variant(*p_args[0],w.ptr(),len);
}
@@ -666,25 +694,24 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
} break;
case BYTES_TO_VAR: {
VALIDATE_ARG_COUNT(1);
- if (p_args[0]->get_type()!=Variant::RAW_ARRAY) {
+ if (p_args[0]->get_type()!=Variant::POOL_BYTE_ARRAY) {
r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument=0;
- r_error.expected=Variant::RAW_ARRAY;
+ r_error.expected=Variant::POOL_BYTE_ARRAY;
r_ret=Variant();
return;
}
- ByteArray varr=*p_args[0];
+ PoolByteArray varr=*p_args[0];
Variant ret;
{
- ByteArray::Read r=varr.read();
+ PoolByteArray::Read r=varr.read();
Error err = decode_variant(ret,r.ptr(),varr.size(),NULL);
if (err!=OK) {
- ERR_PRINT("Not enough bytes for decoding..");
+ r_ret=RTR("Not enough bytes for decoding bytes, or invalid format.");
r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument=0;
- r_error.expected=Variant::RAW_ARRAY;
- r_ret=Variant();
+ r_error.expected=Variant::POOL_BYTE_ARRAY;
return;
}
@@ -701,13 +728,14 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
r_error.error=Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
r_error.argument=1;
+ r_ret=Variant();
} break;
case 1: {
VALIDATE_ARG_NUM(0);
int count=*p_args[0];
- Array arr(true);
+ Array arr;
if (count<=0) {
r_ret=arr;
return;
@@ -733,7 +761,7 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
int from=*p_args[0];
int to=*p_args[1];
- Array arr(true);
+ Array arr;
if (from>=to) {
r_ret=arr;
return;
@@ -759,12 +787,12 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
int incr=*p_args[2];
if (incr==0) {
- ERR_EXPLAIN("step argument is zero!");
+ r_ret=RTR("step argument is zero!");
r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
- ERR_FAIL();
+ return;
}
- Array arr(true);
+ Array arr;
if (from>=to && incr>0) {
r_ret=arr;
return;
@@ -812,6 +840,8 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
r_error.error=Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
r_error.argument=3;
+ r_ret=Variant();
+
} break;
}
@@ -821,9 +851,11 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
if (p_args[0]->get_type()!=Variant::STRING) {
r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument=0;
+ r_error.expected=Variant::STRING;
r_ret=Variant();
+ } else {
+ r_ret=ResourceLoader::load(*p_args[0]);
}
- r_ret=ResourceLoader::load(*p_args[0]);
} break;
case INST2DICT: {
@@ -847,8 +879,8 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument=0;
r_error.expected=Variant::DICTIONARY;
- ERR_PRINT("Not a script with an instance");
-
+ r_ret=RTR("Not a script with an instance");
+ return;
} else {
GDInstance *ins = static_cast<GDInstance*>(obj->get_script_instance());
@@ -858,7 +890,7 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument=0;
r_error.expected=Variant::DICTIONARY;
- ERR_PRINT("Not based on a script");
+ r_ret=RTR("Not based on a script");
return;
}
@@ -879,15 +911,17 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument=0;
r_error.expected=Variant::DICTIONARY;
- print_line("PATH: "+p->path);
- ERR_PRINT("Not based on a resource file");
+ r_ret=Variant();
+
+
+ r_ret=RTR("Not based on a resource file");
return;
}
NodePath cp(sname,Vector<StringName>(),false);
- Dictionary d(true);
+ Dictionary d;
d["@subpath"]=cp;
d["@path"]=p->path;
@@ -926,6 +960,8 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument=0;
r_error.expected=Variant::DICTIONARY;
+ r_ret=Variant();
+
return;
}
@@ -936,6 +972,8 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument=0;
r_error.expected=Variant::OBJECT;
+ r_ret=RTR("Invalid instance dictionary format (missing @path)");
+
return;
}
@@ -945,6 +983,7 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument=0;
r_error.expected=Variant::OBJECT;
+ r_ret=RTR("Invalid instance dictionary format (can't load script at @path)");
return;
}
@@ -955,6 +994,8 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument=0;
r_error.expected=Variant::OBJECT;
+ r_ret=Variant();
+ r_ret=RTR("Invalid instance dictionary format (invalid script at @path)");
return;
}
@@ -971,22 +1012,75 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument=0;
r_error.expected=Variant::OBJECT;
+ r_ret=Variant();
+ r_ret=RTR("Invalid instance dictionary (invalid subclasses)");
return;
}
}
r_ret = gdscr->_new(NULL,0,r_error);
- GDInstance *ins = static_cast<GDInstance*>(static_cast<Object*>(r_ret)->get_script_instance());
- Ref<GDScript> gd_ref = ins->get_script();
+ GDInstance *ins = static_cast<GDInstance*>(static_cast<Object*>(r_ret)->get_script_instance());
+ Ref<GDScript> gd_ref = ins->get_script();
+
+ for(Map<StringName,GDScript::MemberInfo>::Element *E = gd_ref->member_indices.front(); E; E = E->next()) {
+ if(d.has(E->key())) {
+ ins->members[E->get().index] = d[E->key()];
+ }
+ }
+
+ } break;
+ case VALIDATE_JSON: {
+
+ VALIDATE_ARG_COUNT(1);
+
+ if (p_args[0]->get_type()!=Variant::STRING) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=0;
+ r_error.expected=Variant::STRING;
+ r_ret=Variant();
+ return;
+ }
+
+ String errs;
+ int errl;
+
+ Error err = JSON::parse(*p_args[0],r_ret,errs,errl);
- for(Map<StringName,GDScript::MemberInfo>::Element *E = gd_ref->member_indices.front(); E; E = E->next()) {
- if(d.has(E->key())) {
- ins->members[E->get().index] = d[E->key()];
- }
- }
+ if (err!=OK) {
+ r_ret=itos(errl)+":"+errs;
+ } else {
+ r_ret="";
+ }
} break;
+ case PARSE_JSON: {
+
+ VALIDATE_ARG_COUNT(1);
+
+ if (p_args[0]->get_type()!=Variant::STRING) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=0;
+ r_error.expected=Variant::STRING;
+ r_ret=Variant();
+ return;
+ }
+
+ String errs;
+ int errl;
+
+ Error err = JSON::parse(*p_args[0],r_ret,errs,errl);
+
+ if (err!=OK) {
+ r_ret=Variant();
+ }
+
+ } break;
+ case TO_JSON: {
+ VALIDATE_ARG_COUNT(1);
+
+ r_ret = JSON::print(*p_args[0]);
+ } break;
case HASH: {
VALIDATE_ARG_COUNT(1);
@@ -998,11 +1092,15 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
if (p_arg_count<3) {
r_error.error=Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
r_error.argument=3;
+ r_ret=Variant();
+
return;
}
if (p_arg_count>4) {
r_error.error=Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
r_error.argument=4;
+ r_ret=Variant();
+
return;
}
@@ -1010,16 +1108,46 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
VALIDATE_ARG_NUM(1);
VALIDATE_ARG_NUM(2);
- Color color(*p_args[0],*p_args[1],*p_args[2]);
+ Color color((float)*p_args[0]/255.0f,(float)*p_args[1]/255.0f,(float)*p_args[2]/255.0f);
if (p_arg_count==4) {
VALIDATE_ARG_NUM(3);
- color.a=*p_args[3];
+ color.a=(float)*p_args[3]/255.0f;
}
r_ret=color;
} break;
+ case COLORN: {
+
+ if (p_arg_count<1) {
+ r_error.error=Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_error.argument=1;
+ r_ret=Variant();
+ return;
+ }
+
+ if (p_arg_count>2) {
+ r_error.error=Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
+ r_error.argument=2;
+ r_ret=Variant();
+ return;
+ }
+
+ if (p_args[0]->get_type()!=Variant::STRING) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=0;
+ r_ret=Variant();
+ } else {
+ Color color = Color::named(*p_args[0]);
+ if (p_arg_count==2) {
+ VALIDATE_ARG_NUM(1);
+ color.a=*p_args[1];
+ }
+ r_ret=color;
+ }
+
+ } break;
case PRINT_STACK: {
@@ -1036,6 +1164,7 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
if (p_args[0]->get_type()!=Variant::INT && p_args[0]->get_type()!=Variant::REAL) {
r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument=0;
+ r_error.expected=Variant::INT;
r_ret=Variant();
break;
}
@@ -1098,6 +1227,8 @@ bool GDFunctions::is_deterministic(Function p_func) {
case LOGIC_NEAREST_PO2:
case TYPE_CONVERT:
case TYPE_OF:
+ case TYPE_EXISTS:
+ case TEXT_CHAR:
case TEXT_STR:
case COLOR8:
// enable for debug only, otherwise not desirable - case GEN_RANGE:
@@ -1281,12 +1412,12 @@ MethodInfo GDFunctions::get_info(Function p_func) {
return mi;
} break;
case MATH_SEED: {
- MethodInfo mi("seed",PropertyInfo(Variant::REAL,"seed"));
+ MethodInfo mi("seed",PropertyInfo(Variant::INT,"seed"));
mi.return_val.type=Variant::NIL;
return mi;
} break;
case MATH_RANDSEED: {
- MethodInfo mi("rand_seed",PropertyInfo(Variant::REAL,"seed"));
+ MethodInfo mi("rand_seed",PropertyInfo(Variant::INT,"seed"));
mi.return_val.type=Variant::ARRAY;
return mi;
} break;
@@ -1361,6 +1492,20 @@ MethodInfo GDFunctions::get_info(Function p_func) {
return mi;
} break;
+ case TYPE_EXISTS: {
+
+ MethodInfo mi("type_exists",PropertyInfo(Variant::STRING,"type"));
+ mi.return_val.type=Variant::BOOL;
+ return mi;
+
+ } break;
+ case TEXT_CHAR: {
+
+ MethodInfo mi("char",PropertyInfo(Variant::INT,"ascii"));
+ mi.return_val.type=Variant::STRING;
+ return mi;
+
+ } break;
case TEXT_STR: {
MethodInfo mi("str",PropertyInfo(Variant::NIL,"what"),PropertyInfo(Variant::NIL,"..."));
@@ -1417,13 +1562,13 @@ MethodInfo GDFunctions::get_info(Function p_func) {
} break;
case VAR_TO_BYTES: {
MethodInfo mi("var2bytes",PropertyInfo(Variant::NIL,"var"));
- mi.return_val.type=Variant::RAW_ARRAY;
+ mi.return_val.type=Variant::POOL_BYTE_ARRAY;
return mi;
} break;
case BYTES_TO_VAR: {
- MethodInfo mi("bytes2var:Variant",PropertyInfo(Variant::RAW_ARRAY,"bytes"));
+ MethodInfo mi("bytes2var:Variant",PropertyInfo(Variant::POOL_BYTE_ARRAY,"bytes"));
mi.return_val.type=Variant::NIL;
return mi;
} break;
@@ -1452,6 +1597,24 @@ MethodInfo GDFunctions::get_info(Function p_func) {
mi.return_val.type=Variant::OBJECT;
return mi;
} break;
+ case VALIDATE_JSON: {
+
+ MethodInfo mi("validate_json:Variant",PropertyInfo(Variant::STRING,"json"));
+ mi.return_val.type=Variant::STRING;
+ return mi;
+ } break;
+ case PARSE_JSON: {
+
+ MethodInfo mi("parse_json:Variant",PropertyInfo(Variant::STRING,"json"));
+ mi.return_val.type=Variant::NIL;
+ return mi;
+ } break;
+ case TO_JSON: {
+
+ MethodInfo mi("to_json",PropertyInfo(Variant::NIL,"var:Variant"));
+ mi.return_val.type=Variant::STRING;
+ return mi;
+ } break;
case HASH: {
MethodInfo mi("hash",PropertyInfo(Variant::NIL,"var:Variant"));
@@ -1464,6 +1627,12 @@ MethodInfo GDFunctions::get_info(Function p_func) {
mi.return_val.type=Variant::COLOR;
return mi;
} break;
+ case COLORN: {
+
+ MethodInfo mi("ColorN",PropertyInfo(Variant::STRING,"name"),PropertyInfo(Variant::REAL,"alpha"));
+ mi.return_val.type=Variant::COLOR;
+ return mi;
+ } break;
case PRINT_STACK: {
MethodInfo mi("print_stack");
diff --git a/modules/gdscript/gd_functions.h b/modules/gdscript/gd_functions.h
index 8c88472567..6e30b4dbb5 100644
--- a/modules/gdscript/gd_functions.h
+++ b/modules/gdscript/gd_functions.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2017 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 */
@@ -81,6 +81,8 @@ public:
FUNC_FUNCREF,
TYPE_CONVERT,
TYPE_OF,
+ TYPE_EXISTS,
+ TEXT_CHAR,
TEXT_STR,
TEXT_PRINT,
TEXT_PRINT_TABBED,
@@ -95,8 +97,12 @@ public:
RESOURCE_LOAD,
INST2DICT,
DICT2INST,
+ VALIDATE_JSON,
+ PARSE_JSON,
+ TO_JSON,
HASH,
COLOR8,
+ COLORN,
PRINT_STACK,
INSTANCE_FROM_ID,
FUNC_MAX
diff --git a/modules/gdscript/gd_parser.cpp b/modules/gdscript/gd_parser.cpp
index 901a458179..c783fac429 100644
--- a/modules/gdscript/gd_parser.cpp
+++ b/modules/gdscript/gd_parser.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2017 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 */
@@ -31,6 +31,7 @@
#include "io/resource_loader.h"
#include "os/file_access.h"
#include "script_language.h"
+#include "gd_script.h"
template<class T>
T* GDParser::alloc_node() {
@@ -120,6 +121,7 @@ bool GDParser::_parse_arguments(Node* p_parent,Vector<Node*>& p_args,bool p_stat
tokenizer->advance();
} else {
+ parenthesis ++;
int argidx=0;
while(true) {
@@ -164,6 +166,7 @@ bool GDParser::_parse_arguments(Node* p_parent,Vector<Node*>& p_args,bool p_stat
}
}
+ parenthesis --;
}
return true;
@@ -202,6 +205,7 @@ bool GDParser::_get_completable_identifier(CompletionType p_type,StringName& ide
completion_line=tokenizer->get_token_line();
completion_block=current_block;
completion_found=true;
+ completion_ident_is_call=false;
tokenizer->advance();
if (tokenizer->get_token()==GDTokenizer::TK_IDENTIFIER) {
@@ -209,6 +213,9 @@ bool GDParser::_get_completable_identifier(CompletionType p_type,StringName& ide
tokenizer->advance();
}
+ if (tokenizer->get_token()==GDTokenizer::TK_PARENTHESIS_OPEN) {
+ completion_ident_is_call=true;
+ }
return true;
}
@@ -216,15 +223,17 @@ bool GDParser::_get_completable_identifier(CompletionType p_type,StringName& ide
}
-GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_allow_assign) {
+GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_allow_assign,bool p_parsing_constant) {
-// Vector<Node*> expressions;
-// Vector<OperatorNode::Operator> operators;
+ //Vector<Node*> expressions;
+ //Vector<OperatorNode::Operator> operators;
Vector<Expression> expression;
Node *expr=NULL;
+ int op_line = tokenizer->get_token_line(); // when operators are created at the bottom, the line might have been changed (\n found)
+
while(true) {
@@ -243,7 +252,7 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
//subexpression ()
tokenizer->advance();
parenthesis++;
- Node* subexpr = _parse_expression(p_parent,p_static);
+ Node* subexpr = _parse_expression(p_parent,p_static,p_allow_assign,p_parsing_constant);
parenthesis--;
if (!subexpr)
return NULL;
@@ -256,6 +265,98 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
tokenizer->advance();
expr=subexpr;
+ } else if (tokenizer->get_token()==GDTokenizer::TK_DOLLAR) {
+ tokenizer->advance();
+
+ String path;
+
+ bool need_identifier=true;
+ bool done=false;
+
+ while(!done) {
+
+ switch(tokenizer->get_token()) {
+ case GDTokenizer::TK_CURSOR: {
+ completion_cursor=StringName();
+ completion_type=COMPLETION_GET_NODE;
+ completion_class=current_class;
+ completion_function=current_function;
+ completion_line=tokenizer->get_token_line();
+ completion_cursor=path;
+ completion_argument=0;
+ completion_block=current_block;
+ completion_found=true;
+ tokenizer->advance();
+ } break;
+ case GDTokenizer::TK_CONSTANT: {
+
+ if (!need_identifier) {
+ done=true;
+ break;
+ }
+
+ if (tokenizer->get_token_constant().get_type()!=Variant::STRING) {
+ _set_error("Expected string constant or identifier after '$' or '/'.");
+ return NULL;
+ }
+
+ path+=String(tokenizer->get_token_constant());
+ tokenizer->advance();
+ need_identifier=false;
+
+ } break;
+ case GDTokenizer::TK_IDENTIFIER: {
+ if (!need_identifier) {
+ done=true;
+ break;
+ }
+
+ path+=String(tokenizer->get_token_identifier());
+ tokenizer->advance();
+ need_identifier=false;
+
+ } break;
+ case GDTokenizer::TK_OP_DIV: {
+
+ if (need_identifier) {
+ done=true;
+ break;
+ }
+
+ path+="/";
+ tokenizer->advance();
+ need_identifier=true;
+
+ } break;
+ default: {
+ done=true;
+ break;
+ }
+ }
+ }
+
+ if (path=="") {
+ _set_error("Path expected after $.");
+ return NULL;
+
+ }
+
+ OperatorNode *op = alloc_node<OperatorNode>();
+ op->op=OperatorNode::OP_CALL;
+
+ op->arguments.push_back(alloc_node<SelfNode>());
+
+ IdentifierNode *funcname = alloc_node<IdentifierNode>();
+ funcname->name="get_node";
+
+ op->arguments.push_back(funcname);
+
+ ConstantNode *nodepath = alloc_node<ConstantNode>();
+ nodepath->value = NodePath(StringName(path));
+ op->arguments.push_back(nodepath);
+
+ expr=op;
+
} else if (tokenizer->get_token()==GDTokenizer::TK_CURSOR) {
tokenizer->advance();
continue; //no point in cursor in the middle of expression
@@ -357,35 +458,56 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
OperatorNode *yield = alloc_node<OperatorNode>();
yield->op=OperatorNode::OP_YIELD;
+ while (tokenizer->get_token()==GDTokenizer::TK_NEWLINE) {
+ tokenizer->advance();
+ }
+
if (tokenizer->get_token()==GDTokenizer::TK_PARENTHESIS_CLOSE) {
expr=yield;
tokenizer->advance();
} else {
+ parenthesis ++;
+
Node *object = _parse_and_reduce_expression(p_parent,p_static);
if (!object)
return NULL;
yield->arguments.push_back(object);
if (tokenizer->get_token()!=GDTokenizer::TK_COMMA) {
-
_set_error("Expected ',' after first argument of 'yield'");
return NULL;
}
tokenizer->advance();
+ if (tokenizer->get_token()==GDTokenizer::TK_CURSOR) {
+
+
+ completion_cursor=StringName();
+ completion_node=object;
+ completion_type=COMPLETION_YIELD;
+ completion_class=current_class;
+ completion_function=current_function;
+ completion_line=tokenizer->get_token_line();
+ completion_argument=0;
+ completion_block=current_block;
+ completion_found=true;
+ tokenizer->advance();
+ }
+
Node *signal = _parse_and_reduce_expression(p_parent,p_static);
if (!signal)
return NULL;
yield->arguments.push_back(signal);
if (tokenizer->get_token()!=GDTokenizer::TK_PARENTHESIS_CLOSE) {
-
_set_error("Expected ')' after second argument of 'yield'");
return NULL;
}
+ parenthesis --;
+
tokenizer->advance();
expr=yield;
@@ -477,20 +599,30 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
} else if (tokenizer->get_token()==GDTokenizer::TK_IDENTIFIER) {
//identifier (reference)
- const ClassNode* cln = static_cast<const ClassNode*>(get_parse_tree());
+ const ClassNode* cln = current_class;
bool bfn = false;
StringName identifier;
if (_get_completable_identifier(COMPLETION_IDENTIFIER,identifier)) {
}
- for( int i=0; i<cln->constant_expressions.size(); ++i ) {
+ if (p_parsing_constant) {
+ for( int i=0; i<cln->constant_expressions.size(); ++i ) {
- if( cln->constant_expressions[i].identifier == identifier ) {
+ if( cln->constant_expressions[i].identifier == identifier ) {
- expr = cln->constant_expressions[i].expression;
- bfn = true;
- break;
+ expr = cln->constant_expressions[i].expression;
+ bfn = true;
+ break;
+ }
+ }
+
+ if (GDScriptLanguage::get_singleton()->get_global_map().has(identifier)) {
+ //check from constants
+ ConstantNode *constant = alloc_node<ConstantNode>();
+ constant->value = GDScriptLanguage::get_singleton()->get_global_array()[ GDScriptLanguage::get_singleton()->get_global_map()[identifier] ];
+ expr=constant;
+ bfn = true;
}
}
@@ -500,15 +632,15 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
expr = id;
}
- } else if (/*tokenizer->get_token()==GDTokenizer::TK_OP_ADD ||*/ tokenizer->get_token()==GDTokenizer::TK_OP_SUB || tokenizer->get_token()==GDTokenizer::TK_OP_NOT || tokenizer->get_token()==GDTokenizer::TK_OP_BIT_INVERT) {
-
- //single prefix operators like !expr -expr ++expr --expr
- OperatorNode *op = alloc_node<OperatorNode>();
+ } else if (tokenizer->get_token()==GDTokenizer::TK_OP_ADD || tokenizer->get_token()==GDTokenizer::TK_OP_SUB || tokenizer->get_token()==GDTokenizer::TK_OP_NOT || tokenizer->get_token()==GDTokenizer::TK_OP_BIT_INVERT) {
+ //single prefix operators like !expr +expr -expr ++expr --expr
+ alloc_node<OperatorNode>();
Expression e;
e.is_op=true;
switch(tokenizer->get_token()) {
+ case GDTokenizer::TK_OP_ADD: e.op=OperatorNode::OP_POS; break;
case GDTokenizer::TK_OP_SUB: e.op=OperatorNode::OP_NEG; break;
case GDTokenizer::TK_OP_NOT: e.op=OperatorNode::OP_NOT; break;
case GDTokenizer::TK_OP_BIT_INVERT: e.op=OperatorNode::OP_BIT_INVERT;; break;
@@ -567,7 +699,7 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
_set_error("',' or ']' expected");
return NULL;
}
- Node *n = _parse_expression(arr,p_static);
+ Node *n = _parse_expression(arr,p_static,p_allow_assign,p_parsing_constant);
if (!n)
return NULL;
arr->elements.push_back(n);
@@ -674,7 +806,7 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
expecting=DICT_EXPECT_VALUE;
} else {
//python/js style more flexible
- key = _parse_expression(dict,p_static);
+ key = _parse_expression(dict,p_static,p_allow_assign,p_parsing_constant);
if (!key)
return NULL;
expecting=DICT_EXPECT_COLON;
@@ -682,7 +814,7 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
}
if (expecting==DICT_EXPECT_VALUE) {
- Node *value = _parse_expression(dict,p_static);
+ Node *value = _parse_expression(dict,p_static,p_allow_assign,p_parsing_constant);
if (!value)
return NULL;
expecting=DICT_EXPECT_COMMA;
@@ -730,7 +862,7 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
//find list [ or find dictionary {
- print_line("found bug?");
+ //print_line("found bug?");
_set_error("Error parsing expression, misplaced: "+String(tokenizer->get_token_name(tokenizer->get_token())));
return NULL; //nothing
@@ -833,7 +965,7 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
tokenizer->advance(1);
- Node *subexpr = _parse_expression(op,p_static);
+ Node *subexpr = _parse_expression(op,p_static,p_allow_assign,p_parsing_constant);
if (!subexpr) {
return NULL;
}
@@ -909,6 +1041,8 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
case GDTokenizer::TK_OP_BIT_OR: op=OperatorNode::OP_BIT_OR ; break;
case GDTokenizer::TK_OP_BIT_XOR: op=OperatorNode::OP_BIT_XOR ; break;
case GDTokenizer::TK_PR_EXTENDS: op=OperatorNode::OP_EXTENDS; break;
+ case GDTokenizer::TK_CF_IF: op=OperatorNode::OP_TERNARY_IF; break;
+ case GDTokenizer::TK_CF_ELSE: op=OperatorNode::OP_TERNARY_ELSE; break;
default: valid=false; break;
}
@@ -931,6 +1065,7 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
int next_op=-1;
int min_priority=0xFFFFF;
bool is_unary=false;
+ bool is_ternary=false;
for(int i=0;i<expression.size();i++) {
@@ -944,6 +1079,8 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
int priority;
bool unary=false;
+ bool ternary=false;
+ bool error=false;
switch(expression[i].op) {
@@ -951,6 +1088,7 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
case OperatorNode::OP_BIT_INVERT: priority=0; unary=true; break;
case OperatorNode::OP_NEG: priority=1; unary=true; break;
+ case OperatorNode::OP_POS: priority=1; unary=true; break;
case OperatorNode::OP_MUL: priority=2; break;
case OperatorNode::OP_DIV: priority=2; break;
@@ -974,25 +1112,27 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
case OperatorNode::OP_EQUAL: priority=8; break;
case OperatorNode::OP_NOT_EQUAL: priority=8; break;
+
case OperatorNode::OP_IN: priority=10; break;
-
+
case OperatorNode::OP_NOT: priority=11; unary=true; break;
case OperatorNode::OP_AND: priority=12; break;
case OperatorNode::OP_OR: priority=13; break;
-
- // ?: = 10
-
- case OperatorNode::OP_ASSIGN: priority=14; break;
- case OperatorNode::OP_ASSIGN_ADD: priority=14; break;
- case OperatorNode::OP_ASSIGN_SUB: priority=14; break;
- case OperatorNode::OP_ASSIGN_MUL: priority=14; break;
- case OperatorNode::OP_ASSIGN_DIV: priority=14; break;
- case OperatorNode::OP_ASSIGN_MOD: priority=14; break;
- case OperatorNode::OP_ASSIGN_SHIFT_LEFT: priority=14; break;
- case OperatorNode::OP_ASSIGN_SHIFT_RIGHT: priority=14; break;
- case OperatorNode::OP_ASSIGN_BIT_AND: priority=14; break;
- case OperatorNode::OP_ASSIGN_BIT_OR: priority=14; break;
- case OperatorNode::OP_ASSIGN_BIT_XOR: priority=14; break;
+
+ case OperatorNode::OP_TERNARY_IF: priority=14; ternary=true; break;
+ case OperatorNode::OP_TERNARY_ELSE: priority=14; error=true; break; // Errors out when found without IF (since IF would consume it)
+
+ case OperatorNode::OP_ASSIGN: priority=15; break;
+ case OperatorNode::OP_ASSIGN_ADD: priority=15; break;
+ case OperatorNode::OP_ASSIGN_SUB: priority=15; break;
+ case OperatorNode::OP_ASSIGN_MUL: priority=15; break;
+ case OperatorNode::OP_ASSIGN_DIV: priority=15; break;
+ case OperatorNode::OP_ASSIGN_MOD: priority=15; break;
+ case OperatorNode::OP_ASSIGN_SHIFT_LEFT: priority=15; break;
+ case OperatorNode::OP_ASSIGN_SHIFT_RIGHT: priority=15; break;
+ case OperatorNode::OP_ASSIGN_BIT_AND: priority=15; break;
+ case OperatorNode::OP_ASSIGN_BIT_OR: priority=15; break;
+ case OperatorNode::OP_ASSIGN_BIT_XOR: priority=15; break;
default: {
@@ -1003,11 +1143,16 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
}
if (priority<min_priority) {
+ if(error) {
+ _set_error("Unexpected operator");
+ return NULL;
+ }
// < is used for left to right (default)
// <= is used for right to left
next_op=i;
min_priority=priority;
is_unary=unary;
+ is_ternary=ternary;
}
}
@@ -1041,21 +1186,31 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
OperatorNode *op = alloc_node<OperatorNode>();
op->op=expression[i].op;
op->arguments.push_back(expression[i+1].node);
+ op->line=op_line; //line might have been changed from a \n
expression[i].is_op=false;
expression[i].node=op;
expression.remove(i+1);
}
- } else {
-
+ } else if(is_ternary) {
if (next_op <1 || next_op>=(expression.size()-1)) {
_set_error("Parser bug..");
ERR_FAIL_V(NULL);
}
+
+ if(next_op>=(expression.size()-2) || expression[next_op+2].op != OperatorNode::OP_TERNARY_ELSE) {
+ _set_error("Expected else after ternary if.");
+ ERR_FAIL_V(NULL);
+ }
+ if(next_op>=(expression.size()-3)) {
+ _set_error("Expected value after ternary else.");
+ ERR_FAIL_V(NULL);
+ }
OperatorNode *op = alloc_node<OperatorNode>();
op->op=expression[next_op].op;
+ op->line=op_line; //line might have been changed from a \n
if (expression[next_op-1].is_op) {
@@ -1069,8 +1224,56 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
// can be followed by an unary op in a valid combination,
// due to how precedence works, unaries will always dissapear first
+ _set_error("Unexpected two consecutive operators after ternary if.");
+ return NULL;
+ }
+
+ if (expression[next_op+3].is_op) {
+ // this is not invalid and can really appear
+ // but it becomes invalid anyway because no binary op
+ // can be followed by an unary op in a valid combination,
+ // due to how precedence works, unaries will always dissapear first
+
+ _set_error("Unexpected two consecutive operators after ternary else.");
+ return NULL;
+ }
+
+
+ op->arguments.push_back(expression[next_op+1].node); //next expression goes as first
+ op->arguments.push_back(expression[next_op-1].node); //left expression goes as when-true
+ op->arguments.push_back(expression[next_op+3].node); //expression after next goes as when-false
+
+ //replace all 3 nodes by this operator and make it an expression
+ expression[next_op-1].node=op;
+ expression.remove(next_op);
+ expression.remove(next_op);
+ expression.remove(next_op);
+ expression.remove(next_op);
+ } else {
+
+ if (next_op <1 || next_op>=(expression.size()-1)) {
_set_error("Parser bug..");
+ ERR_FAIL_V(NULL);
+ }
+
+ OperatorNode *op = alloc_node<OperatorNode>();
+ op->op=expression[next_op].op;
+ op->line=op_line; //line might have been changed from a \n
+ if (expression[next_op-1].is_op) {
+
+ _set_error("Parser bug..");
+ ERR_FAIL_V(NULL);
+ }
+
+ if (expression[next_op+1].is_op) {
+ // this is not invalid and can really appear
+ // but it becomes invalid anyway because no binary op
+ // can be followed by an unary op in a valid combination,
+ // due to how precedence works, unaries will always dissapear first
+
+ _set_error("Unexpected two consecutive operators.");
+ return NULL;
}
@@ -1114,7 +1317,7 @@ GDParser::Node* GDParser::_reduce_expression(Node *p_node,bool p_to_const) {
//reduce constant array expression
ConstantNode *cn = alloc_node<ConstantNode>();
- Array arr(!p_to_const);
+ Array arr;
//print_line("mk array "+itos(!p_to_const));
arr.resize(an->elements.size());
for(int i=0;i<an->elements.size();i++) {
@@ -1149,7 +1352,7 @@ GDParser::Node* GDParser::_reduce_expression(Node *p_node,bool p_to_const) {
//reduce constant array expression
ConstantNode *cn = alloc_node<ConstantNode>();
- Dictionary dict(!p_to_const);
+ Dictionary dict;
for(int i=0;i<dn->elements.size();i++) {
ConstantNode *key_c = static_cast<ConstantNode*>(dn->elements[i].key);
ConstantNode *value_c = static_cast<ConstantNode*>(dn->elements[i].value);
@@ -1258,6 +1461,8 @@ GDParser::Node* GDParser::_reduce_expression(Node *p_node,bool p_to_const) {
} break;
}
+ error_line=op->line;
+
return p_node;
}
@@ -1293,6 +1498,7 @@ GDParser::Node* GDParser::_reduce_expression(Node *p_node,bool p_to_const) {
Variant v = ca->value.get(cb->value,&valid);
if (!valid) {
_set_error("invalid index in constant expression");
+ error_line=op->line;
return op;
}
@@ -1330,6 +1536,7 @@ GDParser::Node* GDParser::_reduce_expression(Node *p_node,bool p_to_const) {
Variant v = ca->value.get_named(ib->name,&valid);
if (!valid) {
_set_error("invalid index '"+String(ib->name)+"' in constant expression");
+ error_line=op->line;
return op;
}
@@ -1359,9 +1566,19 @@ GDParser::Node* GDParser::_reduce_expression(Node *p_node,bool p_to_const) {
if (op->arguments[0]->type==Node::TYPE_CONSTANT) {
_set_error("Can't assign to constant",tokenizer->get_token_line()-1);
+ error_line=op->line;
return op;
}
+ if (op->arguments[0]->type==Node::TYPE_OPERATOR) {
+ OperatorNode *on = static_cast<OperatorNode*>(op->arguments[0]);
+ if (on->op != OperatorNode::OP_INDEX && on->op != OperatorNode::OP_INDEX_NAMED) {
+ _set_error("Can't assign to an expression",tokenizer->get_token_line()-1);
+ error_line=op->line;
+ return op;
+ }
+ }
+
} break;
default: { break; }
}
@@ -1374,6 +1591,7 @@ GDParser::Node* GDParser::_reduce_expression(Node *p_node,bool p_to_const) {
Variant::evaluate(m_vop,static_cast<ConstantNode*>(op->arguments[0])->value,Variant(),res,valid);\
if (!valid) {\
_set_error("Invalid operand for unary operator");\
+ error_line=op->line;\
return p_node;\
}\
ConstantNode *cn = alloc_node<ConstantNode>();\
@@ -1386,6 +1604,7 @@ GDParser::Node* GDParser::_reduce_expression(Node *p_node,bool p_to_const) {
Variant::evaluate(m_vop,static_cast<ConstantNode*>(op->arguments[0])->value,static_cast<ConstantNode*>(op->arguments[1])->value,res,valid);\
if (!valid) {\
_set_error("Invalid operands for operator");\
+ error_line=op->line;\
return p_node;\
}\
ConstantNode *cn = alloc_node<ConstantNode>();\
@@ -1396,6 +1615,7 @@ GDParser::Node* GDParser::_reduce_expression(Node *p_node,bool p_to_const) {
//unary operators
case OperatorNode::OP_NEG: { _REDUCE_UNARY(Variant::OP_NEGATE); } break;
+ case OperatorNode::OP_POS: { _REDUCE_UNARY(Variant::OP_POSITIVE); } break;
case OperatorNode::OP_NOT: { _REDUCE_UNARY(Variant::OP_NOT); } break;
case OperatorNode::OP_BIT_INVERT: { _REDUCE_UNARY(Variant::OP_BIT_NEGATE); } break;
//binary operators (in precedence order)
@@ -1432,7 +1652,7 @@ GDParser::Node* GDParser::_reduce_expression(Node *p_node,bool p_to_const) {
GDParser::Node* GDParser::_parse_and_reduce_expression(Node *p_parent,bool p_static,bool p_reduce_const,bool p_allow_assign) {
- Node* expr=_parse_expression(p_parent,p_static,p_allow_assign);
+ Node* expr=_parse_expression(p_parent,p_static,p_allow_assign,p_reduce_const);
if (!expr || error_set)
return NULL;
expr = _reduce_expression(expr,p_reduce_const);
@@ -1526,6 +1746,10 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) {
return;
}
tokenizer->advance();
+ if(tokenizer->get_token()==GDTokenizer::TK_SEMICOLON) {
+ // Ignore semicolon after 'pass'
+ tokenizer->advance();
+ }
} break;
case GDTokenizer::TK_PR_VAR: {
//variale declaration and (eventual) initialization
@@ -1611,6 +1835,7 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) {
case GDTokenizer::TK_CF_IF: {
tokenizer->advance();
+
Node *condition = _parse_and_reduce_expression(p_block,p_static);
if (!condition) {
if (_recover_from_completion()) {
@@ -1797,6 +2022,64 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) {
return;
}
+ if (container->type==Node::TYPE_OPERATOR) {
+
+ OperatorNode* op = static_cast<OperatorNode*>(container);
+ if (op->op==OperatorNode::OP_CALL && op->arguments[0]->type==Node::TYPE_BUILT_IN_FUNCTION && static_cast<BuiltInFunctionNode*>(op->arguments[0])->function==GDFunctions::GEN_RANGE) {
+ //iterating a range, so see if range() can be optimized without allocating memory, by replacing it by vectors (which can work as iterable too!)
+
+ Vector<Node*> args;
+ Vector<double> constants;
+
+ bool constant=true;
+
+ for(int i=1;i<op->arguments.size();i++) {
+ args.push_back(op->arguments[i]);
+ if (constant && op->arguments[i]->type==Node::TYPE_CONSTANT) {
+ ConstantNode *c = static_cast<ConstantNode*>(op->arguments[i]);
+ if (c->value.get_type()==Variant::REAL || c->value.get_type()==Variant::INT) {
+ constants.push_back(c->value);
+ } else {
+ constant=false;
+ }
+ }
+ }
+
+ if (args.size()>0 || args.size()<4) {
+
+ if (constant) {
+
+ ConstantNode *cn = alloc_node<ConstantNode>();
+ switch(args.size()) {
+ case 1: cn->value=constants[0]; break;
+ case 2: cn->value=Vector2(constants[0],constants[1]); break;
+ case 3: cn->value=Vector3(constants[0],constants[1],constants[2]); break;
+ }
+ container=cn;
+ } else {
+ OperatorNode *on = alloc_node<OperatorNode>();
+ on->op=OperatorNode::OP_CALL;
+
+ TypeNode *tn = alloc_node<TypeNode>();
+ on->arguments.push_back(tn);
+
+ switch(args.size()) {
+ case 1: tn->vtype=Variant::REAL; break;
+ case 2: tn->vtype=Variant::VECTOR2; break;
+ case 3: tn->vtype=Variant::VECTOR3; break;
+ }
+
+ for(int i=0;i<args.size();i++) {
+ on->arguments.push_back(args[i]);
+ }
+
+ container=on;
+ }
+ }
+ }
+
+ }
+
ControlFlowNode *cf_for = alloc_node<ControlFlowNode>();
cf_for->cf_type=ControlFlowNode::CF_FOR;
@@ -2060,6 +2343,7 @@ void GDParser::_parse_class(ClassNode *p_class) {
if (error_set)
return;
+
if (indent_level>tab_level.back()->get()) {
p_class->end_line=tokenizer->get_token_line();
return; //go back a level
@@ -2217,6 +2501,11 @@ void GDParser::_parse_class(ClassNode *p_class) {
bool defaulting=false;
while(true) {
+ if (tokenizer->get_token()==GDTokenizer::TK_NEWLINE) {
+ tokenizer->advance();
+ continue;
+ }
+
if (tokenizer->get_token()==GDTokenizer::TK_PR_VAR) {
tokenizer->advance(); //var before the identifier is allowed
@@ -2269,6 +2558,10 @@ void GDParser::_parse_class(ClassNode *p_class) {
default_values.push_back(on);
}
+ while (tokenizer->get_token()==GDTokenizer::TK_NEWLINE) {
+ tokenizer->advance();
+ }
+
if (tokenizer->get_token()==GDTokenizer::TK_COMMA) {
tokenizer->advance();
continue;
@@ -2310,6 +2603,7 @@ void GDParser::_parse_class(ClassNode *p_class) {
if (tokenizer->get_token()!=GDTokenizer::TK_PARENTHESIS_CLOSE) {
//has arguments
+ parenthesis ++;
while(true) {
Node *arg = _parse_and_reduce_expression(p_class,_static);
@@ -2327,6 +2621,7 @@ void GDParser::_parse_class(ClassNode *p_class) {
break;
}
+ parenthesis --;
}
tokenizer->advance();
@@ -2356,6 +2651,9 @@ void GDParser::_parse_class(ClassNode *p_class) {
function->_static=_static;
function->line=fnline;
+ function->rpc_mode=rpc_mode;
+ rpc_mode=ScriptInstance::RPC_MODE_DISABLED;
+
if (_static)
p_class->static_functions.push_back(function);
@@ -2387,6 +2685,10 @@ void GDParser::_parse_class(ClassNode *p_class) {
if (tokenizer->get_token()==GDTokenizer::TK_PARENTHESIS_OPEN) {
tokenizer->advance();
while(true) {
+ if (tokenizer->get_token()==GDTokenizer::TK_NEWLINE) {
+ tokenizer->advance();
+ continue;
+ }
if (tokenizer->get_token()==GDTokenizer::TK_PARENTHESIS_CLOSE) {
@@ -2402,6 +2704,10 @@ void GDParser::_parse_class(ClassNode *p_class) {
sig.arguments.push_back(tokenizer->get_token_identifier());
tokenizer->advance();
+ while (tokenizer->get_token()==GDTokenizer::TK_NEWLINE) {
+ tokenizer->advance();
+ }
+
if (tokenizer->get_token()==GDTokenizer::TK_COMMA) {
tokenizer->advance();
} else if (tokenizer->get_token()!=GDTokenizer::TK_PARENTHESIS_CLOSE) {
@@ -2433,18 +2739,43 @@ void GDParser::_parse_class(ClassNode *p_class) {
return;
}
current_export.type=type;
+ current_export.usage|=PROPERTY_USAGE_SCRIPT_VARIABLE;
tokenizer->advance();
+
+ String hint_prefix ="";
+
+ if(type == Variant::ARRAY && tokenizer->get_token()==GDTokenizer::TK_COMMA) {
+ tokenizer->advance();
+
+ while(tokenizer->get_token()==GDTokenizer::TK_BUILT_IN_TYPE) {
+ type = tokenizer->get_token_type();
+
+ tokenizer->advance();
+
+ if(type == Variant::ARRAY) {
+ hint_prefix += itos(Variant::ARRAY)+":";
+ if (tokenizer->get_token()==GDTokenizer::TK_COMMA) {
+ tokenizer->advance();
+ }
+ } else {
+ hint_prefix += itos(type);
+ break;
+ }
+ }
+ }
+
if (tokenizer->get_token()==GDTokenizer::TK_COMMA) {
// hint expected next!
tokenizer->advance();
- switch(current_export.type) {
+
+ switch(type) {
case Variant::INT: {
if (tokenizer->get_token()==GDTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier()=="FLAGS") {
- current_export.hint=PROPERTY_HINT_ALL_FLAGS;
+ //current_export.hint=PROPERTY_HINT_ALL_FLAGS;
tokenizer->advance();
if (tokenizer->get_token()==GDTokenizer::TK_PARENTHESIS_CLOSE) {
@@ -2793,13 +3124,20 @@ void GDParser::_parse_class(ClassNode *p_class) {
return;
} break;
}
-
+
+ }
+ if(current_export.type == Variant::ARRAY && !hint_prefix.empty()) {
+ if(current_export.hint) {
+ hint_prefix += "/"+itos(current_export.hint);
+ }
+ current_export.hint_string=hint_prefix+":"+current_export.hint_string;
+ current_export.hint=PROPERTY_HINT_NONE;
}
} else if (tokenizer->get_token()==GDTokenizer::TK_IDENTIFIER) {
String identifier = tokenizer->get_token_identifier();
- if (!ObjectTypeDB::is_type(identifier,"Resource")) {
+ if (!ClassDB::is_parent_class(identifier,"Resource")) {
current_export=PropertyInfo();
_set_error("Export hint not a type or resource.");
@@ -2807,6 +3145,8 @@ void GDParser::_parse_class(ClassNode *p_class) {
current_export.type=Variant::OBJECT;
current_export.hint=PROPERTY_HINT_RESOURCE_TYPE;
+ current_export.usage|=PROPERTY_USAGE_SCRIPT_VARIABLE;
+
current_export.hint_string=identifier;
tokenizer->advance();
@@ -2824,25 +3164,101 @@ void GDParser::_parse_class(ClassNode *p_class) {
}
- if (tokenizer->get_token()!=GDTokenizer::TK_PR_VAR) {
+ if (tokenizer->get_token()!=GDTokenizer::TK_PR_VAR && tokenizer->get_token()!=GDTokenizer::TK_PR_ONREADY && tokenizer->get_token()!=GDTokenizer::TK_PR_REMOTE && tokenizer->get_token()!=GDTokenizer::TK_PR_MASTER && tokenizer->get_token()!=GDTokenizer::TK_PR_SLAVE && tokenizer->get_token()!=GDTokenizer::TK_PR_SYNC) {
current_export=PropertyInfo();
- _set_error("Expected 'var'.");
+ _set_error("Expected 'var', 'onready', 'remote', 'master', 'slave' or 'sync'.");
return;
}
- }; //fallthrough to var
+ continue;
+ } break;
case GDTokenizer::TK_PR_ONREADY: {
- if (token==GDTokenizer::TK_PR_ONREADY) {
- //may be fallthrough from export, ignore if so
- tokenizer->advance();
+ //may be fallthrough from export, ignore if so
+ tokenizer->advance();
+ if (tokenizer->get_token()!=GDTokenizer::TK_PR_VAR) {
+ _set_error("Expected 'var'.");
+ return;
+ }
+
+ continue;
+ } break;
+ case GDTokenizer::TK_PR_REMOTE: {
+
+ //may be fallthrough from export, ignore if so
+ tokenizer->advance();
+ if (current_export.type) {
+ if (tokenizer->get_token()!=GDTokenizer::TK_PR_VAR) {
+ _set_error("Expected 'var'.");
+ return;
+ }
+
+ } else {
+ if (tokenizer->get_token()!=GDTokenizer::TK_PR_VAR && tokenizer->get_token()!=GDTokenizer::TK_PR_FUNCTION) {
+ _set_error("Expected 'var' or 'func'.");
+ return;
+ }
+ }
+ rpc_mode=ScriptInstance::RPC_MODE_REMOTE;
+
+ continue;
+ } break;
+ case GDTokenizer::TK_PR_MASTER: {
+
+ //may be fallthrough from export, ignore if so
+ tokenizer->advance();
+ if (current_export.type) {
if (tokenizer->get_token()!=GDTokenizer::TK_PR_VAR) {
_set_error("Expected 'var'.");
return;
}
+
+ } else {
+ if (tokenizer->get_token()!=GDTokenizer::TK_PR_VAR && tokenizer->get_token()!=GDTokenizer::TK_PR_FUNCTION) {
+ _set_error("Expected 'var' or 'func'.");
+ return;
+ }
}
- }; //fallthrough to var
+
+ rpc_mode=ScriptInstance::RPC_MODE_MASTER;
+ continue;
+ } break;
+ case GDTokenizer::TK_PR_SLAVE: {
+
+ //may be fallthrough from export, ignore if so
+ tokenizer->advance();
+ if (current_export.type) {
+ if (tokenizer->get_token()!=GDTokenizer::TK_PR_VAR) {
+ _set_error("Expected 'var'.");
+ return;
+ }
+
+ } else {
+ if (tokenizer->get_token()!=GDTokenizer::TK_PR_VAR && tokenizer->get_token()!=GDTokenizer::TK_PR_FUNCTION) {
+ _set_error("Expected 'var' or 'func'.");
+ return;
+ }
+ }
+
+ rpc_mode=ScriptInstance::RPC_MODE_SLAVE;
+ continue;
+ } break;
+ case GDTokenizer::TK_PR_SYNC: {
+
+ //may be fallthrough from export, ignore if so
+ tokenizer->advance();
+ if (tokenizer->get_token()!=GDTokenizer::TK_PR_VAR && tokenizer->get_token()!=GDTokenizer::TK_PR_FUNCTION) {
+ if (current_export.type)
+ _set_error("Expected 'var'.");
+ else
+ _set_error("Expected 'var' or 'func'.");
+ return;
+ }
+
+ rpc_mode=ScriptInstance::RPC_MODE_SYNC;
+ continue;
+ } break;
case GDTokenizer::TK_PR_VAR: {
//variale declaration and (eventual) initialization
@@ -2866,8 +3282,12 @@ void GDParser::_parse_class(ClassNode *p_class) {
member.expression=NULL;
member._export.name=member.identifier;
member.line=tokenizer->get_token_line();
+ member.rpc_mode=rpc_mode;
+
tokenizer->advance();
+ rpc_mode=ScriptInstance::RPC_MODE_DISABLED;
+
if (tokenizer->get_token()==GDTokenizer::TK_OP_ASSIGN) {
#ifdef DEBUG_ENABLED
@@ -2926,6 +3346,17 @@ void GDParser::_parse_class(ClassNode *p_class) {
return;
}
member._export.type=cn->value.get_type();
+ member._export.usage|=PROPERTY_USAGE_SCRIPT_VARIABLE;
+ if (cn->value.get_type()==Variant::OBJECT) {
+ Object *obj = cn->value;
+ Resource *res = obj->cast_to<Resource>();
+ if(res==NULL) {
+ _set_error("Exported constant not a type or resource.");
+ return;
+ }
+ member._export.hint=PROPERTY_HINT_RESOURCE_TYPE;
+ member._export.hint_string=res->get_class();
+ }
}
}
#ifdef TOOLS_ENABLED
@@ -3054,7 +3485,124 @@ void GDParser::_parse_class(ClassNode *p_class) {
}
} break;
+ case GDTokenizer::TK_PR_ENUM: {
+ //mutiple constant declarations..
+
+ int last_assign = -1; // Incremented by 1 right before the assingment.
+ String enum_name;
+ Dictionary enum_dict;
+
+ tokenizer->advance();
+ if (tokenizer->get_token()==GDTokenizer::TK_IDENTIFIER) {
+ enum_name=tokenizer->get_token_identifier();
+ tokenizer->advance();
+ }
+ if (tokenizer->get_token()!=GDTokenizer::TK_CURLY_BRACKET_OPEN) {
+ _set_error("Expected '{' in enum declaration");
+ return;
+ }
+ tokenizer->advance();
+
+ while(true) {
+ if(tokenizer->get_token()==GDTokenizer::TK_NEWLINE) {
+
+ tokenizer->advance(); // Ignore newlines
+ } else if (tokenizer->get_token()==GDTokenizer::TK_CURLY_BRACKET_CLOSE) {
+
+ tokenizer->advance();
+ break; // End of enum
+ } else if (tokenizer->get_token()!=GDTokenizer::TK_IDENTIFIER) {
+
+ if(tokenizer->get_token()==GDTokenizer::TK_EOF) {
+ _set_error("Unexpected end of file.");
+ } else {
+ _set_error(String("Unexpected ") + GDTokenizer::get_token_name(tokenizer->get_token()) + ", expected identifier");
+ }
+
+ return;
+ } else { // tokenizer->get_token()==GDTokenizer::TK_IDENTIFIER
+ ClassNode::Constant constant;
+
+ constant.identifier=tokenizer->get_token_identifier();
+
+ tokenizer->advance();
+
+ if (tokenizer->get_token()==GDTokenizer::TK_OP_ASSIGN) {
+ tokenizer->advance();
+
+ Node *subexpr=NULL;
+
+ subexpr = _parse_and_reduce_expression(p_class,true,true);
+ if (!subexpr) {
+ if (_recover_from_completion()) {
+ break;
+ }
+ return;
+ }
+
+ if (subexpr->type!=Node::TYPE_CONSTANT) {
+ _set_error("Expected constant expression");
+ }
+
+ const ConstantNode *subexpr_const = static_cast<const ConstantNode*>(subexpr);
+
+ if(subexpr_const->value.get_type() != Variant::INT) {
+ _set_error("Expected an int value for enum");
+ }
+
+ last_assign = subexpr_const->value;
+
+ constant.expression=subexpr;
+
+ } else {
+ last_assign = last_assign + 1;
+ ConstantNode *cn = alloc_node<ConstantNode>();
+ cn->value = last_assign;
+ constant.expression = cn;
+ }
+
+ if(tokenizer->get_token()==GDTokenizer::TK_COMMA) {
+ tokenizer->advance();
+ }
+
+ if(enum_name != "") {
+ const ConstantNode *cn = static_cast<const ConstantNode*>(constant.expression);
+ enum_dict[constant.identifier] = cn->value;
+ }
+
+ p_class->constant_expressions.push_back(constant);
+ }
+
+ }
+
+ if(enum_name != "") {
+ ClassNode::Constant enum_constant;
+ enum_constant.identifier=enum_name;
+ ConstantNode *cn = alloc_node<ConstantNode>();
+ cn->value = enum_dict;
+ enum_constant.expression=cn;
+ p_class->constant_expressions.push_back(enum_constant);
+ }
+
+ if (!_end_statement()) {
+ _set_error("Expected end of statement (enum)");
+ return;
+ }
+
+
+
+ } break;
+
+ case GDTokenizer::TK_CONSTANT: {
+ if(tokenizer->get_token_constant().get_type() == Variant::STRING) {
+ tokenizer->advance();
+ // Ignore
+ } else {
+ _set_error(String()+"Unexpected constant of type: "+Variant::get_type_name(tokenizer->get_token_constant().get_type()));
+ return;
+ }
+ } break;
default: {
@@ -3209,6 +3757,7 @@ void GDParser::clear() {
current_class=NULL;
completion_found=false;
+ rpc_mode=ScriptInstance::RPC_MODE_DISABLED;
current_function=NULL;
@@ -3272,6 +3821,11 @@ int GDParser::get_completion_argument_index() {
return completion_argument;
}
+int GDParser::get_completion_identifier_is_function() {
+
+ return completion_ident_is_call;
+}
+
GDParser::GDParser() {
head=NULL;
diff --git a/modules/gdscript/gd_parser.h b/modules/gdscript/gd_parser.h
index 6c49c1df52..e8f5f0f981 100644
--- a/modules/gdscript/gd_parser.h
+++ b/modules/gdscript/gd_parser.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2017 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 */
@@ -33,6 +33,7 @@
#include "gd_functions.h"
#include "map.h"
#include "object.h"
+#include "script_language.h"
class GDParser {
public:
@@ -88,6 +89,7 @@ public:
StringName getter;
int line;
Node *expression;
+ ScriptInstance::RPCMode rpc_mode;
};
struct Constant {
StringName identifier;
@@ -119,12 +121,13 @@ public:
struct FunctionNode : public Node {
bool _static;
+ ScriptInstance::RPCMode rpc_mode;
StringName name;
Vector<StringName> arguments;
Vector<Node*> default_values;
BlockNode *body;
- FunctionNode() { type=TYPE_FUNCTION; _static=false; }
+ FunctionNode() { type=TYPE_FUNCTION; _static=false; rpc_mode=ScriptInstance::RPC_MODE_DISABLED; }
};
@@ -206,6 +209,7 @@ public:
OP_INDEX_NAMED,
//unary operators
OP_NEG,
+ OP_POS,
OP_NOT,
OP_BIT_INVERT,
OP_PREINC,
@@ -244,6 +248,9 @@ public:
OP_BIT_AND,
OP_BIT_OR,
OP_BIT_XOR,
+ //ternary operators
+ OP_TERNARY_IF,
+ OP_TERNARY_ELSE,
};
Operator op;
@@ -369,13 +376,15 @@ public:
enum CompletionType {
COMPLETION_NONE,
COMPLETION_BUILT_IN_TYPE_CONSTANT,
+ COMPLETION_GET_NODE,
COMPLETION_FUNCTION,
COMPLETION_IDENTIFIER,
COMPLETION_PARENT_FUNCTION,
COMPLETION_METHOD,
COMPLETION_CALL_ARGUMENTS,
COMPLETION_INDEX,
- COMPLETION_VIRTUAL_FUNC
+ COMPLETION_VIRTUAL_FUNC,
+ COMPLETION_YIELD,
};
@@ -425,9 +434,13 @@ private:
int completion_line;
int completion_argument;
bool completion_found;
+ bool completion_ident_is_call;
PropertyInfo current_export;
+ ScriptInstance::RPCMode rpc_mode;
+
+
void _set_error(const String& p_error, int p_line=-1, int p_column=-1);
bool _recover_from_completion();
@@ -435,7 +448,7 @@ private:
bool _parse_arguments(Node* p_parent, Vector<Node*>& p_args, bool p_static, bool p_can_codecomplete=false);
bool _enter_indent_block(BlockNode *p_block=NULL);
bool _parse_newline();
- Node* _parse_expression(Node *p_parent,bool p_static,bool p_allow_assign=false);
+ Node* _parse_expression(Node *p_parent, bool p_static, bool p_allow_assign=false, bool p_parsing_constant=false);
Node* _reduce_expression(Node *p_node,bool p_to_const=false);
Node* _parse_and_reduce_expression(Node *p_parent,bool p_static,bool p_reduce_const=false,bool p_allow_assign=false);
@@ -468,7 +481,7 @@ public:
BlockNode *get_completion_block();
FunctionNode *get_completion_function();
int get_completion_argument_index();
-
+ int get_completion_identifier_is_function();
void clear();
GDParser();
diff --git a/modules/gdscript/gd_pretty_print.cpp b/modules/gdscript/gd_pretty_print.cpp
deleted file mode 100644
index cca3cd3984..0000000000
--- a/modules/gdscript/gd_pretty_print.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
-/*************************************************************************/
-/* gd_pretty_print.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* http://www.godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2016 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_pretty_print.h"
-
-GDPrettyPrint::GDPrettyPrint() {
-
-
-}
diff --git a/modules/gdscript/gd_pretty_print.h b/modules/gdscript/gd_pretty_print.h
deleted file mode 100644
index 0106d873d9..0000000000
--- a/modules/gdscript/gd_pretty_print.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*************************************************************************/
-/* gd_pretty_print.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* http://www.godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2016 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. */
-/*************************************************************************/
-#ifndef GD_PRETTY_PRINT_H
-#define GD_PRETTY_PRINT_H
-
-
-
-
-class GDPrettyPrint {
-public:
- GDPrettyPrint();
-};
-
-#endif // GD_PRETTY_PRINT_H
diff --git a/modules/gdscript/gd_script.cpp b/modules/gdscript/gd_script.cpp
index b1919b3468..b216ef42b9 100644
--- a/modules/gdscript/gd_script.cpp
+++ b/modules/gdscript/gd_script.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2017 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 */
@@ -34,1432 +34,6 @@
#include "io/file_access_encrypted.h"
#include "os/os.h"
-
-
-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_CLASS: {
-
- return &p_script->_static_ref;
- } 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 *o=p_script;
- ERR_FAIL_INDEX_V(address,_global_names_count,NULL);
- const StringName *sn = &_global_names_ptr[address];
-
- while(o) {
- GDScript *s=o;
- while(s) {
-
- Map<StringName,Variant>::Element *E=s->constants.find(*sn);
- if (E) {
- return &E->get();
- }
- s=s->_base;
- }
- o=o->_owner;
- }
-
-
- 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. Nonexistent "+p_where+".";
- } else if (p_err.error==Variant::CallError::CALL_ERROR_INSTANCE_IS_NULL) {
- err_text="Attempt to call "+p_where+" on a null instance.";
- } else {
- err_text="Bug, call error: #"+itos(p_err.error);
- }
-
- return err_text;
-
-}
-
-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, CallState *p_state) {
-
-
- 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
-
- uint32_t alloca_size=0;
- GDScript *_class;
- int ip=0;
- int line=_initial_line;
-
-
-
- if (p_state) {
- //use existing (supplied) state (yielded)
- stack=(Variant*)p_state->stack.ptr();
- call_args=(Variant**)&p_state->stack[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
-
- } else {
-
- if (p_argcount!=_argument_count) {
-
- if (p_argcount>_argument_count) {
-
- r_err.error=Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
- r_err.argument=_argument_count;
-
-
- return Variant();
- } else if (p_argcount < _argument_count - _default_arg_count) {
-
- r_err.error=Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_err.argument=_argument_count - _default_arg_count;
- return Variant();
- } else {
-
- defarg=_argument_count-p_argcount;
- }
- }
-
- alloca_size = sizeof(Variant*)*_call_size + sizeof(Variant)*_stack_size;
-
- if (alloca_size) {
-
- uint8_t *aptr = (uint8_t*)alloca(alloca_size);
-
- if (_stack_size) {
-
- stack=(Variant*)aptr;
- for(int i=0;i<p_argcount;i++)
- 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;
- }
-
- 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;
- }
- }
-
- 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
-
-
-#ifdef DEBUG_ENABLED
-
- uint64_t function_start_time;
- uint64_t function_call_time;
-
- if (GDScriptLanguage::get_singleton()->profiling) {
- function_start_time=OS::get_singleton()->get_ticks_usec();
- function_call_time=0;
- profile.call_count++;
- profile.frame_call_count++;
- }
-#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);
-
-#ifdef DEBUG_ENABLED
- Variant ret;
- Variant::evaluate(op,*a,*b,ret,valid);
-#else
- Variant::evaluate(op,*a,*b,*dst,valid);
-#endif
-
- if (!valid) {
-#ifdef DEBUG_ENABLED
-
- if (ret.get_type()==Variant::STRING) {
- //return a string when invalid with the error
- err_text=ret;
- err_text += " in operator '"+Variant::get_operator_name(op)+"'.";
- } else {
- err_text="Invalid operands '"+Variant::get_type_name(a->get_type())+"' and '"+Variant::get_type_name(b->get_type())+"' in operator '"+Variant::get_operator_name(op)+"'.";
- }
-#endif
- break;
-
- }
-#ifdef DEBUG_ENABLED
- *dst=ret;
-#endif
-
- 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;
-#ifdef DEBUG_ENABLED
- //allow better error message in cases where src and dst are the same stack position
- Variant ret = src->get(*index,&valid);
-#else
- *dst = src->get(*index,&valid);
-
-#endif
- 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;
- }
-#ifdef DEBUG_ENABLED
- *dst=ret;
-#endif
- 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;
-#ifdef DEBUG_ENABLED
- //allow better error message in cases where src and dst are the same stack position
- Variant ret = src->get_named(*index,&valid);
-
-#else
- *dst = src->get_named(*index,&valid);
-#endif
-
- if (!valid) {
- if (src->has_method(*index)) {
- err_text="Invalid get index '"+index->operator String()+"' (on base: '"+_get_var_type(src)+"'). Did you mean '."+index->operator String()+"()' ?";
- } else {
- err_text="Invalid get index '"+index->operator String()+"' (on base: '"+_get_var_type(src)+"').";
- }
- break;
- }
-#ifdef DEBUG_ENABLED
- *dst=ret;
-#endif
- 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;
- }
-
-#ifdef DEBUG_ENABLED
- uint64_t call_time;
-
- if (GDScriptLanguage::get_singleton()->profiling) {
- call_time=OS::get_singleton()->get_ticks_usec();
- }
-
-#endif
- Variant::CallError err;
- if (call_ret) {
-
- GET_VARIANT_PTR(ret,argc);
- *ret = base->call(*methodname,(const Variant**)argptrs,argc,err);
- } else {
-
- base->call(*methodname,(const Variant**)argptrs,argc,err);
- }
-#ifdef DEBUG_ENABLED
- if (GDScriptLanguage::get_singleton()->profiling) {
- function_call_time+=OS::get_singleton()->get_ticks_usec() - call_time;
- }
-#endif
-
- 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;
- }
- }
- } 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;
- }
- }
- }
- 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=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_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++) {
- memnew_placement(&gdfs->state.stack[sizeof(Variant)*i],Variant(stack[i]));
- }
- 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;
-
- } 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_BREAKPOINT: {
-#ifdef DEBUG_ENABLED
- if (ScriptDebugger::get_singleton()) {
- GDScriptLanguage::get_singleton()->debug_break("Breakpoint Statement",true);
- }
-#endif
- ip+=1;
- } 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(),ERR_HANDLER_SCRIPT);
- }
-
-
- break;
- }
-
-#ifdef DEBUG_ENABLED
- if (GDScriptLanguage::get_singleton()->profiling) {
- uint64_t time_taken = OS::get_singleton()->get_ticks_usec() - function_start_time;
- profile.total_time+=time_taken;
- profile.self_time+=time_taken-function_call_time;
- profile.frame_total_time+=time_taken;
- profile.frame_self_time+=time_taken-function_call_time;
- GDScriptLanguage::get_singleton()->script_frame_time+=time_taken-function_call_time;
-
- }
-
-#endif
- 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() : function_list(this) {
-
- _stack_size=0;
- _call_size=0;
- name="<anonymous>";
-#ifdef DEBUG_ENABLED
- _func_cname=NULL;
-
- if (GDScriptLanguage::get_singleton()->lock) {
- GDScriptLanguage::get_singleton()->lock->lock();
- }
- GDScriptLanguage::get_singleton()->function_list.add(&function_list);
-
- if (GDScriptLanguage::get_singleton()->lock) {
- GDScriptLanguage::get_singleton()->lock->unlock();
- }
-
- profile.call_count=0;
- profile.self_time=0;
- profile.total_time=0;
- profile.frame_call_count=0;
- profile.frame_self_time=0;
- profile.frame_total_time=0;
- profile.last_frame_call_count=0;
- profile.last_frame_self_time=0;
- profile.last_frame_total_time=0;
-
-#endif
-}
-
-GDFunction::~GDFunction() {
-#ifdef DEBUG_ENABLED
- if (GDScriptLanguage::get_singleton()->lock) {
- GDScriptLanguage::get_singleton()->lock->lock();
- }
- GDScriptLanguage::get_singleton()->function_list.remove(&function_list);
-
- if (GDScriptLanguage::get_singleton()->lock) {
- GDScriptLanguage::get_singleton()->lock->unlock();
- }
-#endif
-}
-
-/////////////////////
-
-
-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() {
-
- ObjectTypeDB::bind_method(_MD("resume:Variant","arg"),&GDFunctionState::resume,DEFVAL(Variant()));
- 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();
- }
- }
-}
-
///////////////////////////
GDNativeClass::GDNativeClass(const StringName& p_name) {
@@ -1476,7 +50,7 @@ GDNativeClass::GDNativeClass(const StringName& p_name) {
bool GDNativeClass::_get(const StringName& p_name,Variant &r_ret) const {
bool ok;
- int v = ObjectTypeDB::get_integer_constant(name, p_name, &ok);
+ int v = ClassDB::get_integer_constant(name, p_name, &ok);
if (ok) {
r_ret=v;
@@ -1489,7 +63,7 @@ bool GDNativeClass::_get(const StringName& p_name,Variant &r_ret) const {
void GDNativeClass::_bind_methods() {
- ObjectTypeDB::bind_method(_MD("new"),&GDNativeClass::_new);
+ ClassDB::bind_method(_MD("new"),&GDNativeClass::_new);
}
@@ -1512,7 +86,7 @@ Variant GDNativeClass::_new() {
Object *GDNativeClass::instance() {
- return ObjectTypeDB::instance(name);
+ return ClassDB::instance(name);
}
@@ -1527,18 +101,39 @@ GDInstance* GDScript::_create_instance(const Variant** p_args,int p_argcount,Obj
instance->members.resize(member_indices.size());
instance->script=Ref<GDScript>(this);
instance->owner=p_owner;
+#ifdef DEBUG_ENABLED
+ //needed for hot reloading
+ for(Map<StringName,MemberInfo>::Element *E=member_indices.front();E;E=E->next()) {
+ instance->member_indices_cache[E->key()]=E->get().index;
+ }
+#endif
instance->owner->set_script_instance(instance);
/* STEP 2, INITIALIZE AND CONSRTUCT */
+#ifndef NO_THREADS
+ GDScriptLanguage::singleton->lock->lock();
+#endif
+
instances.insert(instance->owner);
+#ifndef NO_THREADS
+ GDScriptLanguage::singleton->lock->unlock();
+#endif
+
initializer->call(instance,p_args,p_argcount,r_error);
if (r_error.error!=Variant::CallError::CALL_OK) {
instance->script=Ref<GDScript>();
instance->owner->set_script_instance(NULL);
+#ifndef NO_THREADS
+ GDScriptLanguage::singleton->lock->lock();
+#endif
instances.erase(p_owner);
+#ifndef NO_THREADS
+ GDScriptLanguage::singleton->lock->unlock();
+#endif
+
ERR_FAIL_COND_V(r_error.error!=Variant::CallError::CALL_OK, NULL); //error constructing
}
@@ -1565,6 +160,8 @@ Variant GDScript::_new(const Variant** p_args,int p_argcount,Variant::CallError&
_baseptr=_baseptr->_base;
}
+ ERR_FAIL_COND_V(_baseptr->native.is_null(), Variant());
+
if (_baseptr->native.ptr()) {
owner=_baseptr->native->instance();
} else {
@@ -1599,6 +196,15 @@ bool GDScript::can_instance() const {
}
+Ref<Script> GDScript::get_base_script() const {
+
+ if (_base) {
+ return Ref<GDScript>( _base );
+ } else {
+ return Ref<Script>();
+ }
+}
+
StringName GDScript::get_instance_base_type() const {
if (native.is_valid())
@@ -1669,13 +275,94 @@ void GDScript::_update_placeholder(PlaceHolderScriptInstance *p_placeholder) {
}*/
#endif
+
+void GDScript::get_script_method_list(List<MethodInfo> *p_list) const {
+
+ for (const Map<StringName,GDFunction*>::Element *E=member_functions.front();E;E=E->next()) {
+ MethodInfo mi;
+ mi.name=E->key();
+ for(int i=0;i<E->get()->get_argument_count();i++) {
+ PropertyInfo arg;
+ arg.type=Variant::NIL; //variant
+ arg.name=E->get()->get_argument_name(i);
+ mi.arguments.push_back(arg);
+ }
+
+ mi.return_val.name="Variant";
+ p_list->push_back(mi);
+ }
+}
+
+void GDScript::get_script_property_list(List<PropertyInfo> *p_list) const {
+
+ const GDScript *sptr=this;
+ List<PropertyInfo> props;
+
+ while(sptr) {
+
+ 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()].index;
+ 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]);
+
+ }
+
+ sptr = sptr->_base;
+ }
+
+ for (List<PropertyInfo>::Element *E=props.front();E;E=E->next()) {
+ p_list->push_back(E->get());
+ }
+
+}
+
+bool GDScript::has_method(const StringName& p_method) const {
+
+ return member_functions.has(p_method);
+}
+
+MethodInfo GDScript::get_method_info(const StringName& p_method) const {
+
+ const Map<StringName,GDFunction*>::Element *E=member_functions.find(p_method);
+ if (!E)
+ return MethodInfo();
+
+ MethodInfo mi;
+ mi.name=E->key();
+ for(int i=0;i<E->get()->get_argument_count();i++) {
+ PropertyInfo arg;
+ arg.type=Variant::NIL; //variant
+ arg.name=E->get()->get_argument_name(i);
+ mi.arguments.push_back(arg);
+ }
+
+ mi.return_val.name="Variant";
+ return mi;
+
+}
+
+
bool GDScript::get_property_default_value(const StringName& p_property, Variant &r_value) const {
#ifdef TOOLS_ENABLED
- //for (const Map<StringName,Variant>::Element *I=member_default_values.front();I;I=I->next()) {
- // print_line("\t"+String(String(I->key())+":"+String(I->get())));
- //}
+ /*
+ for (const Map<StringName,Variant>::Element *I=member_default_values.front();I;I=I->next()) {
+ print_line("\t"+String(String(I->key())+":"+String(I->get())));
+ }
+ */
const Map<StringName,Variant>::Element *E=member_default_values_cache.find(p_property);
if (E) {
r_value=E->get();
@@ -1720,12 +407,12 @@ ScriptInstance* GDScript::instance_create(Object *p_this) {
top=top->_base;
if (top->native.is_valid()) {
- if (!ObjectTypeDB::is_type(p_this->get_type_name(),top->native->get_name())) {
+ if (!ClassDB::is_parent_class(p_this->get_class_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()+"'");
+ GDScriptLanguage::get_singleton()->debug_break_parse(get_path(),0,"Script inherits from native type '"+String(top->native->get_name())+"', so it can't be instanced in object of type: '"+p_this->get_class()+"'");
}
- 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_EXPLAIN("Script inherits from native type '"+String(top->native->get_name())+"', so it can't be instanced in object of type: '"+p_this->get_class()+"'");
ERR_FAIL_V(NULL);
}
@@ -1737,7 +424,16 @@ ScriptInstance* GDScript::instance_create(Object *p_this) {
}
bool GDScript::instance_has(const Object *p_this) const {
- return instances.has((Object*)p_this);
+#ifndef NO_THREADS
+ GDScriptLanguage::singleton->lock->lock();
+#endif
+ bool hasit = instances.has((Object*)p_this);
+
+#ifndef NO_THREADS
+ GDScriptLanguage::singleton->lock->unlock();
+#endif
+
+ return hasit;
}
bool GDScript::has_source_code() const {
@@ -1926,10 +622,18 @@ void GDScript::_set_subclass_path(Ref<GDScript>& p_sc,const String& p_path) {
}
}
-Error GDScript::reload() {
+Error GDScript::reload(bool p_keep_state) {
+
+#ifndef NO_THREADS
+ GDScriptLanguage::singleton->lock->lock();
+#endif
+ bool has_instances = instances.size();
+#ifndef NO_THREADS
+ GDScriptLanguage::singleton->lock->unlock();
+#endif
- ERR_FAIL_COND_V(instances.size(),ERR_ALREADY_IN_USE);
+ ERR_FAIL_COND_V(!p_keep_state && has_instances,ERR_ALREADY_IN_USE);
String basedir=path;
@@ -1957,7 +661,7 @@ Error GDScript::reload() {
bool can_run = ScriptServer::is_scripting_enabled() || parser.is_tool_script();
GDCompiler compiler;
- err = compiler.compile(&parser,this);
+ err = compiler.compile(&parser,this,p_keep_state);
if (err) {
@@ -2083,9 +787,9 @@ void GDScript::_get_property_list(List<PropertyInfo> *p_properties) const {
void GDScript::_bind_methods() {
- ObjectTypeDB::bind_native_method(METHOD_FLAGS_DEFAULT,"new",&GDScript::_new,MethodInfo("new"));
+ ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT,"new",&GDScript::_new,MethodInfo(Variant::OBJECT,"new"));
- ObjectTypeDB::bind_method(_MD("get_as_byte_code"),&GDScript::get_as_byte_code);
+ ClassDB::bind_method(_MD("get_as_byte_code"),&GDScript::get_as_byte_code);
}
@@ -2162,7 +866,7 @@ Error GDScript::load_byte_code(const String& p_path) {
Error GDScript::load_source_code(const String& p_path) {
- DVector<uint8_t> sourcef;
+ PoolVector<uint8_t> sourcef;
Error err;
FileAccess *f=FileAccess::open(p_path,FileAccess::READ,&err);
if (err) {
@@ -2172,7 +876,7 @@ Error GDScript::load_source_code(const String& p_path) {
int len = f->get_len();
sourcef.resize(len+1);
- DVector<uint8_t>::Write w = sourcef.write();
+ PoolVector<uint8_t>::Write w = sourcef.write();
int r = f->get_buffer(w.ptr(),len);
f->close();
memdelete(f);
@@ -2263,7 +967,7 @@ void GDScript::get_script_signal_list(List<MethodInfo> *r_signals) const {
}
-GDScript::GDScript() {
+GDScript::GDScript() : script_list(this) {
_static_ref=this;
@@ -2277,12 +981,37 @@ GDScript::GDScript() {
source_changed_cache=false;
#endif
+#ifdef DEBUG_ENABLED
+ if (GDScriptLanguage::get_singleton()->lock) {
+ GDScriptLanguage::get_singleton()->lock->lock();
+ }
+ GDScriptLanguage::get_singleton()->script_list.add(&script_list);
+
+ if (GDScriptLanguage::get_singleton()->lock) {
+ GDScriptLanguage::get_singleton()->lock->unlock();
+ }
+#endif
}
GDScript::~GDScript() {
for (Map<StringName,GDFunction*>::Element *E=member_functions.front();E;E=E->next()) {
memdelete( E->get() );
}
+
+ for (Map<StringName,Ref<GDScript> >::Element *E=subclasses.front();E;E=E->next()) {
+ E->get()->_owner=NULL; //bye, you are no longer owned cause I died
+ }
+
+#ifdef DEBUG_ENABLED
+ if (GDScriptLanguage::get_singleton()->lock) {
+ GDScriptLanguage::get_singleton()->lock->lock();
+ }
+ GDScriptLanguage::get_singleton()->script_list.remove(&script_list);
+
+ if (GDScriptLanguage::get_singleton()->lock) {
+ GDScriptLanguage::get_singleton()->lock->unlock();
+ }
+#endif
}
@@ -2359,11 +1088,16 @@ bool GDInstance::get(const StringName& p_name, Variant &r_ret) const {
}
{
- const Map<StringName,Variant>::Element *E = script->constants.find(p_name);
- if (E) {
- r_ret=E->get();
- return true; //index found
+ const GDScript *sl = sptr;
+ while(sl) {
+ const Map<StringName,Variant>::Element *E = sl->constants.find(p_name);
+ if (E) {
+ r_ret=E->get();
+ return true; //index found
+
+ }
+ sl=sl->_base;
}
}
@@ -2612,6 +1346,8 @@ void GDInstance::call_multilevel_reversed(const StringName& p_method,const Varia
}
}
+
+
void GDInstance::notification(int p_notification) {
//notification is not virutal, it gets called at ALL levels just like in C.
@@ -2644,6 +1380,77 @@ ScriptLanguage *GDInstance::get_language() {
return GDScriptLanguage::get_singleton();
}
+GDInstance::RPCMode GDInstance::get_rpc_mode(const StringName& p_method) const {
+
+ const GDScript *cscript = script.ptr();
+
+ while(cscript) {
+ const Map<StringName,GDFunction*>::Element *E=cscript->member_functions.find(p_method);
+ if (E) {
+
+ if (E->get()->get_rpc_mode()!=RPC_MODE_DISABLED) {
+ return E->get()->get_rpc_mode();
+ }
+
+ }
+ cscript=cscript->_base;
+ }
+
+ return RPC_MODE_DISABLED;
+}
+
+GDInstance::RPCMode GDInstance::get_rset_mode(const StringName& p_variable) const {
+
+ const GDScript *cscript = script.ptr();
+
+ while(cscript) {
+ const Map<StringName,GDScript::MemberInfo>::Element *E=cscript->member_indices.find(p_variable);
+ if (E) {
+
+ if (E->get().rpc_mode) {
+ return E->get().rpc_mode;
+ }
+
+ }
+ cscript=cscript->_base;
+ }
+
+ return RPC_MODE_DISABLED;
+}
+
+
+
+void GDInstance::reload_members() {
+
+#ifdef DEBUG_ENABLED
+
+ members.resize(script->member_indices.size()); //resize
+
+ Vector<Variant> new_members;
+ new_members.resize(script->member_indices.size());
+
+ //pass the values to the new indices
+ for(Map<StringName,GDScript::MemberInfo>::Element *E=script->member_indices.front();E;E=E->next()) {
+
+ if (member_indices_cache.has(E->key())) {
+ Variant value = members[member_indices_cache[E->key()]];
+ new_members[E->get().index]=value;
+ }
+
+ }
+
+ //apply
+ members=new_members;
+
+ //pass the values to the new indices
+ member_indices_cache.clear();
+ for(Map<StringName,GDScript::MemberInfo>::Element *E=script->member_indices.front();E;E=E->next()) {
+
+ member_indices_cache[E->key()]=E->get().index;
+ }
+
+#endif
+}
GDInstance::GDInstance() {
owner=NULL;
@@ -2652,7 +1459,15 @@ GDInstance::GDInstance() {
GDInstance::~GDInstance() {
if (script.is_valid() && owner) {
- script->instances.erase(owner);
+#ifndef NO_THREADS
+ GDScriptLanguage::singleton->lock->lock();
+#endif
+
+ script->instances.erase(owner);
+#ifndef NO_THREADS
+ GDScriptLanguage::singleton->lock->unlock();
+#endif
+
}
}
@@ -2706,7 +1521,7 @@ void GDScriptLanguage::init() {
//populate native classes
List<StringName> class_list;
- ObjectTypeDB::get_type_list(&class_list);
+ ClassDB::get_class_list(&class_list);
for(List<StringName>::Element *E=class_list.front();E;E=E->next()) {
StringName n = E->get();
@@ -2722,9 +1537,9 @@ void GDScriptLanguage::init() {
//populate singletons
- List<Globals::Singleton> singletons;
- Globals::get_singleton()->get_singletons(&singletons);
- for(List<Globals::Singleton>::Element *E=singletons.front();E;E=E->next()) {
+ List<GlobalConfig::Singleton> singletons;
+ GlobalConfig::get_singleton()->get_singletons(&singletons);
+ for(List<GlobalConfig::Singleton>::Element *E=singletons.front();E;E=E->next()) {
_add_global(E->get().name,E->get().ptr);
}
@@ -2867,9 +1682,189 @@ int GDScriptLanguage::profiling_get_frame_data(ProfilingInfo *p_info_arr,int p_i
}
+struct GDScriptDepSort {
+
+ //must support sorting so inheritance works properly (parent must be reloaded first)
+ bool operator()(const Ref<GDScript> &A, const Ref<GDScript>& B) const {
+
+ if (A==B)
+ return false; //shouldn't happen but..
+ const GDScript *I=B->get_base().ptr();
+ while(I) {
+ if (I==A.ptr()) {
+ // A is a base of B
+ return true;
+ }
+
+ I=I->get_base().ptr();
+ }
+
+ return false; //not a base
+ }
+};
+
+void GDScriptLanguage::reload_all_scripts() {
+
+
+
+#ifdef DEBUG_ENABLED
+ print_line("RELOAD ALL SCRIPTS");
+ if (lock) {
+ lock->lock();
+ }
+
+ List<Ref<GDScript> > scripts;
+
+ SelfList<GDScript> *elem=script_list.first();
+ while(elem) {
+ if (elem->self()->get_path().is_resource_file()) {
+ print_line("FOUND: "+elem->self()->get_path());
+ scripts.push_back(Ref<GDScript>(elem->self())); //cast to gdscript to avoid being erased by accident
+ }
+ elem=elem->next();
+ }
+
+ if (lock) {
+ lock->unlock();
+ }
+
+ //as scripts are going to be reloaded, must proceed without locking here
+
+ scripts.sort_custom<GDScriptDepSort>(); //update in inheritance dependency order
+
+ for(List<Ref<GDScript> >::Element *E=scripts.front();E;E=E->next()) {
+
+ print_line("RELOADING: "+E->get()->get_path());
+ E->get()->load_source_code(E->get()->get_path());
+ E->get()->reload(true);
+ }
+#endif
+}
+
+
+void GDScriptLanguage::reload_tool_script(const Ref<Script>& p_script,bool p_soft_reload) {
+
+
+#ifdef DEBUG_ENABLED
+
+ if (lock) {
+ lock->lock();
+ }
+
+ List<Ref<GDScript> > scripts;
+
+ SelfList<GDScript> *elem=script_list.first();
+ while(elem) {
+ if (elem->self()->get_path().is_resource_file()) {
+
+ scripts.push_back(Ref<GDScript>(elem->self())); //cast to gdscript to avoid being erased by accident
+ }
+ elem=elem->next();
+ }
+
+ if (lock) {
+ lock->unlock();
+ }
+
+ //when someone asks you why dynamically typed languages are easier to write....
+
+ Map< Ref<GDScript>, Map<ObjectID,List<Pair<StringName,Variant> > > > to_reload;
+
+ //as scripts are going to be reloaded, must proceed without locking here
+
+ scripts.sort_custom<GDScriptDepSort>(); //update in inheritance dependency order
+
+ for(List<Ref<GDScript> >::Element *E=scripts.front();E;E=E->next()) {
+
+ bool reload = E->get()==p_script || to_reload.has(E->get()->get_base());
+
+ if (!reload)
+ continue;
+
+ to_reload.insert(E->get(),Map<ObjectID,List<Pair<StringName,Variant> > >());
+
+ if (!p_soft_reload) {
+
+ //save state and remove script from instances
+ Map<ObjectID,List<Pair<StringName,Variant> > >& map = to_reload[E->get()];
+
+ while(E->get()->instances.front()) {
+ Object *obj = E->get()->instances.front()->get();
+ //save instance info
+ List<Pair<StringName,Variant> > state;
+ if (obj->get_script_instance()) {
+
+ obj->get_script_instance()->get_property_state(state);
+ map[obj->get_instance_ID()]=state;
+ obj->set_script(RefPtr());
+ }
+ }
+
+ //same thing for placeholders
+#ifdef TOOLS_ENABLED
+
+ while(E->get()->placeholders.size()) {
+
+ Object *obj = E->get()->placeholders.front()->get()->get_owner();
+ //save instance info
+ List<Pair<StringName,Variant> > state;
+ if (obj->get_script_instance()) {
+
+ obj->get_script_instance()->get_property_state(state);
+ map[obj->get_instance_ID()]=state;
+ obj->set_script(RefPtr());
+ }
+ }
+#endif
+
+ for(Map<ObjectID,List<Pair<StringName,Variant> > >::Element *F=E->get()->pending_reload_state.front();F;F=F->next()) {
+ map[F->key()]=F->get(); //pending to reload, use this one instead
+ }
+ }
+ }
+
+ for(Map< Ref<GDScript>, Map<ObjectID,List<Pair<StringName,Variant> > > >::Element *E=to_reload.front();E;E=E->next()) {
+
+ Ref<GDScript> scr = E->key();
+ scr->reload(p_soft_reload);
+
+ //restore state if saved
+ for (Map<ObjectID,List<Pair<StringName,Variant> > >::Element *F=E->get().front();F;F=F->next()) {
+
+ Object *obj = ObjectDB::get_instance(F->key());
+ if (!obj)
+ continue;
+
+ if (!p_soft_reload) {
+ //clear it just in case (may be a pending reload state)
+ obj->set_script(RefPtr());
+ }
+ obj->set_script(scr.get_ref_ptr());
+ if (!obj->get_script_instance()) {
+ //failed, save reload state for next time if not saved
+ if (!scr->pending_reload_state.has(obj->get_instance_ID())) {
+ scr->pending_reload_state[obj->get_instance_ID()]=F->get();
+ }
+ continue;
+ }
+
+ for (List<Pair<StringName,Variant> >::Element *G=F->get().front();G;G=G->next()) {
+ obj->get_script_instance()->set(G->get().first,G->get().second);
+ }
+
+ scr->pending_reload_state.erase(obj->get_instance_ID()); //as it reloaded, remove pending state
+ }
+
+ //if instance states were saved, set them!
+ }
+
+
+#endif
+}
+
void GDScriptLanguage::frame() {
- // print_line("calls: "+itos(calls));
+ //print_line("calls: "+itos(calls));
calls=0;
#ifdef DEBUG_ENABLED
@@ -2911,6 +1906,7 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const {
"false",
"float",
"int",
+ "bool",
"null",
"PI",
"self",
@@ -2943,6 +1939,10 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const {
"pass",
"return",
"while",
+ "remote",
+ "sync",
+ "master",
+ "slave",
0};
@@ -2984,7 +1984,7 @@ GDScriptLanguage::GDScriptLanguage() {
script_frame_time=0;
_debug_call_stack_pos=0;
- int dmcs=GLOBAL_DEF("debug/script_max_call_stack",1024);
+ int dmcs=GLOBAL_DEF("debug/script/max_call_stack",1024);
if (ScriptDebugger::get_singleton()) {
//debugging enabled!
@@ -3070,7 +2070,7 @@ bool ResourceFormatLoaderGDScript::handles_type(const String& p_type) const {
String ResourceFormatLoaderGDScript::get_resource_type(const String &p_path) const {
- String el = p_path.extension().to_lower();
+ String el = p_path.get_extension().to_lower();
if (el=="gd" || el=="gdc" || el=="gde")
return "GDScript";
return "";
@@ -3100,6 +2100,11 @@ Error ResourceFormatSaverGDScript::save(const String &p_path,const RES& p_resour
}
file->close();
memdelete(file);
+
+ if (ScriptServer::is_reload_scripts_on_save_enabled()) {
+ GDScriptLanguage::get_singleton()->reload_tool_script(p_resource,false);
+ }
+
return OK;
}
diff --git a/modules/gdscript/gd_script.h b/modules/gdscript/gd_script.h
index 5f6cd012d7..4d3baa5bc0 100644
--- a/modules/gdscript/gd_script.h
+++ b/modules/gdscript/gd_script.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2017 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 */
@@ -32,210 +32,10 @@
#include "script_language.h"
#include "io/resource_loader.h"
#include "io/resource_saver.h"
-#include "os/thread.h"
-#include "pair.h"
-#include "self_list.h"
-
-class GDInstance;
-class GDScript;
-
-
-
-class GDFunction {
-public:
-
- enum Opcode {
- OPCODE_OPERATOR,
- OPCODE_EXTENDS_TEST,
- OPCODE_SET,
- OPCODE_GET,
- OPCODE_SET_NAMED,
- OPCODE_GET_NAMED,
- OPCODE_ASSIGN,
- OPCODE_ASSIGN_TRUE,
- OPCODE_ASSIGN_FALSE,
- OPCODE_CONSTRUCT, //only for basic types!!
- OPCODE_CONSTRUCT_ARRAY,
- OPCODE_CONSTRUCT_DICTIONARY,
- OPCODE_CALL,
- OPCODE_CALL_RETURN,
- OPCODE_CALL_BUILT_IN,
- OPCODE_CALL_SELF,
- OPCODE_CALL_SELF_BASE,
- OPCODE_YIELD,
- OPCODE_YIELD_SIGNAL,
- OPCODE_YIELD_RESUME,
- OPCODE_JUMP,
- OPCODE_JUMP_IF,
- OPCODE_JUMP_IF_NOT,
- OPCODE_JUMP_TO_DEF_ARGUMENT,
- OPCODE_RETURN,
- OPCODE_ITERATE_BEGIN,
- OPCODE_ITERATE,
- OPCODE_ASSERT,
- OPCODE_BREAKPOINT,
- OPCODE_LINE,
- OPCODE_END
- };
-
- enum Address {
- ADDR_BITS=24,
- ADDR_MASK=((1<<ADDR_BITS)-1),
- ADDR_TYPE_MASK=~ADDR_MASK,
- ADDR_TYPE_SELF=0,
- ADDR_TYPE_CLASS=1,
- ADDR_TYPE_MEMBER=2,
- ADDR_TYPE_CLASS_CONSTANT=3,
- ADDR_TYPE_LOCAL_CONSTANT=4,
- ADDR_TYPE_STACK=5,
- ADDR_TYPE_STACK_VARIABLE=6,
- ADDR_TYPE_GLOBAL=7,
- ADDR_TYPE_NIL=8
- };
-
- struct StackDebug {
-
- int line;
- int pos;
- bool added;
- StringName identifier;
- };
-
-private:
-friend class GDCompiler;
-
- StringName source;
-
- mutable Variant nil;
- mutable Variant *_constants_ptr;
- int _constant_count;
- const StringName *_global_names_ptr;
- int _global_names_count;
- const int *_default_arg_ptr;
- int _default_arg_count;
- const int *_code_ptr;
- int _code_size;
- int _argument_count;
- int _stack_size;
- int _call_size;
- int _initial_line;
- bool _static;
- GDScript *_script;
-
- StringName name;
- Vector<Variant> constants;
- Vector<StringName> global_names;
- Vector<int> default_arguments;
- Vector<int> code;
-
-#ifdef TOOLS_ENABLED
- Vector<StringName> arg_names;
-#endif
-
- List<StackDebug> stack_debug;
-
- _FORCE_INLINE_ Variant *_get_variant(int p_address,GDInstance *p_instance,GDScript *p_script,Variant &self,Variant *p_stack,String& r_error) const;
- _FORCE_INLINE_ String _get_call_error(const Variant::CallError& p_err, const String& p_where,const Variant**argptrs) const;
-
-friend class GDScriptLanguage;
-
- SelfList<GDFunction> function_list;
-#ifdef DEBUG_ENABLED
- CharString func_cname;
- const char*_func_cname;
-
- struct Profile {
- StringName signature;
- uint64_t call_count;
- uint64_t self_time;
- uint64_t total_time;
- uint64_t frame_call_count;
- uint64_t frame_self_time;
- uint64_t frame_total_time;
- uint64_t last_frame_call_count;
- uint64_t last_frame_self_time;
- uint64_t last_frame_total_time;
- } profile;
-
-#endif
-
-public:
-
-
-
- struct CallState {
-
- GDInstance *instance;
- Vector<uint8_t> stack;
- int stack_size;
- Variant self;
- uint32_t alloca_size;
- GDScript *_class;
- int ip;
- int line;
- int defarg;
- Variant result;
-
- };
-
- _FORCE_INLINE_ bool is_static() const { return _static; }
-
- const int* get_code() const; //used for debug
- int get_code_size() const;
- Variant get_constant(int p_idx) const;
- StringName get_global_name(int p_idx) const;
- StringName get_name() const;
- int get_max_stack_size() const;
- int get_default_argument_count() const;
- int get_default_argument_addr(int p_idx) const;
- GDScript *get_script() const { return _script; }
-
- void debug_get_stack_member_state(int p_line,List<Pair<StringName,int> > *r_stackvars) const;
-
- _FORCE_INLINE_ bool is_empty() const { return _code_size==0; }
-
- int get_argument_count() const { return _argument_count; }
- StringName get_argument_name(int p_idx) const {
-#ifdef TOOLS_ENABLED
- ERR_FAIL_INDEX_V(p_idx,arg_names.size(),StringName());
- return arg_names[p_idx];
-#endif
- return StringName();
-
- }
- Variant get_default_argument(int p_idx) const {
- ERR_FAIL_INDEX_V(p_idx,default_arguments.size(),Variant());
- return default_arguments[p_idx];
- }
-
- Variant call(GDInstance *p_instance,const Variant **p_args, int p_argcount,Variant::CallError& r_err,CallState *p_state=NULL);
-
- GDFunction();
- ~GDFunction();
-};
-
-
-class GDFunctionState : public Reference {
-
- OBJ_TYPE(GDFunctionState,Reference);
-friend class GDFunction;
- GDFunction *function;
- GDFunction::CallState state;
- Variant _signal_callback(const Variant** p_args, int p_argcount, Variant::CallError& r_error);
-protected:
- static void _bind_methods();
-public:
-
- bool is_valid() const;
- Variant resume(const Variant& p_arg=Variant());
- GDFunctionState();
- ~GDFunctionState();
-};
-
-
+#include "gd_function.h"
class GDNativeClass : public Reference {
- OBJ_TYPE(GDNativeClass,Reference);
+ GDCLASS(GDNativeClass,Reference);
StringName name;
protected:
@@ -255,7 +55,7 @@ public:
class GDScript : public Script {
- OBJ_TYPE(GDScript,Script);
+ GDCLASS(GDScript,Script);
bool tool;
bool valid;
@@ -264,6 +64,8 @@ class GDScript : public Script {
int index;
StringName setter;
StringName getter;
+ ScriptInstance::RPCMode rpc_mode;
+
};
friend class GDInstance;
@@ -285,8 +87,11 @@ friend class GDScriptLanguage;
Map<StringName,Ref<GDScript> > subclasses;
Map<StringName,Vector<StringName> > _signals;
+
#ifdef TOOLS_ENABLED
+ Map<StringName,int> member_lines;
+
Map<StringName,Variant> member_default_values;
List<PropertyInfo> members_cache;
@@ -307,6 +112,7 @@ friend class GDScriptLanguage;
String source;
String path;
String name;
+ SelfList<GDScript> script_list;
GDInstance* _create_instance(const Variant** p_args,int p_argcount,Object *p_owner,bool p_isref,Variant::CallError &r_error);
@@ -319,7 +125,11 @@ friend class GDScriptLanguage;
virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder);
#endif
+#ifdef DEBUG_ENABLED
+
+ Map<ObjectID,List<Pair<StringName,Variant> > > pending_reload_state;
+#endif
bool _update_exports();
@@ -329,7 +139,7 @@ protected:
void _get_property_list(List<PropertyInfo> *p_properties) const;
Variant call(const StringName& p_method,const Variant** p_args,int p_argcount,Variant::CallError &r_error);
-// void call_multilevel(const StringName& p_method,const Variant** p_args,int p_argcount);
+ //void call_multilevel(const StringName& p_method,const Variant** p_args,int p_argcount);
static void _bind_methods();
public:
@@ -356,6 +166,8 @@ public:
Variant _new(const Variant** p_args,int p_argcount,Variant::CallError& r_error);
virtual bool can_instance() const;
+ virtual Ref<Script> get_base_script() const;
+
virtual StringName get_instance_base_type() const; // this may not work in all scripts, will return empty if so
virtual ScriptInstance* instance_create(Object *p_this);
virtual bool instance_has(const Object *p_this) const;
@@ -365,7 +177,7 @@ public:
virtual void set_source_code(const String& p_code);
virtual void update_exports();
- virtual Error reload();
+ virtual Error reload(bool p_keep_state=false);
virtual String get_node_type() const;
void set_script_path(const String& p_path) { path=p_path; } //because subclasses need a path too...
@@ -376,8 +188,25 @@ public:
bool get_property_default_value(const StringName& p_property,Variant& r_value) const;
+ virtual void get_script_method_list(List<MethodInfo> *p_list) const;
+ virtual bool has_method(const StringName& p_method) const;
+ virtual MethodInfo get_method_info(const StringName& p_method) const;
+
+ virtual void get_script_property_list(List<PropertyInfo> *p_list) const;
+
+
virtual ScriptLanguage *get_language() const;
+ virtual int get_member_line(const StringName& p_member) const {
+#ifdef TOOLS_ENABLED
+ if (member_lines.has(p_member))
+ return member_lines[p_member];
+ else
+#endif
+ return -1;
+
+ }
+
GDScript();
~GDScript();
};
@@ -386,9 +215,13 @@ class GDInstance : public ScriptInstance {
friend class GDScript;
friend class GDFunction;
friend class GDFunctions;
+friend class GDCompiler;
Object *owner;
Ref<GDScript> script;
+#ifdef DEBUG_ENABLED
+ Map<StringName,int> member_indices_cache; //used only for hot script reloading
+#endif
Vector<Variant> members;
bool base_ref;
@@ -397,6 +230,8 @@ friend class GDFunctions;
public:
+ _FORCE_INLINE_ Object* get_owner() { return owner; }
+
virtual bool set(const StringName& p_name, const Variant& p_value);
virtual bool get(const StringName& p_name, Variant &r_ret) const;
virtual void get_property_list(List<PropertyInfo> *p_properties) const;
@@ -409,7 +244,7 @@ public:
virtual void call_multilevel(const StringName& p_method,const Variant** p_args,int p_argcount);
virtual void call_multilevel_reversed(const StringName& p_method,const Variant** p_args,int p_argcount);
- Variant debug_get_member_by_index(int p_idx) const { return members[p_idx]; }
+ Variant debug_get_member_by_index(int p_idx) const { return members[p_idx]; }
virtual void notification(int p_notification);
@@ -419,6 +254,11 @@ public:
void set_path(const String& p_path);
+ void reload_members();
+
+ virtual RPCMode get_rpc_mode(const StringName& p_method) const;
+ virtual RPCMode get_rset_mode(const StringName& p_variable) const;
+
GDInstance();
~GDInstance();
@@ -434,28 +274,36 @@ class GDScriptLanguage : public ScriptLanguage {
Map<StringName,int> globals;
- struct CallLevel {
+ struct CallLevel {
- Variant *stack;
- GDFunction *function;
- GDInstance *instance;
- int *ip;
- int *line;
+ Variant *stack;
+ GDFunction *function;
+ GDInstance *instance;
+ int *ip;
+ int *line;
- };
+ };
- int _debug_parse_err_line;
- String _debug_parse_err_file;
- String _debug_error;
- int _debug_call_stack_pos;
- int _debug_max_call_stack;
- CallLevel *_call_stack;
+ int _debug_parse_err_line;
+ String _debug_parse_err_file;
+ String _debug_error;
+ int _debug_call_stack_pos;
+ int _debug_max_call_stack;
+ CallLevel *_call_stack;
void _add_global(const StringName& p_name,const Variant& p_value);
+friend class GDInstance;
Mutex *lock;
+
+
+
+
+friend class GDScript;
+
+ SelfList<GDScript>::List script_list;
friend class GDFunction;
SelfList<GDFunction>::List function_list;
@@ -466,54 +314,54 @@ public:
int calls;
- bool debug_break(const String& p_error,bool p_allow_continue=true);
- bool debug_break_parse(const String& p_file, int p_line,const String& p_error);
+ bool debug_break(const String& p_error,bool p_allow_continue=true);
+ bool debug_break_parse(const String& p_file, int p_line,const String& p_error);
- _FORCE_INLINE_ void enter_function(GDInstance *p_instance,GDFunction *p_function, Variant *p_stack, int *p_ip, int *p_line) {
+ _FORCE_INLINE_ void enter_function(GDInstance *p_instance,GDFunction *p_function, Variant *p_stack, int *p_ip, int *p_line) {
- if (Thread::get_main_ID()!=Thread::get_caller_ID())
- return; //no support for other threads than main for now
+ if (Thread::get_main_ID()!=Thread::get_caller_ID())
+ return; //no support for other threads than main for now
- if (ScriptDebugger::get_singleton()->get_lines_left()>0 && ScriptDebugger::get_singleton()->get_depth()>=0)
- ScriptDebugger::get_singleton()->set_depth( ScriptDebugger::get_singleton()->get_depth() +1 );
+ if (ScriptDebugger::get_singleton()->get_lines_left()>0 && ScriptDebugger::get_singleton()->get_depth()>=0)
+ ScriptDebugger::get_singleton()->set_depth( ScriptDebugger::get_singleton()->get_depth() +1 );
- if (_debug_call_stack_pos >= _debug_max_call_stack) {
- //stack overflow
- _debug_error="Stack Overflow (Stack Size: "+itos(_debug_max_call_stack)+")";
- ScriptDebugger::get_singleton()->debug(this);
- return;
- }
+ if (_debug_call_stack_pos >= _debug_max_call_stack) {
+ //stack overflow
+ _debug_error="Stack Overflow (Stack Size: "+itos(_debug_max_call_stack)+")";
+ ScriptDebugger::get_singleton()->debug(this);
+ return;
+ }
- _call_stack[_debug_call_stack_pos].stack=p_stack;
- _call_stack[_debug_call_stack_pos].instance=p_instance;
- _call_stack[_debug_call_stack_pos].function=p_function;
- _call_stack[_debug_call_stack_pos].ip=p_ip;
- _call_stack[_debug_call_stack_pos].line=p_line;
- _debug_call_stack_pos++;
- }
+ _call_stack[_debug_call_stack_pos].stack=p_stack;
+ _call_stack[_debug_call_stack_pos].instance=p_instance;
+ _call_stack[_debug_call_stack_pos].function=p_function;
+ _call_stack[_debug_call_stack_pos].ip=p_ip;
+ _call_stack[_debug_call_stack_pos].line=p_line;
+ _debug_call_stack_pos++;
+ }
- _FORCE_INLINE_ void exit_function() {
+ _FORCE_INLINE_ void exit_function() {
- if (Thread::get_main_ID()!=Thread::get_caller_ID())
- return; //no support for other threads than main for now
+ if (Thread::get_main_ID()!=Thread::get_caller_ID())
+ return; //no support for other threads than main for now
- if (ScriptDebugger::get_singleton()->get_lines_left()>0 && ScriptDebugger::get_singleton()->get_depth()>=0)
- ScriptDebugger::get_singleton()->set_depth( ScriptDebugger::get_singleton()->get_depth() -1 );
+ if (ScriptDebugger::get_singleton()->get_lines_left()>0 && ScriptDebugger::get_singleton()->get_depth()>=0)
+ ScriptDebugger::get_singleton()->set_depth( ScriptDebugger::get_singleton()->get_depth() -1 );
- if (_debug_call_stack_pos==0) {
+ if (_debug_call_stack_pos==0) {
- _debug_error="Stack Underflow (Engine Bug)";
- ScriptDebugger::get_singleton()->debug(this);
- return;
- }
+ _debug_error="Stack Underflow (Engine Bug)";
+ ScriptDebugger::get_singleton()->debug(this);
+ return;
+ }
- _debug_call_stack_pos--;
- }
+ _debug_call_stack_pos--;
+ }
virtual Vector<StackInfo> debug_get_current_stack_info() {
- if (Thread::get_main_ID()!=Thread::get_caller_ID())
- return Vector<StackInfo>();
+ if (Thread::get_main_ID()!=Thread::get_caller_ID())
+ return Vector<StackInfo>();
Vector<StackInfo> csi;
csi.resize(_debug_call_stack_pos);
@@ -555,13 +403,16 @@ public:
virtual void get_reserved_words(List<String> *p_words) const;
virtual void get_comment_delimiters(List<String> *p_delimiters) const;
virtual void get_string_delimiters(List<String> *p_delimiters) const;
- virtual String get_template(const String& p_class_name, const String& p_base_class_name) const;
+ virtual Ref<Script> get_template(const String& p_class_name, const String& p_base_class_name) const;
virtual bool validate(const String& p_script,int &r_line_error,int &r_col_error,String& r_test_error, const String& p_path="",List<String> *r_functions=NULL) const;
virtual Script *create_script() const;
virtual bool has_named_classes() const;
virtual int find_function(const String& p_function,const String& p_code) const;
- virtual String make_function(const String& p_class,const String& p_name,const StringArray& p_args) const;
+ virtual String make_function(const String& p_class,const String& p_name,const PoolStringArray& p_args) const;
virtual Error complete_code(const String& p_code, const String& p_base_path, Object*p_owner,List<String>* r_options,String& r_call_hint);
+#ifdef TOOLS_ENABLED
+ virtual Error lookup_code(const String& p_code, const String& p_symbol, const String& p_base_path, Object*p_owner, LookupResult& r_result);
+#endif
virtual void auto_indent_code(String& p_code,int p_from_line,int p_to_line) const;
virtual void add_global_constant(const StringName& p_variable,const Variant& p_value);
@@ -578,6 +429,9 @@ public:
virtual void debug_get_globals(List<String> *p_locals, List<Variant> *p_values, int p_max_subitems=-1,int p_max_depth=-1);
virtual String debug_parse_stack_level_expression(int p_level,const String& p_expression,int p_max_subitems=-1,int p_max_depth=-1);
+ virtual void reload_all_scripts();
+ virtual void reload_tool_script(const Ref<Script>& p_script,bool p_soft_reload);
+
virtual void frame();
virtual void get_public_functions(List<MethodInfo> *p_functions) const;
diff --git a/modules/gdscript/gd_tokenizer.cpp b/modules/gdscript/gd_tokenizer.cpp
index 56eacfd20e..5dabf50ba1 100644
--- a/modules/gdscript/gd_tokenizer.cpp
+++ b/modules/gdscript/gd_tokenizer.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2017 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 */
@@ -79,8 +79,8 @@ const char* GDTokenizer::token_names[TK_MAX]={
"for",
"do",
"while",
-"switch",
-"case",
+"switch (reserved)",
+"case (reserved)",
"break",
"continue",
"pass",
@@ -95,11 +95,16 @@ const char* GDTokenizer::token_names[TK_MAX]={
"setget",
"const",
"var",
+"enum",
"preload",
"assert",
"yield",
"signal",
"breakpoint",
+"rpc",
+"sync",
+"master",
+"slave",
"'['",
"']'",
"'{'",
@@ -277,7 +282,7 @@ void GDTokenizerText::_advance() {
case '\n': {
line++;
INCPOS(1);
- column=0;
+ column=1;
int i=0;
while(GETCHAR(i)==' ' || GETCHAR(i)=='\t') {
i++;
@@ -298,7 +303,7 @@ void GDTokenizerText::_advance() {
}
}
INCPOS(1);
- column=0;
+ column=1;
line++;
int i=0;
while(GETCHAR(i)==' ' || GETCHAR(i)=='\t') {
@@ -330,7 +335,7 @@ void GDTokenizerText::_advance() {
break;
} else if (_code[pos]=='\n') {
new_line++;
- new_col=0;
+ new_col=1;
} else {
new_col++;
}
@@ -353,7 +358,7 @@ void GDTokenizerText::_advance() {
}
}
INCPOS(1);
- column=0;
+ column=1;
line++;
continue;
@@ -455,6 +460,9 @@ void GDTokenizerText::_advance() {
case ':':
_make_token(TK_COLON); //for methods maybe but now useless.
break;
+ case '$':
+ _make_token(TK_DOLLAR); //for the get_node() shortener
+ break;
case '^': {
if (GETCHAR(1)=='=') {
_make_token(TK_OP_ASSIGN_BIT_XOR);
@@ -505,9 +513,11 @@ void GDTokenizerText::_advance() {
if (GETCHAR(1)=='=') {
_make_token(TK_OP_ASSIGN_ADD);
INCPOS(1);
- //} else if (GETCHAR(1)=='+') {
- // _make_token(TK_OP_PLUS_PLUS);
- // INCPOS(1);
+ /*
+ } else if (GETCHAR(1)=='+') {
+ _make_token(TK_OP_PLUS_PLUS);
+ INCPOS(1);
+ */
} else {
_make_token(TK_OP_ADD);
}
@@ -518,9 +528,11 @@ void GDTokenizerText::_advance() {
if (GETCHAR(1)=='=') {
_make_token(TK_OP_ASSIGN_SUB);
INCPOS(1);
- //} else if (GETCHAR(1)=='-') {
- // _make_token(TK_OP_MINUS_MINUS);
- // INCPOS(1);
+ /*
+ } else if (GETCHAR(1)=='-') {
+ _make_token(TK_OP_MINUS_MINUS);
+ INCPOS(1);
+ */
} else {
_make_token(TK_OP_SUB);
}
@@ -649,7 +661,7 @@ void GDTokenizerText::_advance() {
} else {
if (CharType(GETCHAR(i))=='\n') {
line++;
- column=0;
+ column=1;
}
str+=CharType(GETCHAR(i));
@@ -723,14 +735,14 @@ void GDTokenizerText::_advance() {
INCPOS(str.length());
if (hexa_found) {
- int val = str.hex_to_int();
+ int64_t val = str.hex_to_int64();
_make_constant(val);
- } else if (period_found) {
- real_t val = str.to_double();
+ } else if (period_found || exponent_found) {
+ double val = str.to_double();
//print_line("*%*%*%*% to convert: "+str+" result: "+rtos(val));
_make_constant(val);
} else {
- int val = str.to_int();
+ int64_t val = str.to_int64();
_make_constant(val);
}
@@ -780,13 +792,12 @@ void GDTokenizerText::_advance() {
{Variant::STRING,"String"},
{Variant::VECTOR2,"Vector2"},
{Variant::RECT2,"Rect2"},
- {Variant::MATRIX32,"Matrix32"},
+ {Variant::TRANSFORM2D,"Transform2D"},
{Variant::VECTOR3,"Vector3"},
- {Variant::_AABB,"AABB"},
- {Variant::_AABB,"Rect3"},
+ {Variant::RECT3,"Rect3"},
{Variant::PLANE,"Plane"},
{Variant::QUAT,"Quat"},
- {Variant::MATRIX3,"Matrix3"},
+ {Variant::BASIS,"Basis"},
{Variant::TRANSFORM,"Transform"},
{Variant::COLOR,"Color"},
{Variant::IMAGE,"Image"},
@@ -796,13 +807,13 @@ void GDTokenizerText::_advance() {
{Variant::NODE_PATH,"NodePath"},
{Variant::DICTIONARY,"Dictionary"},
{Variant::ARRAY,"Array"},
- {Variant::RAW_ARRAY,"RawArray"},
- {Variant::INT_ARRAY,"IntArray"},
- {Variant::REAL_ARRAY,"FloatArray"},
- {Variant::STRING_ARRAY,"StringArray"},
- {Variant::VECTOR2_ARRAY,"Vector2Array"},
- {Variant::VECTOR3_ARRAY,"Vector3Array"},
- {Variant::COLOR_ARRAY,"ColorArray"},
+ {Variant::POOL_BYTE_ARRAY,"PoolByteArray"},
+ {Variant::POOL_INT_ARRAY,"PoolIntArray"},
+ {Variant::POOL_REAL_ARRAY,"PoolFloatArray"},
+ {Variant::POOL_STRING_ARRAY,"PoolStringArray"},
+ {Variant::POOL_VECTOR2_ARRAY,"PoolVector2Array"},
+ {Variant::POOL_VECTOR3_ARRAY,"PoolVector3Array"},
+ {Variant::POOL_COLOR_ARRAY,"PoolColorArray"},
{Variant::VARIANT_MAX,NULL},
};
@@ -865,7 +876,12 @@ void GDTokenizerText::_advance() {
{TK_PR_YIELD,"yield"},
{TK_PR_SIGNAL,"signal"},
{TK_PR_BREAKPOINT,"breakpoint"},
+ {TK_PR_REMOTE,"remote"},
+ {TK_PR_MASTER,"master"},
+ {TK_PR_SLAVE,"slave"},
+ {TK_PR_SYNC,"sync"},
{TK_PR_CONST,"const"},
+ {TK_PR_ENUM,"enum"},
//controlflow
{TK_CF_IF,"if"},
{TK_CF_ELIF,"elif"},
@@ -874,6 +890,7 @@ void GDTokenizerText::_advance() {
{TK_CF_WHILE,"while"},
{TK_CF_DO,"do"},
{TK_CF_SWITCH,"switch"},
+ {TK_CF_CASE,"case"},
{TK_CF_BREAK,"break"},
{TK_CF_CONTINUE,"continue"},
{TK_CF_RETURN,"return"},
@@ -932,7 +949,7 @@ void GDTokenizerText::set_code(const String& p_code) {
}
code_pos=0;
line=1; //it is stand-ar-ized that lines begin in 1 in code..
- column=0;
+ column=1; //the same holds for columns
tk_rb_pos=0;
error_flag=false;
last_error="";
@@ -1046,7 +1063,7 @@ void GDTokenizerText::advance(int p_amount) {
//////////////////////////////////////////////////////////////////////////////////////////////////////
-#define BYTECODE_VERSION 10
+#define BYTECODE_VERSION 12
Error GDTokenizerBuffer::set_code_buffer(const Vector<uint8_t> & p_buffer) {
@@ -1155,7 +1172,6 @@ Vector<uint8_t> GDTokenizerBuffer::parse_code_string(const String& p_code) {
GDTokenizerText tt;
tt.set_code(p_code);
int line=-1;
- int col=0;
while(true) {
@@ -1322,7 +1338,7 @@ StringName GDTokenizerBuffer::get_token_identifier(int p_offset) const{
ERR_FAIL_INDEX_V(offset,tokens.size(),StringName());
uint32_t identifier = tokens[offset]>>TOKEN_BITS;
- ERR_FAIL_INDEX_V(identifier,identifiers.size(),StringName());
+ ERR_FAIL_INDEX_V(identifier,(uint32_t)identifiers.size(),StringName());
return identifiers[identifier];
}
@@ -1381,7 +1397,7 @@ const Variant& GDTokenizerBuffer::get_token_constant(int p_offset) const{
int offset = token+p_offset;
ERR_FAIL_INDEX_V(offset,tokens.size(),nil);
uint32_t constant = tokens[offset]>>TOKEN_BITS;
- ERR_FAIL_INDEX_V(constant,constants.size(),nil);
+ ERR_FAIL_INDEX_V(constant,(uint32_t)constants.size(),nil);
return constants[constant];
}
diff --git a/modules/gdscript/gd_tokenizer.h b/modules/gdscript/gd_tokenizer.h
index aaff573090..18e5547d36 100644
--- a/modules/gdscript/gd_tokenizer.h
+++ b/modules/gdscript/gd_tokenizer.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2017 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 */
@@ -102,11 +102,16 @@ public:
TK_PR_SETGET,
TK_PR_CONST,
TK_PR_VAR,
+ TK_PR_ENUM,
TK_PR_PRELOAD,
TK_PR_ASSERT,
TK_PR_YIELD,
TK_PR_SIGNAL,
TK_PR_BREAKPOINT,
+ TK_PR_REMOTE,
+ TK_PR_SYNC,
+ TK_PR_MASTER,
+ TK_PR_SLAVE,
TK_BRACKET_OPEN,
TK_BRACKET_CLOSE,
TK_CURLY_BRACKET_OPEN,
@@ -118,6 +123,7 @@ public:
TK_PERIOD,
TK_QUESTION_MARK,
TK_COLON,
+ TK_DOLLAR,
TK_NEWLINE,
TK_CONST_PI,
TK_ERROR,
diff --git a/modules/gdscript/register_types.cpp b/modules/gdscript/register_types.cpp
index 2aea494f39..051d1d85cd 100644
--- a/modules/gdscript/register_types.cpp
+++ b/modules/gdscript/register_types.cpp
@@ -1,14 +1,31 @@
-/*************************************************/
-/* register_script_types.cpp */
-/*************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/*************************************************/
-/* Source code within this file is: */
-/* (c) 2007-2016 Juan Linietsky, Ariel Manzur */
-/* All Rights Reserved. */
-/*************************************************/
-
+/*************************************************************************/
+/* register_types.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 "register_types.h"
#include "gd_script.h"
@@ -31,7 +48,7 @@ ResourceFormatSaverGDScript *resource_saver_gd=NULL;
class EditorExportGDScript : public EditorExportPlugin {
- OBJ_TYPE(EditorExportGDScript,EditorExportPlugin);
+ GDCLASS(EditorExportGDScript,EditorExportPlugin);
public:
@@ -83,7 +100,7 @@ public:
if (err==OK) {
fae->store_buffer(file.ptr(),file.size());
- p_path=p_path.basename()+".gde";
+ p_path=p_path.get_basename()+".gde";
}
memdelete(fae);
@@ -94,7 +111,7 @@ public:
} else {
- p_path=p_path.basename()+".gdc";
+ p_path=p_path.get_basename()+".gdc";
return file;
}
}
@@ -121,8 +138,8 @@ static void register_editor_plugin() {
void register_gdscript_types() {
- ObjectTypeDB::register_type<GDScript>();
- ObjectTypeDB::register_virtual_type<GDFunctionState>();
+ ClassDB::register_class<GDScript>();
+ ClassDB::register_virtual_class<GDFunctionState>();
script_language_gd=memnew( GDScriptLanguage );
//script_language_gd->init();
@@ -141,7 +158,7 @@ void register_gdscript_types() {
void unregister_gdscript_types() {
-
+ ScriptServer::unregister_language(script_language_gd);
if (script_language_gd)
memdelete( script_language_gd );
diff --git a/modules/gdscript/register_types.h b/modules/gdscript/register_types.h
index aed11cd1d4..5778dfcadc 100644
--- a/modules/gdscript/register_types.h
+++ b/modules/gdscript/register_types.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2017 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 */