/*************************************************************************/ /* gd_compiler.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ /* "Software"), to deal in the Software without restriction, including */ /* without limitation the rights to use, copy, modify, merge, publish, */ /* distribute, sublicense, and/or sell copies of the Software, and to */ /* permit persons to whom the Software is furnished to do so, subject to */ /* the following conditions: */ /* */ /* The above copyright notice and this permission notice shall be */ /* included in all copies or substantial portions of the Software. */ /* */ /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "gd_compiler.h" #include "gd_script.h" /* TODO: *AND and OR need early abort -Inheritance properly process (done?) *create built in initializer and constructor *assign operators *build arrays and dictionaries *call parent constructor */ void GDCompiler::_set_error(const String& p_error,const GDParser::Node *p_node) { if (error!="") return; error=p_error; err_line=p_node->line; err_column=p_node->column; } bool GDCompiler::_create_unary_operator(CodeGen& codegen,const GDParser::OperatorNode *on,Variant::Operator op, int p_stack_level) { ERR_FAIL_COND_V(on->arguments.size()!=1,false); int src_address_a = _parse_expression(codegen,on->arguments[0],p_stack_level); if (src_address_a<0) return false; codegen.opcodes.push_back(GDFunction::OPCODE_OPERATOR); // perform operator codegen.opcodes.push_back(op); //which operator codegen.opcodes.push_back(src_address_a); // argument 1 codegen.opcodes.push_back(src_address_a); // argument 2 (repeated) //codegen.opcodes.push_back(GDFunction::ADDR_TYPE_NIL); // argument 2 (unary only takes one parameter) return true; } bool GDCompiler::_create_binary_operator(CodeGen& codegen,const GDParser::OperatorNode *on,Variant::Operator op, int p_stack_level,bool p_initializer) { ERR_FAIL_COND_V(on->arguments.size()!=2,false); int src_address_a = _parse_expression(codegen,on->arguments[0],p_stack_level,false,p_initializer); if (src_address_a<0) return false; if (src_address_a&GDFunction::ADDR_TYPE_STACK<arguments[1],p_stack_level,false,p_initializer); if (src_address_b<0) return false; codegen.opcodes.push_back(GDFunction::OPCODE_OPERATOR); // perform operator codegen.opcodes.push_back(op); //which operator codegen.opcodes.push_back(src_address_a); // argument 1 codegen.opcodes.push_back(src_address_b); // argument 2 (unary only takes one parameter) return true; } /* int GDCompiler::_parse_subexpression(CodeGen& codegen,const GDParser::Node *p_expression) { int ret = _parse_expression(codegen,p_expression); if (ret<0) return ret; if (ret&(GDFunction::ADDR_TYPE_STACK<op) { case GDParser::OperatorNode::OP_ASSIGN_ADD: var_op=Variant::OP_ADD; break; case GDParser::OperatorNode::OP_ASSIGN_SUB: var_op=Variant::OP_SUBSTRACT; break; case GDParser::OperatorNode::OP_ASSIGN_MUL: var_op=Variant::OP_MULTIPLY; break; case GDParser::OperatorNode::OP_ASSIGN_DIV: var_op=Variant::OP_DIVIDE; break; case GDParser::OperatorNode::OP_ASSIGN_MOD: var_op=Variant::OP_MODULE; break; case GDParser::OperatorNode::OP_ASSIGN_SHIFT_LEFT: var_op=Variant::OP_SHIFT_LEFT; break; case GDParser::OperatorNode::OP_ASSIGN_SHIFT_RIGHT: var_op=Variant::OP_SHIFT_RIGHT; break; case GDParser::OperatorNode::OP_ASSIGN_BIT_AND: var_op=Variant::OP_BIT_AND; break; case GDParser::OperatorNode::OP_ASSIGN_BIT_OR: var_op=Variant::OP_BIT_OR; break; case GDParser::OperatorNode::OP_ASSIGN_BIT_XOR: var_op=Variant::OP_BIT_XOR; break; case GDParser::OperatorNode::OP_INIT_ASSIGN: case GDParser::OperatorNode::OP_ASSIGN: { //none } break; default: { ERR_FAIL_V(-1); } } bool initializer = p_expression->op==GDParser::OperatorNode::OP_INIT_ASSIGN; if (var_op==Variant::OP_MAX) { return _parse_expression(codegen,p_expression->arguments[1],p_stack_level,false,initializer); } if (!_create_binary_operator(codegen,p_expression,var_op,p_stack_level,initializer)) return -1; int dst_addr=(p_stack_level)|(GDFunction::ADDR_TYPE_STACK<type) { //should parse variable declaration and adjust stack accordingly... case GDParser::Node::TYPE_IDENTIFIER: { //return identifier //wait, identifier could be a local variable or something else... careful here, must reference properly //as stack may be more interesting to work with //This could be made much simpler by just indexing "self", but done this way (with custom self-addressing modes) increases peformance a lot. const GDParser::IdentifierNode *in = static_cast(p_expression); StringName identifier = in->name; // TRY STACK! if (!p_initializer && codegen.stack_identifiers.has(identifier)) { int pos = codegen.stack_identifiers[identifier]; return pos|(GDFunction::ADDR_TYPE_STACK_VARIABLE<_static) { // TRY MEMBER VARIABLES! //static function if (codegen.script->member_indices.has(identifier)) { int idx = codegen.script->member_indices[identifier].index; return idx|(GDFunction::ADDR_TYPE_MEMBER<constants.has(identifier)) { //int idx=scr->constants[identifier]; int idx = codegen.get_name_map_pos(identifier); return idx|(GDFunction::ADDR_TYPE_CLASS_CONSTANT<native.is_valid()) nc=scr->native.ptr(); scr=scr->_base; } // CLASS C++ Integer Constant if (nc) { bool success=false; int constant = ObjectTypeDB::get_integer_constant(nc->get_name(),identifier,&success); if (success) { Variant key=constant; int idx; if (!codegen.constant_map.has(key)) { idx=codegen.constant_map.size(); codegen.constant_map[key]=idx; } else { idx=codegen.constant_map[key]; } return idx|(GDFunction::ADDR_TYPE_LOCAL_CONSTANT<_owner; } /* handled in constants now if (codegen.script->subclasses.has(identifier)) { //same with a subclass, make it a local constant. int idx = codegen.get_constant_pos(codegen.script->subclasses[identifier]); return idx|(GDFunction::ADDR_TYPE_LOCAL_CONSTANT<get_global_map().has(identifier)) { int idx = GDScriptLanguage::get_singleton()->get_global_map()[identifier]; return idx|(GDFunction::ADDR_TYPE_GLOBAL<(p_expression); int idx; if (!codegen.constant_map.has(cn->value)) { idx=codegen.constant_map.size(); codegen.constant_map[cn->value]=idx; } else { idx=codegen.constant_map[cn->value]; } return idx|(GDFunction::ADDR_TYPE_LOCAL_CONSTANT<_static) { _set_error("'self' not present in static function!",p_expression); return -1; } return (GDFunction::ADDR_TYPE_SELF<(p_expression); Vector values; int slevel=p_stack_level; for(int i=0;ielements.size();i++) { int ret = _parse_expression(codegen,an->elements[i],slevel); if (ret<0) return ret; if (ret&GDFunction::ADDR_TYPE_STACK<(p_expression); Vector values; int slevel=p_stack_level; for(int i=0;ielements.size();i++) { int ret = _parse_expression(codegen,dn->elements[i].key,slevel); if (ret<0) return ret; if (ret&GDFunction::ADDR_TYPE_STACK<elements[i].value,slevel); if (ret<0) return ret; if (ret&GDFunction::ADDR_TYPE_STACK<elements.size()); for(int i=0;i(p_expression); switch(on->op) { //call/constructor operator case GDParser::OperatorNode::OP_PARENT_CALL: { ERR_FAIL_COND_V(on->arguments.size()<1,-1); const GDParser::IdentifierNode *in = (const GDParser::IdentifierNode *)on->arguments[0]; Vector arguments; int slevel = p_stack_level; for(int i=1;iarguments.size();i++) { int ret = _parse_expression(codegen,on->arguments[i],slevel); if (ret<0) return ret; if (ret&GDFunction::ADDR_TYPE_STACK<name)); //instance codegen.opcodes.push_back(arguments.size()); //argument count codegen.alloc_call(arguments.size()); for(int i=0;iarguments[0]->type==GDParser::Node::TYPE_TYPE) { //construct a basic type ERR_FAIL_COND_V(on->arguments.size()<1,-1); const GDParser::TypeNode *tn = (const GDParser::TypeNode *)on->arguments[0]; int vtype = tn->vtype; Vector arguments; int slevel = p_stack_level; for(int i=1;iarguments.size();i++) { int ret = _parse_expression(codegen,on->arguments[i],slevel); if (ret<0) return ret; if (ret&GDFunction::ADDR_TYPE_STACK<arguments[0]->type==GDParser::Node::TYPE_BUILT_IN_FUNCTION) { //built in function ERR_FAIL_COND_V(on->arguments.size()<1,-1); Vector arguments; int slevel = p_stack_level; for(int i=1;iarguments.size();i++) { int ret = _parse_expression(codegen,on->arguments[i],slevel); if (ret<0) return ret; if (ret&GDFunction::ADDR_TYPE_STACK<(on->arguments[0])->function); codegen.opcodes.push_back(on->arguments.size()-1); codegen.alloc_call(on->arguments.size()-1); for(int i=0;iarguments.size()<2,-1); const GDParser::Node *instance = on->arguments[0]; bool in_static=false; if (instance->type==GDParser::Node::TYPE_SELF) { //room for optimization } Vector arguments; int slevel = p_stack_level; for(int i=0;iarguments.size();i++) { int ret; if (i==0 && on->arguments[i]->type==GDParser::Node::TYPE_SELF && codegen.function_node && codegen.function_node->_static) { //static call to self ret=(GDFunction::ADDR_TYPE_CLASS<arguments[i]->type!=GDParser::Node::TYPE_IDENTIFIER) { _set_error("Attempt to call a non-identifier.",on); return -1; } GDParser::IdentifierNode *id = static_cast(on->arguments[i]); ret=codegen.get_name_map_pos(id->name); } else { ret = _parse_expression(codegen,on->arguments[i],slevel); if (ret<0) return ret; if (ret&GDFunction::ADDR_TYPE_STACK<arguments.size()-2); codegen.alloc_call(on->arguments.size()-2); for(int i=0;iarguments.size() && on->arguments.size()!=2,-1); Vector arguments; int slevel = p_stack_level; for(int i=0;iarguments.size();i++) { int ret = _parse_expression(codegen,on->arguments[i],slevel); if (ret<0) return ret; if (ret&GDFunction::ADDR_TYPE_STACK<arguments.size()!=2,-1); int slevel = p_stack_level; bool named=(on->op==GDParser::OperatorNode::OP_INDEX_NAMED); int from = _parse_expression(codegen,on->arguments[0],slevel); if (from<0) return from; int index; if (named) { index=codegen.get_name_map_pos(static_cast(on->arguments[1])->name); } else { if (on->arguments[1]->type==GDParser::Node::TYPE_CONSTANT && static_cast(on->arguments[1])->value.get_type()==Variant::STRING) { //also, somehow, named (speed up anyway) StringName name = static_cast(on->arguments[1])->value; index=codegen.get_name_map_pos(name); named=true; } else { //regular indexing if (from&GDFunction::ADDR_TYPE_STACK<arguments[1],slevel); if (index<0) return index; } } codegen.opcodes.push_back(named?GDFunction::OPCODE_GET_NAMED:GDFunction::OPCODE_GET); // perform operator codegen.opcodes.push_back(from); // argument 1 codegen.opcodes.push_back(index); // argument 2 (unary only takes one parameter) } break; case GDParser::OperatorNode::OP_AND: { // AND 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.opcodes.push_back(GDFunction::OPCODE_JUMP_IF_NOT); codegen.opcodes.push_back(res); int jump_fail_pos2=codegen.opcodes.size(); codegen.opcodes.push_back(0); codegen.alloc_stack(p_stack_level); //it will be used.. codegen.opcodes.push_back(GDFunction::OPCODE_ASSIGN_TRUE); codegen.opcodes.push_back(p_stack_level|GDFunction::ADDR_TYPE_STACK<arguments[0],p_stack_level); if (res<0) return res; codegen.opcodes.push_back(GDFunction::OPCODE_JUMP_IF); codegen.opcodes.push_back(res); int jump_success_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.opcodes.push_back(GDFunction::OPCODE_JUMP_IF); codegen.opcodes.push_back(res); int jump_success_pos2=codegen.opcodes.size(); codegen.opcodes.push_back(0); codegen.alloc_stack(p_stack_level); //it will be used.. codegen.opcodes.push_back(GDFunction::OPCODE_ASSIGN_FALSE); codegen.opcodes.push_back(p_stack_level|GDFunction::ADDR_TYPE_STACK<arguments.size()!=2,-1); if (on->arguments[0]->type==GDParser::Node::TYPE_OPERATOR && (static_cast(on->arguments[0])->op==GDParser::OperatorNode::OP_INDEX || static_cast(on->arguments[0])->op==GDParser::OperatorNode::OP_INDEX_NAMED)) { //SET (chained) MODE!! int slevel=p_stack_level; GDParser::OperatorNode* op = static_cast(on->arguments[0]); /* Find chain of sets */ List chain; { //create get/set chain GDParser::OperatorNode* n=op; while(true) { chain.push_back(n); if (n->arguments[0]->type!=GDParser::Node::TYPE_OPERATOR) break; n = static_cast(n->arguments[0]); if (n->op!=GDParser::OperatorNode::OP_INDEX && n->op!=GDParser::OperatorNode::OP_INDEX_NAMED) break; } } /* Chain of gets */ //get at (potential) root stack pos, so it can be returned int prev_pos = _parse_expression(codegen,chain.back()->get()->arguments[0],slevel); if (prev_pos<0) return prev_pos; int retval=prev_pos; //print_line("retval: "+itos(retval)); if (retval&GDFunction::ADDR_TYPE_STACK< setchain; int prev_key_idx=-1; for(List::Element *E=chain.back();E;E=E->prev()) { if (E==chain.front()) //ignore first break; bool named = E->get()->op==GDParser::OperatorNode::OP_INDEX_NAMED; int key_idx; if (named) { key_idx = codegen.get_name_map_pos(static_cast(E->get()->arguments[1])->name); //printf("named key %x\n",key_idx); } else { if (prev_pos&(GDFunction::ADDR_TYPE_STACK<get()->arguments[1]; key_idx = _parse_expression(codegen,key,slevel); //printf("expr key %x\n",key_idx); //stack was raised here if retval was stack but.. } if (key_idx<0) return key_idx; codegen.opcodes.push_back(named ? GDFunction::OPCODE_GET_NAMED : GDFunction::OPCODE_GET); codegen.opcodes.push_back(prev_pos); codegen.opcodes.push_back(key_idx); slevel++; codegen.alloc_stack(slevel); int dst_pos = (GDFunction::ADDR_TYPE_STACK<(op)->op==GDParser::OperatorNode::OP_INDEX_NAMED) { set_index=codegen.get_name_map_pos(static_cast(op->arguments[1])->name); named=true; } else { set_index = _parse_expression(codegen,op->arguments[1],slevel+1); named=false; } if (set_index<0) return set_index; if (set_index&GDFunction::ADDR_TYPE_STACK<arguments[0],slevel,false,on->op==GDParser::OperatorNode::OP_INIT_ASSIGN); if (dst_address_a<0) return -1; if (dst_address_a&GDFunction::ADDR_TYPE_STACK<arguments.size()!=2,false); int slevel = p_stack_level; int src_address_a = _parse_expression(codegen,on->arguments[0],slevel); if (src_address_a<0) return -1; if (src_address_a&GDFunction::ADDR_TYPE_STACK<arguments[1],slevel); if (src_address_b<0) return -1; codegen.opcodes.push_back(GDFunction::OPCODE_EXTENDS_TEST); // perform operator codegen.opcodes.push_back(src_address_a); // argument 1 codegen.opcodes.push_back(src_address_b); // argument 2 (unary only takes one parameter) } break; default: { ERR_EXPLAIN("Bug in bytecode compiler, unexpected operator #"+itos(on->op)+" in parse tree while parsing expression."); ERR_FAIL_V(0); //unreachable code } break; } int dst_addr=(p_stack_level)|(GDFunction::ADDR_TYPE_STACK<line; for(int i=0;istatements.size();i++) { const GDParser::Node *s = p_block->statements[i]; switch(s->type) { case GDParser::Node::TYPE_NEWLINE: { const GDParser::NewLineNode *nl = static_cast(s); codegen.opcodes.push_back(GDFunction::OPCODE_LINE); codegen.opcodes.push_back(nl->line); codegen.current_line=nl->line; } break; case GDParser::Node::TYPE_CONTROL_FLOW: { // try subblocks const GDParser::ControlFlowNode *cf = static_cast(s); switch(cf->cf_type) { case GDParser::ControlFlowNode::CF_IF: { #ifdef DEBUG_ENABLED codegen.opcodes.push_back(GDFunction::OPCODE_LINE); codegen.opcodes.push_back(cf->line); codegen.current_line=cf->line; #endif int ret = _parse_expression(codegen,cf->arguments[0],p_stack_level,false); if (ret<0) return ERR_PARSE_ERROR; codegen.opcodes.push_back(GDFunction::OPCODE_JUMP_IF_NOT); codegen.opcodes.push_back(ret); int else_addr=codegen.opcodes.size(); codegen.opcodes.push_back(0); //temporary Error err = _parse_block(codegen,cf->body,p_stack_level,p_break_addr,p_continue_addr); if (err) return err; if (cf->body_else) { codegen.opcodes.push_back(GDFunction::OPCODE_JUMP); int end_addr=codegen.opcodes.size(); codegen.opcodes.push_back(0); codegen.opcodes[else_addr]=codegen.opcodes.size(); Error err = _parse_block(codegen,cf->body_else,p_stack_level,p_break_addr,p_continue_addr); if (err) return err; codegen.opcodes[end_addr]=codegen.opcodes.size(); } else { //end without else codegen.opcodes[else_addr]=codegen.opcodes.size(); } } break; case GDParser::ControlFlowNode::CF_FOR: { int slevel=p_stack_level; int iter_stack_pos=slevel; int iterator_pos = (slevel++)|(GDFunction::ADDR_TYPE_STACK<(cf->arguments[0])->name,iter_stack_pos); int ret = _parse_expression(codegen,cf->arguments[1],slevel,false); if (ret<0) return ERR_COMPILATION_FAILED; //assign container codegen.opcodes.push_back(GDFunction::OPCODE_ASSIGN); codegen.opcodes.push_back(container_pos); codegen.opcodes.push_back(ret); //begin loop codegen.opcodes.push_back(GDFunction::OPCODE_ITERATE_BEGIN); codegen.opcodes.push_back(counter_pos); codegen.opcodes.push_back(container_pos); codegen.opcodes.push_back(codegen.opcodes.size()+4); codegen.opcodes.push_back(iterator_pos); codegen.opcodes.push_back(GDFunction::OPCODE_JUMP); //skip code for next codegen.opcodes.push_back(codegen.opcodes.size()+8); //break loop int break_pos=codegen.opcodes.size(); codegen.opcodes.push_back(GDFunction::OPCODE_JUMP); //skip code for next codegen.opcodes.push_back(0); //skip code for next //next loop int continue_pos=codegen.opcodes.size(); codegen.opcodes.push_back(GDFunction::OPCODE_ITERATE); codegen.opcodes.push_back(counter_pos); codegen.opcodes.push_back(container_pos); codegen.opcodes.push_back(break_pos); codegen.opcodes.push_back(iterator_pos); Error err = _parse_block(codegen,cf->body,slevel,break_pos,continue_pos); if (err) return err; codegen.opcodes.push_back(GDFunction::OPCODE_JUMP); codegen.opcodes.push_back(continue_pos); codegen.opcodes[break_pos+1]=codegen.opcodes.size(); codegen.pop_stack_identifiers(); } break; case GDParser::ControlFlowNode::CF_WHILE: { codegen.opcodes.push_back(GDFunction::OPCODE_JUMP); codegen.opcodes.push_back(codegen.opcodes.size()+3); int break_addr=codegen.opcodes.size(); codegen.opcodes.push_back(GDFunction::OPCODE_JUMP); codegen.opcodes.push_back(0); int continue_addr=codegen.opcodes.size(); int ret = _parse_expression(codegen,cf->arguments[0],p_stack_level,false); if (ret<0) return ERR_PARSE_ERROR; codegen.opcodes.push_back(GDFunction::OPCODE_JUMP_IF_NOT); codegen.opcodes.push_back(ret); codegen.opcodes.push_back(break_addr); Error err = _parse_block(codegen,cf->body,p_stack_level,break_addr,continue_addr); if (err) return err; codegen.opcodes.push_back(GDFunction::OPCODE_JUMP); codegen.opcodes.push_back(continue_addr); codegen.opcodes[break_addr+1]=codegen.opcodes.size(); } break; case GDParser::ControlFlowNode::CF_SWITCH: { } break; case GDParser::ControlFlowNode::CF_BREAK: { if (p_break_addr<0) { _set_error("'break'' not within loop",cf); return ERR_COMPILATION_FAILED; } codegen.opcodes.push_back(GDFunction::OPCODE_JUMP); codegen.opcodes.push_back(p_break_addr); } break; case GDParser::ControlFlowNode::CF_CONTINUE: { if (p_continue_addr<0) { _set_error("'continue' not within loop",cf); return ERR_COMPILATION_FAILED; } codegen.opcodes.push_back(GDFunction::OPCODE_JUMP); codegen.opcodes.push_back(p_continue_addr); } break; case GDParser::ControlFlowNode::CF_RETURN: { int ret; if (cf->arguments.size()) { ret = _parse_expression(codegen,cf->arguments[0],p_stack_level,false); if (ret<0) return ERR_PARSE_ERROR; } else { ret=GDFunction::ADDR_TYPE_NIL << GDFunction::ADDR_BITS; } codegen.opcodes.push_back(GDFunction::OPCODE_RETURN); codegen.opcodes.push_back(ret); } break; } } break; case GDParser::Node::TYPE_ASSERT: { // try subblocks const GDParser::AssertNode *as = static_cast(s); int ret = _parse_expression(codegen,as->condition,p_stack_level,false); if (ret<0) return ERR_PARSE_ERROR; codegen.opcodes.push_back(GDFunction::OPCODE_ASSERT); codegen.opcodes.push_back(ret); } break; case GDParser::Node::TYPE_LOCAL_VAR: { const GDParser::LocalVarNode *lv = static_cast(s); codegen.add_stack_identifier(lv->name,p_stack_level++); codegen.alloc_stack(p_stack_level); new_identifiers++; } break; default: { //expression int ret = _parse_expression(codegen,s,p_stack_level,true); if (ret<0) return ERR_PARSE_ERROR; } break; } } codegen.pop_stack_identifiers(); return OK; } Error GDCompiler::_parse_function(GDScript *p_script,const GDParser::ClassNode *p_class,const GDParser::FunctionNode *p_func) { Vector bytecode; CodeGen codegen; codegen.class_node=p_class; codegen.script=p_script; codegen.function_node=p_func; codegen.stack_max=0; codegen.current_line=0; codegen.call_max=0; codegen.debug_stack=ScriptDebugger::get_singleton()!=NULL; Vector argnames; int stack_level=0; if (p_func) { for(int i=0;iarguments.size();i++) { int idx = i; codegen.add_stack_identifier(p_func->arguments[i],i); #ifdef TOOLS_ENABLED argnames.push_back(p_func->arguments[i]); #endif } stack_level=p_func->arguments.size(); } codegen.alloc_stack(stack_level); /* Parse initializer -if applies- */ bool is_initializer=false || !p_func; if (!p_func || String(p_func->name)=="_init") { //parse initializer for class members if (!p_func && p_class->extends_used && p_script->native.is_null()){ //call implicit parent constructor codegen.opcodes.push_back(GDFunction::OPCODE_CALL_SELF_BASE); codegen.opcodes.push_back(codegen.get_name_map_pos("_init")); codegen.opcodes.push_back(0); codegen.opcodes.push_back((GDFunction::ADDR_TYPE_STACK<initializer,stack_level); if (err) return err; is_initializer=true; } /* Parse default argument code -if applies- */ Vector defarg_addr; StringName func_name; if (p_func) { if (p_func->default_values.size()) { codegen.opcodes.push_back(GDFunction::OPCODE_JUMP_TO_DEF_ARGUMENT); defarg_addr.push_back(codegen.opcodes.size()); for(int i=0;idefault_values.size();i++) { _parse_expression(codegen,p_func->default_values[i],stack_level,true); defarg_addr.push_back(codegen.opcodes.size()); } defarg_addr.invert(); } Error err = _parse_block(codegen,p_func->body,stack_level); if (err) return err; func_name=p_func->name; } else { func_name="_init"; } codegen.opcodes.push_back(GDFunction::OPCODE_END); GDFunction *gdfunc=NULL; //if (String(p_func->name)=="") { //initializer func // gdfunc = &p_script->initializer; //} else { //regular func p_script->member_functions[func_name]=GDFunction(); gdfunc = &p_script->member_functions[func_name]; //} if (p_func) gdfunc->_static=p_func->_static; #ifdef TOOLS_ENABLED gdfunc->arg_names=argnames; #endif //constants if (codegen.constant_map.size()) { gdfunc->_constant_count=codegen.constant_map.size(); gdfunc->constants.resize(codegen.constant_map.size()); gdfunc->_constants_ptr=&gdfunc->constants[0]; const Variant *K=NULL; while((K=codegen.constant_map.next(K))) { int idx = codegen.constant_map[*K]; gdfunc->constants[idx]=*K; } } else { gdfunc->_constants_ptr=NULL; gdfunc->_constant_count=0; } //global names if (codegen.name_map.size()) { gdfunc->global_names.resize(codegen.name_map.size()); gdfunc->_global_names_ptr = &gdfunc->global_names[0]; for(Map::Element *E=codegen.name_map.front();E;E=E->next()) { gdfunc->global_names[E->get()]=E->key(); } gdfunc->_global_names_count=gdfunc->global_names.size(); } else { gdfunc->_global_names_ptr = NULL; gdfunc->_global_names_count =0; } if (codegen.opcodes.size()) { gdfunc->code=codegen.opcodes; gdfunc->_code_ptr=&gdfunc->code[0]; gdfunc->_code_size=codegen.opcodes.size(); } else { gdfunc->_code_ptr=NULL; gdfunc->_code_size=0; } if (defarg_addr.size()) { gdfunc->default_arguments=defarg_addr; gdfunc->_default_arg_count=defarg_addr.size(); gdfunc->_default_arg_ptr=&gdfunc->default_arguments[0]; } else { gdfunc->_default_arg_count=0; gdfunc->_default_arg_ptr=NULL; } gdfunc->_argument_count=p_func ? p_func->arguments.size() : 0; gdfunc->_stack_size=codegen.stack_max; gdfunc->_call_size=codegen.call_max; gdfunc->name=func_name; gdfunc->_script=p_script; gdfunc->source=source; #ifdef DEBUG_ENABLED { gdfunc->func_cname=(String(source)+" - "+String(func_name)).utf8(); gdfunc->_func_cname=gdfunc->func_cname.get_data(); } #endif if (p_func) { gdfunc->_initial_line=p_func->line; } else { gdfunc->_initial_line=0; } if (codegen.debug_stack) gdfunc->stack_debug=codegen.stack_debug; if (is_initializer) p_script->initializer=gdfunc; return OK; } Error GDCompiler::_parse_class(GDScript *p_script,GDScript *p_owner,const GDParser::ClassNode *p_class) { p_script->native=Ref(); p_script->base=Ref(); p_script->_base=NULL; p_script->members.clear(); p_script->constants.clear(); p_script->member_functions.clear(); p_script->member_indices.clear(); p_script->member_info.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; if (p_class->extends_used) { //do inheritance String path = p_class->extends_file; Ref script; Ref native; if (path!="") { //path (and optionally subclasses) if (path.is_rel_path()) { String base = p_script->get_path(); if (base=="" || base.is_rel_path()) { _set_error("Could not resolve relative path for parent class: "+path,p_class); return ERR_FILE_NOT_FOUND; } path=base.get_base_dir().plus_file(path); } script = ResourceLoader::load(path); if (script.is_null()) { _set_error("Could not load base class: "+path,p_class); return ERR_FILE_NOT_FOUND; } if (!script->valid) { _set_error("Script not fully loaded (cyclic preload?): "+path,p_class); return ERR_BUSY; } //print_line("EXTENDS PATH: "+path+" script is "+itos(script.is_valid())+" indices is "+itos(script->member_indices.size())+" valid? "+itos(script->valid)); if (p_class->extends_class.size()) { for(int i=0;iextends_class.size();i++) { String sub = p_class->extends_class[i]; if (script->subclasses.has(sub)) { script=script->subclasses[sub]; } else { _set_error("Could not find subclass: "+sub,p_class); return ERR_FILE_NOT_FOUND; } } } } else { ERR_FAIL_COND_V(p_class->extends_class.size()==0,ERR_BUG); //look around for the subclasses String base=p_class->extends_class[0]; GDScript *p = p_owner; Ref base_class; while(p) { if (p->subclasses.has(base)) { base_class=p->subclasses[base]; break; } p=p->_owner; } if (base_class.is_valid()) { for(int i=1;iextends_class.size();i++) { String subclass=p_class->extends_class[i]; if (base_class->subclasses.has(subclass)) { base_class=base_class->subclasses[subclass]; } else { _set_error("Could not find subclass: "+subclass,p_class); return ERR_FILE_NOT_FOUND; } } script=base_class; } else { if (p_class->extends_class.size()>1) { _set_error("Invalid inheritance (unknown class+subclasses)",p_class); return ERR_FILE_NOT_FOUND; } //if not found, try engine classes if (!GDScriptLanguage::get_singleton()->get_global_map().has(base)) { _set_error("Unknown class: '"+base+"'",p_class); return ERR_FILE_NOT_FOUND; } int base_idx = GDScriptLanguage::get_singleton()->get_global_map()[base]; native = GDScriptLanguage::get_singleton()->get_global_array()[base_idx]; if (!native.is_valid()) { _set_error("Global not a class: '"+base+"'",p_class); return ERR_FILE_NOT_FOUND; } } } if (script.is_valid()) { p_script->base=script; p_script->_base=p_script->base.ptr(); p_script->member_indices=script->member_indices; } else if (native.is_valid()) { p_script->native=native; } else { _set_error("Could not determine inheritance",p_class); return ERR_FILE_NOT_FOUND; } } //print_line("Script: "+p_script->get_path()+" indices: "+itos(p_script->member_indices.size())); for(int i=0;ivariables.size();i++) { StringName name = p_class->variables[i].identifier; if (p_script->member_indices.has(name)) { _set_error("Member '"+name+"' already exists (in current or parent class)",p_class); return ERR_ALREADY_EXISTS; } if (p_class->variables[i]._export.type!=Variant::NIL) { p_script->member_info[name]=p_class->variables[i]._export; #ifdef TOOLS_ENABLED if (p_class->variables[i].default_value.get_type()!=Variant::NIL) { p_script->member_default_values[name]=p_class->variables[i].default_value; } #endif } //int new_idx = p_script->member_indices.size(); GDScript::MemberInfo minfo; minfo.index = p_script->member_indices.size(); minfo.setter = p_class->variables[i].setter; minfo.getter = p_class->variables[i].getter; p_script->member_indices[name]=minfo; p_script->members.insert(name); } for(int i=0;iconstant_expressions.size();i++) { StringName name = p_class->constant_expressions[i].identifier; ERR_CONTINUE( p_class->constant_expressions[i].expression->type!=GDParser::Node::TYPE_CONSTANT ); GDParser::ConstantNode *constant = static_cast(p_class->constant_expressions[i].expression); p_script->constants.insert(name,constant->value); //p_script->constants[constant->value].make_const(); } //parse sub-classes for(int i=0;isubclasses.size();i++) { StringName name = p_class->subclasses[i]->name; Ref subclass = memnew( GDScript ); Error err = _parse_class(subclass.ptr(),p_script,p_class->subclasses[i]); if (err) return err; p_script->constants.insert(name,subclass); //once parsed, goes to the list of constants p_script->subclasses.insert(name,subclass); } //parse methods bool has_initializer=false; for(int i=0;ifunctions.size();i++) { if (!has_initializer && p_class->functions[i]->name=="_init") has_initializer=true; Error err = _parse_function(p_script,p_class,p_class->functions[i]); if (err) return err; } //parse static methods for(int i=0;istatic_functions.size();i++) { Error err = _parse_function(p_script,p_class,p_class->static_functions[i]); if (err) return err; } if (!has_initializer) { //create a constructor Error err = _parse_function(p_script,p_class,NULL); if (err) return err; } return OK; } Error GDCompiler::compile(const GDParser *p_parser,GDScript *p_script) { err_line=-1; err_column=-1; error=""; parser=p_parser; const GDParser::Node* root = parser->get_parse_tree(); ERR_FAIL_COND_V(root->type!=GDParser::Node::TYPE_CLASS,ERR_INVALID_DATA); source=p_script->get_path(); Error err = _parse_class(p_script,NULL,static_cast(root)); if (err) return err; return OK; } String GDCompiler::get_error() const { return error; } int GDCompiler::get_error_line() const{ return err_line; } int GDCompiler::get_error_column() const{ return err_column; } GDCompiler::GDCompiler() { }