Quellcode durchsuchen

GDScript: Add typed iterate instructions

George Marques vor 4 Jahren
Ursprung
Commit
e0dca3c6b6

+ 89 - 2
modules/gdscript/gdscript_byte_codegen.cpp

@@ -864,8 +864,95 @@ void GDScriptByteCodeGenerator::write_for(const Address &p_variable, const Addre
 	append(container_pos);
 	append(p_list);
 
+	GDScriptFunction::Opcode begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN;
+	GDScriptFunction::Opcode iterate_opcode = GDScriptFunction::OPCODE_ITERATE;
+
+	if (p_list.type.has_type) {
+		if (p_list.type.kind == GDScriptDataType::BUILTIN) {
+			switch (p_list.type.builtin_type) {
+				case Variant::INT:
+					begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_INT;
+					iterate_opcode = GDScriptFunction::OPCODE_ITERATE_INT;
+					break;
+				case Variant::FLOAT:
+					begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_FLOAT;
+					iterate_opcode = GDScriptFunction::OPCODE_ITERATE_FLOAT;
+					break;
+				case Variant::VECTOR2:
+					begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_VECTOR2;
+					iterate_opcode = GDScriptFunction::OPCODE_ITERATE_VECTOR2;
+					break;
+				case Variant::VECTOR2I:
+					begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_VECTOR2I;
+					iterate_opcode = GDScriptFunction::OPCODE_ITERATE_VECTOR2I;
+					break;
+				case Variant::VECTOR3:
+					begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_VECTOR3;
+					iterate_opcode = GDScriptFunction::OPCODE_ITERATE_VECTOR3;
+					break;
+				case Variant::VECTOR3I:
+					begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_VECTOR3I;
+					iterate_opcode = GDScriptFunction::OPCODE_ITERATE_VECTOR3I;
+					break;
+				case Variant::STRING:
+					begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_STRING;
+					iterate_opcode = GDScriptFunction::OPCODE_ITERATE_STRING;
+					break;
+				case Variant::DICTIONARY:
+					begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_DICTIONARY;
+					iterate_opcode = GDScriptFunction::OPCODE_ITERATE_DICTIONARY;
+					break;
+				case Variant::ARRAY:
+					begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_ARRAY;
+					iterate_opcode = GDScriptFunction::OPCODE_ITERATE_ARRAY;
+					break;
+				case Variant::PACKED_BYTE_ARRAY:
+					begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_PACKED_BYTE_ARRAY;
+					iterate_opcode = GDScriptFunction::OPCODE_ITERATE_PACKED_BYTE_ARRAY;
+					break;
+				case Variant::PACKED_INT32_ARRAY:
+					begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_PACKED_INT32_ARRAY;
+					iterate_opcode = GDScriptFunction::OPCODE_ITERATE_PACKED_INT32_ARRAY;
+					break;
+				case Variant::PACKED_INT64_ARRAY:
+					begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_PACKED_INT64_ARRAY;
+					iterate_opcode = GDScriptFunction::OPCODE_ITERATE_PACKED_INT64_ARRAY;
+					break;
+				case Variant::PACKED_FLOAT32_ARRAY:
+					begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_PACKED_FLOAT32_ARRAY;
+					iterate_opcode = GDScriptFunction::OPCODE_ITERATE_PACKED_FLOAT32_ARRAY;
+					break;
+				case Variant::PACKED_FLOAT64_ARRAY:
+					begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_PACKED_FLOAT64_ARRAY;
+					iterate_opcode = GDScriptFunction::OPCODE_ITERATE_PACKED_FLOAT64_ARRAY;
+					break;
+				case Variant::PACKED_STRING_ARRAY:
+					begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_PACKED_STRING_ARRAY;
+					iterate_opcode = GDScriptFunction::OPCODE_ITERATE_PACKED_STRING_ARRAY;
+					break;
+				case Variant::PACKED_VECTOR2_ARRAY:
+					begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_PACKED_VECTOR2_ARRAY;
+					iterate_opcode = GDScriptFunction::OPCODE_ITERATE_PACKED_VECTOR2_ARRAY;
+					break;
+				case Variant::PACKED_VECTOR3_ARRAY:
+					begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_PACKED_VECTOR3_ARRAY;
+					iterate_opcode = GDScriptFunction::OPCODE_ITERATE_PACKED_VECTOR3_ARRAY;
+					break;
+				case Variant::PACKED_COLOR_ARRAY:
+					begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_PACKED_COLOR_ARRAY;
+					iterate_opcode = GDScriptFunction::OPCODE_ITERATE_PACKED_COLOR_ARRAY;
+					break;
+				default:
+					break;
+			}
+		} else {
+			begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_OBJECT;
+			iterate_opcode = GDScriptFunction::OPCODE_ITERATE_OBJECT;
+		}
+	}
+
 	// Begin loop.
-	append(GDScriptFunction::OPCODE_ITERATE_BEGIN, 3);
+	append(begin_opcode, 3);
 	append(counter_pos);
 	append(container_pos);
 	append(p_variable);
@@ -877,7 +964,7 @@ void GDScriptByteCodeGenerator::write_for(const Address &p_variable, const Addre
 	// Next iteration.
 	int continue_addr = opcodes.size();
 	continue_addrs.push_back(continue_addr);
-	append(GDScriptFunction::OPCODE_ITERATE, 3);
+	append(iterate_opcode, 3);
 	append(counter_pos);
 	append(container_pos);
 	append(p_variable);

+ 54 - 0
modules/gdscript/gdscript_disassembler.cpp

@@ -674,6 +674,58 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
 
 				incr = 2;
 			} break;
+
+#define DISASSEMBLE_ITERATE(m_type)      \
+	case OPCODE_ITERATE_##m_type: {      \
+		text += "for-loop (typed ";      \
+		text += #m_type;                 \
+		text += ") ";                    \
+		text += DADDR(3);                \
+		text += " in ";                  \
+		text += DADDR(2);                \
+		text += " counter ";             \
+		text += DADDR(1);                \
+		text += " end ";                 \
+		text += itos(_code_ptr[ip + 4]); \
+		incr += 5;                       \
+	} break
+
+#define DISASSEMBLE_ITERATE_BEGIN(m_type) \
+	case OPCODE_ITERATE_BEGIN_##m_type: { \
+		text += "for-init (typed ";       \
+		text += #m_type;                  \
+		text += ") ";                     \
+		text += DADDR(3);                 \
+		text += " in ";                   \
+		text += DADDR(2);                 \
+		text += " counter ";              \
+		text += DADDR(1);                 \
+		text += " end ";                  \
+		text += itos(_code_ptr[ip + 4]);  \
+		incr += 5;                        \
+	} break
+
+#define DISASSEMBLE_ITERATE_TYPES(m_macro) \
+	m_macro(INT);                          \
+	m_macro(FLOAT);                        \
+	m_macro(VECTOR2);                      \
+	m_macro(VECTOR2I);                     \
+	m_macro(VECTOR3);                      \
+	m_macro(VECTOR3I);                     \
+	m_macro(STRING);                       \
+	m_macro(DICTIONARY);                   \
+	m_macro(ARRAY);                        \
+	m_macro(PACKED_BYTE_ARRAY);            \
+	m_macro(PACKED_INT32_ARRAY);           \
+	m_macro(PACKED_INT64_ARRAY);           \
+	m_macro(PACKED_FLOAT32_ARRAY);         \
+	m_macro(PACKED_FLOAT64_ARRAY);         \
+	m_macro(PACKED_STRING_ARRAY);          \
+	m_macro(PACKED_VECTOR2_ARRAY);         \
+	m_macro(PACKED_VECTOR3_ARRAY);         \
+	m_macro(PACKED_COLOR_ARRAY);           \
+	m_macro(OBJECT)
+
 			case OPCODE_ITERATE_BEGIN: {
 				text += "for-init ";
 				text += DADDR(3);
@@ -686,6 +738,7 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
 
 				incr += 5;
 			} break;
