diff options
Diffstat (limited to 'modules/gdscript')
| -rw-r--r-- | modules/gdscript/SCsub | 2 | ||||
| -rw-r--r-- | modules/gdscript/doc_classes/GDScript.xml | 2 | ||||
| -rw-r--r-- | modules/gdscript/editor/gdscript_highlighter.cpp | 30 | ||||
| -rw-r--r-- | modules/gdscript/editor/gdscript_highlighter.h | 4 | ||||
| -rw-r--r-- | modules/gdscript/gdscript.cpp | 302 | ||||
| -rw-r--r-- | modules/gdscript/gdscript.h | 29 | ||||
| -rw-r--r-- | modules/gdscript/gdscript_compiler.cpp | 85 | ||||
| -rw-r--r-- | modules/gdscript/gdscript_compiler.h | 4 | ||||
| -rw-r--r-- | modules/gdscript/gdscript_editor.cpp | 170 | ||||
| -rw-r--r-- | modules/gdscript/gdscript_function.cpp | 167 | ||||
| -rw-r--r-- | modules/gdscript/gdscript_function.h | 45 | ||||
| -rw-r--r-- | modules/gdscript/gdscript_functions.cpp | 92 | ||||
| -rw-r--r-- | modules/gdscript/gdscript_functions.h | 9 | ||||
| -rw-r--r-- | modules/gdscript/gdscript_parser.cpp | 547 | ||||
| -rw-r--r-- | modules/gdscript/gdscript_parser.h | 25 | ||||
| -rw-r--r-- | modules/gdscript/gdscript_tokenizer.cpp | 41 | ||||
| -rw-r--r-- | modules/gdscript/gdscript_tokenizer.h | 26 | ||||
| -rw-r--r-- | modules/gdscript/register_types.cpp | 94 | ||||
| -rw-r--r-- | modules/gdscript/register_types.h | 4 |
19 files changed, 1030 insertions, 648 deletions
diff --git a/modules/gdscript/SCsub b/modules/gdscript/SCsub index 73f09f1659..6904154953 100644 --- a/modules/gdscript/SCsub +++ b/modules/gdscript/SCsub @@ -9,5 +9,3 @@ env_gdscript.add_source_files(env.modules_sources, "*.cpp") if env['tools']: env_gdscript.add_source_files(env.modules_sources, "./editor/*.cpp") - -Export('env') diff --git a/modules/gdscript/doc_classes/GDScript.xml b/modules/gdscript/doc_classes/GDScript.xml index 632970f8c0..4cefdbd7cb 100644 --- a/modules/gdscript/doc_classes/GDScript.xml +++ b/modules/gdscript/doc_classes/GDScript.xml @@ -8,7 +8,7 @@ [method new] creates a new instance of the script. [method Object.set_script] extends an existing object, if that object's class matches one of the script's base classes. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/getting_started/scripting/gdscript/index.html</link> + <link>https://docs.godotengine.org/en/latest/getting_started/scripting/gdscript/index.html</link> </tutorials> <demos> </demos> diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp index f2a1a5b50c..4f59b06ae6 100644 --- a/modules/gdscript/editor/gdscript_highlighter.cpp +++ b/modules/gdscript/editor/gdscript_highlighter.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -43,10 +43,6 @@ static bool _is_text_char(CharType c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_'; } -static bool _is_whitespace(CharType c) { - return c == '\t' || c == ' '; -} - static bool _is_char(CharType c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'; @@ -76,6 +72,7 @@ Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_ bool in_word = false; bool in_function_name = false; bool in_variable_declaration = false; + bool in_function_args = false; bool in_member_variable = false; bool in_node_path = false; bool is_hex_notation = false; @@ -121,8 +118,8 @@ Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_ is_hex_notation = false; } - // check for dot or underscore or 'x' for hex notation in floating point number - if ((str[j] == '.' || str[j] == 'x' || str[j] == '_') && !in_word && prev_is_number && !is_number) { + // check for dot or underscore or 'x' for hex notation in floating point number or 'e' for scientific notation + if ((str[j] == '.' || str[j] == 'x' || str[j] == '_' || str[j] == 'e') && !in_word && prev_is_number && !is_number) { is_number = true; is_symbol = false; is_char = false; @@ -224,17 +221,24 @@ Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_ } if (is_symbol) { - in_function_name = false; - in_member_variable = false; - if (expect_type && str[j] != ' ' && str[j] != '\t' && str[j] != ':') { + if (in_function_name) { + in_function_args = true; + } + + if (in_function_args && str[j] == ')') { + in_function_args = false; + } + + if (expect_type && prev_is_char) { expect_type = false; } + if (j > 0 && str[j] == '>' && str[j - 1] == '-') { expect_type = true; } - if (in_variable_declaration || previous_text == "(" || previous_text == ",") { + if (in_variable_declaration || in_function_args) { int k = j; // Skip space while (k < str.length() && (str[k] == '\t' || str[k] == ' ')) { @@ -248,6 +252,8 @@ Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_ } in_variable_declaration = false; + in_function_name = false; + in_member_variable = false; } if (!in_node_path && in_region == -1 && str[j] == '$') { diff --git a/modules/gdscript/editor/gdscript_highlighter.h b/modules/gdscript/editor/gdscript_highlighter.h index b8cb4a65e9..9dc10a5d1b 100644 --- a/modules/gdscript/editor/gdscript_highlighter.h +++ b/modules/gdscript/editor/gdscript_highlighter.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 33d9c011f2..670aabc34c 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,13 +30,13 @@ #include "gdscript.h" -#include "engine.h" +#include "core/engine.h" +#include "core/global_constants.h" +#include "core/io/file_access_encrypted.h" +#include "core/os/file_access.h" +#include "core/os/os.h" +#include "core/project_settings.h" #include "gdscript_compiler.h" -#include "global_constants.h" -#include "io/file_access_encrypted.h" -#include "os/file_access.h" -#include "os/os.h" -#include "project_settings.h" /////////////////////////// @@ -126,10 +126,7 @@ GDScriptInstance *GDScript::_create_instance(const Variant **p_args, int p_argco GDScriptLanguage::singleton->lock->unlock(); #endif - if (r_error.error != Variant::CallError::CALL_OK) { - memdelete(instance); - ERR_FAIL_COND_V(r_error.error != Variant::CallError::CALL_OK, NULL); //error constructing - } + ERR_FAIL_COND_V(r_error.error != Variant::CallError::CALL_OK, NULL); //error constructing } //@TODO make thread safe @@ -155,12 +152,13 @@ Variant GDScript::_new(const Variant **p_args, int p_argcount, Variant::CallErro } ERR_FAIL_COND_V(_baseptr->native.is_null(), Variant()); - if (_baseptr->native.ptr()) { owner = _baseptr->native->instance(); } else { owner = memnew(Reference); //by default, no base means use reference } + ERR_EXPLAIN("Can't inherit from a virtual class"); + ERR_FAIL_COND_V(!owner, Variant()); Reference *r = Object::cast_to<Reference>(owner); if (r) { @@ -226,16 +224,21 @@ void GDScript::_placeholder_erased(PlaceHolderScriptInstance *p_placeholder) { void GDScript::get_script_method_list(List<MethodInfo> *p_list) const { - for (const Map<StringName, GDScriptFunction *>::Element *E = member_functions.front(); E; E = E->next()) { - GDScriptFunction *func = E->get(); - MethodInfo mi; - mi.name = E->key(); - for (int i = 0; i < func->get_argument_count(); i++) { - mi.arguments.push_back(func->get_argument_type(i)); + const GDScript *current = this; + while (current) { + for (const Map<StringName, GDScriptFunction *>::Element *E = member_functions.front(); E; E = E->next()) { + GDScriptFunction *func = E->get(); + MethodInfo mi; + mi.name = E->key(); + for (int i = 0; i < func->get_argument_count(); i++) { + mi.arguments.push_back(func->get_argument_type(i)); + } + + mi.return_val = func->get_return_type(); + p_list->push_back(mi); } - mi.return_val = func->get_return_type(); - p_list->push_back(mi); + current = current->_base; } } @@ -424,31 +427,40 @@ bool GDScript::_update_exports() { base_cache = Ref<GDScript>(); } - if (c->extends_used && String(c->extends_file) != "" && String(c->extends_file) != get_path()) { + if (c->extends_used) { + String path = ""; + if (String(c->extends_file) != "" && String(c->extends_file) != get_path()) { + path = c->extends_file; + if (path.is_rel_path()) { - String path = c->extends_file; - if (path.is_rel_path()) { + String base = get_path(); + if (base == "" || base.is_rel_path()) { - String base = get_path(); - if (base == "" || base.is_rel_path()) { - - ERR_PRINT(("Could not resolve relative path for parent class: " + path).utf8().get_data()); - } else { - path = base.get_base_dir().plus_file(path); + ERR_PRINT(("Could not resolve relative path for parent class: " + path).utf8().get_data()); + } else { + path = base.get_base_dir().plus_file(path); + } } + } else if (c->extends_class.size() != 0) { + String base = c->extends_class[0]; + + if (ScriptServer::is_global_class(base)) + path = ScriptServer::get_global_class_path(base); } - if (path != get_path()) { + if (path != "") { + if (path != get_path()) { - Ref<GDScript> bf = ResourceLoader::load(path); + Ref<GDScript> bf = ResourceLoader::load(path); - if (bf.is_valid()) { + if (bf.is_valid()) { - base_cache = bf; - bf->inheriters_cache.insert(get_instance_id()); + base_cache = bf; + bf->inheriters_cache.insert(get_instance_id()); + } + } else { + ERR_PRINT(("Path extending itself in " + path).utf8().get_data()); } - } else { - ERR_PRINT(("Path extending itself in " + path).utf8().get_data()); } } @@ -469,13 +481,15 @@ bool GDScript::_update_exports() { _signals[c->_signals[i].name] = c->_signals[i].arguments; } } else { - for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) { - E->get()->set_build_failed(true); - } + placeholder_fallback_enabled = true; + return false; } - } else { + } else if (placeholder_fallback_enabled) { + return false; } + placeholder_fallback_enabled = false; + if (base_cache.is_valid()) { if (base_cache->_update_exports()) { changed = true; @@ -490,7 +504,6 @@ bool GDScript::_update_exports() { _update_exports_values(values, propnames); for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) { - E->get()->set_build_failed(false); E->get()->update(propnames, values); } } @@ -635,7 +648,8 @@ Variant GDScript::call(const StringName &p_method, const Variant **p_args, int p if (E) { if (!E->get()->is_static()) { - WARN_PRINT(String("Can't call non-static function: '" + String(p_method) + "' in script.").utf8().get_data()); + ERR_EXPLAIN("Can't call non-static function: '" + String(p_method) + "' in script."); + ERR_FAIL_V(Variant()); } return E->get()->call(NULL, p_args, p_argcount, r_error); @@ -854,7 +868,6 @@ bool GDScript::has_script_signal(const StringName &p_signal) const { else if (base_cache.is_valid()) { return base_cache->has_script_signal(p_signal); } - #endif return false; } @@ -895,6 +908,7 @@ GDScript::GDScript() : tool = false; #ifdef TOOLS_ENABLED source_changed_cache = false; + placeholder_fallback_enabled = false; #endif #ifdef DEBUG_ENABLED @@ -1095,12 +1109,12 @@ void GDScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const //instance a fake script for editing the values Vector<_GDScriptMemberSort> msort; - for (Map<StringName, PropertyInfo>::Element *E = sptr->member_info.front(); E; E = E->next()) { + for (Map<StringName, PropertyInfo>::Element *F = sptr->member_info.front(); F; F = F->next()) { _GDScriptMemberSort ms; - ERR_CONTINUE(!sptr->member_indices.has(E->key())); - ms.index = sptr->member_indices[E->key()].index; - ms.name = E->key(); + ERR_CONTINUE(!sptr->member_indices.has(F->key())); + ms.index = sptr->member_indices[F->key()].index; + ms.name = F->key(); msort.push_back(ms); } @@ -1663,6 +1677,8 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so //restore state if saved for (Map<ObjectID, List<Pair<StringName, Variant> > >::Element *F = E->get().front(); F; F = F->next()) { + List<Pair<StringName, Variant> > &saved_state = F->get(); + Object *obj = ObjectDB::get_instance(F->key()); if (!obj) continue; @@ -1672,16 +1688,26 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so obj->set_script(RefPtr()); } obj->set_script(scr.get_ref_ptr()); - if (!obj->get_script_instance()) { + + ScriptInstance *script_instance = obj->get_script_instance(); + + if (!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(); + scr->pending_reload_state[obj->get_instance_id()] = saved_state; } 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); + if (script_instance->is_placeholder() && scr->is_placeholder_fallback_enabled()) { + PlaceHolderScriptInstance *placeholder = static_cast<PlaceHolderScriptInstance *>(script_instance); + for (List<Pair<StringName, Variant> >::Element *G = saved_state.front(); G; G = G->next()) { + placeholder->property_set_fallback(G->get().first, G->get().second); + } + } else { + for (List<Pair<StringName, Variant> >::Element *G = saved_state.front(); G; G = G->next()) { + script_instance->set(G->get().first, G->get().second); + } } scr->pending_reload_state.erase(obj->get_instance_id()); //as it reloaded, remove pending state @@ -1779,10 +1805,11 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const { "remote", "sync", "master", + "puppet", "slave", "remotesync", "mastersync", - "slavesync", + "puppetsync", 0 }; @@ -1808,73 +1835,92 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b PoolVector<uint8_t> sourcef; Error err; - FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); + FileAccessRef f = FileAccess::open(p_path, FileAccess::READ, &err); if (err) { return String(); } - int len = f->get_len(); - sourcef.resize(len + 1); - PoolVector<uint8_t>::Write w = sourcef.write(); - int r = f->get_buffer(w.ptr(), len); - f->close(); - memdelete(f); - ERR_FAIL_COND_V(r != len, String()); - w[len] = 0; - - String s; - if (s.parse_utf8((const char *)w.ptr())) { - return String(); - } + String source = f->get_as_utf8_string(); GDScriptParser parser; - - parser.parse(s, p_path.get_base_dir(), true, p_path); + parser.parse(source, p_path.get_base_dir(), true, p_path, false, NULL, true); if (parser.get_parse_tree() && parser.get_parse_tree()->type == GDScriptParser::Node::TYPE_CLASS) { const GDScriptParser::ClassNode *c = static_cast<const GDScriptParser::ClassNode *>(parser.get_parse_tree()); + if (r_icon_path) { + if (c->icon_path.empty() || c->icon_path.is_abs_path()) + *r_icon_path = c->icon_path; + else if (c->icon_path.is_rel_path()) + *r_icon_path = p_path.get_base_dir().plus_file(c->icon_path).simplify_path(); + } if (r_base_type) { - GDScriptParser::DataType base_type; - if (c->base_type.has_type) { - base_type = c->base_type; - while (base_type.has_type && base_type.kind != GDScriptParser::DataType::NATIVE) { - switch (base_type.kind) { - case GDScriptParser::DataType::CLASS: { - base_type = base_type.class_type->base_type; - } break; - case GDScriptParser::DataType::GDSCRIPT: { - Ref<GDScript> gds = base_type.script_type; - if (gds.is_valid()) { - base_type.kind = GDScriptParser::DataType::NATIVE; - base_type.native_type = gds->get_instance_base_type(); - } else { - base_type = GDScriptParser::DataType(); + + const GDScriptParser::ClassNode *subclass = c; + String path = p_path; + GDScriptParser subparser; + while (subclass) { + if (subclass->extends_used) { + if (subclass->extends_file) { + if (subclass->extends_class.size() == 0) { + get_global_class_name(subclass->extends_file, r_base_type); + subclass = NULL; + break; + } else { + Vector<StringName> extend_classes = subclass->extends_class; + + FileAccessRef subfile = FileAccess::open(subclass->extends_file, FileAccess::READ); + if (!subfile) { + break; + } + String subsource = subfile->get_as_utf8_string(); + + if (subsource.empty()) { + break; + } + String subpath = subclass->extends_file; + if (subpath.is_rel_path()) { + subpath = path.get_base_dir().plus_file(subpath).simplify_path(); + } + + if (OK != subparser.parse(subsource, subpath.get_base_dir(), true, subpath, false, NULL, true)) { + break; } - } break; - default: { - base_type = GDScriptParser::DataType(); - } break; + path = subpath; + if (!subparser.get_parse_tree() || subparser.get_parse_tree()->type != GDScriptParser::Node::TYPE_CLASS) { + break; + } + subclass = static_cast<const GDScriptParser::ClassNode *>(subparser.get_parse_tree()); + + while (extend_classes.size() > 0) { + bool found = false; + for (int i = 0; i < subclass->subclasses.size(); i++) { + const GDScriptParser::ClassNode *inner_class = subclass->subclasses[i]; + if (inner_class->name == extend_classes[0]) { + extend_classes.remove(0); + found = true; + subclass = inner_class; + break; + } + } + if (!found) { + subclass = NULL; + break; + } + } + } + } else if (subclass->extends_class.size() == 1) { + *r_base_type = subclass->extends_class[0]; + subclass = NULL; + } else { + break; } - } - } - if (base_type.has_type) { - *r_base_type = base_type.native_type; - } else { - // Fallback - if (c->extends_used && c->extends_class.size() == 1) { - *r_base_type = c->extends_class[0]; - } else if (!c->extends_used) { + } else { *r_base_type = "Reference"; + subclass = NULL; } } } - if (r_icon_path) { - if (c->icon_path.empty() || c->icon_path.is_abs_path()) - *r_icon_path = c->icon_path; - else if (c->icon_path.is_rel_path()) - *r_icon_path = p_path.get_base_dir().plus_file(c->icon_path).simplify_path(); - } return c->name; } @@ -1973,14 +2019,19 @@ String GDScriptWarning::get_message() const { } break; case UNSAFE_CAST: { CHECK_SYMBOLS(1); - return "The value is cast to '" + symbols[0] + "' but has an unkown type."; + return "The value is cast to '" + symbols[0] + "' but has an unknown type."; } break; case UNSAFE_CALL_ARGUMENT: { CHECK_SYMBOLS(4); return "The argument '" + symbols[0] + "' of the function '" + symbols[1] + "' requires a the subtype '" + symbols[2] + "' but the supertype '" + symbols[3] + "' was provided"; } break; + case DEPRECATED_KEYWORD: { + CHECK_SYMBOLS(2); + return "The '" + symbols[0] + "' keyword is deprecated and will be removed in a future release, please replace its uses by '" + symbols[1] + "'."; + } break; + case WARNING_MAX: break; // Can't happen, but silences warning } - ERR_EXPLAIN("Invalid GDScript waring code: " + get_name_from_code(code)); + ERR_EXPLAIN("Invalid GDScript warning code: " + get_name_from_code(code)); ERR_FAIL_V(String()); #undef CHECK_SYMBOLS @@ -2018,6 +2069,7 @@ String GDScriptWarning::get_name_from_code(Code p_code) { "UNSAFE_METHOD_ACCESS", "UNSAFE_CAST", "UNSAFE_CALL_ARGUMENT", + "DEPRECATED_KEYWORD", NULL }; @@ -2031,7 +2083,7 @@ GDScriptWarning::Code GDScriptWarning::get_code_from_name(const String &p_name) } } - ERR_EXPLAIN("Invalid GDScript waring name: " + p_name); + ERR_EXPLAIN("Invalid GDScript warning name: " + p_name); ERR_FAIL_V(WARNING_MAX); } @@ -2061,12 +2113,12 @@ GDScriptLanguage::GDScriptLanguage() { _debug_call_stack_pos = 0; int dmcs = GLOBAL_DEF("debug/settings/gdscript/max_call_stack", 1024); + ProjectSettings::get_singleton()->set_custom_property_info("debug/settings/gdscript/max_call_stack", PropertyInfo(Variant::INT, "debug/settings/gdscript/max_call_stack", PROPERTY_HINT_RANGE, "1024,4096,1,or_greater")); //minimum is 1024 + if (ScriptDebugger::get_singleton()) { //debugging enabled! _debug_max_call_stack = dmcs; - if (_debug_max_call_stack < 1024) - _debug_max_call_stack = 1024; _call_stack = memnew_arr(CallLevel, _debug_max_call_stack + 1); } else { @@ -2077,6 +2129,7 @@ GDScriptLanguage::GDScriptLanguage() { #ifdef DEBUG_ENABLED GLOBAL_DEF("debug/gdscript/warnings/enable", true); GLOBAL_DEF("debug/gdscript/warnings/treat_warnings_as_errors", false); + GLOBAL_DEF("debug/gdscript/completion/autocomplete_setters_and_getters", false); for (int i = 0; i < (int)GDScriptWarning::WARNING_MAX; i++) { String warning = GDScriptWarning::get_name_from_code((GDScriptWarning::Code)i).to_lower(); GLOBAL_DEF("debug/gdscript/warnings/" + warning, !warning.begins_with("unsafe_")); @@ -2112,23 +2165,14 @@ RES ResourceFormatLoaderGDScript::load(const String &p_path, const String &p_ori script->set_script_path(p_original_path); // script needs this. script->set_path(p_original_path); Error err = script->load_byte_code(p_path); - - if (err != OK) { - memdelete(script); - ERR_FAIL_COND_V(err != OK, RES()); - } + ERR_FAIL_COND_V(err != OK, RES()); } else { Error err = script->load_source_code(p_path); - - if (err != OK) { - memdelete(script); - ERR_FAIL_COND_V(err != OK, RES()); - } + ERR_FAIL_COND_V(err != OK, RES()); script->set_script_path(p_original_path); // script needs this. script->set_path(p_original_path); - //script->set_name(p_path.get_file()); script->reload(); } @@ -2158,6 +2202,26 @@ String ResourceFormatLoaderGDScript::get_resource_type(const String &p_path) con return ""; } +void ResourceFormatLoaderGDScript::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) { + + FileAccessRef file = FileAccess::open(p_path, FileAccess::READ); + ERR_FAIL_COND(!file); + + String source = file->get_as_utf8_string(); + if (source.empty()) { + return; + } + + GDScriptParser parser; + if (OK != parser.parse(source, p_path.get_base_dir(), true, p_path, false, NULL, true)) { + return; + } + + for (const List<String>::Element *E = parser.get_dependencies().front(); E; E = E->next()) { + p_dependencies->push_back(E->get()); + } +} + Error ResourceFormatSaverGDScript::save(const String &p_path, const RES &p_resource, uint32_t p_flags) { Ref<GDScript> sqscr = p_resource; diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index d400230f43..ded873c7d3 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,10 +31,10 @@ #ifndef GDSCRIPT_H #define GDSCRIPT_H +#include "core/io/resource_loader.h" +#include "core/io/resource_saver.h" +#include "core/script_language.h" #include "gdscript_function.h" -#include "io/resource_loader.h" -#include "io/resource_saver.h" -#include "script_language.h" class GDScriptNativeClass : public Reference { @@ -97,6 +97,7 @@ class GDScript : public Script { Ref<GDScript> base_cache; Set<ObjectID> inheriters_cache; bool source_changed_cache; + bool placeholder_fallback_enabled; void _update_exports_values(Map<StringName, Variant> &values, List<PropertyInfo> &propnames); #endif @@ -141,13 +142,13 @@ protected: static void _bind_methods(); public: - bool is_valid() const { return valid; } + virtual bool is_valid() const { return valid; } const Map<StringName, Ref<GDScript> > &get_subclasses() const { return subclasses; } const Map<StringName, Variant> &get_constants() const { return constants; } const Set<StringName> &get_members() const { return members; } const GDScriptDataType &get_member_type(const StringName &p_member) const { - ERR_FAIL_COND_V(!member_indices.has(p_member), GDScriptDataType()); + CRASH_COND(!member_indices.has(p_member)); return member_indices[p_member].data_type; } const Map<StringName, GDScriptFunction *> &get_member_functions() const { return member_functions; } @@ -209,6 +210,10 @@ public: virtual void get_constants(Map<StringName, Variant> *p_constants); virtual void get_members(Set<StringName> *p_members); +#ifdef TOOLS_ENABLED + virtual bool is_placeholder_fallback_enabled() const { return placeholder_fallback_enabled; } +#endif + GDScript(); ~GDScript(); }; @@ -286,9 +291,10 @@ struct GDScriptWarning { FUNCTION_USED_AS_PROPERTY, // Property not found, but there's a function with the same name INTEGER_DIVISION, // Integer divide by integer, decimal part is discarded UNSAFE_PROPERTY_ACCESS, // Property not found in the detected type (but can be in subtypes) - UNSAFE_METHOD_ACCESS, // Fucntion not found in the detected type (but can be in subtypes) + UNSAFE_METHOD_ACCESS, // Function not found in the detected type (but can be in subtypes) UNSAFE_CAST, // Cast used in an unknown type UNSAFE_CALL_ARGUMENT, // Function call argument is of a supertype of the require argument + DEPRECATED_KEYWORD, // The keyword is deprecated and should be replaced WARNING_MAX, } code; Vector<String> symbols; @@ -300,8 +306,8 @@ struct GDScriptWarning { static Code get_code_from_name(const String &p_name); GDScriptWarning() : - line(-1), - code(WARNING_MAX) {} + code(WARNING_MAX), + line(-1) {} }; #endif // DEBUG_ENABLED @@ -499,14 +505,17 @@ public: }; class ResourceFormatLoaderGDScript : public ResourceFormatLoader { + GDCLASS(ResourceFormatLoaderGDScript, ResourceFormatLoader) public: virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); virtual void get_recognized_extensions(List<String> *p_extensions) const; virtual bool handles_type(const String &p_type) const; virtual String get_resource_type(const String &p_path) const; + virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false); }; class ResourceFormatSaverGDScript : public ResourceFormatSaver { + GDCLASS(ResourceFormatSaverGDScript, ResourceFormatSaver) public: virtual Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0); virtual void get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const; diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 006fbece53..b2b7b1c260 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -132,7 +132,7 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D result.kind = GDScriptDataType::SCRIPT; result.script_type = p_datatype.script_type; result.native_type = result.script_type->get_instance_base_type(); - } + } break; case GDScriptParser::DataType::GDSCRIPT: { result.kind = GDScriptDataType::GDSCRIPT; result.script_type = p_datatype.script_type; @@ -388,7 +388,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: int ret = _parse_expression(codegen, an->elements[i], slevel); if (ret < 0) return ret; - if (ret & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) { + if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) { slevel++; codegen.alloc_stack(slevel); } @@ -419,7 +419,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: int ret = _parse_expression(codegen, dn->elements[i].key, slevel); if (ret < 0) return ret; - if (ret & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) { + if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) { slevel++; codegen.alloc_stack(slevel); } @@ -429,7 +429,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: ret = _parse_expression(codegen, dn->elements[i].value, slevel); if (ret < 0) return ret; - if (ret & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) { + if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) { slevel++; codegen.alloc_stack(slevel); } @@ -486,7 +486,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: script = codegen.script; } else { StringName name = cn->cast_type.class_type->name; - if (class_map[name] == codegen.script->subclasses[name]) { + if (codegen.script->subclasses.has(name) && class_map[name] == codegen.script->subclasses[name]) { idx = codegen.get_name_map_pos(name); idx |= GDScriptFunction::ADDR_TYPE_CLASS_CONSTANT << GDScriptFunction::ADDR_BITS; } else { @@ -518,7 +518,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: } } - codegen.opcodes.push_back(src_addr); // source adddress + codegen.opcodes.push_back(src_addr); // source address int dst_addr = (p_stack_level) | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS); codegen.opcodes.push_back(dst_addr); // append the stack level as destination address of the opcode codegen.alloc_stack(p_stack_level); @@ -545,7 +545,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: int ret = _parse_expression(codegen, on->arguments[i], slevel); if (ret < 0) return ret; - if (ret & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) { + if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) { slevel++; codegen.alloc_stack(slevel); } @@ -578,7 +578,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: int ret = _parse_expression(codegen, on->arguments[i], slevel); if (ret < 0) return ret; - if (ret & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) { + if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) { slevel++; codegen.alloc_stack(slevel); } @@ -606,7 +606,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: if (ret < 0) return ret; - if (ret & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) { + if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) { slevel++; codegen.alloc_stack(slevel); } @@ -655,7 +655,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: ret = _parse_expression(codegen, on->arguments[i], slevel); if (ret < 0) return ret; - if (ret & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) { + if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) { slevel++; codegen.alloc_stack(slevel); } @@ -681,7 +681,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: int ret = _parse_expression(codegen, on->arguments[i], slevel); if (ret < 0) return ret; - if (ret & (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS)) { + if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) { slevel++; codegen.alloc_stack(slevel); } @@ -1183,7 +1183,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: script = codegen.script; } else { StringName name = assign_type.class_type->name; - if (class_map[name] == codegen.script->subclasses[name]) { + if (codegen.script->subclasses.has(name) && class_map[name] == codegen.script->subclasses[name]) { idx = codegen.get_name_map_pos(name); idx |= GDScriptFunction::ADDR_TYPE_CLASS_CONSTANT << GDScriptFunction::ADDR_BITS; } else { @@ -1360,15 +1360,15 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo // jump unconditionally // continue address // compile the condition - int ret = _parse_expression(codegen, branch.compiled_pattern, p_stack_level); - if (ret < 0) { + int ret2 = _parse_expression(codegen, branch.compiled_pattern, p_stack_level); + if (ret2 < 0) { memdelete(id); memdelete(op); return ERR_PARSE_ERROR; } codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_IF); - codegen.opcodes.push_back(ret); + codegen.opcodes.push_back(ret2); codegen.opcodes.push_back(codegen.opcodes.size() + 3); int continue_addr = codegen.opcodes.size(); codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP); @@ -1401,12 +1401,12 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo 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) + int ret2 = _parse_expression(codegen, cf->arguments[0], p_stack_level, false); + if (ret2 < 0) return ERR_PARSE_ERROR; codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_IF_NOT); - codegen.opcodes.push_back(ret); + codegen.opcodes.push_back(ret2); int else_addr = codegen.opcodes.size(); codegen.opcodes.push_back(0); //temporary @@ -1421,9 +1421,9 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo codegen.opcodes.push_back(0); codegen.opcodes.write[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; + Error err2 = _parse_block(codegen, cf->body_else, p_stack_level, p_break_addr, p_continue_addr); + if (err2) + return err2; codegen.opcodes.write[end_addr] = codegen.opcodes.size(); } else { @@ -1444,14 +1444,14 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo codegen.push_stack_identifiers(); codegen.add_stack_identifier(static_cast<const GDScriptParser::IdentifierNode *>(cf->arguments[0])->name, iter_stack_pos); - int ret = _parse_expression(codegen, cf->arguments[1], slevel, false); - if (ret < 0) + int ret2 = _parse_expression(codegen, cf->arguments[1], slevel, false); + if (ret2 < 0) return ERR_COMPILATION_FAILED; //assign container codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN); codegen.opcodes.push_back(container_pos); - codegen.opcodes.push_back(ret); + codegen.opcodes.push_back(ret2); //begin loop codegen.opcodes.push_back(GDScriptFunction::OPCODE_ITERATE_BEGIN); @@ -1493,11 +1493,11 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo 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) + int ret2 = _parse_expression(codegen, cf->arguments[0], p_stack_level, false); + if (ret2 < 0) return ERR_PARSE_ERROR; codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_IF_NOT); - codegen.opcodes.push_back(ret); + codegen.opcodes.push_back(ret2); codegen.opcodes.push_back(break_addr); Error err = _parse_block(codegen, cf->body, p_stack_level, break_addr, continue_addr); if (err) @@ -1508,9 +1508,6 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo codegen.opcodes.write[break_addr + 1] = codegen.opcodes.size(); } break; - case GDScriptParser::ControlFlowNode::CF_SWITCH: { - - } break; case GDScriptParser::ControlFlowNode::CF_BREAK: { if (p_break_addr < 0) { @@ -1536,21 +1533,21 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo } break; case GDScriptParser::ControlFlowNode::CF_RETURN: { - int ret; + int ret2; if (cf->arguments.size()) { - ret = _parse_expression(codegen, cf->arguments[0], p_stack_level, false); - if (ret < 0) + ret2 = _parse_expression(codegen, cf->arguments[0], p_stack_level, false); + if (ret2 < 0) return ERR_PARSE_ERROR; } else { - ret = GDScriptFunction::ADDR_TYPE_NIL << GDScriptFunction::ADDR_BITS; + ret2 = GDScriptFunction::ADDR_TYPE_NIL << GDScriptFunction::ADDR_BITS; } codegen.opcodes.push_back(GDScriptFunction::OPCODE_RETURN); - codegen.opcodes.push_back(ret); + codegen.opcodes.push_back(ret2); } break; } @@ -1561,12 +1558,12 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo const GDScriptParser::AssertNode *as = static_cast<const GDScriptParser::AssertNode *>(s); - int ret = _parse_expression(codegen, as->condition, p_stack_level, false); - if (ret < 0) + int ret2 = _parse_expression(codegen, as->condition, p_stack_level, false); + if (ret2 < 0) return ERR_PARSE_ERROR; codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSERT); - codegen.opcodes.push_back(ret); + codegen.opcodes.push_back(ret2); #endif } break; case GDScriptParser::Node::TYPE_BREAKPOINT: { @@ -1593,8 +1590,8 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo } break; default: { //expression - int ret = _parse_expression(codegen, s, p_stack_level, true); - if (ret < 0) + int ret2 = _parse_expression(codegen, s, p_stack_level, true); + if (ret2 < 0) return ERR_PARSE_ERROR; } break; } @@ -2119,8 +2116,8 @@ Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptPa 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; + for (Map<StringName, GDScript::MemberInfo>::Element *F = p_script->member_indices.front(); F; F = F->next()) { + instance->member_indices_cache[F->key()] = F->get().index; } instance->owner->set_script_instance(instance); diff --git a/modules/gdscript/gdscript_compiler.h b/modules/gdscript/gdscript_compiler.h index 55f5e2b48e..8440807a56 100644 --- a/modules/gdscript/gdscript_compiler.h +++ b/modules/gdscript/gdscript_compiler.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index d3068fb6d0..fafc73b7e6 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,16 +31,13 @@ #include "gdscript.h" #include "core/engine.h" -#include "editor/editor_settings.h" +#include "core/global_constants.h" +#include "core/os/file_access.h" #include "gdscript_compiler.h" -#include "global_constants.h" -#include "os/file_access.h" #ifdef TOOLS_ENABLED -#include "core/reference.h" #include "editor/editor_file_system.h" #include "editor/editor_settings.h" -#include "engine.h" #endif void GDScriptLanguage::get_comment_delimiters(List<String> *p_delimiters) const { @@ -54,12 +51,6 @@ void GDScriptLanguage::get_string_delimiters(List<String> *p_delimiters) const { p_delimiters->push_back("\"\"\" \"\"\""); } Ref<Script> GDScriptLanguage::get_template(const String &p_class_name, const String &p_base_class_name) const { -#ifdef TOOLS_ENABLED - bool th = EDITOR_DEF("text_editor/completion/add_type_hints", false); -#else - bool th = false; -#endif - String _template = "extends %BASE%\n" "\n" "# Declare member variables here. Examples:\n" @@ -488,12 +479,15 @@ struct GDScriptCompletionContext { Object *base; String base_path; int line; + uint32_t depth; GDScriptCompletionContext() : _class(NULL), function(NULL), block(NULL), - base(NULL) {} + base(NULL), + line(0), + depth(0) {} }; struct GDScriptCompletionIdentifier { @@ -624,6 +618,9 @@ static GDScriptCompletionIdentifier _type_from_gdtype(const GDScriptDataType &p_ ci.type.script_type = p_gdtype.script_type; switch (p_gdtype.kind) { + case GDScriptDataType::UNINITIALIZED: { + ERR_EXPLAIN("Uninitialized completion. Please report a bug."); + } break; case GDScriptDataType::BUILTIN: { ci.type.kind = GDScriptParser::DataType::BUILTIN; } break; @@ -640,12 +637,18 @@ static GDScriptCompletionIdentifier _type_from_gdtype(const GDScriptDataType &p_ return ci; } -static bool _guess_identifier_type(const GDScriptCompletionContext &p_context, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type); -static bool _guess_identifier_type_from_base(const GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type); -static bool _guess_method_return_type_from_base(const GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, GDScriptCompletionIdentifier &r_type); +static bool _guess_identifier_type(GDScriptCompletionContext &p_context, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type); +static bool _guess_identifier_type_from_base(GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type); +static bool _guess_method_return_type_from_base(GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, GDScriptCompletionIdentifier &r_type); -static bool _guess_expression_type(const GDScriptCompletionContext &p_context, const GDScriptParser::Node *p_expression, GDScriptCompletionIdentifier &r_type) { +static bool _guess_expression_type(GDScriptCompletionContext &p_context, const GDScriptParser::Node *p_expression, GDScriptCompletionIdentifier &r_type) { bool found = false; + + if (++p_context.depth > 100) { + print_error("Maximum _guess_expression_type depth limit reached. Please file a bugreport."); + return false; + } + switch (p_expression->type) { case GDScriptParser::Node::TYPE_CONSTANT: { const GDScriptParser::ConstantNode *cn = static_cast<const GDScriptParser::ConstantNode *>(p_expression); @@ -782,12 +785,12 @@ static bool _guess_expression_type(const GDScriptCompletionContext &p_context, c if (mb && mb->is_const()) { bool all_is_const = true; Vector<Variant> args; - GDScriptCompletionContext c = p_context; - c.line = op->line; + GDScriptCompletionContext c2 = p_context; + c2.line = op->line; for (int i = 2; all_is_const && i < op->arguments.size(); i++) { GDScriptCompletionIdentifier arg; - if (_guess_expression_type(c, op->arguments[i], arg)) { + if (_guess_expression_type(c2, op->arguments[i], arg)) { if (arg.type.has_type && arg.type.is_constant && arg.value.get_type() != Variant::OBJECT) { args.push_back(arg.value); } else { @@ -1113,6 +1116,7 @@ static bool _guess_expression_type(const GDScriptCompletionContext &p_context, c } break; } } break; + default: {} } // It may have found a null, but that's never useful @@ -1133,7 +1137,7 @@ static bool _guess_expression_type(const GDScriptCompletionContext &p_context, c return found; } -static bool _guess_identifier_type(const GDScriptCompletionContext &p_context, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type) { +static bool _guess_identifier_type(GDScriptCompletionContext &p_context, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type) { // Look in blocks first const GDScriptParser::BlockNode *blk = p_context.block; @@ -1189,6 +1193,11 @@ static bool _guess_identifier_type(const GDScriptCompletionContext &p_context, c c.line = op->line; c.block = blk; if (_guess_expression_type(p_context, op->arguments[1], r_type)) { + r_type.type.is_meta_type = false; // Right-hand of `is` will be a meta type, but the left-hand value is not + // Not an assignment, it shouldn't carry any value + r_type.value = Variant(); + r_type.assigned_expression = NULL; + return true; } } @@ -1221,7 +1230,7 @@ static bool _guess_identifier_type(const GDScriptCompletionContext &p_context, c int def_from = p_context.function->arguments.size() - p_context.function->default_values.size(); if (i >= def_from) { - int def_idx = def_from - i; + int def_idx = i - def_from; if (p_context.function->default_values[def_idx]->type == GDScriptParser::Node::TYPE_OPERATOR) { const GDScriptParser::OperatorNode *op = static_cast<const GDScriptParser::OperatorNode *>(p_context.function->default_values[def_idx]); if (op->arguments.size() < 2) { @@ -1273,9 +1282,9 @@ static bool _guess_identifier_type(const GDScriptCompletionContext &p_context, c for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) { if (E->get().name == p_context.function->name) { MethodInfo &mi = E->get(); - for (List<PropertyInfo>::Element *E = mi.arguments.front(); E; E = E->next()) { - if (E->get().name == p_identifier) { - r_type = _type_from_property(E->get()); + for (List<PropertyInfo>::Element *F = mi.arguments.front(); F; F = F->next()) { + if (F->get().name == p_identifier) { + r_type = _type_from_property(F->get()); return true; } } @@ -1315,37 +1324,38 @@ static bool _guess_identifier_type(const GDScriptCompletionContext &p_context, c return false; } - // Check ClassDB - if (ClassDB::class_exists(p_identifier)) { - r_type.type.has_type = true; - r_type.type.kind = GDScriptParser::DataType::NATIVE; - r_type.type.native_type = p_identifier; - if (Engine::get_singleton()->has_singleton(p_identifier)) { - r_type.type.is_meta_type = false; - r_type.value = Engine::get_singleton()->get_singleton_object(p_identifier); - } else { - r_type.type.is_meta_type = true; - int idx = GDScriptLanguage::get_singleton()->get_global_map()[p_identifier]; - r_type.value = GDScriptLanguage::get_singleton()->get_global_array()[idx]; + for (int i = 0; i < 2; i++) { + StringName target_id; + switch (i) { + case 0: + // Check ClassDB + target_id = p_identifier; + break; + case 1: + // ClassDB again for underscore-prefixed classes + target_id = String("_") + p_identifier; + break; } - return true; - } - // ClassDB again for underscore-prefixed classes - StringName under_id = String("_") + p_identifier; - if (ClassDB::class_exists(under_id)) { - r_type.type.has_type = true; - r_type.type.kind = GDScriptParser::DataType::NATIVE; - r_type.type.native_type = p_identifier; - if (Engine::get_singleton()->has_singleton(p_identifier)) { - r_type.type.is_meta_type = false; - r_type.value = Engine::get_singleton()->get_singleton_object(p_identifier); - } else { - r_type.type.is_meta_type = true; - int idx = GDScriptLanguage::get_singleton()->get_global_map()[p_identifier]; - r_type.value = GDScriptLanguage::get_singleton()->get_global_array()[idx]; + if (ClassDB::class_exists(target_id)) { + r_type.type.has_type = true; + r_type.type.kind = GDScriptParser::DataType::NATIVE; + r_type.type.native_type = target_id; + if (Engine::get_singleton()->has_singleton(target_id)) { + r_type.type.is_meta_type = false; + r_type.value = Engine::get_singleton()->get_singleton_object(target_id); + } else { + r_type.type.is_meta_type = true; + const Map<StringName, int>::Element *target_elem = GDScriptLanguage::get_singleton()->get_global_map().find(target_id); + // Check because classes like EditorNode are in ClassDB by now, but unknown to GDScript + if (!target_elem) { + return false; + } + int idx = target_elem->get(); + r_type.value = GDScriptLanguage::get_singleton()->get_global_array()[idx]; + } + return true; } - return true; } // Check autoload singletons @@ -1357,7 +1367,7 @@ static bool _guess_identifier_type(const GDScriptCompletionContext &p_context, c return false; } -static bool _guess_identifier_type_from_base(const GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type) { +static bool _guess_identifier_type_from_base(GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type) { GDScriptParser::DataType base_type = p_base.type; bool _static = base_type.is_meta_type; while (base_type.has_type) { @@ -1376,11 +1386,11 @@ static bool _guess_identifier_type_from_base(const GDScriptCompletionContext &p_ for (int i = 0; i < base_type.class_type->variables.size(); i++) { GDScriptParser::ClassNode::Member m = base_type.class_type->variables[i]; if (m.identifier == p_identifier) { - if (m.data_type.has_type) { - r_type.type = m.data_type; - return true; - } if (m.expression) { + if (p_context.line == m.expression->line) { + // Variable used in the same expression + return false; + } if (_guess_expression_type(p_context, m.expression, r_type)) { return true; } @@ -1389,6 +1399,10 @@ static bool _guess_identifier_type_from_base(const GDScriptCompletionContext &p_ return true; } } + if (m.data_type.has_type) { + r_type.type = m.data_type; + return true; + } return false; } } @@ -1542,7 +1556,7 @@ static bool _find_last_return_in_block(const GDScriptCompletionContext &p_contex return false; } -static bool _guess_method_return_type_from_base(const GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, GDScriptCompletionIdentifier &r_type) { +static bool _guess_method_return_type_from_base(GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, GDScriptCompletionIdentifier &r_type) { GDScriptParser::DataType base_type = p_base.type; bool _static = base_type.is_meta_type; @@ -2002,7 +2016,8 @@ static void _find_identifiers_in_base(const GDScriptCompletionContext &p_context if (!_static) { List<MethodInfo> methods; - ClassDB::get_method_list(type, &methods, false, true); + bool is_autocompleting_getters = GLOBAL_GET("debug/gdscript/completion/autocomplete_setters_and_getters").booleanize(); + ClassDB::get_method_list(type, &methods, false, !is_autocompleting_getters); for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) { if (E->get().name.begins_with("_")) { continue; @@ -2109,8 +2124,8 @@ static void _find_identifiers(const GDScriptCompletionContext &p_context, bool p "and", "in", "not", "or", "false", "PI", "TAU", "INF", "NAN", "self", "true", "as", "assert", "breakpoint", "class", "extends", "is", "func", "preload", "setget", "signal", "tool", "yield", "const", "enum", "export", "onready", "static", "var", "break", "continue", "if", "elif", - "else", "for", "pass", "return", "match", "while", "remote", "sync", "master", "slave", - "remotesync", "mastersync", "slavesync", + "else", "for", "pass", "return", "match", "while", "remote", "sync", "master", "puppet", "slave", + "remotesync", "mastersync", "puppetsync", 0 }; @@ -2222,8 +2237,8 @@ static void _find_call_arguments(const GDScriptCompletionContext &p_context, con if (obj) { List<String> options; obj->get_argument_options(p_method, p_argidx, &options); - for (List<String>::Element *E = options.front(); E; E = E->next()) { - r_result.insert(E->get()); + for (List<String>::Element *F = options.front(); F; F = F->next()) { + r_result.insert(F->get()); } } } @@ -2306,7 +2321,7 @@ static void _find_call_arguments(const GDScriptCompletionContext &p_context, con } } -static void _find_call_arguments(const GDScriptCompletionContext &p_context, const GDScriptParser::Node *p_node, int p_argidx, Set<String> &r_result, bool &r_forced, String &r_arghint) { +static void _find_call_arguments(GDScriptCompletionContext &p_context, const GDScriptParser::Node *p_node, int p_argidx, Set<String> &r_result, bool &r_forced, String &r_arghint) { if (!p_node || p_node->type != GDScriptParser::Node::TYPE_OPERATOR) { return; @@ -2443,9 +2458,13 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base context._class = parser.get_completion_class(); context.block = parser.get_completion_block(); context.function = parser.get_completion_function(); - context.base = p_owner; - context.base_path = p_base_path; context.line = parser.get_completion_line(); + + if (!context._class || context._class->owner == NULL) { + context.base = p_owner; + context.base_path = p_base_path; + } + bool is_function = false; switch (parser.get_completion_type()) { @@ -2787,12 +2806,12 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base if (base_type.class_type) { for (Map<StringName, GDScriptParser::ClassNode::Constant>::Element *E = base_type.class_type->constant_expressions.front(); E; E = E->next()) { GDScriptCompletionIdentifier constant; - GDScriptCompletionContext c = context; - c._class = base_type.class_type; - c.function = NULL; - c.block = NULL; - c.line = E->value().expression->line; - if (_guess_expression_type(c, E->value().expression, constant)) { + GDScriptCompletionContext c2 = context; + c2._class = base_type.class_type; + c2.function = NULL; + c2.block = NULL; + c2.line = E->value().expression->line; + if (_guess_expression_type(c2, E->value().expression, constant)) { if (constant.type.has_type && constant.type.is_meta_type) { options.insert(E->key().operator String()); } @@ -3304,7 +3323,7 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol } } else { /* - // Because get_integer_constant_enum and get_integer_constant dont work on @GlobalScope + // Because get_integer_constant_enum and get_integer_constant don't work on @GlobalScope // We cannot determine the exact nature of the identifier here // Otherwise these codes would work StringName enumName = ClassDB::get_integer_constant_enum("@GlobalScope", p_symbol, true); @@ -3352,6 +3371,7 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol return OK; } } break; + default: {} } return ERR_CANT_RESOLVE; diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp index d615b58b24..cff9ba55b8 100644 --- a/modules/gdscript/gdscript_function.cpp +++ b/modules/gdscript/gdscript_function.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,9 +30,9 @@ #include "gdscript_function.h" +#include "core/os/os.h" #include "gdscript.h" #include "gdscript_functions.h" -#include "os/os.h" Variant *GDScriptFunction::_get_variant(int p_address, GDScriptInstance *p_instance, GDScript *p_script, Variant &self, Variant *p_stack, String &r_error) const { @@ -155,6 +155,7 @@ String GDScriptFunction::_get_call_error(const Variant::CallError &p_err, const return err_text; } +#ifdef DEBUG_ENABLED static String _get_var_type(const Variant *p_type) { String basestr; @@ -164,7 +165,6 @@ static String _get_var_type(const Variant *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() + ")"; @@ -173,10 +173,6 @@ static String _get_var_type(const Variant *p_type) { } else { basestr = "previously freed instance"; } - -#else - basestr = "Object"; -#endif } } else { @@ -185,6 +181,7 @@ static String _get_var_type(const Variant *p_type) { return basestr; } +#endif #if defined(__GNUC__) #define OPCODES_TABLE \ @@ -278,7 +275,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #endif uint32_t alloca_size = 0; - GDScript *_class; + GDScript *script; int ip = 0; int line = _initial_line; @@ -289,7 +286,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a line = p_state->line; ip = p_state->ip; alloca_size = p_state->stack.size(); - _class = p_state->_class; + script = p_state->script.ptr(); p_instance = p_state->instance; defarg = p_state->defarg; self = p_state->self; @@ -332,10 +329,15 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } if (!argument_types[i].is_type(*p_args[i], true)) { - r_err.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_err.argument = i; - r_err.expected = argument_types[i].kind == GDScriptDataType::BUILTIN ? argument_types[i].builtin_type : Variant::OBJECT; - return Variant(); + if (argument_types[i].is_type(Variant(), true)) { + memnew_placement(&stack[i], Variant); + continue; + } else { + r_err.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_err.argument = i; + r_err.expected = argument_types[i].kind == GDScriptDataType::BUILTIN ? argument_types[i].builtin_type : Variant::OBJECT; + return Variant(); + } } if (argument_types[i].kind == GDScriptDataType::BUILTIN) { Variant arg = Variant::construct(argument_types[i].builtin_type, &p_args[i], 1, r_err); @@ -371,9 +373,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } else { self = p_instance->owner; } - _class = p_instance->script.ptr(); + script = p_instance->script.ptr(); } else { - _class = _script; + script = _script; } } @@ -398,7 +400,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #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); \ + m_v = _get_variant(_code_ptr[ip + m_code_ofs], p_instance, script, self, stack, err_text); \ if (unlikely(!m_v)) \ OPCODE_BREAK; @@ -407,7 +409,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #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); + m_v = _get_variant(_code_ptr[ip + m_code_ofs], p_instance, script, self, stack, err_text); #endif @@ -422,8 +424,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a profile.call_count++; profile.frame_call_count++; } -#endif bool exit_ok = false; +#endif #ifdef DEBUG_ENABLED OPCODE_WHILE(ip < _code_size) { @@ -480,56 +482,53 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GET_VARIANT_PTR(dst, 3); #ifdef DEBUG_ENABLED - if (a->get_type() != Variant::OBJECT || a->operator Object *() == NULL) { - - err_text = "Left operand of 'is' is not an instance of anything."; - OPCODE_BREAK; - } if (b->get_type() != Variant::OBJECT || b->operator Object *() == NULL) { err_text = "Right operand of 'is' is not a class."; OPCODE_BREAK; } #endif - Object *obj_A = *a; - Object *obj_B = *b; - - GDScript *scr_B = Object::cast_to<GDScript>(obj_B); bool extends_ok = false; + if (a->get_type() == Variant::OBJECT && a->operator Object *() != NULL) { + Object *obj_A = *a; + Object *obj_B = *b; - 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. + GDScript *scr_B = Object::cast_to<GDScript>(obj_B); - if (obj_A->get_script_instance() && obj_A->get_script_instance()->get_language() == GDScriptLanguage::get_singleton()) { + 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. - GDScript *cmp = static_cast<GDScript *>(obj_A->get_script_instance()->get_script().ptr()); - //bool found=false; - while (cmp) { + if (obj_A->get_script_instance() && obj_A->get_script_instance()->get_language() == GDScriptLanguage::get_singleton()) { - if (cmp == scr_B) { - //inherits from script, all ok - extends_ok = true; - break; - } + 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; + cmp = cmp->_base; + } } - } - } else { + } else { - GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(obj_B); + GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(obj_B); #ifdef DEBUG_ENABLED - if (!nc) { + if (!nc) { - err_text = "Right operand of 'is' is not a class (type: '" + obj_B->get_class() + "')."; - OPCODE_BREAK; - } + err_text = "Right operand of 'is' is not a class (type: '" + obj_B->get_class() + "')."; + OPCODE_BREAK; + } #endif - extends_ok = ClassDB::is_parent_class(obj_A->get_class_name(), nc->get_name()); + extends_ok = ClassDB::is_parent_class(obj_A->get_class_name(), nc->get_name()); + } } *dst = extends_ok; @@ -682,8 +681,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GET_VARIANT_PTR(src, 2); bool valid; +#ifndef DEBUG_ENABLED + ClassDB::set_property(p_instance->owner, *index, *src, &valid); +#else bool ok = ClassDB::set_property(p_instance->owner, *index, *src, &valid); -#ifdef DEBUG_ENABLED if (!ok) { err_text = "Internal error setting property: " + String(*index); OPCODE_BREAK; @@ -703,9 +704,11 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GD_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 +#ifndef DEBUG_ENABLED + ClassDB::get_property(p_instance->owner, *index, *dst); +#else + bool ok = ClassDB::get_property(p_instance->owner, *index, *dst); if (!ok) { err_text = "Internal error getting property: " + String(*index); OPCODE_BREAK; @@ -752,13 +755,13 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a OPCODE(OPCODE_ASSIGN_TYPED_BUILTIN) { CHECK_SPACE(4); - Variant::Type var_type = (Variant::Type)_code_ptr[ip + 1]; GET_VARIANT_PTR(dst, 2); GET_VARIANT_PTR(src, 3); +#ifdef DEBUG_ENABLED + Variant::Type var_type = (Variant::Type)_code_ptr[ip + 1]; GD_ERR_BREAK(var_type < 0 || var_type >= Variant::VARIANT_MAX); -#ifdef DEBUG_ENABLED if (src->get_type() != var_type) { if (Variant::can_convert_strict(src->get_type(), var_type)) { Variant::CallError ce; @@ -782,14 +785,14 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a OPCODE(OPCODE_ASSIGN_TYPED_NATIVE) { CHECK_SPACE(4); - GET_VARIANT_PTR(type, 1); GET_VARIANT_PTR(dst, 2); GET_VARIANT_PTR(src, 3); #ifdef DEBUG_ENABLED + GET_VARIANT_PTR(type, 1); GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(type->operator Object *()); GD_ERR_BREAK(!nc); - if (!src->get_type() != Variant::OBJECT && !src->get_type() != Variant::NIL) { + if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) { err_text = "Trying to assign value of type '" + Variant::get_type_name(src->get_type()) + "' to a variable of type '" + nc->get_name() + "'."; OPCODE_BREAK; @@ -811,11 +814,11 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a OPCODE(OPCODE_ASSIGN_TYPED_SCRIPT) { CHECK_SPACE(4); - GET_VARIANT_PTR(type, 1); GET_VARIANT_PTR(dst, 2); GET_VARIANT_PTR(src, 3); #ifdef DEBUG_ENABLED + GET_VARIANT_PTR(type, 1); Script *base_type = Object::cast_to<Script>(type->operator Object *()); GD_ERR_BREAK(!base_type); @@ -1085,7 +1088,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a if (argc >= 1) { methodstr = String(*argptrs[0]) + " (via call)"; if (err.error == Variant::CallError::CALL_ERROR_INVALID_ARGUMENT) { - err.argument -= 1; + err.argument += 1; } } } else if (methodstr == "free") { @@ -1255,11 +1258,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a gdfs->state.stack_size = _stack_size; gdfs->state.self = self; gdfs->state.alloca_size = alloca_size; - gdfs->state._class = _class; + gdfs->state.script = Ref<GDScript>(_script); 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; @@ -1282,10 +1284,11 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a OPCODE_BREAK; } #endif + Object *obj = argobj->operator Object *(); String signal = argname->operator String(); -#ifdef DEBUG_ENABLED +#ifdef DEBUG_ENABLED if (!obj) { err_text = "First argument of yield() is null."; OPCODE_BREAK; @@ -1302,17 +1305,19 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a OPCODE_BREAK; } -#endif Error err = obj->connect(signal, gdfs.ptr(), "_signal_callback", varray(gdfs), Object::CONNECT_ONESHOT); -#ifdef DEBUG_ENABLED if (err != OK) { err_text = "Error connecting to signal: " + signal + " during yield()."; OPCODE_BREAK; } +#else + obj->connect(signal, gdfs.ptr(), "_signal_callback", varray(gdfs), Object::CONNECT_ONESHOT); #endif } +#ifdef DEBUG_ENABLED exit_ok = true; +#endif OPCODE_BREAK; } @@ -1389,7 +1394,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a CHECK_SPACE(2); GET_VARIANT_PTR(r, 1); retvalue = *r; +#ifdef DEBUG_ENABLED exit_ok = true; +#endif OPCODE_BREAK; } @@ -1461,9 +1468,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a OPCODE(OPCODE_ASSERT) { CHECK_SPACE(2); - GET_VARIANT_PTR(test, 1); #ifdef DEBUG_ENABLED + GET_VARIANT_PTR(test, 1); bool result = test->booleanize(); if (!result) { @@ -1518,8 +1525,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a DISPATCH_OPCODE; OPCODE(OPCODE_END) { - +#ifdef DEBUG_ENABLED exit_ok = true; +#endif OPCODE_BREAK; } @@ -1542,8 +1550,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a String err_file; if (p_instance) err_file = p_instance->script->path; - else if (_class) - err_file = _class->path; + else if (script) + err_file = script->path; if (err_file == "") err_file = "<built-in>"; String err_func = name; @@ -1757,17 +1765,14 @@ GDScriptFunction::~GDScriptFunction() { Variant GDScriptFunctionState::_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)) { +#ifdef DEBUG_ENABLED 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()); - } +#else + return Variant(); #endif + } Variant arg; r_error.error = Variant::CallError::CALL_OK; @@ -1837,9 +1842,6 @@ bool GDScriptFunctionState::is_valid(bool p_extended_check) const { //class instance gone? if (state.instance_id && !ObjectDB::get_instance(state.instance_id)) return false; - //script gone? - if (state.script_id && !ObjectDB::get_instance(state.script_id)) - return false; } return true; @@ -1848,17 +1850,14 @@ bool GDScriptFunctionState::is_valid(bool p_extended_check) const { Variant GDScriptFunctionState::resume(const Variant &p_arg) { ERR_FAIL_COND_V(!function, Variant()); -#ifdef DEBUG_ENABLED if (state.instance_id && !ObjectDB::get_instance(state.instance_id)) { +#ifdef DEBUG_ENABLED 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()); - } +#else + return Variant(); #endif + } state.result = p_arg; Variant::CallError err; diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h index 633cca35d3..cefc28d77f 100644 --- a/modules/gdscript/gdscript_function.h +++ b/modules/gdscript/gdscript_function.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,13 +31,13 @@ #ifndef GDSCRIPT_FUNCTION_H #define GDSCRIPT_FUNCTION_H -#include "os/thread.h" -#include "pair.h" -#include "reference.h" -#include "script_language.h" -#include "self_list.h" -#include "string_db.h" -#include "variant.h" +#include "core/os/thread.h" +#include "core/pair.h" +#include "core/reference.h" +#include "core/script_language.h" +#include "core/self_list.h" +#include "core/string_name.h" +#include "core/variant.h" class GDScriptInstance; class GDScript; @@ -45,10 +45,11 @@ class GDScript; struct GDScriptDataType { bool has_type; enum { + UNINITIALIZED, BUILTIN, NATIVE, SCRIPT, - GDSCRIPT + GDSCRIPT, } kind; Variant::Type builtin_type; StringName native_type; @@ -58,6 +59,8 @@ struct GDScriptDataType { if (!has_type) return true; // Can't type check switch (kind) { + case UNINITIALIZED: + break; case BUILTIN: { Variant::Type var_type = p_variant.get_type(); bool valid = builtin_type == var_type; @@ -74,8 +77,14 @@ struct GDScriptDataType { return false; } Object *obj = p_variant.operator Object *(); - if (obj && !ClassDB::is_parent_class(obj->get_class_name(), native_type)) { - return false; + if (obj) { + if (!ClassDB::is_parent_class(obj->get_class_name(), native_type)) { + // Try with underscore prefix + StringName underscore_native_type = "_" + native_type; + if (!ClassDB::is_parent_class(obj->get_class_name(), underscore_native_type)) { + return false; + } + } } return true; } break; @@ -107,6 +116,8 @@ struct GDScriptDataType { PropertyInfo info; if (has_type) { switch (kind) { + case UNINITIALIZED: + break; case BUILTIN: { info.type = builtin_type; } break; @@ -128,7 +139,9 @@ struct GDScriptDataType { } GDScriptDataType() : - has_type(false) {} + has_type(false), + kind(UNINITIALIZED), + builtin_type(Variant::NIL) {} }; class GDScriptFunction { @@ -272,15 +285,13 @@ private: public: struct CallState { - ObjectID instance_id; //by debug only - ObjectID script_id; - + ObjectID instance_id; GDScriptInstance *instance; Vector<uint8_t> stack; int stack_size; Variant self; uint32_t alloca_size; - GDScript *_class; + Ref<GDScript> script; int ip; int line; int defarg; diff --git a/modules/gdscript/gdscript_functions.cpp b/modules/gdscript/gdscript_functions.cpp index d9c20868bd..44d44462ca 100644 --- a/modules/gdscript/gdscript_functions.cpp +++ b/modules/gdscript/gdscript_functions.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,15 +30,15 @@ #include "gdscript_functions.h" -#include "class_db.h" -#include "func_ref.h" +#include "core/class_db.h" +#include "core/func_ref.h" +#include "core/io/json.h" +#include "core/io/marshalls.h" +#include "core/math/math_funcs.h" +#include "core/os/os.h" +#include "core/reference.h" +#include "core/variant_parser.h" #include "gdscript.h" -#include "io/json.h" -#include "io/marshalls.h" -#include "math_funcs.h" -#include "os/os.h" -#include "reference.h" -#include "variant_parser.h" const char *GDScriptFunctions::get_func_name(Function p_func) { @@ -106,6 +106,8 @@ const char *GDScriptFunctions::get_func_name(Function p_func) { "printerr", "printraw", "print_debug", + "push_error", + "push_warning", "var2str", "str2var", "var2bytes", @@ -707,13 +709,40 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_ ScriptLanguage *script = GDScriptLanguage::get_singleton(); if (script->debug_get_stack_level_count() > 0) { - str += "\n\t"; - str += "At: " + script->debug_get_stack_level_source(0) + ":" + itos(script->debug_get_stack_level_line(0)); // + " in function '" + script->debug_get_stack_level_function(0) + "'"; + str += "\n At: " + script->debug_get_stack_level_source(0) + ":" + itos(script->debug_get_stack_level_line(0)) + ":" + script->debug_get_stack_level_function(0) + "()"; } print_line(str); r_ret = Variant(); } break; + case PUSH_ERROR: { + 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(); + break; + } + + String message = *p_args[0]; + ERR_PRINTS(message); + r_ret = Variant(); + } break; + case PUSH_WARNING: { + 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(); + break; + } + + String message = *p_args[0]; + WARN_PRINTS(message); + r_ret = Variant(); + } break; case VAR_TO_STR: { VALIDATE_ARG_COUNT(1); String vars; @@ -729,22 +758,14 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_ r_ret = Variant(); return; } + r_ret = *p_args[0]; VariantParser::StreamString ss; ss.s = *p_args[0]; String errs; int line; - Error err = VariantParser::parse(&ss, r_ret, errs, line); - - if (err != OK) { - r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::STRING; - r_ret = "Parse error at line " + itos(line) + ": " + errs; - return; - } - + (void)VariantParser::parse(&ss, r_ret, errs, line); } break; case VAR_TO_BYTES: { VALIDATE_ARG_COUNT(1); @@ -861,7 +882,7 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_ int incr = *p_args[2]; if (incr == 0) { - r_ret = RTR("step argument is zero!"); + r_ret = RTR("Step argument is zero!"); r_error.error = Variant::CallError::CALL_ERROR_INVALID_METHOD; return; } @@ -1458,7 +1479,7 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) { return mi; } break; case MATH_ATAN2: { - MethodInfo mi("atan2", PropertyInfo(Variant::REAL, "x"), PropertyInfo(Variant::REAL, "y")); + MethodInfo mi("atan2", PropertyInfo(Variant::REAL, "y"), PropertyInfo(Variant::REAL, "x")); mi.return_val.type = Variant::REAL; return mi; } break; @@ -1544,7 +1565,8 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) { } break; case MATH_LERP: { MethodInfo mi("lerp", PropertyInfo(Variant::NIL, "from"), PropertyInfo(Variant::NIL, "to"), PropertyInfo(Variant::REAL, "weight")); - mi.return_val.type = Variant::REAL; + mi.return_val.type = Variant::NIL; + mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; return mi; } break; case MATH_INVERSE_LERP: { @@ -1754,11 +1776,25 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) { return mi; } break; + case PUSH_ERROR: { + + MethodInfo mi(Variant::NIL, "push_error", PropertyInfo(Variant::STRING, "message")); + mi.return_val.type = Variant::NIL; + return mi; + + } break; + case PUSH_WARNING: { + + MethodInfo mi(Variant::NIL, "push_warning", PropertyInfo(Variant::STRING, "message")); + mi.return_val.type = Variant::NIL; + return mi; + + } break; case VAR_TO_STR: { + MethodInfo mi("var2str", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT)); mi.return_val.type = Variant::STRING; return mi; - } break; case STR_TO_VAR: { @@ -1768,10 +1804,10 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) { return mi; } break; case VAR_TO_BYTES: { + MethodInfo mi("var2bytes", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT)); mi.return_val.type = Variant::POOL_BYTE_ARRAY; return mi; - } break; case BYTES_TO_VAR: { @@ -1853,7 +1889,7 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) { } break; case GET_STACK: { MethodInfo mi("get_stack"); - mi.return_val.type = Variant::NIL; + mi.return_val.type = Variant::ARRAY; return mi; } break; diff --git a/modules/gdscript/gdscript_functions.h b/modules/gdscript/gdscript_functions.h index a29f06e839..fcb8f32e54 100644 --- a/modules/gdscript/gdscript_functions.h +++ b/modules/gdscript/gdscript_functions.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,7 +31,7 @@ #ifndef GDSCRIPT_FUNCTIONS_H #define GDSCRIPT_FUNCTIONS_H -#include "variant.h" +#include "core/variant.h" class GDScriptFunctions { public: @@ -97,6 +97,8 @@ public: TEXT_PRINTERR, TEXT_PRINTRAW, TEXT_PRINT_DEBUG, + PUSH_ERROR, + PUSH_WARNING, VAR_TO_STR, STR_TO_VAR, VAR_TO_BYTES, @@ -117,7 +119,6 @@ public: LEN, IS_INSTANCE_VALID, FUNC_MAX - }; static const char *get_func_name(Function p_func); diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 7502f09d8a..f005f88d2e 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,14 +32,13 @@ #include "core/core_string_names.h" #include "core/engine.h" +#include "core/io/resource_loader.h" +#include "core/os/file_access.h" +#include "core/print_string.h" #include "core/project_settings.h" #include "core/reference.h" +#include "core/script_language.h" #include "gdscript.h" -#include "io/resource_loader.h" -#include "os/file_access.h" -#include "print_string.h" -#include "project_settings.h" -#include "script_language.h" template <class T> T *GDScriptParser::alloc_node() { @@ -57,7 +56,9 @@ T *GDScriptParser::alloc_node() { return t; } +#ifdef DEBUG_ENABLED static String _find_function_name(const GDScriptParser::OperatorNode *p_call); +#endif // DEBUG_ENABLED bool GDScriptParser::_end_statement() { @@ -82,8 +83,11 @@ bool GDScriptParser::_enter_indent_block(BlockNode *p_block) { } tokenizer->advance(); - if (tokenizer->get_token() != GDScriptTokenizer::TK_NEWLINE) { + if (tokenizer->get_token() == GDScriptTokenizer::TK_EOF) { + return false; + } + if (tokenizer->get_token() != GDScriptTokenizer::TK_NEWLINE) { // be more python-like int current = tab_level.back()->get(); tab_level.push_back(current); @@ -93,10 +97,11 @@ bool GDScriptParser::_enter_indent_block(BlockNode *p_block) { } while (true) { - if (tokenizer->get_token() != GDScriptTokenizer::TK_NEWLINE) { return false; //wtf + } else if (tokenizer->get_token(1) == GDScriptTokenizer::TK_EOF) { + return false; } else if (tokenizer->get_token(1) != GDScriptTokenizer::TK_NEWLINE) { int indent = tokenizer->get_token_line_indent(); @@ -468,29 +473,31 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s } Ref<Resource> res; - if (!validating) { + dependencies.push_back(path); + if (!dependencies_only) { + if (!validating) { - //this can be too slow for just validating code - if (for_completion && ScriptCodeCompletionCache::get_singleton() && FileAccess::exists(path)) { - res = ScriptCodeCompletionCache::get_singleton()->get_cached_resource(path); - } else if (!for_completion || FileAccess::exists(path)) { - res = ResourceLoader::load(path); - } - } else { + //this can be too slow for just validating code + if (for_completion && ScriptCodeCompletionCache::get_singleton() && FileAccess::exists(path)) { + res = ScriptCodeCompletionCache::get_singleton()->get_cached_resource(path); + } else if (!for_completion || FileAccess::exists(path)) { + res = ResourceLoader::load(path); + } + } else { - if (!FileAccess::exists(path)) { + if (!FileAccess::exists(path)) { + _set_error("Can't preload resource at path: " + path); + return NULL; + } else if (ScriptCodeCompletionCache::get_singleton()) { + res = ScriptCodeCompletionCache::get_singleton()->get_cached_resource(path); + } + } + if (!res.is_valid()) { _set_error("Can't preload resource at path: " + path); return NULL; - } else if (ScriptCodeCompletionCache::get_singleton()) { - res = ScriptCodeCompletionCache::get_singleton()->get_cached_resource(path); } } - if (!res.is_valid()) { - _set_error("Can't preload resource at path: " + path); - return NULL; - } - if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) { _set_error("Expected ')' after 'preload' path"); return NULL; @@ -498,7 +505,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s Ref<GDScript> gds = res; if (gds.is_valid() && !gds->is_valid()) { - _set_error("Could not fully preload the script, possible cyclic reference or compilation error."); + _set_error("Could not fully preload the script, possible cyclic reference or compilation error. Use 'load()' instead if a cyclic reference is intended."); return NULL; } @@ -638,9 +645,21 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s expr = op; } else { - - _set_error("Static constant '" + identifier.operator String() + "' not present in built-in type " + Variant::get_type_name(bi_type) + "."); - return NULL; + // Object is a special case + bool valid = false; + if (bi_type == Variant::OBJECT) { + int object_constant = ClassDB::get_integer_constant("Object", identifier, &valid); + if (valid) { + ConstantNode *cn = alloc_node<ConstantNode>(); + cn->value = object_constant; + cn->datatype = _type_from_variant(cn->value); + expr = cn; + } + } + if (!valid) { + _set_error("Static constant '" + identifier.operator String() + "' not present in built-in type " + Variant::get_type_name(bi_type) + "."); + return NULL; + } } } else { @@ -662,7 +681,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s if (tokenizer->get_token() == GDScriptTokenizer::TK_BUILT_IN_TYPE) { Variant::Type ct = tokenizer->get_token_type(); - if (p_parsing_constant == false) { + if (!p_parsing_constant) { if (ct == Variant::ARRAY) { if (tokenizer->get_token(2) == GDScriptTokenizer::TK_PARENTHESIS_CLOSE) { ArrayNode *arr = alloc_node<ArrayNode>(); @@ -732,7 +751,6 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s while (!bfn && b) { if (b->variables.has(identifier)) { IdentifierNode *id = alloc_node<IdentifierNode>(); - LocalVarNode *lv = b->variables[identifier]; id->name = identifier; id->declared_block = b; id->line = id_line; @@ -740,6 +758,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s bfn = true; #ifdef DEBUG_ENABLED + LocalVarNode *lv = b->variables[identifier]; switch (tokenizer->get_token()) { case GDScriptTokenizer::TK_OP_ASSIGN_ADD: case GDScriptTokenizer::TK_OP_ASSIGN_BIT_AND: @@ -794,6 +813,33 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s expr = constant; bfn = true; } + + if (!dependencies_only) { + if (!bfn && ScriptServer::is_global_class(identifier)) { + Ref<Script> scr = ResourceLoader::load(ScriptServer::get_global_class_path(identifier)); + if (scr.is_valid() && scr->is_valid()) { + ConstantNode *constant = alloc_node<ConstantNode>(); + constant->value = scr; + expr = constant; + bfn = true; + } + } + + // Check parents for the constant + if (!bfn && cln->extends_file != StringName()) { + Ref<GDScript> parent = ResourceLoader::load(cln->extends_file); + if (parent.is_valid() && parent->is_valid()) { + Map<StringName, Variant> parent_constants; + parent->get_constants(&parent_constants); + if (parent_constants.has(identifier)) { + ConstantNode *constant = alloc_node<ConstantNode>(); + constant->value = parent_constants[identifier]; + expr = constant; + bfn = true; + } + } + } + } } if (!bfn) { @@ -2050,7 +2096,7 @@ GDScriptParser::PatternNode *GDScriptParser::_parse_pattern(bool p_static) { return NULL; } pattern->pt_type = GDScriptParser::PatternNode::PT_BIND; - pattern->bind = tokenizer->get_token_identifier(); + pattern->bind = tokenizer->get_token_literal(); // Check if variable name is already used BlockNode *bl = current_block; while (bl) { @@ -2479,7 +2525,7 @@ void GDScriptParser::_generate_pattern(PatternNode *p_pattern, Node *p_node_to_m Node *condition = NULL; - // chech for has, then for pattern + // check for has, then for pattern IdentifierNode *has = alloc_node<IdentifierNode>(); has->name = "has"; @@ -2608,14 +2654,14 @@ void GDScriptParser::_transform_match_statment(MatchNode *p_match_statement) { local_var->assign = e->value(); local_var->set_datatype(local_var->assign->get_datatype()); - IdentifierNode *id = alloc_node<IdentifierNode>(); - id->name = local_var->name; - id->declared_block = branch->body; - id->set_datatype(local_var->assign->get_datatype()); + IdentifierNode *id2 = alloc_node<IdentifierNode>(); + id2->name = local_var->name; + id2->declared_block = branch->body; + id2->set_datatype(local_var->assign->get_datatype()); OperatorNode *op = alloc_node<OperatorNode>(); op->op = OperatorNode::OP_ASSIGN; - op->arguments.push_back(id); + op->arguments.push_back(id2); op->arguments.push_back(local_var->assign); branch->body->statements.push_front(op); @@ -2662,9 +2708,9 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) { if (pending_newline != -1) { - NewLineNode *nl = alloc_node<NewLineNode>(); - nl->line = pending_newline; - p_block->statements.push_back(nl); + NewLineNode *nl2 = alloc_node<NewLineNode>(); + nl2->line = pending_newline; + p_block->statements.push_back(nl2); pending_newline = -1; } @@ -2703,9 +2749,9 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) { return; } - NewLineNode *nl = alloc_node<NewLineNode>(); - nl->line = tokenizer->get_token_line(); - p_block->statements.push_back(nl); + NewLineNode *nl2 = alloc_node<NewLineNode>(); + nl2->line = tokenizer->get_token_line(); + p_block->statements.push_back(nl2); } break; case GDScriptTokenizer::TK_CF_PASS: { @@ -2890,14 +2936,14 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) { cf_else->cf_type = ControlFlowNode::CF_IF; //condition - Node *condition = _parse_and_reduce_expression(p_block, p_static); - if (!condition) { + Node *condition2 = _parse_and_reduce_expression(p_block, p_static); + if (!condition2) { if (_recover_from_completion()) { break; } return; } - cf_else->arguments.push_back(condition); + cf_else->arguments.push_back(condition2); cf_else->cf_type = ControlFlowNode::CF_IF; cf_if->body_else->statements.push_back(cf_else); @@ -2960,8 +3006,8 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) { case GDScriptTokenizer::TK_CF_WHILE: { tokenizer->advance(); - Node *condition = _parse_and_reduce_expression(p_block, p_static); - if (!condition) { + Node *condition2 = _parse_and_reduce_expression(p_block, p_static); + if (!condition2) { if (_recover_from_completion()) { break; } @@ -2971,7 +3017,7 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) { ControlFlowNode *cf_while = alloc_node<ControlFlowNode>(); cf_while->cf_type = ControlFlowNode::CF_WHILE; - cf_while->arguments.push_back(condition); + cf_while->arguments.push_back(condition2); cf_while->body = alloc_node<BlockNode>(); cf_while->body->parent_block = p_block; @@ -3021,7 +3067,6 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) { } DataType iter_type; - iter_type.is_constant = true; if (container->type == Node::TYPE_OPERATOR) { @@ -3346,6 +3391,13 @@ void GDScriptParser::_parse_extends(ClassNode *p_class) { p_class->extends_file = constant; tokenizer->advance(); + // Add parent script as a dependency + String parent = constant; + if (parent.is_rel_path()) { + parent = base_path.plus_file(parent).simplify_path(); + } + dependencies.push_back(parent); + if (tokenizer->get_token() != GDScriptTokenizer::TK_PERIOD) { return; } else @@ -3462,16 +3514,20 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { tokenizer->advance(); if ((tokenizer->get_token() == GDScriptTokenizer::TK_CONSTANT && tokenizer->get_token_constant().get_type() == Variant::STRING)) { - Variant constant = tokenizer->get_token_constant(); - String icon_path = constant.operator String(); +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + Variant constant = tokenizer->get_token_constant(); + String icon_path = constant.operator String(); - String abs_icon_path = icon_path.is_rel_path() ? self_path.get_base_dir().plus_file(icon_path).simplify_path() : icon_path; - if (!FileAccess::exists(abs_icon_path)) { - _set_error("No class icon found at: " + abs_icon_path); - return; - } + String abs_icon_path = icon_path.is_rel_path() ? self_path.get_base_dir().plus_file(icon_path).simplify_path() : icon_path; + if (!FileAccess::exists(abs_icon_path)) { + _set_error("No class icon found at: " + abs_icon_path); + return; + } - p_class->icon_path = icon_path; + p_class->icon_path = icon_path; + } +#endif tokenizer->advance(); } else { @@ -3735,6 +3791,19 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { BlockNode *block = alloc_node<BlockNode>(); block->parent_class = p_class; + FunctionNode *function = alloc_node<FunctionNode>(); + function->name = name; + function->arguments = arguments; + function->argument_types = argument_types; + function->default_values = default_values; + function->_static = _static; + function->line = fnline; +#ifdef DEBUG_ENABLED + function->arguments_usage = arguments_usage; +#endif // DEBUG_ENABLED + function->rpc_mode = rpc_mode; + rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED; + if (name == "_init") { if (_static) { @@ -3765,7 +3834,9 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { parenthesis++; while (true) { + current_function = function; Node *arg = _parse_and_reduce_expression(p_class, _static); + current_function = NULL; cparent->arguments.push_back(arg); if (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) { @@ -3809,19 +3880,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { return; } - FunctionNode *function = alloc_node<FunctionNode>(); - function->name = name; function->return_type = return_type; - function->arguments = arguments; - function->argument_types = argument_types; - function->default_values = default_values; - function->_static = _static; - function->line = fnline; -#ifdef DEBUG_ENABLED - function->arguments_usage = arguments_usage; -#endif // DEBUG_ENABLED - function->rpc_mode = rpc_mode; - rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED; if (_static) p_class->static_functions.push_back(function); @@ -4336,6 +4395,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { current_export.type = Variant::INT; current_export.hint = is_flags ? PROPERTY_HINT_FLAGS : PROPERTY_HINT_ENUM; + current_export.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE; Dictionary enum_values = constant; List<Variant> keys; @@ -4383,10 +4443,10 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { tokenizer->advance(); } - if (tokenizer->get_token() != GDScriptTokenizer::TK_PR_VAR && tokenizer->get_token() != GDScriptTokenizer::TK_PR_ONREADY && tokenizer->get_token() != GDScriptTokenizer::TK_PR_REMOTE && tokenizer->get_token() != GDScriptTokenizer::TK_PR_MASTER && tokenizer->get_token() != GDScriptTokenizer::TK_PR_SLAVE && tokenizer->get_token() != GDScriptTokenizer::TK_PR_SYNC && tokenizer->get_token() != GDScriptTokenizer::TK_PR_REMOTESYNC && tokenizer->get_token() != GDScriptTokenizer::TK_PR_MASTERSYNC && tokenizer->get_token() != GDScriptTokenizer::TK_PR_SLAVESYNC) { + if (tokenizer->get_token() != GDScriptTokenizer::TK_PR_VAR && tokenizer->get_token() != GDScriptTokenizer::TK_PR_ONREADY && tokenizer->get_token() != GDScriptTokenizer::TK_PR_REMOTE && tokenizer->get_token() != GDScriptTokenizer::TK_PR_MASTER && tokenizer->get_token() != GDScriptTokenizer::TK_PR_PUPPET && tokenizer->get_token() != GDScriptTokenizer::TK_PR_SYNC && tokenizer->get_token() != GDScriptTokenizer::TK_PR_REMOTESYNC && tokenizer->get_token() != GDScriptTokenizer::TK_PR_MASTERSYNC && tokenizer->get_token() != GDScriptTokenizer::TK_PR_PUPPETSYNC && tokenizer->get_token() != GDScriptTokenizer::TK_PR_SLAVE) { current_export = PropertyInfo(); - _set_error("Expected 'var', 'onready', 'remote', 'master', 'slave', 'sync', 'remotesync', 'mastersync', 'slavesync'."); + _set_error("Expected 'var', 'onready', 'remote', 'master', 'puppet', 'sync', 'remotesync', 'mastersync', 'puppetsync'."); return; } @@ -4443,7 +4503,11 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { rpc_mode = MultiplayerAPI::RPC_MODE_MASTER; continue; } break; - case GDScriptTokenizer::TK_PR_SLAVE: { + case GDScriptTokenizer::TK_PR_SLAVE: +#ifdef DEBUG_ENABLED + _add_warning(GDScriptWarning::DEPRECATED_KEYWORD, tokenizer->get_token_line(), "slave", "puppet"); +#endif + case GDScriptTokenizer::TK_PR_PUPPET: { //may be fallthrough from export, ignore if so tokenizer->advance(); @@ -4460,7 +4524,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { } } - rpc_mode = MultiplayerAPI::RPC_MODE_SLAVE; + rpc_mode = MultiplayerAPI::RPC_MODE_PUPPET; continue; } break; case GDScriptTokenizer::TK_PR_REMOTESYNC: @@ -4476,7 +4540,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { return; } - rpc_mode = MultiplayerAPI::RPC_MODE_SYNC; + rpc_mode = MultiplayerAPI::RPC_MODE_REMOTESYNC; continue; } break; case GDScriptTokenizer::TK_PR_MASTERSYNC: { @@ -4494,7 +4558,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { rpc_mode = MultiplayerAPI::RPC_MODE_MASTERSYNC; continue; } break; - case GDScriptTokenizer::TK_PR_SLAVESYNC: { + case GDScriptTokenizer::TK_PR_PUPPETSYNC: { //may be fallthrough from export, ignore if so tokenizer->advance(); @@ -4506,13 +4570,14 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { return; } - rpc_mode = MultiplayerAPI::RPC_MODE_SLAVESYNC; + rpc_mode = MultiplayerAPI::RPC_MODE_PUPPETSYNC; continue; } break; case GDScriptTokenizer::TK_PR_VAR: { //variale declaration and (eventual) initialization ClassNode::Member member; + bool autoexport = tokenizer->get_token(-1) == GDScriptTokenizer::TK_PR_EXPORT; if (current_export.type != Variant::NIL) { member._export = current_export; @@ -4534,16 +4599,20 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { member.line = tokenizer->get_token_line(); member.usages = 0; member.rpc_mode = rpc_mode; +#ifdef TOOLS_ENABLED + Variant::CallError ce; + member.default_value = Variant::construct(member._export.type, NULL, 0, ce); +#endif if (current_class->constant_expressions.has(member.identifier)) { - _set_error("A constant named '" + String(member.identifier) + "' alread exists in this class (at line: " + + _set_error("A constant named '" + String(member.identifier) + "' already exists in this class (at line: " + itos(current_class->constant_expressions[member.identifier].expression->line) + ")."); return; } for (int i = 0; i < current_class->variables.size(); i++) { if (current_class->variables[i].identifier == member.identifier) { - _set_error("Variable '" + String(member.identifier) + "' alread exists in this class (at line: " + + _set_error("Variable '" + String(member.identifier) + "' already exists in this class (at line: " + itos(current_class->variables[i].line) + ")."); return; } @@ -4638,10 +4707,20 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { } } #ifdef TOOLS_ENABLED - if (subexpr->type == Node::TYPE_CONSTANT && member._export.type != Variant::NIL) { + if (subexpr->type == Node::TYPE_CONSTANT && (member._export.type != Variant::NIL || member.data_type.has_type)) { ConstantNode *cn = static_cast<ConstantNode *>(subexpr); if (cn->value.get_type() != Variant::NIL) { + if (member._export.type != Variant::NIL && cn->value.get_type() != member._export.type) { + if (Variant::can_convert(cn->value.get_type(), member._export.type)) { + Variant::CallError err; + const Variant *args = &cn->value; + cn->value = Variant::construct(member._export.type, &args, 1, err); + } else { + _set_error("Cannot convert the provided value to the export type."); + return; + } + } member.default_value = cn->value; } } @@ -4656,12 +4735,12 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { op->arguments.push_back(subexpr); #ifdef DEBUG_ENABLED - NewLineNode *nl = alloc_node<NewLineNode>(); - nl->line = line; + NewLineNode *nl2 = alloc_node<NewLineNode>(); + nl2->line = line; if (onready) - p_class->ready->statements.push_back(nl); + p_class->ready->statements.push_back(nl2); else - p_class->initializer->statements.push_back(nl); + p_class->initializer->statements.push_back(nl2); #endif if (onready) p_class->ready->statements.push_back(op); @@ -4676,6 +4755,25 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { _set_error("Type-less export needs a constant expression assigned to infer type."); return; } + + if (member._export.type != Variant::NIL) { + IdentifierNode *id = alloc_node<IdentifierNode>(); + id->name = member.identifier; + + ConstantNode *cn = alloc_node<ConstantNode>(); + + Variant::CallError ce2; + cn->value = Variant::construct(member._export.type, NULL, 0, ce2); + + OperatorNode *op = alloc_node<OperatorNode>(); + op->op = OperatorNode::OP_INIT_ASSIGN; + op->arguments.push_back(id); + op->arguments.push_back(cn); + + p_class->initializer->statements.push_back(op); + + member.initial_assignment = op; + } } if (autoexport && member.data_type.has_type) { @@ -4750,14 +4848,14 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { int line = tokenizer->get_token_line(); if (current_class->constant_expressions.has(const_id)) { - _set_error("Constant '" + String(const_id) + "' alread exists in this class (at line: " + + _set_error("Constant '" + String(const_id) + "' already exists in this class (at line: " + itos(current_class->constant_expressions[const_id].expression->line) + ")."); return; } for (int i = 0; i < current_class->variables.size(); i++) { if (current_class->variables[i].identifier == const_id) { - _set_error("A variable named '" + String(const_id) + "' alread exists in this class (at line: " + + _set_error("A variable named '" + String(const_id) + "' already exists in this class (at line: " + itos(current_class->variables[i].line) + ")."); return; } @@ -4811,13 +4909,28 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { case GDScriptTokenizer::TK_PR_ENUM: { //multiple constant declarations.. - int last_assign = -1; // Incremented by 1 right before the assingment. + int last_assign = -1; // Incremented by 1 right before the assignment. String enum_name; Dictionary enum_dict; tokenizer->advance(); if (tokenizer->is_token_literal(0, true)) { enum_name = tokenizer->get_token_literal(); + + if (current_class->constant_expressions.has(enum_name)) { + _set_error("A constant named '" + String(enum_name) + "' already exists in this class (at line: " + + itos(current_class->constant_expressions[enum_name].expression->line) + ")."); + return; + } + + for (int i = 0; i < current_class->variables.size(); i++) { + if (current_class->variables[i].identifier == enum_name) { + _set_error("A variable named '" + String(enum_name) + "' already exists in this class (at line: " + + itos(current_class->variables[i].line) + ")."); + return; + } + } + tokenizer->advance(); } if (tokenizer->get_token() != GDScriptTokenizer::TK_CURLY_BRACKET_OPEN) { @@ -4844,12 +4957,12 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { return; } else { // tokenizer->is_token_literal(0, true) - ClassNode::Constant constant; - StringName const_id = tokenizer->get_token_literal(); tokenizer->advance(); + ConstantNode *enum_value_expr; + if (tokenizer->get_token() == GDScriptTokenizer::TK_OP_ASSIGN) { tokenizer->advance(); @@ -4866,23 +4979,20 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { return; } - ConstantNode *subexpr_const = static_cast<ConstantNode *>(subexpr); + enum_value_expr = static_cast<ConstantNode *>(subexpr); - if (subexpr_const->value.get_type() != Variant::INT) { + if (enum_value_expr->value.get_type() != Variant::INT) { _set_error("Expected an int value for enum"); return; } - last_assign = subexpr_const->value; - - constant.expression = subexpr_const; + last_assign = enum_value_expr->value; } else { last_assign = last_assign + 1; - ConstantNode *cn = alloc_node<ConstantNode>(); - cn->value = last_assign; - cn->datatype = _type_from_variant(cn->value); - constant.expression = cn; + enum_value_expr = alloc_node<ConstantNode>(); + enum_value_expr->value = last_assign; + enum_value_expr->datatype = _type_from_variant(enum_value_expr->value); } if (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) { @@ -4890,14 +5000,29 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { } if (enum_name != "") { - const ConstantNode *cn = static_cast<const ConstantNode *>(constant.expression); - enum_dict[const_id] = cn->value; - } + enum_dict[const_id] = enum_value_expr->value; + } else { + if (current_class->constant_expressions.has(const_id)) { + _set_error("A constant named '" + String(const_id) + "' already exists in this class (at line: " + + itos(current_class->constant_expressions[const_id].expression->line) + ")."); + return; + } - constant.type.has_type = true; - constant.type.kind = DataType::BUILTIN; - constant.type.builtin_type = Variant::INT; - p_class->constant_expressions.insert(const_id, constant); + for (int i = 0; i < current_class->variables.size(); i++) { + if (current_class->variables[i].identifier == const_id) { + _set_error("A variable named '" + String(const_id) + "' already exists in this class (at line: " + + itos(current_class->variables[i].line) + ")."); + return; + } + } + + ClassNode::Constant constant; + constant.type.has_type = true; + constant.type.kind = DataType::BUILTIN; + constant.type.builtin_type = Variant::INT; + constant.expression = enum_value_expr; + p_class->constant_expressions.insert(const_id, constant); + } } } @@ -5006,7 +5131,7 @@ void GDScriptParser::_determine_inheritance(ClassNode *p_class) { if (ScriptServer::is_global_class(base)) { base_script = ResourceLoader::load(ScriptServer::get_global_class_path(base)); if (!base_script.is_valid()) { - _set_error("Class '" + base + "' could not be fully loaded (script error or cyclic inheritance).", p_class->line); + _set_error("Class '" + base + "' could not be fully loaded (script error or cyclic dependency).", p_class->line); return; } p = NULL; @@ -5046,7 +5171,7 @@ void GDScriptParser::_determine_inheritance(ClassNode *p_class) { if (found) continue; if (p->constant_expressions.has(base)) { - if (!p->constant_expressions[base].expression->type == Node::TYPE_CONSTANT) { + if (p->constant_expressions[base].expression->type != Node::TYPE_CONSTANT) { _set_error("Could not resolve constant '" + base + "'.", p_class->line); return; } @@ -5186,6 +5311,8 @@ String GDScriptParser::DataType::to_string() const { } return class_type->name.operator String(); } break; + case UNRESOLVED: { + } break; } return "Unresolved"; @@ -5327,13 +5454,13 @@ GDScriptParser::DataType GDScriptParser::_resolve_type(const DataType &p_source, String script_path = ScriptServer::get_global_class_path(id); if (script_path == self_path) { result.kind = DataType::CLASS; - result.class_type = current_class; + result.class_type = static_cast<ClassNode *>(head); } else { Ref<Script> script = ResourceLoader::load(script_path); Ref<GDScript> gds = script; if (gds.is_valid()) { if (!gds->is_valid()) { - _set_error("Class '" + id + "' could not be fully loaded (script error or cyclic inheritance).", p_line); + _set_error("Class '" + id + "' could not be fully loaded (script error or cyclic dependency).", p_line); return DataType(); } result.kind = DataType::GDSCRIPT; @@ -5380,6 +5507,12 @@ GDScriptParser::DataType GDScriptParser::_resolve_type(const DataType &p_source, // Inner classes ClassNode *outer_class = p; while (outer_class) { + if (outer_class->name == id) { + found = true; + result.kind = DataType::CLASS; + result.class_type = outer_class; + break; + } for (int i = 0; i < outer_class->subclasses.size(); i++) { if (outer_class->subclasses[i] == p) { continue; @@ -5420,10 +5553,10 @@ GDScriptParser::DataType GDScriptParser::_resolve_type(const DataType &p_source, result.script_type = gds; found = true; } else { - Ref<Script> scr = constants[id]; - if (scr.is_valid()) { + Ref<Script> scr2 = constants[id]; + if (scr2.is_valid()) { result.kind = DataType::SCRIPT; - result.script_type = scr; + result.script_type = scr2; found = true; } } @@ -5515,6 +5648,9 @@ GDScriptParser::DataType GDScriptParser::_type_from_gdtype(const GDScriptDataTyp result.script_type = p_gdtype.script_type; switch (p_gdtype.kind) { + case GDScriptDataType::UNINITIALIZED: { + ERR_EXPLAIN("Uninitialized datatype. Please report a bug."); + } break; case GDScriptDataType::BUILTIN: { result.kind = DataType::BUILTIN; } break; @@ -5692,18 +5828,23 @@ bool GDScriptParser::_is_type_compatible(const DataType &p_container, const Data if (p_container.kind == DataType::BUILTIN && p_expression.kind == DataType::BUILTIN) { bool valid = p_container.builtin_type == p_expression.builtin_type; if (p_allow_implicit_conversion) { - valid = valid || (p_container.builtin_type == Variant::INT && p_expression.builtin_type == Variant::REAL); - valid = valid || (p_container.builtin_type == Variant::REAL && p_expression.builtin_type == Variant::INT); - valid = valid || (p_container.builtin_type == Variant::STRING && p_expression.builtin_type == Variant::NODE_PATH); - valid = valid || (p_container.builtin_type == Variant::NODE_PATH && p_expression.builtin_type == Variant::STRING); - valid = valid || (p_container.builtin_type == Variant::BOOL && p_expression.builtin_type == Variant::REAL); - valid = valid || (p_container.builtin_type == Variant::BOOL && p_expression.builtin_type == Variant::INT); - valid = valid || (p_container.builtin_type == Variant::INT && p_expression.builtin_type == Variant::BOOL); - valid = valid || (p_container.builtin_type == Variant::REAL && p_expression.builtin_type == Variant::BOOL); + valid = valid || Variant::can_convert_strict(p_expression.builtin_type, p_container.builtin_type); } return valid; } + if (p_container.kind == DataType::BUILTIN && p_container.builtin_type == Variant::OBJECT) { + // Object built-in is a special case, it's compatible with any object and with null + if (p_expression.kind == DataType::BUILTIN && p_expression.builtin_type == Variant::NIL) { + return true; + } + if (p_expression.kind == DataType::BUILTIN) { + return false; + } + // If it's not a built-in, must be an object + return true; + } + if (p_container.kind == DataType::BUILTIN || (p_expression.kind == DataType::BUILTIN && p_expression.builtin_type != Variant::NIL)) { // Can't mix built-ins with objects return false; @@ -5758,7 +5899,10 @@ bool GDScriptParser::_is_type_compatible(const DataType &p_container, const Data expr_native = base->base_type.native_type; expr_script = base->base_type.script_type; } - } + } break; + case DataType::BUILTIN: // Already handled above + case DataType::UNRESOLVED: // Not allowed, see above + break; } switch (p_container.kind) { @@ -5801,7 +5945,10 @@ bool GDScriptParser::_is_type_compatible(const DataType &p_container, const Data expr_class = expr_class->base_type.class_type; } return false; - } + } break; + case DataType::BUILTIN: // Already handled above + case DataType::UNRESOLVED: // Not allowed, see above + break; } return false; @@ -5867,7 +6014,7 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) { int idx = current_function->arguments.find(id->name); node_type = current_function->argument_types[idx]; } else { - node_type = _reduce_identifier_type(NULL, id->name, id->line); + node_type = _reduce_identifier_type(NULL, id->name, id->line, false); } } break; case Node::TYPE_CAST: { @@ -6117,7 +6264,7 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) { result.is_constant = false; node_type = result; } else { - node_type = _reduce_identifier_type(&base_type, member_id->name, op->line); + node_type = _reduce_identifier_type(&base_type, member_id->name, op->line, true); #ifdef DEBUG_ENABLED if (!node_type.has_type) { _add_warning(GDScriptWarning::UNSAFE_PROPERTY_ACCESS, op->line, member_id->name.operator String(), base_type.to_string()); @@ -6159,7 +6306,7 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) { if (check_types && index_type.has_type) { if (base_type.kind == DataType::BUILTIN) { // Check if indexing is valid - bool error = index_type.kind != DataType::BUILTIN; + bool error = index_type.kind != DataType::BUILTIN && base_type.builtin_type != Variant::DICTIONARY; if (!error) { switch (base_type.builtin_type) { // Expect int or real as index @@ -6195,6 +6342,7 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) { case Variant::COLOR: { error = index_type.builtin_type != Variant::INT && index_type.builtin_type != Variant::STRING; } break; + default: {} } } if (error) { @@ -6312,6 +6460,7 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) { } } } break; + default: {} } p_node->set_datatype(_resolve_type(node_type, p_node->line)); @@ -6373,6 +6522,10 @@ bool GDScriptParser::_get_function_signature(DataType &p_base_type, const String StringName native; if (p_base_type.kind == DataType::GDSCRIPT) { base_gdscript = p_base_type.script_type; + if (base_gdscript.is_null() || !base_gdscript->is_valid()) { + // GDScript wasn't properly compíled, don't bother trying + return false; + } } else if (p_base_type.kind == DataType::SCRIPT) { base_script = p_base_type.script_type; } else if (p_base_type.kind == DataType::NATIVE) { @@ -6413,6 +6566,12 @@ bool GDScriptParser::_get_function_signature(DataType &p_base_type, const String base_script = base_script->get_base_script(); } + if (native == StringName()) { + // Empty native class, might happen in some Script implementations + // Just ignore it + return false; + } + #ifdef DEBUG_METHODS_ENABLED // Only native remains @@ -6517,7 +6676,7 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat bool match = false; List<MethodInfo> constructors; Variant::get_constructor_list(tn->vtype, &constructors); - PropertyInfo return_type; + PropertyInfo return_type2; for (List<MethodInfo>::Element *E = constructors.front(); E; E = E->next()) { MethodInfo &mi = E->get(); @@ -6556,13 +6715,13 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat if (types_match) { match = true; - return_type = mi.return_val; + return_type2 = mi.return_val; break; } } if (match) { - return _type_from_property(return_type, false); + return _type_from_property(return_type2, false); } else if (check_types) { String err = "No constructor of '"; err += Variant::get_type_name(tn->vtype); @@ -6664,9 +6823,15 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat } } + bool rets = false; return_type.has_type = true; return_type.kind = DataType::BUILTIN; - return_type.builtin_type = Variant::get_method_return_type(base_type.builtin_type, callee_name); + return_type.builtin_type = Variant::get_method_return_type(base_type.builtin_type, callee_name, &rets); + // If the method returns, but it might return any type, (Variant::NIL), pretend we don't know the type. + // At least make sure we know that it returns + if (rets && return_type.builtin_type == Variant::NIL) { + return_type.has_type = false; + } break; } @@ -6684,10 +6849,10 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat valid = _get_function_signature(base_type, callee_name, return_type, arg_types, default_args_count, is_static, is_vararg); - if (valid) { - return_type = original_type; - return_type.is_meta_type = false; - } + return_type = original_type; + return_type.is_meta_type = false; + + valid = true; // There's always an initializer, we can assume this is true } if (!valid) { @@ -6746,6 +6911,7 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat } break; } +#ifdef DEBUG_ENABLED if (!check_types) { return return_type; } @@ -6771,11 +6937,9 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat if (!par_type.has_type) { _mark_line_as_unsafe(p_call->line); -#ifdef DEBUG_ENABLED if (par_type.may_yield && p_call->arguments[i]->type == Node::TYPE_OPERATOR) { _add_warning(GDScriptWarning::FUNCTION_MAY_YIELD, p_call->line, _find_function_name(static_cast<OperatorNode *>(p_call->arguments[i]))); } -#endif // DEBUG_ENABLED } else if (!_is_type_compatible(arg_types[i - arg_diff], par_type, true)) { // Supertypes are acceptable for dynamic compliance if (!_is_type_compatible(par_type, arg_types[i - arg_diff])) { @@ -6788,14 +6952,14 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat _mark_line_as_unsafe(p_call->line); } } else { -#ifdef DEBUG_ENABLED if (arg_type.kind == DataType::BUILTIN && arg_type.builtin_type == Variant::INT && par_type.kind == DataType::BUILTIN && par_type.builtin_type == Variant::REAL) { _add_warning(GDScriptWarning::NARROWING_CONVERSION, p_call->line, callee_name); } -#endif // DEBUG_ENABLED } } +#endif // DEBUG_ENABLED + return return_type; } @@ -6816,9 +6980,9 @@ bool GDScriptParser::_get_member_type(const DataType &p_base_type, const StringN if (!base_type.is_meta_type) { for (int i = 0; i < base->variables.size(); i++) { - ClassNode::Member m = base->variables[i]; - if (m.identifier == p_member) { - r_member_type = m.data_type; + if (base->variables[i].identifier == p_member) { + r_member_type = base->variables[i].data_type; + base->variables.write[i].usages += 1; return true; } } @@ -6849,6 +7013,10 @@ bool GDScriptParser::_get_member_type(const DataType &p_base_type, const StringN Ref<GDScript> gds; if (base_type.kind == DataType::GDSCRIPT) { gds = base_type.script_type; + if (gds.is_null() || !gds->is_valid()) { + // GDScript wasn't properly compíled, don't bother trying + return false; + } } Ref<Script> scr; @@ -6911,6 +7079,12 @@ bool GDScriptParser::_get_member_type(const DataType &p_base_type, const StringN scr = scr->get_base_script(); } + if (native == StringName()) { + // Empty native class, might happen in some Script implementations + // Just ignore it + return false; + } + // Check ClassDB if (!ClassDB::class_exists(native)) { native = "_" + native.operator String(); @@ -7003,44 +7177,33 @@ bool GDScriptParser::_get_member_type(const DataType &p_base_type, const StringN return false; } -GDScriptParser::DataType GDScriptParser::_reduce_identifier_type(const DataType *p_base_type, const StringName &p_identifier, int p_line) { +GDScriptParser::DataType GDScriptParser::_reduce_identifier_type(const DataType *p_base_type, const StringName &p_identifier, int p_line, bool p_is_indexing) { if (p_base_type && !p_base_type->has_type) { return DataType(); } DataType base_type; + DataType member_type; - // Check classes in current file - ClassNode *base = NULL; if (!p_base_type) { - base = current_class; base_type.has_type = true; base_type.is_constant = true; base_type.kind = DataType::CLASS; - base_type.class_type = base; + base_type.class_type = current_class; } else { base_type = DataType(*p_base_type); - if (base_type.kind == DataType::CLASS) { - base = base_type.class_type; - } - } - - DataType member_type; - - for (int i = 0; i < current_class->variables.size(); i++) { - ClassNode::Member m = current_class->variables[i]; - if (current_class->variables[i].identifier == p_identifier) { - member_type = current_class->variables[i].data_type; - current_class->variables.write[i].usages += 1; - return member_type; - } } if (_get_member_type(base_type, p_identifier, member_type)) { return member_type; } + if (p_is_indexing) { + // Don't look for globals since this is an indexed identifier + return DataType(); + } + if (!p_base_type) { // Possibly this is a global, check before failing @@ -7094,11 +7257,12 @@ GDScriptParser::DataType GDScriptParser::_reduce_identifier_type(const DataType DataType result; result.has_type = true; result.script_type = scr; + result.is_constant = true; result.is_meta_type = true; Ref<GDScript> gds = scr; if (gds.is_valid()) { if (!gds->is_valid()) { - _set_error("Class '" + p_identifier + "' could not be fully loaded (script error or cyclic inheritance)."); + _set_error("Class '" + p_identifier + "' could not be fully loaded (script error or cyclic dependency)."); return DataType(); } result.kind = DataType::GDSCRIPT; @@ -7144,6 +7308,7 @@ GDScriptParser::DataType GDScriptParser::_reduce_identifier_type(const DataType if (singleton.is_valid()) { DataType result; result.has_type = true; + result.is_constant = true; result.script_type = singleton; Ref<GDScript> gds = singleton; @@ -7192,7 +7357,7 @@ void GDScriptParser::_check_class_level_types(ClassNode *p_class) { DataType cont = _resolve_type(c.type, c.expression->line); DataType expr = _resolve_type(c.expression->get_datatype(), c.expression->line); - if (!_is_type_compatible(cont, expr)) { + if (check_types && !_is_type_compatible(cont, expr)) { _set_error("Constant value type (" + expr.to_string() + ") is not compatible with declared type (" + cont.to_string() + ").", c.expression->line); return; @@ -7201,6 +7366,12 @@ void GDScriptParser::_check_class_level_types(ClassNode *p_class) { expr.is_constant = true; c.type = expr; c.expression->set_datatype(expr); + + DataType tmp; + if (_get_member_type(p_class->base_type, E->key(), tmp)) { + _set_error("Member '" + String(E->key()) + "' already exists in parent class.", c.expression->line); + return; + } } // Function declarations @@ -7230,7 +7401,7 @@ void GDScriptParser::_check_class_level_types(ClassNode *p_class) { if (v.expression) { DataType expr_type = _reduce_node_type(v.expression); - if (!_is_type_compatible(v.data_type, expr_type)) { + if (check_types && !_is_type_compatible(v.data_type, expr_type)) { // Try supertype test if (_is_type_compatible(expr_type, v.data_type)) { _mark_line_as_unsafe(v.line); @@ -7243,7 +7414,7 @@ void GDScriptParser::_check_class_level_types(ClassNode *p_class) { return; } - // Replace assigment with implict conversion + // Replace assignment with implict conversion BuiltInFunctionNode *convert = alloc_node<BuiltInFunctionNode>(); convert->line = v.line; convert->function = GDScriptFunctions::TYPE_CONVERT; @@ -7319,7 +7490,8 @@ void GDScriptParser::_check_class_level_types(ClassNode *p_class) { found_setter = true; FunctionNode *setter = p_class->functions[j]; - if (setter->arguments.size() != 1) { + if (setter->get_required_argument_count() != 1 && + !(setter->get_required_argument_count() == 0 && setter->default_values.size() > 0)) { _set_error("Setter function needs to receive exactly 1 argument. See '" + setter->name + "()' definition at line " + itos(setter->line) + ".", v.line); @@ -7338,7 +7510,7 @@ void GDScriptParser::_check_class_level_types(ClassNode *p_class) { found_getter = true; FunctionNode *getter = p_class->functions[j]; - if (getter->arguments.size() != 0) { + if (getter->get_required_argument_count() != 0) { _set_error("Getter function can't receive arguments. See '" + getter->name + "()' definition at line " + itos(getter->line) + ".", v.line); @@ -7464,7 +7636,7 @@ void GDScriptParser::_check_function_types(FunctionNode *p_function) { } parent_signature += " " + p_function->name + "("; if (arg_types.size()) { - int i = 0; + int j = 0; for (List<DataType>::Element *E = arg_types.front(); E; E = E->next()) { if (E != arg_types.front()) { parent_signature += ", "; @@ -7474,11 +7646,11 @@ void GDScriptParser::_check_function_types(FunctionNode *p_function) { arg = "Variant"; } parent_signature += arg; - if (i == arg_types.size() - default_arg_count) { + if (j == arg_types.size() - default_arg_count) { parent_signature += "=default"; } - i++; + j++; } } parent_signature += ")"; @@ -7621,7 +7793,7 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { lv->line); return; } - // Replace assigment with implict conversion + // Replace assignment with implict conversion BuiltInFunctionNode *convert = alloc_node<BuiltInFunctionNode>(); convert->line = lv->line; convert->function = GDScriptFunctions::TYPE_CONVERT; @@ -7692,13 +7864,16 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { return; } - if (!lh_type.has_type) { - if (op->arguments[0]->type == Node::TYPE_OPERATOR) { - _mark_line_as_unsafe(op->line); + if (check_types) { + if (!lh_type.has_type) { + if (op->arguments[0]->type == Node::TYPE_OPERATOR) { + _mark_line_as_unsafe(op->line); + } + } + if (lh_type.is_constant) { + _set_error("Cannot assign a new value to a constant.", op->line); + return; } - } else if (lh_type.is_constant) { - _set_error("Cannot assign a new value to a constant.", op->line); - return; } DataType rh_type; @@ -7714,7 +7889,7 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { bool valid = false; rh_type = _get_operation_type(oper, lh_type, arg_type, valid); - if (!valid) { + if (check_types && !valid) { _set_error("Invalid operand types ('" + lh_type.to_string() + "' and '" + arg_type.to_string() + "') to assignment operator '" + Variant::get_operator_name(oper) + "'.", op->line); @@ -7737,7 +7912,7 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { } #endif // DEBUG_ENABLED - if (!_is_type_compatible(lh_type, rh_type)) { + if (check_types && !_is_type_compatible(lh_type, rh_type)) { // Try supertype test if (_is_type_compatible(rh_type, lh_type)) { _mark_line_as_unsafe(op->line); @@ -7749,7 +7924,7 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { op->line); return; } - // Replace assigment with implict conversion + // Replace assignment with implict conversion BuiltInFunctionNode *convert = alloc_node<BuiltInFunctionNode>(); convert->line = op->line; convert->function = GDScriptFunctions::TYPE_CONVERT; @@ -7773,9 +7948,11 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { #endif // DEBUG_ENABLED } } +#ifdef DEBUG_ENABLED if (!rh_type.has_type && (op->op != OperatorNode::OP_ASSIGN || lh_type.has_type || op->arguments[0]->type == Node::TYPE_OPERATOR)) { _mark_line_as_unsafe(op->line); } +#endif // DEBUG_ENABLED } break; case OperatorNode::OP_CALL: case OperatorNode::OP_PARENT_CALL: { @@ -7786,7 +7963,7 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { // Figure out function name for warning String func_name = _find_function_name(op); if (func_name.empty()) { - func_name == "<undetected name>"; + func_name = "<undetected name>"; } _add_warning(GDScriptWarning::RETURN_VALUE_DISCARDED, op->line, func_name); } @@ -7997,6 +8174,10 @@ Error GDScriptParser::_parse(const String &p_base_path) { return ERR_PARSE_ERROR; } + if (dependencies_only) { + return OK; + } + _determine_inheritance(main_class); if (error_set) { @@ -8027,6 +8208,7 @@ Error GDScriptParser::_parse(const String &p_base_path) { } #ifdef DEBUG_ENABLED + // Resolve warning ignores Vector<Pair<int, String> > warning_skips = tokenizer->get_warning_skips(); bool warning_is_error = GLOBAL_GET("debug/gdscript/warnings/treat_warnings_as_errors").booleanize(); @@ -8074,7 +8256,7 @@ Error GDScriptParser::parse_bytecode(const Vector<uint8_t> &p_bytecode, const St return ret; } -Error GDScriptParser::parse(const String &p_code, const String &p_base_path, bool p_just_validate, const String &p_self_path, bool p_for_completion, Set<int> *r_safe_lines) { +Error GDScriptParser::parse(const String &p_code, const String &p_base_path, bool p_just_validate, const String &p_self_path, bool p_for_completion, Set<int> *r_safe_lines, bool p_dependencies_only) { clear(); @@ -8084,6 +8266,7 @@ Error GDScriptParser::parse(const String &p_code, const String &p_base_path, boo validating = p_just_validate; for_completion = p_for_completion; + dependencies_only = p_dependencies_only; #ifdef DEBUG_ENABLED safe_lines = r_safe_lines; #endif // DEBUG_ENABLED @@ -8140,6 +8323,8 @@ void GDScriptParser::clear() { parenthesis = 0; current_export.type = Variant::NIL; check_types = true; + dependencies_only = false; + dependencies.clear(); error = ""; #ifdef DEBUG_ENABLED safe_lines = NULL; diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index 3c51e3f372..809bff8f20 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,11 +31,11 @@ #ifndef GDSCRIPT_PARSER_H #define GDSCRIPT_PARSER_H +#include "core/map.h" +#include "core/object.h" +#include "core/script_language.h" #include "gdscript_functions.h" #include "gdscript_tokenizer.h" -#include "map.h" -#include "object.h" -#include "script_language.h" struct GDScriptDataType; struct GDScriptWarning; @@ -88,11 +88,14 @@ public: case CLASS: { return class_type == other.class_type; } break; + case UNRESOLVED: { + } break; } return false; } DataType() : + kind(UNRESOLVED), has_type(false), is_constant(false), is_meta_type(false), @@ -166,6 +169,7 @@ public: MultiplayerAPI::RPCMode rpc_mode; int usages; }; + struct Constant { Node *expression; DataType type; @@ -217,6 +221,7 @@ public: virtual DataType get_datatype() const { return return_type; } virtual void set_datatype(const DataType &p_datatype) { return_type = p_datatype; } + int get_required_argument_count() { return arguments.size() - default_values.size(); } FunctionNode() { type = TYPE_FUNCTION; @@ -441,7 +446,6 @@ public: CF_IF, CF_FOR, CF_WHILE, - CF_SWITCH, CF_BREAK, CF_CONTINUE, CF_RETURN, @@ -529,6 +533,8 @@ private: int error_line; int error_column; bool check_types; + bool dependencies_only; + List<String> dependencies; #ifdef DEBUG_ENABLED Set<int> *safe_lines; #endif // DEBUG_ENABLED @@ -553,7 +559,6 @@ private: CompletionType completion_type; StringName completion_cursor; - bool completion_static; Variant::Type completion_built_in_constant; Node *completion_node; ClassNode *completion_class; @@ -606,7 +611,7 @@ private: DataType _reduce_node_type(Node *p_node); DataType _reduce_function_call_type(const OperatorNode *p_call); - DataType _reduce_identifier_type(const DataType *p_base_type, const StringName &p_identifier, int p_line); + DataType _reduce_identifier_type(const DataType *p_base_type, const StringName &p_identifier, int p_line, bool p_is_indexing); void _check_class_level_types(ClassNode *p_class); void _check_class_blocks_types(ClassNode *p_class); void _check_function_types(FunctionNode *p_function); @@ -631,7 +636,7 @@ public: #ifdef DEBUG_ENABLED const List<GDScriptWarning> &get_warnings() const { return warnings; } #endif // DEBUG_ENABLED - 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, Set<int> *r_safe_lines = NULL); + 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, Set<int> *r_safe_lines = NULL, bool p_dependencies_only = false); Error parse_bytecode(const Vector<uint8_t> &p_bytecode, const String &p_base_path = "", const String &p_self_path = ""); bool is_tool_script() const; @@ -650,6 +655,8 @@ public: int get_completion_argument_index(); int get_completion_identifier_is_function(); + const List<String> &get_dependencies() const { return dependencies; } + void clear(); GDScriptParser(); ~GDScriptParser(); diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp index 6e7842f190..8b22d6f085 100644 --- a/modules/gdscript/gdscript_tokenizer.cpp +++ b/modules/gdscript/gdscript_tokenizer.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,10 +30,10 @@ #include "gdscript_tokenizer.h" +#include "core/io/marshalls.h" +#include "core/map.h" +#include "core/print_string.h" #include "gdscript_functions.h" -#include "io/marshalls.h" -#include "map.h" -#include "print_string.h" const char *GDScriptTokenizer::token_names[TK_MAX] = { "Empty", @@ -80,10 +80,7 @@ const char *GDScriptTokenizer::token_names[TK_MAX] = { "elif", "else", "for", - "do", "while", - "switch (reserved)", - "case (reserved)", "break", "continue", "pass", @@ -112,10 +109,11 @@ const char *GDScriptTokenizer::token_names[TK_MAX] = { "rpc", "sync", "master", + "puppet", "slave", "remotesync", "mastersync", - "slavesync", + "puppetsync", "'['", "']'", "'{'", @@ -127,8 +125,8 @@ const char *GDScriptTokenizer::token_names[TK_MAX] = { "'.'", "'?'", "':'", - "'->'", "'$'", + "'->'", "'\\n'", "PI", "TAU", @@ -210,10 +208,11 @@ static const _kws _keyword_list[] = { { GDScriptTokenizer::TK_PR_REMOTE, "remote" }, { GDScriptTokenizer::TK_PR_MASTER, "master" }, { GDScriptTokenizer::TK_PR_SLAVE, "slave" }, + { GDScriptTokenizer::TK_PR_PUPPET, "puppet" }, { GDScriptTokenizer::TK_PR_SYNC, "sync" }, { GDScriptTokenizer::TK_PR_REMOTESYNC, "remotesync" }, { GDScriptTokenizer::TK_PR_MASTERSYNC, "mastersync" }, - { GDScriptTokenizer::TK_PR_SLAVESYNC, "slavesync" }, + { GDScriptTokenizer::TK_PR_PUPPETSYNC, "puppetsync" }, { GDScriptTokenizer::TK_PR_CONST, "const" }, { GDScriptTokenizer::TK_PR_ENUM, "enum" }, //controlflow @@ -222,9 +221,6 @@ static const _kws _keyword_list[] = { { GDScriptTokenizer::TK_CF_ELSE, "else" }, { GDScriptTokenizer::TK_CF_FOR, "for" }, { GDScriptTokenizer::TK_CF_WHILE, "while" }, - { GDScriptTokenizer::TK_CF_DO, "do" }, - { GDScriptTokenizer::TK_CF_SWITCH, "switch" }, - { GDScriptTokenizer::TK_CF_CASE, "case" }, { GDScriptTokenizer::TK_CF_BREAK, "break" }, { GDScriptTokenizer::TK_CF_CONTINUE, "continue" }, { GDScriptTokenizer::TK_CF_RETURN, "return" }, @@ -258,11 +254,11 @@ bool GDScriptTokenizer::is_token_literal(int p_offset, bool variable_safe) const case TK_PR_SIGNAL: case TK_PR_REMOTE: case TK_PR_MASTER: - case TK_PR_SLAVE: + case TK_PR_PUPPET: case TK_PR_SYNC: case TK_PR_REMOTESYNC: case TK_PR_MASTERSYNC: - case TK_PR_SLAVESYNC: + case TK_PR_PUPPETSYNC: return true; // Literal for non-variables only: @@ -289,9 +285,6 @@ bool GDScriptTokenizer::is_token_literal(int p_offset, bool variable_safe) const case TK_CF_ELSE: case TK_CF_FOR: case TK_CF_WHILE: - case TK_CF_DO: - case TK_CF_SWITCH: - case TK_CF_CASE: case TK_CF_BREAK: case TK_CF_CONTINUE: case TK_CF_RETURN: @@ -996,11 +989,11 @@ void GDScriptTokenizerText::_advance() { //built in func? - for (int i = 0; i < GDScriptFunctions::FUNC_MAX; i++) { + for (int j = 0; j < GDScriptFunctions::FUNC_MAX; j++) { - if (str == GDScriptFunctions::get_func_name(GDScriptFunctions::Function(i))) { + if (str == GDScriptFunctions::get_func_name(GDScriptFunctions::Function(j))) { - _make_built_in_func(GDScriptFunctions::Function(i)); + _make_built_in_func(GDScriptFunctions::Function(j)); found = true; break; } @@ -1424,7 +1417,7 @@ StringName GDScriptTokenizerBuffer::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, (uint32_t)identifiers.size(), StringName()); + ERR_FAIL_UNSIGNED_INDEX_V(identifier, (uint32_t)identifiers.size(), StringName()); return identifiers[identifier]; } @@ -1480,7 +1473,7 @@ const Variant &GDScriptTokenizerBuffer::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, (uint32_t)constants.size(), nil); + ERR_FAIL_UNSIGNED_INDEX_V(constant, (uint32_t)constants.size(), nil); return constants[constant]; } String GDScriptTokenizerBuffer::get_token_error(int p_offset) const { diff --git a/modules/gdscript/gdscript_tokenizer.h b/modules/gdscript/gdscript_tokenizer.h index 11a291cb2e..7b977ff67c 100644 --- a/modules/gdscript/gdscript_tokenizer.h +++ b/modules/gdscript/gdscript_tokenizer.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,11 +32,11 @@ #define GDSCRIPT_TOKENIZER_H #include "core/pair.h" +#include "core/string_name.h" +#include "core/ustring.h" +#include "core/variant.h" +#include "core/vmap.h" #include "gdscript_functions.h" -#include "string_db.h" -#include "ustring.h" -#include "variant.h" -#include "vmap.h" class GDScriptTokenizer { public: @@ -86,10 +86,7 @@ public: TK_CF_ELIF, TK_CF_ELSE, TK_CF_FOR, - TK_CF_DO, TK_CF_WHILE, - TK_CF_SWITCH, - TK_CF_CASE, TK_CF_BREAK, TK_CF_CONTINUE, TK_CF_PASS, @@ -118,10 +115,11 @@ public: TK_PR_REMOTE, TK_PR_SYNC, TK_PR_MASTER, - TK_PR_SLAVE, + TK_PR_SLAVE, // Deprecated by TK_PR_PUPPET, to remove in 4.0 + TK_PR_PUPPET, TK_PR_REMOTESYNC, TK_PR_MASTERSYNC, - TK_PR_SLAVESYNC, + TK_PR_PUPPETSYNC, TK_BRACKET_OPEN, TK_BRACKET_CLOSE, TK_CURLY_BRACKET_OPEN, @@ -175,7 +173,7 @@ public: #ifdef DEBUG_ENABLED virtual const Vector<Pair<int, String> > &get_warning_skips() const = 0; virtual const Set<String> &get_warning_global_skips() const = 0; - virtual const bool is_ignoring_warnings() const = 0; + virtual bool is_ignoring_warnings() const = 0; #endif // DEBUG_ENABLED virtual ~GDScriptTokenizer(){}; @@ -247,7 +245,7 @@ public: #ifdef DEBUG_ENABLED virtual const Vector<Pair<int, String> > &get_warning_skips() const { return warning_skips; } virtual const Set<String> &get_warning_global_skips() const { return warning_global_skips; } - virtual const bool is_ignoring_warnings() const { return ignore_warnings; } + virtual bool is_ignoring_warnings() const { return ignore_warnings; } #endif // DEBUG_ENABLED }; @@ -291,7 +289,7 @@ public: static Set<String> s; return s; } - virtual const bool is_ignoring_warnings() const { return true; } + virtual bool is_ignoring_warnings() const { return true; } #endif // DEBUG_ENABLED GDScriptTokenizerBuffer(); }; diff --git a/modules/gdscript/register_types.cpp b/modules/gdscript/register_types.cpp index 422223370b..b8a13ed91b 100644 --- a/modules/gdscript/register_types.cpp +++ b/modules/gdscript/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,16 +30,16 @@ #include "register_types.h" +#include "core/io/file_access_encrypted.h" +#include "core/io/resource_loader.h" +#include "core/os/file_access.h" #include "editor/gdscript_highlighter.h" #include "gdscript.h" #include "gdscript_tokenizer.h" -#include "io/file_access_encrypted.h" -#include "io/resource_loader.h" -#include "os/file_access.h" GDScriptLanguage *script_language_gd = NULL; -ResourceFormatLoaderGDScript *resource_loader_gd = NULL; -ResourceFormatSaverGDScript *resource_saver_gd = NULL; +Ref<ResourceFormatLoaderGDScript> resource_loader_gd; +Ref<ResourceFormatSaverGDScript> resource_saver_gd; #ifdef TOOLS_ENABLED @@ -54,20 +54,74 @@ class EditorExportGDScript : public EditorExportPlugin { public: virtual void _export_file(const String &p_path, const String &p_type, const Set<String> &p_features) { - if (!p_path.ends_with(".gd")) + int script_mode = EditorExportPreset::MODE_SCRIPT_COMPILED; + String script_key; + + const Ref<EditorExportPreset> &preset = get_export_preset(); + + if (preset.is_valid()) { + script_mode = preset->get_script_export_mode(); + script_key = preset->get_script_encryption_key().to_lower(); + } + + if (!p_path.ends_with(".gd") || script_mode == EditorExportPreset::MODE_SCRIPT_TEXT) return; Vector<uint8_t> file = FileAccess::get_file_as_array(p_path); if (file.empty()) return; + String txt; txt.parse_utf8((const char *)file.ptr(), file.size()); file = GDScriptTokenizerBuffer::parse_code_string(txt); - if (file.empty()) - return; - - add_file(p_path.get_basename() + ".gdc", file, true); + if (!file.empty()) { + + if (script_mode == EditorExportPreset::MODE_SCRIPT_ENCRYPTED) { + + String tmp_path = EditorSettings::get_singleton()->get_cache_dir().plus_file("script.gde"); + FileAccess *fa = FileAccess::open(tmp_path, FileAccess::WRITE); + + Vector<uint8_t> key; + key.resize(32); + for (int i = 0; i < 32; i++) { + int v = 0; + if (i * 2 < script_key.length()) { + CharType ct = script_key[i * 2]; + if (ct >= '0' && ct <= '9') + ct = ct - '0'; + else if (ct >= 'a' && ct <= 'f') + ct = 10 + ct - 'a'; + v |= ct << 4; + } + + if (i * 2 + 1 < script_key.length()) { + CharType ct = script_key[i * 2 + 1]; + if (ct >= '0' && ct <= '9') + ct = ct - '0'; + else if (ct >= 'a' && ct <= 'f') + ct = 10 + ct - 'a'; + v |= ct; + } + key.write[i] = v; + } + FileAccessEncrypted *fae = memnew(FileAccessEncrypted); + Error err = fae->open_and_parse(fa, key, FileAccessEncrypted::MODE_WRITE_AES256); + + if (err == OK) { + fae->store_buffer(file.ptr(), file.size()); + } + + memdelete(fae); + + file = FileAccess::get_file_as_array(tmp_path); + add_file(p_path.get_basename() + ".gde", file, true); + + } else { + + add_file(p_path.get_basename() + ".gdc", file, true); + } + } } }; @@ -87,9 +141,11 @@ void register_gdscript_types() { script_language_gd = memnew(GDScriptLanguage); ScriptServer::register_language(script_language_gd); - resource_loader_gd = memnew(ResourceFormatLoaderGDScript); + + resource_loader_gd.instance(); ResourceLoader::add_resource_format_loader(resource_loader_gd); - resource_saver_gd = memnew(ResourceFormatSaverGDScript); + + resource_saver_gd.instance(); ResourceSaver::add_resource_format_saver(resource_saver_gd); #ifdef TOOLS_ENABLED @@ -104,8 +160,10 @@ void unregister_gdscript_types() { if (script_language_gd) memdelete(script_language_gd); - if (resource_loader_gd) - memdelete(resource_loader_gd); - if (resource_saver_gd) - memdelete(resource_saver_gd); + + ResourceLoader::remove_resource_format_loader(resource_loader_gd); + resource_loader_gd.unref(); + + ResourceSaver::remove_resource_format_saver(resource_saver_gd); + resource_saver_gd.unref(); } diff --git a/modules/gdscript/register_types.h b/modules/gdscript/register_types.h index 2b88a67c7e..0f01a4cdab 100644 --- a/modules/gdscript/register_types.h +++ b/modules/gdscript/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ |