diff options
Diffstat (limited to 'modules/gdscript')
-rw-r--r-- | modules/gdscript/SCsub | 4 | ||||
-rw-r--r-- | modules/gdscript/config.py | 7 | ||||
-rw-r--r-- | modules/gdscript/gd_compiler.cpp | 313 | ||||
-rw-r--r-- | modules/gdscript/gd_compiler.h | 10 | ||||
-rw-r--r-- | modules/gdscript/gd_editor.cpp | 790 | ||||
-rw-r--r-- | modules/gdscript/gd_function.cpp | 1499 | ||||
-rw-r--r-- | modules/gdscript/gd_function.h | 225 | ||||
-rw-r--r-- | modules/gdscript/gd_functions.cpp | 253 | ||||
-rw-r--r-- | modules/gdscript/gd_functions.h | 8 | ||||
-rw-r--r-- | modules/gdscript/gd_parser.cpp | 660 | ||||
-rw-r--r-- | modules/gdscript/gd_parser.h | 23 | ||||
-rw-r--r-- | modules/gdscript/gd_pretty_print.cpp | 34 | ||||
-rw-r--r-- | modules/gdscript/gd_pretty_print.h | 40 | ||||
-rw-r--r-- | modules/gdscript/gd_script.cpp | 1919 | ||||
-rw-r--r-- | modules/gdscript/gd_script.h | 366 | ||||
-rw-r--r-- | modules/gdscript/gd_tokenizer.cpp | 84 | ||||
-rw-r--r-- | modules/gdscript/gd_tokenizer.h | 8 | ||||
-rw-r--r-- | modules/gdscript/register_types.cpp | 51 | ||||
-rw-r--r-- | modules/gdscript/register_types.h | 2 |
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 */ |