From 27780690255dae225a809392ea9ce0d8c37908e5 Mon Sep 17 00:00:00 2001 From: Danil Alexeev Date: Mon, 18 Mar 2024 14:51:46 +0300 Subject: [PATCH] GDScript: Fix object iterator opcodes --- modules/gdscript/gdscript_vm.cpp | 28 +++++++---- .../runtime/features/object_iterators.gd | 49 +++++++++++++++++++ .../runtime/features/object_iterators.out | 30 ++++++++++++ 3 files changed, 97 insertions(+), 10 deletions(-) create mode 100644 modules/gdscript/tests/scripts/runtime/features/object_iterators.gd create mode 100644 modules/gdscript/tests/scripts/runtime/features/object_iterators.out diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp index 3cb011b2514f..842975698b8d 100644 --- a/modules/gdscript/gdscript_vm.cpp +++ b/modules/gdscript/gdscript_vm.cpp @@ -2660,6 +2660,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GET_VARIANT_PTR(counter, 0); GET_VARIANT_PTR(container, 1); + *counter = Variant(); + bool valid; if (!container->iter_init(*counter, valid)) { #ifdef DEBUG_ENABLED @@ -2987,20 +2989,22 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #else Object *obj = *VariantInternal::get_object(container); #endif + + *counter = Variant(); + Array ref; ref.push_back(*counter); Variant vref; VariantInternal::initialize(&vref, Variant::ARRAY); *VariantInternal::get_array(&vref) = ref; - Variant **args = instruction_args; // Overriding an instruction argument, but we don't need access to that anymore. - args[0] = &vref; + const Variant *args[] = { &vref }; Callable::CallError ce; - Variant has_next = obj->callp(CoreStringNames::get_singleton()->_iter_init, (const Variant **)args, 1, ce); + Variant has_next = obj->callp(CoreStringNames::get_singleton()->_iter_init, args, 1, ce); #ifdef DEBUG_ENABLED - if (ce.error != Callable::CallError::CALL_OK) { + if (ref.size() != 1 || ce.error != Callable::CallError::CALL_OK) { err_text = vformat(R"(There was an error calling "_iter_next" on iterator object of type %s.)", *container); OPCODE_BREAK; } @@ -3010,8 +3014,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size); ip = jumpto; } else { + *counter = ref[0]; + GET_VARIANT_PTR(iterator, 2); - *iterator = obj->callp(CoreStringNames::get_singleton()->_iter_get, (const Variant **)args, 1, ce); + *iterator = obj->callp(CoreStringNames::get_singleton()->_iter_get, (const Variant **)&counter, 1, ce); #ifdef DEBUG_ENABLED if (ce.error != Callable::CallError::CALL_OK) { err_text = vformat(R"(There was an error calling "_iter_get" on iterator object of type %s.)", *container); @@ -3318,20 +3324,20 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #else Object *obj = *VariantInternal::get_object(container); #endif + Array ref; ref.push_back(*counter); Variant vref; VariantInternal::initialize(&vref, Variant::ARRAY); *VariantInternal::get_array(&vref) = ref; - Variant **args = instruction_args; // Overriding an instruction argument, but we don't need access to that anymore. - args[0] = &vref; + const Variant *args[] = { &vref }; Callable::CallError ce; - Variant has_next = obj->callp(CoreStringNames::get_singleton()->_iter_next, (const Variant **)args, 1, ce); + Variant has_next = obj->callp(CoreStringNames::get_singleton()->_iter_next, args, 1, ce); #ifdef DEBUG_ENABLED - if (ce.error != Callable::CallError::CALL_OK) { + if (ref.size() != 1 || ce.error != Callable::CallError::CALL_OK) { err_text = vformat(R"(There was an error calling "_iter_next" on iterator object of type %s.)", *container); OPCODE_BREAK; } @@ -3341,8 +3347,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size); ip = jumpto; } else { + *counter = ref[0]; + GET_VARIANT_PTR(iterator, 2); - *iterator = obj->callp(CoreStringNames::get_singleton()->_iter_get, (const Variant **)args, 1, ce); + *iterator = obj->callp(CoreStringNames::get_singleton()->_iter_get, (const Variant **)&counter, 1, ce); #ifdef DEBUG_ENABLED if (ce.error != Callable::CallError::CALL_OK) { err_text = vformat(R"(There was an error calling "_iter_get" on iterator object of type %s.)", *container); diff --git a/modules/gdscript/tests/scripts/runtime/features/object_iterators.gd b/modules/gdscript/tests/scripts/runtime/features/object_iterators.gd new file mode 100644 index 000000000000..6fe28c6f78e8 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/object_iterators.gd @@ -0,0 +1,49 @@ +class MyIterator: + var count: int + + func _init(p_count: int) -> void: + count = p_count + + func _iter_init(arg: Array) -> bool: + prints("_iter_init", arg) + arg[0] = 0 + return arg[0] < count + + func _iter_next(arg: Array) -> bool: + prints("_iter_next", arg) + arg[0] += 1 + return arg[0] < count + + func _iter_get(arg: Variant) -> Variant: + prints("_iter_get", arg) + return arg + +func test(): + var container := PackedDataContainer.new() + var _err := container.pack([{ + id = 123, + node_path = ^"/some/path", + data = PackedByteArray(), + }]) + + for ref: PackedDataContainerRef in container: + for key: String in ref: + print(key) + + print("===") + + for ref: Variant in container: + for key: String in ref: + print(key) + + print("===") + + var hard_custom := MyIterator.new(3) + for x in hard_custom: + print(x) + + print("===") + + var weak_custom: Variant = MyIterator.new(3) + for x in weak_custom: + print(x) diff --git a/modules/gdscript/tests/scripts/runtime/features/object_iterators.out b/modules/gdscript/tests/scripts/runtime/features/object_iterators.out new file mode 100644 index 000000000000..942a2c9dd824 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/object_iterators.out @@ -0,0 +1,30 @@ +GDTEST_OK +id +node_path +data +=== +id +node_path +data +=== +_iter_init [] +_iter_get 0 +0 +_iter_next [0] +_iter_get 1 +1 +_iter_next [1] +_iter_get 2 +2 +_iter_next [2] +=== +_iter_init [] +_iter_get 0 +0 +_iter_next [0] +_iter_get 1 +1 +_iter_next [1] +_iter_get 2 +2 +_iter_next [2]