Sfoglia il codice sorgente

GDScript: Fix object iterator opcodes

Danil Alexeev 1 anno fa
parent
commit
2778069025

+ 18 - 10
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);

+ 49 - 0
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)

+ 30 - 0
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 [<null>]
+_iter_get 0
+0
+_iter_next [0]
+_iter_get 1
+1
+_iter_next [1]
+_iter_get 2
+2
+_iter_next [2]
+===
+_iter_init [<null>]
+_iter_get 0
+0
+_iter_next [0]
+_iter_get 1
+1
+_iter_next [1]
+_iter_get 2
+2
+_iter_next [2]