Merge pull request #62462 from vnen/gdscript-setter-chaining

GDScript: Fix setter being called in chains for shared types
This commit is contained in:
Rémi Verschelde 2022-06-27 23:25:53 +02:00 committed by GitHub
commit c4a426d6ec
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 104 additions and 23 deletions

View file

@ -3327,13 +3327,20 @@ Vector<Variant> varray(const Variant &p_arg1, const Variant &p_arg2, const Varia
void Variant::static_assign(const Variant &p_variant) {
}
bool Variant::is_shared() const {
switch (type) {
bool Variant::is_type_shared(Variant::Type p_type) {
switch (p_type) {
case OBJECT:
return true;
case ARRAY:
return true;
case DICTIONARY:
case PACKED_BYTE_ARRAY:
case PACKED_INT32_ARRAY:
case PACKED_INT64_ARRAY:
case PACKED_FLOAT32_ARRAY:
case PACKED_FLOAT64_ARRAY:
case PACKED_STRING_ARRAY:
case PACKED_VECTOR2_ARRAY:
case PACKED_VECTOR3_ARRAY:
case PACKED_COLOR_ARRAY:
return true;
default: {
}
@ -3342,6 +3349,10 @@ bool Variant::is_shared() const {
return false;
}
bool Variant::is_shared() const {
return is_type_shared(type);
}
void Variant::_variant_call_error(const String &p_method, Callable::CallError &error) {
switch (error.error) {
case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: {

View file

@ -297,6 +297,7 @@ public:
static String get_type_name(Variant::Type p_type);
static bool can_convert(Type p_type_from, Type p_type_to);
static bool can_convert_strict(Type p_type_from, Type p_type_to);
static bool is_type_shared(Variant::Type p_type);
bool is_ref_counted() const;
_FORCE_INLINE_ bool is_num() const {

View file

@ -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);

View file

@ -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;

View file

@ -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;

View file

@ -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();
}
}
}

View file

@ -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);

View file

@ -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,

View file

@ -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);