Skip to content

Commit b1eb737

Browse files
committed
GDScript: Fix some lambda bugs
1 parent 3ed4497 commit b1eb737

File tree

12 files changed

+314
-84
lines changed

12 files changed

+314
-84
lines changed

.editorconfig

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,3 @@ indent_size = 4
2121
[*.{yml,yaml}]
2222
indent_style = space
2323
indent_size = 2
24-
25-
# GDScript unit test files
26-
[*.gd]
27-
indent_style = tab
28-
indent_size = 4
29-
insert_final_newline = true
30-
trim_trailing_whitespace = true
31-
32-
[*.out]
33-
insert_final_newline = true

modules/gdscript/.editorconfig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[*.gd]
2+
indent_style = tab
3+
indent_size = 4
4+
insert_final_newline = true
5+
trim_trailing_whitespace = true
6+
7+
[*.out]
8+
insert_final_newline = true

modules/gdscript/gdscript_compiler.cpp

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ void GDScriptCompiler::_set_error(const String &p_error, const GDScriptParser::N
8484
}
8585
}
8686

87-
GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::DataType &p_datatype, GDScript *p_owner) {
87+
GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::DataType &p_datatype, GDScript *p_owner, bool p_handle_metatype) {
8888
if (!p_datatype.is_set() || !p_datatype.is_hard_type() || p_datatype.is_coroutine) {
8989
return GDScriptDataType();
9090
}
@@ -101,18 +101,39 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
101101
result.builtin_type = p_datatype.builtin_type;
102102
} break;
103103
case GDScriptParser::DataType::NATIVE: {
104+
if (p_handle_metatype && p_datatype.is_meta_type) {
105+
result.kind = GDScriptDataType::NATIVE;
106+
result.builtin_type = Variant::OBJECT;
107+
result.native_type = GDScriptNativeClass::get_class_static();
108+
break;
109+
}
110+
104111
result.kind = GDScriptDataType::NATIVE;
105112
result.native_type = p_datatype.native_type;
106113
result.builtin_type = p_datatype.builtin_type;
107114
} break;
108115
case GDScriptParser::DataType::SCRIPT: {
116+
if (p_handle_metatype && p_datatype.is_meta_type) {
117+
result.kind = GDScriptDataType::NATIVE;
118+
result.builtin_type = Variant::OBJECT;
119+
result.native_type = p_datatype.script_type.is_valid() ? p_datatype.script_type->get_class() : Script::get_class_static();
120+
break;
121+
}
122+
109123
result.kind = GDScriptDataType::SCRIPT;
110124
result.builtin_type = p_datatype.builtin_type;
111125
result.script_type_ref = p_datatype.script_type;
112126
result.script_type = result.script_type_ref.ptr();
113127
result.native_type = p_datatype.native_type;
114128
} break;
115129
case GDScriptParser::DataType::CLASS: {
130+
if (p_handle_metatype && p_datatype.is_meta_type) {
131+
result.kind = GDScriptDataType::NATIVE;
132+
result.builtin_type = Variant::OBJECT;
133+
result.native_type = GDScript::get_class_static();
134+
break;
135+
}
136+
116137
result.kind = GDScriptDataType::GDSCRIPT;
117138
result.builtin_type = p_datatype.builtin_type;
118139
result.native_type = p_datatype.native_type;
@@ -148,6 +169,12 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
148169
}
149170
} break;
150171
case GDScriptParser::DataType::ENUM:
172+
if (p_handle_metatype && p_datatype.is_meta_type) {
173+
result.kind = GDScriptDataType::BUILTIN;
174+
result.builtin_type = Variant::DICTIONARY;
175+
break;
176+
}
177+
151178
result.kind = GDScriptDataType::BUILTIN;
152179
result.builtin_type = p_datatype.builtin_type;
153180
break;
@@ -159,7 +186,7 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
159186
}
160187

161188
if (p_datatype.has_container_element_type()) {
162-
result.set_container_element_type(_gdtype_from_datatype(p_datatype.get_container_element_type(), p_owner));
189+
result.set_container_element_type(_gdtype_from_datatype(p_datatype.get_container_element_type(), p_owner, false));
163190
}
164191

