diff options
Diffstat (limited to 'modules/gdscript')
| -rw-r--r-- | modules/gdscript/config.py | 1 | ||||
| -rw-r--r-- | modules/gdscript/doc_classes/GDScriptFunctionState.xml | 45 | ||||
| -rw-r--r-- | modules/gdscript/gdscript_analyzer.cpp | 125 | ||||
| -rw-r--r-- | modules/gdscript/gdscript_analyzer.h | 3 | ||||
| -rw-r--r-- | modules/gdscript/gdscript_byte_codegen.cpp | 92 | ||||
| -rw-r--r-- | modules/gdscript/gdscript_byte_codegen.h | 33 | ||||
| -rw-r--r-- | modules/gdscript/gdscript_codegen.h | 7 | ||||
| -rw-r--r-- | modules/gdscript/gdscript_compiler.cpp | 64 | ||||
| -rw-r--r-- | modules/gdscript/gdscript_disassembler.cpp | 5 | ||||
| -rw-r--r-- | modules/gdscript/gdscript_parser.cpp | 10 | ||||
| -rw-r--r-- | modules/gdscript/register_types.cpp | 1 |
11 files changed, 199 insertions, 187 deletions
diff --git a/modules/gdscript/config.py b/modules/gdscript/config.py index 6fc227e7f5..61ce6185a5 100644 --- a/modules/gdscript/config.py +++ b/modules/gdscript/config.py @@ -10,7 +10,6 @@ def get_doc_classes(): return [ "@GDScript", "GDScript", - "GDScriptFunctionState", ] diff --git a/modules/gdscript/doc_classes/GDScriptFunctionState.xml b/modules/gdscript/doc_classes/GDScriptFunctionState.xml deleted file mode 100644 index 5e369b32d9..0000000000 --- a/modules/gdscript/doc_classes/GDScriptFunctionState.xml +++ /dev/null @@ -1,45 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" ?> -<class name="GDScriptFunctionState" inherits="Reference" version="4.0"> - <brief_description> - State of a function call after yielding. - </brief_description> - <description> - FIXME: Outdated docs as of GDScript rewrite in 4.0. - Calling [code]yield[/code] within a function will cause that function to yield and return its current state as an object of this type. The yielded function call can then be resumed later by calling [method resume] on this state object. - </description> - <tutorials> - </tutorials> - <methods> - <method name="is_valid" qualifiers="const"> - <return type="bool"> - </return> - <argument index="0" name="extended_check" type="bool" default="false"> - </argument> - <description> - Check whether the function call may be resumed. This is not the case if the function state was already resumed. - If [code]extended_check[/code] is enabled, it also checks if the associated script and object still exist. The extended check is done in debug mode as part of [method GDScriptFunctionState.resume], but you can use this if you know you may be trying to resume without knowing for sure the object and/or script have survived up to that point. - </description> - </method> - <method name="resume"> - <return type="Variant"> - </return> - <argument index="0" name="arg" type="Variant" default="null"> - </argument> - <description> - Resume execution of the yielded function call. - If handed an argument, return the argument from the [code]yield[/code] call in the yielded function call. You can pass e.g. an [Array] to hand multiple arguments. - This function returns what the resumed function call returns, possibly another function state if yielded again. - </description> - </method> - </methods> - <signals> - <signal name="completed"> - <argument index="0" name="result" type="Variant"> - </argument> - <description> - </description> - </signal> - </signals> - <constants> - </constants> -</class> diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 6b23ab1616..3f2fdc04a5 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -978,17 +978,19 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) { if (!call->arguments[i]->is_constant) { all_is_constant = false; - } else { + } else if (all_is_constant) { args.write[i] = call->arguments[i]->reduced_value; } GDScriptParser::DataType arg_type = call->arguments[i]->get_datatype(); - if (arg_type.kind != GDScriptParser::DataType::BUILTIN) { - all_is_constant = false; - push_error(vformat(R"*(Invalid argument for "range()" call. Argument %d should be int or float but "%s" was given.)*", i + 1, arg_type.to_string()), call->arguments[i]); - } else if (arg_type.builtin_type != Variant::INT && arg_type.builtin_type != Variant::FLOAT) { - all_is_constant = false; - push_error(vformat(R"*(Invalid argument for "range()" call. Argument %d should be int or float but "%s" was given.)*", i + 1, arg_type.to_string()), call->arguments[i]); + if (!arg_type.is_variant()) { + if (arg_type.kind != GDScriptParser::DataType::BUILTIN) { + all_is_constant = false; + push_error(vformat(R"*(Invalid argument for "range()" call. Argument %d should be int or float but "%s" was given.)*", i + 1, arg_type.to_string()), call->arguments[i]); + } else if (arg_type.builtin_type != Variant::INT && arg_type.builtin_type != Variant::FLOAT) { + all_is_constant = false; + push_error(vformat(R"*(Invalid argument for "range()" call. Argument %d should be int or float but "%s" was given.)*", i + 1, arg_type.to_string()), call->arguments[i]); + } } } @@ -1011,11 +1013,15 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) { } } - GDScriptParser::DataType list_type; - list_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; - list_type.kind = GDScriptParser::DataType::BUILTIN; - list_type.builtin_type = Variant::ARRAY; - p_for->list->set_datatype(list_type); + if (p_for->list->is_constant) { + p_for->list->set_datatype(type_from_variant(p_for->list->reduced_value, p_for->list)); + } else { + GDScriptParser::DataType list_type; + list_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; + list_type.kind = GDScriptParser::DataType::BUILTIN; + list_type.builtin_type = Variant::ARRAY; + p_for->list->set_datatype(list_type); + } } } } @@ -2025,9 +2031,17 @@ void GDScriptAnalyzer::reduce_get_node(GDScriptParser::GetNodeNode *p_get_node) p_get_node->set_datatype(result); } -GDScriptParser::DataType GDScriptAnalyzer::make_global_class_meta_type(const StringName &p_class_name) { +GDScriptParser::DataType GDScriptAnalyzer::make_global_class_meta_type(const StringName &p_class_name, const GDScriptParser::Node *p_source) { Ref<GDScriptParserRef> ref = get_parser_for(ScriptServer::get_global_class_path(p_class_name)); - ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED); + Error err = ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED); + + if (err) { + push_error(vformat(R"(Could not resolve class "%s", because of a parser error.)", p_class_name), p_source); + GDScriptParser::DataType type; + type.type_source = GDScriptParser::DataType::UNDETECTED; + type.kind = GDScriptParser::DataType::VARIANT; + return type; + } GDScriptParser::DataType type; type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; @@ -2299,7 +2313,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident } if (ScriptServer::is_global_class(name)) { - p_identifier->set_datatype(make_global_class_meta_type(name)); + p_identifier->set_datatype(make_global_class_meta_type(name, p_identifier)); return; } @@ -2716,7 +2730,7 @@ void GDScriptAnalyzer::reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op) 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, p_unary_op); + result = get_operation_type(p_unary_op->variant_op, 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); @@ -3082,81 +3096,31 @@ 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, 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 GDScriptAnalyzer::get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, bool &r_valid, const GDScriptParser::Node *p_source) { + // Unary version. + GDScriptParser::DataType nil_type; + nil_type.builtin_type = Variant::NIL; + return get_operation_type(p_operation, p_a, nil_type, r_valid, p_source); +} +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) { GDScriptParser::DataType result; result.kind = GDScriptParser::DataType::VARIANT; Variant::Type a_type = p_a.builtin_type; Variant::Type b_type = p_b.builtin_type; - Variant a; - REF a_ref; - if (a_type == Variant::OBJECT) { - a_ref.instance(); - a = a_ref; - } else { - Callable::CallError err; - Variant::construct(a_type, a, nullptr, 0, err); - if (err.error != Callable::CallError::CALL_OK) { - r_valid = false; - ERR_FAIL_V_MSG(result, vformat("Could not construct value of type %s", Variant::get_type_name(a_type))); - } - } - Variant b; - REF b_ref; - if (b_type == Variant::OBJECT) { - b_ref.instance(); - b = b_ref; - } else { - Callable::CallError err; - Variant::construct(b_type, b, nullptr, 0, err); - if (err.error != Callable::CallError::CALL_OK) { - r_valid = false; - ERR_FAIL_V_MSG(result, vformat("Could not construct value of type %s", Variant::get_type_name(b_type))); - } - } - - // Avoid division by zero. - switch (b_type) { - case Variant::INT: - b = 1; - break; - case Variant::FLOAT: - b = 1.0; - break; - case Variant::VECTOR2: - b = Vector2(1.0, 1.0); - break; - case Variant::VECTOR2I: - b = Vector2i(1, 1); - break; - case Variant::VECTOR3: - b = Vector3(1.0, 1.0, 1.0); - break; - case Variant::VECTOR3I: - b = Vector3i(1, 1, 1); - break; - case Variant::COLOR: - b = Color(1.0, 1.0, 1.0, 1.0); - break; - default: - // No change needed. - break; - } + Variant::ValidatedOperatorEvaluator op_eval = Variant::get_validated_operator_evaluator(p_operation, a_type, b_type); - // Avoid error in formatting operator (%) where it doesn't find a placeholder. - if (a_type == Variant::STRING && b_type != Variant::ARRAY) { - a = String("%s"); + if (op_eval == nullptr) { + r_valid = false; + return result; } - Variant ret; - Variant::evaluate(p_operation, a, b, ret, r_valid); + r_valid = true; - if (r_valid) { - return type_from_variant(ret, p_source); - } + result.kind = GDScriptParser::DataType::BUILTIN; + result.builtin_type = Variant::get_operator_return_type(p_operation, a_type, b_type); return result; } @@ -3358,7 +3322,6 @@ Error GDScriptAnalyzer::resolve_program() { } depended_parsers[E->get()]->raise_status(GDScriptParserRef::FULLY_SOLVED); } - depended_parsers.clear(); return parser->errors.empty() ? OK : ERR_PARSE_ERROR; } diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h index 0a952cc621..9925167856 100644 --- a/modules/gdscript/gdscript_analyzer.h +++ b/modules/gdscript/gdscript_analyzer.h @@ -96,12 +96,13 @@ class GDScriptAnalyzer { 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); + GDScriptParser::DataType make_global_class_meta_type(const StringName &p_class_name, const GDScriptParser::Node *p_source); bool get_function_signature(GDScriptParser::Node *p_source, GDScriptParser::DataType base_type, const StringName &p_function, GDScriptParser::DataType &r_return_type, List<GDScriptParser::DataType> &r_par_types, int &r_default_arg_count, bool &r_static, bool &r_vararg); 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, const GDScriptParser::Node *p_source); + GDScriptParser::DataType get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, 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_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp index 5f1c738207..7c20cda39c 100644 --- a/modules/gdscript/gdscript_byte_codegen.cpp +++ b/modules/gdscript/gdscript_byte_codegen.cpp @@ -77,11 +77,22 @@ uint32_t GDScriptByteCodeGenerator::add_or_get_name(const StringName &p_name) { uint32_t GDScriptByteCodeGenerator::add_temporary() { current_temporaries++; - return increase_stack(); + int idx = increase_stack(); +#ifdef DEBUG_ENABLED + temp_stack.push_back(idx); +#endif + return idx; } void GDScriptByteCodeGenerator::pop_temporary() { + ERR_FAIL_COND(current_temporaries == 0); current_stack_size--; +#ifdef DEBUG_ENABLED + if (temp_stack.back()->get() != current_stack_size) { + ERR_PRINT("Mismatched popping of temporary value"); + } + temp_stack.pop_back(); +#endif current_temporaries--; } @@ -111,6 +122,11 @@ void GDScriptByteCodeGenerator::write_start(GDScript *p_script, const StringName } GDScriptFunction *GDScriptByteCodeGenerator::write_end() { +#ifdef DEBUG_ENABLED + if (current_temporaries != 0) { + ERR_PRINT("Non-zero temporary variables at end of function: " + itos(current_temporaries)); + } +#endif append(GDScriptFunction::OPCODE_END, 0); if (constant_map.size()) { @@ -305,7 +321,28 @@ void GDScriptByteCodeGenerator::set_initial_line(int p_line) { #define IS_BUILTIN_TYPE(m_var, m_type) \ (m_var.type.has_type && m_var.type.kind == GDScriptDataType::BUILTIN && m_var.type.builtin_type == m_type) -void GDScriptByteCodeGenerator::write_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand, const Address &p_right_operand) { +void GDScriptByteCodeGenerator::write_unary_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand) { + if (HAS_BUILTIN_TYPE(p_left_operand)) { + // Gather specific operator. + Variant::ValidatedOperatorEvaluator op_func = Variant::get_validated_operator_evaluator(p_operator, p_left_operand.type.builtin_type, Variant::NIL); + + append(GDScriptFunction::OPCODE_OPERATOR_VALIDATED, 3); + append(p_left_operand); + append(Address()); + append(p_target); + append(op_func); + return; + } + + // No specific types, perform variant evaluation. + append(GDScriptFunction::OPCODE_OPERATOR, 3); + append(p_left_operand); + append(Address()); + append(p_target); + append(p_operator); +} + +void GDScriptByteCodeGenerator::write_binary_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand, const Address &p_right_operand) { if (HAS_BUILTIN_TYPE(p_left_operand) && HAS_BUILTIN_TYPE(p_right_operand)) { // Gather specific operator. Variant::ValidatedOperatorEvaluator op_func = Variant::get_validated_operator_evaluator(p_operator, p_left_operand.type.builtin_type, p_right_operand.type.builtin_type); @@ -905,23 +942,39 @@ void GDScriptByteCodeGenerator::write_endif() { if_jmp_addrs.pop_back(); } -void GDScriptByteCodeGenerator::write_for(const Address &p_variable, const Address &p_list) { - int counter_pos = add_temporary() | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS); - int container_pos = add_temporary() | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS); +void GDScriptByteCodeGenerator::start_for(const GDScriptDataType &p_iterator_type, const GDScriptDataType &p_list_type) { + Address counter(Address::LOCAL_VARIABLE, add_local("@counter_pos", p_iterator_type), p_iterator_type); + Address container(Address::LOCAL_VARIABLE, add_local("@container_pos", p_list_type), p_list_type); - current_breaks_to_patch.push_back(List<int>()); + // Store state. + for_counter_variables.push_back(counter); + for_container_variables.push_back(container); +} + +void GDScriptByteCodeGenerator::write_for_assignment(const Address &p_variable, const Address &p_list) { + const Address &container = for_container_variables.back()->get(); // Assign container. append(GDScriptFunction::OPCODE_ASSIGN, 2); - append(container_pos); + append(container); append(p_list); + for_iterator_variables.push_back(p_variable); +} + +void GDScriptByteCodeGenerator::write_for() { + const Address &iterator = for_iterator_variables.back()->get(); + const Address &counter = for_counter_variables.back()->get(); + const Address &container = for_container_variables.back()->get(); + + current_breaks_to_patch.push_back(List<int>()); + GDScriptFunction::Opcode begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN; GDScriptFunction::Opcode iterate_opcode = GDScriptFunction::OPCODE_ITERATE; - if (p_list.type.has_type) { - if (p_list.type.kind == GDScriptDataType::BUILTIN) { - switch (p_list.type.builtin_type) { + if (container.type.has_type) { + if (container.type.kind == GDScriptDataType::BUILTIN) { + switch (container.type.builtin_type) { case Variant::INT: begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_INT; iterate_opcode = GDScriptFunction::OPCODE_ITERATE_INT; @@ -1005,9 +1058,9 @@ void GDScriptByteCodeGenerator::write_for(const Address &p_variable, const Addre // Begin loop. append(begin_opcode, 3); - append(counter_pos); - append(container_pos); - append(p_variable); + append(counter); + append(container); + append(iterator); for_jmp_addrs.push_back(opcodes.size()); append(0); // End of loop address, will be patched. append(GDScriptFunction::OPCODE_JUMP, 0); @@ -1017,9 +1070,9 @@ void GDScriptByteCodeGenerator::write_for(const Address &p_variable, const Addre int continue_addr = opcodes.size(); continue_addrs.push_back(continue_addr); append(iterate_opcode, 3); - append(counter_pos); - append(container_pos); - append(p_variable); + append(counter); + append(container); + append(iterator); for_jmp_addrs.push_back(opcodes.size()); append(0); // Jump destination, will be patched. } @@ -1042,9 +1095,10 @@ void GDScriptByteCodeGenerator::write_endfor() { } current_breaks_to_patch.pop_back(); - // Remove loop temporaries. - pop_temporary(); - pop_temporary(); + // Pop state. + for_iterator_variables.pop_back(); + for_counter_variables.pop_back(); + for_container_variables.pop_back(); } void GDScriptByteCodeGenerator::start_while_condition() { diff --git a/modules/gdscript/gdscript_byte_codegen.h b/modules/gdscript/gdscript_byte_codegen.h index a546920fb2..34d2bb6098 100644 --- a/modules/gdscript/gdscript_byte_codegen.h +++ b/modules/gdscript/gdscript_byte_codegen.h @@ -43,6 +43,7 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator { Vector<int> opcodes; List<Map<StringName, int>> stack_id_stack; Map<StringName, int> stack_identifiers; + List<int> stack_identifiers_counts; Map<StringName, int> local_constants; List<GDScriptFunction::StackDebug> stack_debug; @@ -51,11 +52,16 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator { int current_stack_size = 0; int current_temporaries = 0; + int current_locals = 0; int current_line = 0; int stack_max = 0; int instr_args_max = 0; int ptrcall_max = 0; +#ifdef DEBUG_ENABLED + List<int> temp_stack; +#endif + HashMap<Variant, int, VariantHasher, VariantComparator> constant_map; Map<StringName, int> name_map; #ifdef TOOLS_ENABLED @@ -72,8 +78,12 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator { Map<Variant::ValidatedConstructor, int> constructors_map; Map<MethodBind *, int> method_bind_map; - List<int> if_jmp_addrs; // List since this can be nested. + // Lists since these can be nested. + List<int> if_jmp_addrs; List<int> for_jmp_addrs; + List<Address> for_iterator_variables; + List<Address> for_counter_variables; + List<Address> for_container_variables; List<int> while_jmp_addrs; List<int> continue_addrs; @@ -89,6 +99,7 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator { List<List<int>> match_continues_to_patch; void add_stack_identifier(const StringName &p_id, int p_stackpos) { + current_locals++; stack_identifiers[p_id] = p_stackpos; if (debug_stack) { block_identifiers[p_id] = p_stackpos; @@ -102,17 +113,26 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator { } void push_stack_identifiers() { + stack_identifiers_counts.push_back(current_locals); stack_id_stack.push_back(stack_identifiers); if (debug_stack) { - block_identifier_stack.push_back(block_identifiers); + Map<StringName, int> block_ids(block_identifiers); + block_identifier_stack.push_back(block_ids); block_identifiers.clear(); } } void pop_stack_identifiers() { + current_locals = stack_identifiers_counts.back()->get(); + stack_identifiers_counts.pop_back(); stack_identifiers = stack_id_stack.back()->get(); - current_stack_size = stack_identifiers.size() + current_temporaries; stack_id_stack.pop_back(); +#ifdef DEBUG_ENABLED + if (current_temporaries != 0) { + ERR_PRINT("Leaving block with non-zero temporary variables: " + itos(current_temporaries)); + } +#endif + current_stack_size = current_locals; if (debug_stack) { for (Map<StringName, int>::Element *E = block_identifiers.front(); E; E = E->next()) { @@ -357,7 +377,8 @@ public: #endif virtual void set_initial_line(int p_line) override; - virtual void write_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand, const Address &p_right_operand) override; + virtual void write_unary_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand) override; + virtual void write_binary_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand, const Address &p_right_operand) override; virtual void write_type_test(const Address &p_target, const Address &p_source, const Address &p_type) override; virtual void write_type_test_builtin(const Address &p_target, const Address &p_source, Variant::Type p_type) override; virtual void write_and_left_operand(const Address &p_left_operand) override; @@ -397,7 +418,9 @@ public: virtual void write_if(const Address &p_condition) override; virtual void write_else() override; virtual void write_endif() override; - virtual void write_for(const Address &p_variable, const Address &p_list) override; + virtual void start_for(const GDScriptDataType &p_iterator_type, const GDScriptDataType &p_list_type) override; + virtual void write_for_assignment(const Address &p_variable, const Address &p_list) override; + virtual void write_for() override; virtual void write_endfor() override; virtual void start_while_condition() override; virtual void write_while(const Address &p_condition) override; diff --git a/modules/gdscript/gdscript_codegen.h b/modules/gdscript/gdscript_codegen.h index 2616a34719..35e326c61f 100644 --- a/modules/gdscript/gdscript_codegen.h +++ b/modules/gdscript/gdscript_codegen.h @@ -98,7 +98,8 @@ public: // virtual void alloc_stack(int p_level) = 0; // Is this needed? // virtual void alloc_call(int p_arg_count) = 0; // This might be automatic from other functions. - virtual void write_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand, const Address &p_right_operand) = 0; + virtual void write_unary_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand) = 0; + virtual void write_binary_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand, const Address &p_right_operand) = 0; virtual void write_type_test(const Address &p_target, const Address &p_source, const Address &p_type) = 0; virtual void write_type_test_builtin(const Address &p_target, const Address &p_source, Variant::Type p_type) = 0; virtual void write_and_left_operand(const Address &p_left_operand) = 0; @@ -139,7 +140,9 @@ public: // virtual void write_elseif(const Address &p_condition) = 0; This kind of makes things more difficult for no real benefit. virtual void write_else() = 0; virtual void write_endif() = 0; - virtual void write_for(const Address &p_variable, const Address &p_list) = 0; + virtual void start_for(const GDScriptDataType &p_iterator_type, const GDScriptDataType &p_list_type) = 0; + virtual void write_for_assignment(const Address &p_variable, const Address &p_list) = 0; + virtual void write_for() = 0; virtual void write_endfor() = 0; virtual void start_while_condition() = 0; // Used to allow a jump to the expression evaluation. virtual void write_while(const Address &p_condition) = 0; diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 69fe6d27cc..b41dc15324 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -437,7 +437,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code gen->pop_temporary(); } - return source; + return result; } break; case GDScriptParser::Node::CALL: { const GDScriptParser::CallNode *call = static_cast<const GDScriptParser::CallNode *>(p_expression); @@ -677,7 +677,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code return GDScriptCodeGenerator::Address(); } - gen->write_operator(result, unary->variant_op, operand, GDScriptCodeGenerator::Address()); + gen->write_unary_operator(result, unary->variant_op, operand); if (operand.mode == GDScriptCodeGenerator::Address::TEMPORARY) { gen->pop_temporary(); @@ -745,7 +745,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code GDScriptCodeGenerator::Address left_operand = _parse_expression(codegen, r_error, binary->left_operand); GDScriptCodeGenerator::Address right_operand = _parse_expression(codegen, r_error, binary->right_operand); - gen->write_operator(result, binary->variant_op, left_operand, right_operand); + gen->write_binary_operator(result, binary->variant_op, left_operand, right_operand); if (right_operand.mode == GDScriptCodeGenerator::Address::TEMPORARY) { gen->pop_temporary(); @@ -908,7 +908,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code } else { gen->write_get(value, key, prev_base); } - gen->write_operator(value, assignment->variant_op, value, assigned); + gen->write_binary_operator(value, assignment->variant_op, value, assigned); if (assigned.mode == GDScriptCodeGenerator::Address::TEMPORARY) { gen->pop_temporary(); } @@ -966,7 +966,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code if (assignment->operation != GDScriptParser::AssignmentNode::OP_NONE) { GDScriptCodeGenerator::Address member = codegen.add_temporary(); gen->write_get_member(member, name); - gen->write_operator(assigned, assignment->variant_op, member, assigned); + gen->write_binary_operator(assigned, assignment->variant_op, member, assigned); gen->pop_temporary(); } @@ -1016,7 +1016,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code if (assignment->operation != GDScriptParser::AssignmentNode::OP_NONE) { // Perform operation. op_result = codegen.add_temporary(); - gen->write_operator(op_result, assignment->variant_op, target, assigned); + gen->write_binary_operator(op_result, assignment->variant_op, target, assigned); } else { op_result = assigned; assigned = GDScriptCodeGenerator::Address(); @@ -1072,7 +1072,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c // Check type equality. GDScriptCodeGenerator::Address type_equality_addr = codegen.add_temporary(equality_type); - codegen.generator->write_operator(type_equality_addr, Variant::OP_EQUAL, p_type_addr, literal_type_addr); + codegen.generator->write_binary_operator(type_equality_addr, Variant::OP_EQUAL, p_type_addr, literal_type_addr); codegen.generator->write_and_left_operand(type_equality_addr); // Get literal. @@ -1083,7 +1083,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c // Check value equality. GDScriptCodeGenerator::Address equality_addr = codegen.add_temporary(equality_type); - codegen.generator->write_operator(equality_addr, Variant::OP_EQUAL, p_value_addr, literal_addr); + codegen.generator->write_binary_operator(equality_addr, Variant::OP_EQUAL, p_value_addr, literal_addr); codegen.generator->write_and_right_operand(equality_addr); // AND both together (reuse temporary location). @@ -1135,11 +1135,11 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c codegen.generator->write_call_builtin(result_addr, GDScriptFunctions::TYPE_OF, typeof_args); // Check type equality. - codegen.generator->write_operator(result_addr, Variant::OP_EQUAL, p_type_addr, result_addr); + codegen.generator->write_binary_operator(result_addr, Variant::OP_EQUAL, p_type_addr, result_addr); codegen.generator->write_and_left_operand(result_addr); // Check value equality. - codegen.generator->write_operator(result_addr, Variant::OP_EQUAL, p_value_addr, expr_addr); + codegen.generator->write_binary_operator(result_addr, Variant::OP_EQUAL, p_value_addr, expr_addr); codegen.generator->write_and_right_operand(equality_test_addr); // AND both type and value equality. @@ -1185,7 +1185,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c // Check type equality. GDScriptCodeGenerator::Address result_addr = codegen.add_temporary(temp_type); - codegen.generator->write_operator(result_addr, Variant::OP_EQUAL, p_type_addr, array_type_addr); + codegen.generator->write_binary_operator(result_addr, Variant::OP_EQUAL, p_type_addr, array_type_addr); codegen.generator->write_and_left_operand(result_addr); // Store pattern length in constant map. @@ -1201,7 +1201,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c // Test length compatibility. temp_type.builtin_type = Variant::BOOL; GDScriptCodeGenerator::Address length_compat_addr = codegen.add_temporary(temp_type); - codegen.generator->write_operator(length_compat_addr, p_pattern->rest_used ? Variant::OP_GREATER_EQUAL : Variant::OP_EQUAL, value_length_addr, array_length_addr); + codegen.generator->write_binary_operator(length_compat_addr, p_pattern->rest_used ? Variant::OP_GREATER_EQUAL : Variant::OP_EQUAL, value_length_addr, array_length_addr); codegen.generator->write_and_right_operand(length_compat_addr); // AND type and length check. @@ -1284,7 +1284,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c // Check type equality. GDScriptCodeGenerator::Address result_addr = codegen.add_temporary(temp_type); - codegen.generator->write_operator(result_addr, Variant::OP_EQUAL, p_type_addr, dict_type_addr); + codegen.generator->write_binary_operator(result_addr, Variant::OP_EQUAL, p_type_addr, dict_type_addr); codegen.generator->write_and_left_operand(result_addr); // Store pattern length in constant map. @@ -1300,7 +1300,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c // Test length compatibility. temp_type.builtin_type = Variant::BOOL; GDScriptCodeGenerator::Address length_compat_addr = codegen.add_temporary(temp_type); - codegen.generator->write_operator(length_compat_addr, p_pattern->rest_used ? Variant::OP_GREATER_EQUAL : Variant::OP_EQUAL, value_length_addr, dict_length_addr); + codegen.generator->write_binary_operator(length_compat_addr, p_pattern->rest_used ? Variant::OP_GREATER_EQUAL : Variant::OP_EQUAL, value_length_addr, dict_length_addr); codegen.generator->write_and_right_operand(length_compat_addr); // AND type and length check. @@ -1474,13 +1474,27 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui codegen.start_block(); // Evaluate the match expression. + GDScriptCodeGenerator::Address value_local = codegen.add_local("@match_value", _gdtype_from_datatype(match->test->get_datatype())); GDScriptCodeGenerator::Address value = _parse_expression(codegen, error, match->test); if (error) { return error; } + // Assign to local. + // TODO: This can be improved by passing the target to parse_expression(). + gen->write_assign(value_local, value); + + if (value.mode == GDScriptCodeGenerator::Address::TEMPORARY) { + codegen.generator->pop_temporary(); + } + // Then, let's save the type of the value in the stack too, so we can reuse for later comparisons. - GDScriptCodeGenerator::Address type = codegen.add_temporary(); + GDScriptDataType typeof_type; + typeof_type.has_type = true; + typeof_type.kind = GDScriptDataType::BUILTIN; + typeof_type.builtin_type = Variant::INT; + GDScriptCodeGenerator::Address type = codegen.add_local("@match_type", typeof_type); + Vector<GDScriptCodeGenerator::Address> typeof_args; typeof_args.push_back(value); gen->write_call_builtin(type, GDScriptFunctions::TYPE_OF, typeof_args); @@ -1534,12 +1548,6 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui gen->write_endif(); } - gen->pop_temporary(); - - if (value.mode == GDScriptCodeGenerator::Address::TEMPORARY) { - codegen.generator->pop_temporary(); - } - gen->end_match(); } break; case GDScriptParser::Node::IF: { @@ -1577,12 +1585,20 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui codegen.start_block(); GDScriptCodeGenerator::Address iterator = codegen.add_local(for_n->variable->name, _gdtype_from_datatype(for_n->variable->get_datatype())); + gen->start_for(iterator.type, _gdtype_from_datatype(for_n->list->get_datatype())); + GDScriptCodeGenerator::Address list = _parse_expression(codegen, error, for_n->list); if (error) { return error; } - gen->write_for(iterator, list); + gen->write_for_assignment(iterator, list); + + if (list.mode == GDScriptCodeGenerator::Address::TEMPORARY) { + codegen.generator->pop_temporary(); + } + + gen->write_for(); error = _parse_block(codegen, for_n->loop); if (error) { @@ -1591,10 +1607,6 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui gen->write_endfor(); - if (list.mode == GDScriptCodeGenerator::Address::TEMPORARY) { - codegen.generator->pop_temporary(); - } - codegen.end_block(); } break; case GDScriptParser::Node::WHILE: { diff --git a/modules/gdscript/gdscript_disassembler.cpp b/modules/gdscript/gdscript_disassembler.cpp index c918251772..92a44c57f8 100644 --- a/modules/gdscript/gdscript_disassembler.cpp +++ b/modules/gdscript/gdscript_disassembler.cpp @@ -360,15 +360,12 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const { incr += 4; } break; case OPCODE_CAST_TO_NATIVE: { - Variant class_name = _constants_ptr[_code_ptr[ip + 1]]; - GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(class_name.operator Object *()); - text += "cast native "; text += DADDR(2); text += " = "; text += DADDR(1); text += " as "; - text += nc->get_name(); + text += DADDR(3); incr += 4; } break; diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index fde3662d66..372af204f5 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -184,6 +184,7 @@ void GDScriptParser::push_error(const String &p_message, const Node *p_origin) { #ifdef DEBUG_ENABLED void GDScriptParser::push_warning(const Node *p_source, GDScriptWarning::Code p_code, const String &p_symbol1, const String &p_symbol2, const String &p_symbol3, const String &p_symbol4) { + ERR_FAIL_COND(p_source == nullptr); Vector<String> symbols; if (!p_symbol1.empty()) { symbols.push_back(p_symbol1); @@ -201,6 +202,7 @@ void GDScriptParser::push_warning(const Node *p_source, GDScriptWarning::Code p_ } void GDScriptParser::push_warning(const Node *p_source, GDScriptWarning::Code p_code, const Vector<String> &p_symbols) { + ERR_FAIL_COND(p_source == nullptr); if (is_ignoring_warnings) { return; } @@ -1419,9 +1421,13 @@ GDScriptParser::Node *GDScriptParser::parse_statement() { } #ifdef DEBUG_ENABLED - if (unreachable) { + if (unreachable && result != nullptr) { current_suite->has_unreachable_code = true; - push_warning(result, GDScriptWarning::UNREACHABLE_CODE, current_function->identifier->name); + if (current_function) { + push_warning(result, GDScriptWarning::UNREACHABLE_CODE, current_function->identifier->name); + } else { + // TODO: Properties setters and getters with unreachable code are not being warned + } } #endif diff --git a/modules/gdscript/register_types.cpp b/modules/gdscript/register_types.cpp index 065f01e654..6c2af66c65 100644 --- a/modules/gdscript/register_types.cpp +++ b/modules/gdscript/register_types.cpp @@ -112,7 +112,6 @@ static void _editor_init() { void register_gdscript_types() { ClassDB::register_class<GDScript>(); - ClassDB::register_virtual_class<GDScriptFunctionState>(); script_language_gd = memnew(GDScriptLanguage); ScriptServer::register_language(script_language_gd); |