+				DISASSEMBLE_ITERATE_TYPES(DISASSEMBLE_ITERATE_BEGIN);
 			case OPCODE_ITERATE: {
 				text += "for-loop ";
 				text += DADDR(2);
@@ -698,6 +751,7 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
 
 				incr += 5;
 			} break;
+				DISASSEMBLE_ITERATE_TYPES(DISASSEMBLE_ITERATE);
 			case OPCODE_LINE: {
 				int line = _code_ptr[ip + 1] - 1;
 				if (line >= 0 && line < p_code_lines.size()) {

+ 38 - 0
modules/gdscript/gdscript_function.h

@@ -238,7 +238,45 @@ public:
 		OPCODE_JUMP_TO_DEF_ARGUMENT,
 		OPCODE_RETURN,
 		OPCODE_ITERATE_BEGIN,
+		OPCODE_ITERATE_BEGIN_INT,
+		OPCODE_ITERATE_BEGIN_FLOAT,
+		OPCODE_ITERATE_BEGIN_VECTOR2,
+		OPCODE_ITERATE_BEGIN_VECTOR2I,
+		OPCODE_ITERATE_BEGIN_VECTOR3,
+		OPCODE_ITERATE_BEGIN_VECTOR3I,
+		OPCODE_ITERATE_BEGIN_STRING,
+		OPCODE_ITERATE_BEGIN_DICTIONARY,
+		OPCODE_ITERATE_BEGIN_ARRAY,
+		OPCODE_ITERATE_BEGIN_PACKED_BYTE_ARRAY,
+		OPCODE_ITERATE_BEGIN_PACKED_INT32_ARRAY,
+		OPCODE_ITERATE_BEGIN_PACKED_INT64_ARRAY,
+		OPCODE_ITERATE_BEGIN_PACKED_FLOAT32_ARRAY,
+		OPCODE_ITERATE_BEGIN_PACKED_FLOAT64_ARRAY,
+		OPCODE_ITERATE_BEGIN_PACKED_STRING_ARRAY,
+		OPCODE_ITERATE_BEGIN_PACKED_VECTOR2_ARRAY,
+		OPCODE_ITERATE_BEGIN_PACKED_VECTOR3_ARRAY,
+		OPCODE_ITERATE_BEGIN_PACKED_COLOR_ARRAY,
+		OPCODE_ITERATE_BEGIN_OBJECT,
 		OPCODE_ITERATE,
+		OPCODE_ITERATE_INT,
+		OPCODE_ITERATE_FLOAT,
+		OPCODE_ITERATE_VECTOR2,
+		OPCODE_ITERATE_VECTOR2I,
+		OPCODE_ITERATE_VECTOR3,
+		OPCODE_ITERATE_VECTOR3I,
+		OPCODE_ITERATE_STRING,
+		OPCODE_ITERATE_DICTIONARY,
+		OPCODE_ITERATE_ARRAY,
+		OPCODE_ITERATE_PACKED_BYTE_ARRAY,
+		OPCODE_ITERATE_PACKED_INT32_ARRAY,
+		OPCODE_ITERATE_PACKED_INT64_ARRAY,
+		OPCODE_ITERATE_PACKED_FLOAT32_ARRAY,
+		OPCODE_ITERATE_PACKED_FLOAT64_ARRAY,
+		OPCODE_ITERATE_PACKED_STRING_ARRAY,
+		OPCODE_ITERATE_PACKED_VECTOR2_ARRAY,
+		OPCODE_ITERATE_PACKED_VECTOR3_ARRAY,
+		OPCODE_ITERATE_PACKED_COLOR_ARRAY,
+		OPCODE_ITERATE_OBJECT,
 		OPCODE_ASSERT,
 		OPCODE_BREAKPOINT,
 		OPCODE_LINE,

+ 763 - 87
modules/gdscript/gdscript_vm.cpp

@@ -30,6 +30,7 @@
 
 #include "gdscript_function.h"
 
+#include "core/core_string_names.h"
 #include "core/os/os.h"
 #include "gdscript.h"
 #include "gdscript_functions.h"
@@ -185,93 +186,131 @@ String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const
 }
 
 #if defined(__GNUC__)
-#define OPCODES_TABLE                               \
-	static const void *switch_table_ops[] = {       \
-		&&OPCODE_OPERATOR,                          \
-		&&OPCODE_OPERATOR_VALIDATED,                \
-		&&OPCODE_EXTENDS_TEST,                      \
-		&&OPCODE_IS_BUILTIN,                        \
-		&&OPCODE_SET_KEYED,                         \
-		&&OPCODE_SET_KEYED_VALIDATED,               \
-		&&OPCODE_SET_INDEXED_VALIDATED,             \
-		&&OPCODE_GET_KEYED,                         \
-		&&OPCODE_GET_KEYED_VALIDATED,               \
-		&&OPCODE_GET_INDEXED_VALIDATED,             \
-		&&OPCODE_SET_NAMED,                         \
-		&&OPCODE_SET_NAMED_VALIDATED,               \
-		&&OPCODE_GET_NAMED,                         \
-		&&OPCODE_GET_NAMED_VALIDATED,               \
-		&&OPCODE_SET_MEMBER,                        \
-		&&OPCODE_GET_MEMBER,                        \
-		&&OPCODE_ASSIGN,                            \
-		&&OPCODE_ASSIGN_TRUE,                       \
-		&&OPCODE_ASSIGN_FALSE,                      \
-		&&OPCODE_ASSIGN_TYPED_BUILTIN,              \
-		&&OPCODE_ASSIGN_TYPED_NATIVE,               \
-		&&OPCODE_ASSIGN_TYPED_SCRIPT,               \
-		&&OPCODE_CAST_TO_BUILTIN,                   \
-		&&OPCODE_CAST_TO_NATIVE,                    \
-		&&OPCODE_CAST_TO_SCRIPT,                    \
-		&&OPCODE_CONSTRUCT,                         \
-		&&OPCODE_CONSTRUCT_ARRAY,                   \
-		&&OPCODE_CONSTRUCT_DICTIONARY,              \
-		&&OPCODE_CALL,                              \
-		&&OPCODE_CALL_RETURN,                       \
-		&&OPCODE_CALL_ASYNC,                        \
-		&&OPCODE_CALL_BUILT_IN,                     \
-		&&OPCODE_CALL_BUILTIN_TYPE_VALIDATED,       \
-		&&OPCODE_CALL_SELF_BASE,                    \
-		&&OPCODE_CALL_METHOD_BIND,                  \
-		&&OPCODE_CALL_METHOD_BIND_RET,              \
-		&&OPCODE_CALL_PTRCALL_NO_RETURN,            \
-		&&OPCODE_CALL_PTRCALL_BOOL,                 \
-		&&OPCODE_CALL_PTRCALL_INT,                  \
-		&&OPCODE_CALL_PTRCALL_FLOAT,                \
-		&&OPCODE_CALL_PTRCALL_STRING,               \
-		&&OPCODE_CALL_PTRCALL_VECTOR2,              \
-		&&OPCODE_CALL_PTRCALL_VECTOR2I,             \
-		&&OPCODE_CALL_PTRCALL_RECT2,                \
-		&&OPCODE_CALL_PTRCALL_RECT2I,               \
-		&&OPCODE_CALL_PTRCALL_VECTOR3,              \
-		&&OPCODE_CALL_PTRCALL_VECTOR3I,             \
-		&&OPCODE_CALL_PTRCALL_TRANSFORM2D,          \
-		&&OPCODE_CALL_PTRCALL_PLANE,                \
-		&&OPCODE_CALL_PTRCALL_QUAT,                 \
-		&&OPCODE_CALL_PTRCALL_AABB,                 \
-		&&OPCODE_CALL_PTRCALL_BASIS,                \
-		&&OPCODE_CALL_PTRCALL_TRANSFORM,            \
-		&&OPCODE_CALL_PTRCALL_COLOR,                \
-		&&OPCODE_CALL_PTRCALL_STRING_NAME,          \
-		&&OPCODE_CALL_PTRCALL_NODE_PATH,            \
-		&&OPCODE_CALL_PTRCALL_RID,                  \
-		&&OPCODE_CALL_PTRCALL_OBJECT,               \
-		&&OPCODE_CALL_PTRCALL_CALLABLE,             \
-		&&OPCODE_CALL_PTRCALL_SIGNAL,               \
-		&&OPCODE_CALL_PTRCALL_DICTIONARY,           \
-		&&OPCODE_CALL_PTRCALL_ARRAY,                \
-		&&OPCODE_CALL_PTRCALL_PACKED_BYTE_ARRAY,    \
-		&&OPCODE_CALL_PTRCALL_PACKED_INT32_ARRAY,   \
-		&&OPCODE_CALL_PTRCALL_PACKED_INT64_ARRAY,   \
-		&&OPCODE_CALL_PTRCALL_PACKED_FLOAT32_ARRAY, \
-		&&OPCODE_CALL_PTRCALL_PACKED_FLOAT64_ARRAY, \
-		&&OPCODE_CALL_PTRCALL_PACKED_STRING_ARRAY,  \
-		&&OPCODE_CALL_PTRCALL_PACKED_VECTOR2_ARRAY, \
-		&&OPCODE_CALL_PTRCALL_PACKED_VECTOR3_ARRAY, \
-		&&OPCODE_CALL_PTRCALL_PACKED_COLOR_ARRAY,   \
-		&&OPCODE_AWAIT,                             \
-		&&OPCODE_AWAIT_RESUME,                      \
-		&&OPCODE_JUMP,                              \
-		&&OPCODE_JUMP_IF,                           \
-		&&OPCODE_JUMP_IF_NOT,                       \
-		&&OPCODE_JUMP_TO_DEF_ARGUMENT,              \
-		&&OPCODE_RETURN,                            \
-		&&OPCODE_ITERATE_BEGIN,                     \
-		&&OPCODE_ITERATE,                           \
-		&&OPCODE_ASSERT,                            \
-		&&OPCODE_BREAKPOINT,                        \
-		&&OPCODE_LINE,                              \
-		&&OPCODE_END                                \
-	};                                              \
+#define OPCODES_TABLE                                \
+	static const void *switch_table_ops[] = {        \
+		&&OPCODE_OPERATOR,                           \
+		&&OPCODE_OPERATOR_VALIDATED,                 \
+		&&OPCODE_EXTENDS_TEST,                       \
+		&&OPCODE_IS_BUILTIN,                         \
+		&&OPCODE_SET_KEYED,                          \
+		&&OPCODE_SET_KEYED_VALIDATED,                \
+		&&OPCODE_SET_INDEXED_VALIDATED,              \
+		&&OPCODE_GET_KEYED,                          \
+		&&OPCODE_GET_KEYED_VALIDATED,                \
+		&&OPCODE_GET_INDEXED_VALIDATED,              \
+		&&OPCODE_SET_NAMED,                          \
+		&&OPCODE_SET_NAMED_VALIDATED,                \
+		&&OPCODE_GET_NAMED,                          \
+		&&OPCODE_GET_NAMED_VALIDATED,                \
+		&&OPCODE_SET_MEMBER,                         \
+		&&OPCODE_GET_MEMBER,                         \
+		&&OPCODE_ASSIGN,                             \
+		&&OPCODE_ASSIGN_TRUE,                        \
+		&&OPCODE_ASSIGN_FALSE,                       \
+		&&OPCODE_ASSIGN_TYPED_BUILTIN,               \
+		&&OPCODE_ASSIGN_TYPED_NATIVE,                \
+		&&OPCODE_ASSIGN_TYPED_SCRIPT,                \
+		&&OPCODE_CAST_TO_BUILTIN,                    \
+		&&OPCODE_CAST_TO_NATIVE,                     \
+		&&OPCODE_CAST_TO_SCRIPT,                     \
+		&&OPCODE_CONSTRUCT,                          \
+		&&OPCODE_CONSTRUCT_ARRAY,                    \
+		&&OPCODE_CONSTRUCT_DICTIONARY,               \
+		&&OPCODE_CALL,                               \
+		&&OPCODE_CALL_RETURN,                        \
+		&&OPCODE_CALL_ASYNC,                         \
+		&&OPCODE_CALL_BUILT_IN,                      \
+		&&OPCODE_CALL_BUILTIN_TYPE_VALIDATED,        \
+		&&OPCODE_CALL_SELF_BASE,                     \
+		&&OPCODE_CALL_METHOD_BIND,                   \
+		&&OPCODE_CALL_METHOD_BIND_RET,               \
+		&&OPCODE_CALL_PTRCALL_NO_RETURN,             \
+		&&OPCODE_CALL_PTRCALL_BOOL,                  \
+		&&OPCODE_CALL_PTRCALL_INT,                   \
+		&&OPCODE_CALL_PTRCALL_FLOAT,                 \
+		&&OPCODE_CALL_PTRCALL_STRING,                \
+		&&OPCODE_CALL_PTRCALL_VECTOR2,               \
+		&&OPCODE_CALL_PTRCALL_VECTOR2I,              \
+		&&OPCODE_CALL_PTRCALL_RECT2,                 \
+		&&OPCODE_CALL_PTRCALL_RECT2I,                \
+		&&OPCODE_CALL_PTRCALL_VECTOR3,               \
+		&&OPCODE_CALL_PTRCALL_VECTOR3I,              \
+		&&OPCODE_CALL_PTRCALL_TRANSFORM2D,           \
+		&&OPCODE_CALL_PTRCALL_PLANE,                 \
+		&&OPCODE_CALL_PTRCALL_QUAT,                  \
+		&&OPCODE_CALL_PTRCALL_AABB,                  \
+		&&OPCODE_CALL_PTRCALL_BASIS,                 \
+		&&OPCODE_CALL_PTRCALL_TRANSFORM,             \
+		&&OPCODE_CALL_PTRCALL_COLOR,                 \
+		&&OPCODE_CALL_PTRCALL_STRING_NAME,           \
+		&&OPCODE_CALL_PTRCALL_NODE_PATH,             \
+		&&OPCODE_CALL_PTRCALL_RID,                   \
+		&&OPCODE_CALL_PTRCALL_OBJECT,                \
+		&&OPCODE_CALL_PTRCALL_CALLABLE,              \
+		&&OPCODE_CALL_PTRCALL_SIGNAL,                \
+		&&OPCODE_CALL_PTRCALL_DICTIONARY,            \
+		&&OPCODE_CALL_PTRCALL_ARRAY,                 \
+		&&OPCODE_CALL_PTRCALL_PACKED_BYTE_ARRAY,     \
+		&&OPCODE_CALL_PTRCALL_PACKED_INT32_ARRAY,    \
+		&&OPCODE_CALL_PTRCALL_PACKED_INT64_ARRAY,    \
+		&&OPCODE_CALL_PTRCALL_PACKED_FLOAT32_ARRAY,  \
+		&&OPCODE_CALL_PTRCALL_PACKED_FLOAT64_ARRAY,  \
+		&&OPCODE_CALL_PTRCALL_PACKED_STRING_ARRAY,   \
+		&&OPCODE_CALL_PTRCALL_PACKED_VECTOR2_ARRAY,  \
+		&&OPCODE_CALL_PTRCALL_PACKED_VECTOR3_ARRAY,  \
+		&&OPCODE_CALL_PTRCALL_PACKED_COLOR_ARRAY,    \
+		&&OPCODE_AWAIT,                              \
+		&&OPCODE_AWAIT_RESUME,                       \
+		&&OPCODE_JUMP,                               \
+		&&OPCODE_JUMP_IF,                            \
+		&&OPCODE_JUMP_IF_NOT,                        \
+		&&OPCODE_JUMP_TO_DEF_ARGUMENT,               \
+		&&OPCODE_RETURN,                             \
+		&&OPCODE_ITERATE_BEGIN,                      \
+		&&OPCODE_ITERATE_BEGIN_INT,                  \
+		&&OPCODE_ITERATE_BEGIN_FLOAT,                \
+		&&OPCODE_ITERATE_BEGIN_VECTOR2,              \
+		&&OPCODE_ITERATE_BEGIN_VECTOR2I,             \
+		&&OPCODE_ITERATE_BEGIN_VECTOR3,              \
+		&&OPCODE_ITERATE_BEGIN_VECTOR3I,             \
+		&&OPCODE_ITERATE_BEGIN_STRING,               \
+		&&OPCODE_ITERATE_BEGIN_DICTIONARY,           \
+		&&OPCODE_ITERATE_BEGIN_ARRAY,                \
+		&&OPCODE_ITERATE_BEGIN_PACKED_BYTE_ARRAY,    \
+		&&OPCODE_ITERATE_BEGIN_PACKED_INT32_ARRAY,   \
+		&&OPCODE_ITERATE_BEGIN_PACKED_INT64_ARRAY,   \
+		&&OPCODE_ITERATE_BEGIN_PACKED_FLOAT32_ARRAY, \
+		&&OPCODE_ITERATE_BEGIN_PACKED_FLOAT64_ARRAY, \
+		&&OPCODE_ITERATE_BEGIN_PACKED_STRING_ARRAY,  \
+		&&OPCODE_ITERATE_BEGIN_PACKED_VECTOR2_ARRAY, \
+		&&OPCODE_ITERATE_BEGIN_PACKED_VECTOR3_ARRAY, \
+		&&OPCODE_ITERATE_BEGIN_PACKED_COLOR_ARRAY,   \
+		&&OPCODE_ITERATE_BEGIN_OBJECT,               \
+		&&OPCODE_ITERATE,                            \
+		&&OPCODE_ITERATE_INT,                        \
+		&&OPCODE_ITERATE_FLOAT,                      \
+		&&OPCODE_ITERATE_VECTOR2,                    \
+		&&OPCODE_ITERATE_VECTOR2I,                   \
+		&&OPCODE_ITERATE_VECTOR3,                    \
+		&&OPCODE_ITERATE_VECTOR3I,                   \
+		&&OPCODE_ITERATE_STRING,                     \
+		&&OPCODE_ITERATE_DICTIONARY,                 \
+		&&OPCODE_ITERATE_ARRAY,                      \
+		&&OPCODE_ITERATE_PACKED_BYTE_ARRAY,          \
+		&&OPCODE_ITERATE_PACKED_INT32_ARRAY,         \
+		&&OPCODE_ITERATE_PACKED_INT64_ARRAY,         \
+		&&OPCODE_ITERATE_PACKED_FLOAT32_ARRAY,       \
+		&&OPCODE_ITERATE_PACKED_FLOAT64_ARRAY,       \
+		&&OPCODE_ITERATE_PACKED_STRING_ARRAY,        \
+		&&OPCODE_ITERATE_PACKED_VECTOR2_ARRAY,       \
+		&&OPCODE_ITERATE_PACKED_VECTOR3_ARRAY,       \
+		&&OPCODE_ITERATE_PACKED_COLOR_ARRAY,         \
+		&&OPCODE_ITERATE_OBJECT,                     \
+		&&OPCODE_ASSERT,                             \
+		&&OPCODE_BREAKPOINT,                         \
+		&&OPCODE_LINE,                               \
+		&&OPCODE_END                                 \
+	};                                               \
 	static_assert((sizeof(switch_table_ops) / sizeof(switch_table_ops[0]) == (OPCODE_END + 1)), "Opcodes in jump table aren't the same as opcodes in enum.");
 
 #define OPCODE(m_op) \
@@ -1979,6 +2018,344 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
 			}
 			DISPATCH_OPCODE;
 
