summaryrefslogtreecommitdiffstats
path: root/modules/gdscript/gdscript_lambda_callable.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gdscript/gdscript_lambda_callable.cpp')
-rw-r--r--modules/gdscript/gdscript_lambda_callable.cpp126
1 files changed, 124 insertions, 2 deletions
diff --git a/modules/gdscript/gdscript_lambda_callable.cpp b/modules/gdscript/gdscript_lambda_callable.cpp
index 3b89f077bd..547f5607d3 100644
--- a/modules/gdscript/gdscript_lambda_callable.cpp
+++ b/modules/gdscript/gdscript_lambda_callable.cpp
@@ -44,11 +44,18 @@ bool GDScriptLambdaCallable::compare_less(const CallableCustom *p_a, const Calla
return p_a < p_b;
}
+bool GDScriptLambdaCallable::is_valid() const {
+ return CallableCustom::is_valid() && function != nullptr;
+}
+
uint32_t GDScriptLambdaCallable::hash() const {
return h;
}
String GDScriptLambdaCallable::get_as_text() const {
+ if (function == nullptr) {
+ return "<invalid lambda>";
+ }
if (function->get_name() != StringName()) {
return function->get_name().operator String() + "(lambda)";
}
@@ -74,29 +81,78 @@ StringName GDScriptLambdaCallable::get_method() const {
void GDScriptLambdaCallable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
int captures_amount = captures.size();
+ if (function == nullptr) {
+ r_return_value = Variant();
+ r_call_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL;
+ return;
+ }
+
if (captures_amount > 0) {
Vector<const Variant *> args;
args.resize(p_argcount + captures_amount);
for (int i = 0; i < captures_amount; i++) {
args.write[i] = &captures[i];
+ if (captures[i].get_type() == Variant::OBJECT) {
+ bool was_freed = false;
+ captures[i].get_validated_object_with_check(was_freed);
+ if (was_freed) {
+ ERR_PRINT(vformat(R"(Lambda capture at index %d was freed. Passed "null" instead.)", i));
+ static Variant nil;
+ args.write[i] = &nil;
+ }
+ }
}
for (int i = 0; i < p_argcount; i++) {
args.write[i + captures_amount] = p_arguments[i];
}
r_return_value = function->call(nullptr, args.ptrw(), args.size(), r_call_error);
- r_call_error.argument -= captures_amount;
+ switch (r_call_error.error) {
+ case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT:
+ r_call_error.argument -= captures_amount;
+#ifdef DEBUG_ENABLED
+ if (r_call_error.argument < 0) {
+ ERR_PRINT(vformat("GDScript bug (please report): Invalid value of lambda capture at index %d.", captures_amount + r_call_error.argument));
+ r_call_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; // TODO: Add a more suitable error code.
+ r_call_error.argument = 0;
+ r_call_error.expected = 0;
+ }
+#endif
+ break;
+ case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS:
+ case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
+ r_call_error.expected -= captures_amount;
+#ifdef DEBUG_ENABLED
+ if (r_call_error.expected < 0) {
+ ERR_PRINT("GDScript bug (please report): Invalid lambda captures count.");
+ r_call_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; // TODO: Add a more suitable error code.
+ r_call_error.argument = 0;
+ r_call_error.expected = 0;
+ }
+#endif
+ break;
+ default:
+ break;
+ }
} else {
r_return_value = function->call(nullptr, p_arguments, p_argcount, r_call_error);
}
}
GDScriptLambdaCallable::GDScriptLambdaCallable(Ref<GDScript> p_script, GDScriptFunction *p_function, const Vector<Variant> &p_captures) {
+ ERR_FAIL_NULL(p_script.ptr());
+ ERR_FAIL_NULL(p_function);
script = p_script;
function = p_function;
captures = p_captures;
h = (uint32_t)hash_murmur3_one_64((uint64_t)this);
+
+ updatable_func_ptr_element = p_script->_add_func_ptr_to_update(&function);
+}
+
+GDScriptLambdaCallable::~GDScriptLambdaCallable() {
+ GDScript::_remove_func_ptr_to_update(updatable_func_ptr_element);
}
bool GDScriptLambdaSelfCallable::compare_equal(const CallableCustom *p_a, const CallableCustom *p_b) {
@@ -109,11 +165,18 @@ bool GDScriptLambdaSelfCallable::compare_less(const CallableCustom *p_a, const C
return p_a < p_b;
}
+bool GDScriptLambdaSelfCallable::is_valid() const {
+ return CallableCustom::is_valid() && function != nullptr;
+}
+
uint32_t GDScriptLambdaSelfCallable::hash() const {
return h;
}
String GDScriptLambdaSelfCallable::get_as_text() const {
+ if (function == nullptr) {
+ return "<invalid lambda>";
+ }
if (function->get_name() != StringName()) {
return function->get_name().operator String() + "(lambda)";
}
@@ -143,36 +206,95 @@ void GDScriptLambdaSelfCallable::call(const Variant **p_arguments, int p_argcoun
int captures_amount = captures.size();
+ if (function == nullptr) {
+ r_return_value = Variant();
+ r_call_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL;
+ return;
+ }
+
if (captures_amount > 0) {
Vector<const Variant *> args;
args.resize(p_argcount + captures_amount);
for (int i = 0; i < captures_amount; i++) {
args.write[i] = &captures[i];
+ if (captures[i].get_type() == Variant::OBJECT) {
+ bool was_freed = false;
+ captures[i].get_validated_object_with_check(was_freed);
+ if (was_freed) {
+ ERR_PRINT(vformat(R"(Lambda capture at index %d was freed. Passed "null" instead.)", i));
+ static Variant nil;
+ args.write[i] = &nil;
+ }
+ }
}
for (int i = 0; i < p_argcount; i++) {
args.write[i + captures_amount] = p_arguments[i];
}
r_return_value = function->call(static_cast<GDScriptInstance *>(object->get_script_instance()), args.ptrw(), args.size(), r_call_error);
- r_call_error.argument -= captures_amount;
+ switch (r_call_error.error) {
+ case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT:
+ r_call_error.argument -= captures_amount;
+#ifdef DEBUG_ENABLED
+ if (r_call_error.argument < 0) {
+ ERR_PRINT(vformat("GDScript bug (please report): Invalid value of lambda capture at index %d.", captures_amount + r_call_error.argument));
+ r_call_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; // TODO: Add a more suitable error code.
+ r_call_error.argument = 0;
+ r_call_error.expected = 0;
+ }
+#endif
+ break;
+ case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS:
+ case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
+ r_call_error.expected -= captures_amount;
+#ifdef DEBUG_ENABLED
+ if (r_call_error.expected < 0) {
+ ERR_PRINT("GDScript bug (please report): Invalid lambda captures count.");
+ r_call_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; // TODO: Add a more suitable error code.
+ r_call_error.argument = 0;
+ r_call_error.expected = 0;
+ }
+#endif
+ break;
+ default:
+ break;
+ }
} else {
r_return_value = function->call(static_cast<GDScriptInstance *>(object->get_script_instance()), p_arguments, p_argcount, r_call_error);
}
}
GDScriptLambdaSelfCallable::GDScriptLambdaSelfCallable(Ref<RefCounted> p_self, GDScriptFunction *p_function, const Vector<Variant> &p_captures) {
+ ERR_FAIL_NULL(p_self.ptr());
+ ERR_FAIL_NULL(p_function);
reference = p_self;
object = p_self.ptr();
function = p_function;
captures = p_captures;
h = (uint32_t)hash_murmur3_one_64((uint64_t)this);
+
+ GDScript *gds = p_function->get_script();
+ if (gds != nullptr) {
+ updatable_func_ptr_element = gds->_add_func_ptr_to_update(&function);
+ }
}
GDScriptLambdaSelfCallable::GDScriptLambdaSelfCallable(Object *p_self, GDScriptFunction *p_function, const Vector<Variant> &p_captures) {
+ ERR_FAIL_NULL(p_self);
+ ERR_FAIL_NULL(p_function);
object = p_self;
function = p_function;
captures = p_captures;
h = (uint32_t)hash_murmur3_one_64((uint64_t)this);
+
+ GDScript *gds = p_function->get_script();
+ if (gds != nullptr) {
+ updatable_func_ptr_element = gds->_add_func_ptr_to_update(&function);
+ }
+}
+
+GDScriptLambdaSelfCallable::~GDScriptLambdaSelfCallable() {
+ GDScript::_remove_func_ptr_to_update(updatable_func_ptr_element);
}