diff options
Diffstat (limited to 'modules')
24 files changed, 313 insertions, 198 deletions
diff --git a/modules/csg/doc_classes/CSGShape3D.xml b/modules/csg/doc_classes/CSGShape3D.xml index 43ce988c30..dac556c7f1 100644 --- a/modules/csg/doc_classes/CSGShape3D.xml +++ b/modules/csg/doc_classes/CSGShape3D.xml @@ -71,10 +71,10 @@ <member name="collision_layer" type="int" setter="set_collision_layer" getter="get_collision_layer" default="1"> The physics layers this area is in. Collidable objects can exist in any of 32 different layers. These layers work like a tagging system, and are not visual. A collidable can use these layers to select with which objects it can collide, using the collision_mask property. - A contact is detected if object A is in any of the layers that object B scans, or object B is in any layer scanned by object A. + A contact is detected if object A is in any of the layers that object B scans, or object B is in any layer scanned by object A. See [url=https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information. </member> <member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask" default="1"> - The physics layers this CSG shape scans for collisions. + The physics layers this CSG shape scans for collisions. See [url=https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information. </member> <member name="operation" type="int" setter="set_operation" getter="get_operation" enum="CSGShape3D.Operation" default="0"> The operation that is performed on this shape. This is ignored for the first CSG child node as the operation is between this node and the previous child of this nodes parent. diff --git a/modules/enet/doc_classes/NetworkedMultiplayerENet.xml b/modules/enet/doc_classes/NetworkedMultiplayerENet.xml index c908af7479..f46ef2d812 100644 --- a/modules/enet/doc_classes/NetworkedMultiplayerENet.xml +++ b/modules/enet/doc_classes/NetworkedMultiplayerENet.xml @@ -7,8 +7,8 @@ A PacketPeer implementation that should be passed to [member SceneTree.network_peer] after being initialized as either a client or server. Events can then be handled by connecting to [SceneTree] signals. </description> <tutorials> - <link>https://docs.godotengine.org/en/latest/tutorials/networking/high_level_multiplayer.html</link> - <link>http://enet.bespin.org/usergroup0.html</link> + <link title="High-level multiplayer">https://docs.godotengine.org/en/latest/tutorials/networking/high_level_multiplayer.html</link> + <link title="API documentation on the ENet website">http://enet.bespin.org/usergroup0.html</link> </tutorials> <methods> <method name="close_connection"> diff --git a/modules/gdnative/doc_classes/GDNativeLibrary.xml b/modules/gdnative/doc_classes/GDNativeLibrary.xml index 1aab864102..05cda05f9f 100644 --- a/modules/gdnative/doc_classes/GDNativeLibrary.xml +++ b/modules/gdnative/doc_classes/GDNativeLibrary.xml @@ -7,8 +7,8 @@ A GDNative library can implement [NativeScript]s, global functions to call with the [GDNative] class, or low-level engine extensions through interfaces such as [XRInterfaceGDNative]. The library must be compiled for each platform and architecture that the project will run on. </description> <tutorials> - <link>https://docs.godotengine.org/en/latest/tutorials/plugins/gdnative/gdnative-c-example.html</link> - <link>https://docs.godotengine.org/en/latest/tutorials/plugins/gdnative/gdnative-cpp-example.html</link> + <link title="GDNative C example">https://docs.godotengine.org/en/latest/tutorials/plugins/gdnative/gdnative-c-example.html</link> + <link title="GDNative C++ example">https://docs.godotengine.org/en/latest/tutorials/plugins/gdnative/gdnative-cpp-example.html</link> </tutorials> <methods> <method name="get_current_dependencies" qualifiers="const"> diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml index 9e40a69712..e528fc6623 100644 --- a/modules/gdscript/doc_classes/@GDScript.xml +++ b/modules/gdscript/doc_classes/@GDScript.xml @@ -167,6 +167,7 @@ i = ceil(1.45) # i is 2 i = ceil(1.001) # i is 2 [/codeblock] + See also [method floor], [method round], and [method stepify]. </description> </method> <method name="char"> @@ -338,6 +339,7 @@ # a is -3.0 a = floor(-2.99) [/codeblock] + See also [method ceil], [method round], and [method stepify]. [b]Note:[/b] This method returns a float. If you need an integer, you can use [code]int(s)[/code] directly. </description> </method> @@ -1043,6 +1045,7 @@ [codeblock] round(2.6) # Returns 3 [/codeblock] + See also [method floor], [method ceil], and [method stepify]. </description> </method> <method name="seed"> @@ -1161,6 +1164,7 @@ stepify(100, 32) # Returns 96 stepify(3.14159, 0.01) # Returns 3.14 [/codeblock] + See also [method ceil], [method floor], and [method round]. </description> </method> <method name="str" qualifiers="vararg"> diff --git a/modules/gdscript/doc_classes/GDScript.xml b/modules/gdscript/doc_classes/GDScript.xml index 62ccb93901..631a102130 100644 --- a/modules/gdscript/doc_classes/GDScript.xml +++ b/modules/gdscript/doc_classes/GDScript.xml @@ -8,7 +8,7 @@ [method new] creates a new instance of the script. [method Object.set_script] extends an existing object, if that object's class matches one of the script's base classes. </description> <tutorials> - <link>https://docs.godotengine.org/en/latest/getting_started/scripting/gdscript/index.html</link> + <link title="GDScript tutorial index">https://docs.godotengine.org/en/latest/getting_started/scripting/gdscript/index.html</link> </tutorials> <methods> <method name="get_as_byte_code" qualifiers="const"> diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 541d46a2af..0263e32c5b 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -603,10 +603,8 @@ Error GDScript::reload(bool p_keep_state) { } if (!source_path.empty()) { MutexLock lock(GDScriptCache::singleton->lock); - Ref<GDScript> self(this); if (!GDScriptCache::singleton->shallow_gdscript_cache.has(source_path)) { - GDScriptCache::singleton->shallow_gdscript_cache[source_path] = self; - self->unreference(); + GDScriptCache::singleton->shallow_gdscript_cache[source_path] = this; } } } diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index 9906b4014d..79317ff846 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -69,6 +69,7 @@ class GDScript : public Script { friend class GDScriptInstance; friend class GDScriptFunction; + friend class GDScriptAnalyzer; friend class GDScriptCompiler; friend class GDScriptFunctions; friend class GDScriptLanguage; diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 3253c8e945..c25307ed7f 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -182,7 +182,7 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class, return ERR_PARSE_ERROR; } - Error err = parser->raise_status(GDScriptParserRef::INHERITANCE_SOLVED); + Error err = parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED); if (err != OK) { push_error(vformat(R"(Could not resolve super class inheritance from "%s".)", p_class->extends_path), p_class); return err; @@ -208,11 +208,12 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class, return ERR_PARSE_ERROR; } - Error err = parser->raise_status(GDScriptParserRef::INHERITANCE_SOLVED); + Error err = parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED); if (err != OK) { push_error(vformat(R"(Could not resolve super class inheritance from "%s".)", name), p_class); return err; } + base = parser->get_parser()->head->get_datatype(); } } else if (ProjectSettings::get_singleton()->has_autoload(name) && ProjectSettings::get_singleton()->get_autoload(name).is_singleton) { const ProjectSettings::AutoloadInfo &info = ProjectSettings::get_singleton()->get_autoload(name); @@ -227,7 +228,7 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class, return ERR_PARSE_ERROR; } - Error err = parser->raise_status(GDScriptParserRef::INHERITANCE_SOLVED); + Error err = parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED); if (err != OK) { push_error(vformat(R"(Could not resolve super class inheritance from "%s".)", name), p_class); return err; @@ -302,6 +303,16 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class, return ERR_PARSE_ERROR; } + // Check for cyclic inheritance. + const GDScriptParser::ClassNode *base_class = result.class_type; + while (base_class) { + if (base_class->fqcn == p_class->fqcn) { + push_error("Cyclic inheritance.", p_class); + return ERR_PARSE_ERROR; + } + base_class = base_class->base_type.class_type; + } + p_class->base_type = result; class_type.native_type = result.native_type; p_class->set_datatype(class_type); @@ -309,7 +320,10 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class, if (p_recursive) { for (int i = 0; i < p_class->members.size(); i++) { if (p_class->members[i].type == GDScriptParser::ClassNode::Member::CLASS) { - resolve_inheritance(p_class->members[i].m_class, true); + Error err = resolve_inheritance(p_class->members[i].m_class, true); + if (err) { + return err; + } } } } @@ -1253,6 +1267,7 @@ void GDScriptAnalyzer::resolve_pararameter(GDScriptParser::ParameterNode *p_para reduce_expression(p_parameter->default_value); result = p_parameter->default_value->get_datatype(); result.type_source = GDScriptParser::DataType::INFERRED; + result.is_constant = false; } if (p_parameter->datatype_specifier != nullptr) { @@ -1442,7 +1457,7 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig bool compatible = true; GDScriptParser::DataType op_type = p_assignment->assigned_value->get_datatype(); if (p_assignment->operation != GDScriptParser::AssignmentNode::OP_NONE) { - op_type = get_operation_type(p_assignment->variant_op, p_assignment->assignee->get_datatype(), p_assignment->assigned_value->get_datatype(), compatible); + op_type = get_operation_type(p_assignment->variant_op, p_assignment->assignee->get_datatype(), p_assignment->assigned_value->get_datatype(), compatible, p_assignment->assigned_value); } if (compatible) { @@ -1605,7 +1620,7 @@ void GDScriptAnalyzer::reduce_binary_op(GDScriptParser::BinaryOpNode *p_binary_o ERR_PRINT("Parser bug: unknown binary operation."); } } - p_binary_op->set_datatype(type_from_variant(p_binary_op->reduced_value)); + p_binary_op->set_datatype(type_from_variant(p_binary_op->reduced_value, p_binary_op)); return; } @@ -1619,7 +1634,7 @@ void GDScriptAnalyzer::reduce_binary_op(GDScriptParser::BinaryOpNode *p_binary_o } else { if (p_binary_op->variant_op < Variant::OP_MAX) { bool valid = false; - result = get_operation_type(p_binary_op->variant_op, p_binary_op->left_operand->get_datatype(), right_type, valid); + result = get_operation_type(p_binary_op->variant_op, p_binary_op->left_operand->get_datatype(), right_type, valid, p_binary_op); if (!valid) { push_error(vformat(R"(Invalid operands "%s" and "%s" for "%s" operator.)", p_binary_op->left_operand->get_datatype().to_string(), right_type.to_string(), Variant::get_operator_name(p_binary_op->variant_op)), p_binary_op); @@ -2048,7 +2063,7 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod if (valid) { p_identifier->is_constant = true; p_identifier->reduced_value = result; - p_identifier->set_datatype(type_from_variant(result)); + p_identifier->set_datatype(type_from_variant(result, p_identifier)); } else { push_error(vformat(R"(Cannot find constant "%s" on type "%s".)", name, base.to_string()), p_identifier); } @@ -2179,7 +2194,7 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod if (valid) { p_identifier->is_constant = true; p_identifier->reduced_value = int_constant; - p_identifier->set_datatype(type_from_variant(int_constant)); + p_identifier->set_datatype(type_from_variant(int_constant, p_identifier)); return; } } @@ -2276,10 +2291,34 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident return; } + // Try singletons. + // Do this before globals because this might be a singleton loading another one before it's compiled. + if (ProjectSettings::get_singleton()->has_autoload(name)) { + const ProjectSettings::AutoloadInfo &autoload = ProjectSettings::get_singleton()->get_autoload(name); + if (autoload.is_singleton) { + // Singleton exists, so it's at least a Node. + GDScriptParser::DataType result; + result.kind = GDScriptParser::DataType::NATIVE; + result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; + if (autoload.path.to_lower().ends_with(GDScriptLanguage::get_singleton()->get_extension())) { + Ref<GDScriptParserRef> parser = get_parser_for(autoload.path); + if (parser.is_valid()) { + Error err = parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED); + if (err == OK) { + result = type_from_metatype(parser->get_parser()->head->get_datatype()); + } + } + } + result.is_constant = true; + p_identifier->set_datatype(result); + return; + } + } + if (GDScriptLanguage::get_singleton()->get_global_map().has(name)) { int idx = GDScriptLanguage::get_singleton()->get_global_map()[name]; Variant constant = GDScriptLanguage::get_singleton()->get_global_array()[idx]; - p_identifier->set_datatype(type_from_variant(constant)); + p_identifier->set_datatype(type_from_variant(constant, p_identifier)); p_identifier->is_constant = true; p_identifier->reduced_value = constant; return; @@ -2287,7 +2326,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(name)) { Variant constant = GDScriptLanguage::get_singleton()->get_named_globals_map()[name]; - p_identifier->set_datatype(type_from_variant(constant)); + p_identifier->set_datatype(type_from_variant(constant, p_identifier)); p_identifier->is_constant = true; p_identifier->reduced_value = constant; return; @@ -2309,7 +2348,7 @@ void GDScriptAnalyzer::reduce_literal(GDScriptParser::LiteralNode *p_literal) { p_literal->reduced_value = p_literal->value; p_literal->is_constant = true; - p_literal->set_datatype(type_from_variant(p_literal->reduced_value)); + p_literal->set_datatype(type_from_variant(p_literal->reduced_value, p_literal)); } void GDScriptAnalyzer::reduce_preload(GDScriptParser::PreloadNode *p_preload) { @@ -2346,7 +2385,7 @@ void GDScriptAnalyzer::reduce_preload(GDScriptParser::PreloadNode *p_preload) { p_preload->is_constant = true; p_preload->reduced_value = p_preload->resource; - p_preload->set_datatype(type_from_variant(p_preload->reduced_value)); + p_preload->set_datatype(type_from_variant(p_preload->reduced_value, p_preload)); } void GDScriptAnalyzer::reduce_self(GDScriptParser::SelfNode *p_self) { @@ -2395,7 +2434,7 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri } else { p_subscript->is_constant = true; p_subscript->reduced_value = value; - result_type = type_from_variant(value); + result_type = type_from_variant(value, p_subscript); } result_type.kind = GDScriptParser::DataType::VARIANT; } else { @@ -2435,7 +2474,7 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri } else { p_subscript->is_constant = true; p_subscript->reduced_value = value; - result_type = type_from_variant(value); + result_type = type_from_variant(value, p_subscript); } result_type.kind = GDScriptParser::DataType::VARIANT; } else { @@ -2659,13 +2698,13 @@ void GDScriptAnalyzer::reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op) if (p_unary_op->operand->is_constant) { p_unary_op->is_constant = true; p_unary_op->reduced_value = Variant::evaluate(p_unary_op->variant_op, p_unary_op->operand->reduced_value, Variant()); - result = type_from_variant(p_unary_op->reduced_value); + result = type_from_variant(p_unary_op->reduced_value, p_unary_op); } else if (p_unary_op->operand->get_datatype().is_variant()) { result.kind = GDScriptParser::DataType::VARIANT; mark_node_unsafe(p_unary_op); } else { bool valid = false; - result = get_operation_type(p_unary_op->variant_op, p_unary_op->operand->get_datatype(), p_unary_op->operand->get_datatype(), valid); + result = get_operation_type(p_unary_op->variant_op, p_unary_op->operand->get_datatype(), p_unary_op->operand->get_datatype(), valid, p_unary_op); if (!valid) { push_error(vformat(R"(Invalid operand of type "%s" for unary operator "%s".)", p_unary_op->operand->get_datatype().to_string(), Variant::get_operator_name(p_unary_op->variant_op)), p_unary_op->operand); @@ -2675,7 +2714,7 @@ void GDScriptAnalyzer::reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op) p_unary_op->set_datatype(result); } -GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_value) { +GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_value, const GDScriptParser::Node *p_source) { GDScriptParser::DataType result; result.is_constant = true; result.kind = GDScriptParser::DataType::BUILTIN; @@ -2697,19 +2736,44 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_va scr = obj->get_script(); } if (scr.is_valid()) { - result.script_type = scr; - result.script_path = scr->get_path(); - Ref<GDScript> gds = scr; - if (gds.is_valid()) { - result.kind = GDScriptParser::DataType::CLASS; - Ref<GDScriptParserRef> ref = get_parser_for(gds->get_path()); - ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED); - result.class_type = ref->get_parser()->head; - result.script_path = ref->get_parser()->script_path; + if (scr->is_valid()) { + result.script_type = scr; + result.script_path = scr->get_path(); + Ref<GDScript> gds = scr; + if (gds.is_valid()) { + result.kind = GDScriptParser::DataType::CLASS; + // This might be an inner class, so we want to get the parser for the root. + // But still get the inner class from that tree. + GDScript *current = gds.ptr(); + List<StringName> class_chain; + while (current->_owner) { + // Push to front so it's in reverse. + class_chain.push_front(current->name); + current = current->_owner; + } + + Ref<GDScriptParserRef> ref = get_parser_for(current->path); + ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED); + + GDScriptParser::ClassNode *found = ref->get_parser()->head; + + // It should be okay to assume this exists, since we have a complete script already. + for (const List<StringName>::Element *E = class_chain.front(); E; E = E->next()) { + found = found->get_member(E->get()).m_class; + } + + result.class_type = found; + result.script_path = ref->get_parser()->script_path; + } else { + result.kind = GDScriptParser::DataType::SCRIPT; + } + result.native_type = scr->get_instance_base_type(); } else { - result.kind = GDScriptParser::DataType::SCRIPT; + push_error(vformat(R"(Constant value uses script from "%s" which is loaded but not compiled.)", scr->get_path()), p_source); + result.kind = GDScriptParser::DataType::VARIANT; + result.type_source = GDScriptParser::DataType::UNDETECTED; + result.is_meta_type = false; } - result.native_type = scr->get_instance_base_type(); } else { result.kind = GDScriptParser::DataType::NATIVE; if (result.native_type == GDScriptNativeClass::get_class_static()) { @@ -2965,7 +3029,7 @@ bool GDScriptAnalyzer::is_shadowing(GDScriptParser::IdentifierNode *p_local, con } #endif -GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, const GDScriptParser::DataType &p_b, bool &r_valid) { +GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, const GDScriptParser::DataType &p_b, bool &r_valid, const GDScriptParser::Node *p_source) { // This function creates dummy variant values and apply the operation to those. Less error-prone than keeping a table of valid operations. GDScriptParser::DataType result; @@ -3038,7 +3102,7 @@ GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator Variant::evaluate(p_operation, a, b, ret, r_valid); if (r_valid) { - return type_from_variant(ret); + return type_from_variant(ret, p_source); } return result; @@ -3236,6 +3300,9 @@ Error GDScriptAnalyzer::resolve_program() { List<String> parser_keys; depended_parsers.get_key_list(&parser_keys); for (const List<String>::Element *E = parser_keys.front(); E != nullptr; E = E->next()) { + if (depended_parsers[E->get()].is_null()) { + return ERR_PARSE_ERROR; + } depended_parsers[E->get()]->raise_status(GDScriptParserRef::FULLY_SOLVED); } depended_parsers.clear(); diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h index da767522ad..4e06e0a530 100644 --- a/modules/gdscript/gdscript_analyzer.h +++ b/modules/gdscript/gdscript_analyzer.h @@ -90,7 +90,7 @@ class GDScriptAnalyzer { void reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op); // Helpers. - GDScriptParser::DataType type_from_variant(const Variant &p_value); + GDScriptParser::DataType type_from_variant(const Variant &p_value, const GDScriptParser::Node *p_source); GDScriptParser::DataType type_from_metatype(const GDScriptParser::DataType &p_meta_type) const; GDScriptParser::DataType type_from_property(const PropertyInfo &p_property) const; GDScriptParser::DataType make_global_class_meta_type(const StringName &p_class_name); @@ -98,7 +98,7 @@ class GDScriptAnalyzer { bool function_signature_from_info(const MethodInfo &p_info, GDScriptParser::DataType &r_return_type, List<GDScriptParser::DataType> &r_par_types, int &r_default_arg_count, bool &r_static, bool &r_vararg); bool validate_call_arg(const List<GDScriptParser::DataType> &p_par_types, int p_default_args_count, bool p_is_vararg, const GDScriptParser::CallNode *p_call); bool validate_call_arg(const MethodInfo &p_method, const GDScriptParser::CallNode *p_call); - GDScriptParser::DataType get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, const GDScriptParser::DataType &p_b, bool &r_valid); + GDScriptParser::DataType get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, const GDScriptParser::DataType &p_b, bool &r_valid, const GDScriptParser::Node *p_source); bool is_type_compatible(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion = false) const; void push_error(const String &p_message, const GDScriptParser::Node *p_origin); void mark_node_unsafe(const GDScriptParser::Node *p_node); diff --git a/modules/gdscript/gdscript_cache.cpp b/modules/gdscript/gdscript_cache.cpp index cdb14d6281..992f8f4b58 100644 --- a/modules/gdscript/gdscript_cache.cpp +++ b/modules/gdscript/gdscript_cache.cpp @@ -127,7 +127,7 @@ Ref<GDScriptParserRef> GDScriptCache::get_parser(const String &p_path, GDScriptP singleton->dependencies[p_owner].insert(p_path); } if (singleton->parser_map.has(p_path)) { - ref = singleton->parser_map[p_path]; + ref = Ref<GDScriptParserRef>(singleton->parser_map[p_path]); } else { if (!FileAccess::exists(p_path)) { r_error = ERR_FILE_NOT_FOUND; @@ -137,8 +137,7 @@ Ref<GDScriptParserRef> GDScriptCache::get_parser(const String &p_path, GDScriptP ref.instance(); ref->parser = parser; ref->path = p_path; - singleton->parser_map[p_path] = ref; - ref->unreference(); + singleton->parser_map[p_path] = ref.ptr(); } r_error = ref->raise_status(p_status); @@ -186,10 +185,7 @@ Ref<GDScript> GDScriptCache::get_shallow_script(const String &p_path, const Stri script->set_script_path(p_path); script->load_source_code(p_path); - singleton->shallow_gdscript_cache[p_path] = script; - // The one in cache is not a hard reference: if the script dies somewhere else it's fine. - // Scripts remove themselves from cache when they die. - script->unreference(); + singleton->shallow_gdscript_cache[p_path] = script.ptr(); return script; } @@ -217,7 +213,7 @@ Ref<GDScript> GDScriptCache::get_full_script(const String &p_path, Error &r_erro return script; } - singleton->full_gdscript_cache[p_path] = script; + singleton->full_gdscript_cache[p_path] = script.ptr(); singleton->shallow_gdscript_cache.erase(p_path); return script; @@ -226,7 +222,7 @@ Ref<GDScript> GDScriptCache::get_full_script(const String &p_path, Error &r_erro Error GDScriptCache::finish_compiling(const String &p_owner) { // Mark this as compiled. Ref<GDScript> script = get_shallow_script(p_owner); - singleton->full_gdscript_cache[p_owner] = script; + singleton->full_gdscript_cache[p_owner] = script.ptr(); singleton->shallow_gdscript_cache.erase(p_owner); Set<String> depends = singleton->dependencies[p_owner]; diff --git a/modules/gdscript/gdscript_cache.h b/modules/gdscript/gdscript_cache.h index 770704d6eb..865df34051 100644 --- a/modules/gdscript/gdscript_cache.h +++ b/modules/gdscript/gdscript_cache.h @@ -70,9 +70,9 @@ public: class GDScriptCache { // String key is full path. - HashMap<String, Ref<GDScriptParserRef>> parser_map; - HashMap<String, Ref<GDScript>> shallow_gdscript_cache; - HashMap<String, Ref<GDScript>> full_gdscript_cache; + HashMap<String, GDScriptParserRef *> parser_map; + HashMap<String, GDScript *> shallow_gdscript_cache; + HashMap<String, GDScript *> full_gdscript_cache; HashMap<String, Set<String>> dependencies; friend class GDScript; diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index e34d87f5cc..67a894912f 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -2609,16 +2609,27 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar p_script->_base = base.ptr(); if (p_class->base_type.kind == GDScriptParser::DataType::CLASS && p_class->base_type.class_type != nullptr) { - if (!parsed_classes.has(p_script->_base)) { - if (parsing_classes.has(p_script->_base)) { - String class_name = p_class->identifier ? p_class->identifier->name : "<main>"; - _set_error("Cyclic class reference for '" + class_name + "'.", p_class); - return ERR_PARSE_ERROR; + if (p_class->base_type.script_path == main_script->path) { + if (!parsed_classes.has(p_script->_base)) { + if (parsing_classes.has(p_script->_base)) { + String class_name = p_class->identifier ? p_class->identifier->name : "<main>"; + _set_error("Cyclic class reference for '" + 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; + } } - Error err = _parse_class_level(p_script->_base, p_class->base_type.class_type, p_keep_state); + } else { + Error err = OK; + base = GDScriptCache::get_full_script(p_class->base_type.script_path, err, main_script->path); if (err) { return err; } + if (base.is_null() && !base->is_valid()) { + return ERR_COMPILATION_FAILED; + } } } @@ -2696,11 +2707,7 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar const GDScriptParser::ConstantNode *constant = member.constant; StringName name = constant->identifier->name; - ERR_CONTINUE(constant->initializer->type != GDScriptParser::Node::LITERAL); - - const GDScriptParser::LiteralNode *literal = static_cast<const GDScriptParser::LiteralNode *>(constant->initializer); - - p_script->constants.insert(name, literal->value); + p_script->constants.insert(name, constant->initializer->reduced_value); #ifdef TOOLS_ENABLED p_script->member_lines[name] = constant->start_line; diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 88bf8cde0f..2e372575da 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -483,6 +483,8 @@ String GDScriptLanguage::make_function(const String &p_class, const String &p_na #ifdef TOOLS_ENABLED +#define COMPLETION_RECURSION_LIMIT 200 + struct GDScriptCompletionIdentifier { GDScriptParser::DataType type; String enumeration; @@ -766,9 +768,11 @@ static void _find_identifiers_in_suite(const GDScriptParser::SuiteNode *p_suite, } } -static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base, bool p_only_functions, Map<String, ScriptCodeCompletionOption> &r_result); +static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base, bool p_only_functions, Map<String, ScriptCodeCompletionOption> &r_result, int p_recursion_depth); + +static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class, bool p_only_functions, bool p_static, bool p_parent_only, Map<String, ScriptCodeCompletionOption> &r_result, int p_recursion_depth) { + ERR_FAIL_COND(p_recursion_depth > COMPLETION_RECURSION_LIMIT); -static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class, bool p_only_functions, bool p_static, bool p_parent_only, Map<String, ScriptCodeCompletionOption> &r_result) { if (!p_parent_only) { bool outer = false; const GDScriptParser::ClassNode *clss = p_class; @@ -820,7 +824,6 @@ static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class, break; case GDScriptParser::ClassNode::Member::SIGNAL: if (p_only_functions || outer) { - clss = clss->outer; continue; } option = ScriptCodeCompletionOption(member.signal->identifier->name, ScriptCodeCompletionOption::KIND_SIGNAL); @@ -840,10 +843,12 @@ static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class, base_type.type = p_class->base_type; base_type.type.is_meta_type = p_static; - _find_identifiers_in_base(base_type, p_only_functions, r_result); + _find_identifiers_in_base(base_type, p_only_functions, r_result, p_recursion_depth + 1); } -static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base, bool p_only_functions, Map<String, ScriptCodeCompletionOption> &r_result) { +static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base, bool p_only_functions, Map<String, ScriptCodeCompletionOption> &r_result, int p_recursion_depth) { + ERR_FAIL_COND(p_recursion_depth > COMPLETION_RECURSION_LIMIT); + GDScriptParser::DataType base_type = p_base.type; bool _static = base_type.is_meta_type; @@ -856,7 +861,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base while (!base_type.has_no_type()) { switch (base_type.kind) { case GDScriptParser::DataType::CLASS: { - _find_identifiers_in_class(base_type.class_type, p_only_functions, _static, false, r_result); + _find_identifiers_in_class(base_type.class_type, p_only_functions, _static, false, r_result, p_recursion_depth + 1); // This already finds all parent identifiers, so we are done. base_type = GDScriptParser::DataType(); } break; @@ -1007,14 +1012,14 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base } } -static void _find_identifiers(GDScriptParser::CompletionContext &p_context, bool p_only_functions, Map<String, ScriptCodeCompletionOption> &r_result) { +static void _find_identifiers(GDScriptParser::CompletionContext &p_context, bool p_only_functions, Map<String, ScriptCodeCompletionOption> &r_result, int p_recursion_depth) { if (!p_only_functions && p_context.current_suite) { // This includes function parameters, since they are also locals. _find_identifiers_in_suite(p_context.current_suite, r_result); } if (p_context.current_class) { - _find_identifiers_in_class(p_context.current_class, p_only_functions, (!p_context.current_function || p_context.current_function->is_static), false, r_result); + _find_identifiers_in_class(p_context.current_class, p_only_functions, (!p_context.current_function || p_context.current_function->is_static), false, r_result, p_recursion_depth + 1); } for (int i = 0; i < GDScriptFunctions::FUNC_MAX; i++) { @@ -2453,7 +2458,7 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path break; } if (!_guess_expression_type(completion_context, static_cast<const GDScriptParser::AssignmentNode *>(completion_context.node)->assignee, type)) { - _find_identifiers(completion_context, false, options); + _find_identifiers(completion_context, false, options, 0); r_forced = true; break; } @@ -2462,7 +2467,7 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path _find_enumeration_candidates(completion_context, type.enumeration, options); r_forced = options.size() > 0; } else { - _find_identifiers(completion_context, false, options); + _find_identifiers(completion_context, false, options, 0); r_forced = true; } } break; @@ -2470,7 +2475,7 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path is_function = true; [[fallthrough]]; case GDScriptParser::COMPLETION_IDENTIFIER: { - _find_identifiers(completion_context, is_function, options); + _find_identifiers(completion_context, is_function, options, 0); } break; case GDScriptParser::COMPLETION_ATTRIBUTE_METHOD: is_function = true; @@ -2484,7 +2489,7 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path break; } - _find_identifiers_in_base(base, is_function, options); + _find_identifiers_in_base(base, is_function, options, 0); } } break; case GDScriptParser::COMPLETION_SUBSCRIPT: { @@ -2504,7 +2509,7 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path c.current_class = nullptr; } - _find_identifiers_in_base(base, false, options); + _find_identifiers_in_base(base, false, options, 0); } break; case GDScriptParser::COMPLETION_TYPE_ATTRIBUTE: { if (!completion_context.current_class) { @@ -2530,7 +2535,7 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path // TODO: Improve this to only list types. if (found) { - _find_identifiers_in_base(base, false, options); + _find_identifiers_in_base(base, false, options, 0); } r_forced = true; } break; @@ -2651,7 +2656,7 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path if (!completion_context.current_class) { break; } - _find_identifiers_in_class(completion_context.current_class, true, false, true, options); + _find_identifiers_in_class(completion_context.current_class, true, false, true, options, 0); } break; } diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index aeec1c0379..4761506381 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -586,6 +586,14 @@ GDScriptParser::ClassNode *GDScriptParser::parse_class() { return n_class; } + if (match(GDScriptTokenizer::Token::EXTENDS)) { + if (n_class->extends_used) { + push_error(R"(Cannot use "extends" more than once in the same class.)"); + } + parse_extends(); + end_statement("superclass"); + } + parse_class_body(); consume(GDScriptTokenizer::Token::DEDENT, R"(Missing unindent at the end of the class body.)"); @@ -631,7 +639,6 @@ void GDScriptParser::parse_extends() { current_class->extends_path = previous.literal; if (!match(GDScriptTokenizer::Token::PERIOD)) { - end_statement("superclass path"); return; } } @@ -1356,7 +1363,7 @@ GDScriptParser::Node *GDScriptParser::parse_statement() { advance(); ReturnNode *n_return = alloc_node<ReturnNode>(); if (!is_statement_end()) { - if (current_function->identifier->name == GDScriptLanguage::get_singleton()->strings._init) { + if (current_function && current_function->identifier->name == GDScriptLanguage::get_singleton()->strings._init) { push_error(R"(Constructor cannot return a value.)"); } n_return->return_value = parse_expression(false); @@ -1942,8 +1949,8 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_literal(ExpressionNode *p_ } GDScriptParser::ExpressionNode *GDScriptParser::parse_self(ExpressionNode *p_previous_operand, bool p_can_assign) { - if (!current_function || current_function->is_static) { - push_error(R"(Cannot use "self" outside a non-static function.)"); + if (current_function && current_function->is_static) { + push_error(R"(Cannot use "self" inside a static function.)"); } SelfNode *self = alloc_node<SelfNode>(); self->current_class = current_class; diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp index 737e920693..0145ac39ff 100644 --- a/modules/gdscript/gdscript_tokenizer.cpp +++ b/modules/gdscript/gdscript_tokenizer.cpp @@ -621,7 +621,19 @@ GDScriptTokenizer::Token GDScriptTokenizer::number() { } // Allow '_' to be used in a number, for readability. + bool previous_was_underscore = false; while (digit_check_func(_peek()) || _peek() == '_') { + if (_peek() == '_') { + if (previous_was_underscore) { + Token error = make_error(R"(Only one underscore can be used as a numeric separator.)"); + error.start_column = column; + error.leftmost_column = column; + error.end_column = column + 1; + error.rightmost_column = column + 1; + push_error(error); + } + previous_was_underscore = true; + } _advance(); } @@ -672,7 +684,27 @@ GDScriptTokenizer::Token GDScriptTokenizer::number() { _advance(); } // Consume exponent digits. + if (!_is_digit(_peek())) { + Token error = make_error(R"(Expected exponent value after "e".)"); + error.start_column = column; + error.leftmost_column = column; + error.end_column = column + 1; + error.rightmost_column = column + 1; + push_error(error); + } + previous_was_underscore = false; while (_is_digit(_peek()) || _peek() == '_') { + if (_peek() == '_') { + if (previous_was_underscore) { + Token error = make_error(R"(Only one underscore can be used as a numeric separator.)"); + error.start_column = column; + error.leftmost_column = column; + error.end_column = column + 1; + error.rightmost_column = column + 1; + push_error(error); + } + previous_was_underscore = true; + } _advance(); } } diff --git a/modules/gridmap/doc_classes/GridMap.xml b/modules/gridmap/doc_classes/GridMap.xml index 79220da7c2..57fbc5bfc0 100644 --- a/modules/gridmap/doc_classes/GridMap.xml +++ b/modules/gridmap/doc_classes/GridMap.xml @@ -10,7 +10,7 @@ Internally, a GridMap is split into a sparse collection of octants for efficient rendering and physics processing. Every octant has the same dimensions and can contain several cells. </description> <tutorials> - <link>https://docs.godotengine.org/en/latest/tutorials/3d/using_gridmaps.html</link> + <link title="Using gridmaps">https://docs.godotengine.org/en/latest/tutorials/3d/using_gridmaps.html</link> </tutorials> <methods> <method name="clear"> @@ -205,7 +205,7 @@ GridMaps act as static bodies, meaning they aren't affected by gravity or other forces. They only affect other physics bodies that collide with them. </member> <member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask" default="1"> - The physics layers this GridMap detects collisions in. + The physics layers this GridMap detects collisions in. See [url=https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information. </member> <member name="mesh_library" type="MeshLibrary" setter="set_mesh_library" getter="get_mesh_library"> The assigned [MeshLibrary]. diff --git a/modules/mono/doc_classes/CSharpScript.xml b/modules/mono/doc_classes/CSharpScript.xml index e1e9d1381f..45a6f991bf 100644 --- a/modules/mono/doc_classes/CSharpScript.xml +++ b/modules/mono/doc_classes/CSharpScript.xml @@ -8,7 +8,7 @@ See also [GodotSharp]. </description> <tutorials> - <link>https://docs.godotengine.org/en/latest/getting_started/scripting/c_sharp/index.html</link> + <link title="C# tutorial index">https://docs.godotengine.org/en/latest/getting_started/scripting/c_sharp/index.html</link> </tutorials> <methods> <method name="new" qualifiers="vararg"> diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs index f36e581a5f..7bfba779fb 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs @@ -161,8 +161,21 @@ namespace GodotTools.Build // Try to find 15.0 with vswhere - string vsWherePath = Environment.GetEnvironmentVariable(Internal.GodotIs32Bits() ? "ProgramFiles" : "ProgramFiles(x86)"); - vsWherePath += "\\Microsoft Visual Studio\\Installer\\vswhere.exe"; + var envNames = Internal.GodotIs32Bits() ? new[] { "ProgramFiles", "ProgramW6432" } : new[] { "ProgramFiles(x86)", "ProgramFiles" }; + + string vsWherePath = null; + foreach (var envName in envNames) + { + vsWherePath = Environment.GetEnvironmentVariable(envName); + if (!string.IsNullOrEmpty(vsWherePath)) + { + vsWherePath += "\\Microsoft Visual Studio\\Installer\\vswhere.exe"; + if (File.Exists(vsWherePath)) + break; + } + + vsWherePath = null; + } var vsWhereArgs = new[] {"-latest", "-products", "*", "-requires", "Microsoft.Component.MSBuild"}; diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs index e819848212..599ca94699 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs @@ -19,7 +19,7 @@ namespace GodotTools.Export public class ExportPlugin : EditorExportPlugin { [Flags] - enum I18NCodesets + enum I18NCodesets : long { None = 0, CJK = 1, diff --git a/modules/mono/editor/godotsharp_export.cpp b/modules/mono/editor/godotsharp_export.cpp index b15e9b060a..2edd8c87dc 100644 --- a/modules/mono/editor/godotsharp_export.cpp +++ b/modules/mono/editor/godotsharp_export.cpp @@ -43,6 +43,16 @@ namespace GodotSharpExport { +MonoAssemblyName *new_mono_assembly_name() { + // Mono has no public API to create an empty MonoAssemblyName and the struct is private. + // As such the only way to create it is with a stub name and then clear it. + + MonoAssemblyName *aname = mono_assembly_name_new("stub"); + CRASH_COND(aname == nullptr); + mono_assembly_name_free(aname); // Frees the string fields, not the struct + return aname; +} + struct AssemblyRefInfo { String name; uint16_t major; @@ -67,7 +77,7 @@ AssemblyRefInfo get_assemblyref_name(MonoImage *p_image, int index) { }; } -Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Dictionary &r_assembly_dependencies) { +Error get_assembly_dependencies(GDMonoAssembly *p_assembly, MonoAssemblyName *reusable_aname, const Vector<String> &p_search_dirs, Dictionary &r_assembly_dependencies) { MonoImage *image = p_assembly->get_image(); for (int i = 0; i < mono_image_get_table_rows(image, MONO_TABLE_ASSEMBLYREF); i++) { @@ -79,26 +89,16 @@ Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> continue; } - GDMonoAssembly *ref_assembly = nullptr; - - { - MonoAssemblyName *ref_aname = mono_assembly_name_new("A"); // We can't allocate an empty MonoAssemblyName, hence "A" - CRASH_COND(ref_aname == nullptr); - SCOPE_EXIT { - mono_assembly_name_free(ref_aname); - mono_free(ref_aname); - }; - - mono_assembly_get_assemblyref(image, i, ref_aname); + mono_assembly_get_assemblyref(image, i, reusable_aname); - if (!GDMono::get_singleton()->load_assembly(ref_name, ref_aname, &ref_assembly, /* refonly: */ true, p_search_dirs)) { - ERR_FAIL_V_MSG(ERR_CANT_RESOLVE, "Cannot load assembly (refonly): '" + ref_name + "'."); - } - - r_assembly_dependencies[ref_name] = ref_assembly->get_path(); + GDMonoAssembly *ref_assembly = NULL; + if (!GDMono::get_singleton()->load_assembly(ref_name, reusable_aname, &ref_assembly, /* refonly: */ true, p_search_dirs)) { + ERR_FAIL_V_MSG(ERR_CANT_RESOLVE, "Cannot load assembly (refonly): '" + ref_name + "'."); } - Error err = get_assembly_dependencies(ref_assembly, p_search_dirs, r_assembly_dependencies); + r_assembly_dependencies[ref_name] = ref_assembly->get_path(); + + Error err = get_assembly_dependencies(ref_assembly, reusable_aname, p_search_dirs, r_assembly_dependencies); ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot load one of the dependencies for the assembly: '" + ref_name + "'."); } @@ -130,7 +130,10 @@ Error get_exported_assembly_dependencies(const Dictionary &p_initial_assemblies, ERR_FAIL_COND_V_MSG(!load_success, ERR_CANT_RESOLVE, "Cannot load assembly (refonly): '" + assembly_name + "'."); - Error err = get_assembly_dependencies(assembly, search_dirs, r_assembly_dependencies); + MonoAssemblyName *reusable_aname = new_mono_assembly_name(); + SCOPE_EXIT { mono_free(reusable_aname); }; + + Error err = get_assembly_dependencies(assembly, reusable_aname, search_dirs, r_assembly_dependencies); if (err != OK) { return err; } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs index d851abc6d3..3700a6194f 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs @@ -565,6 +565,9 @@ namespace Godot rgba = rgba.Substring(1); } + // If enabled, use 1 hex digit per channel instead of 2. + // Other sizes aren't in the HTML/CSS spec but we could add them if desired. + bool isShorthand = rgba.Length < 5; bool alpha; if (rgba.Length == 8) @@ -575,47 +578,60 @@ namespace Godot { alpha = false; } + else if (rgba.Length == 4) + { + alpha = true; + } + else if (rgba.Length == 3) + { + alpha = false; + } else { throw new ArgumentOutOfRangeException("Invalid color code. Length is " + rgba.Length + " but a length of 6 or 8 is expected: " + rgba); } - if (alpha) + a = 1.0f; + if (isShorthand) { - a = ParseCol8(rgba, 6) / 255f; - - if (a < 0) + r = ParseCol4(rgba, 0) / 15f; + g = ParseCol4(rgba, 1) / 15f; + b = ParseCol4(rgba, 2) / 15f; + if (alpha) { - throw new ArgumentOutOfRangeException("Invalid color code. Alpha part is not valid hexadecimal: " + rgba); + a = ParseCol4(rgba, 3) / 15f; } } else { - a = 1.0f; + r = ParseCol8(rgba, 0) / 255f; + g = ParseCol8(rgba, 2) / 255f; + b = ParseCol8(rgba, 4) / 255f; + if (alpha) + { + a = ParseCol8(rgba, 6) / 255f; + } } - int from = alpha ? 2 : 0; - - r = ParseCol8(rgba, 0) / 255f; - if (r < 0) { throw new ArgumentOutOfRangeException("Invalid color code. Red part is not valid hexadecimal: " + rgba); } - g = ParseCol8(rgba, 2) / 255f; - if (g < 0) { throw new ArgumentOutOfRangeException("Invalid color code. Green part is not valid hexadecimal: " + rgba); } - b = ParseCol8(rgba, 4) / 255f; - if (b < 0) { throw new ArgumentOutOfRangeException("Invalid color code. Blue part is not valid hexadecimal: " + rgba); } + + if (a < 0) + { + throw new ArgumentOutOfRangeException("Invalid color code. Alpha part is not valid hexadecimal: " + rgba); + } } /// <summary> @@ -751,45 +767,28 @@ namespace Godot value = max; } - private static int ParseCol8(string str, int ofs) + private static int ParseCol4(string str, int ofs) { - int ig = 0; + char character = str[ofs]; - for (int i = 0; i < 2; i++) + if (character >= '0' && character <= '9') { - int c = str[i + ofs]; - int v; - - if (c >= '0' && c <= '9') - { - v = c - '0'; - } - else if (c >= 'a' && c <= 'f') - { - v = c - 'a'; - v += 10; - } - else if (c >= 'A' && c <= 'F') - { - v = c - 'A'; - v += 10; - } - else - { - return -1; - } - - if (i == 0) - { - ig += v * 16; - } - else - { - ig += v; - } + return character - '0'; + } + else if (character >= 'a' && character <= 'f') + { + return character + (10 - 'a'); } + else if (character >= 'A' && character <= 'F') + { + return character + (10 - 'A'); + } + return -1; + } - return ig; + private static int ParseCol8(string str, int ofs) + { + return ParseCol4(str, ofs) * 16 + ParseCol4(str, ofs + 1); } private String ToHex32(float val) @@ -828,46 +827,24 @@ namespace Godot if (color[0] == '#') { - color = color.Substring(1, color.Length - 1); + color = color.Substring(1); } - bool alpha; - - switch (color.Length) + // Check if the amount of hex digits is valid. + int len = color.Length; + if (!(len == 3 || len == 4 || len == 6 || len == 8)) { - case 8: - alpha = true; - break; - case 6: - alpha = false; - break; - default: - return false; + return false; } - if (alpha) - { - if (ParseCol8(color, 0) < 0) + // Check if each hex digit is valid. + for (int i = 0; i < len; i++) { + if (ParseCol4(color, i) == -1) { return false; } } - int from = alpha ? 2 : 0; - - if (ParseCol8(color, from + 0) < 0) - { - return false; - } - if (ParseCol8(color, from + 2) < 0) - { - return false; - } - if (ParseCol8(color, from + 4) < 0) - { - return false; - } - return true; } diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h index 9db4a5f3f0..5958bf3cc1 100644 --- a/modules/mono/mono_gd/gd_mono_utils.h +++ b/modules/mono/mono_gd/gd_mono_utils.h @@ -44,7 +44,8 @@ if (unlikely(m_exc != nullptr)) { \ GDMonoUtils::debug_unhandled_exception(m_exc); \ GD_UNREACHABLE(); \ - } + } else \ + ((void)0) namespace GDMonoUtils { @@ -162,20 +163,24 @@ StringName get_native_godot_class_name(GDMonoClass *p_class); #define GD_MONO_BEGIN_RUNTIME_INVOKE \ int &_runtime_invoke_count_ref = GDMonoUtils::get_runtime_invoke_count_ref(); \ - _runtime_invoke_count_ref += 1; + _runtime_invoke_count_ref += 1; \ + ((void)0) -#define GD_MONO_END_RUNTIME_INVOKE \ - _runtime_invoke_count_ref -= 1; +#define GD_MONO_END_RUNTIME_INVOKE \ + _runtime_invoke_count_ref -= 1; \ + ((void)0) #define GD_MONO_SCOPE_THREAD_ATTACH \ GDMonoUtils::ScopeThreadAttach __gdmono__scope__thread__attach__; \ - (void)__gdmono__scope__thread__attach__; + (void)__gdmono__scope__thread__attach__; \ + ((void)0) #ifdef DEBUG_ENABLED -#define GD_MONO_ASSERT_THREAD_ATTACHED \ - { CRASH_COND(!GDMonoUtils::is_thread_attached()); } +#define GD_MONO_ASSERT_THREAD_ATTACHED \ + CRASH_COND(!GDMonoUtils::is_thread_attached()); \ + ((void)0) #else -#define GD_MONO_ASSERT_THREAD_ATTACHED +#define GD_MONO_ASSERT_THREAD_ATTACHED ((void)0) #endif #endif // GD_MONOUTILS_H diff --git a/modules/mono/utils/macros.h b/modules/mono/utils/macros.h index dc542477f5..c76619cca4 100644 --- a/modules/mono/utils/macros.h +++ b/modules/mono/utils/macros.h @@ -46,7 +46,7 @@ #define GD_UNREACHABLE() \ CRASH_NOW(); \ do { \ - } while (true); + } while (true) #endif namespace gdmono { diff --git a/modules/visual_script/doc_classes/VisualScript.xml b/modules/visual_script/doc_classes/VisualScript.xml index db1ef2adc6..088d84d2ec 100644 --- a/modules/visual_script/doc_classes/VisualScript.xml +++ b/modules/visual_script/doc_classes/VisualScript.xml @@ -9,7 +9,7 @@ You are most likely to use this class via the Visual Script editor or when writing plugins for it. </description> <tutorials> - <link>https://docs.godotengine.org/en/latest/getting_started/scripting/visual_script/index.html</link> + <link title="VisualScript tutorial index">https://docs.godotengine.org/en/latest/getting_started/scripting/visual_script/index.html</link> </tutorials> <methods> <method name="add_custom_signal"> |