Improved lambda hotswapping when uniquely named
This commit is contained in:
parent
6757f27dec
commit
20b2dceec8
13 changed files with 267 additions and 125 deletions
|
@ -1505,14 +1505,15 @@ GDScript::UpdatableFuncPtr::~UpdatableFuncPtr() {
|
|||
}
|
||||
|
||||
void GDScript::_recurse_replace_function_ptrs(const HashMap<GDScriptFunction *, GDScriptFunction *> &p_replacements) const {
|
||||
MutexLock lock(func_ptrs_to_update_mutex);
|
||||
for (UpdatableFuncPtr *updatable : func_ptrs_to_update) {
|
||||
HashMap<GDScriptFunction *, GDScriptFunction *>::ConstIterator replacement = p_replacements.find(updatable->ptr);
|
||||
if (replacement) {
|
||||
updatable->ptr = replacement->value;
|
||||
} else {
|
||||
// Probably a lambda from another reload, ignore.
|
||||
updatable->ptr = nullptr;
|
||||
{
|
||||
MutexLock lock(func_ptrs_to_update_mutex);
|
||||
for (UpdatableFuncPtr *updatable : func_ptrs_to_update) {
|
||||
HashMap<GDScriptFunction *, GDScriptFunction *>::ConstIterator replacement = p_replacements.find(updatable->ptr);
|
||||
if (replacement) {
|
||||
updatable->ptr = replacement->value;
|
||||
} else {
|
||||
updatable->ptr = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3110,129 +3110,113 @@ 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_func->get_script();
|
||||
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);
|
||||
|
||||
ERR_FAIL_NULL_V(info.script, info);
|
||||
GDScript::LambdaInfo *extra_info = info.script->lambda_info.getptr(p_func);
|
||||
if (extra_info != nullptr) {
|
||||
info.capture_count = extra_info->capture_count;
|
||||
info.use_self = extra_info->use_self;
|
||||
} else {
|
||||
info.capture_count = 0;
|
||||
info.use_self = false;
|
||||
}
|
||||
|
||||
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.
|
||||
void GDScriptCompiler::LambdaSourceInfoList::collect_function_lambda_replacement_info(GDScriptFunction *p_func) {
|
||||
// Only collect the lambdas inside.
|
||||
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));
|
||||
GDScriptFunction *lambda = p_func->lambdas[i];
|
||||
LambdaSourceInfo &info = infos[lambda->get_name()].push_back({})->get();
|
||||
|
||||
info.function = lambda;
|
||||
info.parent = p_func;
|
||||
info.script = lambda->get_script();
|
||||
info.name = lambda->get_name();
|
||||
info.is_static = lambda->is_static();
|
||||
info.use_self = false;
|
||||
info.capture_count = 0;
|
||||
info.arg_count = lambda->get_argument_count();
|
||||
info.default_arg_count = lambda->get_default_argument_count();
|
||||
info.sublambdas.collect_function_lambda_replacement_info(lambda);
|
||||
|
||||
if (info.script) {
|
||||
GDScript::LambdaInfo *extra_info = info.script->lambda_info.getptr(lambda);
|
||||
if (extra_info != nullptr) {
|
||||
info.capture_count = extra_info->capture_count;
|
||||
info.use_self = extra_info->use_self;
|
||||
} else {
|
||||
info.capture_count = 0;
|
||||
info.use_self = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
GDScriptCompiler::ScriptLambdaInfo GDScriptCompiler::_get_script_lambda_replacement_info(GDScript *p_script) {
|
||||
ScriptLambdaInfo info;
|
||||
|
||||
void GDScriptCompiler::ScriptLambdaInfo::collect_script_lambda_replacement_info(GDScript *p_script) {
|
||||
if (p_script->implicit_initializer) {
|
||||
info.implicit_initializer_info = _get_function_lambda_replacement_info(p_script->implicit_initializer);
|
||||
initializers.collect_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);
|
||||
initializers.collect_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);
|
||||
initializers.collect_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));
|
||||
member_functions[E.key].collect_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()));
|
||||
subclasses[KV.key].collect_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) {
|
||||
bool GDScriptCompiler::LambdaSourceInfo::can_be_replaced_by(const LambdaSourceInfo &p_new_info) const {
|
||||
if (p_new_info.capture_count != capture_count || p_new_info.use_self != use_self) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (p_new_info->capture_count != p_old_info.capture_count || p_new_info->use_self != p_old_info.use_self) {
|
||||
if (p_new_info.script != script) {
|
||||
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) {
|
||||
int old_required_arg_count = arg_count - 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;
|
||||
}
|
||||
void GDScriptCompiler::_collect_function_ptr_replacements(HashMap<GDScriptFunction *, GDScriptFunction *> &r_replacements, LambdaSourceInfoList &p_old, LambdaSourceInfoList *p_new) {
|
||||
for (KeyValue<StringName, List<LambdaSourceInfo>> &old_named_infos : p_old.infos) {
|
||||
HashMap<StringName, List<LambdaSourceInfo>>::Iterator new_named_infos_it = p_new->infos.find(old_named_infos.key);
|
||||
bool sizes_equal = new_named_infos_it && new_named_infos_it->value.size() == old_named_infos.value.size();
|
||||
|
||||
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);
|
||||
}
|
||||
List<LambdaSourceInfo>::Element *old_info_E = old_named_infos.value.front();
|
||||
List<LambdaSourceInfo>::Element *new_info_E = new_named_infos_it ? new_named_infos_it->value.front() : 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);
|
||||
while (old_info_E) {
|
||||
LambdaSourceInfo &old_info = old_info_E->get();
|
||||
LambdaSourceInfo *new_info = nullptr;
|
||||
if (new_info_E && sizes_equal) {
|
||||
new_info = &new_info_E->get();
|
||||
}
|
||||
|
||||
if (new_info && old_info.can_be_replaced_by(*new_info)) {
|
||||
r_replacements[old_info.function] = new_info->function;
|
||||
}
|
||||
|
||||
// This must be run on every `old_info.sublambdas` whether or not we have new info.
|
||||
_collect_function_ptr_replacements(r_replacements, old_info.sublambdas, new_info ? &new_info->sublambdas : nullptr);
|
||||
old_info_E = old_info_E->next();
|
||||
if (new_info_E) {
|
||||
new_info_E = new_info_E->next();
|
||||
}
|
||||
}
|
||||
_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);
|
||||
void GDScriptCompiler::_collect_function_ptr_replacements(HashMap<GDScriptFunction *, GDScriptFunction *> &r_replacements, ScriptLambdaInfo &p_old_info, ScriptLambdaInfo *p_new_info) {
|
||||
_collect_function_ptr_replacements(r_replacements, p_old_info.initializers, p_new_info ? &p_new_info->initializers : 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 (KeyValue<StringName, LambdaSourceInfoList> &old_kv : p_old_info.member_functions) {
|
||||
_collect_function_ptr_replacements(r_replacements, old_kv.value, p_new_info ? p_new_info->member_functions.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);
|
||||
for (KeyValue<StringName, ScriptLambdaInfo> &old_kv : p_old_info.subclasses) {
|
||||
ScriptLambdaInfo &old_subinfo = old_kv.value;
|
||||
ScriptLambdaInfo *new_subinfo = p_new_info ? p_new_info->subclasses.getptr(old_kv.key) : nullptr;
|
||||
_collect_function_ptr_replacements(r_replacements, old_subinfo, new_subinfo);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3246,7 +3230,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);
|
||||
ScriptLambdaInfo old_lambda_info;
|
||||
old_lambda_info.collect_script_lambda_replacement_info(p_script);
|
||||
|
||||
// Create scripts for subclasses beforehand so they can be referenced
|
||||
make_scripts(p_script, root, p_keep_state);
|
||||
|
@ -3263,10 +3248,11 @@ Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_scri
|
|||
return err;
|
||||
}
|
||||
|
||||
ScriptLambdaInfo new_lambda_info = _get_script_lambda_replacement_info(p_script);
|
||||
ScriptLambdaInfo new_lambda_info;
|
||||
new_lambda_info.collect_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);
|
||||
_collect_function_ptr_replacements(func_ptr_replacements, old_lambda_info, &new_lambda_info);
|
||||
main_script->_recurse_replace_function_ptrs(func_ptr_replacements);
|
||||
|
||||
if (has_static_data && !root->annotated_static_unload) {
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include "gdscript_function.h"
|
||||
#include "gdscript_parser.h"
|
||||
|
||||
#include "core/templates/hash_map.h"
|
||||
#include "core/templates/hash_set.h"
|
||||
|
||||
class GDScriptCompiler {
|
||||
|
@ -44,34 +45,41 @@ class GDScriptCompiler {
|
|||
HashSet<GDScript *> parsing_classes;
|
||||
GDScript *main_script = nullptr;
|
||||
|
||||
struct FunctionLambdaInfo {
|
||||
public:
|
||||
struct LambdaSourceInfo;
|
||||
|
||||
struct LambdaSourceInfoList {
|
||||
HashMap<StringName, List<LambdaSourceInfo>> infos;
|
||||
|
||||
void collect_function_lambda_replacement_info(GDScriptFunction *p_func);
|
||||
};
|
||||
|
||||
struct LambdaSourceInfo {
|
||||
GDScriptFunction *function = nullptr;
|
||||
GDScriptFunction *parent = nullptr;
|
||||
GDScript *script = nullptr;
|
||||
StringName name;
|
||||
int line = 0;
|
||||
int index = 0;
|
||||
int depth = 0;
|
||||
//uint64_t code_hash;
|
||||
//int code_size;
|
||||
int capture_count = 0;
|
||||
bool is_static = false;
|
||||
bool use_self = false;
|
||||
int capture_count = 0;
|
||||
int arg_count = 0;
|
||||
int default_arg_count = 0;
|
||||
//Vector<GDScriptDataType> argument_types;
|
||||
//GDScriptDataType return_type;
|
||||
Vector<FunctionLambdaInfo> sublambdas;
|
||||
LambdaSourceInfoList sublambdas;
|
||||
|
||||
bool can_be_replaced_by(const LambdaSourceInfo &p_new_info) const;
|
||||
};
|
||||
|
||||
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;
|
||||
LambdaSourceInfoList initializers;
|
||||
HashMap<StringName, LambdaSourceInfoList> member_functions;
|
||||
HashMap<StringName, ScriptLambdaInfo> subclasses;
|
||||
|
||||
void collect_script_lambda_replacement_info(GDScript *p_script);
|
||||
};
|
||||
|
||||
private:
|
||||
struct CodeGen {
|
||||
GDScript *script = nullptr;
|
||||
const GDScriptParser::ClassNode *class_node = nullptr;
|
||||
|
@ -161,13 +169,10 @@ class GDScriptCompiler {
|
|||
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 _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);
|
||||
|
||||
static void _collect_function_ptr_replacements(HashMap<GDScriptFunction *, GDScriptFunction *> &r_replacements, LambdaSourceInfoList &p_old, LambdaSourceInfoList *p_new);
|
||||
static void _collect_function_ptr_replacements(HashMap<GDScriptFunction *, GDScriptFunction *> &r_replacements, ScriptLambdaInfo &p_old_info, ScriptLambdaInfo *p_new_info);
|
||||
|
||||
int err_line = 0;
|
||||
int err_column = 0;
|
||||
StringName source;
|
||||
|
|
|
@ -580,6 +580,7 @@ public:
|
|||
_FORCE_INLINE_ bool is_static() const { return _static; }
|
||||
_FORCE_INLINE_ MethodInfo get_method_info() const { return method_info; }
|
||||
_FORCE_INLINE_ int get_argument_count() const { return _argument_count; }
|
||||
_FORCE_INLINE_ int get_default_argument_count() const { return _default_arg_count; }
|
||||
_FORCE_INLINE_ Variant get_rpc_config() const { return rpc_config; }
|
||||
_FORCE_INLINE_ int get_max_stack_size() const { return _stack_size; }
|
||||
|
||||
|
|
|
@ -203,13 +203,11 @@ int GDScriptTestRunner::run_tests() {
|
|||
print_line(test.get_source_relative_filepath());
|
||||
}
|
||||
for (GDScriptTest::TestResult &result : test.run_test()) {
|
||||
String source_file = result.source_test->get_source_file();
|
||||
String output_file = result.source_test->get_output_file();
|
||||
String expected = FileAccess::get_file_as_string(output_file);
|
||||
String expected = FileAccess::get_file_as_string(result.source_test->get_output_file());
|
||||
#ifndef DEBUG_ENABLED
|
||||
expected = strip_warnings(expected);
|
||||
#endif
|
||||
INFO(source_file);
|
||||
INFO(result.source_test->get_source_file());
|
||||
if (!result.passed) {
|
||||
INFO(expected);
|
||||
failed++;
|
||||
|
|
30
modules/gdscript/tests/scripts/hotswap/hotswap_lambda.gd
Normal file
30
modules/gdscript/tests/scripts/hotswap/hotswap_lambda.gd
Normal file
|
@ -0,0 +1,30 @@
|
|||
var member1 := func():
|
||||
return "Member 1"
|
||||
|
||||
var local1: Callable
|
||||
|
||||
var param1: Callable
|
||||
|
||||
var anon1: Callable
|
||||
var anon2: Callable
|
||||
|
||||
func test():
|
||||
var _v1 = func(): return "Local 1"
|
||||
local1 = _v1
|
||||
|
||||
print(local1.call())
|
||||
|
||||
print(member1.call())
|
||||
|
||||
test_parameters()
|
||||
|
||||
print(param1.call())
|
||||
|
||||
anon1 = func(): return "Anonymous 1"
|
||||
anon2 = func(): return "Anonymous 2"
|
||||
|
||||
print(anon1.call())
|
||||
print(anon2.call())
|
||||
|
||||
func test_parameters(_v1 = func(): return "Param 1"):
|
||||
param1 = _v1
|
|
@ -0,0 +1,6 @@
|
|||
GDTEST_OK
|
||||
Local 1
|
||||
Member 1
|
||||
Param 1
|
||||
Anonymous 1
|
||||
Anonymous 2
|
49
modules/gdscript/tests/scripts/hotswap/hotswap_lambda.swap1
Normal file
49
modules/gdscript/tests/scripts/hotswap/hotswap_lambda.swap1
Normal file
|
@ -0,0 +1,49 @@
|
|||
var member1 := func():
|
||||
return "Member 1 Hotswap 1"
|
||||
|
||||
var member2 := func():
|
||||
return "Member 2 Hotswap 1"
|
||||
|
||||
var local1: Callable
|
||||
var local2: Callable
|
||||
|
||||
var param1: Callable
|
||||
var param2: Callable
|
||||
|
||||
var anon1: Callable
|
||||
var anon2: Callable
|
||||
|
||||
func test():
|
||||
var _v1 = func(): return "Local 1 Hotswap 1"
|
||||
var _v2 = func(): return "Local 2 Hotswap 1"
|
||||
# Only set local2 here, local1 has already been set.
|
||||
local2 = _v2
|
||||
|
||||
print(local1.call())
|
||||
print(local2.call())
|
||||
|
||||
|
||||
print(member1.call())
|
||||
if member2:
|
||||
print(member2.call())
|
||||
else:
|
||||
print(member2)
|
||||
|
||||
test_parameters()
|
||||
|
||||
print(param1.call())
|
||||
print(param2.call())
|
||||
|
||||
# Only set anon2 here, anon1 has already been set.
|
||||
sink(func(): return "Anonymous 1 Hotswap 1")
|
||||
sink(func(): return "Anonymous 2 Hotswap 1")
|
||||
|
||||
print(anon1.call())
|
||||
print(anon2.call())
|
||||
|
||||
func test_parameters(_v1 = func(): return "Param 1 Hotswap 1", _v2 = func(): return "Param 2 Hotswap 1"):
|
||||
# Only set param2 here, param1 has already been set.
|
||||
param2 = _v2
|
||||
|
||||
func sink(_v):
|
||||
pass
|
|
@ -0,0 +1,9 @@
|
|||
GDTEST_OK
|
||||
Local 1 Hotswap 1
|
||||
Local 2 Hotswap 1
|
||||
Member 1 Hotswap 1
|
||||
<null>
|
||||
Param 1 Hotswap 1
|
||||
Param 2 Hotswap 1
|
||||
Anonymous 1 Hotswap 1
|
||||
Anonymous 2 Hotswap 1
|
48
modules/gdscript/tests/scripts/hotswap/hotswap_lambda.swap2
Normal file
48
modules/gdscript/tests/scripts/hotswap/hotswap_lambda.swap2
Normal file
|
@ -0,0 +1,48 @@
|
|||
var member2 := func():
|
||||
return "Member 2 Hotswap 2"
|
||||
|
||||
var member1 := func():
|
||||
return "Member 1 Hotswap 2"
|
||||
|
||||
var local1: Callable
|
||||
var local2: Callable
|
||||
|
||||
var param1: Callable
|
||||
var param2: Callable
|
||||
|
||||
var anon1: Callable
|
||||
var anon2: Callable
|
||||
|
||||
func test():
|
||||
var _v2 = func(): return "Local 2 Hotswap 2"
|
||||
var _v1 = func(): return "Local 1 Hotswap 2"
|
||||
# local1 and local2 have already been set.
|
||||
|
||||
print(local1.call())
|
||||
print(local2.call())
|
||||
|
||||
print(member1.call())
|
||||
if member2:
|
||||
print(member2.call())
|
||||
else:
|
||||
print(member2)
|
||||
|
||||
test_parameters()
|
||||
|
||||
print(param1.call())
|
||||
print(param2.call())
|
||||
|
||||
# Only set anon2 here, anon1 has already been set.
|
||||
# No way for compiler to tell that theyve been swapped, so anon1 and anon2's output will be swapped.
|
||||
sink(func(): return "Anonymous 2 Hotswap 2")
|
||||
sink(func(): return "Anonymous 1 Hotswap 2")
|
||||
|
||||
print(anon1.call())
|
||||
print(anon2.call())
|
||||
|
||||
func test_parameters(_v2 = func(): return "Param 2 Hotswap 2", _v1 = func(): return "Param 1 Hotswap 2"):
|
||||
# param1 and param2 have already been set.
|
||||
pass
|
||||
|
||||
func sink(_v):
|
||||
pass
|
|
@ -0,0 +1,9 @@
|
|||
GDTEST_OK
|
||||
Local 1 Hotswap 2
|
||||
Local 2 Hotswap 2
|
||||
Member 1 Hotswap 2
|
||||
<null>
|
||||
Param 1 Hotswap 2
|
||||
Param 2 Hotswap 2
|
||||
Anonymous 2 Hotswap 2
|
||||
Anonymous 1 Hotswap 2
|
|
@ -10,7 +10,7 @@ func test():
|
|||
foo()
|
||||
|
||||
print(lambda_self.get_method()) # Should print "test".
|
||||
print(anon_lambda_self.get_method()) # Should print "<anonymous lambda>".
|
||||
print(anon_lambda_self.get_method()) # Should print "<anonymous lambda(anon_lambda_self)>".
|
||||
|
||||
var lambda_non_self := func test() -> void:
|
||||
pass
|
||||
|
@ -18,4 +18,4 @@ func test():
|
|||
pass
|
||||
|
||||
print(lambda_non_self.get_method()) # Should print "test".
|
||||
print(anon_lambda_non_self.get_method()) # Should print "<anonymous lambda>".
|
||||
print(anon_lambda_non_self.get_method()) # Should print "<anonymous lambda(anon_lambda_non_self)>".
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
GDTEST_OK
|
||||
test
|
||||
<anonymous lambda>
|
||||
<anonymous lambda(anon_lambda_self)>
|
||||
test
|
||||
<anonymous lambda>
|
||||
<anonymous lambda(anon_lambda_non_self)>
|
||||
|
|
Loading…
Reference in a new issue