GDScript: Lambda hot reloading
Co-authored-by: Adam Scott <ascott.ca@gmail.com>
This commit is contained in:
parent
30f2a6d611
commit
9fb8862d73
|
@ -1386,6 +1386,36 @@ String GDScript::debug_get_script_name(const Ref<Script> &p_script) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
thread_local GDScript::UpdatableFuncPtr GDScript::func_ptrs_to_update_thread_local;
|
||||||
|
|
||||||
|
GDScript::UpdatableFuncPtrElement GDScript::_add_func_ptr_to_update(GDScriptFunction **p_func_ptr_ptr) {
|
||||||
|
UpdatableFuncPtrElement result = {};
|
||||||
|
|
||||||
|
{
|
||||||
|
MutexLock lock(func_ptrs_to_update_thread_local.mutex);
|
||||||
|
result.element = func_ptrs_to_update_thread_local.ptrs.push_back(p_func_ptr_ptr);
|
||||||
|
result.mutex = &func_ptrs_to_update_thread_local.mutex;
|
||||||
|
|
||||||
|
if (likely(func_ptrs_to_update_thread_local.initialized)) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
func_ptrs_to_update_thread_local.initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
MutexLock lock(func_ptrs_to_update_mutex);
|
||||||
|
func_ptrs_to_update.push_back(&func_ptrs_to_update_thread_local);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GDScript::_remove_func_ptr_to_update(const UpdatableFuncPtrElement p_func_ptr_element) {
|
||||||
|
ERR_FAIL_NULL(p_func_ptr_element.element);
|
||||||
|
ERR_FAIL_NULL(p_func_ptr_element.mutex);
|
||||||
|
MutexLock lock(*p_func_ptr_element.mutex);
|
||||||
|
p_func_ptr_element.element->erase();
|
||||||
|
}
|
||||||
|
|
||||||
void GDScript::clear(ClearData *p_clear_data) {
|
void GDScript::clear(ClearData *p_clear_data) {
|
||||||
if (clearing) {
|
if (clearing) {
|
||||||
return;
|
return;
|
||||||
|
@ -1403,6 +1433,16 @@ void GDScript::clear(ClearData *p_clear_data) {
|
||||||
is_root = true;
|
is_root = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
MutexLock outer_lock(func_ptrs_to_update_mutex);
|
||||||
|
for (UpdatableFuncPtr *updatable : func_ptrs_to_update) {
|
||||||
|
MutexLock inner_lock(updatable->mutex);
|
||||||
|
for (GDScriptFunction **func_ptr_ptr : updatable->ptrs) {
|
||||||
|
*func_ptr_ptr = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
RBSet<GDScript *> must_clear_dependencies = get_must_clear_dependencies();
|
RBSet<GDScript *> must_clear_dependencies = get_must_clear_dependencies();
|
||||||
for (GDScript *E : must_clear_dependencies) {
|
for (GDScript *E : must_clear_dependencies) {
|
||||||
clear_data->scripts.insert(E);
|
clear_data->scripts.insert(E);
|
||||||
|
|
|
@ -86,6 +86,8 @@ class GDScript : public Script {
|
||||||
friend class GDScriptAnalyzer;
|
friend class GDScriptAnalyzer;
|
||||||
friend class GDScriptCompiler;
|
friend class GDScriptCompiler;
|
||||||
friend class GDScriptDocGen;
|
friend class GDScriptDocGen;
|
||||||
|
friend class GDScriptLambdaCallable;
|
||||||
|
friend class GDScriptLambdaSelfCallable;
|
||||||
friend class GDScriptLanguage;
|
friend class GDScriptLanguage;
|
||||||
friend struct GDScriptUtilityFunctionsDefinitions;
|
friend struct GDScriptUtilityFunctionsDefinitions;
|
||||||
|
|
||||||
|
@ -108,6 +110,30 @@ class GDScript : public Script {
|
||||||
HashMap<StringName, MethodInfo> _signals;
|
HashMap<StringName, MethodInfo> _signals;
|
||||||
Dictionary rpc_config;
|
Dictionary rpc_config;
|
||||||
|
|
||||||
|
struct LambdaInfo {
|
||||||
|
int capture_count;
|
||||||
|
bool use_self;
|
||||||
|
};
|
||||||
|
|
||||||
|
HashMap<GDScriptFunction *, LambdaInfo> lambda_info;
|
||||||
|
|
||||||
|
// List is used here because a ptr to elements are stored, so the memory locations need to be stable
|
||||||
|
struct UpdatableFuncPtr {
|
||||||
|
List<GDScriptFunction **> ptrs;
|
||||||
|
Mutex mutex;
|
||||||
|
bool initialized = false;
|
||||||
|
};
|
||||||
|
struct UpdatableFuncPtrElement {
|
||||||
|
List<GDScriptFunction **>::Element *element = nullptr;
|
||||||
|
Mutex *mutex = nullptr;
|
||||||
|
};
|
||||||
|
static thread_local UpdatableFuncPtr func_ptrs_to_update_thread_local;
|
||||||
|
List<UpdatableFuncPtr *> func_ptrs_to_update;
|
||||||
|
Mutex func_ptrs_to_update_mutex;
|
||||||
|
|
||||||
|
UpdatableFuncPtrElement _add_func_ptr_to_update(GDScriptFunction **p_func_ptr_ptr);
|
||||||
|
static void _remove_func_ptr_to_update(const UpdatableFuncPtrElement p_func_ptr_element);
|
||||||
|
|
||||||
#ifdef TOOLS_ENABLED
|
#ifdef TOOLS_ENABLED
|
||||||
// For static data storage during hot-reloading.
|
// For static data storage during hot-reloading.
|
||||||
HashMap<StringName, MemberInfo> old_static_variables_indices;
|
HashMap<StringName, MemberInfo> old_static_variables_indices;
|
||||||
|
|
|
@ -1371,6 +1371,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
|
||||||
return GDScriptCodeGenerator::Address();
|
return GDScriptCodeGenerator::Address();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
main_script->lambda_info.insert(function, { lambda->captures.size(), lambda->use_self });
|
||||||
gen->write_lambda(result, function, captures, lambda->use_self);
|
gen->write_lambda(result, function, captures, lambda->use_self);
|
||||||
|
|
||||||
for (int i = 0; i < captures.size(); i++) {
|
for (int i = 0; i < captures.size(); i++) {
|
||||||
|
@ -2631,6 +2632,7 @@ Error GDScriptCompiler::_prepare_compilation(GDScript *p_script, const GDScriptP
|
||||||
p_script->implicit_ready = nullptr;
|
p_script->implicit_ready = nullptr;
|
||||||
p_script->static_initializer = nullptr;
|
p_script->static_initializer = nullptr;
|
||||||
p_script->rpc_config.clear();
|
p_script->rpc_config.clear();
|
||||||
|
p_script->lambda_info.clear();
|
||||||
|
|
||||||
p_script->clearing = false;
|
p_script->clearing = false;
|
||||||
|
|
||||||
|
@ -3040,6 +3042,128 @@ void GDScriptCompiler::make_scripts(GDScript *p_script, const GDScriptParser::Cl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GDScriptCompiler::FunctionLambdaInfo GDScriptCompiler::_get_function_replacement_info(GDScriptFunction *p_func, int p_index, int p_depth, GDScriptFunction *p_parent_func) {
|
||||||
|
FunctionLambdaInfo info;
|
||||||
|
info.function = p_func;
|
||||||
|
info.parent = p_parent_func;
|
||||||
|
info.script = p_parent_func;
|
||||||
|
info.name = p_func->get_name();
|
||||||
|
info.line = p_func->_initial_line;
|
||||||
|
info.index = p_index;
|
||||||
|
info.depth = p_depth;
|
||||||
|
info.capture_count = 0;
|
||||||
|
info.use_self = false;
|
||||||
|
info.arg_count = p_func->_argument_count;
|
||||||
|
info.default_arg_count = p_func->_default_arg_count;
|
||||||
|
info.sublambdas = _get_function_lambda_replacement_info(p_func, p_depth, p_parent_func);
|
||||||
|
|
||||||
|
GDScript::LambdaInfo *extra_info = main_script->lambda_info.getptr(p_func);
|
||||||
|
if (extra_info != nullptr) {
|
||||||
|
info.capture_count = extra_info->capture_count;
|
||||||
|
info.use_self = extra_info->use_self;
|
||||||
|
}
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector<GDScriptCompiler::FunctionLambdaInfo> GDScriptCompiler::_get_function_lambda_replacement_info(GDScriptFunction *p_func, int p_depth, GDScriptFunction *p_parent_func) {
|
||||||
|
Vector<FunctionLambdaInfo> result;
|
||||||
|
// Only scrape the lambdas inside p_func.
|
||||||
|
for (int i = 0; i < p_func->lambdas.size(); ++i) {
|
||||||
|
result.push_back(_get_function_replacement_info(p_func->lambdas[i], i, p_depth + 1, p_func));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
GDScriptCompiler::ScriptLambdaInfo GDScriptCompiler::_get_script_lambda_replacement_info(GDScript *p_script) {
|
||||||
|
ScriptLambdaInfo info;
|
||||||
|
|
||||||
|
if (p_script->implicit_initializer) {
|
||||||
|
info.implicit_initializer_info = _get_function_lambda_replacement_info(p_script->implicit_initializer);
|
||||||
|
}
|
||||||
|
if (p_script->implicit_ready) {
|
||||||
|
info.implicit_ready_info = _get_function_lambda_replacement_info(p_script->implicit_ready);
|
||||||
|
}
|
||||||
|
if (p_script->static_initializer) {
|
||||||
|
info.static_initializer_info = _get_function_lambda_replacement_info(p_script->static_initializer);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const KeyValue<StringName, GDScriptFunction *> &E : p_script->member_functions) {
|
||||||
|
info.member_function_infos.insert(E.key, _get_function_lambda_replacement_info(E.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const KeyValue<StringName, Ref<GDScript>> &KV : p_script->get_subclasses()) {
|
||||||
|
info.subclass_info.insert(KV.key, _get_script_lambda_replacement_info(KV.value.ptr()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GDScriptCompiler::_do_function_infos_match(const FunctionLambdaInfo &p_old_info, const FunctionLambdaInfo *p_new_info) {
|
||||||
|
if (p_new_info == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_new_info->capture_count != p_old_info.capture_count || p_new_info->use_self != p_old_info.use_self) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int old_required_arg_count = p_old_info.arg_count - p_old_info.default_arg_count;
|
||||||
|
int new_required_arg_count = p_new_info->arg_count - p_new_info->default_arg_count;
|
||||||
|
if (new_required_arg_count > old_required_arg_count || p_new_info->arg_count < old_required_arg_count) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GDScriptCompiler::_get_function_ptr_replacements(HashMap<GDScriptFunction *, GDScriptFunction *> &r_replacements, const FunctionLambdaInfo &p_old_info, const FunctionLambdaInfo *p_new_info) {
|
||||||
|
ERR_FAIL_COND(r_replacements.has(p_old_info.function));
|
||||||
|
if (!_do_function_infos_match(p_old_info, p_new_info)) {
|
||||||
|
p_new_info = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
r_replacements.insert(p_old_info.function, p_new_info != nullptr ? p_new_info->function : nullptr);
|
||||||
|
_get_function_ptr_replacements(r_replacements, p_old_info.sublambdas, p_new_info != nullptr ? &p_new_info->sublambdas : nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GDScriptCompiler::_get_function_ptr_replacements(HashMap<GDScriptFunction *, GDScriptFunction *> &r_replacements, const Vector<FunctionLambdaInfo> &p_old_infos, const Vector<FunctionLambdaInfo> *p_new_infos) {
|
||||||
|
for (int i = 0; i < p_old_infos.size(); ++i) {
|
||||||
|
const FunctionLambdaInfo &old_info = p_old_infos[i];
|
||||||
|
const FunctionLambdaInfo *new_info = nullptr;
|
||||||
|
if (p_new_infos != nullptr && p_new_infos->size() == p_old_infos.size()) {
|
||||||
|
// For now only attempt if the size is the same.
|
||||||
|
new_info = &p_new_infos->get(i);
|
||||||
|
}
|
||||||
|
_get_function_ptr_replacements(r_replacements, old_info, new_info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GDScriptCompiler::_get_function_ptr_replacements(HashMap<GDScriptFunction *, GDScriptFunction *> &r_replacements, const ScriptLambdaInfo &p_old_info, const ScriptLambdaInfo *p_new_info) {
|
||||||
|
_get_function_ptr_replacements(r_replacements, p_old_info.implicit_initializer_info, p_new_info != nullptr ? &p_new_info->implicit_initializer_info : nullptr);
|
||||||
|
_get_function_ptr_replacements(r_replacements, p_old_info.implicit_ready_info, p_new_info != nullptr ? &p_new_info->implicit_ready_info : nullptr);
|
||||||
|
_get_function_ptr_replacements(r_replacements, p_old_info.static_initializer_info, p_new_info != nullptr ? &p_new_info->static_initializer_info : nullptr);
|
||||||
|
|
||||||
|
for (const KeyValue<StringName, Vector<FunctionLambdaInfo>> &old_kv : p_old_info.member_function_infos) {
|
||||||
|
_get_function_ptr_replacements(r_replacements, old_kv.value, p_new_info != nullptr ? p_new_info->member_function_infos.getptr(old_kv.key) : nullptr);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < p_old_info.other_function_infos.size(); ++i) {
|
||||||
|
const FunctionLambdaInfo &old_other_info = p_old_info.other_function_infos[i];
|
||||||
|
const FunctionLambdaInfo *new_other_info = nullptr;
|
||||||
|
if (p_new_info != nullptr && p_new_info->other_function_infos.size() == p_old_info.other_function_infos.size()) {
|
||||||
|
// For now only attempt if the size is the same.
|
||||||
|
new_other_info = &p_new_info->other_function_infos[i];
|
||||||
|
}
|
||||||
|
// Needs to be called on all old lambdas, even if there's no replacement.
|
||||||
|
_get_function_ptr_replacements(r_replacements, old_other_info, new_other_info);
|
||||||
|
}
|
||||||
|
for (const KeyValue<StringName, ScriptLambdaInfo> &old_kv : p_old_info.subclass_info) {
|
||||||
|
const ScriptLambdaInfo &old_subinfo = old_kv.value;
|
||||||
|
const ScriptLambdaInfo *new_subinfo = p_new_info != nullptr ? p_new_info->subclass_info.getptr(old_kv.key) : nullptr;
|
||||||
|
_get_function_ptr_replacements(r_replacements, old_subinfo, new_subinfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_script, bool p_keep_state) {
|
Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_script, bool p_keep_state) {
|
||||||
err_line = -1;
|
err_line = -1;
|
||||||
err_column = -1;
|
err_column = -1;
|
||||||
|
@ -3050,6 +3174,8 @@ Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_scri
|
||||||
|
|
||||||
source = p_script->get_path();
|
source = p_script->get_path();
|
||||||
|
|
||||||
|
ScriptLambdaInfo old_lambda_info = _get_script_lambda_replacement_info(p_script);
|
||||||
|
|
||||||
// Create scripts for subclasses beforehand so they can be referenced
|
// Create scripts for subclasses beforehand so they can be referenced
|
||||||
make_scripts(p_script, root, p_keep_state);
|
make_scripts(p_script, root, p_keep_state);
|
||||||
|
|
||||||
|
@ -3065,6 +3191,27 @@ Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_scri
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ScriptLambdaInfo new_lambda_info = _get_script_lambda_replacement_info(p_script);
|
||||||
|
|
||||||
|
HashMap<GDScriptFunction *, GDScriptFunction *> func_ptr_replacements;
|
||||||
|
_get_function_ptr_replacements(func_ptr_replacements, old_lambda_info, &new_lambda_info);
|
||||||
|
|
||||||
|
{
|
||||||
|
MutexLock outer_lock(main_script->func_ptrs_to_update_mutex);
|
||||||
|
for (GDScript::UpdatableFuncPtr *updatable : main_script->func_ptrs_to_update) {
|
||||||
|
MutexLock inner_lock(updatable->mutex);
|
||||||
|
for (GDScriptFunction **func_ptr_ptr : updatable->ptrs) {
|
||||||
|
GDScriptFunction **replacement = func_ptr_replacements.getptr(*func_ptr_ptr);
|
||||||
|
if (replacement != nullptr) {
|
||||||
|
*func_ptr_ptr = *replacement;
|
||||||
|
} else {
|
||||||
|
// Probably a lambda from another reload, ignore.
|
||||||
|
*func_ptr_ptr = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (has_static_data && !root->annotated_static_unload) {
|
if (has_static_data && !root->annotated_static_unload) {
|
||||||
GDScriptCache::add_static_script(p_script);
|
GDScriptCache::add_static_script(p_script);
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,34 @@ class GDScriptCompiler {
|
||||||
HashSet<GDScript *> parsing_classes;
|
HashSet<GDScript *> parsing_classes;
|
||||||
GDScript *main_script = nullptr;
|
GDScript *main_script = nullptr;
|
||||||
|
|
||||||
|
struct FunctionLambdaInfo {
|
||||||
|
GDScriptFunction *function;
|
||||||
|
GDScriptFunction *parent;
|
||||||
|
Ref<GDScript> script;
|
||||||
|
StringName name;
|
||||||
|
int line;
|
||||||
|
int index;
|
||||||
|
int depth;
|
||||||
|
//uint64_t code_hash;
|
||||||
|
//int code_size;
|
||||||
|
int capture_count;
|
||||||
|
int use_self;
|
||||||
|
int arg_count;
|
||||||
|
int default_arg_count;
|
||||||
|
//Vector<GDScriptDataType> argument_types;
|
||||||
|
//GDScriptDataType return_type;
|
||||||
|
Vector<FunctionLambdaInfo> sublambdas;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ScriptLambdaInfo {
|
||||||
|
Vector<FunctionLambdaInfo> implicit_initializer_info;
|
||||||
|
Vector<FunctionLambdaInfo> implicit_ready_info;
|
||||||
|
Vector<FunctionLambdaInfo> static_initializer_info;
|
||||||
|
HashMap<StringName, Vector<FunctionLambdaInfo>> member_function_infos;
|
||||||
|
Vector<FunctionLambdaInfo> other_function_infos;
|
||||||
|
HashMap<StringName, ScriptLambdaInfo> subclass_info;
|
||||||
|
};
|
||||||
|
|
||||||
struct CodeGen {
|
struct CodeGen {
|
||||||
GDScript *script = nullptr;
|
GDScript *script = nullptr;
|
||||||
const GDScriptParser::ClassNode *class_node = nullptr;
|
const GDScriptParser::ClassNode *class_node = nullptr;
|
||||||
|
@ -137,6 +165,13 @@ class GDScriptCompiler {
|
||||||
Error _parse_setter_getter(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::VariableNode *p_variable, bool p_is_setter);
|
Error _parse_setter_getter(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::VariableNode *p_variable, bool p_is_setter);
|
||||||
Error _prepare_compilation(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
|
Error _prepare_compilation(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
|
||||||
Error _compile_class(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
|
Error _compile_class(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
|
||||||
|
FunctionLambdaInfo _get_function_replacement_info(GDScriptFunction *p_func, int p_index = -1, int p_depth = 0, GDScriptFunction *p_parent_func = nullptr);
|
||||||
|
Vector<FunctionLambdaInfo> _get_function_lambda_replacement_info(GDScriptFunction *p_func, int p_depth = 0, GDScriptFunction *p_parent_func = nullptr);
|
||||||
|
ScriptLambdaInfo _get_script_lambda_replacement_info(GDScript *p_script);
|
||||||
|
bool _do_function_infos_match(const FunctionLambdaInfo &p_old_info, const FunctionLambdaInfo *p_new_info);
|
||||||
|
void _get_function_ptr_replacements(HashMap<GDScriptFunction *, GDScriptFunction *> &r_replacements, const FunctionLambdaInfo &p_old_info, const FunctionLambdaInfo *p_new_info);
|
||||||
|
void _get_function_ptr_replacements(HashMap<GDScriptFunction *, GDScriptFunction *> &r_replacements, const Vector<FunctionLambdaInfo> &p_old_infos, const Vector<FunctionLambdaInfo> *p_new_infos);
|
||||||
|
void _get_function_ptr_replacements(HashMap<GDScriptFunction *, GDScriptFunction *> &r_replacements, const ScriptLambdaInfo &p_old_info, const ScriptLambdaInfo *p_new_info);
|
||||||
int err_line = 0;
|
int err_line = 0;
|
||||||
int err_column = 0;
|
int err_column = 0;
|
||||||
StringName source;
|
StringName source;
|
||||||
|
|
|
@ -44,11 +44,18 @@ bool GDScriptLambdaCallable::compare_less(const CallableCustom *p_a, const Calla
|
||||||
return p_a < p_b;
|
return p_a < p_b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GDScriptLambdaCallable::is_valid() const {
|
||||||
|
return CallableCustom::is_valid() && function != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t GDScriptLambdaCallable::hash() const {
|
uint32_t GDScriptLambdaCallable::hash() const {
|
||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
String GDScriptLambdaCallable::get_as_text() const {
|
String GDScriptLambdaCallable::get_as_text() const {
|
||||||
|
if (function == nullptr) {
|
||||||
|
return "<invalid lambda>";
|
||||||
|
}
|
||||||
if (function->get_name() != StringName()) {
|
if (function->get_name() != StringName()) {
|
||||||
return function->get_name().operator String() + "(lambda)";
|
return function->get_name().operator String() + "(lambda)";
|
||||||
}
|
}
|
||||||
|
@ -74,6 +81,12 @@ 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 {
|
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();
|
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) {
|
if (captures_amount > 0) {
|
||||||
Vector<const Variant *> args;
|
Vector<const Variant *> args;
|
||||||
args.resize(p_argcount + captures_amount);
|
args.resize(p_argcount + captures_amount);
|
||||||
|
@ -127,11 +140,19 @@ void GDScriptLambdaCallable::call(const Variant **p_arguments, int p_argcount, V
|
||||||
}
|
}
|
||||||
|
|
||||||
GDScriptLambdaCallable::GDScriptLambdaCallable(Ref<GDScript> p_script, GDScriptFunction *p_function, const Vector<Variant> &p_captures) {
|
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;
|
script = p_script;
|
||||||
function = p_function;
|
function = p_function;
|
||||||
captures = p_captures;
|
captures = p_captures;
|
||||||
|
|
||||||
h = (uint32_t)hash_murmur3_one_64((uint64_t)this);
|
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) {
|
bool GDScriptLambdaSelfCallable::compare_equal(const CallableCustom *p_a, const CallableCustom *p_b) {
|
||||||
|
@ -144,11 +165,18 @@ bool GDScriptLambdaSelfCallable::compare_less(const CallableCustom *p_a, const C
|
||||||
return p_a < p_b;
|
return p_a < p_b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GDScriptLambdaSelfCallable::is_valid() const {
|
||||||
|
return CallableCustom::is_valid() && function != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t GDScriptLambdaSelfCallable::hash() const {
|
uint32_t GDScriptLambdaSelfCallable::hash() const {
|
||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
String GDScriptLambdaSelfCallable::get_as_text() const {
|
String GDScriptLambdaSelfCallable::get_as_text() const {
|
||||||
|
if (function == nullptr) {
|
||||||
|
return "<invalid lambda>";
|
||||||
|
}
|
||||||
if (function->get_name() != StringName()) {
|
if (function->get_name() != StringName()) {
|
||||||
return function->get_name().operator String() + "(lambda)";
|
return function->get_name().operator String() + "(lambda)";
|
||||||
}
|
}
|
||||||
|
@ -178,6 +206,12 @@ void GDScriptLambdaSelfCallable::call(const Variant **p_arguments, int p_argcoun
|
||||||
|
|
||||||
int captures_amount = captures.size();
|
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) {
|
if (captures_amount > 0) {
|
||||||
Vector<const Variant *> args;
|
Vector<const Variant *> args;
|
||||||
args.resize(p_argcount + captures_amount);
|
args.resize(p_argcount + captures_amount);
|
||||||
|
@ -231,18 +265,36 @@ void GDScriptLambdaSelfCallable::call(const Variant **p_arguments, int p_argcoun
|
||||||
}
|
}
|
||||||
|
|
||||||
GDScriptLambdaSelfCallable::GDScriptLambdaSelfCallable(Ref<RefCounted> p_self, GDScriptFunction *p_function, const Vector<Variant> &p_captures) {
|
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;
|
reference = p_self;
|
||||||
object = p_self.ptr();
|
object = p_self.ptr();
|
||||||
function = p_function;
|
function = p_function;
|
||||||
captures = p_captures;
|
captures = p_captures;
|
||||||
|
|
||||||
h = (uint32_t)hash_murmur3_one_64((uint64_t)this);
|
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) {
|
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;
|
object = p_self;
|
||||||
function = p_function;
|
function = p_function;
|
||||||
captures = p_captures;
|
captures = p_captures;
|
||||||
|
|
||||||
h = (uint32_t)hash_murmur3_one_64((uint64_t)this);
|
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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,12 +31,13 @@
|
||||||
#ifndef GDSCRIPT_LAMBDA_CALLABLE_H
|
#ifndef GDSCRIPT_LAMBDA_CALLABLE_H
|
||||||
#define GDSCRIPT_LAMBDA_CALLABLE_H
|
#define GDSCRIPT_LAMBDA_CALLABLE_H
|
||||||
|
|
||||||
|
#include "gdscript.h"
|
||||||
|
|
||||||
#include "core/object/ref_counted.h"
|
#include "core/object/ref_counted.h"
|
||||||
#include "core/templates/vector.h"
|
#include "core/templates/vector.h"
|
||||||
#include "core/variant/callable.h"
|
#include "core/variant/callable.h"
|
||||||
#include "core/variant/variant.h"
|
#include "core/variant/variant.h"
|
||||||
|
|
||||||
class GDScript;
|
|
||||||
class GDScriptFunction;
|
class GDScriptFunction;
|
||||||
class GDScriptInstance;
|
class GDScriptInstance;
|
||||||
|
|
||||||
|
@ -44,6 +45,7 @@ class GDScriptLambdaCallable : public CallableCustom {
|
||||||
GDScriptFunction *function = nullptr;
|
GDScriptFunction *function = nullptr;
|
||||||
Ref<GDScript> script;
|
Ref<GDScript> script;
|
||||||
uint32_t h;
|
uint32_t h;
|
||||||
|
GDScript::UpdatableFuncPtrElement updatable_func_ptr_element;
|
||||||
|
|
||||||
Vector<Variant> captures;
|
Vector<Variant> captures;
|
||||||
|
|
||||||
|
@ -51,6 +53,7 @@ class GDScriptLambdaCallable : public CallableCustom {
|
||||||
static bool compare_less(const CallableCustom *p_a, const CallableCustom *p_b);
|
static bool compare_less(const CallableCustom *p_a, const CallableCustom *p_b);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
bool is_valid() const override;
|
||||||
uint32_t hash() const override;
|
uint32_t hash() const override;
|
||||||
String get_as_text() const override;
|
String get_as_text() const override;
|
||||||
CompareEqualFunc get_compare_equal_func() const override;
|
CompareEqualFunc get_compare_equal_func() const override;
|
||||||
|
@ -60,7 +63,7 @@ public:
|
||||||
void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
|
void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
|
||||||
|
|
||||||
GDScriptLambdaCallable(Ref<GDScript> p_script, GDScriptFunction *p_function, const Vector<Variant> &p_captures);
|
GDScriptLambdaCallable(Ref<GDScript> p_script, GDScriptFunction *p_function, const Vector<Variant> &p_captures);
|
||||||
virtual ~GDScriptLambdaCallable() = default;
|
virtual ~GDScriptLambdaCallable();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Lambda callable that references a particular object, so it can use `self` in the body.
|
// Lambda callable that references a particular object, so it can use `self` in the body.
|
||||||
|
@ -69,6 +72,7 @@ class GDScriptLambdaSelfCallable : public CallableCustom {
|
||||||
Ref<RefCounted> reference; // For objects that are RefCounted, keep a reference.
|
Ref<RefCounted> reference; // For objects that are RefCounted, keep a reference.
|
||||||
Object *object = nullptr; // For non RefCounted objects, use a direct pointer.
|
Object *object = nullptr; // For non RefCounted objects, use a direct pointer.
|
||||||
uint32_t h;
|
uint32_t h;
|
||||||
|
GDScript::UpdatableFuncPtrElement updatable_func_ptr_element;
|
||||||
|
|
||||||
Vector<Variant> captures;
|
Vector<Variant> captures;
|
||||||
|
|
||||||
|
@ -76,6 +80,7 @@ class GDScriptLambdaSelfCallable : public CallableCustom {
|
||||||
static bool compare_less(const CallableCustom *p_a, const CallableCustom *p_b);
|
static bool compare_less(const CallableCustom *p_a, const CallableCustom *p_b);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
bool is_valid() const override;
|
||||||
uint32_t hash() const override;
|
uint32_t hash() const override;
|
||||||
String get_as_text() const override;
|
String get_as_text() const override;
|
||||||
CompareEqualFunc get_compare_equal_func() const override;
|
CompareEqualFunc get_compare_equal_func() const override;
|
||||||
|
@ -85,7 +90,7 @@ public:
|
||||||
|
|
||||||
GDScriptLambdaSelfCallable(Ref<RefCounted> p_self, GDScriptFunction *p_function, const Vector<Variant> &p_captures);
|
GDScriptLambdaSelfCallable(Ref<RefCounted> p_self, GDScriptFunction *p_function, const Vector<Variant> &p_captures);
|
||||||
GDScriptLambdaSelfCallable(Object *p_self, GDScriptFunction *p_function, const Vector<Variant> &p_captures);
|
GDScriptLambdaSelfCallable(Object *p_self, GDScriptFunction *p_function, const Vector<Variant> &p_captures);
|
||||||
virtual ~GDScriptLambdaSelfCallable() = default;
|
virtual ~GDScriptLambdaSelfCallable();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // GDSCRIPT_LAMBDA_CALLABLE_H
|
#endif // GDSCRIPT_LAMBDA_CALLABLE_H
|
||||||
|
|
|
@ -73,6 +73,7 @@ void GDScriptRPCCallable::call(const Variant **p_arguments, int p_argcount, Vari
|
||||||
}
|
}
|
||||||
|
|
||||||
GDScriptRPCCallable::GDScriptRPCCallable(Object *p_object, const StringName &p_method) {
|
GDScriptRPCCallable::GDScriptRPCCallable(Object *p_object, const StringName &p_method) {
|
||||||
|
ERR_FAIL_NULL(p_object);
|
||||||
object = p_object;
|
object = p_object;
|
||||||
method = p_method;
|
method = p_method;
|
||||||
h = method.hash();
|
h = method.hash();
|
||||||
|
|
Loading…
Reference in New Issue