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