Path: blob/master/modules/gdscript/gdscript_compiler.cpp
11351 views
/**************************************************************************/1/* gdscript_compiler.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "gdscript_compiler.h"3132#include "gdscript.h"33#include "gdscript_byte_codegen.h"34#include "gdscript_cache.h"35#include "gdscript_utility_functions.h"3637#include "core/config/engine.h"38#include "core/config/project_settings.h"3940#include "scene/scene_string_names.h"4142bool GDScriptCompiler::_is_class_member_property(CodeGen &codegen, const StringName &p_name) {43if (codegen.function_node && codegen.function_node->is_static) {44return false;45}4647if (_is_local_or_parameter(codegen, p_name)) {48return false; //shadowed49}5051return _is_class_member_property(codegen.script, p_name);52}5354bool GDScriptCompiler::_is_class_member_property(GDScript *owner, const StringName &p_name) {55GDScript *scr = owner;56GDScriptNativeClass *nc = nullptr;57while (scr) {58if (scr->native.is_valid()) {59nc = scr->native.ptr();60}61scr = scr->_base;62}6364ERR_FAIL_NULL_V(nc, false);6566return ClassDB::has_property(nc->get_name(), p_name);67}6869bool GDScriptCompiler::_is_local_or_parameter(CodeGen &codegen, const StringName &p_name) {70return codegen.parameters.has(p_name) || codegen.locals.has(p_name);71}7273void GDScriptCompiler::_set_error(const String &p_error, const GDScriptParser::Node *p_node) {74if (!error.is_empty()) {75return;76}7778error = p_error;79if (p_node) {80err_line = p_node->start_line;81err_column = p_node->start_column;82} else {83err_line = 0;84err_column = 0;85}86}8788GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::DataType &p_datatype, GDScript *p_owner, bool p_handle_metatype) {89if (!p_datatype.is_set() || !p_datatype.is_hard_type() || p_datatype.is_coroutine) {90return GDScriptDataType();91}9293GDScriptDataType result;9495switch (p_datatype.kind) {96case GDScriptParser::DataType::VARIANT: {97result.kind = GDScriptDataType::VARIANT;98} break;99case GDScriptParser::DataType::BUILTIN: {100result.kind = GDScriptDataType::BUILTIN;101result.builtin_type = p_datatype.builtin_type;102} break;103case GDScriptParser::DataType::NATIVE: {104if (p_handle_metatype && p_datatype.is_meta_type) {105result.kind = GDScriptDataType::NATIVE;106result.builtin_type = Variant::OBJECT;107// Fixes GH-82255. `GDScriptNativeClass` is obtainable in GDScript,108// but is not a registered and exposed class, so `GDScriptNativeClass`109// is missing from `GDScriptLanguage::get_singleton()->get_global_map()`.110//result.native_type = GDScriptNativeClass::get_class_static();111result.native_type = Object::get_class_static();112break;113}114115result.kind = GDScriptDataType::NATIVE;116result.builtin_type = p_datatype.builtin_type;117result.native_type = p_datatype.native_type;118119#ifdef DEBUG_ENABLED120if (unlikely(!GDScriptLanguage::get_singleton()->get_global_map().has(result.native_type))) {121_set_error(vformat(R"(GDScript bug (please report): Native class "%s" not found.)", result.native_type), nullptr);122return GDScriptDataType();123}124#endif125} break;126case GDScriptParser::DataType::SCRIPT: {127if (p_handle_metatype && p_datatype.is_meta_type) {128result.kind = GDScriptDataType::NATIVE;129result.builtin_type = Variant::OBJECT;130result.native_type = p_datatype.script_type.is_valid() ? p_datatype.script_type->get_class_name() : Script::get_class_static();131break;132}133134result.kind = GDScriptDataType::SCRIPT;135result.builtin_type = p_datatype.builtin_type;136result.script_type_ref = p_datatype.script_type;137result.script_type = result.script_type_ref.ptr();138result.native_type = p_datatype.native_type;139} break;140case GDScriptParser::DataType::CLASS: {141if (p_handle_metatype && p_datatype.is_meta_type) {142result.kind = GDScriptDataType::NATIVE;143result.builtin_type = Variant::OBJECT;144result.native_type = GDScript::get_class_static();145break;146}147148result.kind = GDScriptDataType::GDSCRIPT;149result.builtin_type = p_datatype.builtin_type;150result.native_type = p_datatype.native_type;151152bool is_local_class = parser->has_class(p_datatype.class_type);153154Ref<GDScript> script;155if (is_local_class) {156script = Ref<GDScript>(main_script);157} else {158Error err = OK;159script = GDScriptCache::get_shallow_script(p_datatype.script_path, err, p_owner->path);160if (err) {161_set_error(vformat(R"(Could not find script "%s": %s)", p_datatype.script_path, error_names[err]), nullptr);162return GDScriptDataType();163}164}165166if (script.is_valid()) {167script = Ref<GDScript>(script->find_class(p_datatype.class_type->fqcn));168}169170if (script.is_null()) {171_set_error(vformat(R"(Could not find class "%s" in "%s".)", p_datatype.class_type->fqcn, p_datatype.script_path), nullptr);172return GDScriptDataType();173} else {174// Only hold a strong reference if the owner of the element qualified with this type is not local, to avoid cyclic references (leaks).175// TODO: Might lead to use after free if script_type is a subclass and is used after its parent is freed.176if (!is_local_class) {177result.script_type_ref = script;178}179result.script_type = script.ptr();180result.native_type = p_datatype.native_type;181}182} break;183case GDScriptParser::DataType::ENUM:184if (p_handle_metatype && p_datatype.is_meta_type) {185result.kind = GDScriptDataType::BUILTIN;186result.builtin_type = Variant::DICTIONARY;187break;188}189190result.kind = GDScriptDataType::BUILTIN;191result.builtin_type = p_datatype.builtin_type;192break;193case GDScriptParser::DataType::RESOLVING:194case GDScriptParser::DataType::UNRESOLVED: {195_set_error("Parser bug (please report): converting unresolved type.", nullptr);196return GDScriptDataType();197}198}199200for (int i = 0; i < p_datatype.container_element_types.size(); i++) {201result.set_container_element_type(i, _gdtype_from_datatype(p_datatype.get_container_element_type_or_variant(i), p_owner, false));202}203204return result;205}206207static bool _is_exact_type(const PropertyInfo &p_par_type, const GDScriptDataType &p_arg_type) {208if (!p_arg_type.has_type()) {209return false;210}211if (p_par_type.type == Variant::NIL) {212return false;213}214if (p_par_type.type == Variant::OBJECT) {215if (p_arg_type.kind == GDScriptDataType::BUILTIN) {216return false;217}218StringName class_name;219if (p_arg_type.kind == GDScriptDataType::NATIVE) {220class_name = p_arg_type.native_type;221} else {222class_name = p_arg_type.native_type == StringName() ? p_arg_type.script_type->get_instance_base_type() : p_arg_type.native_type;223}224return p_par_type.class_name == class_name || ClassDB::is_parent_class(class_name, p_par_type.class_name);225} else {226if (p_arg_type.kind != GDScriptDataType::BUILTIN) {227return false;228}229return p_par_type.type == p_arg_type.builtin_type;230}231}232233static bool _can_use_validate_call(const MethodBind *p_method, const Vector<GDScriptCodeGenerator::Address> &p_arguments) {234if (p_method->is_vararg()) {235// Validated call won't work with vararg methods.236return false;237}238if (p_method->get_argument_count() != p_arguments.size()) {239// Validated call won't work with default arguments.240return false;241}242MethodInfo info;243ClassDB::get_method_info(p_method->get_instance_class(), p_method->get_name(), &info);244for (int64_t i = 0; i < info.arguments.size(); ++i) {245if (!_is_exact_type(info.arguments[i], p_arguments[i].type)) {246return false;247}248}249return true;250}251252GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &codegen, Error &r_error, const GDScriptParser::ExpressionNode *p_expression, bool p_root, bool p_initializer) {253if (p_expression->is_constant && !(p_expression->get_datatype().is_meta_type && p_expression->get_datatype().kind == GDScriptParser::DataType::CLASS)) {254return codegen.add_constant(p_expression->reduced_value);255}256257GDScriptCodeGenerator *gen = codegen.generator;258259switch (p_expression->type) {260case GDScriptParser::Node::IDENTIFIER: {261// Look for identifiers in current scope.262const GDScriptParser::IdentifierNode *in = static_cast<const GDScriptParser::IdentifierNode *>(p_expression);263264StringName identifier = in->name;265266switch (in->source) {267// LOCALS.268case GDScriptParser::IdentifierNode::FUNCTION_PARAMETER:269case GDScriptParser::IdentifierNode::LOCAL_VARIABLE:270case GDScriptParser::IdentifierNode::LOCAL_CONSTANT:271case GDScriptParser::IdentifierNode::LOCAL_ITERATOR:272case GDScriptParser::IdentifierNode::LOCAL_BIND: {273// Try function parameters.274if (codegen.parameters.has(identifier)) {275return codegen.parameters[identifier];276}277278// Try local variables and constants.279if (!p_initializer && codegen.locals.has(identifier)) {280return codegen.locals[identifier];281}282} break;283284// MEMBERS.285case GDScriptParser::IdentifierNode::MEMBER_VARIABLE:286case GDScriptParser::IdentifierNode::MEMBER_FUNCTION:287case GDScriptParser::IdentifierNode::MEMBER_SIGNAL:288case GDScriptParser::IdentifierNode::INHERITED_VARIABLE: {289// Try class members.290if (_is_class_member_property(codegen, identifier)) {291// Get property.292GDScriptCodeGenerator::Address temp = codegen.add_temporary(_gdtype_from_datatype(p_expression->get_datatype(), codegen.script));293gen->write_get_member(temp, identifier);294return temp;295}296297// Try members.298if (!codegen.function_node || !codegen.function_node->is_static) {299// Try member variables.300if (codegen.script->member_indices.has(identifier)) {301if (codegen.script->member_indices[identifier].getter != StringName() && codegen.script->member_indices[identifier].getter != codegen.function_name) {302// Perform getter.303GDScriptCodeGenerator::Address temp = codegen.add_temporary(codegen.script->member_indices[identifier].data_type);304Vector<GDScriptCodeGenerator::Address> args; // No argument needed.305gen->write_call_self(temp, codegen.script->member_indices[identifier].getter, args);306return temp;307} else {308// No getter or inside getter: direct member access.309int idx = codegen.script->member_indices[identifier].index;310return GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::MEMBER, idx, codegen.script->get_member_type(identifier));311}312}313}314315// Try methods and signals (can be Callable and Signal).316{317// Search upwards through parent classes:318const GDScriptParser::ClassNode *base_class = codegen.class_node;319while (base_class != nullptr) {320if (base_class->has_member(identifier)) {321const GDScriptParser::ClassNode::Member &member = base_class->get_member(identifier);322if (member.type == GDScriptParser::ClassNode::Member::FUNCTION || member.type == GDScriptParser::ClassNode::Member::SIGNAL) {323// Get like it was a property.324GDScriptCodeGenerator::Address temp = codegen.add_temporary(); // TODO: Get type here.325326GDScriptCodeGenerator::Address base(GDScriptCodeGenerator::Address::SELF);327if (member.type == GDScriptParser::ClassNode::Member::FUNCTION && member.function->is_static) {328base = GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::CLASS);329}330331gen->write_get_named(temp, identifier, base);332return temp;333}334}335base_class = base_class->base_type.class_type;336}337338// Try in native base.339GDScript *scr = codegen.script;340GDScriptNativeClass *nc = nullptr;341while (scr) {342if (scr->native.is_valid()) {343nc = scr->native.ptr();344}345scr = scr->_base;346}347348if (nc && (identifier == CoreStringName(free_) || ClassDB::has_signal(nc->get_name(), identifier) || ClassDB::has_method(nc->get_name(), identifier))) {349// Get like it was a property.350GDScriptCodeGenerator::Address temp = codegen.add_temporary(); // TODO: Get type here.351GDScriptCodeGenerator::Address self(GDScriptCodeGenerator::Address::SELF);352353gen->write_get_named(temp, identifier, self);354return temp;355}356}357} break;358case GDScriptParser::IdentifierNode::MEMBER_CONSTANT:359case GDScriptParser::IdentifierNode::MEMBER_CLASS: {360// Try class constants.361GDScript *owner = codegen.script;362while (owner) {363GDScript *scr = owner;364GDScriptNativeClass *nc = nullptr;365366while (scr) {367if (scr->constants.has(identifier)) {368return codegen.add_constant(scr->constants[identifier]); // TODO: Get type here.369}370if (scr->native.is_valid()) {371nc = scr->native.ptr();372}373scr = scr->_base;374}375376// Class C++ integer constant.377if (nc) {378bool success = false;379int64_t constant = ClassDB::get_integer_constant(nc->get_name(), identifier, &success);380if (success) {381return codegen.add_constant(constant);382}383}384385owner = owner->_owner;386}387} break;388case GDScriptParser::IdentifierNode::STATIC_VARIABLE: {389// Try static variables.390GDScript *scr = codegen.script;391while (scr) {392if (scr->static_variables_indices.has(identifier)) {393if (scr->static_variables_indices[identifier].getter != StringName() && scr->static_variables_indices[identifier].getter != codegen.function_name) {394// Perform getter.395GDScriptCodeGenerator::Address temp = codegen.add_temporary(scr->static_variables_indices[identifier].data_type);396GDScriptCodeGenerator::Address class_addr(GDScriptCodeGenerator::Address::CLASS);397Vector<GDScriptCodeGenerator::Address> args; // No argument needed.398gen->write_call(temp, class_addr, scr->static_variables_indices[identifier].getter, args);399return temp;400} else {401// No getter or inside getter: direct variable access.402GDScriptCodeGenerator::Address temp = codegen.add_temporary(scr->static_variables_indices[identifier].data_type);403GDScriptCodeGenerator::Address _class = codegen.add_constant(scr);404int index = scr->static_variables_indices[identifier].index;405gen->write_get_static_variable(temp, _class, index);406return temp;407}408}409scr = scr->_base;410}411} break;412413// GLOBALS.414case GDScriptParser::IdentifierNode::NATIVE_CLASS:415case GDScriptParser::IdentifierNode::UNDEFINED_SOURCE: {416// Try globals.417if (GDScriptLanguage::get_singleton()->get_global_map().has(identifier)) {418// If it's an autoload singleton, we postpone to load it at runtime.419// This is so one autoload doesn't try to load another before it's compiled.420HashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();421if (autoloads.has(identifier) && autoloads[identifier].is_singleton) {422GDScriptCodeGenerator::Address global = codegen.add_temporary(_gdtype_from_datatype(in->get_datatype(), codegen.script));423int idx = GDScriptLanguage::get_singleton()->get_global_map()[identifier];424gen->write_store_global(global, idx);425return global;426} else {427int idx = GDScriptLanguage::get_singleton()->get_global_map()[identifier];428Variant global = GDScriptLanguage::get_singleton()->get_global_array()[idx];429return codegen.add_constant(global);430}431}432433// Try global classes.434if (ScriptServer::is_global_class(identifier)) {435const GDScriptParser::ClassNode *class_node = codegen.class_node;436while (class_node->outer) {437class_node = class_node->outer;438}439440Ref<Resource> res;441442if (class_node->identifier && class_node->identifier->name == identifier) {443res = Ref<GDScript>(main_script);444} else {445String global_class_path = ScriptServer::get_global_class_path(identifier);446if (ResourceLoader::get_resource_type(global_class_path) == "GDScript") {447Error err = OK;448// Should not need to pass p_owner since analyzer will already have done it.449res = GDScriptCache::get_shallow_script(global_class_path, err);450if (err != OK) {451_set_error("Can't load global class " + String(identifier), p_expression);452r_error = ERR_COMPILATION_FAILED;453return GDScriptCodeGenerator::Address();454}455} else {456res = ResourceLoader::load(global_class_path);457if (res.is_null()) {458_set_error("Can't load global class " + String(identifier) + ", cyclic reference?", p_expression);459r_error = ERR_COMPILATION_FAILED;460return GDScriptCodeGenerator::Address();461}462}463}464465return codegen.add_constant(res);466}467468#ifdef TOOLS_ENABLED469if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(identifier)) {470GDScriptCodeGenerator::Address global = codegen.add_temporary(); // TODO: Get type.471gen->write_store_named_global(global, identifier);472return global;473}474#endif475476} break;477}478479// Not found, error.480_set_error("Identifier not found: " + String(identifier), p_expression);481r_error = ERR_COMPILATION_FAILED;482return GDScriptCodeGenerator::Address();483} break;484case GDScriptParser::Node::LITERAL: {485// Return constant.486const GDScriptParser::LiteralNode *cn = static_cast<const GDScriptParser::LiteralNode *>(p_expression);487488return codegen.add_constant(cn->value);489} break;490case GDScriptParser::Node::SELF: {491//return constant492if (codegen.function_node && codegen.function_node->is_static) {493_set_error("'self' not present in static function.", p_expression);494r_error = ERR_COMPILATION_FAILED;495return GDScriptCodeGenerator::Address();496}497return GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::SELF);498} break;499case GDScriptParser::Node::ARRAY: {500const GDScriptParser::ArrayNode *an = static_cast<const GDScriptParser::ArrayNode *>(p_expression);501Vector<GDScriptCodeGenerator::Address> values;502503// Create the result temporary first since it's the last to be killed.504GDScriptDataType array_type = _gdtype_from_datatype(an->get_datatype(), codegen.script);505GDScriptCodeGenerator::Address result = codegen.add_temporary(array_type);506507for (int i = 0; i < an->elements.size(); i++) {508GDScriptCodeGenerator::Address val = _parse_expression(codegen, r_error, an->elements[i]);509if (r_error) {510return GDScriptCodeGenerator::Address();511}512values.push_back(val);513}514515if (array_type.has_container_element_type(0)) {516gen->write_construct_typed_array(result, array_type.get_container_element_type(0), values);517} else {518gen->write_construct_array(result, values);519}520521for (int i = 0; i < values.size(); i++) {522if (values[i].mode == GDScriptCodeGenerator::Address::TEMPORARY) {523gen->pop_temporary();524}525}526527return result;528} break;529case GDScriptParser::Node::DICTIONARY: {530const GDScriptParser::DictionaryNode *dn = static_cast<const GDScriptParser::DictionaryNode *>(p_expression);531Vector<GDScriptCodeGenerator::Address> elements;532533// Create the result temporary first since it's the last to be killed.534GDScriptDataType dict_type = _gdtype_from_datatype(dn->get_datatype(), codegen.script);535GDScriptCodeGenerator::Address result = codegen.add_temporary(dict_type);536537for (int i = 0; i < dn->elements.size(); i++) {538// Key.539GDScriptCodeGenerator::Address element;540switch (dn->style) {541case GDScriptParser::DictionaryNode::PYTHON_DICT:542// Python-style: key is any expression.543element = _parse_expression(codegen, r_error, dn->elements[i].key);544if (r_error) {545return GDScriptCodeGenerator::Address();546}547break;548case GDScriptParser::DictionaryNode::LUA_TABLE:549// Lua-style: key is an identifier interpreted as StringName.550StringName key = dn->elements[i].key->reduced_value.operator StringName();551element = codegen.add_constant(key);552break;553}554555elements.push_back(element);556557element = _parse_expression(codegen, r_error, dn->elements[i].value);558if (r_error) {559return GDScriptCodeGenerator::Address();560}561562elements.push_back(element);563}564565if (dict_type.has_container_element_types()) {566gen->write_construct_typed_dictionary(result, dict_type.get_container_element_type_or_variant(0), dict_type.get_container_element_type_or_variant(1), elements);567} else {568gen->write_construct_dictionary(result, elements);569}570571for (int i = 0; i < elements.size(); i++) {572if (elements[i].mode == GDScriptCodeGenerator::Address::TEMPORARY) {573gen->pop_temporary();574}575}576577return result;578} break;579case GDScriptParser::Node::CAST: {580const GDScriptParser::CastNode *cn = static_cast<const GDScriptParser::CastNode *>(p_expression);581GDScriptDataType cast_type = _gdtype_from_datatype(cn->get_datatype(), codegen.script, false);582583GDScriptCodeGenerator::Address result;584if (cast_type.has_type()) {585// Create temporary for result first since it will be deleted last.586result = codegen.add_temporary(cast_type);587588GDScriptCodeGenerator::Address src = _parse_expression(codegen, r_error, cn->operand);589590gen->write_cast(result, src, cast_type);591592if (src.mode == GDScriptCodeGenerator::Address::TEMPORARY) {593gen->pop_temporary();594}595} else {596result = _parse_expression(codegen, r_error, cn->operand);597}598599return result;600} break;601case GDScriptParser::Node::CALL: {602const GDScriptParser::CallNode *call = static_cast<const GDScriptParser::CallNode *>(p_expression);603bool is_awaited = p_expression == awaited_node;604GDScriptDataType type = _gdtype_from_datatype(call->get_datatype(), codegen.script);605GDScriptCodeGenerator::Address result;606if (p_root) {607result = GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::NIL);608} else {609result = codegen.add_temporary(type);610}611612Vector<GDScriptCodeGenerator::Address> arguments;613for (int i = 0; i < call->arguments.size(); i++) {614GDScriptCodeGenerator::Address arg = _parse_expression(codegen, r_error, call->arguments[i]);615if (r_error) {616return GDScriptCodeGenerator::Address();617}618arguments.push_back(arg);619}620621if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_type(call->function_name) < Variant::VARIANT_MAX) {622gen->write_construct(result, GDScriptParser::get_builtin_type(call->function_name), arguments);623} else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && Variant::has_utility_function(call->function_name)) {624// Variant utility function.625gen->write_call_utility(result, call->function_name, arguments);626} else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptUtilityFunctions::function_exists(call->function_name)) {627// GDScript utility function.628gen->write_call_gdscript_utility(result, call->function_name, arguments);629} else {630// Regular function.631const GDScriptParser::ExpressionNode *callee = call->callee;632633if (call->is_super) {634// Super call.635gen->write_super_call(result, call->function_name, arguments);636} else {637if (callee->type == GDScriptParser::Node::IDENTIFIER) {638// Self function call.639if (ClassDB::has_method(codegen.script->native->get_name(), call->function_name)) {640// Native method, use faster path.641GDScriptCodeGenerator::Address self;642self.mode = GDScriptCodeGenerator::Address::SELF;643MethodBind *method = ClassDB::get_method(codegen.script->native->get_name(), call->function_name);644645if (_can_use_validate_call(method, arguments)) {646// Exact arguments, use validated call.647gen->write_call_method_bind_validated(result, self, method, arguments);648} else {649// Not exact arguments, but still can use method bind call.650gen->write_call_method_bind(result, self, method, arguments);651}652} else if (call->is_static || codegen.is_static || (codegen.function_node && codegen.function_node->is_static) || call->function_name == "new") {653GDScriptCodeGenerator::Address self;654self.mode = GDScriptCodeGenerator::Address::CLASS;655if (is_awaited) {656gen->write_call_async(result, self, call->function_name, arguments);657} else {658gen->write_call(result, self, call->function_name, arguments);659}660} else {661if (is_awaited) {662gen->write_call_self_async(result, call->function_name, arguments);663} else {664gen->write_call_self(result, call->function_name, arguments);665}666}667} else if (callee->type == GDScriptParser::Node::SUBSCRIPT) {668const GDScriptParser::SubscriptNode *subscript = static_cast<const GDScriptParser::SubscriptNode *>(call->callee);669670if (subscript->is_attribute) {671// May be static built-in method call.672if (!call->is_super && subscript->base->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name) < Variant::VARIANT_MAX) {673gen->write_call_builtin_type_static(result, GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name), subscript->attribute->name, arguments);674} else if (!call->is_super && subscript->base->type == GDScriptParser::Node::IDENTIFIER && call->function_name != SNAME("new") &&675static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->source == GDScriptParser::IdentifierNode::NATIVE_CLASS && !Engine::get_singleton()->has_singleton(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name)) {676// It's a static native method call.677StringName class_name = static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name;678MethodBind *method = ClassDB::get_method(class_name, subscript->attribute->name);679if (_can_use_validate_call(method, arguments)) {680// Exact arguments, use validated call.681gen->write_call_native_static_validated(result, method, arguments);682} else {683// Not exact arguments, use regular static call684gen->write_call_native_static(result, class_name, subscript->attribute->name, arguments);685}686} else {687GDScriptCodeGenerator::Address base = _parse_expression(codegen, r_error, subscript->base);688if (r_error) {689return GDScriptCodeGenerator::Address();690}691if (is_awaited) {692gen->write_call_async(result, base, call->function_name, arguments);693} else if (base.type.kind != GDScriptDataType::VARIANT && base.type.kind != GDScriptDataType::BUILTIN) {694// Native method, use faster path.695StringName class_name;696if (base.type.kind == GDScriptDataType::NATIVE) {697class_name = base.type.native_type;698} else {699class_name = base.type.native_type == StringName() ? base.type.script_type->get_instance_base_type() : base.type.native_type;700}701if (ClassDB::class_exists(class_name) && ClassDB::has_method(class_name, call->function_name)) {702MethodBind *method = ClassDB::get_method(class_name, call->function_name);703if (_can_use_validate_call(method, arguments)) {704// Exact arguments, use validated call.705gen->write_call_method_bind_validated(result, base, method, arguments);706} else {707// Not exact arguments, but still can use method bind call.708gen->write_call_method_bind(result, base, method, arguments);709}710} else {711gen->write_call(result, base, call->function_name, arguments);712}713} else if (base.type.kind == GDScriptDataType::BUILTIN) {714gen->write_call_builtin_type(result, base, base.type.builtin_type, call->function_name, arguments);715} else {716gen->write_call(result, base, call->function_name, arguments);717}718if (base.mode == GDScriptCodeGenerator::Address::TEMPORARY) {719gen->pop_temporary();720}721}722} else {723_set_error("Cannot call something that isn't a function.", call->callee);724r_error = ERR_COMPILATION_FAILED;725return GDScriptCodeGenerator::Address();726}727} else {728_set_error("Compiler bug (please report): incorrect callee type in call node.", call->callee);729r_error = ERR_COMPILATION_FAILED;730return GDScriptCodeGenerator::Address();731}732}733}734735for (int i = 0; i < arguments.size(); i++) {736if (arguments[i].mode == GDScriptCodeGenerator::Address::TEMPORARY) {737gen->pop_temporary();738}739}740return result;741} break;742case GDScriptParser::Node::GET_NODE: {743const GDScriptParser::GetNodeNode *get_node = static_cast<const GDScriptParser::GetNodeNode *>(p_expression);744745Vector<GDScriptCodeGenerator::Address> args;746args.push_back(codegen.add_constant(NodePath(get_node->full_path)));747748GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(get_node->get_datatype(), codegen.script));749750MethodBind *get_node_method = ClassDB::get_method("Node", "get_node");751gen->write_call_method_bind_validated(result, GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::SELF), get_node_method, args);752753return result;754} break;755case GDScriptParser::Node::PRELOAD: {756const GDScriptParser::PreloadNode *preload = static_cast<const GDScriptParser::PreloadNode *>(p_expression);757758// Add resource as constant.759return codegen.add_constant(preload->resource);760} break;761case GDScriptParser::Node::AWAIT: {762const GDScriptParser::AwaitNode *await = static_cast<const GDScriptParser::AwaitNode *>(p_expression);763764GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(p_expression->get_datatype(), codegen.script));765GDScriptParser::ExpressionNode *previous_awaited_node = awaited_node;766awaited_node = await->to_await;767GDScriptCodeGenerator::Address argument = _parse_expression(codegen, r_error, await->to_await);768awaited_node = previous_awaited_node;769if (r_error) {770return GDScriptCodeGenerator::Address();771}772773gen->write_await(result, argument);774775if (argument.mode == GDScriptCodeGenerator::Address::TEMPORARY) {776gen->pop_temporary();777}778779return result;780} break;781// Indexing operator.782case GDScriptParser::Node::SUBSCRIPT: {783const GDScriptParser::SubscriptNode *subscript = static_cast<const GDScriptParser::SubscriptNode *>(p_expression);784GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(subscript->get_datatype(), codegen.script));785786GDScriptCodeGenerator::Address base = _parse_expression(codegen, r_error, subscript->base);787if (r_error) {788return GDScriptCodeGenerator::Address();789}790791bool named = subscript->is_attribute;792StringName name;793GDScriptCodeGenerator::Address index;794if (subscript->is_attribute) {795if (subscript->base->type == GDScriptParser::Node::SELF && codegen.script) {796GDScriptParser::IdentifierNode *identifier = subscript->attribute;797HashMap<StringName, GDScript::MemberInfo>::Iterator MI = codegen.script->member_indices.find(identifier->name);798799#ifdef DEBUG_ENABLED800if (MI && MI->value.getter == codegen.function_name) {801String n = identifier->name;802_set_error("Must use '" + n + "' instead of 'self." + n + "' in getter.", identifier);803r_error = ERR_COMPILATION_FAILED;804return GDScriptCodeGenerator::Address();805}806#endif807808if (MI && MI->value.getter == "") {809// Remove result temp as we don't need it.810gen->pop_temporary();811// Faster than indexing self (as if no self. had been used).812return GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::MEMBER, MI->value.index, _gdtype_from_datatype(subscript->get_datatype(), codegen.script));813}814}815816name = subscript->attribute->name;817named = true;818} else {819if (subscript->index->is_constant && subscript->index->reduced_value.get_type() == Variant::STRING_NAME) {820// Also, somehow, named (speed up anyway).821name = subscript->index->reduced_value;822named = true;823} else {824// Regular indexing.825index = _parse_expression(codegen, r_error, subscript->index);826if (r_error) {827return GDScriptCodeGenerator::Address();828}829}830}831832if (named) {833gen->write_get_named(result, name, base);834} else {835gen->write_get(result, index, base);836}837838if (index.mode == GDScriptCodeGenerator::Address::TEMPORARY) {839gen->pop_temporary();840}841if (base.mode == GDScriptCodeGenerator::Address::TEMPORARY) {842gen->pop_temporary();843}844845return result;846} break;847case GDScriptParser::Node::UNARY_OPERATOR: {848const GDScriptParser::UnaryOpNode *unary = static_cast<const GDScriptParser::UnaryOpNode *>(p_expression);849850GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(unary->get_datatype(), codegen.script));851852GDScriptCodeGenerator::Address operand = _parse_expression(codegen, r_error, unary->operand);853if (r_error) {854return GDScriptCodeGenerator::Address();855}856857gen->write_unary_operator(result, unary->variant_op, operand);858859if (operand.mode == GDScriptCodeGenerator::Address::TEMPORARY) {860gen->pop_temporary();861}862863return result;864}865case GDScriptParser::Node::BINARY_OPERATOR: {866const GDScriptParser::BinaryOpNode *binary = static_cast<const GDScriptParser::BinaryOpNode *>(p_expression);867868GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(binary->get_datatype(), codegen.script));869870switch (binary->operation) {871case GDScriptParser::BinaryOpNode::OP_LOGIC_AND: {872// AND operator with early out on failure.873GDScriptCodeGenerator::Address left_operand = _parse_expression(codegen, r_error, binary->left_operand);874gen->write_and_left_operand(left_operand);875GDScriptCodeGenerator::Address right_operand = _parse_expression(codegen, r_error, binary->right_operand);876gen->write_and_right_operand(right_operand);877878gen->write_end_and(result);879880if (right_operand.mode == GDScriptCodeGenerator::Address::TEMPORARY) {881gen->pop_temporary();882}883if (left_operand.mode == GDScriptCodeGenerator::Address::TEMPORARY) {884gen->pop_temporary();885}886} break;887case GDScriptParser::BinaryOpNode::OP_LOGIC_OR: {888// OR operator with early out on success.889GDScriptCodeGenerator::Address left_operand = _parse_expression(codegen, r_error, binary->left_operand);890gen->write_or_left_operand(left_operand);891GDScriptCodeGenerator::Address right_operand = _parse_expression(codegen, r_error, binary->right_operand);892gen->write_or_right_operand(right_operand);893894gen->write_end_or(result);895896if (right_operand.mode == GDScriptCodeGenerator::Address::TEMPORARY) {897gen->pop_temporary();898}899if (left_operand.mode == GDScriptCodeGenerator::Address::TEMPORARY) {900gen->pop_temporary();901}902} break;903default: {904GDScriptCodeGenerator::Address left_operand = _parse_expression(codegen, r_error, binary->left_operand);905GDScriptCodeGenerator::Address right_operand = _parse_expression(codegen, r_error, binary->right_operand);906907gen->write_binary_operator(result, binary->variant_op, left_operand, right_operand);908909if (right_operand.mode == GDScriptCodeGenerator::Address::TEMPORARY) {910gen->pop_temporary();911}912if (left_operand.mode == GDScriptCodeGenerator::Address::TEMPORARY) {913gen->pop_temporary();914}915}916}917return result;918} break;919case GDScriptParser::Node::TERNARY_OPERATOR: {920// x IF a ELSE y operator with early out on failure.921const GDScriptParser::TernaryOpNode *ternary = static_cast<const GDScriptParser::TernaryOpNode *>(p_expression);922GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(ternary->get_datatype(), codegen.script));923924gen->write_start_ternary(result);925926GDScriptCodeGenerator::Address condition = _parse_expression(codegen, r_error, ternary->condition);927if (r_error) {928return GDScriptCodeGenerator::Address();929}930gen->write_ternary_condition(condition);931932if (condition.mode == GDScriptCodeGenerator::Address::TEMPORARY) {933gen->pop_temporary();934}935936GDScriptCodeGenerator::Address true_expr = _parse_expression(codegen, r_error, ternary->true_expr);937if (r_error) {938return GDScriptCodeGenerator::Address();939}940gen->write_ternary_true_expr(true_expr);941if (true_expr.mode == GDScriptCodeGenerator::Address::TEMPORARY) {942gen->pop_temporary();943}944945GDScriptCodeGenerator::Address false_expr = _parse_expression(codegen, r_error, ternary->false_expr);946if (r_error) {947return GDScriptCodeGenerator::Address();948}949gen->write_ternary_false_expr(false_expr);950if (false_expr.mode == GDScriptCodeGenerator::Address::TEMPORARY) {951gen->pop_temporary();952}953954gen->write_end_ternary();955956return result;957} break;958case GDScriptParser::Node::TYPE_TEST: {959const GDScriptParser::TypeTestNode *type_test = static_cast<const GDScriptParser::TypeTestNode *>(p_expression);960GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(type_test->get_datatype(), codegen.script));961962GDScriptCodeGenerator::Address operand = _parse_expression(codegen, r_error, type_test->operand);963GDScriptDataType test_type = _gdtype_from_datatype(type_test->test_datatype, codegen.script, false);964if (r_error) {965return GDScriptCodeGenerator::Address();966}967968if (test_type.has_type()) {969gen->write_type_test(result, operand, test_type);970} else {971gen->write_assign_true(result);972}973974if (operand.mode == GDScriptCodeGenerator::Address::TEMPORARY) {975gen->pop_temporary();976}977978return result;979} break;980case GDScriptParser::Node::ASSIGNMENT: {981const GDScriptParser::AssignmentNode *assignment = static_cast<const GDScriptParser::AssignmentNode *>(p_expression);982983if (assignment->assignee->type == GDScriptParser::Node::SUBSCRIPT) {984// SET (chained) MODE!985const GDScriptParser::SubscriptNode *subscript = static_cast<GDScriptParser::SubscriptNode *>(assignment->assignee);986#ifdef DEBUG_ENABLED987if (subscript->is_attribute && subscript->base->type == GDScriptParser::Node::SELF && codegen.script) {988HashMap<StringName, GDScript::MemberInfo>::Iterator MI = codegen.script->member_indices.find(subscript->attribute->name);989if (MI && MI->value.setter == codegen.function_name) {990String n = subscript->attribute->name;991_set_error("Must use '" + n + "' instead of 'self." + n + "' in setter.", subscript);992r_error = ERR_COMPILATION_FAILED;993return GDScriptCodeGenerator::Address();994}995}996#endif997/* Find chain of sets */998999StringName assign_class_member_property;10001001GDScriptCodeGenerator::Address target_member_property;1002bool is_member_property = false;1003bool member_property_has_setter = false;1004bool member_property_is_in_setter = false;1005bool is_static = false;1006GDScriptCodeGenerator::Address static_var_class;1007int static_var_index = 0;1008GDScriptDataType static_var_data_type;1009StringName var_name;1010StringName member_property_setter_function;10111012List<const GDScriptParser::SubscriptNode *> chain;10131014{1015// Create get/set chain.1016const GDScriptParser::SubscriptNode *n = subscript;1017while (true) {1018chain.push_back(n);1019if (n->base->type != GDScriptParser::Node::SUBSCRIPT) {1020// Check for a property.1021if (n->base->type == GDScriptParser::Node::IDENTIFIER) {1022GDScriptParser::IdentifierNode *identifier = static_cast<GDScriptParser::IdentifierNode *>(n->base);1023var_name = identifier->name;1024if (_is_class_member_property(codegen, var_name)) {1025assign_class_member_property = var_name;1026} else if (!_is_local_or_parameter(codegen, var_name)) {1027if (codegen.script->member_indices.has(var_name)) {1028is_member_property = true;1029is_static = false;1030const GDScript::MemberInfo &minfo = codegen.script->member_indices[var_name];1031member_property_setter_function = minfo.setter;1032member_property_has_setter = member_property_setter_function != StringName();1033member_property_is_in_setter = member_property_has_setter && member_property_setter_function == codegen.function_name;1034target_member_property.mode = GDScriptCodeGenerator::Address::MEMBER;1035target_member_property.address = minfo.index;1036target_member_property.type = minfo.data_type;1037} else {1038// Try static variables.1039GDScript *scr = codegen.script;1040while (scr) {1041if (scr->static_variables_indices.has(var_name)) {1042is_member_property = true;1043is_static = true;1044const GDScript::MemberInfo &minfo = scr->static_variables_indices[var_name];1045member_property_setter_function = minfo.setter;1046member_property_has_setter = member_property_setter_function != StringName();1047member_property_is_in_setter = member_property_has_setter && member_property_setter_function == codegen.function_name;1048static_var_class = codegen.add_constant(scr);1049static_var_index = minfo.index;1050static_var_data_type = minfo.data_type;1051break;1052}1053scr = scr->_base;1054}1055}1056}1057}1058break;1059}1060n = static_cast<const GDScriptParser::SubscriptNode *>(n->base);1061}1062}10631064/* Chain of gets */10651066// Get at (potential) root stack pos, so it can be returned.1067GDScriptCodeGenerator::Address base = _parse_expression(codegen, r_error, chain.back()->get()->base);1068const bool base_known_type = base.type.has_type();1069const bool base_is_shared = Variant::is_type_shared(base.type.builtin_type);10701071if (r_error) {1072return GDScriptCodeGenerator::Address();1073}10741075GDScriptCodeGenerator::Address prev_base = base;10761077// In case the base has a setter, don't use the address directly, as we want to call that setter.1078// So use a temp value instead and call the setter at the end.1079GDScriptCodeGenerator::Address base_temp;1080if ((!base_known_type || !base_is_shared) && base.mode == GDScriptCodeGenerator::Address::MEMBER && member_property_has_setter && !member_property_is_in_setter) {1081base_temp = codegen.add_temporary(base.type);1082gen->write_assign(base_temp, base);1083prev_base = base_temp;1084}10851086struct ChainInfo {1087bool is_named = false;1088GDScriptCodeGenerator::Address base;1089GDScriptCodeGenerator::Address key;1090StringName name;1091};10921093List<ChainInfo> set_chain;10941095for (List<const GDScriptParser::SubscriptNode *>::Element *E = chain.back(); E; E = E->prev()) {1096if (E == chain.front()) {1097// Skip the main subscript, since we'll assign to that.1098break;1099}1100const GDScriptParser::SubscriptNode *subscript_elem = E->get();1101GDScriptCodeGenerator::Address value = codegen.add_temporary(_gdtype_from_datatype(subscript_elem->get_datatype(), codegen.script));1102GDScriptCodeGenerator::Address key;1103StringName name;11041105if (subscript_elem->is_attribute) {1106name = subscript_elem->attribute->name;1107gen->write_get_named(value, name, prev_base);1108} else {1109key = _parse_expression(codegen, r_error, subscript_elem->index);1110if (r_error) {1111return GDScriptCodeGenerator::Address();1112}1113gen->write_get(value, key, prev_base);1114}11151116// Store base and key for setting it back later.1117set_chain.push_front({ subscript_elem->is_attribute, prev_base, key, name }); // Push to front to invert the list.1118prev_base = value;1119}11201121// Get value to assign.1122GDScriptCodeGenerator::Address assigned = _parse_expression(codegen, r_error, assignment->assigned_value);1123if (r_error) {1124return GDScriptCodeGenerator::Address();1125}1126// Get the key if needed.1127GDScriptCodeGenerator::Address key;1128StringName name;1129if (subscript->is_attribute) {1130name = subscript->attribute->name;1131} else {1132key = _parse_expression(codegen, r_error, subscript->index);1133if (r_error) {1134return GDScriptCodeGenerator::Address();1135}1136}11371138// Perform operator if any.1139if (assignment->operation != GDScriptParser::AssignmentNode::OP_NONE) {1140GDScriptCodeGenerator::Address op_result = codegen.add_temporary(_gdtype_from_datatype(assignment->get_datatype(), codegen.script));1141GDScriptCodeGenerator::Address value = codegen.add_temporary(_gdtype_from_datatype(subscript->get_datatype(), codegen.script));1142if (subscript->is_attribute) {1143gen->write_get_named(value, name, prev_base);1144} else {1145gen->write_get(value, key, prev_base);1146}1147gen->write_binary_operator(op_result, assignment->variant_op, value, assigned);1148gen->pop_temporary();1149if (assigned.mode == GDScriptCodeGenerator::Address::TEMPORARY) {1150gen->pop_temporary();1151}1152assigned = op_result;1153}11541155// Perform assignment.1156if (subscript->is_attribute) {1157gen->write_set_named(prev_base, name, assigned);1158} else {1159gen->write_set(prev_base, key, assigned);1160}1161if (key.mode == GDScriptCodeGenerator::Address::TEMPORARY) {1162gen->pop_temporary();1163}1164if (assigned.mode == GDScriptCodeGenerator::Address::TEMPORARY) {1165gen->pop_temporary();1166}11671168assigned = prev_base;11691170// Set back the values into their bases.1171for (const ChainInfo &info : set_chain) {1172bool known_type = assigned.type.has_type();1173bool is_shared = Variant::is_type_shared(assigned.type.builtin_type);11741175if (!known_type || !is_shared) {1176if (!known_type) {1177// Jump shared values since they are already updated in-place.1178gen->write_jump_if_shared(assigned);1179}1180if (!info.is_named) {1181gen->write_set(info.base, info.key, assigned);1182} else {1183gen->write_set_named(info.base, info.name, assigned);1184}1185if (!known_type) {1186gen->write_end_jump_if_shared();1187}1188}1189if (!info.is_named && info.key.mode == GDScriptCodeGenerator::Address::TEMPORARY) {1190gen->pop_temporary();1191}1192if (assigned.mode == GDScriptCodeGenerator::Address::TEMPORARY) {1193gen->pop_temporary();1194}1195assigned = info.base;1196}11971198bool known_type = assigned.type.has_type();1199bool is_shared = Variant::is_type_shared(assigned.type.builtin_type);12001201if (!known_type || !is_shared) {1202// If this is a class member property, also assign to it.1203// This allow things like: position.x += 2.01204if (assign_class_member_property != StringName()) {1205if (!known_type) {1206gen->write_jump_if_shared(assigned);1207}1208gen->write_set_member(assigned, assign_class_member_property);1209if (!known_type) {1210gen->write_end_jump_if_shared();1211}1212} else if (is_member_property) {1213// Same as above but for script members.1214if (!known_type) {1215gen->write_jump_if_shared(assigned);1216}1217if (member_property_has_setter && !member_property_is_in_setter) {1218Vector<GDScriptCodeGenerator::Address> args;1219args.push_back(assigned);1220GDScriptCodeGenerator::Address call_base = is_static ? GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::CLASS) : GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::SELF);1221gen->write_call(GDScriptCodeGenerator::Address(), call_base, member_property_setter_function, args);1222} else if (is_static) {1223GDScriptCodeGenerator::Address temp = codegen.add_temporary(static_var_data_type);1224gen->write_assign(temp, assigned);1225gen->write_set_static_variable(temp, static_var_class, static_var_index);1226gen->pop_temporary();1227} else {1228gen->write_assign(target_member_property, assigned);1229}1230if (!known_type) {1231gen->write_end_jump_if_shared();1232}1233}1234} else if (base_temp.mode == GDScriptCodeGenerator::Address::TEMPORARY) {1235if (!base_known_type) {1236gen->write_jump_if_shared(base);1237}1238// Save the temp value back to the base by calling its setter.1239gen->write_call(GDScriptCodeGenerator::Address(), base, member_property_setter_function, { assigned });1240if (!base_known_type) {1241gen->write_end_jump_if_shared();1242}1243}12441245if (assigned.mode == GDScriptCodeGenerator::Address::TEMPORARY) {1246gen->pop_temporary();1247}1248} else if (assignment->assignee->type == GDScriptParser::Node::IDENTIFIER && _is_class_member_property(codegen, static_cast<GDScriptParser::IdentifierNode *>(assignment->assignee)->name)) {1249// Assignment to member property.1250GDScriptCodeGenerator::Address assigned_value = _parse_expression(codegen, r_error, assignment->assigned_value);1251if (r_error) {1252return GDScriptCodeGenerator::Address();1253}12541255GDScriptCodeGenerator::Address to_assign = assigned_value;1256bool has_operation = assignment->operation != GDScriptParser::AssignmentNode::OP_NONE;12571258StringName name = static_cast<GDScriptParser::IdentifierNode *>(assignment->assignee)->name;12591260if (has_operation) {1261GDScriptCodeGenerator::Address op_result = codegen.add_temporary(_gdtype_from_datatype(assignment->get_datatype(), codegen.script));1262GDScriptCodeGenerator::Address member = codegen.add_temporary(_gdtype_from_datatype(assignment->assignee->get_datatype(), codegen.script));1263gen->write_get_member(member, name);1264gen->write_binary_operator(op_result, assignment->variant_op, member, assigned_value);1265gen->pop_temporary(); // Pop member temp.1266to_assign = op_result;1267}12681269gen->write_set_member(to_assign, name);12701271if (to_assign.mode == GDScriptCodeGenerator::Address::TEMPORARY) {1272gen->pop_temporary(); // Pop the assigned expression or the temp result if it has operation.1273}1274if (has_operation && assigned_value.mode == GDScriptCodeGenerator::Address::TEMPORARY) {1275gen->pop_temporary(); // Pop the assigned expression if not done before.1276}1277} else {1278// Regular assignment.1279if (assignment->assignee->type != GDScriptParser::Node::IDENTIFIER) {1280_set_error("Compiler bug (please report): Expected the assignee to be an identifier here.", assignment->assignee);1281r_error = ERR_COMPILATION_FAILED;1282return GDScriptCodeGenerator::Address();1283}1284GDScriptCodeGenerator::Address member;1285bool is_member = false;1286bool has_setter = false;1287bool is_in_setter = false;1288bool is_static = false;1289GDScriptCodeGenerator::Address static_var_class;1290int static_var_index = 0;1291GDScriptDataType static_var_data_type;1292StringName var_name;1293StringName setter_function;1294var_name = static_cast<const GDScriptParser::IdentifierNode *>(assignment->assignee)->name;1295if (!_is_local_or_parameter(codegen, var_name)) {1296if (codegen.script->member_indices.has(var_name)) {1297is_member = true;1298is_static = false;1299GDScript::MemberInfo &minfo = codegen.script->member_indices[var_name];1300setter_function = minfo.setter;1301has_setter = setter_function != StringName();1302is_in_setter = has_setter && setter_function == codegen.function_name;1303member.mode = GDScriptCodeGenerator::Address::MEMBER;1304member.address = minfo.index;1305member.type = minfo.data_type;1306} else {1307// Try static variables.1308GDScript *scr = codegen.script;1309while (scr) {1310if (scr->static_variables_indices.has(var_name)) {1311is_member = true;1312is_static = true;1313GDScript::MemberInfo &minfo = scr->static_variables_indices[var_name];1314setter_function = minfo.setter;1315has_setter = setter_function != StringName();1316is_in_setter = has_setter && setter_function == codegen.function_name;1317static_var_class = codegen.add_constant(scr);1318static_var_index = minfo.index;1319static_var_data_type = minfo.data_type;1320break;1321}1322scr = scr->_base;1323}1324}1325}13261327GDScriptCodeGenerator::Address target;1328if (is_member) {1329target = member; // _parse_expression could call its getter, but we want to know the actual address1330} else {1331target = _parse_expression(codegen, r_error, assignment->assignee);1332if (r_error) {1333return GDScriptCodeGenerator::Address();1334}1335}13361337GDScriptCodeGenerator::Address assigned_value = _parse_expression(codegen, r_error, assignment->assigned_value);1338if (r_error) {1339return GDScriptCodeGenerator::Address();1340}13411342GDScriptCodeGenerator::Address to_assign;1343bool has_operation = assignment->operation != GDScriptParser::AssignmentNode::OP_NONE;1344if (has_operation) {1345// Perform operation.1346GDScriptCodeGenerator::Address op_result = codegen.add_temporary(_gdtype_from_datatype(assignment->get_datatype(), codegen.script));1347GDScriptCodeGenerator::Address og_value = _parse_expression(codegen, r_error, assignment->assignee);1348gen->write_binary_operator(op_result, assignment->variant_op, og_value, assigned_value);1349to_assign = op_result;13501351if (og_value.mode == GDScriptCodeGenerator::Address::TEMPORARY) {1352gen->pop_temporary();1353}1354} else {1355to_assign = assigned_value;1356}13571358if (has_setter && !is_in_setter) {1359// Call setter.1360Vector<GDScriptCodeGenerator::Address> args;1361args.push_back(to_assign);1362GDScriptCodeGenerator::Address call_base = is_static ? GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::CLASS) : GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::SELF);1363gen->write_call(GDScriptCodeGenerator::Address(), call_base, setter_function, args);1364} else if (is_static) {1365GDScriptCodeGenerator::Address temp = codegen.add_temporary(static_var_data_type);1366if (assignment->use_conversion_assign) {1367gen->write_assign_with_conversion(temp, to_assign);1368} else {1369gen->write_assign(temp, to_assign);1370}1371gen->write_set_static_variable(temp, static_var_class, static_var_index);1372gen->pop_temporary();1373} else {1374// Just assign.1375if (assignment->use_conversion_assign) {1376gen->write_assign_with_conversion(target, to_assign);1377} else {1378gen->write_assign(target, to_assign);1379}1380}13811382if (to_assign.mode == GDScriptCodeGenerator::Address::TEMPORARY) {1383gen->pop_temporary(); // Pop assigned value or temp operation result.1384}1385if (has_operation && assigned_value.mode == GDScriptCodeGenerator::Address::TEMPORARY) {1386gen->pop_temporary(); // Pop assigned value if not done before.1387}1388if (target.mode == GDScriptCodeGenerator::Address::TEMPORARY) {1389gen->pop_temporary(); // Pop the target to assignment.1390}1391}1392return GDScriptCodeGenerator::Address(); // Assignment does not return a value.1393} break;1394case GDScriptParser::Node::LAMBDA: {1395const GDScriptParser::LambdaNode *lambda = static_cast<const GDScriptParser::LambdaNode *>(p_expression);1396GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(lambda->get_datatype(), codegen.script));13971398Vector<GDScriptCodeGenerator::Address> captures;1399captures.resize(lambda->captures.size());1400for (int i = 0; i < lambda->captures.size(); i++) {1401captures.write[i] = _parse_expression(codegen, r_error, lambda->captures[i]);1402if (r_error) {1403return GDScriptCodeGenerator::Address();1404}1405}14061407GDScriptFunction *function = _parse_function(r_error, codegen.script, codegen.class_node, lambda->function, false, true);1408if (r_error) {1409return GDScriptCodeGenerator::Address();1410}14111412codegen.script->lambda_info.insert(function, { (int)lambda->captures.size(), lambda->use_self });1413gen->write_lambda(result, function, captures, lambda->use_self);14141415for (int i = 0; i < captures.size(); i++) {1416if (captures[i].mode == GDScriptCodeGenerator::Address::TEMPORARY) {1417gen->pop_temporary();1418}1419}14201421return result;1422} break;1423default: {1424_set_error("Compiler bug (please report): Unexpected node in parse tree while parsing expression.", p_expression); // Unreachable code.1425r_error = ERR_COMPILATION_FAILED;1426return GDScriptCodeGenerator::Address();1427} break;1428}1429}14301431GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &codegen, Error &r_error, const GDScriptParser::PatternNode *p_pattern, const GDScriptCodeGenerator::Address &p_value_addr, const GDScriptCodeGenerator::Address &p_type_addr, const GDScriptCodeGenerator::Address &p_previous_test, bool p_is_first, bool p_is_nested) {1432switch (p_pattern->pattern_type) {1433case GDScriptParser::PatternNode::PT_LITERAL: {1434if (p_is_nested) {1435codegen.generator->write_and_left_operand(p_previous_test);1436} else if (!p_is_first) {1437codegen.generator->write_or_left_operand(p_previous_test);1438}14391440// Get literal type into constant map.1441Variant::Type literal_type = p_pattern->literal->value.get_type();1442GDScriptCodeGenerator::Address literal_type_addr = codegen.add_constant(literal_type);14431444// Equality is always a boolean.1445GDScriptDataType equality_type;1446equality_type.kind = GDScriptDataType::BUILTIN;1447equality_type.builtin_type = Variant::BOOL;14481449// Check type equality.1450GDScriptCodeGenerator::Address type_equality_addr = codegen.add_temporary(equality_type);1451codegen.generator->write_binary_operator(type_equality_addr, Variant::OP_EQUAL, p_type_addr, literal_type_addr);14521453if (literal_type == Variant::STRING) {1454GDScriptCodeGenerator::Address type_stringname_addr = codegen.add_constant(Variant::STRING_NAME);14551456// Check StringName <-> String type equality.1457GDScriptCodeGenerator::Address tmp_comp_addr = codegen.add_temporary(equality_type);14581459codegen.generator->write_binary_operator(tmp_comp_addr, Variant::OP_EQUAL, p_type_addr, type_stringname_addr);1460codegen.generator->write_binary_operator(type_equality_addr, Variant::OP_OR, type_equality_addr, tmp_comp_addr);14611462codegen.generator->pop_temporary(); // Remove tmp_comp_addr from stack.1463} else if (literal_type == Variant::STRING_NAME) {1464GDScriptCodeGenerator::Address type_string_addr = codegen.add_constant(Variant::STRING);14651466// Check String <-> StringName type equality.1467GDScriptCodeGenerator::Address tmp_comp_addr = codegen.add_temporary(equality_type);14681469codegen.generator->write_binary_operator(tmp_comp_addr, Variant::OP_EQUAL, p_type_addr, type_string_addr);1470codegen.generator->write_binary_operator(type_equality_addr, Variant::OP_OR, type_equality_addr, tmp_comp_addr);14711472codegen.generator->pop_temporary(); // Remove tmp_comp_addr from stack.1473}14741475codegen.generator->write_and_left_operand(type_equality_addr);14761477// Get literal.1478GDScriptCodeGenerator::Address literal_addr = _parse_expression(codegen, r_error, p_pattern->literal);1479if (r_error) {1480return GDScriptCodeGenerator::Address();1481}14821483// Check value equality.1484GDScriptCodeGenerator::Address equality_addr = codegen.add_temporary(equality_type);1485codegen.generator->write_binary_operator(equality_addr, Variant::OP_EQUAL, p_value_addr, literal_addr);1486codegen.generator->write_and_right_operand(equality_addr);14871488// AND both together (reuse temporary location).1489codegen.generator->write_end_and(type_equality_addr);14901491codegen.generator->pop_temporary(); // Remove equality_addr from stack.14921493if (literal_addr.mode == GDScriptCodeGenerator::Address::TEMPORARY) {1494codegen.generator->pop_temporary();1495}14961497// If this isn't the first, we need to OR with the previous pattern. If it's nested, we use AND instead.1498if (p_is_nested) {1499// Use the previous value as target, since we only need one temporary variable.1500codegen.generator->write_and_right_operand(type_equality_addr);1501codegen.generator->write_end_and(p_previous_test);1502} else if (!p_is_first) {1503// Use the previous value as target, since we only need one temporary variable.1504codegen.generator->write_or_right_operand(type_equality_addr);1505codegen.generator->write_end_or(p_previous_test);1506} else {1507// Just assign this value to the accumulator temporary.1508codegen.generator->write_assign(p_previous_test, type_equality_addr);1509}1510codegen.generator->pop_temporary(); // Remove type_equality_addr.15111512return p_previous_test;1513} break;1514case GDScriptParser::PatternNode::PT_EXPRESSION: {1515if (p_is_nested) {1516codegen.generator->write_and_left_operand(p_previous_test);1517} else if (!p_is_first) {1518codegen.generator->write_or_left_operand(p_previous_test);1519}15201521GDScriptCodeGenerator::Address type_string_addr = codegen.add_constant(Variant::STRING);1522GDScriptCodeGenerator::Address type_stringname_addr = codegen.add_constant(Variant::STRING_NAME);15231524// Equality is always a boolean.1525GDScriptDataType equality_type;1526equality_type.kind = GDScriptDataType::BUILTIN;1527equality_type.builtin_type = Variant::BOOL;15281529// Create the result temps first since it's the last to go away.1530GDScriptCodeGenerator::Address result_addr = codegen.add_temporary(equality_type);1531GDScriptCodeGenerator::Address equality_test_addr = codegen.add_temporary(equality_type);1532GDScriptCodeGenerator::Address stringy_comp_addr = codegen.add_temporary(equality_type);1533GDScriptCodeGenerator::Address stringy_comp_addr_2 = codegen.add_temporary(equality_type);1534GDScriptCodeGenerator::Address expr_type_addr = codegen.add_temporary();15351536// Evaluate expression.1537GDScriptCodeGenerator::Address expr_addr;1538expr_addr = _parse_expression(codegen, r_error, p_pattern->expression);1539if (r_error) {1540return GDScriptCodeGenerator::Address();1541}15421543// Evaluate expression type.1544Vector<GDScriptCodeGenerator::Address> typeof_args;1545typeof_args.push_back(expr_addr);1546codegen.generator->write_call_utility(expr_type_addr, "typeof", typeof_args);15471548// Check type equality.1549codegen.generator->write_binary_operator(result_addr, Variant::OP_EQUAL, p_type_addr, expr_type_addr);15501551// Check for String <-> StringName comparison.1552codegen.generator->write_binary_operator(stringy_comp_addr, Variant::OP_EQUAL, p_type_addr, type_string_addr);1553codegen.generator->write_binary_operator(stringy_comp_addr_2, Variant::OP_EQUAL, expr_type_addr, type_stringname_addr);1554codegen.generator->write_binary_operator(stringy_comp_addr, Variant::OP_AND, stringy_comp_addr, stringy_comp_addr_2);1555codegen.generator->write_binary_operator(result_addr, Variant::OP_OR, result_addr, stringy_comp_addr);15561557// Check for StringName <-> String comparison.1558codegen.generator->write_binary_operator(stringy_comp_addr, Variant::OP_EQUAL, p_type_addr, type_stringname_addr);1559codegen.generator->write_binary_operator(stringy_comp_addr_2, Variant::OP_EQUAL, expr_type_addr, type_string_addr);1560codegen.generator->write_binary_operator(stringy_comp_addr, Variant::OP_AND, stringy_comp_addr, stringy_comp_addr_2);1561codegen.generator->write_binary_operator(result_addr, Variant::OP_OR, result_addr, stringy_comp_addr);15621563codegen.generator->pop_temporary(); // Remove expr_type_addr from stack.1564codegen.generator->pop_temporary(); // Remove stringy_comp_addr_2 from stack.1565codegen.generator->pop_temporary(); // Remove stringy_comp_addr from stack.15661567codegen.generator->write_and_left_operand(result_addr);15681569// Check value equality.1570codegen.generator->write_binary_operator(equality_test_addr, Variant::OP_EQUAL, p_value_addr, expr_addr);1571codegen.generator->write_and_right_operand(equality_test_addr);15721573// AND both type and value equality.1574codegen.generator->write_end_and(result_addr);15751576// We don't need the expression temporary anymore.1577if (expr_addr.mode == GDScriptCodeGenerator::Address::TEMPORARY) {1578codegen.generator->pop_temporary();1579}1580codegen.generator->pop_temporary(); // Remove equality_test_addr from stack.15811582// If this isn't the first, we need to OR with the previous pattern. If it's nested, we use AND instead.1583if (p_is_nested) {1584// Use the previous value as target, since we only need one temporary variable.1585codegen.generator->write_and_right_operand(result_addr);1586codegen.generator->write_end_and(p_previous_test);1587} else if (!p_is_first) {1588// Use the previous value as target, since we only need one temporary variable.1589codegen.generator->write_or_right_operand(result_addr);1590codegen.generator->write_end_or(p_previous_test);1591} else {1592// Just assign this value to the accumulator temporary.1593codegen.generator->write_assign(p_previous_test, result_addr);1594}1595codegen.generator->pop_temporary(); // Remove temp result addr.15961597return p_previous_test;1598} break;1599case GDScriptParser::PatternNode::PT_ARRAY: {1600if (p_is_nested) {1601codegen.generator->write_and_left_operand(p_previous_test);1602} else if (!p_is_first) {1603codegen.generator->write_or_left_operand(p_previous_test);1604}1605// Get array type into constant map.1606GDScriptCodeGenerator::Address array_type_addr = codegen.add_constant((int)Variant::ARRAY);16071608// Equality is always a boolean.1609GDScriptDataType temp_type;1610temp_type.kind = GDScriptDataType::BUILTIN;1611temp_type.builtin_type = Variant::BOOL;16121613// Check type equality.1614GDScriptCodeGenerator::Address result_addr = codegen.add_temporary(temp_type);1615codegen.generator->write_binary_operator(result_addr, Variant::OP_EQUAL, p_type_addr, array_type_addr);1616codegen.generator->write_and_left_operand(result_addr);16171618// Store pattern length in constant map.1619GDScriptCodeGenerator::Address array_length_addr = codegen.add_constant(p_pattern->rest_used ? p_pattern->array.size() - 1 : p_pattern->array.size());16201621// Get value length.1622temp_type.builtin_type = Variant::INT;1623GDScriptCodeGenerator::Address value_length_addr = codegen.add_temporary(temp_type);1624Vector<GDScriptCodeGenerator::Address> len_args;1625len_args.push_back(p_value_addr);1626codegen.generator->write_call_gdscript_utility(value_length_addr, "len", len_args);16271628// Test length compatibility.1629temp_type.builtin_type = Variant::BOOL;1630GDScriptCodeGenerator::Address length_compat_addr = codegen.add_temporary(temp_type);1631codegen.generator->write_binary_operator(length_compat_addr, p_pattern->rest_used ? Variant::OP_GREATER_EQUAL : Variant::OP_EQUAL, value_length_addr, array_length_addr);1632codegen.generator->write_and_right_operand(length_compat_addr);16331634// AND type and length check.1635codegen.generator->write_end_and(result_addr);16361637// Remove length temporaries.1638codegen.generator->pop_temporary();1639codegen.generator->pop_temporary();16401641// Create temporaries outside the loop so they can be reused.1642GDScriptCodeGenerator::Address element_addr = codegen.add_temporary();1643GDScriptCodeGenerator::Address element_type_addr = codegen.add_temporary();16441645// Evaluate element by element.1646for (int i = 0; i < p_pattern->array.size(); i++) {1647if (p_pattern->array[i]->pattern_type == GDScriptParser::PatternNode::PT_REST) {1648// Don't want to access an extra element of the user array.1649break;1650}16511652// Use AND here too, as we don't want to be checking elements if previous test failed (which means this might be an invalid get).1653codegen.generator->write_and_left_operand(result_addr);16541655// Add index to constant map.1656GDScriptCodeGenerator::Address index_addr = codegen.add_constant(i);16571658// Get the actual element from the user-sent array.1659codegen.generator->write_get(element_addr, index_addr, p_value_addr);16601661// Also get type of element.1662Vector<GDScriptCodeGenerator::Address> typeof_args;1663typeof_args.push_back(element_addr);1664codegen.generator->write_call_utility(element_type_addr, "typeof", typeof_args);16651666// Try the pattern inside the element.1667result_addr = _parse_match_pattern(codegen, r_error, p_pattern->array[i], element_addr, element_type_addr, result_addr, false, true);1668if (r_error != OK) {1669return GDScriptCodeGenerator::Address();1670}16711672codegen.generator->write_and_right_operand(result_addr);1673codegen.generator->write_end_and(result_addr);1674}1675// Remove element temporaries.1676codegen.generator->pop_temporary();1677codegen.generator->pop_temporary();16781679// If this isn't the first, we need to OR with the previous pattern. If it's nested, we use AND instead.1680if (p_is_nested) {1681// Use the previous value as target, since we only need one temporary variable.1682codegen.generator->write_and_right_operand(result_addr);1683codegen.generator->write_end_and(p_previous_test);1684} else if (!p_is_first) {1685// Use the previous value as target, since we only need one temporary variable.1686codegen.generator->write_or_right_operand(result_addr);1687codegen.generator->write_end_or(p_previous_test);1688} else {1689// Just assign this value to the accumulator temporary.1690codegen.generator->write_assign(p_previous_test, result_addr);1691}1692codegen.generator->pop_temporary(); // Remove temp result addr.16931694return p_previous_test;1695} break;1696case GDScriptParser::PatternNode::PT_DICTIONARY: {1697if (p_is_nested) {1698codegen.generator->write_and_left_operand(p_previous_test);1699} else if (!p_is_first) {1700codegen.generator->write_or_left_operand(p_previous_test);1701}1702// Get dictionary type into constant map.1703GDScriptCodeGenerator::Address dict_type_addr = codegen.add_constant((int)Variant::DICTIONARY);17041705// Equality is always a boolean.1706GDScriptDataType temp_type;1707temp_type.kind = GDScriptDataType::BUILTIN;1708temp_type.builtin_type = Variant::BOOL;17091710// Check type equality.1711GDScriptCodeGenerator::Address result_addr = codegen.add_temporary(temp_type);1712codegen.generator->write_binary_operator(result_addr, Variant::OP_EQUAL, p_type_addr, dict_type_addr);1713codegen.generator->write_and_left_operand(result_addr);17141715// Store pattern length in constant map.1716GDScriptCodeGenerator::Address dict_length_addr = codegen.add_constant(p_pattern->rest_used ? p_pattern->dictionary.size() - 1 : p_pattern->dictionary.size());17171718// Get user's dictionary length.1719temp_type.builtin_type = Variant::INT;1720GDScriptCodeGenerator::Address value_length_addr = codegen.add_temporary(temp_type);1721Vector<GDScriptCodeGenerator::Address> func_args;1722func_args.push_back(p_value_addr);1723codegen.generator->write_call_gdscript_utility(value_length_addr, "len", func_args);17241725// Test length compatibility.1726temp_type.builtin_type = Variant::BOOL;1727GDScriptCodeGenerator::Address length_compat_addr = codegen.add_temporary(temp_type);1728codegen.generator->write_binary_operator(length_compat_addr, p_pattern->rest_used ? Variant::OP_GREATER_EQUAL : Variant::OP_EQUAL, value_length_addr, dict_length_addr);1729codegen.generator->write_and_right_operand(length_compat_addr);17301731// AND type and length check.1732codegen.generator->write_end_and(result_addr);17331734// Remove length temporaries.1735codegen.generator->pop_temporary();1736codegen.generator->pop_temporary();17371738// Create temporaries outside the loop so they can be reused.1739GDScriptCodeGenerator::Address element_addr = codegen.add_temporary();1740GDScriptCodeGenerator::Address element_type_addr = codegen.add_temporary();17411742// Evaluate element by element.1743for (int i = 0; i < p_pattern->dictionary.size(); i++) {1744const GDScriptParser::PatternNode::Pair &element = p_pattern->dictionary[i];1745if (element.value_pattern && element.value_pattern->pattern_type == GDScriptParser::PatternNode::PT_REST) {1746// Ignore rest pattern.1747break;1748}17491750// Use AND here too, as we don't want to be checking elements if previous test failed (which means this might be an invalid get).1751codegen.generator->write_and_left_operand(result_addr);17521753// Get the pattern key.1754GDScriptCodeGenerator::Address pattern_key_addr = _parse_expression(codegen, r_error, element.key);1755if (r_error) {1756return GDScriptCodeGenerator::Address();1757}17581759// Check if pattern key exists in user's dictionary. This will be AND-ed with next result.1760func_args.clear();1761func_args.push_back(pattern_key_addr);1762codegen.generator->write_call(result_addr, p_value_addr, "has", func_args);17631764if (element.value_pattern != nullptr) {1765// Use AND here too, as we don't want to be checking elements if previous test failed (which means this might be an invalid get).1766codegen.generator->write_and_left_operand(result_addr);17671768// Get actual value from user dictionary.1769codegen.generator->write_get(element_addr, pattern_key_addr, p_value_addr);17701771// Also get type of value.1772func_args.clear();1773func_args.push_back(element_addr);1774codegen.generator->write_call_utility(element_type_addr, "typeof", func_args);17751776// Try the pattern inside the value.1777result_addr = _parse_match_pattern(codegen, r_error, element.value_pattern, element_addr, element_type_addr, result_addr, false, true);1778if (r_error != OK) {1779return GDScriptCodeGenerator::Address();1780}1781codegen.generator->write_and_right_operand(result_addr);1782codegen.generator->write_end_and(result_addr);1783}17841785codegen.generator->write_and_right_operand(result_addr);1786codegen.generator->write_end_and(result_addr);17871788// Remove pattern key temporary.1789if (pattern_key_addr.mode == GDScriptCodeGenerator::Address::TEMPORARY) {1790codegen.generator->pop_temporary();1791}1792}17931794// Remove element temporaries.1795codegen.generator->pop_temporary();1796codegen.generator->pop_temporary();17971798// If this isn't the first, we need to OR with the previous pattern. If it's nested, we use AND instead.1799if (p_is_nested) {1800// Use the previous value as target, since we only need one temporary variable.1801codegen.generator->write_and_right_operand(result_addr);1802codegen.generator->write_end_and(p_previous_test);1803} else if (!p_is_first) {1804// Use the previous value as target, since we only need one temporary variable.1805codegen.generator->write_or_right_operand(result_addr);1806codegen.generator->write_end_or(p_previous_test);1807} else {1808// Just assign this value to the accumulator temporary.1809codegen.generator->write_assign(p_previous_test, result_addr);1810}1811codegen.generator->pop_temporary(); // Remove temp result addr.18121813return p_previous_test;1814} break;1815case GDScriptParser::PatternNode::PT_REST:1816// Do nothing.1817return p_previous_test;1818break;1819case GDScriptParser::PatternNode::PT_BIND: {1820if (p_is_nested) {1821codegen.generator->write_and_left_operand(p_previous_test);1822} else if (!p_is_first) {1823codegen.generator->write_or_left_operand(p_previous_test);1824}1825// Get the bind address.1826GDScriptCodeGenerator::Address bind = codegen.locals[p_pattern->bind->name];18271828// Assign value to bound variable.1829codegen.generator->write_assign(bind, p_value_addr);1830}1831[[fallthrough]]; // Act like matching anything too.1832case GDScriptParser::PatternNode::PT_WILDCARD:1833// If this is a fall through we don't want to do this again.1834if (p_pattern->pattern_type != GDScriptParser::PatternNode::PT_BIND) {1835if (p_is_nested) {1836codegen.generator->write_and_left_operand(p_previous_test);1837} else if (!p_is_first) {1838codegen.generator->write_or_left_operand(p_previous_test);1839}1840}1841// This matches anything so just do the same as `if(true)`.1842// If this isn't the first, we need to OR with the previous pattern. If it's nested, we use AND instead.1843if (p_is_nested) {1844// Use the operator with the `true` constant so it works as always matching.1845GDScriptCodeGenerator::Address constant = codegen.add_constant(true);1846codegen.generator->write_and_right_operand(constant);1847codegen.generator->write_end_and(p_previous_test);1848} else if (!p_is_first) {1849// Use the operator with the `true` constant so it works as always matching.1850GDScriptCodeGenerator::Address constant = codegen.add_constant(true);1851codegen.generator->write_or_right_operand(constant);1852codegen.generator->write_end_or(p_previous_test);1853} else {1854// Just assign this value to the accumulator temporary.1855codegen.generator->write_assign_true(p_previous_test);1856}1857return p_previous_test;1858}18591860_set_error("Compiler bug (please report): Reaching the end of pattern compilation without matching a pattern.", p_pattern);1861r_error = ERR_COMPILATION_FAILED;1862return p_previous_test;1863}18641865List<GDScriptCodeGenerator::Address> GDScriptCompiler::_add_block_locals(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block) {1866List<GDScriptCodeGenerator::Address> addresses;1867for (int i = 0; i < p_block->locals.size(); i++) {1868if (p_block->locals[i].type == GDScriptParser::SuiteNode::Local::PARAMETER || p_block->locals[i].type == GDScriptParser::SuiteNode::Local::FOR_VARIABLE) {1869// Parameters are added directly from function and loop variables are declared explicitly.1870continue;1871}1872addresses.push_back(codegen.add_local(p_block->locals[i].name, _gdtype_from_datatype(p_block->locals[i].get_datatype(), codegen.script)));1873}1874return addresses;1875}18761877// Avoid keeping in the stack long-lived references to objects, which may prevent `RefCounted` objects from being freed.1878void GDScriptCompiler::_clear_block_locals(CodeGen &codegen, const List<GDScriptCodeGenerator::Address> &p_locals) {1879for (const GDScriptCodeGenerator::Address &local : p_locals) {1880if (local.type.can_contain_object()) {1881codegen.generator->clear_address(local);1882}1883}1884}18851886Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block, bool p_add_locals, bool p_clear_locals) {1887Error err = OK;1888GDScriptCodeGenerator *gen = codegen.generator;1889List<GDScriptCodeGenerator::Address> block_locals;18901891gen->clear_temporaries();1892codegen.start_block();18931894if (p_add_locals) {1895block_locals = _add_block_locals(codegen, p_block);1896}18971898for (int i = 0; i < p_block->statements.size(); i++) {1899const GDScriptParser::Node *s = p_block->statements[i];19001901gen->write_newline(s->start_line);19021903switch (s->type) {1904case GDScriptParser::Node::MATCH: {1905const GDScriptParser::MatchNode *match = static_cast<const GDScriptParser::MatchNode *>(s);19061907codegen.start_block(); // Add an extra block, since @special locals belong to the match scope.19081909// Evaluate the match expression.1910GDScriptCodeGenerator::Address value = codegen.add_local("@match_value", _gdtype_from_datatype(match->test->get_datatype(), codegen.script));1911GDScriptCodeGenerator::Address value_expr = _parse_expression(codegen, err, match->test);1912if (err) {1913return err;1914}19151916// Assign to local.1917// TODO: This can be improved by passing the target to parse_expression().1918gen->write_assign(value, value_expr);19191920if (value_expr.mode == GDScriptCodeGenerator::Address::TEMPORARY) {1921codegen.generator->pop_temporary();1922}19231924// Then, let's save the type of the value in the stack too, so we can reuse for later comparisons.1925GDScriptDataType typeof_type;1926typeof_type.kind = GDScriptDataType::BUILTIN;1927typeof_type.builtin_type = Variant::INT;1928GDScriptCodeGenerator::Address type = codegen.add_local("@match_type", typeof_type);19291930Vector<GDScriptCodeGenerator::Address> typeof_args;1931typeof_args.push_back(value);1932gen->write_call_utility(type, "typeof", typeof_args);19331934// Now we can actually start testing.1935// For each branch.1936for (int j = 0; j < match->branches.size(); j++) {1937if (j > 0) {1938// Use `else` to not check the next branch after matching.1939gen->write_else();1940}19411942const GDScriptParser::MatchBranchNode *branch = match->branches[j];19431944codegen.start_block(); // Add an extra block, since binds belong to the match branch scope.19451946// Add locals in block before patterns, so temporaries don't use the stack address for binds.1947List<GDScriptCodeGenerator::Address> branch_locals = _add_block_locals(codegen, branch->block);19481949gen->write_newline(branch->start_line);19501951// For each pattern in branch.1952GDScriptCodeGenerator::Address pattern_result = codegen.add_temporary();1953for (int k = 0; k < branch->patterns.size(); k++) {1954pattern_result = _parse_match_pattern(codegen, err, branch->patterns[k], value, type, pattern_result, k == 0, false);1955if (err != OK) {1956return err;1957}1958}19591960// If there's a guard, check its condition too.1961if (branch->guard_body != nullptr) {1962// Do this first so the guard does not run unless the pattern matched.1963gen->write_and_left_operand(pattern_result);19641965// Don't actually use the block for the guard.1966// 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.1967GDScriptCodeGenerator::Address guard_result = _parse_expression(codegen, err, static_cast<GDScriptParser::ExpressionNode *>(branch->guard_body->statements[0]));1968if (err) {1969return err;1970}19711972gen->write_and_right_operand(guard_result);1973gen->write_end_and(pattern_result);19741975if (guard_result.mode == GDScriptCodeGenerator::Address::TEMPORARY) {1976codegen.generator->pop_temporary();1977}1978}19791980// Check if pattern did match.1981gen->write_if(pattern_result);19821983// Remove the result from stack.1984gen->pop_temporary();19851986// Parse the branch block.1987err = _parse_block(codegen, branch->block, false); // Don't add locals again.1988if (err) {1989return err;1990}19911992_clear_block_locals(codegen, branch_locals);19931994codegen.end_block(); // Get out of extra block for binds.1995}19961997// End all nested `if`s.1998for (int j = 0; j < match->branches.size(); j++) {1999gen->write_endif();2000}20012002codegen.end_block(); // Get out of extra block for match's @special locals.2003} break;2004case GDScriptParser::Node::IF: {2005const GDScriptParser::IfNode *if_n = static_cast<const GDScriptParser::IfNode *>(s);2006GDScriptCodeGenerator::Address condition = _parse_expression(codegen, err, if_n->condition);2007if (err) {2008return err;2009}20102011gen->write_if(condition);20122013if (condition.mode == GDScriptCodeGenerator::Address::TEMPORARY) {2014codegen.generator->pop_temporary();2015}20162017err = _parse_block(codegen, if_n->true_block);2018if (err) {2019return err;2020}20212022if (if_n->false_block) {2023gen->write_else();20242025err = _parse_block(codegen, if_n->false_block);2026if (err) {2027return err;2028}2029}20302031gen->write_endif();2032} break;2033case GDScriptParser::Node::FOR: {2034const GDScriptParser::ForNode *for_n = static_cast<const GDScriptParser::ForNode *>(s);20352036// Add an extra block, since the iterator and @special locals belong to the loop scope.2037// Also we use custom logic to clear block locals.2038codegen.start_block();20392040GDScriptCodeGenerator::Address iterator = codegen.add_local(for_n->variable->name, _gdtype_from_datatype(for_n->variable->get_datatype(), codegen.script));20412042// Optimize `range()` call to not allocate an array.2043GDScriptParser::CallNode *range_call = nullptr;2044if (for_n->list && for_n->list->type == GDScriptParser::Node::CALL) {2045GDScriptParser::CallNode *call = static_cast<GDScriptParser::CallNode *>(for_n->list);2046if (call->get_callee_type() == GDScriptParser::Node::IDENTIFIER) {2047if (static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name == "range") {2048range_call = call;2049}2050}2051}20522053gen->start_for(iterator.type, _gdtype_from_datatype(for_n->list->get_datatype(), codegen.script), range_call != nullptr);20542055if (range_call != nullptr) {2056Vector<GDScriptCodeGenerator::Address> args;2057args.resize(range_call->arguments.size());20582059for (int j = 0; j < args.size(); j++) {2060args.write[j] = _parse_expression(codegen, err, range_call->arguments[j]);2061if (err) {2062return err;2063}2064}20652066switch (args.size()) {2067case 1:2068gen->write_for_range_assignment(codegen.add_constant(0), args[0], codegen.add_constant(1));2069break;2070case 2:2071gen->write_for_range_assignment(args[0], args[1], codegen.add_constant(1));2072break;2073case 3:2074gen->write_for_range_assignment(args[0], args[1], args[2]);2075break;2076default:2077_set_error(R"*(Analyzer bug: Wrong "range()" argument count.)*", range_call);2078return ERR_BUG;2079}20802081for (int j = 0; j < args.size(); j++) {2082if (args[j].mode == GDScriptCodeGenerator::Address::TEMPORARY) {2083codegen.generator->pop_temporary();2084}2085}2086} else {2087GDScriptCodeGenerator::Address list = _parse_expression(codegen, err, for_n->list);2088if (err) {2089return err;2090}20912092gen->write_for_list_assignment(list);20932094if (list.mode == GDScriptCodeGenerator::Address::TEMPORARY) {2095codegen.generator->pop_temporary();2096}2097}20982099gen->write_for(iterator, for_n->use_conversion_assign, range_call != nullptr);21002101// Loop variables must be cleared even when `break`/`continue` is used.2102List<GDScriptCodeGenerator::Address> loop_locals = _add_block_locals(codegen, for_n->loop);21032104//_clear_block_locals(codegen, loop_locals); // Inside loop, before block - for `continue`. // TODO21052106err = _parse_block(codegen, for_n->loop, false); // Don't add locals again.2107if (err) {2108return err;2109}21102111gen->write_endfor(range_call != nullptr);21122113_clear_block_locals(codegen, loop_locals); // Outside loop, after block - for `break` and normal exit.21142115codegen.end_block(); // Get out of extra block for loop iterator, @special locals, and custom locals clearing.2116} break;2117case GDScriptParser::Node::WHILE: {2118const GDScriptParser::WhileNode *while_n = static_cast<const GDScriptParser::WhileNode *>(s);21192120codegen.start_block(); // Add an extra block, since we use custom logic to clear block locals.21212122gen->start_while_condition();21232124GDScriptCodeGenerator::Address condition = _parse_expression(codegen, err, while_n->condition);2125if (err) {2126return err;2127}21282129gen->write_while(condition);21302131if (condition.mode == GDScriptCodeGenerator::Address::TEMPORARY) {2132codegen.generator->pop_temporary();2133}21342135// Loop variables must be cleared even when `break`/`continue` is used.2136List<GDScriptCodeGenerator::Address> loop_locals = _add_block_locals(codegen, while_n->loop);21372138//_clear_block_locals(codegen, loop_locals); // Inside loop, before block - for `continue`. // TODO21392140err = _parse_block(codegen, while_n->loop, false); // Don't add locals again.2141if (err) {2142return err;2143}21442145gen->write_endwhile();21462147_clear_block_locals(codegen, loop_locals); // Outside loop, after block - for `break` and normal exit.21482149codegen.end_block(); // Get out of extra block for custom locals clearing.2150} break;2151case GDScriptParser::Node::BREAK: {2152gen->write_break();2153} break;2154case GDScriptParser::Node::CONTINUE: {2155gen->write_continue();2156} break;2157case GDScriptParser::Node::RETURN: {2158const GDScriptParser::ReturnNode *return_n = static_cast<const GDScriptParser::ReturnNode *>(s);21592160GDScriptCodeGenerator::Address return_value;21612162if (return_n->return_value != nullptr) {2163return_value = _parse_expression(codegen, err, return_n->return_value);2164if (err) {2165return err;2166}2167}21682169if (return_n->void_return) {2170// Always return "null", even if the expression is a call to a void function.2171gen->write_return(codegen.add_constant(Variant()));2172} else {2173gen->write_return(return_value);2174}2175if (return_value.mode == GDScriptCodeGenerator::Address::TEMPORARY) {2176codegen.generator->pop_temporary();2177}2178} break;2179case GDScriptParser::Node::ASSERT: {2180#ifdef DEBUG_ENABLED2181const GDScriptParser::AssertNode *as = static_cast<const GDScriptParser::AssertNode *>(s);21822183GDScriptCodeGenerator::Address condition = _parse_expression(codegen, err, as->condition);2184if (err) {2185return err;2186}21872188GDScriptCodeGenerator::Address message;21892190if (as->message) {2191message = _parse_expression(codegen, err, as->message);2192if (err) {2193return err;2194}2195}2196gen->write_assert(condition, message);21972198if (condition.mode == GDScriptCodeGenerator::Address::TEMPORARY) {2199codegen.generator->pop_temporary();2200}2201if (message.mode == GDScriptCodeGenerator::Address::TEMPORARY) {2202codegen.generator->pop_temporary();2203}2204#endif2205} break;2206case GDScriptParser::Node::BREAKPOINT: {2207#ifdef DEBUG_ENABLED2208gen->write_breakpoint();2209#endif2210} break;2211case GDScriptParser::Node::VARIABLE: {2212const GDScriptParser::VariableNode *lv = static_cast<const GDScriptParser::VariableNode *>(s);2213// Should be already in stack when the block began.2214GDScriptCodeGenerator::Address local = codegen.locals[lv->identifier->name];2215GDScriptDataType local_type = _gdtype_from_datatype(lv->get_datatype(), codegen.script);22162217bool initialized = false;2218if (lv->initializer != nullptr) {2219GDScriptCodeGenerator::Address src_address = _parse_expression(codegen, err, lv->initializer);2220if (err) {2221return err;2222}2223if (lv->use_conversion_assign) {2224gen->write_assign_with_conversion(local, src_address);2225} else {2226gen->write_assign(local, src_address);2227}2228if (src_address.mode == GDScriptCodeGenerator::Address::TEMPORARY) {2229codegen.generator->pop_temporary();2230}2231initialized = true;2232} else if (local_type.kind == GDScriptDataType::BUILTIN || codegen.generator->is_local_dirty(local)) {2233// Initialize with default for the type. Built-in types must always be cleared (they cannot be `null`).2234// Objects and untyped variables are assigned to `null` only if the stack address has been reused and not cleared.2235codegen.generator->clear_address(local);2236initialized = true;2237}22382239// Don't check `is_local_dirty()` since the variable must be assigned to `null` **on each iteration**.2240if (!initialized && p_block->is_in_loop) {2241codegen.generator->clear_address(local);2242}2243} break;2244case GDScriptParser::Node::CONSTANT: {2245// Local constants.2246const GDScriptParser::ConstantNode *lc = static_cast<const GDScriptParser::ConstantNode *>(s);2247if (!lc->initializer->is_constant) {2248_set_error("Local constant must have a constant value as initializer.", lc->initializer);2249return ERR_PARSE_ERROR;2250}22512252codegen.add_local_constant(lc->identifier->name, lc->initializer->reduced_value);2253} break;2254case GDScriptParser::Node::PASS:2255// Nothing to do.2256break;2257default: {2258// Expression.2259if (s->is_expression()) {2260GDScriptCodeGenerator::Address expr = _parse_expression(codegen, err, static_cast<const GDScriptParser::ExpressionNode *>(s), true);2261if (err) {2262return err;2263}2264if (expr.mode == GDScriptCodeGenerator::Address::TEMPORARY) {2265codegen.generator->pop_temporary();2266}2267} else {2268_set_error("Compiler bug (please report): unexpected node in parse tree while parsing statement.", s); // Unreachable code.2269return ERR_INVALID_DATA;2270}2271} break;2272}22732274gen->clear_temporaries();2275}22762277if (p_add_locals && p_clear_locals) {2278_clear_block_locals(codegen, block_locals);2279}22802281codegen.end_block();2282return OK;2283}22842285GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready, bool p_for_lambda) {2286r_error = OK;2287CodeGen codegen;2288codegen.generator = memnew(GDScriptByteCodeGenerator);22892290codegen.class_node = p_class;2291codegen.script = p_script;2292codegen.function_node = p_func;22932294StringName func_name;2295bool is_abstract = false;2296bool is_static = false;2297Variant rpc_config;2298GDScriptDataType return_type;2299return_type.kind = GDScriptDataType::BUILTIN;2300return_type.builtin_type = Variant::NIL;23012302if (p_func) {2303if (p_func->identifier) {2304func_name = p_func->identifier->name;2305} else {2306func_name = "<anonymous lambda>";2307}2308is_abstract = p_func->is_abstract;2309is_static = p_func->is_static;2310rpc_config = p_func->rpc_config;2311return_type = _gdtype_from_datatype(p_func->get_datatype(), p_script);2312} else {2313if (p_for_ready) {2314func_name = "@implicit_ready";2315} else {2316func_name = "@implicit_new";2317}2318}23192320MethodInfo method_info;23212322codegen.function_name = func_name;2323method_info.name = func_name;2324codegen.is_static = is_static;2325if (is_abstract) {2326method_info.flags |= METHOD_FLAG_VIRTUAL_REQUIRED;2327}2328if (is_static) {2329method_info.flags |= METHOD_FLAG_STATIC;2330}2331codegen.generator->write_start(p_script, func_name, is_static, rpc_config, return_type);23322333int optional_parameters = 0;2334GDScriptCodeGenerator::Address vararg_addr;23352336if (p_func) {2337for (int i = 0; i < p_func->parameters.size(); i++) {2338const GDScriptParser::ParameterNode *parameter = p_func->parameters[i];2339GDScriptDataType par_type = _gdtype_from_datatype(parameter->get_datatype(), p_script);2340uint32_t par_addr = codegen.generator->add_parameter(parameter->identifier->name, parameter->initializer != nullptr, par_type);2341codegen.parameters[parameter->identifier->name] = GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::FUNCTION_PARAMETER, par_addr, par_type);23422343method_info.arguments.push_back(parameter->get_datatype().to_property_info(parameter->identifier->name));23442345if (parameter->initializer != nullptr) {2346optional_parameters++;2347}2348}23492350if (p_func->is_vararg()) {2351vararg_addr = codegen.add_local(p_func->rest_parameter->identifier->name, _gdtype_from_datatype(p_func->rest_parameter->get_datatype(), codegen.script));2352method_info.flags |= METHOD_FLAG_VARARG;2353}23542355method_info.default_arguments.append_array(p_func->default_arg_values);2356}23572358// Parse initializer if applies.2359bool is_implicit_initializer = !p_for_ready && !p_func && !p_for_lambda;2360bool is_initializer = p_func && !p_for_lambda && p_func->identifier->name == GDScriptLanguage::get_singleton()->strings._init;2361bool is_implicit_ready = !p_func && p_for_ready;23622363if (!p_for_lambda && is_implicit_initializer) {2364// Initialize the default values for typed variables before anything.2365// This avoids crashes if they are accessed with validated calls before being properly initialized.2366// It may happen with out-of-order access or with `@onready` variables.2367for (const GDScriptParser::ClassNode::Member &member : p_class->members) {2368if (member.type != GDScriptParser::ClassNode::Member::VARIABLE) {2369continue;2370}23712372const GDScriptParser::VariableNode *field = member.variable;2373if (field->is_static) {2374continue;2375}23762377GDScriptDataType field_type = _gdtype_from_datatype(field->get_datatype(), codegen.script);2378if (field_type.has_type()) {2379codegen.generator->write_newline(field->start_line);23802381GDScriptCodeGenerator::Address dst_address(GDScriptCodeGenerator::Address::MEMBER, codegen.script->member_indices[field->identifier->name].index, field_type);23822383if (field_type.builtin_type == Variant::ARRAY && field_type.has_container_element_type(0)) {2384codegen.generator->write_construct_typed_array(dst_address, field_type.get_container_element_type(0), Vector<GDScriptCodeGenerator::Address>());2385} else if (field_type.builtin_type == Variant::DICTIONARY && field_type.has_container_element_types()) {2386codegen.generator->write_construct_typed_dictionary(dst_address, field_type.get_container_element_type_or_variant(0),2387field_type.get_container_element_type_or_variant(1), Vector<GDScriptCodeGenerator::Address>());2388} else if (field_type.kind == GDScriptDataType::BUILTIN) {2389codegen.generator->write_construct(dst_address, field_type.builtin_type, Vector<GDScriptCodeGenerator::Address>());2390}2391// The `else` branch is for objects, in such case we leave it as `null`.2392}2393}2394}23952396if (!p_for_lambda && (is_implicit_initializer || is_implicit_ready)) {2397// Initialize class fields.2398for (int i = 0; i < p_class->members.size(); i++) {2399if (p_class->members[i].type != GDScriptParser::ClassNode::Member::VARIABLE) {2400continue;2401}2402const GDScriptParser::VariableNode *field = p_class->members[i].variable;2403if (field->is_static) {2404continue;2405}24062407if (field->onready != is_implicit_ready) {2408// Only initialize in `@implicit_ready()`.2409continue;2410}24112412if (field->initializer) {2413codegen.generator->write_newline(field->initializer->start_line);24142415GDScriptCodeGenerator::Address src_address = _parse_expression(codegen, r_error, field->initializer, false, true);2416if (r_error) {2417memdelete(codegen.generator);2418return nullptr;2419}24202421GDScriptDataType field_type = _gdtype_from_datatype(field->get_datatype(), codegen.script);2422GDScriptCodeGenerator::Address dst_address(GDScriptCodeGenerator::Address::MEMBER, codegen.script->member_indices[field->identifier->name].index, field_type);24232424if (field->use_conversion_assign) {2425codegen.generator->write_assign_with_conversion(dst_address, src_address);2426} else {2427codegen.generator->write_assign(dst_address, src_address);2428}2429if (src_address.mode == GDScriptCodeGenerator::Address::TEMPORARY) {2430codegen.generator->pop_temporary();2431}2432}2433}2434}24352436// Parse default argument code if applies.2437if (p_func) {2438if (optional_parameters > 0) {2439codegen.generator->start_parameters();2440for (int i = p_func->parameters.size() - optional_parameters; i < p_func->parameters.size(); i++) {2441const GDScriptParser::ParameterNode *parameter = p_func->parameters[i];2442GDScriptCodeGenerator::Address src_addr = _parse_expression(codegen, r_error, parameter->initializer);2443if (r_error) {2444memdelete(codegen.generator);2445return nullptr;2446}2447GDScriptCodeGenerator::Address dst_addr = codegen.parameters[parameter->identifier->name];2448codegen.generator->write_assign_default_parameter(dst_addr, src_addr, parameter->use_conversion_assign);2449if (src_addr.mode == GDScriptCodeGenerator::Address::TEMPORARY) {2450codegen.generator->pop_temporary();2451}2452}2453codegen.generator->end_parameters();2454}24552456// No need to reset locals at the end of the function, the stack will be cleared anyway.2457r_error = _parse_block(codegen, p_func->body, true, false);2458if (r_error) {2459memdelete(codegen.generator);2460return nullptr;2461}2462}24632464#ifdef DEBUG_ENABLED2465if (EngineDebugger::is_active()) {2466String signature;2467// Path.2468if (!p_script->get_script_path().is_empty()) {2469signature += p_script->get_script_path();2470}2471// Location.2472if (p_func) {2473signature += "::" + itos(p_func->body->start_line);2474} else {2475signature += "::0";2476}24772478// Function and class.24792480if (p_class->identifier) {2481signature += "::" + String(p_class->identifier->name) + "." + String(func_name);2482} else {2483signature += "::" + String(func_name);2484}24852486if (p_for_lambda) {2487signature += "(lambda)";2488}24892490codegen.generator->set_signature(signature);2491}2492#endif24932494if (p_func) {2495codegen.generator->set_initial_line(p_func->start_line);2496} else {2497codegen.generator->set_initial_line(0);2498}24992500GDScriptFunction *gd_function = codegen.generator->write_end();25012502if (is_initializer) {2503p_script->initializer = gd_function;2504} else if (is_implicit_initializer) {2505p_script->implicit_initializer = gd_function;2506} else if (is_implicit_ready) {2507p_script->implicit_ready = gd_function;2508}25092510if (p_func) {2511// If no `return` statement, then return type is `void`, not `Variant`.2512if (p_func->body->has_return) {2513gd_function->return_type = _gdtype_from_datatype(p_func->get_datatype(), p_script);2514method_info.return_val = p_func->get_datatype().to_property_info(String());2515} else {2516gd_function->return_type = GDScriptDataType();2517gd_function->return_type.kind = GDScriptDataType::BUILTIN;2518gd_function->return_type.builtin_type = Variant::NIL;2519}25202521if (p_func->is_vararg()) {2522gd_function->_vararg_index = vararg_addr.address;2523}2524}25252526gd_function->method_info = method_info;25272528if (!is_implicit_initializer && !is_implicit_ready && !p_for_lambda) {2529p_script->member_functions[func_name] = gd_function;2530}25312532memdelete(codegen.generator);25332534return gd_function;2535}25362537GDScriptFunction *GDScriptCompiler::_make_static_initializer(Error &r_error, GDScript *p_script, const GDScriptParser::ClassNode *p_class) {2538r_error = OK;2539CodeGen codegen;2540codegen.generator = memnew(GDScriptByteCodeGenerator);25412542codegen.class_node = p_class;2543codegen.script = p_script;25442545StringName func_name = SNAME("@static_initializer");2546bool is_static = true;2547Variant rpc_config;2548GDScriptDataType return_type;2549return_type.kind = GDScriptDataType::BUILTIN;2550return_type.builtin_type = Variant::NIL;25512552codegen.function_name = func_name;2553codegen.is_static = is_static;2554codegen.generator->write_start(p_script, func_name, is_static, rpc_config, return_type);25552556// The static initializer is always called on the same class where the static variables are defined,2557// so the CLASS address (current class) can be used instead of `codegen.add_constant(p_script)`.2558GDScriptCodeGenerator::Address class_addr(GDScriptCodeGenerator::Address::CLASS);25592560// Initialize the default values for typed variables before anything.2561// This avoids crashes if they are accessed with validated calls before being properly initialized.2562// It may happen with out-of-order access or with `@onready` variables.2563for (const GDScriptParser::ClassNode::Member &member : p_class->members) {2564if (member.type != GDScriptParser::ClassNode::Member::VARIABLE) {2565continue;2566}25672568const GDScriptParser::VariableNode *field = member.variable;2569if (!field->is_static) {2570continue;2571}25722573GDScriptDataType field_type = _gdtype_from_datatype(field->get_datatype(), codegen.script);2574if (field_type.has_type()) {2575codegen.generator->write_newline(field->start_line);25762577if (field_type.builtin_type == Variant::ARRAY && field_type.has_container_element_type(0)) {2578GDScriptCodeGenerator::Address temp = codegen.add_temporary(field_type);2579codegen.generator->write_construct_typed_array(temp, field_type.get_container_element_type(0), Vector<GDScriptCodeGenerator::Address>());2580codegen.generator->write_set_static_variable(temp, class_addr, p_script->static_variables_indices[field->identifier->name].index);2581codegen.generator->pop_temporary();2582} else if (field_type.builtin_type == Variant::DICTIONARY && field_type.has_container_element_types()) {2583GDScriptCodeGenerator::Address temp = codegen.add_temporary(field_type);2584codegen.generator->write_construct_typed_dictionary(temp, field_type.get_container_element_type_or_variant(0),2585field_type.get_container_element_type_or_variant(1), Vector<GDScriptCodeGenerator::Address>());2586codegen.generator->write_set_static_variable(temp, class_addr, p_script->static_variables_indices[field->identifier->name].index);2587codegen.generator->pop_temporary();2588} else if (field_type.kind == GDScriptDataType::BUILTIN) {2589GDScriptCodeGenerator::Address temp = codegen.add_temporary(field_type);2590codegen.generator->write_construct(temp, field_type.builtin_type, Vector<GDScriptCodeGenerator::Address>());2591codegen.generator->write_set_static_variable(temp, class_addr, p_script->static_variables_indices[field->identifier->name].index);2592codegen.generator->pop_temporary();2593}2594// The `else` branch is for objects, in such case we leave it as `null`.2595}2596}25972598for (int i = 0; i < p_class->members.size(); i++) {2599// Initialize static fields.2600if (p_class->members[i].type != GDScriptParser::ClassNode::Member::VARIABLE) {2601continue;2602}2603const GDScriptParser::VariableNode *field = p_class->members[i].variable;2604if (!field->is_static) {2605continue;2606}26072608if (field->initializer) {2609codegen.generator->write_newline(field->initializer->start_line);26102611GDScriptCodeGenerator::Address src_address = _parse_expression(codegen, r_error, field->initializer, false, true);2612if (r_error) {2613memdelete(codegen.generator);2614return nullptr;2615}26162617GDScriptDataType field_type = _gdtype_from_datatype(field->get_datatype(), codegen.script);2618GDScriptCodeGenerator::Address temp = codegen.add_temporary(field_type);26192620if (field->use_conversion_assign) {2621codegen.generator->write_assign_with_conversion(temp, src_address);2622} else {2623codegen.generator->write_assign(temp, src_address);2624}2625if (src_address.mode == GDScriptCodeGenerator::Address::TEMPORARY) {2626codegen.generator->pop_temporary();2627}26282629codegen.generator->write_set_static_variable(temp, class_addr, p_script->static_variables_indices[field->identifier->name].index);2630codegen.generator->pop_temporary();2631}2632}26332634if (p_script->has_method(GDScriptLanguage::get_singleton()->strings._static_init)) {2635codegen.generator->write_newline(p_class->start_line);2636codegen.generator->write_call(GDScriptCodeGenerator::Address(), class_addr, GDScriptLanguage::get_singleton()->strings._static_init, Vector<GDScriptCodeGenerator::Address>());2637}26382639#ifdef DEBUG_ENABLED2640if (EngineDebugger::is_active()) {2641String signature;2642// Path.2643if (!p_script->get_script_path().is_empty()) {2644signature += p_script->get_script_path();2645}2646// Location.2647signature += "::0";26482649// Function and class.26502651if (p_class->identifier) {2652signature += "::" + String(p_class->identifier->name) + "." + String(func_name);2653} else {2654signature += "::" + String(func_name);2655}26562657codegen.generator->set_signature(signature);2658}2659#endif26602661codegen.generator->set_initial_line(p_class->start_line);26622663GDScriptFunction *gd_function = codegen.generator->write_end();26642665memdelete(codegen.generator);26662667return gd_function;2668}26692670Error GDScriptCompiler::_parse_setter_getter(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::VariableNode *p_variable, bool p_is_setter) {2671Error err = OK;26722673GDScriptParser::FunctionNode *function;26742675if (p_is_setter) {2676function = p_variable->setter;2677} else {2678function = p_variable->getter;2679}26802681_parse_function(err, p_script, p_class, function);26822683return err;2684}26852686// Prepares given script, and inner class scripts, for compilation. It populates class members and2687// initializes method RPC info for its base classes first, then for itself, then for inner classes.2688// WARNING: This function cannot initiate compilation of other classes, or it will result in2689// cyclic dependency issues.2690Error GDScriptCompiler::_prepare_compilation(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {2691if (parsed_classes.has(p_script)) {2692return OK;2693}26942695if (parsing_classes.has(p_script)) {2696String class_name = p_class->identifier ? String(p_class->identifier->name) : p_class->fqcn;2697_set_error(vformat(R"(Cyclic class reference for "%s".)", class_name), p_class);2698return ERR_PARSE_ERROR;2699}27002701parsing_classes.insert(p_script);27022703p_script->clearing = true;27042705p_script->cancel_pending_functions(true);27062707p_script->native = Ref<GDScriptNativeClass>();2708p_script->base = Ref<GDScript>();2709p_script->_base = nullptr;2710p_script->members.clear();27112712// This makes possible to clear script constants and member_functions without heap-use-after-free errors.2713HashMap<StringName, Variant> constants;2714for (const KeyValue<StringName, Variant> &E : p_script->constants) {2715constants.insert(E.key, E.value);2716}2717p_script->constants.clear();2718constants.clear();2719HashMap<StringName, GDScriptFunction *> member_functions;2720for (const KeyValue<StringName, GDScriptFunction *> &E : p_script->member_functions) {2721member_functions.insert(E.key, E.value);2722}2723p_script->member_functions.clear();2724for (const KeyValue<StringName, GDScriptFunction *> &E : member_functions) {2725memdelete(E.value);2726}2727member_functions.clear();27282729p_script->static_variables.clear();27302731if (p_script->implicit_initializer) {2732memdelete(p_script->implicit_initializer);2733}2734if (p_script->implicit_ready) {2735memdelete(p_script->implicit_ready);2736}2737if (p_script->static_initializer) {2738memdelete(p_script->static_initializer);2739}27402741p_script->member_functions.clear();2742p_script->member_indices.clear();2743p_script->static_variables_indices.clear();2744p_script->static_variables.clear();2745p_script->_signals.clear();2746p_script->initializer = nullptr;2747p_script->implicit_initializer = nullptr;2748p_script->implicit_ready = nullptr;2749p_script->static_initializer = nullptr;2750p_script->rpc_config.clear();2751p_script->lambda_info.clear();27522753p_script->clearing = false;27542755p_script->tool = parser->is_tool();2756p_script->_is_abstract = p_class->is_abstract;27572758if (p_script->local_name != StringName()) {2759if (ClassDB::class_exists(p_script->local_name) && ClassDB::is_class_exposed(p_script->local_name)) {2760_set_error(vformat(R"(The class "%s" shadows a native class)", p_script->local_name), p_class);2761return ERR_ALREADY_EXISTS;2762}2763}27642765GDScriptDataType base_type = _gdtype_from_datatype(p_class->base_type, p_script, false);27662767if (base_type.native_type == StringName()) {2768_set_error(vformat(R"(Parser bug (please report): Empty native type in base class "%s")", p_script->path), p_class);2769return ERR_BUG;2770}27712772int native_idx = GDScriptLanguage::get_singleton()->get_global_map()[base_type.native_type];27732774p_script->native = GDScriptLanguage::get_singleton()->get_global_array()[native_idx];2775if (p_script->native.is_null()) {2776_set_error("Compiler bug (please report): script native type is null.", nullptr);2777return ERR_BUG;2778}27792780// Inheritance2781switch (base_type.kind) {2782case GDScriptDataType::NATIVE:2783// Nothing more to do.2784break;2785case GDScriptDataType::GDSCRIPT: {2786Ref<GDScript> base = Ref<GDScript>(base_type.script_type);2787if (base.is_null()) {2788_set_error("Compiler bug (please report): base script type is null.", nullptr);2789return ERR_BUG;2790}27912792if (main_script->has_class(base.ptr())) {2793Error err = _prepare_compilation(base.ptr(), p_class->base_type.class_type, p_keep_state);2794if (err) {2795return err;2796}2797} else if (!base->is_valid()) {2798Error err = OK;2799Ref<GDScript> base_root = GDScriptCache::get_shallow_script(base->path, err, p_script->path);2800if (err) {2801_set_error(vformat(R"(Could not parse base class "%s" from "%s": %s)", base->fully_qualified_name, base->path, error_names[err]), nullptr);2802return err;2803}2804if (base_root.is_valid()) {2805base = Ref<GDScript>(base_root->find_class(base->fully_qualified_name));2806}2807if (base.is_null()) {2808_set_error(vformat(R"(Could not find class "%s" in "%s".)", base->fully_qualified_name, base->path), nullptr);2809return ERR_COMPILATION_FAILED;2810}28112812err = _prepare_compilation(base.ptr(), p_class->base_type.class_type, p_keep_state);2813if (err) {2814_set_error(vformat(R"(Could not populate class members of base class "%s" in "%s".)", base->fully_qualified_name, base->path), nullptr);2815return err;2816}2817}28182819p_script->base = base;2820p_script->_base = base.ptr();2821p_script->member_indices = base->member_indices;2822} break;2823default: {2824_set_error("Parser bug (please report): invalid inheritance.", nullptr);2825return ERR_BUG;2826} break;2827}28282829// Duplicate RPC information from base GDScript2830// Base script isn't valid because it should not have been compiled yet, but the reference contains relevant info.2831if (base_type.kind == GDScriptDataType::GDSCRIPT && p_script->base.is_valid()) {2832p_script->rpc_config = p_script->base->rpc_config.duplicate();2833}28342835for (int i = 0; i < p_class->members.size(); i++) {2836const GDScriptParser::ClassNode::Member &member = p_class->members[i];2837switch (member.type) {2838case GDScriptParser::ClassNode::Member::VARIABLE: {2839const GDScriptParser::VariableNode *variable = member.variable;2840StringName name = variable->identifier->name;28412842GDScript::MemberInfo minfo;2843switch (variable->property) {2844case GDScriptParser::VariableNode::PROP_NONE:2845break; // Nothing to do.2846case GDScriptParser::VariableNode::PROP_SETGET:2847if (variable->setter_pointer != nullptr) {2848minfo.setter = variable->setter_pointer->name;2849}2850if (variable->getter_pointer != nullptr) {2851minfo.getter = variable->getter_pointer->name;2852}2853break;2854case GDScriptParser::VariableNode::PROP_INLINE:2855if (variable->setter != nullptr) {2856minfo.setter = "@" + variable->identifier->name + "_setter";2857}2858if (variable->getter != nullptr) {2859minfo.getter = "@" + variable->identifier->name + "_getter";2860}2861break;2862}2863minfo.data_type = _gdtype_from_datatype(variable->get_datatype(), p_script);28642865PropertyInfo prop_info = variable->get_datatype().to_property_info(name);2866PropertyInfo export_info = variable->export_info;28672868if (variable->exported) {2869if (!minfo.data_type.has_type()) {2870prop_info.type = export_info.type;2871prop_info.class_name = export_info.class_name;2872}2873prop_info.hint = export_info.hint;2874prop_info.hint_string = export_info.hint_string;2875prop_info.usage = export_info.usage;2876}2877prop_info.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE;2878minfo.property_info = prop_info;28792880if (variable->is_static) {2881minfo.index = p_script->static_variables_indices.size();2882p_script->static_variables_indices[name] = minfo;2883} else {2884minfo.index = p_script->member_indices.size();2885p_script->member_indices[name] = minfo;2886p_script->members.insert(name);2887}28882889#ifdef TOOLS_ENABLED2890if (variable->initializer != nullptr && variable->initializer->is_constant) {2891p_script->member_default_values[name] = variable->initializer->reduced_value;2892GDScriptCompiler::convert_to_initializer_type(p_script->member_default_values[name], variable);2893} else {2894p_script->member_default_values.erase(name);2895}2896#endif2897} break;28982899case GDScriptParser::ClassNode::Member::CONSTANT: {2900const GDScriptParser::ConstantNode *constant = member.constant;2901StringName name = constant->identifier->name;29022903p_script->constants.insert(name, constant->initializer->reduced_value);2904} break;29052906case GDScriptParser::ClassNode::Member::ENUM_VALUE: {2907const GDScriptParser::EnumNode::Value &enum_value = member.enum_value;2908StringName name = enum_value.identifier->name;29092910p_script->constants.insert(name, enum_value.value);2911} break;29122913case GDScriptParser::ClassNode::Member::SIGNAL: {2914const GDScriptParser::SignalNode *signal = member.signal;2915StringName name = signal->identifier->name;29162917p_script->_signals[name] = signal->method_info;2918} break;29192920case GDScriptParser::ClassNode::Member::ENUM: {2921const GDScriptParser::EnumNode *enum_n = member.m_enum;2922StringName name = enum_n->identifier->name;29232924p_script->constants.insert(name, enum_n->dictionary);2925} break;29262927case GDScriptParser::ClassNode::Member::GROUP: {2928const GDScriptParser::AnnotationNode *annotation = member.annotation;2929// Avoid name conflict. See GH-78252.2930StringName name = vformat("@group_%d_%s", p_script->members.size(), annotation->export_info.name);29312932// This is not a normal member, but we need this to keep indices in order.2933GDScript::MemberInfo minfo;2934minfo.index = p_script->member_indices.size();29352936PropertyInfo prop_info;2937prop_info.name = annotation->export_info.name;2938prop_info.usage = annotation->export_info.usage;2939prop_info.hint_string = annotation->export_info.hint_string;2940minfo.property_info = prop_info;29412942p_script->member_indices[name] = minfo;2943p_script->members.insert(name);2944} break;29452946case GDScriptParser::ClassNode::Member::FUNCTION: {2947const GDScriptParser::FunctionNode *function_n = member.function;29482949Variant config = function_n->rpc_config;2950if (config.get_type() != Variant::NIL) {2951p_script->rpc_config[function_n->identifier->name] = config;2952}2953} break;2954default:2955break; // Nothing to do here.2956}2957}29582959p_script->static_variables.resize(p_script->static_variables_indices.size());29602961parsed_classes.insert(p_script);2962parsing_classes.erase(p_script);29632964// Populate inner classes.2965for (int i = 0; i < p_class->members.size(); i++) {2966const GDScriptParser::ClassNode::Member &member = p_class->members[i];2967if (member.type != member.CLASS) {2968continue;2969}2970const GDScriptParser::ClassNode *inner_class = member.m_class;2971StringName name = inner_class->identifier->name;2972Ref<GDScript> &subclass = p_script->subclasses[name];2973GDScript *subclass_ptr = subclass.ptr();29742975// Subclass might still be parsing, just skip it2976if (!parsing_classes.has(subclass_ptr)) {2977Error err = _prepare_compilation(subclass_ptr, inner_class, p_keep_state);2978if (err) {2979return err;2980}2981}29822983p_script->constants.insert(name, subclass); //once parsed, goes to the list of constants2984}29852986return OK;2987}29882989Error GDScriptCompiler::_compile_class(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {2990// Compile member functions, getters, and setters.2991for (int i = 0; i < p_class->members.size(); i++) {2992const GDScriptParser::ClassNode::Member &member = p_class->members[i];2993if (member.type == member.FUNCTION) {2994const GDScriptParser::FunctionNode *function = member.function;2995Error err = OK;2996_parse_function(err, p_script, p_class, function);2997if (err) {2998return err;2999}3000} else if (member.type == member.VARIABLE) {3001const GDScriptParser::VariableNode *variable = member.variable;3002if (variable->property == GDScriptParser::VariableNode::PROP_INLINE) {3003if (variable->setter != nullptr) {3004Error err = _parse_setter_getter(p_script, p_class, variable, true);3005if (err) {3006return err;3007}3008}3009if (variable->getter != nullptr) {3010Error err = _parse_setter_getter(p_script, p_class, variable, false);3011if (err) {3012return err;3013}3014}3015}3016}3017}30183019{3020// Create `@implicit_new()` special function in any case.3021Error err = OK;3022_parse_function(err, p_script, p_class, nullptr);3023if (err) {3024return err;3025}3026}30273028if (p_class->onready_used) {3029// Create `@implicit_ready()` special function.3030Error err = OK;3031_parse_function(err, p_script, p_class, nullptr, true);3032if (err) {3033return err;3034}3035}30363037if (p_class->has_static_data) {3038Error err = OK;3039GDScriptFunction *func = _make_static_initializer(err, p_script, p_class);3040p_script->static_initializer = func;3041if (err) {3042return err;3043}3044}30453046#ifdef DEBUG_ENABLED30473048//validate instances if keeping state30493050if (p_keep_state) {3051for (RBSet<Object *>::Element *E = p_script->instances.front(); E;) {3052RBSet<Object *>::Element *N = E->next();30533054ScriptInstance *si = E->get()->get_script_instance();3055if (si->is_placeholder()) {3056#ifdef TOOLS_ENABLED3057PlaceHolderScriptInstance *psi = static_cast<PlaceHolderScriptInstance *>(si);30583059if (p_script->is_tool()) {3060//re-create as an instance3061p_script->placeholders.erase(psi); //remove placeholder30623063GDScriptInstance *instance = memnew(GDScriptInstance);3064instance->members.resize(p_script->member_indices.size());3065instance->script = Ref<GDScript>(p_script);3066instance->owner = E->get();30673068//needed for hot reloading3069for (const KeyValue<StringName, GDScript::MemberInfo> &F : p_script->member_indices) {3070instance->member_indices_cache[F.key] = F.value.index;3071}3072instance->owner->set_script_instance(instance);30733074/* STEP 2, INITIALIZE AND CONSTRUCT */30753076Callable::CallError ce;3077p_script->initializer->call(instance, nullptr, 0, ce);30783079if (ce.error != Callable::CallError::CALL_OK) {3080//well, tough luck, not gonna do anything here3081}3082}3083#endif // TOOLS_ENABLED3084} else {3085GDScriptInstance *gi = static_cast<GDScriptInstance *>(si);3086gi->reload_members();3087}30883089E = N;3090}3091}3092#endif //DEBUG_ENABLED30933094has_static_data = p_class->has_static_data;30953096for (int i = 0; i < p_class->members.size(); i++) {3097if (p_class->members[i].type != GDScriptParser::ClassNode::Member::CLASS) {3098continue;3099}3100const GDScriptParser::ClassNode *inner_class = p_class->members[i].m_class;3101StringName name = inner_class->identifier->name;3102GDScript *subclass = p_script->subclasses[name].ptr();31033104Error err = _compile_class(subclass, inner_class, p_keep_state);3105if (err) {3106return err;3107}31083109has_static_data = has_static_data || inner_class->has_static_data;3110}31113112p_script->_static_default_init();31133114p_script->valid = true;3115return OK;3116}31173118void GDScriptCompiler::convert_to_initializer_type(Variant &p_variant, const GDScriptParser::VariableNode *p_node) {3119// Set p_variant to the value of p_node's initializer, with the type of p_node's variable.3120GDScriptParser::DataType member_t = p_node->datatype;3121GDScriptParser::DataType init_t = p_node->initializer->datatype;3122if (member_t.is_hard_type() && init_t.is_hard_type() &&3123member_t.kind == GDScriptParser::DataType::BUILTIN && init_t.kind == GDScriptParser::DataType::BUILTIN) {3124if (Variant::can_convert_strict(init_t.builtin_type, member_t.builtin_type)) {3125const Variant *v = &p_node->initializer->reduced_value;3126Callable::CallError ce;3127Variant::construct(member_t.builtin_type, p_variant, &v, 1, ce);3128}3129}3130}31313132void GDScriptCompiler::make_scripts(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {3133p_script->fully_qualified_name = p_class->fqcn;3134p_script->local_name = p_class->identifier ? p_class->identifier->name : StringName();3135p_script->global_name = p_class->get_global_name();3136p_script->simplified_icon_path = p_class->simplified_icon_path;31373138HashMap<StringName, Ref<GDScript>> old_subclasses;31393140if (p_keep_state) {3141old_subclasses = p_script->subclasses;3142}31433144p_script->subclasses.clear();31453146for (int i = 0; i < p_class->members.size(); i++) {3147if (p_class->members[i].type != GDScriptParser::ClassNode::Member::CLASS) {3148continue;3149}3150const GDScriptParser::ClassNode *inner_class = p_class->members[i].m_class;3151StringName name = inner_class->identifier->name;31523153Ref<GDScript> subclass;31543155if (old_subclasses.has(name)) {3156subclass = old_subclasses[name];3157} else {3158subclass = GDScriptLanguage::get_singleton()->get_orphan_subclass(inner_class->fqcn);3159}31603161if (subclass.is_null()) {3162subclass.instantiate();3163}31643165subclass->_owner = p_script;3166subclass->path = p_script->path;3167p_script->subclasses.insert(name, subclass);31683169make_scripts(subclass.ptr(), inner_class, p_keep_state);3170}3171}31723173GDScriptCompiler::FunctionLambdaInfo GDScriptCompiler::_get_function_replacement_info(GDScriptFunction *p_func, int p_index, int p_depth, GDScriptFunction *p_parent_func) {3174FunctionLambdaInfo info;3175info.function = p_func;3176info.parent = p_parent_func;3177info.script = p_func->get_script();3178info.name = p_func->get_name();3179info.line = p_func->_initial_line;3180info.index = p_index;3181info.depth = p_depth;3182info.capture_count = 0;3183info.use_self = false;3184info.arg_count = p_func->_argument_count;3185info.default_arg_count = p_func->_default_arg_count;3186info.sublambdas = _get_function_lambda_replacement_info(p_func, p_depth, p_parent_func);31873188ERR_FAIL_NULL_V(info.script, info);3189GDScript::LambdaInfo *extra_info = info.script->lambda_info.getptr(p_func);3190if (extra_info != nullptr) {3191info.capture_count = extra_info->capture_count;3192info.use_self = extra_info->use_self;3193} else {3194info.capture_count = 0;3195info.use_self = false;3196}31973198return info;3199}32003201Vector<GDScriptCompiler::FunctionLambdaInfo> GDScriptCompiler::_get_function_lambda_replacement_info(GDScriptFunction *p_func, int p_depth, GDScriptFunction *p_parent_func) {3202Vector<FunctionLambdaInfo> result;3203// Only scrape the lambdas inside p_func.3204for (int i = 0; i < p_func->lambdas.size(); ++i) {3205result.push_back(_get_function_replacement_info(p_func->lambdas[i], i, p_depth + 1, p_func));3206}3207return result;3208}32093210GDScriptCompiler::ScriptLambdaInfo GDScriptCompiler::_get_script_lambda_replacement_info(GDScript *p_script) {3211ScriptLambdaInfo info;32123213if (p_script->implicit_initializer) {3214info.implicit_initializer_info = _get_function_lambda_replacement_info(p_script->implicit_initializer);3215}3216if (p_script->implicit_ready) {3217info.implicit_ready_info = _get_function_lambda_replacement_info(p_script->implicit_ready);3218}3219if (p_script->static_initializer) {3220info.static_initializer_info = _get_function_lambda_replacement_info(p_script->static_initializer);3221}32223223for (const KeyValue<StringName, GDScriptFunction *> &E : p_script->member_functions) {3224info.member_function_infos.insert(E.key, _get_function_lambda_replacement_info(E.value));3225}32263227for (const KeyValue<StringName, Ref<GDScript>> &KV : p_script->get_subclasses()) {3228info.subclass_info.insert(KV.key, _get_script_lambda_replacement_info(KV.value.ptr()));3229}32303231return info;3232}32333234bool GDScriptCompiler::_do_function_infos_match(const FunctionLambdaInfo &p_old_info, const FunctionLambdaInfo *p_new_info) {3235if (p_new_info == nullptr) {3236return false;3237}32383239if (p_new_info->capture_count != p_old_info.capture_count || p_new_info->use_self != p_old_info.use_self) {3240return false;3241}32423243int old_required_arg_count = p_old_info.arg_count - p_old_info.default_arg_count;3244int new_required_arg_count = p_new_info->arg_count - p_new_info->default_arg_count;3245if (new_required_arg_count > old_required_arg_count || p_new_info->arg_count < old_required_arg_count) {3246return false;3247}32483249return true;3250}32513252void GDScriptCompiler::_get_function_ptr_replacements(HashMap<GDScriptFunction *, GDScriptFunction *> &r_replacements, const FunctionLambdaInfo &p_old_info, const FunctionLambdaInfo *p_new_info) {3253ERR_FAIL_COND(r_replacements.has(p_old_info.function));3254if (!_do_function_infos_match(p_old_info, p_new_info)) {3255p_new_info = nullptr;3256}32573258r_replacements.insert(p_old_info.function, p_new_info != nullptr ? p_new_info->function : nullptr);3259_get_function_ptr_replacements(r_replacements, p_old_info.sublambdas, p_new_info != nullptr ? &p_new_info->sublambdas : nullptr);3260}32613262void GDScriptCompiler::_get_function_ptr_replacements(HashMap<GDScriptFunction *, GDScriptFunction *> &r_replacements, const Vector<FunctionLambdaInfo> &p_old_infos, const Vector<FunctionLambdaInfo> *p_new_infos) {3263for (int i = 0; i < p_old_infos.size(); ++i) {3264const FunctionLambdaInfo &old_info = p_old_infos[i];3265const FunctionLambdaInfo *new_info = nullptr;3266if (p_new_infos != nullptr && p_new_infos->size() == p_old_infos.size()) {3267// For now only attempt if the size is the same.3268new_info = &p_new_infos->get(i);3269}3270_get_function_ptr_replacements(r_replacements, old_info, new_info);3271}3272}32733274void GDScriptCompiler::_get_function_ptr_replacements(HashMap<GDScriptFunction *, GDScriptFunction *> &r_replacements, const ScriptLambdaInfo &p_old_info, const ScriptLambdaInfo *p_new_info) {3275_get_function_ptr_replacements(r_replacements, p_old_info.implicit_initializer_info, p_new_info != nullptr ? &p_new_info->implicit_initializer_info : nullptr);3276_get_function_ptr_replacements(r_replacements, p_old_info.implicit_ready_info, p_new_info != nullptr ? &p_new_info->implicit_ready_info : nullptr);3277_get_function_ptr_replacements(r_replacements, p_old_info.static_initializer_info, p_new_info != nullptr ? &p_new_info->static_initializer_info : nullptr);32783279for (const KeyValue<StringName, Vector<FunctionLambdaInfo>> &old_kv : p_old_info.member_function_infos) {3280_get_function_ptr_replacements(r_replacements, old_kv.value, p_new_info != nullptr ? p_new_info->member_function_infos.getptr(old_kv.key) : nullptr);3281}3282for (int i = 0; i < p_old_info.other_function_infos.size(); ++i) {3283const FunctionLambdaInfo &old_other_info = p_old_info.other_function_infos[i];3284const FunctionLambdaInfo *new_other_info = nullptr;3285if (p_new_info != nullptr && p_new_info->other_function_infos.size() == p_old_info.other_function_infos.size()) {3286// For now only attempt if the size is the same.3287new_other_info = &p_new_info->other_function_infos[i];3288}3289// Needs to be called on all old lambdas, even if there's no replacement.3290_get_function_ptr_replacements(r_replacements, old_other_info, new_other_info);3291}3292for (const KeyValue<StringName, ScriptLambdaInfo> &old_kv : p_old_info.subclass_info) {3293const ScriptLambdaInfo &old_subinfo = old_kv.value;3294const ScriptLambdaInfo *new_subinfo = p_new_info != nullptr ? p_new_info->subclass_info.getptr(old_kv.key) : nullptr;3295_get_function_ptr_replacements(r_replacements, old_subinfo, new_subinfo);3296}3297}32983299Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_script, bool p_keep_state) {3300err_line = -1;3301err_column = -1;3302error = "";3303parser = p_parser;3304main_script = p_script;3305const GDScriptParser::ClassNode *root = parser->get_tree();33063307source = p_script->get_path();33083309ScriptLambdaInfo old_lambda_info = _get_script_lambda_replacement_info(p_script);33103311// Create scripts for subclasses beforehand so they can be referenced3312make_scripts(p_script, root, p_keep_state);33133314main_script->_owner = nullptr;3315Error err = _prepare_compilation(main_script, parser->get_tree(), p_keep_state);33163317if (err) {3318return err;3319}33203321err = _compile_class(main_script, root, p_keep_state);3322if (err) {3323return err;3324}33253326ScriptLambdaInfo new_lambda_info = _get_script_lambda_replacement_info(p_script);33273328HashMap<GDScriptFunction *, GDScriptFunction *> func_ptr_replacements;3329_get_function_ptr_replacements(func_ptr_replacements, old_lambda_info, &new_lambda_info);3330main_script->_recurse_replace_function_ptrs(func_ptr_replacements);33313332if (has_static_data && !root->annotated_static_unload) {3333GDScriptCache::add_static_script(p_script);3334}33353336err = GDScriptCache::finish_compiling(main_script->path);3337if (err) {3338_set_error(R"(Failed to compile depended scripts.)", nullptr);3339}3340return err;3341}33423343String GDScriptCompiler::get_error() const {3344return error;3345}33463347int GDScriptCompiler::get_error_line() const {3348return err_line;3349}33503351int GDScriptCompiler::get_error_column() const {3352return err_column;3353}33543355GDScriptCompiler::GDScriptCompiler() {3356}335733583359