diff --git a/bin/tests/test_gdscript.cpp b/bin/tests/test_gdscript.cpp index 43d65f782b7..1ee27ec661b 100644 --- a/bin/tests/test_gdscript.cpp +++ b/bin/tests/test_gdscript.cpp @@ -580,6 +580,28 @@ static void _disassemble_class(const Ref& p_class,const Vector txt+="\"]"; incr+=4; + } break; + case GDFunction::OPCODE_SET_MEMBER: { + + txt+=" set_member "; + txt+="[\""; + txt+=func.get_global_name(code[ip+1]); + txt+="\"]="; + txt+=DADDR(2); + incr+=3; + + + } break; + case GDFunction::OPCODE_GET_MEMBER: { + + txt+=" get_member "; + txt+=DADDR(2); + txt+="="; + txt+="[\""; + txt+=func.get_global_name(code[ip+1]); + txt+="\"]"; + incr+=3; + } break; case GDFunction::OPCODE_ASSIGN: { diff --git a/core/object_type_db.cpp b/core/object_type_db.cpp index babbb6b8adb..94cc865b623 100644 --- a/core/object_type_db.cpp +++ b/core/object_type_db.cpp @@ -991,6 +991,22 @@ StringName ClassDB::get_property_getter(StringName p_class,const StringName p_pr return StringName(); } +bool ClassDB::has_property(const StringName& p_class, const StringName& p_property, bool p_no_inheritance) { + + + ClassInfo *type=classes.getptr(p_class); + ClassInfo *check=type; + while(check) { + if (check->property_setget.has(p_property)) + return true; + + if (p_no_inheritance) + break; + check=check->inherits_ptr; + } + + return false; +} void ClassDB::set_method_flags(StringName p_class,StringName p_method,int p_flags) { diff --git a/core/object_type_db.h b/core/object_type_db.h index aebe7670288..3613d351e4d 100644 --- a/core/object_type_db.h +++ b/core/object_type_db.h @@ -481,6 +481,7 @@ public: static void get_property_list(StringName p_class, List *p_list, bool p_no_inheritance=false, const Object *p_validator=NULL); static bool set_property(Object* p_object, const StringName& p_property, const Variant& p_value, bool *r_valid=NULL); static bool get_property(Object* p_object,const StringName& p_property, Variant& r_value); + static bool has_property(const StringName& p_class,const StringName& p_property,bool p_no_inheritance=false); static Variant::Type get_property_type(const StringName& p_class, const StringName& p_property,bool *r_is_valid=NULL); static StringName get_property_setter(StringName p_class,const StringName p_property); static StringName get_property_getter(StringName p_class,const StringName p_property); diff --git a/modules/gdscript/gd_compiler.cpp b/modules/gdscript/gd_compiler.cpp index 75a49ace314..7483af298c1 100644 --- a/modules/gdscript/gd_compiler.cpp +++ b/modules/gdscript/gd_compiler.cpp @@ -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< chain; { @@ -784,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(n->arguments[0]); + if (_is_class_member_property(codegen,identifier->name)) { + assign_property = identifier->name; + + } + + } break; + } n = static_cast(n->arguments[0]); if (n->op!=GDParser::OperatorNode::OP_INDEX && n->op!=GDParser::OperatorNode::OP_INDEX_NAMED) break; @@ -810,6 +860,17 @@ int GDCompiler::_parse_expression(CodeGen& codegen,const GDParser::Node *p_expre Vector setchain; + + 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::Element *E=chain.back();E;E=E->prev()) { @@ -840,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); @@ -852,7 +913,10 @@ 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); @@ -881,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<arguments[0]->type==GDParser::Node::TYPE_IDENTIFIER && _is_class_member_property(codegen,static_cast(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(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<(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++; @@ -1249,6 +1334,10 @@ Error GDCompiler::_parse_function(GDScript *p_script,const GDParser::ClassNode * if (p_func) { for(int i=0;iarguments.size();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]); @@ -1653,6 +1742,10 @@ Error GDCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, const GDPa _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) { @@ -1691,6 +1784,11 @@ Error GDCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, const GDPa 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(p_class->constant_expressions[i].expression); p_script->constants.insert(name,constant->value); diff --git a/modules/gdscript/gd_compiler.h b/modules/gdscript/gd_compiler.h index eefd6de3e78..dd211a852c7 100644 --- a/modules/gdscript/gd_compiler.h +++ b/modules/gdscript/gd_compiler.h @@ -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 _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); diff --git a/modules/gdscript/gd_editor.cpp b/modules/gdscript/gd_editor.cpp index 1a9bef66915..6be47fe8aab 100644 --- a/modules/gdscript/gd_editor.cpp +++ b/modules/gdscript/gd_editor.cpp @@ -347,7 +347,7 @@ String GDScriptLanguage::make_function(const String& p_class,const String& p_nam struct GDCompletionIdentifier { - StringName GDCLASS; + StringName obj_type; Ref script; Variant::Type type; Variant value; //im case there is a value, also return it @@ -365,10 +365,10 @@ static GDCompletionIdentifier _get_type_from_variant(const Variant& p_variant) { Object *obj = p_variant; if (obj) { //if (obj->cast_to()) { - // t.GDCLASS=obj->cast_to()->get_name(); + // t.obj_type=obj->cast_to()->get_name(); // t.value=Variant(); //} else { - t.GDCLASS=obj->get_class(); + t.obj_type=obj->get_class(); //} } } @@ -380,7 +380,7 @@ static GDCompletionIdentifier _get_type_from_pinfo(const PropertyInfo& p_info) { GDCompletionIdentifier t; t.type=p_info.type; if (p_info.hint==PROPERTY_HINT_RESOURCE_TYPE) { - t.GDCLASS=p_info.hint_string; + t.obj_type=p_info.hint_string; } return t; } @@ -508,7 +508,7 @@ static GDCompletionIdentifier _get_native_class(GDCompletionContext& context) { id.type=Variant::OBJECT; if (context.base) id.value=context.base; - id.GDCLASS=nc->get_name(); + id.obj_type=nc->get_name(); return id; } @@ -609,15 +609,15 @@ static bool _guess_expression_type(GDCompletionContext& context,const GDParser:: GDNativeClass *gdnc = obj->cast_to(); r_type.type=Variant::OBJECT; r_type.value=Variant(); - r_type.GDCLASS=gdnc->get_name(); + r_type.obj_type=gdnc->get_name(); return true; } } - if (ClassDB::has_method(base.GDCLASS,id)) { + if (ClassDB::has_method(base.obj_type,id)) { #ifdef TOOLS_ENABLED - MethodBind *mb = ClassDB::get_method(base.GDCLASS,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.. @@ -643,7 +643,7 @@ static bool _guess_expression_type(GDCompletionContext& context,const GDParser:: } } - if (all_valid && String(id)=="get_node" && ClassDB::is_parent_class(base.GDCLASS,"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/")) { @@ -686,7 +686,7 @@ static bool _guess_expression_type(GDCompletionContext& context,const GDParser:: scr = ResourceLoader::load(script); - r_type.GDCLASS="Node"; + r_type.obj_type="Node"; r_type.type=Variant::OBJECT; r_type.script=scr; r_type.value=Variant(); @@ -729,7 +729,7 @@ static bool _guess_expression_type(GDCompletionContext& context,const GDParser:: r_type.type=pi.type; if (pi.hint==PROPERTY_HINT_RESOURCE_TYPE) { - r_type.GDCLASS=pi.hint_string; + r_type.obj_type=pi.hint_string; } @@ -755,7 +755,7 @@ static bool _guess_expression_type(GDCompletionContext& context,const GDParser:: MethodInfo mi = E->get(); r_type.type=mi.return_val.type; if (mi.return_val.hint==PROPERTY_HINT_RESOURCE_TYPE) { - r_type.GDCLASS=mi.return_val.hint_string; + r_type.obj_type=mi.return_val.hint_string; } return true; } @@ -940,6 +940,15 @@ static bool _guess_expression_type(GDCompletionContext& context,const GDParser:: 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; @@ -1064,11 +1073,11 @@ static bool _guess_identifier_type(GDCompletionContext& context,int p_line,const if (argindex!=-1) { GDCompletionIdentifier id =_get_native_class(context); - if (id.type==Variant::OBJECT && id.GDCLASS!=StringName()) { + if (id.type==Variant::OBJECT && id.obj_type!=StringName()) { //this kinda sucks but meh List vmethods; - ClassDB::get_virtual_methods(id.GDCLASS,&vmethods); + ClassDB::get_virtual_methods(id.obj_type,&vmethods); for (List::Element *E=vmethods.front();E;E=E->next()) { @@ -1081,14 +1090,14 @@ static bool _guess_identifier_type(GDCompletionContext& context,int p_line,const r_type.type=Variant::OBJECT; - r_type.GDCLASS=arg.name.substr(scp+1,arg.name.length()); + r_type.obj_type=arg.name.substr(scp+1,arg.name.length()); return true; } else { r_type.type=arg.type; if (arg.hint==PROPERTY_HINT_RESOURCE_TYPE) - r_type.GDCLASS=arg.hint_string; + r_type.obj_type=arg.hint_string; return true; } } @@ -1174,7 +1183,7 @@ static bool _guess_identifier_type(GDCompletionContext& context,int p_line,const scr = ResourceLoader::load(script); - r_type.GDCLASS="Node"; + r_type.obj_type="Node"; r_type.type=Variant::OBJECT; r_type.script=scr; r_type.value=Variant(); @@ -1298,26 +1307,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 constants; ClassDB::get_integer_constant_list(type,&constants); for(List::Element *E=constants.front();E;E=E->next()) { result.insert(E->get()); } - List methods; - ClassDB::get_method_list(type,&methods); - for(List::Element *E=methods.front();E;E=E->next()) { - if (E->get().name.begins_with("_")) + List pinfo; + + ClassDB::get_property_list(type,&pinfo); + + for (List::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 methods; + ClassDB::get_method_list(type,&methods); + for(List::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; @@ -1483,10 +1509,10 @@ static void _find_type_arguments(GDCompletionContext& context,const GDParser::No } - } else if (id.type==Variant::OBJECT && id.GDCLASS!=StringName()) { + } else if (id.type==Variant::OBJECT && id.obj_type!=StringName()) { - MethodBind *m = ClassDB::get_method(id.GDCLASS,p_method); + MethodBind *m = ClassDB::get_method(id.obj_type,p_method); if (!m) { //not in static method, see script @@ -1699,7 +1725,7 @@ static void _find_type_arguments(GDCompletionContext& context,const GDParser::No if (p_argidx==0) { List sigs; - ClassDB::get_signal_list(id.GDCLASS,&sigs); + ClassDB::get_signal_list(id.obj_type,&sigs); if (id.script.is_valid()) { id.script->get_script_signal_list(&sigs); @@ -1735,7 +1761,7 @@ static void _find_type_arguments(GDCompletionContext& context,const GDParser::No }*/ } else { - if (p_argidx==0 && (String(p_method)=="get_node" || String(p_method)=="has_node") && ClassDB::is_parent_class(id.GDCLASS,"Node")) { + if (p_argidx==0 && (String(p_method)=="get_node" || String(p_method)=="has_node") && ClassDB::is_parent_class(id.obj_type,"Node")) { List props; Globals::get_singleton()->get_property_list(&props); @@ -1962,7 +1988,7 @@ static void _find_call_arguments(GDCompletionContext& context,const GDParser::No GDCompletionIdentifier ci; ci.type=Variant::OBJECT; - ci.GDCLASS=nc->get_name(); + ci.obj_type=nc->get_name(); if (!context._class->owner) ci.value=context.base; @@ -2143,7 +2169,7 @@ Error GDScriptLanguage::complete_code(const String& p_code, const String& p_base GDCompletionIdentifier t; if (_guess_expression_type(context,static_cast(node)->arguments[0],p.get_completion_line(),t)) { - if (t.type==Variant::OBJECT && t.GDCLASS=="GDNativeClass") { + if (t.type==Variant::OBJECT && t.obj_type=="GDNativeClass") { //native enum Ref gdn = t.value; if (gdn.is_valid()) { @@ -2153,8 +2179,19 @@ Error GDScriptLanguage::complete_code(const String& p_code, const String& p_base for (List::Element *E=cnames.front();E;E=E->next()) { options.insert(E->get()); } + + List pinfo; + ClassDB::get_property_list(cn,&pinfo); + + for (List::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.GDCLASS!=StringName()) { + } else if (t.type==Variant::OBJECT && t.obj_type!=StringName()) { Ref on_script; @@ -2288,10 +2325,23 @@ Error GDScriptLanguage::complete_code(const String& p_code, const String& p_base if (!isfunction) { - ClassDB::get_integer_constant_list(t.GDCLASS,r_options); + ClassDB::get_integer_constant_list(t.obj_type,r_options); + + List pinfo; + ClassDB::get_property_list(t.obj_type,&pinfo); + + for (List::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 mi; - ClassDB::get_method_list(t.GDCLASS,&mi); + ClassDB::get_method_list(t.obj_type,&mi); for (List::Element *E=mi.front();E;E=E->next()) { if (E->get().name.begins_with("_")) @@ -2395,9 +2445,9 @@ Error GDScriptLanguage::complete_code(const String& p_code, const String& p_base GDCompletionIdentifier cid = _get_native_class(context); - if (cid.GDCLASS!=StringName()) { + if (cid.obj_type!=StringName()) { List vm; - ClassDB::get_virtual_methods(cid.GDCLASS,&vm); + ClassDB::get_virtual_methods(cid.obj_type,&vm); for(List::Element *E=vm.front();E;E=E->next()) { MethodInfo &mi=E->get(); @@ -2430,10 +2480,10 @@ Error GDScriptLanguage::complete_code(const String& p_code, const String& p_base if (!_guess_expression_type(context,node,p.get_completion_line(),t)) break; - if (t.type==Variant::OBJECT && t.GDCLASS!=StringName()) { + if (t.type==Variant::OBJECT && t.obj_type!=StringName()) { List sigs; - ClassDB::get_signal_list(t.GDCLASS,&sigs); + ClassDB::get_signal_list(t.obj_type,&sigs); for (List::Element *E=sigs.front();E;E=E->next()) { options.insert("\""+E->get().name+"\""); } @@ -2610,12 +2660,12 @@ Error GDScriptLanguage::lookup_code(const String& p_code, const String& p_symbol } GDCompletionIdentifier identifier = _get_native_class(context); - print_line("identifier: "+String(identifier.GDCLASS)); + print_line("identifier: "+String(identifier.obj_type)); - if (ClassDB::has_method(identifier.GDCLASS,p_symbol)) { + if (ClassDB::has_method(identifier.obj_type,p_symbol)) { r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS_METHOD; - r_result.class_name=identifier.GDCLASS; + r_result.class_name=identifier.obj_type; r_result.class_member=p_symbol; return OK; } @@ -2653,16 +2703,29 @@ Error GDScriptLanguage::lookup_code(const String& p_code, const String& p_symbol GDCompletionIdentifier identifier = _get_native_class(context); - if (ClassDB::has_method(identifier.GDCLASS,p_symbol)) { + if (ClassDB::has_method(identifier.obj_type,p_symbol)) { r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS_METHOD; - r_result.class_name=identifier.GDCLASS; + 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) { @@ -2799,7 +2862,7 @@ Error GDScriptLanguage::lookup_code(const String& p_code, const String& p_symbol if (identifier.script.is_valid()) { print_line("var script: "+identifier.script->get_path()); } - print_line("obj type: "+String(identifier.GDCLASS)); + print_line("obj type: "+String(identifier.obj_type)); print_line("value: "+String(identifier.value)); } #endif @@ -2823,7 +2886,7 @@ Error GDScriptLanguage::lookup_code(const String& p_code, const String& p_symbol GDCompletionIdentifier t; if (_guess_expression_type(context,static_cast(node)->arguments[0],p.get_completion_line(),t)) { - if (t.type==Variant::OBJECT && t.GDCLASS=="GDNativeClass") { + if (t.type==Variant::OBJECT && t.obj_type=="GDNativeClass") { //native enum Ref gdn = t.value; if (gdn.is_valid()) { @@ -2833,7 +2896,7 @@ Error GDScriptLanguage::lookup_code(const String& p_code, const String& p_symbol return OK; } - } else if (t.type==Variant::OBJECT && t.GDCLASS!=StringName()) { + } else if (t.type==Variant::OBJECT && t.obj_type!=StringName()) { Ref on_script; @@ -2858,29 +2921,30 @@ Error GDScriptLanguage::lookup_code(const String& p_code, const String& p_symbol } } - if (ClassDB::has_method(t.GDCLASS,p_symbol)) { + if (ClassDB::has_method(t.obj_type,p_symbol)) { r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS_METHOD; - r_result.class_name=t.GDCLASS; + r_result.class_name=t.obj_type; r_result.class_member=p_symbol; return OK; } bool success; - ClassDB::get_integer_constant(t.GDCLASS,p_symbol,&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.GDCLASS; + r_result.class_name=t.obj_type; r_result.class_member=p_symbol; return OK; } - ClassDB::get_property_type(t.GDCLASS,p_symbol,&success); + + 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.GDCLASS; + r_result.class_name=t.obj_type; r_result.class_member=p_symbol; return OK; } @@ -2934,15 +2998,15 @@ Error GDScriptLanguage::lookup_code(const String& p_code, const String& p_symbol GDCompletionIdentifier cid = _get_native_class(context); - if (cid.GDCLASS!=StringName()) { + if (cid.obj_type!=StringName()) { List vm; - ClassDB::get_virtual_methods(cid.GDCLASS,&vm); + ClassDB::get_virtual_methods(cid.obj_type,&vm); for(List::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.GDCLASS; + r_result.class_name=cid.obj_type; r_result.class_member=p_symbol; return OK; diff --git a/modules/gdscript/gd_function.cpp b/modules/gdscript/gd_function.cpp index 2bacb50d862..e3217e92185 100644 --- a/modules/gdscript/gd_function.cpp +++ b/modules/gdscript/gd_function.cpp @@ -487,7 +487,7 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a case OPCODE_GET_NAMED: { - CHECK_SPACE(3); + CHECK_SPACE(4); GET_VARIANT_PTR(src,1); GET_VARIANT_PTR(dst,3); @@ -519,6 +519,46 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a #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); diff --git a/modules/gdscript/gd_function.h b/modules/gdscript/gd_function.h index 976dbb77786..e5262e8ad7f 100644 --- a/modules/gdscript/gd_function.h +++ b/modules/gdscript/gd_function.h @@ -23,6 +23,8 @@ public: OPCODE_GET, OPCODE_SET_NAMED, OPCODE_GET_NAMED, + OPCODE_SET_MEMBER, + OPCODE_GET_MEMBER, OPCODE_ASSIGN, OPCODE_ASSIGN_TRUE, OPCODE_ASSIGN_FALSE, diff --git a/tools/editor/plugins/script_text_editor.cpp b/tools/editor/plugins/script_text_editor.cpp index 63a859a2e44..56e64fccda0 100644 --- a/tools/editor/plugins/script_text_editor.cpp +++ b/tools/editor/plugins/script_text_editor.cpp @@ -1363,8 +1363,12 @@ void ScriptTextEditor::register_editor() { ED_SHORTCUT("script_text_editor/select_all", TTR("Select All"), KEY_MASK_CMD|KEY_A); ED_SHORTCUT("script_text_editor/move_up", TTR("Move Up"), KEY_MASK_ALT|KEY_UP); ED_SHORTCUT("script_text_editor/move_down", TTR("Move Down"), KEY_MASK_ALT|KEY_DOWN); - ED_SHORTCUT("script_text_editor/indent_left", TTR("Indent Left"), KEY_MASK_ALT|KEY_LEFT); - ED_SHORTCUT("script_text_editor/indent_right", TTR("Indent Right"), KEY_MASK_ALT|KEY_RIGHT); + + //leave these at zero, same can be accomplished with tab/shift-tab, including selection + //the next/previous in history shortcut in this case makes a lot more sene. + + ED_SHORTCUT("script_text_editor/indent_left", TTR("Indent Left"), 0); + ED_SHORTCUT("script_text_editor/indent_right", TTR("Indent Right"), 0); ED_SHORTCUT("script_text_editor/toggle_comment", TTR("Toggle Comment"), KEY_MASK_CMD|KEY_K); ED_SHORTCUT("script_text_editor/clone_down", TTR("Clone Down"), KEY_MASK_CMD|KEY_B); #ifdef OSX_ENABLED