diff options
Diffstat (limited to 'modules/gdscript/gdscript_compiler.cpp')
-rw-r--r-- | modules/gdscript/gdscript_compiler.cpp | 183 |
1 files changed, 91 insertions, 92 deletions
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index c78dd1528f..5413eadf60 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -85,7 +85,7 @@ void GDScriptCompiler::_set_error(const String &p_error, const GDScriptParser::N } GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::DataType &p_datatype, GDScript *p_owner) { - if (!p_datatype.is_set() || !p_datatype.is_hard_type()) { + if (!p_datatype.is_set() || !p_datatype.is_hard_type() || p_datatype.is_coroutine) { return GDScriptDataType(); } @@ -148,13 +148,8 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D } } break; case GDScriptParser::DataType::ENUM: - result.has_type = true; result.kind = GDScriptDataType::BUILTIN; - if (p_datatype.is_meta_type) { - result.builtin_type = Variant::DICTIONARY; - } else { - result.builtin_type = Variant::INT; - } + result.builtin_type = p_datatype.builtin_type; break; case GDScriptParser::DataType::RESOLVING: case GDScriptParser::DataType::UNRESOLVED: { @@ -196,7 +191,11 @@ static bool _is_exact_type(const PropertyInfo &p_par_type, const GDScriptDataTyp } } -static bool _have_exact_arguments(const MethodBind *p_method, const Vector<GDScriptCodeGenerator::Address> &p_arguments) { +static bool _can_use_ptrcall(const MethodBind *p_method, const Vector<GDScriptCodeGenerator::Address> &p_arguments) { + if (p_method->is_vararg()) { + // ptrcall won't work with vararg methods. + return false; + } if (p_method->get_argument_count() != p_arguments.size()) { // ptrcall won't work with default arguments. return false; @@ -239,7 +238,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code // Try class members. if (_is_class_member_property(codegen, identifier)) { // Get property. - GDScriptCodeGenerator::Address temp = codegen.add_temporary(); // TODO: Could get the type of the class member here. + GDScriptCodeGenerator::Address temp = codegen.add_temporary(_gdtype_from_datatype(p_expression->get_datatype(), codegen.script)); gen->write_get_member(temp, identifier); return temp; } @@ -250,7 +249,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code if (codegen.script->member_indices.has(identifier)) { if (codegen.script->member_indices[identifier].getter != StringName() && codegen.script->member_indices[identifier].getter != codegen.function_name) { // Perform getter. - GDScriptCodeGenerator::Address temp = codegen.add_temporary(); + GDScriptCodeGenerator::Address temp = codegen.add_temporary(codegen.script->member_indices[identifier].data_type); Vector<GDScriptCodeGenerator::Address> args; // No argument needed. gen->write_call_self(temp, codegen.script->member_indices[identifier].getter, args); return temp; @@ -490,17 +489,10 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code } break; case GDScriptParser::Node::CAST: { const GDScriptParser::CastNode *cn = static_cast<const GDScriptParser::CastNode *>(p_expression); - GDScriptParser::DataType og_cast_type = cn->get_datatype(); - GDScriptDataType cast_type = _gdtype_from_datatype(og_cast_type, codegen.script); + GDScriptDataType cast_type = _gdtype_from_datatype(cn->get_datatype(), codegen.script); GDScriptCodeGenerator::Address result; if (cast_type.has_type) { - if (og_cast_type.kind == GDScriptParser::DataType::ENUM) { - // Enum types are usually treated as dictionaries, but in this case we want to cast to an integer. - cast_type.kind = GDScriptDataType::BUILTIN; - cast_type.builtin_type = Variant::INT; - } - // Create temporary for result first since it will be deleted last. result = codegen.add_temporary(cast_type); @@ -519,6 +511,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code } break; case GDScriptParser::Node::CALL: { const GDScriptParser::CallNode *call = static_cast<const GDScriptParser::CallNode *>(p_expression); + bool is_awaited = p_expression == awaited_node; GDScriptDataType type = _gdtype_from_datatype(call->get_datatype(), codegen.script); GDScriptCodeGenerator::Address result; if (p_root) { @@ -563,7 +556,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code self.mode = GDScriptCodeGenerator::Address::SELF; MethodBind *method = ClassDB::get_method(codegen.script->native->get_name(), call->function_name); - if (_have_exact_arguments(method, arguments)) { + if (_can_use_ptrcall(method, arguments)) { // Exact arguments, use ptrcall. gen->write_call_ptrcall(result, self, method, arguments); } else { @@ -573,13 +566,13 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code } else if ((codegen.function_node && codegen.function_node->is_static) || call->function_name == "new") { GDScriptCodeGenerator::Address self; self.mode = GDScriptCodeGenerator::Address::CLASS; - if (within_await) { + if (is_awaited) { gen->write_call_async(result, self, call->function_name, arguments); } else { gen->write_call(result, self, call->function_name, arguments); } } else { - if (within_await) { + if (is_awaited) { gen->write_call_self_async(result, call->function_name, arguments); } else { gen->write_call_self(result, call->function_name, arguments); @@ -601,7 +594,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code if (r_error) { return GDScriptCodeGenerator::Address(); } - if (within_await) { + if (is_awaited) { gen->write_call_async(result, base, call->function_name, arguments); } else if (base.type.has_type && base.type.kind != GDScriptDataType::BUILTIN) { // Native method, use faster path. @@ -613,7 +606,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code } if (ClassDB::class_exists(class_name) && ClassDB::has_method(class_name, call->function_name)) { MethodBind *method = ClassDB::get_method(class_name, call->function_name); - if (_have_exact_arguments(method, arguments)) { + if (_can_use_ptrcall(method, arguments)) { // Exact arguments, use ptrcall. gen->write_call_ptrcall(result, base, method, arguments); } else { @@ -674,9 +667,10 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code const GDScriptParser::AwaitNode *await = static_cast<const GDScriptParser::AwaitNode *>(p_expression); GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(p_expression->get_datatype(), codegen.script)); - within_await = true; + GDScriptParser::ExpressionNode *previous_awaited_node = awaited_node; + awaited_node = await->to_await; GDScriptCodeGenerator::Address argument = _parse_expression(codegen, r_error, await->to_await); - within_await = false; + awaited_node = previous_awaited_node; if (r_error) { return GDScriptCodeGenerator::Address(); } @@ -813,28 +807,6 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code gen->pop_temporary(); } } break; - case GDScriptParser::BinaryOpNode::OP_TYPE_TEST: { - GDScriptCodeGenerator::Address operand = _parse_expression(codegen, r_error, binary->left_operand); - - if (binary->right_operand->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_type(static_cast<const GDScriptParser::IdentifierNode *>(binary->right_operand)->name) != Variant::VARIANT_MAX) { - // `is` with builtin type) - Variant::Type type = GDScriptParser::get_builtin_type(static_cast<const GDScriptParser::IdentifierNode *>(binary->right_operand)->name); - gen->write_type_test_builtin(result, operand, type); - } else { - GDScriptCodeGenerator::Address type = _parse_expression(codegen, r_error, binary->right_operand); - if (r_error) { - return GDScriptCodeGenerator::Address(); - } - gen->write_type_test(result, operand, type); - if (type.mode == GDScriptCodeGenerator::Address::TEMPORARY) { - gen->pop_temporary(); - } - } - - if (operand.mode == GDScriptCodeGenerator::Address::TEMPORARY) { - gen->pop_temporary(); - } - } break; default: { GDScriptCodeGenerator::Address left_operand = _parse_expression(codegen, r_error, binary->left_operand); GDScriptCodeGenerator::Address right_operand = _parse_expression(codegen, r_error, binary->right_operand); @@ -890,6 +862,28 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code return result; } break; + case GDScriptParser::Node::TYPE_TEST: { + const GDScriptParser::TypeTestNode *type_test = static_cast<const GDScriptParser::TypeTestNode *>(p_expression); + GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(type_test->get_datatype(), codegen.script)); + + GDScriptCodeGenerator::Address operand = _parse_expression(codegen, r_error, type_test->operand); + GDScriptDataType test_type = _gdtype_from_datatype(type_test->test_datatype, codegen.script); + if (r_error) { + return GDScriptCodeGenerator::Address(); + } + + if (test_type.has_type) { + gen->write_type_test(result, operand, test_type); + } else { + gen->write_assign_true(result); + } + + if (operand.mode == GDScriptCodeGenerator::Address::TEMPORARY) { + gen->pop_temporary(); + } + + return result; + } break; case GDScriptParser::Node::ASSIGNMENT: { const GDScriptParser::AssignmentNode *assignment = static_cast<const GDScriptParser::AssignmentNode *>(p_expression); @@ -1171,8 +1165,18 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code bool has_operation = assignment->operation != GDScriptParser::AssignmentNode::OP_NONE; if (has_operation) { // Perform operation. - GDScriptCodeGenerator::Address op_result = codegen.add_temporary(_gdtype_from_datatype(assignment->get_datatype(), codegen.script)); GDScriptCodeGenerator::Address og_value = _parse_expression(codegen, r_error, assignment->assignee); + + if (!has_setter && !assignment->use_conversion_assign) { + // If there's nothing special about the assignment, perform the assignment as part of the operator + gen->write_binary_operator(target, assignment->variant_op, og_value, assigned_value); + if (assigned_value.mode == GDScriptCodeGenerator::Address::TEMPORARY) { + gen->pop_temporary(); // Pop assigned value if not done before. + } + return GDScriptCodeGenerator::Address(); + } + + GDScriptCodeGenerator::Address op_result = codegen.add_temporary(_gdtype_from_datatype(assignment->get_datatype(), codegen.script)); gen->write_binary_operator(op_result, assignment->variant_op, og_value, assigned_value); to_assign = op_result; @@ -1855,7 +1859,12 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui } } - gen->write_return(return_value); + if (return_n->void_return) { + // Always return "null", even if the expression is a call to a void function. + gen->write_return(codegen.add_constant(Variant())); + } else { + gen->write_return(return_value); + } if (return_value.mode == GDScriptCodeGenerator::Address::TEMPORARY) { codegen.generator->pop_temporary(); } @@ -1900,14 +1909,6 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui bool initialized = false; if (lv->initializer != nullptr) { - // For typed arrays we need to make sure this is already initialized correctly so typed assignment work. - if (local_type.has_type && local_type.builtin_type == Variant::ARRAY) { - if (local_type.has_container_element_type()) { - codegen.generator->write_construct_typed_array(local, local_type.get_container_element_type(), Vector<GDScriptCodeGenerator::Address>()); - } else { - codegen.generator->write_construct_array(local, Vector<GDScriptCodeGenerator::Address>()); - } - } GDScriptCodeGenerator::Address src_address = _parse_expression(codegen, err, lv->initializer); if (err) { return err; @@ -2029,6 +2030,32 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_ bool is_initializer = p_func && !p_for_lambda && p_func->identifier->name == GDScriptLanguage::get_singleton()->strings._init; bool is_implicit_ready = !p_func && p_for_ready; + if (!p_for_lambda && is_implicit_initializer) { + // Initialize the default values for type variables before anything. + // This avoids crashes if they are accessed with validated calls before being properly initialized. + // It may happen with out-of-order access or with `@onready` variables. + for (const GDScriptParser::ClassNode::Member &member : p_class->members) { + if (member.type != GDScriptParser::ClassNode::Member::VARIABLE) { + continue; + } + + const GDScriptParser::VariableNode *field = member.variable; + GDScriptDataType field_type = _gdtype_from_datatype(field->get_datatype(), codegen.script); + GDScriptCodeGenerator::Address dst_address(GDScriptCodeGenerator::Address::MEMBER, codegen.script->member_indices[field->identifier->name].index, field_type); + + if (field_type.has_type) { + codegen.generator->write_newline(field->start_line); + + if (field_type.has_container_element_type()) { + codegen.generator->write_construct_typed_array(dst_address, field_type.get_container_element_type(), Vector<GDScriptCodeGenerator::Address>()); + } else if (field_type.kind == GDScriptDataType::BUILTIN) { + codegen.generator->write_construct(dst_address, field_type.builtin_type, Vector<GDScriptCodeGenerator::Address>()); + } + // The `else` branch is for objects, in such case we leave it as `null`. + } + } + } + if (!p_for_lambda && (is_implicit_initializer || is_implicit_ready)) { // Initialize class fields. for (int i = 0; i < p_class->members.size(); i++) { @@ -2048,14 +2075,6 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_ // Emit proper line change. codegen.generator->write_newline(field->initializer->start_line); - // For typed arrays we need to make sure this is already initialized correctly so typed assignment work. - if (field_type.has_type && field_type.builtin_type == Variant::ARRAY) { - if (field_type.has_container_element_type()) { - codegen.generator->write_construct_typed_array(dst_address, field_type.get_container_element_type(), Vector<GDScriptCodeGenerator::Address>()); - } else { - codegen.generator->write_construct_array(dst_address, Vector<GDScriptCodeGenerator::Address>()); - } - } GDScriptCodeGenerator::Address src_address = _parse_expression(codegen, r_error, field->initializer, false, true); if (r_error) { memdelete(codegen.generator); @@ -2070,16 +2089,6 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_ if (src_address.mode == GDScriptCodeGenerator::Address::TEMPORARY) { codegen.generator->pop_temporary(); } - } else if (field_type.has_type) { - codegen.generator->write_newline(field->start_line); - - // Initialize with default for type. - if (field_type.has_container_element_type()) { - codegen.generator->write_construct_typed_array(dst_address, field_type.get_container_element_type(), Vector<GDScriptCodeGenerator::Address>()); - } else if (field_type.kind == GDScriptDataType::BUILTIN) { - codegen.generator->write_construct(dst_address, field_type.builtin_type, Vector<GDScriptCodeGenerator::Address>()); - } - // The `else` branch is for objects, in such case we leave it as `null`. } } } @@ -2096,17 +2105,6 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_ return nullptr; } GDScriptCodeGenerator::Address dst_addr = codegen.parameters[parameter->identifier->name]; - - // For typed arrays we need to make sure this is already initialized correctly so typed assignment work. - GDScriptDataType par_type = dst_addr.type; - if (par_type.has_type && par_type.builtin_type == Variant::ARRAY) { - if (par_type.has_container_element_type()) { - codegen.generator->write_construct_typed_array(dst_addr, par_type.get_container_element_type(), Vector<GDScriptCodeGenerator::Address>()); - } else { - codegen.generator->write_construct_array(dst_addr, Vector<GDScriptCodeGenerator::Address>()); - } - } - codegen.generator->write_assign_default_parameter(dst_addr, src_addr, parameter->use_conversion_assign); if (src_addr.mode == GDScriptCodeGenerator::Address::TEMPORARY) { codegen.generator->pop_temporary(); @@ -2295,13 +2293,15 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri GDScriptDataType base_type = _gdtype_from_datatype(p_class->base_type, p_script); + int native_idx = GDScriptLanguage::get_singleton()->get_global_map()[base_type.native_type]; + p_script->native = GDScriptLanguage::get_singleton()->get_global_array()[native_idx]; + ERR_FAIL_COND_V(p_script->native.is_null(), ERR_BUG); + // Inheritance switch (base_type.kind) { - case GDScriptDataType::NATIVE: { - int native_idx = GDScriptLanguage::get_singleton()->get_global_map()[base_type.native_type]; - p_script->native = GDScriptLanguage::get_singleton()->get_global_array()[native_idx]; - ERR_FAIL_COND_V(p_script->native.is_null(), ERR_BUG); - } break; + case GDScriptDataType::NATIVE: + // Nothing more to do. + break; case GDScriptDataType::GDSCRIPT: { Ref<GDScript> base = Ref<GDScript>(base_type.script_type); if (base.is_null()) { @@ -2333,7 +2333,6 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri p_script->base = base; p_script->_base = base.ptr(); p_script->member_indices = base->member_indices; - p_script->native = base->native; } break; default: { _set_error("Parser bug: invalid inheritance.", nullptr); |