165192
return result;
@@ -533,7 +560,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
533560
} break;
534561
case GDScriptParser::Node::CAST: {
535562
const GDScriptParser::CastNode *cn = static_cast<const GDScriptParser::CastNode *>(p_expression);
536-
GDScriptDataType cast_type = _gdtype_from_datatype(cn->get_datatype(), codegen.script);
563+
GDScriptDataType cast_type = _gdtype_from_datatype(cn->get_datatype(), codegen.script, false);
537564

538565
GDScriptCodeGenerator::Address result;
539566
if (cast_type.has_type) {
@@ -911,7 +938,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
911938
GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(type_test->get_datatype(), codegen.script));
912939

913940
GDScriptCodeGenerator::Address operand = _parse_expression(codegen, r_error, type_test->operand);
914-
GDScriptDataType test_type = _gdtype_from_datatype(type_test->test_datatype, codegen.script);
941+
GDScriptDataType test_type = _gdtype_from_datatype(type_test->test_datatype, codegen.script, false);
915942
if (r_error) {
916943
return GDScriptCodeGenerator::Address();
917944
}
@@ -2587,7 +2614,7 @@ Error GDScriptCompiler::_prepare_compilation(GDScript *p_script, const GDScriptP
25872614
}
25882615
}
25892616

2590-
GDScriptDataType base_type = _gdtype_from_datatype(p_class->base_type, p_script);
2617+
GDScriptDataType base_type = _gdtype_from_datatype(p_class->base_type, p_script, false);
25912618

25922619
int native_idx = GDScriptLanguage::get_singleton()->get_global_map()[base_type.native_type];
25932620
p_script->native = GDScriptLanguage::get_singleton()->get_global_array()[native_idx];

