|
@@ -1371,6 +1371,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
|
|
|
return GDScriptCodeGenerator::Address();
|
|
|
}
|
|
|
|
|
|
+ main_script->lambda_info.insert(function, { lambda->captures.size(), lambda->use_self });
|
|
|
gen->write_lambda(result, function, captures, lambda->use_self);
|
|
|
|
|
|
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->static_initializer = nullptr;
|
|
|
p_script->rpc_config.clear();
|
|
|
+ p_script->lambda_info.clear();
|
|
|
|
|
|
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) {
|
|
|
err_line = -1;
|
|
|
err_column = -1;
|
|
@@ -3050,6 +3174,8 @@ Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_scri
|
|
|
|
|
|
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
|
|
|
make_scripts(p_script, root, p_keep_state);
|
|
|
|
|
@@ -3065,6 +3191,27 @@ Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_scri
|
|
|
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) {
|
|
|
GDScriptCache::add_static_script(p_script);
|
|
|
}
|