diff options
author | George Marques <george@gmarqu.es> | 2022-06-27 12:09:51 -0300 |
---|---|---|
committer | George Marques <george@gmarqu.es> | 2022-06-27 12:09:51 -0300 |
commit | 511a4b761c3b5bf565f6e580fc9774a99e72a53e (patch) | |
tree | 83506363302f698270b87d75edd7e5b9b91a4730 /modules | |
parent | 307dfa9fe960c93b3f4c2f78d9c046c1ffff6a93 (diff) |
GDScript: Fix setter being called in chains for shared types
When a type is shared (i.e. passed by reference) it doesn't need to be
called in a setter chain (e.g. `a.b.c = 0`) since it will be updated in
place.
This commit adds an instruction that jumps when the value is shared so
it can be used to skip those cases and avoid redundant calls of setters.
It also solves issues when assigning to sub-properties of read-only
properties.
Diffstat (limited to 'modules')
-rw-r--r-- | modules/gdscript/gdscript_byte_codegen.cpp | 12 | ||||
-rw-r--r-- | modules/gdscript/gdscript_byte_codegen.h | 2 | ||||
-rw-r--r-- | modules/gdscript/gdscript_codegen.h | 2 | ||||
-rw-r--r-- | modules/gdscript/gdscript_compiler.cpp | 66 | ||||
-rw-r--r-- | modules/gdscript/gdscript_disassembler.cpp | 8 | ||||
-rw-r--r-- | modules/gdscript/gdscript_function.h | 1 | ||||
-rw-r--r-- | modules/gdscript/gdscript_vm.cpp | 16 |
7 files changed, 88 insertions, 19 deletions
diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp index 3d5a39bf38..6a1effd680 100644 --- a/modules/gdscript/gdscript_byte_codegen.cpp +++ b/modules/gdscript/gdscript_byte_codegen.cpp @@ -1336,6 +1336,18 @@ void GDScriptByteCodeGenerator::write_endif() { if_jmp_addrs.pop_back(); } +void GDScriptByteCodeGenerator::write_jump_if_shared(const Address &p_value) { + append(GDScriptFunction::OPCODE_JUMP_IF_SHARED, 1); + append(p_value); + if_jmp_addrs.push_back(opcodes.size()); + append(0); // Jump destination, will be patched. +} + +void GDScriptByteCodeGenerator::write_end_jump_if_shared() { + patch_jump(if_jmp_addrs.back()->get()); + if_jmp_addrs.pop_back(); +} + void GDScriptByteCodeGenerator::start_for(const GDScriptDataType &p_iterator_type, const GDScriptDataType &p_list_type) { Address counter(Address::LOCAL_VARIABLE, add_local("@counter_pos", p_iterator_type), p_iterator_type); Address container(Address::LOCAL_VARIABLE, add_local("@container_pos", p_list_type), p_list_type); diff --git a/modules/gdscript/gdscript_byte_codegen.h b/modules/gdscript/gdscript_byte_codegen.h index 6ee8fda533..f4b402fc96 100644 --- a/modules/gdscript/gdscript_byte_codegen.h +++ b/modules/gdscript/gdscript_byte_codegen.h @@ -479,6 +479,8 @@ public: virtual void write_if(const Address &p_condition) override; virtual void write_else() override; virtual void write_endif() override; + virtual void write_jump_if_shared(const Address &p_value) override; + virtual void write_end_jump_if_shared() override; virtual void start_for(const GDScriptDataType &p_iterator_type, const GDScriptDataType &p_list_type) override; virtual void write_for_assignment(const Address &p_variable, const Address &p_list) override; virtual void write_for() override; diff --git a/modules/gdscript/gdscript_codegen.h b/modules/gdscript/gdscript_codegen.h index 326b66a295..81fa265aca 100644 --- a/modules/gdscript/gdscript_codegen.h +++ b/modules/gdscript/gdscript_codegen.h @@ -140,6 +140,8 @@ public: virtual void write_if(const Address &p_condition) = 0; virtual void write_else() = 0; virtual void write_endif() = 0; + virtual void write_jump_if_shared(const Address &p_value) = 0; + virtual void write_end_jump_if_shared() = 0; virtual void start_for(const GDScriptDataType &p_iterator_type, const GDScriptDataType &p_list_type) = 0; virtual void write_for_assignment(const Address &p_variable, const Address &p_list) = 0; virtual void write_for() = 0; diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 25454030b1..72992c3216 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -1056,13 +1056,25 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code // Set back the values into their bases. for (const ChainInfo &info : set_chain) { - if (!info.is_named) { - gen->write_set(info.base, info.key, assigned); - if (info.key.mode == GDScriptCodeGenerator::Address::TEMPORARY) { - gen->pop_temporary(); + bool known_type = assigned.type.has_type; + bool is_shared = Variant::is_type_shared(assigned.type.builtin_type); + + if (!known_type) { + // Jump shared values since they are already updated in-place. + gen->write_jump_if_shared(assigned); + } + if (known_type && !is_shared) { + if (!info.is_named) { + gen->write_set(info.base, info.key, assigned); + if (info.key.mode == GDScriptCodeGenerator::Address::TEMPORARY) { + gen->pop_temporary(); + } + } else { + gen->write_set_named(info.base, info.name, assigned); } - } else { - gen->write_set_named(info.base, info.name, assigned); + } + if (!known_type) { + gen->write_end_jump_if_shared(); } if (assigned.mode == GDScriptCodeGenerator::Address::TEMPORARY) { gen->pop_temporary(); @@ -1070,19 +1082,35 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code assigned = info.base; } - // If this is a class member property, also assign to it. - // This allow things like: position.x += 2.0 - if (assign_class_member_property != StringName()) { - gen->write_set_member(assigned, assign_class_member_property); - } - // Same as above but for members - if (is_member_property) { - if (member_property_has_setter && !member_property_is_in_setter) { - Vector<GDScriptCodeGenerator::Address> args; - args.push_back(assigned); - gen->write_call(GDScriptCodeGenerator::Address(), GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::SELF), member_property_setter_function, args); - } else { - gen->write_assign(target_member_property, assigned); + bool known_type = assigned.type.has_type; + bool is_shared = Variant::is_type_shared(assigned.type.builtin_type); + + if (!known_type || !is_shared) { + // If this is a class member property, also assign to it. + // This allow things like: position.x += 2.0 + if (assign_class_member_property != StringName()) { + if (!known_type) { + gen->write_jump_if_shared(assigned); + } + gen->write_set_member(assigned, assign_class_member_property); + if (!known_type) { + gen->write_end_jump_if_shared(); + } + } else if (is_member_property) { + // Same as above but for script members. + if (!known_type) { + gen->write_jump_if_shared(assigned); + } + if (member_property_has_setter && !member_property_is_in_setter) { + Vector<GDScriptCodeGenerator::Address> args; + args.push_back(assigned); + gen->write_call(GDScriptCodeGenerator::Address(), GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::SELF), member_property_setter_function, args); + } else { + gen->write_assign(target_member_property, assigned); + } + if (!known_type) { + gen->write_end_jump_if_shared(); + } } } diff --git a/modules/gdscript/gdscript_disassembler.cpp b/modules/gdscript/gdscript_disassembler.cpp index dc114f2eff..726f0efe2b 100644 --- a/modules/gdscript/gdscript_disassembler.cpp +++ b/modules/gdscript/gdscript_disassembler.cpp @@ -838,6 +838,14 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const { incr = 1; } break; + case OPCODE_JUMP_IF_SHARED: { + text += "jump-if-shared "; + text += DADDR(1); + text += " to "; + text += itos(_code_ptr[ip + 2]); + + incr = 3; + } break; case OPCODE_RETURN: { text += "return "; text += DADDR(1); diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h index d2ca795977..3f1265679b 100644 --- a/modules/gdscript/gdscript_function.h +++ b/modules/gdscript/gdscript_function.h @@ -304,6 +304,7 @@ public: OPCODE_JUMP_IF, OPCODE_JUMP_IF_NOT, OPCODE_JUMP_TO_DEF_ARGUMENT, + OPCODE_JUMP_IF_SHARED, OPCODE_RETURN, OPCODE_RETURN_TYPED_BUILTIN, OPCODE_RETURN_TYPED_ARRAY, diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp index 20b8d29ec3..988a98a591 100644 --- a/modules/gdscript/gdscript_vm.cpp +++ b/modules/gdscript/gdscript_vm.cpp @@ -311,6 +311,7 @@ void (*type_init_function_table[])(Variant *) = { &&OPCODE_JUMP_IF, \ &&OPCODE_JUMP_IF_NOT, \ &&OPCODE_JUMP_TO_DEF_ARGUMENT, \ + &&OPCODE_JUMP_IF_SHARED, \ &&OPCODE_RETURN, \ &&OPCODE_RETURN_TYPED_BUILTIN, \ &&OPCODE_RETURN_TYPED_ARRAY, \ @@ -2361,6 +2362,21 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } DISPATCH_OPCODE; + OPCODE(OPCODE_JUMP_IF_SHARED) { + CHECK_SPACE(3); + + GET_INSTRUCTION_ARG(val, 0); + + if (val->is_shared()) { + int to = _code_ptr[ip + 2]; + GD_ERR_BREAK(to < 0 || to > _code_size); + ip = to; + } else { + ip += 3; + } + } + DISPATCH_OPCODE; + OPCODE(OPCODE_RETURN) { CHECK_SPACE(2); GET_INSTRUCTION_ARG(r, 0); |