+			OPCODE(OPCODE_ITERATE_BEGIN_INT) {
+				CHECK_SPACE(8); // Check space for iterate instruction too.
+
+				GET_INSTRUCTION_ARG(counter, 0);
+				GET_INSTRUCTION_ARG(container, 1);
+
+				int64_t size = *VariantInternal::get_int(container);
+
+				VariantInternal::initialize(counter, Variant::INT);
+				*VariantInternal::get_int(counter) = 0;
+
+				if (size > 0) {
+					GET_INSTRUCTION_ARG(iterator, 2);
+					VariantInternal::initialize(iterator, Variant::INT);
+					*VariantInternal::get_int(iterator) = 0;
+
+					// Skip regular iterate.
+					ip += 5;
+				} else {
+					// Jump to end of loop.
+					int jumpto = _code_ptr[ip + 4];
+					GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+					ip = jumpto;
+				}
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_ITERATE_BEGIN_FLOAT) {
+				CHECK_SPACE(8); // Check space for iterate instruction too.
+
+				GET_INSTRUCTION_ARG(counter, 0);
+				GET_INSTRUCTION_ARG(container, 1);
+
+				double size = *VariantInternal::get_float(container);
+
+				VariantInternal::initialize(counter, Variant::FLOAT);
+				*VariantInternal::get_float(counter) = 0.0;
+
+				if (size > 0) {
+					GET_INSTRUCTION_ARG(iterator, 2);
+					VariantInternal::initialize(iterator, Variant::FLOAT);
+					*VariantInternal::get_float(iterator) = 0;
+
+					// Skip regular iterate.
+					ip += 5;
+				} else {
+					// Jump to end of loop.
+					int jumpto = _code_ptr[ip + 4];
+					GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+					ip = jumpto;
+				}
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_ITERATE_BEGIN_VECTOR2) {
+				CHECK_SPACE(8); // Check space for iterate instruction too.
+
+				GET_INSTRUCTION_ARG(counter, 0);
+				GET_INSTRUCTION_ARG(container, 1);
+
+				Vector2 *bounds = VariantInternal::get_vector2(container);
+
+				VariantInternal::initialize(counter, Variant::FLOAT);
+				*VariantInternal::get_float(counter) = bounds->x;
+
+				if (bounds->x < bounds->y) {
+					GET_INSTRUCTION_ARG(iterator, 2);
+					VariantInternal::initialize(iterator, Variant::FLOAT);
+					*VariantInternal::get_float(iterator) = bounds->x;
+
+					// Skip regular iterate.
+					ip += 5;
+				} else {
+					// Jump to end of loop.
+					int jumpto = _code_ptr[ip + 4];
+					GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+					ip = jumpto;
+				}
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_ITERATE_BEGIN_VECTOR2I) {
+				CHECK_SPACE(8); // Check space for iterate instruction too.
+
+				GET_INSTRUCTION_ARG(counter, 0);
+				GET_INSTRUCTION_ARG(container, 1);
+
+				Vector2i *bounds = VariantInternal::get_vector2i(container);
+
+				VariantInternal::initialize(counter, Variant::FLOAT);
+				*VariantInternal::get_int(counter) = bounds->x;
+
+				if (bounds->x < bounds->y) {
+					GET_INSTRUCTION_ARG(iterator, 2);
+					VariantInternal::initialize(iterator, Variant::INT);
+					*VariantInternal::get_int(iterator) = bounds->x;
+
+					// Skip regular iterate.
+					ip += 5;
+				} else {
+					// Jump to end of loop.
+					int jumpto = _code_ptr[ip + 4];
+					GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+					ip = jumpto;
+				}
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_ITERATE_BEGIN_VECTOR3) {
+				CHECK_SPACE(8); // Check space for iterate instruction too.
+
+				GET_INSTRUCTION_ARG(counter, 0);
+				GET_INSTRUCTION_ARG(container, 1);
+
+				Vector3 *bounds = VariantInternal::get_vector3(container);
+				double from = bounds->x;
+				double to = bounds->y;
+				double step = bounds->z;
+
+				VariantInternal::initialize(counter, Variant::FLOAT);
+				*VariantInternal::get_float(counter) = from;
+
+				bool do_continue = from == to ? false : (from < to ? step > 0 : step < 0);
+
+				if (do_continue) {
+					GET_INSTRUCTION_ARG(iterator, 2);
+					VariantInternal::initialize(iterator, Variant::FLOAT);
+					*VariantInternal::get_float(iterator) = from;
+
+					// Skip regular iterate.
+					ip += 5;
+				} else {
+					// Jump to end of loop.
+					int jumpto = _code_ptr[ip + 4];
+					GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+					ip = jumpto;
+				}
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_ITERATE_BEGIN_VECTOR3I) {
+				CHECK_SPACE(8); // Check space for iterate instruction too.
+
+				GET_INSTRUCTION_ARG(counter, 0);
+				GET_INSTRUCTION_ARG(container, 1);
+
+				Vector3i *bounds = VariantInternal::get_vector3i(container);
+				int64_t from = bounds->x;
+				int64_t to = bounds->y;
+				int64_t step = bounds->z;
+
+				VariantInternal::initialize(counter, Variant::INT);
+				*VariantInternal::get_int(counter) = from;
+
+				bool do_continue = from == to ? false : (from < to ? step > 0 : step < 0);
+
+				if (do_continue) {
+					GET_INSTRUCTION_ARG(iterator, 2);
+					VariantInternal::initialize(iterator, Variant::INT);
+					*VariantInternal::get_int(iterator) = from;
+
+					// Skip regular iterate.
+					ip += 5;
+				} else {
+					// Jump to end of loop.
+					int jumpto = _code_ptr[ip + 4];
+					GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+					ip = jumpto;
+				}
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_ITERATE_BEGIN_STRING) {
+				CHECK_SPACE(8); // Check space for iterate instruction too.
+
+				GET_INSTRUCTION_ARG(counter, 0);
+				GET_INSTRUCTION_ARG(container, 1);
+
+				String *str = VariantInternal::get_string(container);
+
+				VariantInternal::initialize(counter, Variant::INT);
+				*VariantInternal::get_int(counter) = 0;
+
+				if (!str->empty()) {
+					GET_INSTRUCTION_ARG(iterator, 2);
+					VariantInternal::initialize(iterator, Variant::STRING);
+					*VariantInternal::get_string(iterator) = str->substr(0, 1);
+
+					// Skip regular iterate.
+					ip += 5;
+				} else {
+					// Jump to end of loop.
+					int jumpto = _code_ptr[ip + 4];
+					GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+					ip = jumpto;
+				}
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_ITERATE_BEGIN_DICTIONARY) {
+				CHECK_SPACE(8); // Check space for iterate instruction too.
+
+				GET_INSTRUCTION_ARG(counter, 0);
+				GET_INSTRUCTION_ARG(container, 1);
+
+				Dictionary *dict = VariantInternal::get_dictionary(container);
+				const Variant *next = dict->next(nullptr);
+				*counter = *next;
+
+				if (!dict->empty()) {
+					GET_INSTRUCTION_ARG(iterator, 2);
+					*iterator = *next;
+
+					// Skip regular iterate.
+					ip += 5;
+				} else {
+					// Jump to end of loop.
+					int jumpto = _code_ptr[ip + 4];
+					GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+					ip = jumpto;
+				}
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_ITERATE_BEGIN_ARRAY) {
+				CHECK_SPACE(8); // Check space for iterate instruction too.
+
+				GET_INSTRUCTION_ARG(counter, 0);
+				GET_INSTRUCTION_ARG(container, 1);
+
+				Array *array = VariantInternal::get_array(container);
+
+				VariantInternal::initialize(counter, Variant::INT);
+				*VariantInternal::get_int(counter) = 0;
+
+				if (!array->empty()) {
+					GET_INSTRUCTION_ARG(iterator, 2);
+					*iterator = array->get(0);
+
+					// Skip regular iterate.
+					ip += 5;
+				} else {
+					// Jump to end of loop.
+					int jumpto = _code_ptr[ip + 4];
+					GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+					ip = jumpto;
+				}
+			}
+			DISPATCH_OPCODE;
+
+#define OPCODE_ITERATE_BEGIN_PACKED_ARRAY(m_var_type, m_elem_type, m_get_func, m_var_ret_type, m_ret_type, m_ret_get_func) \
+	OPCODE(OPCODE_ITERATE_BEGIN_PACKED_##m_var_type##_ARRAY) {                                                             \
+		CHECK_SPACE(8);                                                                                                    \
+		GET_INSTRUCTION_ARG(counter, 0);                                                                                   \
+		GET_INSTRUCTION_ARG(container, 1);                                                                                 \
+		Vector<m_elem_type> *array = VariantInternal::m_get_func(container);                                               \
+		VariantInternal::initialize(counter, Variant::INT);                                                                \
+		*VariantInternal::get_int(counter) = 0;                                                                            \
+		if (!array->empty()) {                                                                                             \
+			GET_INSTRUCTION_ARG(iterator, 2);                                                                              \
+			VariantInternal::initialize(iterator, Variant::m_var_ret_type);                                                \
+			m_ret_type *it = VariantInternal::m_ret_get_func(iterator);                                                    \
+			*it = array->get(0);                                                                                           \
+			ip += 5;                                                                                                       \
+		} else {                                                                                                           \
+			int jumpto = _code_ptr[ip + 4];                                                                                \
+			GD_ERR_BREAK(jumpto<0 || jumpto> _code_size);                                                                  \
+			ip = jumpto;                                                                                                   \
+		}                                                                                                                  \
+	}                                                                                                                      \
+	DISPATCH_OPCODE
+
+			OPCODE_ITERATE_BEGIN_PACKED_ARRAY(BYTE, uint8_t, get_byte_array, INT, int64_t, get_int);
+			OPCODE_ITERATE_BEGIN_PACKED_ARRAY(INT32, int32_t, get_int32_array, INT, int64_t, get_int);
+			OPCODE_ITERATE_BEGIN_PACKED_ARRAY(INT64, int64_t, get_int64_array, INT, int64_t, get_int);
+			OPCODE_ITERATE_BEGIN_PACKED_ARRAY(FLOAT32, float, get_float32_array, FLOAT, double, get_float);
+			OPCODE_ITERATE_BEGIN_PACKED_ARRAY(FLOAT64, double, get_float64_array, FLOAT, double, get_float);
+			OPCODE_ITERATE_BEGIN_PACKED_ARRAY(STRING, String, get_string_array, STRING, String, get_string);
+			OPCODE_ITERATE_BEGIN_PACKED_ARRAY(VECTOR2, Vector2, get_vector2_array, VECTOR2, Vector2, get_vector2);
+			OPCODE_ITERATE_BEGIN_PACKED_ARRAY(VECTOR3, Vector3, get_vector3_array, VECTOR3, Vector3, get_vector3);
+			OPCODE_ITERATE_BEGIN_PACKED_ARRAY(COLOR, Color, get_color_array, COLOR, Color, get_color);
+
+			OPCODE(OPCODE_ITERATE_BEGIN_OBJECT) {
+				CHECK_SPACE(4);
+
+				GET_INSTRUCTION_ARG(counter, 0);
+				GET_INSTRUCTION_ARG(container, 1);
+
+#ifdef DEBUG_ENABLED
+				bool freed = false;
+				Object *obj = container->get_validated_object_with_check(freed);
+				if (freed) {
+					err_text = "Trying to iterate on a previously freed object.";
+					OPCODE_BREAK;
+				} else if (!obj) {
+					err_text = "Trying to iterate on a null value.";
+					OPCODE_BREAK;
+				}
+#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;
+
+				Callable::CallError ce;
+				Variant has_next = obj->call(CoreStringNames::get_singleton()->_iter_init, (const Variant **)args, 1, ce);
+
+#ifdef DEBUG_ENABLED
+				if (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;
+				}
+#endif
+				if (!has_next.booleanize()) {
+					int jumpto = _code_ptr[ip + 4];
+					GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+					ip = jumpto;
+				} else {
+					GET_INSTRUCTION_ARG(iterator, 2);
+					*iterator = obj->call(CoreStringNames::get_singleton()->_iter_get, (const Variant **)args, 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);
+						OPCODE_BREAK;
+					}
+#endif
+
+					ip += 5; // Loop again.
+				}
+			}
+			DISPATCH_OPCODE;
+
 			OPCODE(OPCODE_ITERATE) {
 				CHECK_SPACE(4);
 
@@ -2011,6 +2388,305 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
 			}
 			DISPATCH_OPCODE;
 