modules/gdscript/gdscript_compiler.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ class GDScriptCompiler {
124124
Error _create_binary_operator(CodeGen &codegen, const GDScriptParser::BinaryOpNode *on, Variant::Operator op, bool p_initializer = false, const GDScriptCodeGenerator::Address &p_index_addr = GDScriptCodeGenerator::Address());
125125
Error _create_binary_operator(CodeGen &codegen, const GDScriptParser::ExpressionNode *p_left_operand, const GDScriptParser::ExpressionNode *p_right_operand, Variant::Operator op, bool p_initializer = false, const GDScriptCodeGenerator::Address &p_index_addr = GDScriptCodeGenerator::Address());
126126

127-
GDScriptDataType _gdtype_from_datatype(const GDScriptParser::DataType &p_datatype, GDScript *p_owner);
127+
GDScriptDataType _gdtype_from_datatype(const GDScriptParser::DataType &p_datatype, GDScript *p_owner, bool p_handle_metatype = true);
128128

129129
GDScriptCodeGenerator::Address _parse_assign_right_expression(CodeGen &codegen, Error &r_error, const GDScriptParser::AssignmentNode *p_assignmentint, const GDScriptCodeGenerator::Address &p_index_addr = GDScriptCodeGenerator::Address());
130130
GDScriptCodeGenerator::Address _parse_expression(CodeGen &codegen, Error &r_error, const GDScriptParser::ExpressionNode *p_expression, bool p_root = false, bool p_initializer = false, const GDScriptCodeGenerator::Address &p_index_addr = GDScriptCodeGenerator::Address());

modules/gdscript/gdscript_lambda_callable.cpp

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,13 +79,48 @@ void GDScriptLambdaCallable::call(const Variant **p_arguments, int p_argcount, V
7979
args.resize(p_argcount + captures_amount);
8080
for (int i = 0; i < captures_amount; i++) {
8181
args.write[i] = &captures[i];
82+
if (captures[i].get_type() == Variant::OBJECT) {
83+
bool was_freed = false;
84+
captures[i].get_validated_object_with_check(was_freed);
85+
if (was_freed) {
86+
ERR_PRINT(vformat(R"(Lambda capture at index %d was freed. Passed "null" instead.)", i));
87+
static Variant nil;
88+
args.write[i] = &nil;
89+
}
90+
}
8291
}
8392
for (int i = 0; i < p_argcount; i++) {
8493
args.write[i + captures_amount] = p_arguments[i];
8594
}
8695

8796
r_return_value = function->call(nullptr, args.ptrw(), args.size(), r_call_error);
88-
r_call_error.argument -= captures_amount;
97+
switch (r_call_error.error) {
98+
case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT:
99+
r_call_error.argument -= captures_amount;
100+
#ifdef DEBUG_ENABLED
101+
if (r_call_error.argument < 0) {
102+
ERR_PRINT(vformat("GDScript bug (please report): Invalid value of lambda capture at index %d.", captures_amount + r_call_error.argument));
103+
r_call_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; // TODO: Add a more suitable error code.
104+
r_call_error.argument = 0;
105+
r_call_error.expected = 0;
106+
}
107+
#endif
108+
break;
109+
case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS:
110+
case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
111+
r_call_error.expected -= captures_amount;
112+
#ifdef DEBUG_ENABLED
113+
if (r_call_error.expected < 0) {
114+
ERR_PRINT("GDScript bug (please report): Invalid lambda captures count.");
115+
r_call_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; // TODO: Add a more suitable error code.
116+
r_call_error.argument = 0;
117+
r_call_error.expected = 0;
118+
}
119+
#endif
120+
break;
121+
default:
122+
break;
123+
}
89124
} else {
90125
r_return_value = function->call(nullptr, p_arguments, p_argcount, r_call_error);
91126
}
@@ -148,13 +183,48 @@ void GDScriptLambdaSelfCallable::call(const Variant **p_arguments, int p_argcoun
148183
args.resize(p_argcount + captures_amount);
149184
for (int i = 0; i < captures_amount; i++) {
150185
args.write[i] = &captures[i];
186+
if (captures[i].get_type() == Variant::OBJECT) {
187+
bool was_freed = false;
188+
captures[i].get_validated_object_with_check(was_freed);
189+
if (was_freed) {
190+
ERR_PRINT(vformat(R"(Lambda capture at index %d was freed. Passed "null" instead.)", i));
191+
static Variant nil;
192+
args.write[i] = &nil;
193+
}
194+
}
151195
}
152196
for (int i = 0; i < p_argcount; i++) {
153197
args.write[i + captures_amount] = p_arguments[i];
154198
}
155199

156200
r_return_value = function->call(static_cast<GDScriptInstance *>(object->get_script_instance()), args.ptrw(), args.size(), r_call_error);
157-
r_call_error.argument -= captures_amount;
201+
switch (r_call_error.error) {
202+
case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT:
203+
r_call_error.argument -= captures_amount;
204+
#ifdef DEBUG_ENABLED
205+
if (r_call_error.argument < 0) {
206+
ERR_PRINT(vformat("GDScript bug (please report): Invalid value of lambda capture at index %d.", captures_amount + r_call_error.argument));
207+
r_call_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; // TODO: Add a more suitable error code.
208+
r_call_error.argument = 0;
209+
r_call_error.expected = 0;
210+
}
211+
#endif
212+
break;
213+
case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS:
214+
case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
215+
r_call_error.expected -= captures_amount;
216+
#ifdef DEBUG_ENABLED
217+
if (r_call_error.expected < 0) {
218+
ERR_PRINT("GDScript bug (please report): Invalid lambda captures count.");
219+
r_call_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; // TODO: Add a more suitable error code.
220+
r_call_error.argument = 0;
221+
r_call_error.expected = 0;
222+
}
223+
#endif
224+
break;
225+
default:
226+
break;
227+
}
158228
} else {
159229
r_return_value = function->call(static_cast<GDScriptInstance *>(object->get_script_instance()), p_arguments, p_argcount, r_call_error);
160230
}

modules/gdscript/gdscript_vm.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const
116116

117117
if (p_err.error == Callable::CallError::CALL_ERROR_INVALID_ARGUMENT) {
118118
int errorarg = p_err.argument;
119+
ERR_FAIL_COND_V_MSG(errorarg < 0 || argptrs[errorarg] == nullptr, "GDScript bug (please report): Invalid CallError argument index or null pointer.", "Invalid CallError argument index or null pointer.");
119120
// Handle the Object to Object case separately as we don't have further class details.
120121
#ifdef DEBUG_ENABLED
121122
if (p_err.expected == Variant::OBJECT && argptrs[errorarg]->get_type() == p_err.expected) {
@@ -128,9 +129,9 @@ String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const
128129
err_text = "Invalid type in " + p_where + ". Cannot convert argument " + itos(errorarg + 1) + " from " + Variant::get_type_name(argptrs[errorarg]->get_type()) + " to " + Variant::get_type_name(Variant::Type(p_err.expected)) + ".";
129130
}
130131
} else if (p_err.error == Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS) {
131-
err_text = "Invalid call to " + p_where + ". Expected " + itos(p_err.argument) + " arguments.";
132+
err_text = "Invalid call to " + p_where + ". Expected " + itos(p_err.expected) + " arguments.";
132133
} else if (p_err.error == Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS) {
133-
err_text = "Invalid call to " + p_where + ". Expected " + itos(p_err.argument) + " arguments.";
134+
err_text = "Invalid call to " + p_where + ". Expected " + itos(p_err.expected) + " arguments.";
134135
} else if (p_err.error == Callable::CallError::CALL_ERROR_INVALID_METHOD) {
135136
err_text = "Invalid call. Nonexistent " + p_where + ".";
136137
} else if (p_err.error == Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL) {
@@ -511,13 +512,13 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
511512
if (p_argcount != _argument_count) {
512513
if (p_argcount > _argument_count) {
513514
r_err.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
514-
r_err.argument = _argument_count;
515+
r_err.expected = _argument_count;
515516

516517
call_depth--;
517518
return _get_default_variant_for_data_type(return_type);
518519
} else if (p_argcount < _argument_count - _default_arg_count) {
519520
r_err.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
520-
r_err.argument = _argument_count - _default_arg_count;
521+
r_err.expected = _argument_count - _default_arg_count;
521522
call_depth--;
522523
return _get_default_variant_for_data_type(return_type);
523524
} else {

modules/gdscript/tests/scripts/runtime/features/member_info.gd

Lines changed: 6 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ class MyClass:
55

66
enum MyEnum {}
77

8+
const Utils = preload("../../utils.notest.gd")
9+
810
static var test_static_var_untyped
911
static var test_static_var_weak_null = null
1012
static var test_static_var_weak_int = 1
@@ -58,68 +60,13 @@ func test():
5860
var script: Script = get_script()
5961
for property in script.get_property_list():
6062
if str(property.name).begins_with("test_"):
61-
if not (property.usage & PROPERTY_USAGE_SCRIPT_VARIABLE):
62-
print("Error: Missing `PROPERTY_USAGE_SCRIPT_VARIABLE` flag.")
63-
print("static var ", property.name, ": ", get_type(property))
63+
print(Utils.get_property_signature(property, true))
6464
for property in get_property_list():
6565
if str(property.name).begins_with("test_"):
66-
if not (property.usage & PROPERTY_USAGE_SCRIPT_VARIABLE):
67-
print("Error: Missing `PROPERTY_USAGE_SCRIPT_VARIABLE` flag.")
68-
print("var ", property.name, ": ", get_type(property))
66+
print(Utils.get_property_signature(property))
6967
for method in get_method_list():
7068
if str(method.name).begins_with("test_"):
71-
print(get_signature(method))
69+
print(Utils.get_method_signature(method))
7270
for method in get_signal_list():
7371
if str(method.name).begins_with("test_"):
74-
print(get_signature(method, true))
75-
76-
func get_type(property: Dictionary, is_return: bool = false) -> String:
77-
match property.type:
78-
TYPE_NIL:
79-
if property.usage & PROPERTY_USAGE_NIL_IS_VARIANT:
80-
return "Variant"
81-
return "void" if is_return else "null"
82-
TYPE_BOOL:
83-
return "bool"
84-
TYPE_INT:
85-
if property.usage & PROPERTY_USAGE_CLASS_IS_ENUM:
86-
return property.class_name
87-
return "int"
88-
TYPE_STRING:
89-
return "String"
90-
TYPE_DICTIONARY:
91-
return "Dictionary"
92-
TYPE_ARRAY:
93-
if property.hint == PROPERTY_HINT_ARRAY_TYPE:
94-
return "Array[%s]" % property.hint_string
95-
return "Array"
96-
TYPE_OBJECT:
97-
if not str(property.class_name).is_empty():
98-
return property.class_name
99-
return "Object"
100-
return "<error>"
101-
102-
func get_signature(method: Dictionary, is_signal: bool = false) -> String:
103-
var result: String = ""
104-
if method.flags & METHOD_FLAG_STATIC:
105-
result += "static "
106-
result += ("signal " if is_signal else "func ") + method.name + "("
107-
108-
var args: Array[Dictionary] = method.args
109-
var default_args: Array = method.default_args
110-
var mandatory_argc: int = args.size() - default_args.size()
111-
for i in args.size():
112-
if i > 0:
113-
result += ", "
114-
var arg: Dictionary = args[i]
115-
result += arg.name + ": " + get_type(arg)
116-
if i >= mandatory_argc:
117-
result += " = " + var_to_str(default_args[i - mandatory_argc])
118-
119-
result += ")"
120-
if is_signal:
121-
if get_type(method.return, true) != "void":
122-
print("Error: Signal return type must be `void`.")
123-
else:
124-
result += " -> " + get_type(method.return, true)
125-
return result
72+
print(Utils.get_method_signature(method, true))

modules/gdscript/tests/scripts/runtime/features/member_info.out

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ static var test_static_var_hard_int: int
66
var test_var_untyped: Variant
77
var test_var_weak_null: Variant
88
var test_var_weak_int: Variant
9-
var test_var_weak_int_exported: int
9+
@export var test_var_weak_int_exported: int
1010
var test_var_weak_variant_type: Variant
11-
var test_var_weak_variant_type_exported: Variant.Type
11+
@export var test_var_weak_variant_type_exported: Variant.Type
1212
var test_var_hard_variant: Variant
1313
var test_var_hard_int: int
1414
var test_var_hard_variant_type: Variant.Type
15-
var test_var_hard_variant_type_exported: Variant.Type
15+
@export var test_var_hard_variant_type_exported: Variant.Type
1616
var test_var_hard_node_process_mode: Node.ProcessMode
1717
var test_var_hard_my_enum: TestMemberInfo.MyEnum
1818
var test_var_hard_array: Array

0 commit comments

Comments
 (0)