diff options
Diffstat (limited to 'modules/gdscript/gdscript_compiler.cpp')
-rw-r--r-- | modules/gdscript/gdscript_compiler.cpp | 330 |
1 files changed, 258 insertions, 72 deletions
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 7f2c401afc..01e5751dc8 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -37,6 +37,7 @@ #include "core/config/engine.h" #include "core/config/project_settings.h" +#include "core/core_string_names.h" bool GDScriptCompiler::_is_class_member_property(CodeGen &codegen, const StringName &p_name) { if (codegen.function_node && codegen.function_node->is_static) { @@ -60,7 +61,7 @@ bool GDScriptCompiler::_is_class_member_property(GDScript *owner, const StringNa scr = scr->_base; } - ERR_FAIL_COND_V(!nc, false); + ERR_FAIL_NULL_V(nc, false); return ClassDB::has_property(nc->get_name(), p_name); } @@ -104,13 +105,24 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D if (p_handle_metatype && p_datatype.is_meta_type) { result.kind = GDScriptDataType::NATIVE; result.builtin_type = Variant::OBJECT; - result.native_type = GDScriptNativeClass::get_class_static(); + // Fixes GH-82255. `GDScriptNativeClass` is obtainable in GDScript, + // but is not a registered and exposed class, so `GDScriptNativeClass` + // is missing from `GDScriptLanguage::get_singleton()->get_global_map()`. + //result.native_type = GDScriptNativeClass::get_class_static(); + result.native_type = Object::get_class_static(); break; } result.kind = GDScriptDataType::NATIVE; - result.native_type = p_datatype.native_type; result.builtin_type = p_datatype.builtin_type; + result.native_type = p_datatype.native_type; + +#ifdef DEBUG_ENABLED + if (unlikely(!GDScriptLanguage::get_singleton()->get_global_map().has(result.native_type))) { + ERR_PRINT(vformat(R"(GDScript bug: Native class "%s" not found.)", result.native_type)); + result.native_type = Object::get_class_static(); + } +#endif } break; case GDScriptParser::DataType::SCRIPT: { if (p_handle_metatype && p_datatype.is_meta_type) { @@ -185,8 +197,8 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D } } - if (p_datatype.has_container_element_type()) { - result.set_container_element_type(_gdtype_from_datatype(p_datatype.get_container_element_type(), p_owner, false)); + for (int i = 0; i < p_datatype.container_element_types.size(); i++) { + result.set_container_element_type(i, _gdtype_from_datatype(p_datatype.get_container_element_type_or_variant(i), p_owner, false)); } return result; @@ -218,27 +230,27 @@ static bool _is_exact_type(const PropertyInfo &p_par_type, const GDScriptDataTyp } } -static bool _can_use_ptrcall(const MethodBind *p_method, const Vector<GDScriptCodeGenerator::Address> &p_arguments) { +static bool _can_use_validate_call(const MethodBind *p_method, const Vector<GDScriptCodeGenerator::Address> &p_arguments) { if (p_method->is_vararg()) { - // ptrcall won't work with vararg methods. + // Validated call won't work with vararg methods. return false; } if (p_method->get_argument_count() != p_arguments.size()) { - // ptrcall won't work with default arguments. + // Validated call won't work with default arguments. return false; } MethodInfo info; ClassDB::get_method_info(p_method->get_instance_class(), p_method->get_name(), &info); - for (int i = 0; i < p_arguments.size(); i++) { - const PropertyInfo &prop = info.arguments[i]; - if (!_is_exact_type(prop, p_arguments[i].type)) { + int i = 0; + for (List<PropertyInfo>::ConstIterator itr = info.arguments.begin(); itr != info.arguments.end(); ++itr, ++i) { + if (!_is_exact_type(*itr, p_arguments[i].type)) { return false; } } return true; } -GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &codegen, Error &r_error, const GDScriptParser::ExpressionNode *p_expression, bool p_root, bool p_initializer, const GDScriptCodeGenerator::Address &p_index_addr) { +GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &codegen, Error &r_error, const GDScriptParser::ExpressionNode *p_expression, bool p_root, bool p_initializer) { if (p_expression->is_constant && !(p_expression->get_datatype().is_meta_type && p_expression->get_datatype().kind == GDScriptParser::DataType::CLASS)) { return codegen.add_constant(p_expression->reduced_value); } @@ -311,9 +323,13 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code if (member.type == GDScriptParser::ClassNode::Member::FUNCTION || member.type == GDScriptParser::ClassNode::Member::SIGNAL) { // Get like it was a property. GDScriptCodeGenerator::Address temp = codegen.add_temporary(); // TODO: Get type here. - GDScriptCodeGenerator::Address self(GDScriptCodeGenerator::Address::SELF); - gen->write_get_named(temp, identifier, self); + GDScriptCodeGenerator::Address base(GDScriptCodeGenerator::Address::SELF); + if (member.type == GDScriptParser::ClassNode::Member::FUNCTION && member.function->is_static) { + base = GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::CLASS); + } + + gen->write_get_named(temp, identifier, base); return temp; } } @@ -330,7 +346,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code scr = scr->_base; } - if (nc && (ClassDB::has_signal(nc->get_name(), identifier) || ClassDB::has_method(nc->get_name(), identifier))) { + if (nc && (identifier == CoreStringNames::get_singleton()->_free || ClassDB::has_signal(nc->get_name(), identifier) || ClassDB::has_method(nc->get_name(), identifier))) { // Get like it was a property. GDScriptCodeGenerator::Address temp = codegen.add_temporary(); // TODO: Get type here. GDScriptCodeGenerator::Address self(GDScriptCodeGenerator::Address::SELF); @@ -496,8 +512,8 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code values.push_back(val); } - if (array_type.has_container_element_type()) { - gen->write_construct_typed_array(result, array_type.get_container_element_type(), values); + if (array_type.has_container_element_type(0)) { + gen->write_construct_typed_array(result, array_type.get_container_element_type(0), values); } else { gen->write_construct_array(result, values); } @@ -601,11 +617,8 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code arguments.push_back(arg); } - if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_type(call->function_name) != Variant::VARIANT_MAX) { - // Construct a built-in type. - Variant::Type vtype = GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name); - - gen->write_construct(result, vtype, arguments); + if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_type(call->function_name) < Variant::VARIANT_MAX) { + gen->write_construct(result, GDScriptParser::get_builtin_type(call->function_name), arguments); } else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && Variant::has_utility_function(call->function_name)) { // Variant utility function. gen->write_call_utility(result, call->function_name, arguments); @@ -628,14 +641,14 @@ 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 (_can_use_ptrcall(method, arguments)) { - // Exact arguments, use ptrcall. - gen->write_call_ptrcall(result, self, method, arguments); + if (_can_use_validate_call(method, arguments)) { + // Exact arguments, use validated call. + gen->write_call_method_bind_validated(result, self, method, arguments); } else { // Not exact arguments, but still can use method bind call. gen->write_call_method_bind(result, self, method, arguments); } - } else if (codegen.is_static || (codegen.function_node && codegen.function_node->is_static) || call->function_name == "new") { + } else if (call->is_static || codegen.is_static || (codegen.function_node && codegen.function_node->is_static) || call->function_name == "new") { GDScriptCodeGenerator::Address self; self.mode = GDScriptCodeGenerator::Address::CLASS; if (is_awaited) { @@ -660,7 +673,15 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code } else if (!call->is_super && subscript->base->type == GDScriptParser::Node::IDENTIFIER && call->function_name != SNAME("new") && ClassDB::class_exists(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name) && !Engine::get_singleton()->has_singleton(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name)) { // It's a static native method call. - gen->write_call_native_static(result, static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name, subscript->attribute->name, arguments); + StringName class_name = static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name; + MethodBind *method = ClassDB::get_method(class_name, subscript->attribute->name); + if (_can_use_validate_call(method, arguments)) { + // Exact arguments, use validated call. + gen->write_call_native_static_validated(result, method, arguments); + } else { + // Not exact arguments, use regular static call + gen->write_call_native_static(result, class_name, subscript->attribute->name, arguments); + } } else { GDScriptCodeGenerator::Address base = _parse_expression(codegen, r_error, subscript->base); if (r_error) { @@ -678,9 +699,9 @@ 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 (_can_use_ptrcall(method, arguments)) { - // Exact arguments, use ptrcall. - gen->write_call_ptrcall(result, base, method, arguments); + if (_can_use_validate_call(method, arguments)) { + // Exact arguments, use validated call. + gen->write_call_method_bind_validated(result, base, method, arguments); } else { // Not exact arguments, but still can use method bind call. gen->write_call_method_bind(result, base, method, arguments); @@ -725,7 +746,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(get_node->get_datatype(), codegen.script)); MethodBind *get_node_method = ClassDB::get_method("Node", "get_node"); - gen->write_call_ptrcall(result, GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::SELF), get_node_method, args); + gen->write_call_method_bind_validated(result, GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::SELF), get_node_method, args); return result; } break; @@ -768,9 +789,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code bool named = subscript->is_attribute; StringName name; GDScriptCodeGenerator::Address index; - if (p_index_addr.mode != GDScriptCodeGenerator::Address::NIL) { - index = p_index_addr; - } else if (subscript->is_attribute) { + if (subscript->is_attribute) { if (subscript->base->type == GDScriptParser::Node::SELF && codegen.script) { GDScriptParser::IdentifierNode *identifier = subscript->attribute; HashMap<StringName, GDScript::MemberInfo>::Iterator MI = codegen.script->member_indices.find(identifier->name); @@ -1363,6 +1382,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code return GDScriptCodeGenerator::Address(); } + codegen.script->lambda_info.insert(function, { (int)lambda->captures.size(), lambda->use_self }); gen->write_lambda(result, function, captures, lambda->use_self); for (int i = 0; i < captures.size(); i++) { @@ -1814,7 +1834,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c ERR_FAIL_V_MSG(p_previous_test, "Reaching the end of pattern compilation without matching a pattern."); } -List<GDScriptCodeGenerator::Address> GDScriptCompiler::_add_locals_in_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block) { +List<GDScriptCodeGenerator::Address> GDScriptCompiler::_add_block_locals(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block) { List<GDScriptCodeGenerator::Address> addresses; for (int i = 0; i < p_block->locals.size(); i++) { if (p_block->locals[i].type == GDScriptParser::SuiteNode::Local::PARAMETER || p_block->locals[i].type == GDScriptParser::SuiteNode::Local::FOR_VARIABLE) { @@ -1826,27 +1846,25 @@ List<GDScriptCodeGenerator::Address> GDScriptCompiler::_add_locals_in_block(Code return addresses; } -// Avoid keeping in the stack long-lived references to objects, which may prevent RefCounted objects from being freed. -void GDScriptCompiler::_clear_addresses(CodeGen &codegen, const List<GDScriptCodeGenerator::Address> &p_addresses) { - for (const List<GDScriptCodeGenerator::Address>::Element *E = p_addresses.front(); E; E = E->next()) { - GDScriptDataType type = E->get().type; - // If not an object and cannot contain an object, no need to clear. - if (type.kind != GDScriptDataType::BUILTIN || type.builtin_type == Variant::ARRAY || type.builtin_type == Variant::DICTIONARY) { - codegen.generator->write_assign_false(E->get()); +// Avoid keeping in the stack long-lived references to objects, which may prevent `RefCounted` objects from being freed. +void GDScriptCompiler::_clear_block_locals(CodeGen &codegen, const List<GDScriptCodeGenerator::Address> &p_locals) { + for (const GDScriptCodeGenerator::Address &local : p_locals) { + if (local.type.can_contain_object()) { + codegen.generator->clear_address(local); } } } -Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block, bool p_add_locals, bool p_reset_locals) { +Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block, bool p_add_locals, bool p_clear_locals) { Error err = OK; GDScriptCodeGenerator *gen = codegen.generator; List<GDScriptCodeGenerator::Address> block_locals; - gen->clean_temporaries(); + gen->clear_temporaries(); codegen.start_block(); if (p_add_locals) { - block_locals = _add_locals_in_block(codegen, p_block); + block_locals = _add_block_locals(codegen, p_block); } for (int i = 0; i < p_block->statements.size(); i++) { @@ -1861,7 +1879,7 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui case GDScriptParser::Node::MATCH: { const GDScriptParser::MatchNode *match = static_cast<const GDScriptParser::MatchNode *>(s); - codegen.start_block(); + codegen.start_block(); // Add an extra block, since the binding pattern and @special variables belong to the branch scope. // Evaluate the match expression. GDScriptCodeGenerator::Address value = codegen.add_local("@match_value", _gdtype_from_datatype(match->test->get_datatype(), codegen.script)); @@ -1902,7 +1920,7 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui codegen.start_block(); // Create an extra block around for binds. // Add locals in block before patterns, so temporaries don't use the stack address for binds. - List<GDScriptCodeGenerator::Address> branch_locals = _add_locals_in_block(codegen, branch->block); + List<GDScriptCodeGenerator::Address> branch_locals = _add_block_locals(codegen, branch->block); #ifdef DEBUG_ENABLED // Add a newline before each branch, since the debugger needs those. @@ -1917,6 +1935,26 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui } } + // If there's a guard, check its condition too. + if (branch->guard_body != nullptr) { + // Do this first so the guard does not run unless the pattern matched. + gen->write_and_left_operand(pattern_result); + + // Don't actually use the block for the guard. + // The binds are already in the locals and we don't want to clear the result of the guard condition before we check the actual match. + GDScriptCodeGenerator::Address guard_result = _parse_expression(codegen, err, static_cast<GDScriptParser::ExpressionNode *>(branch->guard_body->statements[0])); + if (err) { + return err; + } + + gen->write_and_right_operand(guard_result); + gen->write_end_and(pattern_result); + + if (guard_result.mode == GDScriptCodeGenerator::Address::TEMPORARY) { + codegen.generator->pop_temporary(); + } + } + // Check if pattern did match. gen->write_if(pattern_result); @@ -1929,7 +1967,7 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui return err; } - _clear_addresses(codegen, branch_locals); + _clear_block_locals(codegen, branch_locals); codegen.end_block(); // Get out of extra block. } @@ -1971,7 +2009,8 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui case GDScriptParser::Node::FOR: { const GDScriptParser::ForNode *for_n = static_cast<const GDScriptParser::ForNode *>(s); - codegen.start_block(); + codegen.start_block(); // Add an extra block, since the iterator and @special variables belong to the loop scope. + GDScriptCodeGenerator::Address iterator = codegen.add_local(for_n->variable->name, _gdtype_from_datatype(for_n->variable->get_datatype(), codegen.script)); gen->start_for(iterator.type, _gdtype_from_datatype(for_n->list->get_datatype(), codegen.script)); @@ -1989,14 +2028,21 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui gen->write_for(iterator, for_n->use_conversion_assign); - err = _parse_block(codegen, for_n->loop); + // Loop variables must be cleared even when `break`/`continue` is used. + List<GDScriptCodeGenerator::Address> loop_locals = _add_block_locals(codegen, for_n->loop); + + //_clear_block_locals(codegen, loop_locals); // Inside loop, before block - for `continue`. // TODO + + err = _parse_block(codegen, for_n->loop, false); // Don't add locals again. if (err) { return err; } gen->write_endfor(); - codegen.end_block(); + _clear_block_locals(codegen, loop_locals); // Outside loop, after block - for `break` and normal exit. + + codegen.end_block(); // Get out of extra block. } break; case GDScriptParser::Node::WHILE: { const GDScriptParser::WhileNode *while_n = static_cast<const GDScriptParser::WhileNode *>(s); @@ -2014,12 +2060,19 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui codegen.generator->pop_temporary(); } - err = _parse_block(codegen, while_n->loop); + // Loop variables must be cleared even when `break`/`continue` is used. + List<GDScriptCodeGenerator::Address> loop_locals = _add_block_locals(codegen, while_n->loop); + + //_clear_block_locals(codegen, loop_locals); // Inside loop, before block - for `continue`. // TODO + + err = _parse_block(codegen, while_n->loop, false); // Don't add locals again. if (err) { return err; } gen->write_endwhile(); + + _clear_block_locals(codegen, loop_locals); // Outside loop, after block - for `break` and normal exit. } break; case GDScriptParser::Node::BREAK: { gen->write_break(); @@ -2102,21 +2155,16 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui codegen.generator->pop_temporary(); } initialized = true; - } else if (local_type.has_type) { - // Initialize with default for type. - if (local_type.has_container_element_type()) { - codegen.generator->write_construct_typed_array(local, local_type.get_container_element_type(), Vector<GDScriptCodeGenerator::Address>()); - initialized = true; - } else if (local_type.kind == GDScriptDataType::BUILTIN) { - codegen.generator->write_construct(local, local_type.builtin_type, Vector<GDScriptCodeGenerator::Address>()); - initialized = true; - } - // The `else` branch is for objects, in such case we leave it as `null`. + } else if ((local_type.has_type && local_type.kind == GDScriptDataType::BUILTIN) || codegen.generator->is_local_dirty(local)) { + // Initialize with default for the type. Built-in types must always be cleared (they cannot be `null`). + // Objects and untyped variables are assigned to `null` only if the stack address has been re-used and not cleared. + codegen.generator->clear_address(local); + initialized = true; } - // Assigns a null for the unassigned variables in loops. + // Don't check `is_local_dirty()` since the variable must be assigned to `null` **on each iteration**. if (!initialized && p_block->is_in_loop) { - codegen.generator->write_construct(local, Variant::NIL, Vector<GDScriptCodeGenerator::Address>()); + codegen.generator->clear_address(local); } } break; case GDScriptParser::Node::CONSTANT: { @@ -2148,11 +2196,11 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui } break; } - gen->clean_temporaries(); + gen->clear_temporaries(); } - if (p_add_locals && p_reset_locals) { - _clear_addresses(codegen, block_locals); + if (p_add_locals && p_clear_locals) { + _clear_block_locals(codegen, block_locals); } codegen.end_block(); @@ -2247,8 +2295,8 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_ GDScriptCodeGenerator::Address dst_address(GDScriptCodeGenerator::Address::MEMBER, codegen.script->member_indices[field->identifier->name].index, field_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>()); + if (field_type.has_container_element_type(0)) { + codegen.generator->write_construct_typed_array(dst_address, field_type.get_container_element_type(0), Vector<GDScriptCodeGenerator::Address>()); } else if (field_type.kind == GDScriptDataType::BUILTIN) { codegen.generator->write_construct(dst_address, field_type.builtin_type, Vector<GDScriptCodeGenerator::Address>()); } @@ -2437,9 +2485,9 @@ GDScriptFunction *GDScriptCompiler::_make_static_initializer(Error &r_error, GDS if (field_type.has_type) { codegen.generator->write_newline(field->start_line); - if (field_type.has_container_element_type()) { + if (field_type.has_container_element_type(0)) { GDScriptCodeGenerator::Address temp = codegen.add_temporary(field_type); - codegen.generator->write_construct_typed_array(temp, field_type.get_container_element_type(), Vector<GDScriptCodeGenerator::Address>()); + codegen.generator->write_construct_typed_array(temp, field_type.get_container_element_type(0), Vector<GDScriptCodeGenerator::Address>()); codegen.generator->write_set_static_variable(temp, class_addr, p_script->static_variables_indices[field->identifier->name].index); codegen.generator->pop_temporary(); } else if (field_type.kind == GDScriptDataType::BUILTIN) { @@ -2603,6 +2651,7 @@ Error GDScriptCompiler::_prepare_compilation(GDScript *p_script, const GDScriptP p_script->implicit_ready = nullptr; p_script->static_initializer = nullptr; p_script->rpc_config.clear(); + p_script->lambda_info.clear(); p_script->clearing = false; @@ -2617,7 +2666,10 @@ Error GDScriptCompiler::_prepare_compilation(GDScript *p_script, const GDScriptP GDScriptDataType base_type = _gdtype_from_datatype(p_class->base_type, p_script, false); + ERR_FAIL_COND_V_MSG(base_type.native_type == StringName(), ERR_BUG, vformat(R"(Failed to get base class for "%s")", p_script->path)); + 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); @@ -2783,7 +2835,7 @@ Error GDScriptCompiler::_prepare_compilation(GDScript *p_script, const GDScriptP minfo.property_info = prop_info; p_script->member_indices[name] = minfo; - p_script->members.insert(Variant()); + p_script->members.insert(name); } break; case GDScriptParser::ClassNode::Member::FUNCTION: { @@ -3012,6 +3064,132 @@ void GDScriptCompiler::make_scripts(GDScript *p_script, const GDScriptParser::Cl } } +GDScriptCompiler::FunctionLambdaInfo GDScriptCompiler::_get_function_replacement_info(GDScriptFunction *p_func, int p_index, int p_depth, GDScriptFunction *p_parent_func) { + FunctionLambdaInfo info; + info.function = p_func; + info.parent = p_parent_func; + info.script = p_func->get_script(); + info.name = p_func->get_name(); + info.line = p_func->_initial_line; + info.index = p_index; + info.depth = p_depth; + info.capture_count = 0; + info.use_self = false; + info.arg_count = p_func->_argument_count; + info.default_arg_count = p_func->_default_arg_count; + info.sublambdas = _get_function_lambda_replacement_info(p_func, p_depth, p_parent_func); + + ERR_FAIL_NULL_V(info.script, info); + GDScript::LambdaInfo *extra_info = info.script->lambda_info.getptr(p_func); + if (extra_info != nullptr) { + info.capture_count = extra_info->capture_count; + info.use_self = extra_info->use_self; + } else { + info.capture_count = 0; + info.use_self = false; + } + + return info; +} + +Vector<GDScriptCompiler::FunctionLambdaInfo> GDScriptCompiler::_get_function_lambda_replacement_info(GDScriptFunction *p_func, int p_depth, GDScriptFunction *p_parent_func) { + Vector<FunctionLambdaInfo> result; + // Only scrape the lambdas inside p_func. + for (int i = 0; i < p_func->lambdas.size(); ++i) { + result.push_back(_get_function_replacement_info(p_func->lambdas[i], i, p_depth + 1, p_func)); + } + return result; +} + +GDScriptCompiler::ScriptLambdaInfo GDScriptCompiler::_get_script_lambda_replacement_info(GDScript *p_script) { + ScriptLambdaInfo info; + + if (p_script->implicit_initializer) { + info.implicit_initializer_info = _get_function_lambda_replacement_info(p_script->implicit_initializer); + } + if (p_script->implicit_ready) { + info.implicit_ready_info = _get_function_lambda_replacement_info(p_script->implicit_ready); + } + if (p_script->static_initializer) { + info.static_initializer_info = _get_function_lambda_replacement_info(p_script->static_initializer); + } + + for (const KeyValue<StringName, GDScriptFunction *> &E : p_script->member_functions) { + info.member_function_infos.insert(E.key, _get_function_lambda_replacement_info(E.value)); + } + + for (const KeyValue<StringName, Ref<GDScript>> &KV : p_script->get_subclasses()) { + info.subclass_info.insert(KV.key, _get_script_lambda_replacement_info(KV.value.ptr())); + } + + return info; +} + +bool GDScriptCompiler::_do_function_infos_match(const FunctionLambdaInfo &p_old_info, const FunctionLambdaInfo *p_new_info) { + if (p_new_info == nullptr) { + return false; + } + + if (p_new_info->capture_count != p_old_info.capture_count || p_new_info->use_self != p_old_info.use_self) { + return false; + } + + int old_required_arg_count = p_old_info.arg_count - p_old_info.default_arg_count; + int new_required_arg_count = p_new_info->arg_count - p_new_info->default_arg_count; + if (new_required_arg_count > old_required_arg_count || p_new_info->arg_count < old_required_arg_count) { + return false; + } + + return true; +} + +void GDScriptCompiler::_get_function_ptr_replacements(HashMap<GDScriptFunction *, GDScriptFunction *> &r_replacements, const FunctionLambdaInfo &p_old_info, const FunctionLambdaInfo *p_new_info) { + ERR_FAIL_COND(r_replacements.has(p_old_info.function)); + if (!_do_function_infos_match(p_old_info, p_new_info)) { + p_new_info = nullptr; + } + + r_replacements.insert(p_old_info.function, p_new_info != nullptr ? p_new_info->function : nullptr); + _get_function_ptr_replacements(r_replacements, p_old_info.sublambdas, p_new_info != nullptr ? &p_new_info->sublambdas : nullptr); +} + +void GDScriptCompiler::_get_function_ptr_replacements(HashMap<GDScriptFunction *, GDScriptFunction *> &r_replacements, const Vector<FunctionLambdaInfo> &p_old_infos, const Vector<FunctionLambdaInfo> *p_new_infos) { + for (int i = 0; i < p_old_infos.size(); ++i) { + const FunctionLambdaInfo &old_info = p_old_infos[i]; + const FunctionLambdaInfo *new_info = nullptr; + if (p_new_infos != nullptr && p_new_infos->size() == p_old_infos.size()) { + // For now only attempt if the size is the same. + new_info = &p_new_infos->get(i); + } + _get_function_ptr_replacements(r_replacements, old_info, new_info); + } +} + +void GDScriptCompiler::_get_function_ptr_replacements(HashMap<GDScriptFunction *, GDScriptFunction *> &r_replacements, const ScriptLambdaInfo &p_old_info, const ScriptLambdaInfo *p_new_info) { + _get_function_ptr_replacements(r_replacements, p_old_info.implicit_initializer_info, p_new_info != nullptr ? &p_new_info->implicit_initializer_info : nullptr); + _get_function_ptr_replacements(r_replacements, p_old_info.implicit_ready_info, p_new_info != nullptr ? &p_new_info->implicit_ready_info : nullptr); + _get_function_ptr_replacements(r_replacements, p_old_info.static_initializer_info, p_new_info != nullptr ? &p_new_info->static_initializer_info : nullptr); + + for (const KeyValue<StringName, Vector<FunctionLambdaInfo>> &old_kv : p_old_info.member_function_infos) { + _get_function_ptr_replacements(r_replacements, old_kv.value, p_new_info != nullptr ? p_new_info->member_function_infos.getptr(old_kv.key) : nullptr); + } + for (int i = 0; i < p_old_info.other_function_infos.size(); ++i) { + const FunctionLambdaInfo &old_other_info = p_old_info.other_function_infos[i]; + const FunctionLambdaInfo *new_other_info = nullptr; + if (p_new_info != nullptr && p_new_info->other_function_infos.size() == p_old_info.other_function_infos.size()) { + // For now only attempt if the size is the same. + new_other_info = &p_new_info->other_function_infos[i]; + } + // Needs to be called on all old lambdas, even if there's no replacement. + _get_function_ptr_replacements(r_replacements, old_other_info, new_other_info); + } + for (const KeyValue<StringName, ScriptLambdaInfo> &old_kv : p_old_info.subclass_info) { + const ScriptLambdaInfo &old_subinfo = old_kv.value; + const ScriptLambdaInfo *new_subinfo = p_new_info != nullptr ? p_new_info->subclass_info.getptr(old_kv.key) : nullptr; + _get_function_ptr_replacements(r_replacements, old_subinfo, new_subinfo); + } +} + Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_script, bool p_keep_state) { err_line = -1; err_column = -1; @@ -3022,6 +3200,8 @@ Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_scri source = p_script->get_path(); + ScriptLambdaInfo old_lambda_info = _get_script_lambda_replacement_info(p_script); + // Create scripts for subclasses beforehand so they can be referenced make_scripts(p_script, root, p_keep_state); @@ -3037,6 +3217,12 @@ Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_scri return err; } + ScriptLambdaInfo new_lambda_info = _get_script_lambda_replacement_info(p_script); + + HashMap<GDScriptFunction *, GDScriptFunction *> func_ptr_replacements; + _get_function_ptr_replacements(func_ptr_replacements, old_lambda_info, &new_lambda_info); + main_script->_recurse_replace_function_ptrs(func_ptr_replacements); + if (has_static_data && !root->annotated_static_unload) { GDScriptCache::add_static_script(p_script); } |