+			OPCODE(OPCODE_ITERATE_INT) {
+				CHECK_SPACE(4);
+
+				GET_INSTRUCTION_ARG(counter, 0);
+				GET_INSTRUCTION_ARG(container, 1);
+
+				int64_t size = *VariantInternal::get_int(container);
+				int64_t *count = VariantInternal::get_int(counter);
+
+				(*count)++;
+
+				if (*count >= size) {
+					int jumpto = _code_ptr[ip + 4];
+					GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+					ip = jumpto;
+				} else {
+					GET_INSTRUCTION_ARG(iterator, 2);
+					*VariantInternal::get_int(iterator) = *count;
+
+					ip += 5; // Loop again.
+				}
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_ITERATE_FLOAT) {
+				CHECK_SPACE(4);
+
+				GET_INSTRUCTION_ARG(counter, 0);
+				GET_INSTRUCTION_ARG(container, 1);
+
+				double size = *VariantInternal::get_float(container);
+				double *count = VariantInternal::get_float(counter);
+
+				(*count)++;
+
+				if (*count >= size) {
+					int jumpto = _code_ptr[ip + 4];
+					GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+					ip = jumpto;
+				} else {
+					GET_INSTRUCTION_ARG(iterator, 2);
+					*VariantInternal::get_float(iterator) = *count;
+
+					ip += 5; // Loop again.
+				}
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_ITERATE_VECTOR2) {
+				CHECK_SPACE(4);
+
+				GET_INSTRUCTION_ARG(counter, 0);
+				GET_INSTRUCTION_ARG(container, 1);
+
+				const Vector2 *bounds = VariantInternal::get_vector2((const Variant *)container);
+				double *count = VariantInternal::get_float(counter);
+
+				(*count)++;
+
+				if (*count >= bounds->y) {
+					int jumpto = _code_ptr[ip + 4];
+					GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+					ip = jumpto;
+				} else {
+					GET_INSTRUCTION_ARG(iterator, 2);
+					*VariantInternal::get_float(iterator) = *count;
+
+					ip += 5; // Loop again.
+				}
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_ITERATE_VECTOR2I) {
+				CHECK_SPACE(4);
+
+				GET_INSTRUCTION_ARG(counter, 0);
+				GET_INSTRUCTION_ARG(container, 1);
+
+				const Vector2i *bounds = VariantInternal::get_vector2i((const Variant *)container);
+				int64_t *count = VariantInternal::get_int(counter);
+
+				(*count)++;
+
+				if (*count >= bounds->y) {
+					int jumpto = _code_ptr[ip + 4];
+					GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+					ip = jumpto;
+				} else {
+					GET_INSTRUCTION_ARG(iterator, 2);
+					*VariantInternal::get_int(iterator) = *count;
+
+					ip += 5; // Loop again.
+				}
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_ITERATE_VECTOR3) {
+				CHECK_SPACE(4);
+
+				GET_INSTRUCTION_ARG(counter, 0);
+				GET_INSTRUCTION_ARG(container, 1);
+
+				const Vector3 *bounds = VariantInternal::get_vector3((const Variant *)container);
+				double *count = VariantInternal::get_float(counter);
+
+				*count += bounds->z;
+
+				if ((bounds->z < 0 && *count <= bounds->y) || (bounds->z > 0 && *count >= bounds->y)) {
+					int jumpto = _code_ptr[ip + 4];
+					GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+					ip = jumpto;
+				} else {
+					GET_INSTRUCTION_ARG(iterator, 2);
+					*VariantInternal::get_float(iterator) = *count;
+
+					ip += 5; // Loop again.
+				}
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_ITERATE_VECTOR3I) {
+				CHECK_SPACE(4);
+
+				GET_INSTRUCTION_ARG(counter, 0);
+				GET_INSTRUCTION_ARG(container, 1);
+
+				const Vector3i *bounds = VariantInternal::get_vector3i((const Variant *)container);
+				int64_t *count = VariantInternal::get_int(counter);
+
+				*count += bounds->z;
+
+				if ((bounds->z < 0 && *count <= bounds->y) || (bounds->z > 0 && *count >= bounds->y)) {
+					int jumpto = _code_ptr[ip + 4];
+					GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+					ip = jumpto;
+				} else {
+					GET_INSTRUCTION_ARG(iterator, 2);
+					*VariantInternal::get_int(iterator) = *count;
+
+					ip += 5; // Loop again.
+				}
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_ITERATE_STRING) {
+				CHECK_SPACE(4);
+
+				GET_INSTRUCTION_ARG(counter, 0);
+				GET_INSTRUCTION_ARG(container, 1);
+
+				const String *str = VariantInternal::get_string((const Variant *)container);
+				int64_t *idx = VariantInternal::get_int(counter);
+				(*idx)++;
+
+				if (*idx >= str->length()) {
+					int jumpto = _code_ptr[ip + 4];
+					GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+					ip = jumpto;
+				} else {
+					GET_INSTRUCTION_ARG(iterator, 2);
+					*VariantInternal::get_string(iterator) = str->substr(*idx, 1);
+
+					ip += 5; // Loop again.
+				}
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_ITERATE_DICTIONARY) {
+				CHECK_SPACE(4);
+
+				GET_INSTRUCTION_ARG(counter, 0);
+				GET_INSTRUCTION_ARG(container, 1);
+
+				const Dictionary *dict = VariantInternal::get_dictionary((const Variant *)container);
+				const Variant *next = dict->next(counter);
+
+				if (!next) {
+					int jumpto = _code_ptr[ip + 4];
+					GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+					ip = jumpto;
+				} else {
+					GET_INSTRUCTION_ARG(iterator, 2);
+					*counter = *next;
+					*iterator = *next;
+
+					ip += 5; // Loop again.
+				}
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_ITERATE_ARRAY) {
+				CHECK_SPACE(4);
+
+				GET_INSTRUCTION_ARG(counter, 0);
+				GET_INSTRUCTION_ARG(container, 1);
+
+				const Array *array = VariantInternal::get_array((const Variant *)container);
+				int64_t *idx = VariantInternal::get_int(counter);
+				(*idx)++;
+
+				if (*idx >= array->size()) {
+					int jumpto = _code_ptr[ip + 4];
+					GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+					ip = jumpto;
+				} else {
+					GET_INSTRUCTION_ARG(iterator, 2);
+					*iterator = array->get(*idx);
+
+					ip += 5; // Loop again.
+				}
+			}
+			DISPATCH_OPCODE;
+
+#define OPCODE_ITERATE_PACKED_ARRAY(m_var_type, m_elem_type, m_get_func, m_ret_get_func)            \
+	OPCODE(OPCODE_ITERATE_PACKED_##m_var_type##_ARRAY) {                                            \
+		CHECK_SPACE(4);                                                                             \
+		GET_INSTRUCTION_ARG(counter, 0);                                                            \
+		GET_INSTRUCTION_ARG(container, 1);                                                          \
+		const Vector<m_elem_type> *array = VariantInternal::m_get_func((const Variant *)container); \
+		int64_t *idx = VariantInternal::get_int(counter);                                           \
+		(*idx)++;                                                                                   \
+		if (*idx >= array->size()) {                                                                \
+			int jumpto = _code_ptr[ip + 4];                                                         \
+			GD_ERR_BREAK(jumpto<0 || jumpto> _code_size);                                           \
+			ip = jumpto;                                                                            \
+		} else {                                                                                    \
+			GET_INSTRUCTION_ARG(iterator, 2);                                                       \
+			*VariantInternal::m_ret_get_func(iterator) = array->get(*idx);                          \
+			ip += 5;                                                                                \
+		}                                                                                           \
+	}                                                                                               \
+	DISPATCH_OPCODE
+
+			OPCODE_ITERATE_PACKED_ARRAY(BYTE, uint8_t, get_byte_array, get_int);
+			OPCODE_ITERATE_PACKED_ARRAY(INT32, int32_t, get_int32_array, get_int);
+			OPCODE_ITERATE_PACKED_ARRAY(INT64, int64_t, get_int64_array, get_int);
+			OPCODE_ITERATE_PACKED_ARRAY(FLOAT32, float, get_float32_array, get_float);
+			OPCODE_ITERATE_PACKED_ARRAY(FLOAT64, double, get_float64_array, get_float);
+			OPCODE_ITERATE_PACKED_ARRAY(STRING, String, get_string_array, get_string);
+			OPCODE_ITERATE_PACKED_ARRAY(VECTOR2, Vector2, get_vector2_array, get_vector2);
+			OPCODE_ITERATE_PACKED_ARRAY(VECTOR3, Vector3, get_vector3_array, get_vector3);
+			OPCODE_ITERATE_PACKED_ARRAY(COLOR, Color, get_color_array, get_color);
+
+			OPCODE(OPCODE_ITERATE_OBJECT) {
+				CHECK_SPACE(4);
+
+				GET_INSTRUCTION_ARG(counter, 0);
+				GET_INSTRUCTION_ARG(container, 1);
+
+#ifdef DEBUG_ENABLED
+				bool freed = false;
+				Object *obj = container->get_validated_object_with_check(freed);
+				if (freed) {
+					err_text = "Trying to iterate on a previously freed object.";
+					OPCODE_BREAK;
+				} else if (!obj) {
+					err_text = "Trying to iterate on a null value.";
+					OPCODE_BREAK;
+				}
+#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;
+
+				Callable::CallError ce;
+				Variant has_next = obj->call(CoreStringNames::get_singleton()->_iter_next, (const Variant **)args, 1, ce);
+
+#ifdef DEBUG_ENABLED
+				if (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;
+				}
+#endif
+				if (!has_next.booleanize()) {
+					int jumpto = _code_ptr[ip + 4];
+					GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+					ip = jumpto;
+				} else {
+					GET_INSTRUCTION_ARG(iterator, 2);
+					*iterator = obj->call(CoreStringNames::get_singleton()->_iter_get, (const Variant **)args, 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);
+						OPCODE_BREAK;
+					}
+#endif
+
+					ip += 5; // Loop again.
+				}
+			}
+			DISPATCH_OPCODE;
+
 			OPCODE(OPCODE_ASSERT) {
 				CHECK_SPACE(3);