diff options
Diffstat (limited to 'modules/gdscript')
20 files changed, 867 insertions, 538 deletions
diff --git a/modules/gdscript/doc_classes/GDScript.xml b/modules/gdscript/doc_classes/GDScript.xml index 4cefdbd7cb..d606a41fab 100644 --- a/modules/gdscript/doc_classes/GDScript.xml +++ b/modules/gdscript/doc_classes/GDScript.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GDScript" inherits="Script" category="Core" version="3.1"> +<class name="GDScript" inherits="Script" category="Core" version="3.2"> <brief_description> A script implemented in the GDScript programming language. </brief_description> @@ -10,8 +10,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/getting_started/scripting/gdscript/index.html</link> </tutorials> - <demos> - </demos> <methods> <method name="get_as_byte_code" qualifiers="const"> <return type="PoolByteArray"> diff --git a/modules/gdscript/doc_classes/GDScriptFunctionState.xml b/modules/gdscript/doc_classes/GDScriptFunctionState.xml index c205cedef5..690953108f 100644 --- a/modules/gdscript/doc_classes/GDScriptFunctionState.xml +++ b/modules/gdscript/doc_classes/GDScriptFunctionState.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GDScriptFunctionState" inherits="Reference" category="Core" version="3.1"> +<class name="GDScriptFunctionState" inherits="Reference" category="Core" version="3.2"> <brief_description> State of a function call after yielding. </brief_description> @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="is_valid" qualifiers="const"> <return type="bool"> diff --git a/modules/gdscript/doc_classes/GDScriptNativeClass.xml b/modules/gdscript/doc_classes/GDScriptNativeClass.xml index 90935b5c22..70583d47a7 100644 --- a/modules/gdscript/doc_classes/GDScriptNativeClass.xml +++ b/modules/gdscript/doc_classes/GDScriptNativeClass.xml @@ -1,13 +1,11 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GDScriptNativeClass" inherits="Reference" category="Core" version="3.1"> +<class name="GDScriptNativeClass" inherits="Reference" category="Core" version="3.2"> <brief_description> </brief_description> <description> </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="new"> <return type="Variant"> diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp index e4aee842ba..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 */ 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 538249c8e2..eada389c51 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 */ @@ -152,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) { @@ -223,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; } } @@ -475,20 +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); - } - return false; - } - } else { - if (!valid) { - for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) { - E->get()->set_build_failed(true); - } + placeholder_fallback_enabled = true; return false; } + } else if (placeholder_fallback_enabled) { + return false; } + placeholder_fallback_enabled = false; + if (base_cache.is_valid()) { if (base_cache->_update_exports()) { changed = true; @@ -503,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); } } @@ -597,7 +597,7 @@ Error GDScript::reload(bool p_keep_state) { return err; } } -#if DEBUG_ENABLED +#ifdef DEBUG_ENABLED for (const List<GDScriptWarning>::Element *E = parser.get_warnings().front(); E; E = E->next()) { const GDScriptWarning &warning = E->get(); if (ScriptDebugger::get_singleton()) { @@ -648,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); @@ -907,6 +908,7 @@ GDScript::GDScript() : tool = false; #ifdef TOOLS_ENABLED source_changed_cache = false; + placeholder_fallback_enabled = false; #endif #ifdef DEBUG_ENABLED @@ -1107,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); } @@ -1675,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; @@ -1684,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 @@ -1821,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(); } - } break; - default: { - base_type = GDScriptParser::DataType(); - } break; + + if (OK != subparser.parse(subsource, subpath.get_base_dir(), true, subpath, false, NULL, true)) { + 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; } @@ -1912,6 +1945,10 @@ String GDScriptWarning::get_message() const { CHECK_SYMBOLS(1); return "The local variable '" + symbols[0] + "' is declared but never used in the block."; } break; + case SHADOWED_VARIABLE: { + CHECK_SYMBOLS(2); + return "The local variable '" + symbols[0] + "' is shadowing an already defined variable at line " + symbols[1] + "."; + } break; case UNUSED_CLASS_VARIABLE: { CHECK_SYMBOLS(1); return "The class variable '" + symbols[0] + "' is declared but never used in the script."; @@ -1932,7 +1969,7 @@ String GDScriptWarning::get_message() const { return "Assignment operation, but the function '" + symbols[0] + "()' returns void."; } break; case NARROWING_CONVERSION: { - return "Narrowing coversion (float is converted to int and lose precision)."; + return "Narrowing conversion (float is converted to int and loses precision)."; } break; case FUNCTION_MAY_YIELD: { CHECK_SYMBOLS(1); @@ -2015,6 +2052,7 @@ String GDScriptWarning::get_name_from_code(Code p_code) { "UNASSIGNED_VARIABLE", "UNASSIGNED_VARIABLE_OP_ASSIGN", "UNUSED_VARIABLE", + "SHADOWED_VARIABLE", "UNUSED_CLASS_VARIABLE", "UNUSED_ARGUMENT", "UNREACHABLE_CODE", @@ -2169,6 +2207,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 752d660ffb..1d75d9e2fe 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 */ @@ -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 @@ -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(); }; @@ -268,6 +273,7 @@ struct GDScriptWarning { UNASSIGNED_VARIABLE, // Variable used but never assigned UNASSIGNED_VARIABLE_OP_ASSIGN, // Variable never assigned but used in an assignment operation (+=, *=, etc) UNUSED_VARIABLE, // Local variable is declared but never used + SHADOWED_VARIABLE, // Variable name shadowed by other variable UNUSED_CLASS_VARIABLE, // Class variable is declared but never used in the file UNUSED_ARGUMENT, // Function argument is never used UNREACHABLE_CODE, // Code after a return statement @@ -439,6 +445,7 @@ public: virtual void get_reserved_words(List<String> *p_words) const; virtual void get_comment_delimiters(List<String> *p_delimiters) const; virtual void get_string_delimiters(List<String> *p_delimiters) const; + virtual String _get_processed_template(const String &p_template, const String &p_base_class_name) const; virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const; virtual bool is_using_templates(); virtual void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script); @@ -500,14 +507,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 caa7fbfeca..f7be0ce37c 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 */ @@ -139,17 +139,32 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D result.native_type = result.script_type->get_instance_base_type(); } break; case GDScriptParser::DataType::CLASS: { - result.kind = GDScriptDataType::GDSCRIPT; - if (!p_datatype.class_type->owner) { - result.script_type = Ref<GDScript>(main_script); - } else { - result.script_type = class_map[p_datatype.class_type->name]; + // Locate class by constructing the path to it and following that path + GDScriptParser::ClassNode *class_type = p_datatype.class_type; + List<StringName> names; + while (class_type->owner) { + names.push_back(class_type->name); + class_type = class_type->owner; } - result.native_type = result.script_type->get_instance_base_type(); + + Ref<GDScript> script = Ref<GDScript>(main_script); + while (names.back()) { + if (!script->subclasses.has(names.back()->get())) { + ERR_PRINT("Parser bug: Cannot locate datatype class."); + result.has_type = false; + return GDScriptDataType(); + } + script = script->subclasses[names.back()->get()]; + names.pop_back(); + } + + result.kind = GDScriptDataType::GDSCRIPT; + result.script_type = script; + result.native_type = script->get_instance_base_type(); } break; default: { ERR_PRINT("Parser bug: converting unresolved type."); - result.has_type = false; + return GDScriptDataType(); } } @@ -460,52 +475,30 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: codegen.alloc_stack(slevel); } - switch (cn->cast_type.kind) { - case GDScriptParser::DataType::BUILTIN: { + GDScriptDataType cast_type = _gdtype_from_datatype(cn->cast_type); + + switch (cast_type.kind) { + case GDScriptDataType::BUILTIN: { codegen.opcodes.push_back(GDScriptFunction::OPCODE_CAST_TO_BUILTIN); - codegen.opcodes.push_back(cn->cast_type.builtin_type); + codegen.opcodes.push_back(cast_type.builtin_type); } break; - case GDScriptParser::DataType::NATIVE: { + case GDScriptDataType::NATIVE: { int class_idx; - if (GDScriptLanguage::get_singleton()->get_global_map().has(cn->cast_type.native_type)) { + if (GDScriptLanguage::get_singleton()->get_global_map().has(cast_type.native_type)) { - class_idx = GDScriptLanguage::get_singleton()->get_global_map()[cn->cast_type.native_type]; + class_idx = GDScriptLanguage::get_singleton()->get_global_map()[cast_type.native_type]; class_idx |= (GDScriptFunction::ADDR_TYPE_GLOBAL << GDScriptFunction::ADDR_BITS); //argument (stack root) } else { - _set_error("Invalid native class type '" + String(cn->cast_type.native_type) + "'.", cn); + _set_error("Invalid native class type '" + String(cast_type.native_type) + "'.", cn); return -1; } codegen.opcodes.push_back(GDScriptFunction::OPCODE_CAST_TO_NATIVE); // perform operator codegen.opcodes.push_back(class_idx); // variable type } break; - case GDScriptParser::DataType::CLASS: { - - Variant script; - int idx = -1; - if (!cn->cast_type.class_type->owner) { - script = codegen.script; - } else { - StringName name = cn->cast_type.class_type->name; - if (class_map[name] == codegen.script->subclasses[name]) { - idx = codegen.get_name_map_pos(name); - idx |= GDScriptFunction::ADDR_TYPE_CLASS_CONSTANT << GDScriptFunction::ADDR_BITS; - } else { - script = class_map[name]; - } - } - - if (idx < 0) { - idx = codegen.get_constant_pos(script); - idx |= GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS; //make it a local constant (faster access) - } - - codegen.opcodes.push_back(GDScriptFunction::OPCODE_CAST_TO_SCRIPT); // perform operator - codegen.opcodes.push_back(idx); // variable type - } break; - case GDScriptParser::DataType::SCRIPT: - case GDScriptParser::DataType::GDSCRIPT: { + case GDScriptDataType::SCRIPT: + case GDScriptDataType::GDSCRIPT: { - Variant script = cn->cast_type.script_type; + Variant script = cast_type.script_type; int idx = codegen.get_constant_pos(script); idx |= GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS; //make it a local constant (faster access) @@ -1149,18 +1142,18 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: if (src_address_b < 0) return -1; - GDScriptParser::DataType assign_type = on->arguments[0]->get_datatype(); + GDScriptDataType assign_type = _gdtype_from_datatype(on->arguments[0]->get_datatype()); if (assign_type.has_type && !on->arguments[1]->get_datatype().has_type) { // Typed assignment switch (assign_type.kind) { - case GDScriptParser::DataType::BUILTIN: { + case GDScriptDataType::BUILTIN: { codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN); // perform operator codegen.opcodes.push_back(assign_type.builtin_type); // variable type codegen.opcodes.push_back(dst_address_a); // argument 1 codegen.opcodes.push_back(src_address_b); // argument 2 } break; - case GDScriptParser::DataType::NATIVE: { + case GDScriptDataType::NATIVE: { int class_idx; if (GDScriptLanguage::get_singleton()->get_global_map().has(assign_type.native_type)) { @@ -1175,34 +1168,8 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: codegen.opcodes.push_back(dst_address_a); // argument 1 codegen.opcodes.push_back(src_address_b); // argument 2 } break; - case GDScriptParser::DataType::CLASS: { - - Variant script; - int idx = -1; - if (!assign_type.class_type->owner) { - script = codegen.script; - } else { - StringName name = assign_type.class_type->name; - if (class_map[name] == codegen.script->subclasses[name]) { - idx = codegen.get_name_map_pos(name); - idx |= GDScriptFunction::ADDR_TYPE_CLASS_CONSTANT << GDScriptFunction::ADDR_BITS; - } else { - script = class_map[name]; - } - } - - if (idx < 0) { - idx = codegen.get_constant_pos(script); - idx |= GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS; //make it a local constant (faster access) - } - - codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_TYPED_SCRIPT); // perform operator - codegen.opcodes.push_back(idx); // variable type - codegen.opcodes.push_back(dst_address_a); // argument 1 - codegen.opcodes.push_back(src_address_b); // argument 2 - } break; - case GDScriptParser::DataType::SCRIPT: - case GDScriptParser::DataType::GDSCRIPT: { + case GDScriptDataType::SCRIPT: + case GDScriptDataType::GDSCRIPT: { Variant script = assign_type.script_type; int idx = codegen.get_constant_pos(script); @@ -1360,15 +1327,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); @@ -1396,17 +1363,12 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo case GDScriptParser::ControlFlowNode::CF_IF: { -#ifdef DEBUG_ENABLED - codegen.opcodes.push_back(GDScriptFunction::OPCODE_LINE); - codegen.opcodes.push_back(cf->line); - codegen.current_line = cf->line; -#endif - int ret = _parse_expression(codegen, cf->arguments[0], p_stack_level, false); - if (ret < 0) + 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 +1383,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 +1406,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 +1455,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 +1470,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 +1495,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 +1520,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 +1552,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; } @@ -1853,22 +1812,21 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser return OK; } -Error GDScriptCompiler::_parse_class_level(GDScript *p_script, GDScript *p_owner, const GDScriptParser::ClassNode *p_class, bool p_keep_state) { +Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) { + + parsing_classes.insert(p_script); if (p_class->owner && p_class->owner->owner) { // Owner is not root - StringName owner_name = p_class->owner->name; - if (!parsed_classes.has(owner_name)) { - if (parsing_classes.has(owner_name)) { - _set_error("Cyclic class reference for '" + String(owner_name) + "'.", p_class); + if (!parsed_classes.has(p_script->_owner)) { + if (parsing_classes.has(p_script->_owner)) { + _set_error("Cyclic class reference for '" + String(p_class->name) + "'.", p_class); return ERR_PARSE_ERROR; } - parsing_classes.insert(owner_name); - Error err = _parse_class_level(class_map[owner_name].ptr(), class_map[owner_name]->_owner, p_class->owner, p_keep_state); + Error err = _parse_class_level(p_script->_owner, p_class->owner, p_keep_state); if (err) { return err; } - parsing_classes.erase(owner_name); } } @@ -1886,47 +1844,39 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, GDScript *p_owner p_script->_signals.clear(); p_script->initializer = NULL; - p_script->subclasses.clear(); - p_script->_owner = p_owner; p_script->tool = p_class->tool; p_script->name = p_class->name; Ref<GDScriptNativeClass> native; + GDScriptDataType base_type = _gdtype_from_datatype(p_class->base_type); // Inheritance - switch (p_class->base_type.kind) { - case GDScriptParser::DataType::CLASS: { - StringName base_name = p_class->base_type.class_type->name; - // Make sure dependency is parsed first - if (!parsed_classes.has(base_name)) { - if (parsing_classes.has(base_name)) { - _set_error("Cyclic class reference for '" + String(base_name) + "'.", p_class); - return ERR_PARSE_ERROR; - } - parsing_classes.insert(base_name); - Error err = _parse_class_level(class_map[base_name].ptr(), class_map[base_name]->_owner, p_class->base_type.class_type, p_keep_state); - if (err) { - return err; - } - parsing_classes.erase(base_name); - } - Ref<GDScript> base = class_map[base_name]; - p_script->base = base; - p_script->_base = p_script->base.ptr(); - p_script->member_indices = base->member_indices; - } break; - case GDScriptParser::DataType::GDSCRIPT: { - Ref<GDScript> base = p_class->base_type.script_type; - p_script->base = base; - p_script->_base = p_script->base.ptr(); - p_script->member_indices = base->member_indices; - } break; - case GDScriptParser::DataType::NATIVE: { - int native_idx = GDScriptLanguage::get_singleton()->get_global_map()[p_class->base_type.native_type]; + switch (base_type.kind) { + case GDScriptDataType::NATIVE: { + int native_idx = GDScriptLanguage::get_singleton()->get_global_map()[base_type.native_type]; native = GDScriptLanguage::get_singleton()->get_global_array()[native_idx]; ERR_FAIL_COND_V(native.is_null(), ERR_BUG); p_script->native = native; } break; + case GDScriptDataType::GDSCRIPT: { + Ref<GDScript> base = base_type.script_type; + p_script->base = base; + p_script->_base = base.ptr(); + p_script->member_indices = base->member_indices; + + if (p_class->base_type.kind == GDScriptParser::DataType::CLASS) { + if (!parsed_classes.has(p_script->_base)) { + if (parsing_classes.has(p_script->_base)) { + _set_error("Cyclic class reference for '" + String(p_class->name) + "'.", p_class); + return ERR_PARSE_ERROR; + } + Error err = _parse_class_level(p_script->_base, p_class->base_type.class_type, p_keep_state); + if (err) { + return err; + } + } + } + } break; default: { _set_error("Parser bug: invalid inheritance.", p_class); return ERR_BUG; @@ -2020,24 +1970,19 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, GDScript *p_owner p_script->_signals[name] = p_class->_signals[i].arguments; } - if (p_class->owner) { - parsed_classes.insert(p_class->name); - if (parsing_classes.has(p_class->name)) { - parsing_classes.erase(p_class->name); - } - } + parsed_classes.insert(p_script); + parsing_classes.erase(p_script); //parse sub-classes for (int i = 0; i < p_class->subclasses.size(); i++) { StringName name = p_class->subclasses[i]->name; - Ref<GDScript> subclass = class_map[name]; + GDScript *subclass = p_script->subclasses[name].ptr(); // Subclass might still be parsing, just skip it - if (!parsed_classes.has(name) && !parsing_classes.has(name)) { - parsing_classes.insert(name); - Error err = _parse_class_level(subclass.ptr(), p_script, p_class->subclasses[i], p_keep_state); + if (!parsed_classes.has(subclass) && !parsing_classes.has(subclass)) { + Error err = _parse_class_level(subclass, p_class->subclasses[i], p_keep_state); if (err) return err; } @@ -2048,7 +1993,6 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, GDScript *p_owner #endif p_script->constants.insert(name, subclass); //once parsed, goes to the list of constants - p_script->subclasses.insert(name, subclass); } return OK; @@ -2119,8 +2063,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); @@ -2147,9 +2091,9 @@ Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptPa for (int i = 0; i < p_class->subclasses.size(); i++) { StringName name = p_class->subclasses[i]->name; - Ref<GDScript> subclass = class_map[name]; + GDScript *subclass = p_script->subclasses[name].ptr(); - Error err = _parse_class_blocks(subclass.ptr(), p_class->subclasses[i], p_keep_state); + Error err = _parse_class_blocks(subclass, p_class->subclasses[i], p_keep_state); if (err) { return err; } @@ -2159,7 +2103,7 @@ Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptPa return OK; } -void GDScriptCompiler::_make_scripts(const GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) { +void GDScriptCompiler::_make_scripts(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) { Map<StringName, Ref<GDScript> > old_subclasses; @@ -2167,6 +2111,8 @@ void GDScriptCompiler::_make_scripts(const GDScript *p_script, const GDScriptPar old_subclasses = p_script->subclasses; } + p_script->subclasses.clear(); + for (int i = 0; i < p_class->subclasses.size(); i++) { StringName name = p_class->subclasses[i]->name; @@ -2178,10 +2124,10 @@ void GDScriptCompiler::_make_scripts(const GDScript *p_script, const GDScriptPar subclass.instance(); } - subclass->_owner = const_cast<GDScript *>(p_script); - class_map.insert(name, subclass); + subclass->_owner = p_script; + p_script->subclasses.insert(name, subclass); - _make_scripts(subclass.ptr(), p_class->subclasses[i], p_keep_state); + _make_scripts(subclass.ptr(), p_class->subclasses[i], false); } } @@ -2200,7 +2146,8 @@ Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_scri // Create scripts for subclasses beforehand so they can be referenced _make_scripts(p_script, static_cast<const GDScriptParser::ClassNode *>(root), p_keep_state); - Error err = _parse_class_level(p_script, NULL, static_cast<const GDScriptParser::ClassNode *>(root), p_keep_state); + p_script->_owner = NULL; + Error err = _parse_class_level(p_script, static_cast<const GDScriptParser::ClassNode *>(root), p_keep_state); if (err) return err; diff --git a/modules/gdscript/gdscript_compiler.h b/modules/gdscript/gdscript_compiler.h index 55f5e2b48e..2cf630ba72 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 */ @@ -38,9 +38,8 @@ class GDScriptCompiler { const GDScriptParser *parser; - Map<StringName, Ref<GDScript> > class_map; - Set<StringName> parsed_classes; - Set<StringName> parsing_classes; + Set<GDScript *> parsed_classes; + Set<GDScript *> parsing_classes; GDScript *main_script; struct CodeGen { @@ -149,9 +148,9 @@ class GDScriptCompiler { int _parse_expression(CodeGen &codegen, const GDScriptParser::Node *p_expression, int p_stack_level, bool p_root = false, bool p_initializer = false); Error _parse_block(CodeGen &codegen, const GDScriptParser::BlockNode *p_block, int p_stack_level = 0, int p_break_addr = -1, int p_continue_addr = -1); Error _parse_function(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready = false); - Error _parse_class_level(GDScript *p_script, GDScript *p_owner, const GDScriptParser::ClassNode *p_class, bool p_keep_state); + Error _parse_class_level(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state); Error _parse_class_blocks(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state); - void _make_scripts(const GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state); + void _make_scripts(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state); int err_line; int err_column; StringName source; diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 068e8d2d92..ab34184bfb 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 */ @@ -44,12 +44,43 @@ void GDScriptLanguage::get_comment_delimiters(List<String> *p_delimiters) const p_delimiters->push_back("#"); } + void GDScriptLanguage::get_string_delimiters(List<String> *p_delimiters) const { p_delimiters->push_back("\" \""); p_delimiters->push_back("' '"); p_delimiters->push_back("\"\"\" \"\"\""); } + +String GDScriptLanguage::_get_processed_template(const String &p_template, const String &p_base_class_name) const { + + String processed_template = p_template; + +#ifdef TOOLS_ENABLED + if (EDITOR_DEF("text_editor/completion/add_type_hints", false)) { + processed_template = processed_template.replace("%INT_TYPE%", ": int"); + processed_template = processed_template.replace("%STRING_TYPE%", ": String"); + processed_template = processed_template.replace("%FLOAT_TYPE%", ": float"); + processed_template = processed_template.replace("%VOID_RETURN%", " -> void"); + } else { + processed_template = processed_template.replace("%INT_TYPE%", ""); + processed_template = processed_template.replace("%STRING_TYPE%", ""); + processed_template = processed_template.replace("%FLOAT_TYPE%", ""); + processed_template = processed_template.replace("%VOID_RETURN%", ""); + } +#else + processed_template = processed_template.replace("%INT_TYPE%", ""); + processed_template = processed_template.replace("%STRING_TYPE%", ""); + processed_template = processed_template.replace("%FLOAT_TYPE%", ""); + processed_template = processed_template.replace("%VOID_RETURN%", ""); +#endif + + processed_template = processed_template.replace("%BASE%", p_base_class_name); + processed_template = processed_template.replace("%TS%", _get_indentation()); + + return processed_template; +} + Ref<Script> GDScriptLanguage::get_template(const String &p_class_name, const String &p_base_class_name) const { String _template = "extends %BASE%\n" "\n" @@ -65,27 +96,7 @@ Ref<Script> GDScriptLanguage::get_template(const String &p_class_name, const Str "#func _process(delta%FLOAT_TYPE%)%VOID_RETURN%:\n" "#%TS%pass\n"; -#ifdef TOOLS_ENABLED - if (EDITOR_DEF("text_editor/completion/add_type_hints", false)) { - _template = _template.replace("%INT_TYPE%", ": int"); - _template = _template.replace("%STRING_TYPE%", ": String"); - _template = _template.replace("%FLOAT_TYPE%", ": float"); - _template = _template.replace("%VOID_RETURN%", " -> void"); - } else { - _template = _template.replace("%INT_TYPE%", ""); - _template = _template.replace("%STRING_TYPE%", ""); - _template = _template.replace("%FLOAT_TYPE%", ""); - _template = _template.replace("%VOID_RETURN%", ""); - } -#else - _template = _template.replace("%INT_TYPE%", ""); - _template = _template.replace("%STRING_TYPE%", ""); - _template = _template.replace("%FLOAT_TYPE%", ""); - _template = _template.replace("%VOID_RETURN%", ""); -#endif - - _template = _template.replace("%BASE%", p_base_class_name); - _template = _template.replace("%TS%", _get_indentation()); + _template = _get_processed_template(_template, p_base_class_name); Ref<GDScript> script; script.instance(); @@ -101,10 +112,8 @@ bool GDScriptLanguage::is_using_templates() { void GDScriptLanguage::make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script) { - String src = p_script->get_source_code(); - src = src.replace("%BASE%", p_base_class_name); - src = src.replace("%TS%", _get_indentation()); - p_script->set_source_code(src); + String _template = _get_processed_template(p_script->get_source_code(), p_base_class_name); + p_script->set_source_code(_template); } bool GDScriptLanguage::validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions, List<ScriptLanguage::Warning> *r_warnings, Set<int> *r_safe_lines) const { @@ -479,12 +488,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 { @@ -615,6 +627,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; @@ -631,12 +646,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); @@ -773,12 +794,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 { @@ -1049,7 +1070,8 @@ static bool _guess_expression_type(const GDScriptCompletionContext &p_context, c case GDScriptParser::OperatorNode::OP_BIT_AND: vop = Variant::OP_BIT_AND; break; case GDScriptParser::OperatorNode::OP_BIT_OR: vop = Variant::OP_BIT_OR; break; case GDScriptParser::OperatorNode::OP_BIT_XOR: vop = Variant::OP_BIT_XOR; break; - default: {} + default: { + } } if (vop == Variant::OP_MAX) { @@ -1104,7 +1126,8 @@ static bool _guess_expression_type(const GDScriptCompletionContext &p_context, c } break; } } break; - default: {} + default: { + } } // It may have found a null, but that's never useful @@ -1125,7 +1148,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; @@ -1181,7 +1204,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; + 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; } } @@ -1266,9 +1293,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; } } @@ -1351,7 +1378,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) { @@ -1540,7 +1567,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; @@ -2221,8 +2248,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()); } } } @@ -2305,7 +2332,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; @@ -2442,9 +2469,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()) { @@ -2786,12 +2817,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()); } @@ -2977,8 +3008,8 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co } } } + base_type = base_type.class_type->base_type; } - base_type = base_type.class_type->base_type; } break; case GDScriptParser::DataType::SCRIPT: case GDScriptParser::DataType::GDSCRIPT: { @@ -3351,7 +3382,8 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol return OK; } } break; - default: {} + default: { + } } return ERR_CANT_RESOLVE; diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp index cd6c21a629..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 */ @@ -329,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); @@ -1083,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") { diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h index 5509edf24a..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 */ @@ -36,7 +36,7 @@ #include "core/reference.h" #include "core/script_language.h" #include "core/self_list.h" -#include "core/string_db.h" +#include "core/string_name.h" #include "core/variant.h" class GDScriptInstance; @@ -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 { diff --git a/modules/gdscript/gdscript_functions.cpp b/modules/gdscript/gdscript_functions.cpp index 9ff33594ce..4fd136d5cc 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 */ @@ -74,6 +74,7 @@ const char *GDScriptFunctions::get_func_name(Function p_func) { "lerp", "inverse_lerp", "range_lerp", + "smoothstep", "dectime", "randomize", "randi", @@ -369,6 +370,13 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_ VALIDATE_ARG_NUM(4); r_ret = Math::range_lerp((double)*p_args[0], (double)*p_args[1], (double)*p_args[2], (double)*p_args[3], (double)*p_args[4]); } break; + case MATH_SMOOTHSTEP: { + VALIDATE_ARG_COUNT(3); + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + r_ret = Math::smoothstep((double)*p_args[0], (double)*p_args[1], (double)*p_args[2]); + } break; case MATH_DECTIME: { VALIDATE_ARG_COUNT(3); VALIDATE_ARG_NUM(0); @@ -768,11 +776,30 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_ (void)VariantParser::parse(&ss, r_ret, errs, line); } break; case VAR_TO_BYTES: { - VALIDATE_ARG_COUNT(1); + bool full_objects = false; + if (p_arg_count < 1) { + r_error.error = Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = 1; + r_ret = Variant(); + return; + } else if (p_arg_count > 2) { + r_error.error = Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; + r_error.argument = 2; + r_ret = Variant(); + } else if (p_arg_count == 2) { + if (p_args[1]->get_type() != Variant::BOOL) { + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 1; + r_error.expected = Variant::BOOL; + r_ret = Variant(); + return; + } + full_objects = *p_args[1]; + } PoolByteArray barr; int len; - Error err = encode_variant(*p_args[0], NULL, len); + Error err = encode_variant(*p_args[0], NULL, len, full_objects); if (err) { r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; r_error.argument = 0; @@ -784,15 +811,35 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_ barr.resize(len); { PoolByteArray::Write w = barr.write(); - encode_variant(*p_args[0], w.ptr(), len); + encode_variant(*p_args[0], w.ptr(), len, full_objects); } r_ret = barr; } break; case BYTES_TO_VAR: { - VALIDATE_ARG_COUNT(1); + bool allow_objects = false; + if (p_arg_count < 1) { + r_error.error = Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = 1; + r_ret = Variant(); + return; + } else if (p_arg_count > 2) { + r_error.error = Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; + r_error.argument = 2; + r_ret = Variant(); + } else if (p_arg_count == 2) { + if (p_args[1]->get_type() != Variant::BOOL) { + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 1; + r_error.expected = Variant::BOOL; + r_ret = Variant(); + return; + } + allow_objects = *p_args[1]; + } + if (p_args[0]->get_type() != Variant::POOL_BYTE_ARRAY) { r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; + r_error.argument = 1; r_error.expected = Variant::POOL_BYTE_ARRAY; r_ret = Variant(); return; @@ -802,7 +849,7 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_ Variant ret; { PoolByteArray::Read r = varr.read(); - Error err = decode_variant(ret, r.ptr(), varr.size(), NULL); + Error err = decode_variant(ret, r.ptr(), varr.size(), NULL, allow_objects); if (err != OK) { r_ret = RTR("Not enough bytes for decoding bytes, or invalid format."); r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; @@ -882,7 +929,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; } @@ -1396,6 +1443,7 @@ bool GDScriptFunctions::is_deterministic(Function p_func) { case MATH_LERP: case MATH_INVERSE_LERP: case MATH_RANGE_LERP: + case MATH_SMOOTHSTEP: case MATH_DECTIME: case MATH_DEG2RAD: case MATH_RAD2DEG: @@ -1565,7 +1613,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: { @@ -1578,6 +1627,11 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) { mi.return_val.type = Variant::REAL; return mi; } break; + case MATH_SMOOTHSTEP: { + MethodInfo mi("smoothstep", PropertyInfo(Variant::REAL, "from"), PropertyInfo(Variant::REAL, "to"), PropertyInfo(Variant::REAL, "weight")); + mi.return_val.type = Variant::REAL; + return mi; + } break; case MATH_DECTIME: { MethodInfo mi("dectime", PropertyInfo(Variant::REAL, "value"), PropertyInfo(Variant::REAL, "amount"), PropertyInfo(Variant::REAL, "step")); mi.return_val.type = Variant::REAL; @@ -1804,13 +1858,15 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) { } break; case VAR_TO_BYTES: { - MethodInfo mi("var2bytes", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT)); + MethodInfo mi("var2bytes", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT), PropertyInfo(Variant::BOOL, "full_objects")); + mi.default_arguments.push_back(false); mi.return_val.type = Variant::POOL_BYTE_ARRAY; return mi; } break; case BYTES_TO_VAR: { - MethodInfo mi(Variant::NIL, "bytes2var", PropertyInfo(Variant::POOL_BYTE_ARRAY, "bytes")); + MethodInfo mi(Variant::NIL, "bytes2var", PropertyInfo(Variant::POOL_BYTE_ARRAY, "bytes"), PropertyInfo(Variant::BOOL, "allow_objects")); + mi.default_arguments.push_back(false); mi.return_val.type = Variant::NIL; mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; return mi; diff --git a/modules/gdscript/gdscript_functions.h b/modules/gdscript/gdscript_functions.h index 33d5f27230..14bf3d7560 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 */ @@ -65,6 +65,7 @@ public: MATH_LERP, MATH_INVERSE_LERP, MATH_RANGE_LERP, + MATH_SMOOTHSTEP, MATH_DECTIME, MATH_RANDOMIZE, MATH_RAND, diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 6ea0dbcb19..80af094c2c 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 */ @@ -473,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; @@ -503,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; } @@ -775,7 +777,8 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s } _add_warning(GDScriptWarning::UNASSIGNED_VARIABLE_OP_ASSIGN, -1, identifier.operator String()); } - } // fallthrough + FALLTHROUGH; + } case GDScriptTokenizer::TK_OP_ASSIGN: { lv->assignments += 1; lv->usages--; // Assignment is not really usage @@ -811,6 +814,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) { @@ -858,7 +888,8 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s case GDScriptTokenizer::TK_OP_SUB: e.op = OperatorNode::OP_NEG; break; case GDScriptTokenizer::TK_OP_NOT: e.op = OperatorNode::OP_NOT; break; case GDScriptTokenizer::TK_OP_BIT_INVERT: e.op = OperatorNode::OP_BIT_INVERT; break; - default: {} + default: { + } } tokenizer->advance(); @@ -1845,7 +1876,9 @@ GDScriptParser::Node *GDScriptParser::_reduce_expression(Node *p_node, bool p_to } } break; - default: { break; } + default: { + break; + } } //now se if all are constants if (!all_constants) @@ -1958,7 +1991,9 @@ GDScriptParser::Node *GDScriptParser::_reduce_expression(Node *p_node, bool p_to return op->arguments[2]; } } break; - default: { ERR_FAIL_V(op); } + default: { + ERR_FAIL_V(op); + } } ERR_FAIL_V(op); @@ -2067,7 +2102,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) { @@ -2196,6 +2231,8 @@ GDScriptParser::PatternNode *GDScriptParser::_parse_pattern(bool p_static) { void GDScriptParser::_parse_pattern_block(BlockNode *p_block, Vector<PatternBranchNode *> &p_branches, bool p_static) { int indent_level = tab_level.back()->get(); + p_block->has_return = true; + while (true) { while (tokenizer->get_token() == GDScriptTokenizer::TK_NEWLINE && _parse_newline()) @@ -2253,8 +2290,8 @@ void GDScriptParser::_parse_pattern_block(BlockNode *p_block, Vector<PatternBran current_block = p_block; - if (catch_all && branch->body->has_return) { - p_block->has_return = true; + if (!branch->body->has_return) { + p_block->has_return = false; } p_branches.push_back(branch); @@ -2625,14 +2662,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); @@ -2679,9 +2716,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; } @@ -2712,6 +2749,8 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) { } break; case GDScriptTokenizer::TK_NEWLINE: { + int line = tokenizer->get_token_line(); + if (!_parse_newline()) { if (!error_set) { p_block->end_line = tokenizer->get_token_line(); @@ -2720,9 +2759,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 = line; + p_block->statements.push_back(nl2); } break; case GDScriptTokenizer::TK_CF_PASS: { @@ -2907,14 +2946,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); @@ -2977,8 +3016,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; } @@ -2988,7 +3027,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; @@ -3038,7 +3077,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) { @@ -3363,6 +3401,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 @@ -3455,6 +3500,10 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { _set_error("'class_name' is only valid for the main class namespace."); return; } + if (self_path.empty()) { + _set_error("'class_name' not allowed in built-in scripts."); + return; + } if (tokenizer->get_token(1) != GDScriptTokenizer::TK_IDENTIFIER) { _set_error("'class_name' syntax: 'class_name <UniqueName>'"); @@ -3479,16 +3528,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 { @@ -3593,7 +3646,8 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { return; } - }; //fallthrough to function + FALLTHROUGH; + } case GDScriptTokenizer::TK_PR_FUNCTION: { bool _static = false; @@ -3636,6 +3690,11 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { _add_warning(GDScriptWarning::FUNCTION_CONFLICTS_VARIABLE, -1, name); } } + for (int i = 0; i < p_class->subclasses.size(); i++) { + if (p_class->subclasses[i]->name == name) { + _add_warning(GDScriptWarning::FUNCTION_CONFLICTS_CONSTANT, -1, name); + } + } #endif // DEBUG_ENABLED if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_OPEN) { @@ -4043,7 +4102,8 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { break; } - }; //fallthrough to use the same + FALLTHROUGH; + } case Variant::REAL: { if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "EASE") { @@ -4468,6 +4528,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { #ifdef DEBUG_ENABLED _add_warning(GDScriptWarning::DEPRECATED_KEYWORD, tokenizer->get_token_line(), "slave", "puppet"); #endif + FALLTHROUGH; case GDScriptTokenizer::TK_PR_PUPPET: { //may be fallthrough from export, ignore if so @@ -4535,9 +4596,10 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { continue; } break; case GDScriptTokenizer::TK_PR_VAR: { - //variale declaration and (eventual) initialization + // variable 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; @@ -4559,6 +4621,10 @@ 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) + "' already exists in this class (at line: " + @@ -4573,6 +4639,13 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { return; } } + + for (int i = 0; i < current_class->subclasses.size(); i++) { + if (current_class->subclasses[i]->name == member.identifier) { + _set_error("A class named '" + String(member.identifier) + "' already exists in this class (at line " + itos(current_class->subclasses[i]->line) + ")."); + return; + } + } #ifdef DEBUG_ENABLED for (int i = 0; i < current_class->functions.size(); i++) { if (current_class->functions[i]->name == member.identifier) { @@ -4663,10 +4736,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; } } @@ -4681,12 +4764,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); @@ -4701,6 +4784,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) { @@ -4788,6 +4890,13 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { } } + for (int i = 0; i < current_class->subclasses.size(); i++) { + if (current_class->subclasses[i]->name == const_id) { + _set_error("A class named '" + String(const_id) + "' already exists in this class (at line " + itos(current_class->subclasses[i]->line) + ")."); + return; + } + } + tokenizer->advance(); if (tokenizer->get_token() == GDScriptTokenizer::TK_COLON) { @@ -4858,6 +4967,13 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { } } + for (int i = 0; i < current_class->subclasses.size(); i++) { + if (current_class->subclasses[i]->name == enum_name) { + _set_error("A class named '" + String(enum_name) + "' already exists in this class (at line " + itos(current_class->subclasses[i]->line) + ")."); + return; + } + } + tokenizer->advance(); } if (tokenizer->get_token() != GDScriptTokenizer::TK_CURLY_BRACKET_OPEN) { @@ -4943,6 +5059,13 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { } } + for (int i = 0; i < current_class->subclasses.size(); i++) { + if (current_class->subclasses[i]->name == const_id) { + _set_error("A class named '" + String(const_id) + "' already exists in this class (at line " + itos(current_class->subclasses[i]->line) + ")."); + return; + } + } + ClassNode::Constant constant; constant.type.has_type = true; constant.type.kind = DataType::BUILTIN; @@ -5058,7 +5181,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; @@ -5213,7 +5336,8 @@ String GDScriptParser::DataType::to_string() const { if (!gds_class.empty()) { return gds_class; } - } // fallthrough + FALLTHROUGH; + } case SCRIPT: { if (is_meta_type) { return script_type->get_class_name().operator String(); @@ -5381,13 +5505,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; @@ -5434,6 +5558,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; @@ -5474,10 +5604,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; } } @@ -5569,6 +5699,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; @@ -5932,7 +6065,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: { @@ -6182,7 +6315,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()); @@ -6224,7 +6357,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 @@ -6260,7 +6393,8 @@ 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: {} + default: { + } } } if (error) { @@ -6378,7 +6512,8 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) { } } } break; - default: {} + default: { + } } p_node->set_datatype(_resolve_type(node_type, p_node->line)); @@ -6594,7 +6729,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(); @@ -6633,13 +6768,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); @@ -6767,10 +6902,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) { @@ -6829,6 +6964,7 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat } break; } +#ifdef DEBUG_ENABLED if (!check_types) { return return_type; } @@ -6854,11 +6990,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])) { @@ -6871,14 +7005,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; } @@ -6899,9 +7033,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; } } @@ -7096,43 +7230,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++) { - 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 @@ -7186,11 +7310,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; @@ -7236,6 +7361,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; @@ -7284,7 +7410,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; @@ -7328,7 +7454,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); @@ -7417,7 +7543,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); @@ -7436,7 +7563,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); @@ -7531,6 +7658,11 @@ void GDScriptParser::_check_function_types(FunctionNode *p_function) { if (p_function->arguments_usage[i] == 0 && !p_function->arguments[i].operator String().begins_with("_")) { _add_warning(GDScriptWarning::UNUSED_ARGUMENT, p_function->line, p_function->name, p_function->arguments[i].operator String()); } + for (int j = 0; j < current_class->variables.size(); j++) { + if (current_class->variables[j].identifier == p_function->arguments[i]) { + _add_warning(GDScriptWarning::SHADOWED_VARIABLE, p_function->line, p_function->arguments[i], itos(current_class->variables[j].line)); + } + } #endif // DEBUG_ENABLED } @@ -7562,7 +7694,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 += ", "; @@ -7572,11 +7704,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 += ")"; @@ -7604,6 +7736,17 @@ void GDScriptParser::_check_function_types(FunctionNode *p_function) { p_function->return_type.has_type = false; p_function->return_type.may_yield = true; } + +#ifdef DEBUG_ENABLED + for (Map<StringName, LocalVarNode *>::Element *E = p_function->body->variables.front(); E; E = E->next()) { + LocalVarNode *lv = E->get(); + for (int i = 0; i < current_class->variables.size(); i++) { + if (current_class->variables[i].identifier == lv->name) { + _add_warning(GDScriptWarning::SHADOWED_VARIABLE, lv->line, lv->name, itos(current_class->variables[i].line)); + } + } + } +#endif // DEBUG_ENABLED } void GDScriptParser::_check_class_blocks_types(ClassNode *p_class) { @@ -7790,13 +7933,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; @@ -7812,7 +7958,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); @@ -7835,7 +7981,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); @@ -7871,9 +8017,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: { @@ -7963,7 +8111,8 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { if (cn->value.get_type() == Variant::STRING) { break; } - } // falthrough + FALLTHROUGH; + } default: { _mark_line_as_safe(statement->line); _reduce_node_type(statement); // Test for safety anyway @@ -8095,6 +8244,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) { @@ -8125,6 +8278,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(); @@ -8172,7 +8326,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(); @@ -8182,6 +8336,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 @@ -8238,6 +8393,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 8121fb7f85..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 */ @@ -95,6 +95,7 @@ public: } DataType() : + kind(UNRESOLVED), has_type(false), is_constant(false), is_meta_type(false), @@ -168,6 +169,7 @@ public: MultiplayerAPI::RPCMode rpc_mode; int usages; }; + struct Constant { Node *expression; DataType type; @@ -219,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; @@ -443,7 +446,6 @@ public: CF_IF, CF_FOR, CF_WHILE, - CF_SWITCH, CF_BREAK, CF_CONTINUE, CF_RETURN, @@ -531,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 @@ -607,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); @@ -632,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; @@ -651,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 c37142b3c1..b8048fb5dd 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 */ @@ -80,10 +80,7 @@ const char *GDScriptTokenizer::token_names[TK_MAX] = { "elif", "else", "for", - "do", "while", - "switch (reserved)", - "case (reserved)", "break", "continue", "pass", @@ -224,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" }, @@ -291,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: @@ -348,7 +339,8 @@ StringName GDScriptTokenizer::get_token_literal(int p_offset) const { return "null"; case Variant::BOOL: return value ? "true" : "false"; - default: {} + default: { + } } } case TK_OP_AND: @@ -543,13 +535,14 @@ void GDScriptTokenizerText::_advance() { } } #ifdef DEBUG_ENABLED - if (comment.begins_with("#warning-ignore:")) { - String code = comment.get_slice(":", 1); + String comment_content = comment.trim_prefix("#").trim_prefix(" "); + if (comment_content.begins_with("warning-ignore:")) { + String code = comment_content.get_slice(":", 1); warning_skips.push_back(Pair<int, String>(line, code.strip_edges().to_lower())); - } else if (comment.begins_with("#warning-ignore-all:")) { - String code = comment.get_slice(":", 1); + } else if (comment_content.begins_with("warning-ignore-all:")) { + String code = comment_content.get_slice(":", 1); warning_global_skips.insert(code.strip_edges().to_lower()); - } else if (comment.strip_edges() == "#warnings-disable") { + } else if (comment_content.strip_edges() == "warnings-disable") { ignore_warnings = true; } #endif // DEBUG_ENABLED @@ -753,7 +746,7 @@ void GDScriptTokenizerText::_advance() { } INCPOS(1); is_node_path = true; - + FALLTHROUGH; case '\'': case '"': { @@ -903,7 +896,7 @@ void GDScriptTokenizerText::_advance() { } hexa_found = true; } else if (!hexa_found && GETCHAR(i) == 'e') { - if (hexa_found || exponent_found) { + if (exponent_found) { _make_error("Invalid numeric constant at 'e'"); return; } @@ -998,11 +991,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; } @@ -1208,7 +1201,8 @@ Error GDScriptTokenizerBuffer::set_code_buffer(const Vector<uint8_t> &p_buffer) Variant v; int len; - Error err = decode_variant(v, b, total_len, &len); + // An object cannot be constant, never decode objects + Error err = decode_variant(v, b, total_len, &len, false); if (err) return err; b += len; @@ -1310,7 +1304,8 @@ Vector<uint8_t> GDScriptTokenizerBuffer::parse_code_string(const String &p_code) ERR_FAIL_V(Vector<uint8_t>()); } break; - default: {} + default: { + } }; token_array.push_back(token); @@ -1376,11 +1371,12 @@ Vector<uint8_t> GDScriptTokenizerBuffer::parse_code_string(const String &p_code) for (Map<int, Variant>::Element *E = rev_constant_map.front(); E; E = E->next()) { int len; - Error err = encode_variant(E->get(), NULL, len); + // Objects cannot be constant, never encode objects + Error err = encode_variant(E->get(), NULL, len, false); ERR_FAIL_COND_V(err != OK, Vector<uint8_t>()); int pos = buf.size(); buf.resize(pos + len); - encode_variant(E->get(), &buf.write[pos], len); + encode_variant(E->get(), &buf.write[pos], len, false); } for (Map<int, uint32_t>::Element *E = rev_line_map.front(); E; E = E->next()) { @@ -1426,7 +1422,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]; } @@ -1482,7 +1478,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 cc894fb101..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,7 +32,7 @@ #define GDSCRIPT_TOKENIZER_H #include "core/pair.h" -#include "core/string_db.h" +#include "core/string_name.h" #include "core/ustring.h" #include "core/variant.h" #include "core/vmap.h" @@ -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,7 +115,7 @@ 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, @@ -176,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(){}; @@ -248,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 }; @@ -292,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 26dcbdcf89..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 */ @@ -38,8 +38,8 @@ #include "gdscript_tokenizer.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 */ |