diff options
Diffstat (limited to 'modules/gdscript/gdscript_compiler.cpp')
-rw-r--r-- | modules/gdscript/gdscript_compiler.cpp | 464 |
1 files changed, 264 insertions, 200 deletions
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 63ca34fc24..ab0fe5c37d 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -35,6 +35,8 @@ #include "gdscript_cache.h" #include "gdscript_utility_functions.h" +#include "core/config/project_settings.h" + bool GDScriptCompiler::_is_class_member_property(CodeGen &codegen, const StringName &p_name) { if (codegen.function_node && codegen.function_node->is_static) { return false; @@ -107,7 +109,9 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D // Locate class by constructing the path to it and following that path GDScriptParser::ClassNode *class_type = p_datatype.class_type; if (class_type) { - if (class_type->fqcn.begins_with(main_script->path) || (!main_script->name.is_empty() && class_type->fqcn.begins_with(main_script->name))) { + const bool is_inner_by_path = (!main_script->path.is_empty()) && (class_type->fqcn.split("::")[0] == main_script->path); + const bool is_inner_by_name = (!main_script->name.is_empty()) && (class_type->fqcn.split("::")[0] == main_script->name); + if (is_inner_by_path || is_inner_by_name) { // Local class. List<StringName> names; while (class_type->outer) { @@ -126,8 +130,7 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D names.pop_back(); } result.kind = GDScriptDataType::GDSCRIPT; - result.script_type_ref = script; - result.script_type = result.script_type_ref.ptr(); + result.script_type = script.ptr(); result.native_type = script->get_instance_base_type(); } else { result.kind = GDScriptDataType::GDSCRIPT; @@ -137,22 +140,22 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D } } } break; + case GDScriptParser::DataType::ENUM: case GDScriptParser::DataType::ENUM_VALUE: result.has_type = true; result.kind = GDScriptDataType::BUILTIN; result.builtin_type = Variant::INT; break; - case GDScriptParser::DataType::ENUM: - result.has_type = true; - result.kind = GDScriptDataType::BUILTIN; - result.builtin_type = Variant::DICTIONARY; - break; case GDScriptParser::DataType::UNRESOLVED: { ERR_PRINT("Parser bug: converting unresolved type."); return GDScriptDataType(); } } + if (p_datatype.has_container_element_type()) { + result.set_container_element_type(_gdtype_from_datatype(p_datatype.get_container_element_type())); + } + // Only hold strong reference to the script if it's not the owner of the // element qualified with this type, to avoid cyclic references (leaks). if (result.script_type && result.script_type == p_owner) { @@ -262,7 +265,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code GDScriptNativeClass *nc = nullptr; while (scr) { if (scr->constants.has(identifier)) { - return GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::CLASS_CONSTANT, gen->add_or_get_name(identifier)); // TODO: Get type here. + return codegen.add_constant(scr->constants[identifier]); // TODO: Get type here. } if (scr->native.is_valid()) { nc = scr->native.ptr(); @@ -317,9 +320,21 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code } } + // Try globals. if (GDScriptLanguage::get_singleton()->get_global_map().has(identifier)) { - int idx = GDScriptLanguage::get_singleton()->get_global_map()[identifier]; - return GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::GLOBAL, idx); // TODO: Get type. + // If it's an autoload singleton, we postpone to load it at runtime. + // This is so one autoload doesn't try to load another before it's compiled. + OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list(); + if (autoloads.has(identifier) && autoloads[identifier].is_singleton) { + GDScriptCodeGenerator::Address global = codegen.add_temporary(_gdtype_from_datatype(in->get_datatype())); + int idx = GDScriptLanguage::get_singleton()->get_global_map()[identifier]; + gen->write_store_global(global, idx); + return global; + } else { + int idx = GDScriptLanguage::get_singleton()->get_global_map()[identifier]; + Variant global = GDScriptLanguage::get_singleton()->get_global_array()[idx]; + return codegen.add_constant(global); + } } // Try global classes. @@ -347,7 +362,9 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code #ifdef TOOLS_ENABLED if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(identifier)) { - return GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::NAMED_GLOBAL, gen->add_or_get_name(identifier)); // TODO: Get type. + GDScriptCodeGenerator::Address global = codegen.add_temporary(); // TODO: Get type. + gen->write_store_named_global(global, identifier); + return global; } #endif @@ -376,10 +393,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code Vector<GDScriptCodeGenerator::Address> values; // Create the result temporary first since it's the last to be killed. - GDScriptDataType array_type; - array_type.has_type = true; - array_type.kind = GDScriptDataType::BUILTIN; - array_type.builtin_type = Variant::ARRAY; + GDScriptDataType array_type = _gdtype_from_datatype(an->get_datatype()); GDScriptCodeGenerator::Address result = codegen.add_temporary(array_type); for (int i = 0; i < an->elements.size(); i++) { @@ -390,7 +404,11 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code values.push_back(val); } - gen->write_construct_array(result, values); + if (array_type.has_container_element_type()) { + gen->write_construct_typed_array(result, array_type.get_container_element_type(), values); + } else { + gen->write_construct_array(result, values); + } for (int i = 0; i < values.size(); i++) { if (values[i].mode == GDScriptCodeGenerator::Address::TEMPORARY) { @@ -423,8 +441,8 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code } break; case GDScriptParser::DictionaryNode::LUA_TABLE: - // Lua-style: key is an identifier interpreted as string. - String key = static_cast<const GDScriptParser::IdentifierNode *>(dn->elements[i].key)->name; + // Lua-style: key is an identifier interpreted as StringName. + StringName key = dn->elements[i].key->reduced_value.operator StringName(); element = codegen.add_constant(key); break; } @@ -470,6 +488,9 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code const GDScriptParser::CallNode *call = static_cast<const GDScriptParser::CallNode *>(p_expression); GDScriptDataType type = _gdtype_from_datatype(call->get_datatype()); GDScriptCodeGenerator::Address result = codegen.add_temporary(type); + GDScriptCodeGenerator::Address nil = GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::NIL); + + GDScriptCodeGenerator::Address return_addr = p_root ? nil : result; Vector<GDScriptCodeGenerator::Address> arguments; for (int i = 0; i < call->arguments.size(); i++) { @@ -520,52 +541,57 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code if (within_await) { gen->write_call_async(result, self, call->function_name, arguments); } else { - gen->write_call(result, self, call->function_name, arguments); + gen->write_call(return_addr, self, call->function_name, arguments); } } else { if (within_await) { gen->write_call_self_async(result, call->function_name, arguments); } else { - gen->write_call_self(result, call->function_name, arguments); + gen->write_call_self(return_addr, call->function_name, arguments); } } } else if (callee->type == GDScriptParser::Node::SUBSCRIPT) { const GDScriptParser::SubscriptNode *subscript = static_cast<const GDScriptParser::SubscriptNode *>(call->callee); if (subscript->is_attribute) { - GDScriptCodeGenerator::Address base = _parse_expression(codegen, r_error, subscript->base); - if (r_error) { - return GDScriptCodeGenerator::Address(); - } - if (within_await) { - gen->write_call_async(result, base, call->function_name, arguments); - } else if (base.type.has_type && base.type.kind != GDScriptDataType::BUILTIN) { - // Native method, use faster path. - StringName class_name; - if (base.type.kind == GDScriptDataType::NATIVE) { - class_name = base.type.native_type; - } else { - class_name = base.type.native_type == StringName() ? base.type.script_type->get_instance_base_type() : base.type.native_type; + // May be static built-in method call. + if (!call->is_super && subscript->base->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name) < Variant::VARIANT_MAX) { + gen->write_call_builtin_type_static(result, GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name), subscript->attribute->name, arguments); + } else { + GDScriptCodeGenerator::Address base = _parse_expression(codegen, r_error, subscript->base); + if (r_error) { + return GDScriptCodeGenerator::Address(); } - if (ClassDB::class_exists(class_name) && ClassDB::has_method(class_name, call->function_name)) { - MethodBind *method = ClassDB::get_method(class_name, call->function_name); - if (_have_exact_arguments(method, arguments)) { - // Exact arguments, use ptrcall. - gen->write_call_ptrcall(result, base, method, arguments); + if (within_await) { + gen->write_call_async(result, base, call->function_name, arguments); + } else if (base.type.has_type && base.type.kind != GDScriptDataType::BUILTIN) { + // Native method, use faster path. + StringName class_name; + if (base.type.kind == GDScriptDataType::NATIVE) { + class_name = base.type.native_type; } else { - // Not exact arguments, but still can use method bind call. - gen->write_call_method_bind(result, base, method, arguments); + class_name = base.type.native_type == StringName() ? base.type.script_type->get_instance_base_type() : base.type.native_type; } + if (ClassDB::class_exists(class_name) && ClassDB::has_method(class_name, call->function_name)) { + MethodBind *method = ClassDB::get_method(class_name, call->function_name); + if (_have_exact_arguments(method, arguments)) { + // Exact arguments, use ptrcall. + gen->write_call_ptrcall(result, base, method, arguments); + } else { + // Not exact arguments, but still can use method bind call. + gen->write_call_method_bind(result, base, method, arguments); + } + } else { + gen->write_call(return_addr, base, call->function_name, arguments); + } + } else if (base.type.has_type && base.type.kind == GDScriptDataType::BUILTIN) { + gen->write_call_builtin_type(result, base, base.type.builtin_type, call->function_name, arguments); } else { - gen->write_call(result, base, call->function_name, arguments); + gen->write_call(return_addr, base, call->function_name, arguments); + } + if (base.mode == GDScriptCodeGenerator::Address::TEMPORARY) { + gen->pop_temporary(); } - } else if (base.type.has_type && base.type.kind == GDScriptDataType::BUILTIN) { - gen->write_call_builtin_type(result, base, base.type.builtin_type, call->function_name, arguments); - } else { - gen->write_call(result, base, call->function_name, arguments); - } - if (base.mode == GDScriptCodeGenerator::Address::TEMPORARY) { - gen->pop_temporary(); } } else { _set_error("Cannot call something that isn't a function.", call->callee); @@ -676,9 +702,9 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code name = subscript->attribute->name; named = true; } else { - if (subscript->index->type == GDScriptParser::Node::LITERAL && static_cast<const GDScriptParser::LiteralNode *>(subscript->index)->value.get_type() == Variant::STRING) { + if (subscript->index->is_constant && subscript->index->reduced_value.get_type() == Variant::STRING_NAME) { // Also, somehow, named (speed up anyway). - name = static_cast<const GDScriptParser::LiteralNode *>(subscript->index)->value; + name = subscript->index->reduced_value; named = true; } else { // Regular indexing. @@ -707,7 +733,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code case GDScriptParser::Node::UNARY_OPERATOR: { const GDScriptParser::UnaryOpNode *unary = static_cast<const GDScriptParser::UnaryOpNode *>(p_expression); - GDScriptCodeGenerator::Address result = codegen.add_temporary(); + GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(unary->get_datatype())); GDScriptCodeGenerator::Address operand = _parse_expression(codegen, r_error, unary->operand); if (r_error) { @@ -725,7 +751,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code case GDScriptParser::Node::BINARY_OPERATOR: { const GDScriptParser::BinaryOpNode *binary = static_cast<const GDScriptParser::BinaryOpNode *>(p_expression); - GDScriptCodeGenerator::Address result = codegen.add_temporary(); + GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(binary->get_datatype())); switch (binary->operation) { case GDScriptParser::BinaryOpNode::OP_LOGIC_AND: { @@ -777,6 +803,10 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code gen->pop_temporary(); } } + + if (operand.mode == GDScriptCodeGenerator::Address::TEMPORARY) { + gen->pop_temporary(); + } } break; default: { GDScriptCodeGenerator::Address left_operand = _parse_expression(codegen, r_error, binary->left_operand); @@ -939,7 +969,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code // Perform operator if any. if (assignment->operation != GDScriptParser::AssignmentNode::OP_NONE) { - GDScriptCodeGenerator::Address value = codegen.add_temporary(); + GDScriptCodeGenerator::Address value = codegen.add_temporary(_gdtype_from_datatype(subscript->get_datatype())); if (subscript->is_attribute) { gen->write_get_named(value, name, prev_base); } else { @@ -958,6 +988,9 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code } else { gen->write_set(prev_base, key, assigned); } + if (key.mode == GDScriptCodeGenerator::Address::TEMPORARY) { + gen->pop_temporary(); + } if (assigned.mode == GDScriptCodeGenerator::Address::TEMPORARY) { gen->pop_temporary(); } @@ -965,8 +998,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code assigned = prev_base; // Set back the values into their bases. - for (List<ChainInfo>::Element *E = set_chain.front(); E; E = E->next()) { - const ChainInfo &info = E->get(); + for (const ChainInfo &info : set_chain) { if (!info.is_named) { gen->write_set(info.base, info.key, assigned); if (info.key.mode == GDScriptCodeGenerator::Address::TEMPORARY) { @@ -992,25 +1024,32 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code } } else if (assignment->assignee->type == GDScriptParser::Node::IDENTIFIER && _is_class_member_property(codegen, static_cast<GDScriptParser::IdentifierNode *>(assignment->assignee)->name)) { // Assignment to member property. - GDScriptCodeGenerator::Address assigned = _parse_expression(codegen, r_error, assignment->assigned_value); + GDScriptCodeGenerator::Address assigned_value = _parse_expression(codegen, r_error, assignment->assigned_value); if (r_error) { return GDScriptCodeGenerator::Address(); } - GDScriptCodeGenerator::Address assign_temp = assigned; + + GDScriptCodeGenerator::Address to_assign = assigned_value; + bool has_operation = assignment->operation != GDScriptParser::AssignmentNode::OP_NONE; StringName name = static_cast<GDScriptParser::IdentifierNode *>(assignment->assignee)->name; - if (assignment->operation != GDScriptParser::AssignmentNode::OP_NONE) { + if (has_operation) { + GDScriptCodeGenerator::Address op_result = codegen.add_temporary(); GDScriptCodeGenerator::Address member = codegen.add_temporary(); gen->write_get_member(member, name); - gen->write_binary_operator(assigned, assignment->variant_op, member, assigned); - gen->pop_temporary(); + gen->write_binary_operator(op_result, assignment->variant_op, member, assigned_value); + gen->pop_temporary(); // Pop member temp. + to_assign = op_result; } - gen->write_set_member(assigned, name); + gen->write_set_member(to_assign, name); - if (assign_temp.mode == GDScriptCodeGenerator::Address::TEMPORARY) { - gen->pop_temporary(); + if (to_assign.mode == GDScriptCodeGenerator::Address::TEMPORARY) { + gen->pop_temporary(); // Pop the assigned expression or the temp result if it has operation. + } + if (has_operation && assigned_value.mode == GDScriptCodeGenerator::Address::TEMPORARY) { + gen->pop_temporary(); // Pop the assigned expression if not done before. } } else { // Regular assignment. @@ -1044,19 +1083,25 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code } } - GDScriptCodeGenerator::Address assigned = _parse_expression(codegen, r_error, assignment->assigned_value); - GDScriptCodeGenerator::Address op_result; + GDScriptCodeGenerator::Address assigned_value = _parse_expression(codegen, r_error, assignment->assigned_value); if (r_error) { return GDScriptCodeGenerator::Address(); } - if (assignment->operation != GDScriptParser::AssignmentNode::OP_NONE) { + GDScriptCodeGenerator::Address to_assign; + bool has_operation = assignment->operation != GDScriptParser::AssignmentNode::OP_NONE; + if (has_operation) { // Perform operation. - op_result = codegen.add_temporary(); - gen->write_binary_operator(op_result, assignment->variant_op, target, assigned); + GDScriptCodeGenerator::Address op_result = codegen.add_temporary(); + GDScriptCodeGenerator::Address og_value = _parse_expression(codegen, r_error, assignment->assignee); + gen->write_binary_operator(op_result, assignment->variant_op, og_value, assigned_value); + to_assign = op_result; + + if (og_value.mode == GDScriptCodeGenerator::Address::TEMPORARY) { + gen->pop_temporary(); + } } else { - op_result = assigned; - assigned = GDScriptCodeGenerator::Address(); + to_assign = assigned_value; } GDScriptDataType assign_type = _gdtype_from_datatype(assignment->assignee->get_datatype()); @@ -1064,25 +1109,57 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code if (has_setter && !is_in_setter) { // Call setter. Vector<GDScriptCodeGenerator::Address> args; - args.push_back(op_result); + args.push_back(to_assign); gen->write_call(GDScriptCodeGenerator::Address(), GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::SELF), setter_function, args); } else { // Just assign. - gen->write_assign(target, op_result); + if (assignment->use_conversion_assign) { + gen->write_assign_with_conversion(target, to_assign); + } else { + gen->write_assign(target, to_assign); + } } - if (op_result.mode == GDScriptCodeGenerator::Address::TEMPORARY) { - gen->pop_temporary(); + if (to_assign.mode == GDScriptCodeGenerator::Address::TEMPORARY) { + gen->pop_temporary(); // Pop assigned value or temp operation result. } - if (assigned.mode == GDScriptCodeGenerator::Address::TEMPORARY) { - gen->pop_temporary(); + if (has_operation && assigned_value.mode == GDScriptCodeGenerator::Address::TEMPORARY) { + gen->pop_temporary(); // Pop assigned value if not done before. } if (target.mode == GDScriptCodeGenerator::Address::TEMPORARY) { - gen->pop_temporary(); + gen->pop_temporary(); // Pop the target to assignment. } } return GDScriptCodeGenerator::Address(); // Assignment does not return a value. } break; + case GDScriptParser::Node::LAMBDA: { + const GDScriptParser::LambdaNode *lambda = static_cast<const GDScriptParser::LambdaNode *>(p_expression); + GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(lambda->get_datatype())); + + Vector<GDScriptCodeGenerator::Address> captures; + captures.resize(lambda->captures.size()); + for (int i = 0; i < lambda->captures.size(); i++) { + captures.write[i] = _parse_expression(codegen, r_error, lambda->captures[i]); + if (r_error) { + return GDScriptCodeGenerator::Address(); + } + } + + GDScriptFunction *function = _parse_function(r_error, codegen.script, codegen.class_node, lambda->function, false, true); + if (r_error) { + return GDScriptCodeGenerator::Address(); + } + + gen->write_lambda(result, function, captures); + + for (int i = 0; i < captures.size(); i++) { + if (captures[i].mode == GDScriptCodeGenerator::Address::TEMPORARY) { + gen->pop_temporary(); + } + } + + return result; + } break; default: { ERR_FAIL_V_MSG(GDScriptCodeGenerator::Address(), "Bug in bytecode compiler, unexpected node in parse tree while parsing expression."); // Unreachable code. } break; @@ -1733,16 +1810,37 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui const GDScriptParser::VariableNode *lv = static_cast<const GDScriptParser::VariableNode *>(s); // Should be already in stack when the block began. GDScriptCodeGenerator::Address local = codegen.locals[lv->identifier->name]; + GDScriptParser::DataType local_type = lv->get_datatype(); if (lv->initializer != nullptr) { + // For typed arrays we need to make sure this is already initialized correctly so typed assignment work. + if (local_type.is_hard_type() && local_type.builtin_type == Variant::ARRAY) { + if (local_type.has_container_element_type()) { + codegen.generator->write_construct_typed_array(local, _gdtype_from_datatype(local_type.get_container_element_type(), codegen.script), Vector<GDScriptCodeGenerator::Address>()); + } else { + codegen.generator->write_construct_array(local, Vector<GDScriptCodeGenerator::Address>()); + } + } GDScriptCodeGenerator::Address src_address = _parse_expression(codegen, error, lv->initializer); if (error) { return error; } - gen->write_assign(local, src_address); + if (lv->use_conversion_assign) { + gen->write_assign_with_conversion(local, src_address); + } else { + gen->write_assign(local, src_address); + } if (src_address.mode == GDScriptCodeGenerator::Address::TEMPORARY) { codegen.generator->pop_temporary(); } + } else if (lv->get_datatype().is_hard_type()) { + // Initialize with default for type. + if (local_type.has_container_element_type()) { + codegen.generator->write_construct_typed_array(local, _gdtype_from_datatype(local_type.get_container_element_type(), codegen.script), Vector<GDScriptCodeGenerator::Address>()); + } else if (local_type.kind == GDScriptParser::DataType::BUILTIN) { + codegen.generator->write_construct(local, local_type.builtin_type, Vector<GDScriptCodeGenerator::Address>()); + } + // The `else` branch is for objects, in such case we leave it as `null`. } } break; case GDScriptParser::Node::CONSTANT: { @@ -1779,8 +1877,8 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui return OK; } -Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready) { - Error error = OK; +GDScriptFunction *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) { + r_error = OK; CodeGen codegen; codegen.generator = memnew(GDScriptByteCodeGenerator); @@ -1790,16 +1888,20 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser StringName func_name; bool is_static = false; - MultiplayerAPI::RPCMode rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED; + Multiplayer::RPCConfig rpc_config; GDScriptDataType return_type; return_type.has_type = true; return_type.kind = GDScriptDataType::BUILTIN; return_type.builtin_type = Variant::NIL; if (p_func) { - func_name = p_func->identifier->name; + if (p_func->identifier) { + func_name = p_func->identifier->name; + } else { + func_name = "<anonymous lambda>"; + } is_static = p_func->is_static; - rpc_mode = p_func->rpc_mode; + rpc_config = p_func->rpc_config; return_type = _gdtype_from_datatype(p_func->get_datatype(), p_script); } else { if (p_for_ready) { @@ -1810,7 +1912,7 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser } codegen.function_name = func_name; - codegen.generator->write_start(p_script, func_name, is_static, rpc_mode, return_type); + codegen.generator->write_start(p_script, func_name, is_static, rpc_config, return_type); int optional_parameters = 0; @@ -1828,11 +1930,11 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser } // Parse initializer if applies. - bool is_implicit_initializer = !p_for_ready && !p_func; - bool is_initializer = p_func && String(p_func->identifier->name) == GDScriptLanguage::get_singleton()->strings._init; - bool is_for_ready = p_for_ready || (p_func && String(p_func->identifier->name) == "_ready"); + bool is_implicit_initializer = !p_for_ready && !p_func && !p_for_lambda; + bool is_initializer = p_func && !p_for_lambda && String(p_func->identifier->name) == GDScriptLanguage::get_singleton()->strings._init; + bool is_for_ready = p_for_ready || (p_func && !p_for_lambda && String(p_func->identifier->name) == "_ready"); - if (is_implicit_initializer || is_for_ready) { + if (!p_for_lambda && (is_implicit_initializer || is_for_ready)) { // Initialize class fields. for (int i = 0; i < p_class->members.size(); i++) { if (p_class->members[i].type != GDScriptParser::ClassNode::Member::VARIABLE) { @@ -1844,21 +1946,45 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser continue; } + GDScriptParser::DataType field_type = field->get_datatype(); + + GDScriptCodeGenerator::Address dst_address(GDScriptCodeGenerator::Address::MEMBER, codegen.script->member_indices[field->identifier->name].index, _gdtype_from_datatype(field->get_datatype())); if (field->initializer) { // Emit proper line change. codegen.generator->write_newline(field->initializer->start_line); - GDScriptCodeGenerator::Address src_address = _parse_expression(codegen, error, field->initializer, false, true); - if (error) { + // For typed arrays we need to make sure this is already initialized correctly so typed assignment work. + if (field_type.is_hard_type() && field_type.builtin_type == Variant::ARRAY && field_type.has_container_element_type()) { + if (field_type.has_container_element_type()) { + codegen.generator->write_construct_typed_array(dst_address, _gdtype_from_datatype(field_type.get_container_element_type(), codegen.script), Vector<GDScriptCodeGenerator::Address>()); + } else { + codegen.generator->write_construct_array(dst_address, Vector<GDScriptCodeGenerator::Address>()); + } + } + GDScriptCodeGenerator::Address src_address = _parse_expression(codegen, r_error, field->initializer, false, true); + if (r_error) { memdelete(codegen.generator); - return error; + return nullptr; } - GDScriptCodeGenerator::Address dst_address(GDScriptCodeGenerator::Address::MEMBER, codegen.script->member_indices[field->identifier->name].index, _gdtype_from_datatype(field->get_datatype())); - codegen.generator->write_assign(dst_address, src_address); + if (field->use_conversion_assign) { + codegen.generator->write_assign_with_conversion(dst_address, src_address); + } else { + codegen.generator->write_assign(dst_address, src_address); + } if (src_address.mode == GDScriptCodeGenerator::Address::TEMPORARY) { codegen.generator->pop_temporary(); } + } else if (field->get_datatype().is_hard_type()) { + codegen.generator->write_newline(field->start_line); + + // Initialize with default for type. + if (field_type.has_container_element_type()) { + codegen.generator->write_construct_typed_array(dst_address, _gdtype_from_datatype(field_type.get_container_element_type(), codegen.script), Vector<GDScriptCodeGenerator::Address>()); + } else if (field_type.kind == GDScriptParser::DataType::BUILTIN) { + codegen.generator->write_construct(dst_address, field_type.builtin_type, Vector<GDScriptCodeGenerator::Address>()); + } + // The `else` branch is for objects, in such case we leave it as `null`. } } } @@ -1869,10 +1995,10 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser codegen.generator->start_parameters(); for (int i = p_func->parameters.size() - optional_parameters; i < p_func->parameters.size(); i++) { const GDScriptParser::ParameterNode *parameter = p_func->parameters[i]; - GDScriptCodeGenerator::Address src_addr = _parse_expression(codegen, error, parameter->default_value, true); - if (error) { + GDScriptCodeGenerator::Address src_addr = _parse_expression(codegen, r_error, parameter->default_value, true); + if (r_error) { memdelete(codegen.generator); - return error; + return nullptr; } GDScriptCodeGenerator::Address dst_addr = codegen.parameters[parameter->identifier->name]; codegen.generator->write_assign_default_parameter(dst_addr, src_addr); @@ -1883,10 +2009,10 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser codegen.generator->end_parameters(); } - Error err = _parse_block(codegen, p_func->body); - if (err) { + r_error = _parse_block(codegen, p_func->body); + if (r_error) { memdelete(codegen.generator); - return err; + return nullptr; } } @@ -1912,6 +2038,10 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser signature += "::" + String(func_name); } + if (p_for_lambda) { + signature += "(lambda)"; + } + codegen.generator->set_signature(signature); } #endif @@ -1919,8 +2049,10 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser if (p_func) { codegen.generator->set_initial_line(p_func->start_line); #ifdef TOOLS_ENABLED - p_script->member_lines[func_name] = p_func->start_line; - p_script->doc_functions[func_name] = p_func->doc_description; + if (!p_for_lambda) { + p_script->member_lines[func_name] = p_func->start_line; + p_script->doc_functions[func_name] = p_func->doc_description; + } #endif } else { codegen.generator->set_initial_line(0); @@ -1949,84 +2081,29 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser #endif } - p_script->member_functions[func_name] = gd_function; + if (!p_for_lambda) { + p_script->member_functions[func_name] = gd_function; + } memdelete(codegen.generator); - return OK; + return gd_function; } Error GDScriptCompiler::_parse_setter_getter(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::VariableNode *p_variable, bool p_is_setter) { Error error = OK; - CodeGen codegen; - codegen.generator = memnew(GDScriptByteCodeGenerator); - codegen.class_node = p_class; - codegen.script = p_script; + GDScriptParser::FunctionNode *function; - StringName func_name; - - if (p_is_setter) { - func_name = "@" + p_variable->identifier->name + "_setter"; - } else { - func_name = "@" + p_variable->identifier->name + "_getter"; - } - - GDScriptDataType return_type; if (p_is_setter) { - return_type.has_type = true; - return_type.kind = GDScriptDataType::BUILTIN; - return_type.builtin_type = Variant::NIL; + function = p_variable->setter; } else { - return_type = _gdtype_from_datatype(p_variable->get_datatype(), p_script); + function = p_variable->getter; } - codegen.generator->write_start(p_script, func_name, false, p_variable->rpc_mode, return_type); + _parse_function(error, p_script, p_class, function); - if (p_is_setter) { - uint32_t par_addr = codegen.generator->add_parameter(p_variable->setter_parameter->name, false, _gdtype_from_datatype(p_variable->get_datatype())); - codegen.parameters[p_variable->setter_parameter->name] = GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::FUNCTION_PARAMETER, par_addr, _gdtype_from_datatype(p_variable->get_datatype())); - } - - error = _parse_block(codegen, p_is_setter ? p_variable->setter : p_variable->getter); - if (error) { - memdelete(codegen.generator); - return error; - } - - GDScriptFunction *gd_function = codegen.generator->write_end(); - - p_script->member_functions[func_name] = gd_function; - -#ifdef DEBUG_ENABLED - if (EngineDebugger::is_active()) { - String signature; - //path - if (p_script->get_path() != String()) { - signature += p_script->get_path(); - } - //loc - signature += "::" + itos(p_is_setter ? p_variable->setter->start_line : p_variable->getter->start_line); - - //function and class - - if (p_class->identifier) { - signature += "::" + String(p_class->identifier->name) + "." + String(func_name); - } else { - signature += "::" + String(func_name); - } - - codegen.generator->set_signature(signature); - } -#endif - codegen.generator->set_initial_line(p_is_setter ? p_variable->setter->start_line : p_variable->getter->start_line); - -#ifdef TOOLS_ENABLED - p_script->member_lines[func_name] = p_is_setter ? p_variable->setter->start_line : p_variable->getter->start_line; -#endif - memdelete(codegen.generator); - - return OK; + return error; } Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) { @@ -2069,8 +2146,8 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar p_script->_base = nullptr; p_script->members.clear(); p_script->constants.clear(); - for (Map<StringName, GDScriptFunction *>::Element *E = p_script->member_functions.front(); E; E = E->next()) { - memdelete(E->get()); + for (const KeyValue<StringName, GDScriptFunction *> &E : p_script->member_functions) { + memdelete(E.value); } p_script->member_functions.clear(); p_script->member_indices.clear(); @@ -2081,6 +2158,13 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar p_script->tool = parser->is_tool(); p_script->name = p_class->identifier ? p_class->identifier->name : ""; + if (p_script->name != "") { + if (ClassDB::class_exists(p_script->name) && ClassDB::is_class_exposed(p_script->name)) { + _set_error("The class '" + p_script->name + "' shadows a native class", p_class); + return ERR_ALREADY_EXISTS; + } + } + Ref<GDScriptNativeClass> native; GDScriptDataType base_type = _gdtype_from_datatype(p_class->base_type); @@ -2116,7 +2200,7 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar if (err) { return err; } - if (base.is_null() && !base->is_valid()) { + if (base.is_null() || !base->is_valid()) { return ERR_COMPILATION_FAILED; } } @@ -2161,7 +2245,6 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar } break; } - minfo.rpc_mode = variable->rpc_mode; minfo.data_type = _gdtype_from_datatype(variable->get_datatype(), p_script); PropertyInfo prop_info = minfo.data_type; @@ -2175,7 +2258,7 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar } prop_info.hint = export_info.hint; prop_info.hint_string = export_info.hint_string; - prop_info.usage = export_info.usage; + prop_info.usage = export_info.usage | PROPERTY_USAGE_SCRIPT_VARIABLE; } else { prop_info.usage = PROPERTY_USAGE_SCRIPT_VARIABLE; } @@ -2233,28 +2316,6 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar const GDScriptParser::SignalNode *signal = member.signal; StringName name = signal->identifier->name; - GDScript *c = p_script; - - while (c) { - if (c->_signals.has(name)) { - _set_error("Signal '" + name + "' redefined (in current or parent class)", p_class); - return ERR_ALREADY_EXISTS; - } - - if (c->base.is_valid()) { - c = c->base.ptr(); - } else { - c = nullptr; - } - } - - if (native.is_valid()) { - if (ClassDB::has_signal(native->get_name(), name)) { - _set_error("Signal '" + name + "' redefined (original in native class '" + String(native->get_name()) + "')", p_class); - return ERR_ALREADY_EXISTS; - } - } - Vector<StringName> parameters_names; parameters_names.resize(signal->parameters.size()); for (int j = 0; j < signal->parameters.size(); j++) { @@ -2345,7 +2406,8 @@ Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptPa if (!has_ready && function->identifier->name == "_ready") { has_ready = true; } - Error err = _parse_function(p_script, p_class, function); + Error err = OK; + _parse_function(err, p_script, p_class, function); if (err) { return err; } @@ -2370,7 +2432,8 @@ Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptPa { // Create an implicit constructor in any case. - Error err = _parse_function(p_script, p_class, nullptr); + Error err = OK; + _parse_function(err, p_script, p_class, nullptr); if (err) { return err; } @@ -2378,7 +2441,8 @@ Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptPa if (!has_ready && p_class->onready_used) { //create a _ready constructor - Error err = _parse_function(p_script, p_class, nullptr, true); + Error err = OK; + _parse_function(err, p_script, p_class, nullptr, true); if (err) { return err; } @@ -2402,14 +2466,14 @@ Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptPa p_script->placeholders.erase(psi); //remove placeholder GDScriptInstance *instance = memnew(GDScriptInstance); - instance->base_ref = Object::cast_to<Reference>(E->get()); + instance->base_ref_counted = Object::cast_to<RefCounted>(E->get()); instance->members.resize(p_script->member_indices.size()); instance->script = Ref<GDScript>(p_script); instance->owner = E->get(); //needed for hot reloading - for (Map<StringName, GDScript::MemberInfo>::Element *F = p_script->member_indices.front(); F; F = F->next()) { - instance->member_indices_cache[F->key()] = F->get().index; + for (const KeyValue<StringName, GDScript::MemberInfo> &F : p_script->member_indices) { + instance->member_indices_cache[F.key] = F.value.index; } instance->owner->set_script_instance(instance); @@ -2477,7 +2541,7 @@ void GDScriptCompiler::_make_scripts(GDScript *p_script, const GDScriptParser::C if (orphan_subclass.is_valid()) { subclass = orphan_subclass; } else { - subclass.instance(); + subclass.instantiate(); } } |