Browse Source

Merge pull request #43725 from vnen/gdscript-typed-vm-2

GDScript: Typed VM Take 2
Rémi Verschelde 4 years ago
parent
commit
4ed42bfc29

+ 232 - 1
core/variant/variant_internal.h

@@ -38,7 +38,82 @@
 class VariantInternal {
 public:
 	// Set type.
-	_FORCE_INLINE_ static void initialize(Variant *v, Variant::Type p_type) { v->type = p_type; }
+	_FORCE_INLINE_ static void initialize(Variant *v, Variant::Type p_type) {
+		v->clear();
+		v->type = p_type;
+
+		switch (p_type) {
+			case Variant::AABB:
+				init_aabb(v);
+				break;
+			case Variant::TRANSFORM2D:
+				init_transform2d(v);
+				break;
+			case Variant::TRANSFORM:
+				init_transform(v);
+				break;
+			case Variant::STRING:
+				init_string(v);
+				break;
+			case Variant::STRING_NAME:
+				init_string_name(v);
+				break;
+			case Variant::NODE_PATH:
+				init_node_path(v);
+				break;
+			case Variant::CALLABLE:
+				init_callable(v);
+				break;
+			case Variant::SIGNAL:
+				init_signal(v);
+				break;
+			case Variant::DICTIONARY:
+				init_dictionary(v);
+				break;
+			case Variant::ARRAY:
+				init_array(v);
+				break;
+			case Variant::PACKED_BYTE_ARRAY:
+				init_byte_array(v);
+				break;
+			case Variant::PACKED_INT32_ARRAY:
+				init_int32_array(v);
+				break;
+			case Variant::PACKED_INT64_ARRAY:
+				init_int64_array(v);
+				break;
+			case Variant::PACKED_FLOAT32_ARRAY:
+				init_float32_array(v);
+				break;
+			case Variant::PACKED_FLOAT64_ARRAY:
+				init_float64_array(v);
+				break;
+			case Variant::PACKED_STRING_ARRAY:
+				init_string_array(v);
+				break;
+			case Variant::PACKED_VECTOR2_ARRAY:
+				init_vector2_array(v);
+				break;
+			case Variant::PACKED_VECTOR3_ARRAY:
+				init_vector3_array(v);
+				break;
+			case Variant::PACKED_COLOR_ARRAY:
+				init_color_array(v);
+				break;
+			default:
+				break;
+		}
+	}
+
+	_FORCE_INLINE_ static void set_object(Variant *v, Object *obj) {
+		if (obj) {
+			v->_get_obj().obj = obj;
+			v->_get_obj().id = obj->get_instance_id();
+		} else {
+			v->_get_obj().obj = nullptr;
+			v->_get_obj().id = ObjectID();
+		}
+	}
 
 	// Atomic types.
 	_FORCE_INLINE_ static bool *get_bool(Variant *v) { return &v->_data._bool; }
@@ -216,6 +291,162 @@ public:
 		v->_get_obj().obj = nullptr;
 		v->_get_obj().id = ObjectID();
 	}
+
+	_FORCE_INLINE_ static void *get_opaque_pointer(Variant *v) {
+		switch (v->type) {
+			case Variant::NIL:
+				return nullptr;
+			case Variant::BOOL:
+				return get_bool(v);
+			case Variant::INT:
+				return get_int(v);
+			case Variant::FLOAT:
+				return get_float(v);
+			case Variant::STRING:
+				return get_string(v);
+			case Variant::VECTOR2:
+				return get_vector2(v);
+			case Variant::VECTOR2I:
+				return get_vector2i(v);
+			case Variant::VECTOR3:
+				return get_vector3(v);
+			case Variant::VECTOR3I:
+				return get_vector3i(v);
+			case Variant::RECT2:
+				return get_rect2(v);
+			case Variant::RECT2I:
+				return get_rect2i(v);
+			case Variant::TRANSFORM:
+				return get_transform(v);
+			case Variant::TRANSFORM2D:
+				return get_transform2d(v);
+			case Variant::QUAT:
+				return get_quat(v);
+			case Variant::PLANE:
+				return get_plane(v);
+			case Variant::BASIS:
+				return get_basis(v);
+			case Variant::AABB:
+				return get_aabb(v);
+			case Variant::COLOR:
+				return get_color(v);
+			case Variant::STRING_NAME:
+				return get_string_name(v);
+			case Variant::NODE_PATH:
+				return get_node_path(v);
+			case Variant::RID:
+				return get_rid(v);
+			case Variant::CALLABLE:
+				return get_callable(v);
+			case Variant::SIGNAL:
+				return get_signal(v);
+			case Variant::DICTIONARY:
+				return get_dictionary(v);
+			case Variant::ARRAY:
+				return get_array(v);
+			case Variant::PACKED_BYTE_ARRAY:
+				return get_byte_array(v);
+			case Variant::PACKED_INT32_ARRAY:
+				return get_int32_array(v);
+			case Variant::PACKED_INT64_ARRAY:
+				return get_int64_array(v);
+			case Variant::PACKED_FLOAT32_ARRAY:
+				return get_float32_array(v);
+			case Variant::PACKED_FLOAT64_ARRAY:
+				return get_float64_array(v);
+			case Variant::PACKED_STRING_ARRAY:
+				return get_string_array(v);
+			case Variant::PACKED_VECTOR2_ARRAY:
+				return get_vector2_array(v);
+			case Variant::PACKED_VECTOR3_ARRAY:
+				return get_vector3_array(v);
+			case Variant::PACKED_COLOR_ARRAY:
+				return get_color_array(v);
+			case Variant::OBJECT:
+				return v->_get_obj().obj;
+			case Variant::VARIANT_MAX:
+				ERR_FAIL_V(nullptr);
+		}
+		ERR_FAIL_V(nullptr);
+	}
+
+	_FORCE_INLINE_ static const void *get_opaque_pointer(const Variant *v) {
+		switch (v->type) {
+			case Variant::NIL:
+				return nullptr;
+			case Variant::BOOL:
+				return get_bool(v);
+			case Variant::INT:
+				return get_int(v);
+			case Variant::FLOAT:
+				return get_float(v);
+			case Variant::STRING:
+				return get_string(v);
+			case Variant::VECTOR2:
+				return get_vector2(v);
+			case Variant::VECTOR2I:
+				return get_vector2i(v);
+			case Variant::VECTOR3:
+				return get_vector3(v);
+			case Variant::VECTOR3I:
+				return get_vector3i(v);
+			case Variant::RECT2:
+				return get_rect2(v);
+			case Variant::RECT2I:
+				return get_rect2i(v);
+			case Variant::TRANSFORM:
+				return get_transform(v);
+			case Variant::TRANSFORM2D:
+				return get_transform2d(v);
+			case Variant::QUAT:
+				return get_quat(v);
+			case Variant::PLANE:
+				return get_plane(v);
+			case Variant::BASIS:
+				return get_basis(v);
+			case Variant::AABB:
+				return get_aabb(v);
+			case Variant::COLOR:
+				return get_color(v);
+			case Variant::STRING_NAME:
+				return get_string_name(v);
+			case Variant::NODE_PATH:
+				return get_node_path(v);
+			case Variant::RID:
+				return get_rid(v);
+			case Variant::CALLABLE:
+				return get_callable(v);
+			case Variant::SIGNAL:
+				return get_signal(v);
+			case Variant::DICTIONARY:
+				return get_dictionary(v);
+			case Variant::ARRAY:
+				return get_array(v);
+			case Variant::PACKED_BYTE_ARRAY:
+				return get_byte_array(v);
+			case Variant::PACKED_INT32_ARRAY:
+				return get_int32_array(v);
+			case Variant::PACKED_INT64_ARRAY:
+				return get_int64_array(v);
+			case Variant::PACKED_FLOAT32_ARRAY:
+				return get_float32_array(v);
+			case Variant::PACKED_FLOAT64_ARRAY:
+				return get_float64_array(v);
+			case Variant::PACKED_STRING_ARRAY:
+				return get_string_array(v);
+			case Variant::PACKED_VECTOR2_ARRAY:
+				return get_vector2_array(v);
+			case Variant::PACKED_VECTOR3_ARRAY:
+				return get_vector3_array(v);
+			case Variant::PACKED_COLOR_ARRAY:
+				return get_color_array(v);
+			case Variant::OBJECT:
+				return v->_get_obj().obj;
+			case Variant::VARIANT_MAX:
+				ERR_FAIL_V(nullptr);
+		}
+		ERR_FAIL_V(nullptr);
+	}
 };
 
 template <class T>

+ 529 - 116
modules/gdscript/gdscript_byte_codegen.cpp

@@ -111,7 +111,7 @@ void GDScriptByteCodeGenerator::write_start(GDScript *p_script, const StringName
 }
 
 GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
-	append(GDScriptFunction::OPCODE_END);
+	append(GDScriptFunction::OPCODE_END, 0);
 
 	if (constant_map.size()) {
 		function->_constant_count = constant_map.size();
@@ -158,11 +158,132 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
 		function->_default_arg_ptr = nullptr;
 	}
 
+	if (operator_func_map.size()) {
+		function->operator_funcs.resize(operator_func_map.size());
+		function->_operator_funcs_count = function->operator_funcs.size();
+		function->_operator_funcs_ptr = function->operator_funcs.ptr();
+		for (const Map<Variant::ValidatedOperatorEvaluator, int>::Element *E = operator_func_map.front(); E; E = E->next()) {
+			function->operator_funcs.write[E->get()] = E->key();
+		}
+	} else {
+		function->_operator_funcs_count = 0;
+		function->_operator_funcs_ptr = nullptr;
+	}
+
+	if (setters_map.size()) {
+		function->setters.resize(setters_map.size());
+		function->_setters_count = function->setters.size();
+		function->_setters_ptr = function->setters.ptr();
+		for (const Map<Variant::ValidatedSetter, int>::Element *E = setters_map.front(); E; E = E->next()) {
+			function->setters.write[E->get()] = E->key();
+		}
+	} else {
+		function->_setters_count = 0;
+		function->_setters_ptr = nullptr;
+	}
+
+	if (getters_map.size()) {
+		function->getters.resize(getters_map.size());
+		function->_getters_count = function->getters.size();
+		function->_getters_ptr = function->getters.ptr();
+		for (const Map<Variant::ValidatedGetter, int>::Element *E = getters_map.front(); E; E = E->next()) {
+			function->getters.write[E->get()] = E->key();
+		}
+	} else {
+		function->_getters_count = 0;
+		function->_getters_ptr = nullptr;
+	}
+
+	if (keyed_setters_map.size()) {
+		function->keyed_setters.resize(keyed_setters_map.size());
+		function->_keyed_setters_count = function->keyed_setters.size();
+		function->_keyed_setters_ptr = function->keyed_setters.ptr();
+		for (const Map<Variant::ValidatedKeyedSetter, int>::Element *E = keyed_setters_map.front(); E; E = E->next()) {
+			function->keyed_setters.write[E->get()] = E->key();
+		}
+	} else {
+		function->_keyed_setters_count = 0;
+		function->_keyed_setters_ptr = nullptr;
+	}
+
+	if (keyed_getters_map.size()) {
+		function->keyed_getters.resize(keyed_getters_map.size());
+		function->_keyed_getters_count = function->keyed_getters.size();
+		function->_keyed_getters_ptr = function->keyed_getters.ptr();
+		for (const Map<Variant::ValidatedKeyedGetter, int>::Element *E = keyed_getters_map.front(); E; E = E->next()) {
+			function->keyed_getters.write[E->get()] = E->key();
+		}
+	} else {
+		function->_keyed_getters_count = 0;
+		function->_keyed_getters_ptr = nullptr;
+	}
+
+	if (indexed_setters_map.size()) {
+		function->indexed_setters.resize(indexed_setters_map.size());
+		function->_indexed_setters_count = function->indexed_setters.size();
+		function->_indexed_setters_ptr = function->indexed_setters.ptr();
+		for (const Map<Variant::ValidatedIndexedSetter, int>::Element *E = indexed_setters_map.front(); E; E = E->next()) {
+			function->indexed_setters.write[E->get()] = E->key();
+		}
+	} else {
+		function->_indexed_setters_count = 0;
+		function->_indexed_setters_ptr = nullptr;
+	}
+
+	if (indexed_getters_map.size()) {
+		function->indexed_getters.resize(indexed_getters_map.size());
+		function->_indexed_getters_count = function->indexed_getters.size();
+		function->_indexed_getters_ptr = function->indexed_getters.ptr();
+		for (const Map<Variant::ValidatedIndexedGetter, int>::Element *E = indexed_getters_map.front(); E; E = E->next()) {
+			function->indexed_getters.write[E->get()] = E->key();
+		}
+	} else {
+		function->_indexed_getters_count = 0;
+		function->_indexed_getters_ptr = nullptr;
+	}
+
+	if (builtin_method_map.size()) {
+		function->builtin_methods.resize(builtin_method_map.size());
+		function->_builtin_methods_ptr = function->builtin_methods.ptr();
+		function->_builtin_methods_count = builtin_method_map.size();
+		for (const Map<Variant::ValidatedBuiltInMethod, int>::Element *E = builtin_method_map.front(); E; E = E->next()) {
+			function->builtin_methods.write[E->get()] = E->key();
+		}
+	} else {
+		function->_builtin_methods_ptr = nullptr;
+		function->_builtin_methods_count = 0;
+	}
+
+	if (constructors_map.size()) {
+		function->constructors.resize(constructors_map.size());
+		function->_constructors_ptr = function->constructors.ptr();
+		function->_constructors_count = constructors_map.size();
+		for (const Map<Variant::ValidatedConstructor, int>::Element *E = constructors_map.front(); E; E = E->next()) {
+			function->constructors.write[E->get()] = E->key();
+		}
+	} else {
+		function->_constructors_ptr = nullptr;
+		function->_constructors_count = 0;
+	}
+
+	if (method_bind_map.size()) {
+		function->methods.resize(method_bind_map.size());
+		function->_methods_ptr = function->methods.ptrw();
+		function->_methods_count = method_bind_map.size();
+		for (const Map<MethodBind *, int>::Element *E = method_bind_map.front(); E; E = E->next()) {
+			function->methods.write[E->get()] = E->key();
+		}
+	} else {
+		function->_methods_ptr = nullptr;
+		function->_methods_count = 0;
+	}
+
 	if (debug_stack) {
 		function->stack_debug = stack_debug;
 	}
 	function->_stack_size = stack_max;
-	function->_call_size = call_max;
+	function->_instruction_args_size = instr_args_max;
+	function->_ptrcall_args_size = ptrcall_max;
 
 	ended = true;
 	return function;
@@ -178,37 +299,56 @@ void GDScriptByteCodeGenerator::set_initial_line(int p_line) {
 	function->_initial_line = p_line;
 }
 
+#define HAS_BUILTIN_TYPE(m_var) \
+	(m_var.type.has_type && m_var.type.kind == GDScriptDataType::BUILTIN)
+
+#define IS_BUILTIN_TYPE(m_var, m_type) \
+	(m_var.type.has_type && m_var.type.kind == GDScriptDataType::BUILTIN && m_var.type.builtin_type == m_type)
+
 void GDScriptByteCodeGenerator::write_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand, const Address &p_right_operand) {
-	append(GDScriptFunction::OPCODE_OPERATOR);
-	append(p_operator);
+	if (HAS_BUILTIN_TYPE(p_left_operand) && HAS_BUILTIN_TYPE(p_right_operand)) {
+		// Gather specific operator.
+		Variant::ValidatedOperatorEvaluator op_func = Variant::get_validated_operator_evaluator(p_operator, p_left_operand.type.builtin_type, p_right_operand.type.builtin_type);
+
+		append(GDScriptFunction::OPCODE_OPERATOR_VALIDATED, 3);
+		append(p_left_operand);
+		append(p_right_operand);
+		append(p_target);
+		append(op_func);
+		return;
+	}
+
+	// No specific types, perform variant evaluation.
+	append(GDScriptFunction::OPCODE_OPERATOR, 3);
 	append(p_left_operand);
 	append(p_right_operand);
 	append(p_target);
+	append(p_operator);
 }
 
 void GDScriptByteCodeGenerator::write_type_test(const Address &p_target, const Address &p_source, const Address &p_type) {
-	append(GDScriptFunction::OPCODE_EXTENDS_TEST);
+	append(GDScriptFunction::OPCODE_EXTENDS_TEST, 3);
 	append(p_source);
 	append(p_type);
 	append(p_target);
 }
 
 void GDScriptByteCodeGenerator::write_type_test_builtin(const Address &p_target, const Address &p_source, Variant::Type p_type) {
-	append(GDScriptFunction::OPCODE_IS_BUILTIN);
+	append(GDScriptFunction::OPCODE_IS_BUILTIN, 3);
 	append(p_source);
-	append(p_type);
 	append(p_target);
+	append(p_type);
 }
 
 void GDScriptByteCodeGenerator::write_and_left_operand(const Address &p_left_operand) {
-	append(GDScriptFunction::OPCODE_JUMP_IF_NOT);
+	append(GDScriptFunction::OPCODE_JUMP_IF_NOT, 1);
 	append(p_left_operand);
 	logic_op_jump_pos1.push_back(opcodes.size());
 	append(0); // Jump target, will be patched.
 }
 
 void GDScriptByteCodeGenerator::write_and_right_operand(const Address &p_right_operand) {
-	append(GDScriptFunction::OPCODE_JUMP_IF_NOT);
+	append(GDScriptFunction::OPCODE_JUMP_IF_NOT, 1);
 	append(p_right_operand);
 	logic_op_jump_pos2.push_back(opcodes.size());
 	append(0); // Jump target, will be patched.
@@ -216,29 +356,29 @@ void GDScriptByteCodeGenerator::write_and_right_operand(const Address &p_right_o
 
 void GDScriptByteCodeGenerator::write_end_and(const Address &p_target) {
 	// If here means both operands are true.
-	append(GDScriptFunction::OPCODE_ASSIGN_TRUE);
+	append(GDScriptFunction::OPCODE_ASSIGN_TRUE, 1);
 	append(p_target);
 	// Jump away from the fail condition.
-	append(GDScriptFunction::OPCODE_JUMP);
+	append(GDScriptFunction::OPCODE_JUMP, 0);
 	append(opcodes.size() + 3);
 	// Here it means one of operands is false.
 	patch_jump(logic_op_jump_pos1.back()->get());
 	patch_jump(logic_op_jump_pos2.back()->get());
 	logic_op_jump_pos1.pop_back();
 	logic_op_jump_pos2.pop_back();
-	append(GDScriptFunction::OPCODE_ASSIGN_FALSE);
+	append(GDScriptFunction::OPCODE_ASSIGN_FALSE, 0);
 	append(p_target);
 }
 
 void GDScriptByteCodeGenerator::write_or_left_operand(const Address &p_left_operand) {
-	append(GDScriptFunction::OPCODE_JUMP_IF);
+	append(GDScriptFunction::OPCODE_JUMP_IF, 1);
 	append(p_left_operand);
 	logic_op_jump_pos1.push_back(opcodes.size());
 	append(0); // Jump target, will be patched.
 }
 
 void GDScriptByteCodeGenerator::write_or_right_operand(const Address &p_right_operand) {
-	append(GDScriptFunction::OPCODE_JUMP_IF);
+	append(GDScriptFunction::OPCODE_JUMP_IF, 1);
 	append(p_right_operand);
 	logic_op_jump_pos2.push_back(opcodes.size());
 	append(0); // Jump target, will be patched.
@@ -246,17 +386,17 @@ void GDScriptByteCodeGenerator::write_or_right_operand(const Address &p_right_op
 
 void GDScriptByteCodeGenerator::write_end_or(const Address &p_target) {
 	// If here means both operands are false.
-	append(GDScriptFunction::OPCODE_ASSIGN_FALSE);
+	append(GDScriptFunction::OPCODE_ASSIGN_FALSE, 1);
 	append(p_target);
 	// Jump away from the success condition.
-	append(GDScriptFunction::OPCODE_JUMP);
+	append(GDScriptFunction::OPCODE_JUMP, 0);
 	append(opcodes.size() + 3);
 	// Here it means one of operands is false.
 	patch_jump(logic_op_jump_pos1.back()->get());
 	patch_jump(logic_op_jump_pos2.back()->get());
 	logic_op_jump_pos1.pop_back();
 	logic_op_jump_pos2.pop_back();
-	append(GDScriptFunction::OPCODE_ASSIGN_TRUE);
+	append(GDScriptFunction::OPCODE_ASSIGN_TRUE, 1);
 	append(p_target);
 }
 
@@ -265,18 +405,18 @@ void GDScriptByteCodeGenerator::write_start_ternary(const Address &p_target) {
 }
 
 void GDScriptByteCodeGenerator::write_ternary_condition(const Address &p_condition) {
-	append(GDScriptFunction::OPCODE_JUMP_IF_NOT);
+	append(GDScriptFunction::OPCODE_JUMP_IF_NOT, 1);
 	append(p_condition);
 	ternary_jump_fail_pos.push_back(opcodes.size());
 	append(0); // Jump target, will be patched.
 }
 
 void GDScriptByteCodeGenerator::write_ternary_true_expr(const Address &p_expr) {
-	append(GDScriptFunction::OPCODE_ASSIGN);
+	append(GDScriptFunction::OPCODE_ASSIGN, 2);
 	append(ternary_result.back()->get());
 	append(p_expr);
 	// Jump away from the false path.
-	append(GDScriptFunction::OPCODE_JUMP);
+	append(GDScriptFunction::OPCODE_JUMP, 0);
 	ternary_jump_skip_pos.push_back(opcodes.size());
 	append(0);
 	// Fail must jump here.
@@ -285,7 +425,7 @@ void GDScriptByteCodeGenerator::write_ternary_true_expr(const Address &p_expr) {
 }
 
 void GDScriptByteCodeGenerator::write_ternary_false_expr(const Address &p_expr) {
-	append(GDScriptFunction::OPCODE_ASSIGN);
+	append(GDScriptFunction::OPCODE_ASSIGN, 2);
 	append(ternary_result.back()->get());
 	append(p_expr);
 }
@@ -296,43 +436,100 @@ void GDScriptByteCodeGenerator::write_end_ternary() {
 }
 
 void GDScriptByteCodeGenerator::write_set(const Address &p_target, const Address &p_index, const Address &p_source) {
-	append(GDScriptFunction::OPCODE_SET);
+	if (HAS_BUILTIN_TYPE(p_target)) {
+		if (IS_BUILTIN_TYPE(p_index, Variant::INT) && Variant::get_member_validated_indexed_setter(p_target.type.builtin_type)) {
+			// Use indexed setter instead.
+			Variant::ValidatedIndexedSetter setter = Variant::get_member_validated_indexed_setter(p_target.type.builtin_type);
+			append(GDScriptFunction::OPCODE_SET_INDEXED_VALIDATED, 3);
+			append(p_target);
+			append(p_index);
+			append(p_source);
+			append(setter);
+			return;
+		} else if (Variant::get_member_validated_keyed_setter(p_target.type.builtin_type)) {
+			Variant::ValidatedKeyedSetter setter = Variant::get_member_validated_keyed_setter(p_target.type.builtin_type);
+			append(GDScriptFunction::OPCODE_SET_KEYED_VALIDATED, 3);
+			append(p_target);
+			append(p_index);
+			append(p_source);
+			append(setter);
+			return;
+		}
+	}
+
+	append(GDScriptFunction::OPCODE_SET_KEYED, 3);
 	append(p_target);
 	append(p_index);
 	append(p_source);
 }
 
 void GDScriptByteCodeGenerator::write_get(const Address &p_target, const Address &p_index, const Address &p_source) {
-	append(GDScriptFunction::OPCODE_GET);
+	if (HAS_BUILTIN_TYPE(p_source)) {
+		if (IS_BUILTIN_TYPE(p_index, Variant::INT) && Variant::get_member_validated_indexed_getter(p_source.type.builtin_type)) {
+			// Use indexed getter instead.
+			Variant::ValidatedIndexedGetter getter = Variant::get_member_validated_indexed_getter(p_source.type.builtin_type);
+			append(GDScriptFunction::OPCODE_GET_INDEXED_VALIDATED, 3);
+			append(p_source);
+			append(p_index);
+			append(p_target);
+			append(getter);
+			return;
+		} else if (Variant::get_member_validated_keyed_getter(p_source.type.builtin_type)) {
+			Variant::ValidatedKeyedGetter getter = Variant::get_member_validated_keyed_getter(p_source.type.builtin_type);
+			append(GDScriptFunction::OPCODE_GET_KEYED_VALIDATED, 3);
+			append(p_source);
+			append(p_index);
+			append(p_target);
+			append(getter);
+			return;
+		}
+	}
+	append(GDScriptFunction::OPCODE_GET_KEYED, 3);
 	append(p_source);
 	append(p_index);
 	append(p_target);
 }
 
 void GDScriptByteCodeGenerator::write_set_named(const Address &p_target, const StringName &p_name, const Address &p_source) {
-	append(GDScriptFunction::OPCODE_SET_NAMED);
+	if (HAS_BUILTIN_TYPE(p_target) && Variant::get_member_validated_setter(p_target.type.builtin_type, p_name)) {
+		Variant::ValidatedSetter setter = Variant::get_member_validated_setter(p_target.type.builtin_type, p_name);
+		append(GDScriptFunction::OPCODE_SET_NAMED_VALIDATED, 2);
+		append(p_target);
+		append(p_source);
+		append(setter);
+		return;
+	}
+	append(GDScriptFunction::OPCODE_SET_NAMED, 2);
 	append(p_target);
-	append(p_name);
 	append(p_source);
+	append(p_name);
 }
 
 void GDScriptByteCodeGenerator::write_get_named(const Address &p_target, const StringName &p_name, const Address &p_source) {
-	append(GDScriptFunction::OPCODE_GET_NAMED);
+	if (HAS_BUILTIN_TYPE(p_source) && Variant::get_member_validated_getter(p_source.type.builtin_type, p_name)) {
+		Variant::ValidatedGetter getter = Variant::get_member_validated_getter(p_source.type.builtin_type, p_name);
+		append(GDScriptFunction::OPCODE_GET_NAMED_VALIDATED, 2);
+		append(p_source);
+		append(p_target);
+		append(getter);
+		return;
+	}
+	append(GDScriptFunction::OPCODE_GET_NAMED, 2);
 	append(p_source);
-	append(p_name);
 	append(p_target);
+	append(p_name);
 }
 
 void GDScriptByteCodeGenerator::write_set_member(const Address &p_value, const StringName &p_name) {
-	append(GDScriptFunction::OPCODE_SET_MEMBER);
-	append(p_name);
+	append(GDScriptFunction::OPCODE_SET_MEMBER, 1);
 	append(p_value);
+	append(p_name);
 }
 
 void GDScriptByteCodeGenerator::write_get_member(const Address &p_target, const StringName &p_name) {
-	append(GDScriptFunction::OPCODE_GET_MEMBER);
-	append(p_name);
+	append(GDScriptFunction::OPCODE_GET_MEMBER, 1);
 	append(p_target);
+	append(p_name);
 }
 
 void GDScriptByteCodeGenerator::write_assign(const Address &p_target, const Address &p_source) {
@@ -340,34 +537,35 @@ void GDScriptByteCodeGenerator::write_assign(const Address &p_target, const Addr
 		// Typed assignment.
 		switch (p_target.type.kind) {
 			case GDScriptDataType::BUILTIN: {
-				append(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN);
-				append(p_target.type.builtin_type);
+				append(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN, 2);
 				append(p_target);
 				append(p_source);
+				append(p_target.type.builtin_type);
 			} break;
 			case GDScriptDataType::NATIVE: {
 				int class_idx = GDScriptLanguage::get_singleton()->get_global_map()[p_target.type.native_type];
 				class_idx |= (GDScriptFunction::ADDR_TYPE_GLOBAL << GDScriptFunction::ADDR_BITS);
-				append(GDScriptFunction::OPCODE_ASSIGN_TYPED_NATIVE);
-				append(class_idx);
+				append(GDScriptFunction::OPCODE_ASSIGN_TYPED_NATIVE, 3);
 				append(p_target);
 				append(p_source);
+				append(class_idx);
 			} break;
 			case GDScriptDataType::SCRIPT:
 			case GDScriptDataType::GDSCRIPT: {
 				Variant script = p_target.type.script_type;
 				int idx = get_constant_pos(script);
+				idx |= (GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS);
 
-				append(GDScriptFunction::OPCODE_ASSIGN_TYPED_SCRIPT);
-				append(idx);
+				append(GDScriptFunction::OPCODE_ASSIGN_TYPED_SCRIPT, 3);
 				append(p_target);
 				append(p_source);
+				append(idx);
 			} break;
 			default: {
 				ERR_PRINT("Compiler bug: unresolved assign.");
 
 				// Shouldn't get here, but fail-safe to a regular assignment
-				append(GDScriptFunction::OPCODE_ASSIGN);
+				append(GDScriptFunction::OPCODE_ASSIGN, 2);
 				append(p_target);
 				append(p_source);
 			}
@@ -375,13 +573,13 @@ void GDScriptByteCodeGenerator::write_assign(const Address &p_target, const Addr
 	} else {
 		if (p_target.type.kind == GDScriptDataType::BUILTIN && p_source.type.kind == GDScriptDataType::BUILTIN && p_target.type.builtin_type != p_source.type.builtin_type) {
 			// Need conversion..
-			append(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN);
-			append(p_target.type.builtin_type);
+			append(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN, 2);
 			append(p_target);
 			append(p_source);
+			append(p_target.type.builtin_type);
 		} else {
 			// Either untyped assignment or already type-checked by the parser
-			append(GDScriptFunction::OPCODE_ASSIGN);
+			append(GDScriptFunction::OPCODE_ASSIGN, 2);
 			append(p_target);
 			append(p_source);
 		}
@@ -389,34 +587,37 @@ void GDScriptByteCodeGenerator::write_assign(const Address &p_target, const Addr
 }
 
 void GDScriptByteCodeGenerator::write_assign_true(const Address &p_target) {
-	append(GDScriptFunction::OPCODE_ASSIGN_TRUE);
+	append(GDScriptFunction::OPCODE_ASSIGN_TRUE, 1);
 	append(p_target);
 }
 
 void GDScriptByteCodeGenerator::write_assign_false(const Address &p_target) {
-	append(GDScriptFunction::OPCODE_ASSIGN_FALSE);
+	append(GDScriptFunction::OPCODE_ASSIGN_FALSE, 1);
 	append(p_target);
 }
 
 void GDScriptByteCodeGenerator::write_cast(const Address &p_target, const Address &p_source, const GDScriptDataType &p_type) {
+	int index = 0;
+
 	switch (p_type.kind) {
 		case GDScriptDataType::BUILTIN: {
-			append(GDScriptFunction::OPCODE_CAST_TO_BUILTIN);
-			append(p_type.builtin_type);
+			append(GDScriptFunction::OPCODE_CAST_TO_BUILTIN, 2);
+			index = p_type.builtin_type;
 		} break;
 		case GDScriptDataType::NATIVE: {
 			int class_idx = GDScriptLanguage::get_singleton()->get_global_map()[p_type.native_type];
 			class_idx |= (GDScriptFunction::ADDR_TYPE_GLOBAL << GDScriptFunction::ADDR_BITS);
-			append(GDScriptFunction::OPCODE_CAST_TO_NATIVE);
-			append(class_idx);
+			append(GDScriptFunction::OPCODE_CAST_TO_NATIVE, 3);
+			index = class_idx;
 		} break;
 		case GDScriptDataType::SCRIPT:
 		case GDScriptDataType::GDSCRIPT: {
 			Variant script = p_type.script_type;
 			int idx = get_constant_pos(script);
+			idx |= (GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS);
 
-			append(GDScriptFunction::OPCODE_CAST_TO_SCRIPT);
-			append(idx);
+			append(GDScriptFunction::OPCODE_CAST_TO_SCRIPT, 3);
+			index = idx;
 		} break;
 		default: {
 			return;
@@ -425,147 +626,272 @@ void GDScriptByteCodeGenerator::write_cast(const Address &p_target, const Addres
 
 	append(p_source);
 	append(p_target);
+	append(index);
 }
 
 void GDScriptByteCodeGenerator::write_call(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) {
-	append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN);
-	append(p_arguments.size());
-	append(p_base);
-	append(p_function_name);
+	append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN, 2 + p_arguments.size());
 	for (int i = 0; i < p_arguments.size(); i++) {
 		append(p_arguments[i]);
 	}
+	append(p_base);
 	append(p_target);
-	alloc_call(p_arguments.size());
+	append(p_arguments.size());
+	append(p_function_name);
 }
 
 void GDScriptByteCodeGenerator::write_super_call(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) {
-	append(GDScriptFunction::OPCODE_CALL_SELF_BASE);
-	append(p_function_name);
-	append(p_arguments.size());
+	append(GDScriptFunction::OPCODE_CALL_SELF_BASE, 1 + p_arguments.size());
 	for (int i = 0; i < p_arguments.size(); i++) {
 		append(p_arguments[i]);
 	}
 	append(p_target);
-	alloc_call(p_arguments.size());
+	append(p_arguments.size());
+	append(p_function_name);
 }
 
 void GDScriptByteCodeGenerator::write_call_async(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) {
-	append(GDScriptFunction::OPCODE_CALL_ASYNC);
-	append(p_arguments.size());
-	append(p_base);
-	append(p_function_name);
+	append(GDScriptFunction::OPCODE_CALL_ASYNC, 2 + p_arguments.size());
 	for (int i = 0; i < p_arguments.size(); i++) {
 		append(p_arguments[i]);
 	}
+	append(p_base);
 	append(p_target);
-	alloc_call(p_arguments.size());
+	append(p_arguments.size());
+	append(p_function_name);
 }
 
 void GDScriptByteCodeGenerator::write_call_builtin(const Address &p_target, GDScriptFunctions::Function p_function, const Vector<Address> &p_arguments) {
-	append(GDScriptFunction::OPCODE_CALL_BUILT_IN);
-	append(p_function);
-	append(p_arguments.size());
+	append(GDScriptFunction::OPCODE_CALL_BUILT_IN, 1 + p_arguments.size());
 	for (int i = 0; i < p_arguments.size(); i++) {
 		append(p_arguments[i]);
 	}
 	append(p_target);
-	alloc_call(p_arguments.size());
+	append(p_arguments.size());
+	append(p_function);
 }
 
-void GDScriptByteCodeGenerator::write_call_method_bind(const Address &p_target, const Address &p_base, const MethodBind *p_method, const Vector<Address> &p_arguments) {
-	append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN);
-	append(p_arguments.size());
-	append(p_base);
-	append(p_method->get_name());
+void GDScriptByteCodeGenerator::write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) {
+	bool is_validated = false;
+
+	// Check if all types are correct.
+	if (Variant::is_builtin_method_vararg(p_type, p_method)) {
+		is_validated = true; // Vararg works fine with any argument, since they can be any type.
+	} else if (p_arguments.size() == Variant::get_builtin_method_argument_count(p_type, p_method)) {
+		bool all_types_exact = true;
+		for (int i = 0; i < p_arguments.size(); i++) {
+			if (!IS_BUILTIN_TYPE(p_arguments[i], Variant::get_builtin_method_argument_type(p_type, p_method, i))) {
+				all_types_exact = false;
+				break;
+			}
+		}
+
+		is_validated = all_types_exact;
+	}
+
+	if (!is_validated) {
+		// Perform regular call.
+		write_call(p_target, p_base, p_method, p_arguments);
+		return;
+	}
+
+	append(GDScriptFunction::OPCODE_CALL_BUILTIN_TYPE_VALIDATED, 2 + p_arguments.size());
+
 	for (int i = 0; i < p_arguments.size(); i++) {
 		append(p_arguments[i]);
 	}
+	append(p_base);
 	append(p_target);
-	alloc_call(p_arguments.size());
+	append(p_arguments.size());
+	append(Variant::get_validated_builtin_method(p_type, p_method));
 }
 
-void GDScriptByteCodeGenerator::write_call_ptrcall(const Address &p_target, const Address &p_base, const MethodBind *p_method, const Vector<Address> &p_arguments) {
-	append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN);
-	append(p_arguments.size());
+void GDScriptByteCodeGenerator::write_call_method_bind(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) {
+	append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL_METHOD_BIND : GDScriptFunction::OPCODE_CALL_METHOD_BIND_RET, 2 + p_arguments.size());
+	for (int i = 0; i < p_arguments.size(); i++) {
+		append(p_arguments[i]);
+	}
 	append(p_base);
-	append(p_method->get_name());
+	append(p_target);
+	append(p_arguments.size());
+	append(p_method);
+}
+
+void GDScriptByteCodeGenerator::write_call_ptrcall(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) {
+#define CASE_TYPE(m_type)                                                               \
+	case Variant::m_type:                                                               \
+		append(GDScriptFunction::OPCODE_CALL_PTRCALL_##m_type, 2 + p_arguments.size()); \
+		break
+
+	bool is_ptrcall = true;
+
+	if (p_method->has_return()) {
+		MethodInfo info;
+		ClassDB::get_method_info(p_method->get_instance_class(), p_method->get_name(), &info);
+		switch (info.return_val.type) {
+			CASE_TYPE(BOOL);
+			CASE_TYPE(INT);
+			CASE_TYPE(FLOAT);
+			CASE_TYPE(STRING);
+			CASE_TYPE(VECTOR2);
+			CASE_TYPE(VECTOR2I);
+			CASE_TYPE(RECT2);
+			CASE_TYPE(RECT2I);
+			CASE_TYPE(VECTOR3);
+			CASE_TYPE(VECTOR3I);
+			CASE_TYPE(TRANSFORM2D);
+			CASE_TYPE(PLANE);
+			CASE_TYPE(AABB);
+			CASE_TYPE(BASIS);
+			CASE_TYPE(TRANSFORM);
+			CASE_TYPE(COLOR);
+			CASE_TYPE(STRING_NAME);
+			CASE_TYPE(NODE_PATH);
+			CASE_TYPE(RID);
+			CASE_TYPE(QUAT);
+			CASE_TYPE(OBJECT);
+			CASE_TYPE(CALLABLE);
+			CASE_TYPE(SIGNAL);
+			CASE_TYPE(DICTIONARY);
+			CASE_TYPE(ARRAY);
+			CASE_TYPE(PACKED_BYTE_ARRAY);
+			CASE_TYPE(PACKED_INT32_ARRAY);
+			CASE_TYPE(PACKED_INT64_ARRAY);
+			CASE_TYPE(PACKED_FLOAT32_ARRAY);
+			CASE_TYPE(PACKED_FLOAT64_ARRAY);
+			CASE_TYPE(PACKED_STRING_ARRAY);
+			CASE_TYPE(PACKED_VECTOR2_ARRAY);
+			CASE_TYPE(PACKED_VECTOR3_ARRAY);
+			CASE_TYPE(PACKED_COLOR_ARRAY);
+			default:
+				append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL_METHOD_BIND : GDScriptFunction::OPCODE_CALL_METHOD_BIND_RET, 2 + p_arguments.size());
+				is_ptrcall = false;
+				break;
+		}
+	} else {
+		append(GDScriptFunction::OPCODE_CALL_PTRCALL_NO_RETURN, 2 + p_arguments.size());
+	}
+
 	for (int i = 0; i < p_arguments.size(); i++) {
 		append(p_arguments[i]);
 	}
+	append(p_base);
 	append(p_target);
-	alloc_call(p_arguments.size());
+	append(p_arguments.size());
+	append(p_method);
+	if (is_ptrcall) {
+		alloc_ptrcall(p_arguments.size());
+	}
+
+#undef CASE_TYPE
 }
 
 void GDScriptByteCodeGenerator::write_call_self(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) {
-	append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN);
-	append(p_arguments.size());
-	append(GDScriptFunction::ADDR_TYPE_SELF << GDScriptFunction::ADDR_BITS);
-	append(p_function_name);
+	append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN, 2 + p_arguments.size());
 	for (int i = 0; i < p_arguments.size(); i++) {
 		append(p_arguments[i]);
 	}
+	append(GDScriptFunction::ADDR_TYPE_SELF << GDScriptFunction::ADDR_BITS);
 	append(p_target);
-	alloc_call(p_arguments.size());
+	append(p_arguments.size());
+	append(p_function_name);
 }
 
 void GDScriptByteCodeGenerator::write_call_script_function(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) {
-	append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN);
-	append(p_arguments.size());
-	append(p_base);
-	append(p_function_name);
+	append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN, 2 + p_arguments.size());
 	for (int i = 0; i < p_arguments.size(); i++) {
 		append(p_arguments[i]);
 	}
+	append(p_base);
 	append(p_target);
-	alloc_call(p_arguments.size());
+	append(p_arguments.size());
+	append(p_function_name);
 }
 
 void GDScriptByteCodeGenerator::write_construct(const Address &p_target, Variant::Type p_type, const Vector<Address> &p_arguments) {
-	append(GDScriptFunction::OPCODE_CONSTRUCT);
-	append(p_type);
-	append(p_arguments.size());
+	// Try to find an appropriate constructor.
+	bool all_have_type = true;
+	Vector<Variant::Type> arg_types;
+	for (int i = 0; i < p_arguments.size(); i++) {
+		if (!HAS_BUILTIN_TYPE(p_arguments[i])) {
+			all_have_type = false;
+			break;
+		}
+		arg_types.push_back(p_arguments[i].type.builtin_type);
+	}
+	if (all_have_type) {
+		int valid_constructor = -1;
+		for (int i = 0; i < Variant::get_constructor_count(p_type); i++) {
+			if (Variant::get_constructor_argument_count(p_type, i) != p_arguments.size()) {
+				continue;
+			}
+			int types_correct = true;
+			for (int j = 0; j < arg_types.size(); j++) {
+				if (arg_types[j] != Variant::get_constructor_argument_type(p_type, i, j)) {
+					types_correct = false;
+					break;
+				}
+			}
+			if (types_correct) {
+				valid_constructor = i;
+				break;
+			}
+		}
+		if (valid_constructor >= 0) {
+			append(GDScriptFunction::OPCODE_CONSTRUCT_VALIDATED, 1 + p_arguments.size());
+			for (int i = 0; i < p_arguments.size(); i++) {
+				append(p_arguments[i]);
+			}
+			append(p_target);
+			append(p_arguments.size());
+			append(Variant::get_validated_constructor(p_type, valid_constructor));
+			return;
+		}
+	}
+
+	append(GDScriptFunction::OPCODE_CONSTRUCT, 1 + p_arguments.size());
 	for (int i = 0; i < p_arguments.size(); i++) {
 		append(p_arguments[i]);
 	}
 	append(p_target);
-	alloc_call(p_arguments.size());
+	append(p_arguments.size());
+	append(p_type);
 }
 
 void GDScriptByteCodeGenerator::write_construct_array(const Address &p_target, const Vector<Address> &p_arguments) {
-	append(GDScriptFunction::OPCODE_CONSTRUCT_ARRAY);
-	append(p_arguments.size());
+	append(GDScriptFunction::OPCODE_CONSTRUCT_ARRAY, 1 + p_arguments.size());
 	for (int i = 0; i < p_arguments.size(); i++) {
 		append(p_arguments[i]);
 	}
 	append(p_target);
+	append(p_arguments.size());
 }
 
 void GDScriptByteCodeGenerator::write_construct_dictionary(const Address &p_target, const Vector<Address> &p_arguments) {
-	append(GDScriptFunction::OPCODE_CONSTRUCT_DICTIONARY);
-	append(p_arguments.size() / 2); // This is number of key-value pairs, so only half of actual arguments.
+	append(GDScriptFunction::OPCODE_CONSTRUCT_DICTIONARY, 1 + p_arguments.size());
 	for (int i = 0; i < p_arguments.size(); i++) {
 		append(p_arguments[i]);
 	}
 	append(p_target);
+	append(p_arguments.size() / 2); // This is number of key-value pairs, so only half of actual arguments.
 }
 
 void GDScriptByteCodeGenerator::write_await(const Address &p_target, const Address &p_operand) {
-	append(GDScriptFunction::OPCODE_AWAIT);
+	append(GDScriptFunction::OPCODE_AWAIT, 1);
 	append(p_operand);
-	append(GDScriptFunction::OPCODE_AWAIT_RESUME);
+	append(GDScriptFunction::OPCODE_AWAIT_RESUME, 1);
 	append(p_target);
 }
 
 void GDScriptByteCodeGenerator::write_if(const Address &p_condition) {
-	append(GDScriptFunction::OPCODE_JUMP_IF_NOT);
+	append(GDScriptFunction::OPCODE_JUMP_IF_NOT, 1);
 	append(p_condition);
 	if_jmp_addrs.push_back(opcodes.size());
 	append(0); // Jump destination, will be patched.
 }
 
 void GDScriptByteCodeGenerator::write_else() {
-	append(GDScriptFunction::OPCODE_JUMP); // Jump from true if block;
+	append(GDScriptFunction::OPCODE_JUMP, 0); // Jump from true if block;
 	int else_jmp_addr = opcodes.size();
 	append(0); // Jump destination, will be patched.
 
@@ -586,34 +912,121 @@ void GDScriptByteCodeGenerator::write_for(const Address &p_variable, const Addre
 	current_breaks_to_patch.push_back(List<int>());
 
 	// Assign container.
-	append(GDScriptFunction::OPCODE_ASSIGN);
+	append(GDScriptFunction::OPCODE_ASSIGN, 2);
 	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);
+	append(begin_opcode, 3);
 	append(counter_pos);
 	append(container_pos);
+	append(p_variable);
 	for_jmp_addrs.push_back(opcodes.size());
 	append(0); // End of loop address, will be patched.
-	append(p_variable);
-	append(GDScriptFunction::OPCODE_JUMP);
+	append(GDScriptFunction::OPCODE_JUMP, 0);
 	append(opcodes.size() + 6); // Skip over 'continue' code.
 
 	// Next iteration.
 	int continue_addr = opcodes.size();
 	continue_addrs.push_back(continue_addr);
-	append(GDScriptFunction::OPCODE_ITERATE);
+	append(iterate_opcode, 3);
 	append(counter_pos);
 	append(container_pos);
+	append(p_variable);
 	for_jmp_addrs.push_back(opcodes.size());
 	append(0); // Jump destination, will be patched.
-	append(p_variable);
 }
 
 void GDScriptByteCodeGenerator::write_endfor() {
 	// Jump back to loop check.
-	append(GDScriptFunction::OPCODE_JUMP);
+	append(GDScriptFunction::OPCODE_JUMP, 0);
 	append(continue_addrs.back()->get());
 	continue_addrs.pop_back();
 
@@ -641,7 +1054,7 @@ void GDScriptByteCodeGenerator::start_while_condition() {
 
 void GDScriptByteCodeGenerator::write_while(const Address &p_condition) {
 	// Condition check.
-	append(GDScriptFunction::OPCODE_JUMP_IF_NOT);
+	append(GDScriptFunction::OPCODE_JUMP_IF_NOT, 1);
 	append(p_condition);
 	while_jmp_addrs.push_back(opcodes.size());
 	append(0); // End of loop address, will be patched.
@@ -649,7 +1062,7 @@ void GDScriptByteCodeGenerator::write_while(const Address &p_condition) {
 
 void GDScriptByteCodeGenerator::write_endwhile() {
 	// Jump back to loop check.
-	append(GDScriptFunction::OPCODE_JUMP);
+	append(GDScriptFunction::OPCODE_JUMP, 0);
 	append(continue_addrs.back()->get());
 	continue_addrs.pop_back();
 
@@ -687,39 +1100,39 @@ void GDScriptByteCodeGenerator::end_match() {
 }
 
 void GDScriptByteCodeGenerator::write_break() {
-	append(GDScriptFunction::OPCODE_JUMP);
+	append(GDScriptFunction::OPCODE_JUMP, 0);
 	current_breaks_to_patch.back()->get().push_back(opcodes.size());
 	append(0);
 }
 
 void GDScriptByteCodeGenerator::write_continue() {
-	append(GDScriptFunction::OPCODE_JUMP);
+	append(GDScriptFunction::OPCODE_JUMP, 0);
 	append(continue_addrs.back()->get());
 }
 
 void GDScriptByteCodeGenerator::write_continue_match() {
-	append(GDScriptFunction::OPCODE_JUMP);
+	append(GDScriptFunction::OPCODE_JUMP, 0);
 	match_continues_to_patch.back()->get().push_back(opcodes.size());
 	append(0);
 }
 
 void GDScriptByteCodeGenerator::write_breakpoint() {
-	append(GDScriptFunction::OPCODE_BREAKPOINT);
+	append(GDScriptFunction::OPCODE_BREAKPOINT, 0);
 }
 
 void GDScriptByteCodeGenerator::write_newline(int p_line) {
-	append(GDScriptFunction::OPCODE_LINE);
+	append(GDScriptFunction::OPCODE_LINE, 0);
 	append(p_line);
 	current_line = p_line;
 }
 
 void GDScriptByteCodeGenerator::write_return(const Address &p_return_value) {
-	append(GDScriptFunction::OPCODE_RETURN);
+	append(GDScriptFunction::OPCODE_RETURN, 1);
 	append(p_return_value);
 }
 
 void GDScriptByteCodeGenerator::write_assert(const Address &p_test, const Address &p_message) {
-	append(GDScriptFunction::OPCODE_ASSERT);
+	append(GDScriptFunction::OPCODE_ASSERT, 2);
 	append(p_test);
 	append(p_message);
 }

+ 154 - 12
modules/gdscript/gdscript_byte_codegen.h

@@ -33,6 +33,8 @@
 
 #include "gdscript_codegen.h"
 
+#include "gdscript_function.h"
+
 class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
 	bool ended = false;
 	GDScriptFunction *function = nullptr;
@@ -49,15 +51,26 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
 
 	int current_stack_size = 0;
 	int current_temporaries = 0;
+	int current_line = 0;
+	int stack_max = 0;
+	int instr_args_max = 0;
+	int ptrcall_max = 0;
 
 	HashMap<Variant, int, VariantHasher, VariantComparator> constant_map;
 	Map<StringName, int> name_map;
 #ifdef TOOLS_ENABLED
 	Vector<StringName> named_globals;
 #endif
-	int current_line = 0;
-	int stack_max = 0;
-	int call_max = 0;
+	Map<Variant::ValidatedOperatorEvaluator, int> operator_func_map;
+	Map<Variant::ValidatedSetter, int> setters_map;
+	Map<Variant::ValidatedGetter, int> getters_map;
+	Map<Variant::ValidatedKeyedSetter, int> keyed_setters_map;
+	Map<Variant::ValidatedKeyedGetter, int> keyed_getters_map;
+	Map<Variant::ValidatedIndexedSetter, int> indexed_setters_map;
+	Map<Variant::ValidatedIndexedGetter, int> indexed_getters_map;
+	Map<Variant::ValidatedBuiltInMethod, int> builtin_method_map;
+	Map<Variant::ValidatedConstructor, int> constructors_map;
+	Map<MethodBind *, int> method_bind_map;
 
 	List<int> if_jmp_addrs; // List since this can be nested.
 	List<int> for_jmp_addrs;
@@ -134,22 +147,105 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
 		return pos;
 	}
 
+	int get_operation_pos(const Variant::ValidatedOperatorEvaluator p_operation) {
+		if (operator_func_map.has(p_operation))
+			return operator_func_map[p_operation];
+		int pos = operator_func_map.size();
+		operator_func_map[p_operation] = pos;
+		return pos;
+	}
+
+	int get_setter_pos(const Variant::ValidatedSetter p_setter) {
+		if (setters_map.has(p_setter))
+			return setters_map[p_setter];
+		int pos = setters_map.size();
+		setters_map[p_setter] = pos;
+		return pos;
+	}
+
+	int get_getter_pos(const Variant::ValidatedGetter p_getter) {
+		if (getters_map.has(p_getter))
+			return getters_map[p_getter];
+		int pos = getters_map.size();
+		getters_map[p_getter] = pos;
+		return pos;
+	}
+
+	int get_keyed_setter_pos(const Variant::ValidatedKeyedSetter p_keyed_setter) {
+		if (keyed_setters_map.has(p_keyed_setter))
+			return keyed_setters_map[p_keyed_setter];
+		int pos = keyed_setters_map.size();
+		keyed_setters_map[p_keyed_setter] = pos;
+		return pos;
+	}
+
+	int get_keyed_getter_pos(const Variant::ValidatedKeyedGetter p_keyed_getter) {
+		if (keyed_getters_map.has(p_keyed_getter))
+			return keyed_getters_map[p_keyed_getter];
+		int pos = keyed_getters_map.size();
+		keyed_getters_map[p_keyed_getter] = pos;
+		return pos;
+	}
+
+	int get_indexed_setter_pos(const Variant::ValidatedIndexedSetter p_indexed_setter) {
+		if (indexed_setters_map.has(p_indexed_setter))
+			return indexed_setters_map[p_indexed_setter];
+		int pos = indexed_setters_map.size();
+		indexed_setters_map[p_indexed_setter] = pos;
+		return pos;
+	}
+
+	int get_indexed_getter_pos(const Variant::ValidatedIndexedGetter p_indexed_getter) {
+		if (indexed_getters_map.has(p_indexed_getter))
+			return indexed_getters_map[p_indexed_getter];
+		int pos = indexed_getters_map.size();
+		indexed_getters_map[p_indexed_getter] = pos;
+		return pos;
+	}
+
+	int get_builtin_method_pos(const Variant::ValidatedBuiltInMethod p_method) {
+		if (builtin_method_map.has(p_method)) {
+			return builtin_method_map[p_method];
+		}
+		int pos = builtin_method_map.size();
+		builtin_method_map[p_method] = pos;
+		return pos;
+	}
+
+	int get_constructor_pos(const Variant::ValidatedConstructor p_constructor) {
+		if (constructors_map.has(p_constructor)) {
+			return constructors_map[p_constructor];
+		}
+		int pos = constructors_map.size();
+		constructors_map[p_constructor] = pos;
+		return pos;
+	}
+
+	int get_method_bind_pos(MethodBind *p_method) {
+		if (method_bind_map.has(p_method)) {
+			return method_bind_map[p_method];
+		}
+		int pos = method_bind_map.size();
+		method_bind_map[p_method] = pos;
+		return pos;
+	}
+
 	void alloc_stack(int p_level) {
 		if (p_level >= stack_max)
 			stack_max = p_level + 1;
 	}
 
-	void alloc_call(int p_params) {
-		if (p_params >= call_max)
-			call_max = p_params;
-	}
-
 	int increase_stack() {
 		int top = current_stack_size++;
 		alloc_stack(current_stack_size);
 		return top;
 	}
 
+	void alloc_ptrcall(int p_params) {
+		if (p_params >= ptrcall_max)
+			ptrcall_max = p_params;
+	}
+
 	int address_of(const Address &p_address) {
 		switch (p_address.mode) {
 			case Address::SELF:
@@ -177,8 +273,13 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
 		return -1; // Unreachable.
 	}
 
-	void append(int code) {
-		opcodes.push_back(code);
+	void append(GDScriptFunction::Opcode p_code, int p_argument_count) {
+		opcodes.push_back((p_code & GDScriptFunction::INSTR_MASK) | (p_argument_count << GDScriptFunction::INSTR_BITS));
+		instr_args_max = MAX(instr_args_max, p_argument_count);
+	}
+
+	void append(int p_code) {
+		opcodes.push_back(p_code);
 	}
 
 	void append(const Address &p_address) {
@@ -189,6 +290,46 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
 		opcodes.push_back(get_name_map_pos(p_name));
 	}
 
+	void append(const Variant::ValidatedOperatorEvaluator p_operation) {
+		opcodes.push_back(get_operation_pos(p_operation));
+	}
+
+	void append(const Variant::ValidatedSetter p_setter) {
+		opcodes.push_back(get_setter_pos(p_setter));
+	}
+
+	void append(const Variant::ValidatedGetter p_getter) {
+		opcodes.push_back(get_getter_pos(p_getter));
+	}
+
+	void append(const Variant::ValidatedKeyedSetter p_keyed_setter) {
+		opcodes.push_back(get_keyed_setter_pos(p_keyed_setter));
+	}
+
+	void append(const Variant::ValidatedKeyedGetter p_keyed_getter) {
+		opcodes.push_back(get_keyed_getter_pos(p_keyed_getter));
+	}
+
+	void append(const Variant::ValidatedIndexedSetter p_indexed_setter) {
+		opcodes.push_back(get_indexed_setter_pos(p_indexed_setter));
+	}
+
+	void append(const Variant::ValidatedIndexedGetter p_indexed_getter) {
+		opcodes.push_back(get_indexed_getter_pos(p_indexed_getter));
+	}
+
+	void append(const Variant::ValidatedBuiltInMethod p_method) {
+		opcodes.push_back(get_builtin_method_pos(p_method));
+	}
+
+	void append(const Variant::ValidatedConstructor p_constructor) {
+		opcodes.push_back(get_constructor_pos(p_constructor));
+	}
+
+	void append(MethodBind *p_method) {
+		opcodes.push_back(get_method_bind_pos(p_method));
+	}
+
 	void patch_jump(int p_address) {
 		opcodes.write[p_address] = opcodes.size();
 	}
@@ -244,8 +385,9 @@ public:
 	virtual void write_super_call(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
 	virtual void write_call_async(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
 	virtual void write_call_builtin(const Address &p_target, GDScriptFunctions::Function p_function, const Vector<Address> &p_arguments) override;
-	virtual void write_call_method_bind(const Address &p_target, const Address &p_base, const MethodBind *p_method, const Vector<Address> &p_arguments) override;
-	virtual void write_call_ptrcall(const Address &p_target, const Address &p_base, const MethodBind *p_method, const Vector<Address> &p_arguments) override;
+	virtual void write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) override;
+	virtual void write_call_method_bind(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) override;
+	virtual void write_call_ptrcall(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) override;
 	virtual void write_call_self(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
 	virtual void write_call_script_function(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
 	virtual void write_construct(const Address &p_target, Variant::Type p_type, const Vector<Address> &p_arguments) override;

+ 3 - 2
modules/gdscript/gdscript_codegen.h

@@ -126,8 +126,9 @@ public:
 	virtual void write_super_call(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
 	virtual void write_call_async(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
 	virtual void write_call_builtin(const Address &p_target, GDScriptFunctions::Function p_function, const Vector<Address> &p_arguments) = 0;
-	virtual void write_call_method_bind(const Address &p_target, const Address &p_base, const MethodBind *p_method, const Vector<Address> &p_arguments) = 0;
-	virtual void write_call_ptrcall(const Address &p_target, const Address &p_base, const MethodBind *p_method, const Vector<Address> &p_arguments) = 0;
+	virtual void write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) = 0;
+	virtual void write_call_method_bind(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) = 0;
+	virtual void write_call_ptrcall(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) = 0;
 	virtual void write_call_self(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
 	virtual void write_call_script_function(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
 	virtual void write_construct(const Address &p_target, Variant::Type p_type, const Vector<Address> &p_arguments) = 0;

+ 79 - 2
modules/gdscript/gdscript_compiler.cpp

@@ -158,6 +158,48 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
 	return result;
 }
 
+static bool _is_exact_type(const PropertyInfo &p_par_type, const GDScriptDataType &p_arg_type) {
+	if (!p_arg_type.has_type) {
+		return false;
+	}
+	if (p_par_type.type == Variant::NIL) {
+		return false;
+	}
+	if (p_par_type.type == Variant::OBJECT) {
+		if (p_arg_type.kind == GDScriptDataType::BUILTIN) {
+			return false;
+		}
+		StringName class_name;
+		if (p_arg_type.kind == GDScriptDataType::NATIVE) {
+			class_name = p_arg_type.native_type;
+		} else {
+			class_name = p_arg_type.native_type == StringName() ? p_arg_type.script_type->get_instance_base_type() : p_arg_type.native_type;
+		}
+		return p_par_type.class_name == class_name || ClassDB::is_parent_class(class_name, p_par_type.class_name);
+	} else {
+		if (p_arg_type.kind != GDScriptDataType::BUILTIN) {
+			return false;
+		}
+		return p_par_type.type == p_arg_type.builtin_type;
+	}
+}
+
+static bool _have_exact_arguments(const MethodBind *p_method, const Vector<GDScriptCodeGenerator::Address> &p_arguments) {
+	if (p_method->get_argument_count() != p_arguments.size()) {
+		// ptrcall won't work with default arguments.
+		return false;
+	}
+	MethodInfo info;
+	ClassDB::get_method_info(p_method->get_instance_class(), p_method->get_name(), &info);
+	for (int i = 0; i < p_arguments.size(); i++) {
+		const PropertyInfo &prop = info.arguments[i];
+		if (!_is_exact_type(prop, p_arguments[i].type)) {
+			return false;
+		}
+	}
+	return true;
+}
+
 GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &codegen, Error &r_error, const GDScriptParser::ExpressionNode *p_expression, bool p_root, bool p_initializer, const GDScriptCodeGenerator::Address &p_index_addr) {
 	if (p_expression->is_constant) {
 		return codegen.add_constant(p_expression->reduced_value);
@@ -430,7 +472,20 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
 				} else {
 					if (callee->type == GDScriptParser::Node::IDENTIFIER) {
 						// Self function call.
-						if ((codegen.function_node && codegen.function_node->is_static) || call->function_name == "new") {
+						if (ClassDB::has_method(codegen.script->native->get_name(), call->function_name)) {
+							// Native method, use faster path.
+							GDScriptCodeGenerator::Address self;
+							self.mode = GDScriptCodeGenerator::Address::SELF;
+							MethodBind *method = ClassDB::get_method(codegen.script->native->get_name(), call->function_name);
+
+							if (_have_exact_arguments(method, arguments)) {
+								// Exact arguments, use ptrcall.
+								gen->write_call_ptrcall(result, self, method, arguments);
+							} else {
+								// Not exact arguments, but still can use method bind call.
+								gen->write_call_method_bind(result, self, method, arguments);
+							}
+						} else if ((codegen.function_node && codegen.function_node->is_static) || call->function_name == "new") {
 							GDScriptCodeGenerator::Address self;
 							self.mode = GDScriptCodeGenerator::Address::CLASS;
 							gen->write_call(result, self, call->function_name, arguments);
@@ -447,6 +502,28 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
 							}
 							if (within_await) {
 								gen->write_call_async(result, base, call->function_name, arguments);
+							} else if (base.type.has_type && base.type.kind != GDScriptDataType::BUILTIN) {
+								// Native method, use faster path.
+								StringName class_name;
+								if (base.type.kind == GDScriptDataType::NATIVE) {
+									class_name = base.type.native_type;
+								} else {
+									class_name = base.type.native_type == StringName() ? base.type.script_type->get_instance_base_type() : base.type.native_type;
+								}
+								if (ClassDB::class_exists(class_name) && ClassDB::has_method(class_name, call->function_name)) {
+									MethodBind *method = ClassDB::get_method(class_name, call->function_name);
+									if (_have_exact_arguments(method, arguments)) {
+										// Exact arguments, use ptrcall.
+										gen->write_call_ptrcall(result, base, method, arguments);
+									} else {
+										// Not exact arguments, but still can use method bind call.
+										gen->write_call_method_bind(result, base, method, arguments);
+									}
+								} else {
+									gen->write_call(result, base, call->function_name, arguments);
+								}
+							} else if (base.type.has_type && base.type.kind == GDScriptDataType::BUILTIN) {
+								gen->write_call_builtin_type(result, base, base.type.builtin_type, call->function_name, arguments);
 							} else {
 								gen->write_call(result, base, call->function_name, arguments);
 							}
@@ -493,7 +570,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
 			GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(get_node->get_datatype()));
 
 			MethodBind *get_node_method = ClassDB::get_method("Node", "get_node");
-			gen->write_call_method_bind(result, GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::SELF), get_node_method, args);
+			gen->write_call_ptrcall(result, GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::SELF), get_node_method, args);
 
 			return result;
 		} break;

+ 813 - 0
modules/gdscript/gdscript_disassembler.cpp

@@ -0,0 +1,813 @@
+/*************************************************************************/
+/*  gdscript_disassembler.cpp                                            */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#ifdef DEBUG_ENABLED
+
+#include "gdscript_function.h"
+
+#include "core/string/string_builder.h"
+#include "gdscript.h"
+#include "gdscript_functions.h"
+
+static String _get_variant_string(const Variant &p_variant) {
+	String txt;
+	if (p_variant.get_type() == Variant::STRING) {
+		txt = "\"" + String(p_variant) + "\"";
+	} else if (p_variant.get_type() == Variant::STRING_NAME) {
+		txt = "&\"" + String(p_variant) + "\"";
+	} else if (p_variant.get_type() == Variant::NODE_PATH) {
+		txt = "^\"" + String(p_variant) + "\"";
+	} else if (p_variant.get_type() == Variant::OBJECT) {
+		Object *obj = p_variant;
+		if (!obj) {
+			txt = "null";
+		} else {
+			GDScriptNativeClass *cls = Object::cast_to<GDScriptNativeClass>(obj);
+			if (cls) {
+				txt += cls->get_name();
+				txt += " (class)";
+			} else {
+				txt = obj->get_class();
+				if (obj->get_script_instance()) {
+					txt += "(" + obj->get_script_instance()->get_script()->get_path() + ")";
+				}
+			}
+		}
+	} else {
+		txt = p_variant;
+	}
+	return txt;
+}
+
+static String _disassemble_address(const GDScript *p_script, const GDScriptFunction &p_function, int p_address) {
+	int addr = p_address & GDScriptFunction::ADDR_MASK;
+
+	switch (p_address >> GDScriptFunction::ADDR_BITS) {
+		case GDScriptFunction::ADDR_TYPE_SELF: {
+			return "self";
+		} break;
+		case GDScriptFunction::ADDR_TYPE_CLASS: {
+			return "class";
+		} break;
+		case GDScriptFunction::ADDR_TYPE_MEMBER: {
+			return "member(" + p_script->debug_get_member_by_index(addr) + ")";
+		} break;
+		case GDScriptFunction::ADDR_TYPE_CLASS_CONSTANT: {
+			return "class_const(" + p_function.get_global_name(addr) + ")";
+		} break;
+		case GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT: {
+			return "const(" + _get_variant_string(p_function.get_constant(addr)) + ")";
+		} break;
+		case GDScriptFunction::ADDR_TYPE_STACK: {
+			return "stack(" + itos(addr) + ")";
+		} break;
+		case GDScriptFunction::ADDR_TYPE_STACK_VARIABLE: {
+			return "var_stack(" + itos(addr) + ")";
+		} break;
+		case GDScriptFunction::ADDR_TYPE_GLOBAL: {
+			return "global(" + _get_variant_string(GDScriptLanguage::get_singleton()->get_global_array()[addr]) + ")";
+		} break;
+		case GDScriptFunction::ADDR_TYPE_NAMED_GLOBAL: {
+			return "named_global(" + p_function.get_global_name(addr) + ")";
+		} break;
+		case GDScriptFunction::ADDR_TYPE_NIL: {
+			return "nil";
+		} break;
+	}
+
+	return "<err>";
+}
+
+void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
+#define DADDR(m_ip) (_disassemble_address(_script, *this, _code_ptr[ip + m_ip]))
+
+	for (int ip = 0; ip < _code_size;) {
+		StringBuilder text;
+		int incr = 0;
+
+		text += " ";
+		text += itos(ip);
+		text += ": ";
+
+		// This makes the compiler complain if some opcode is unchecked in the switch.
+		Opcode code = Opcode(_code_ptr[ip] & INSTR_MASK);
+		int instr_var_args = (_code_ptr[ip] & INSTR_ARGS_MASK) >> INSTR_BITS;
+
+		switch (code) {
+			case OPCODE_OPERATOR: {
+				int operation = _code_ptr[ip + 4];
+
+				text += "operator ";
+
+				text += DADDR(3);
+				text += " = ";
+				text += DADDR(1);
+				text += " ";
+				text += Variant::get_operator_name(Variant::Operator(operation));
+				text += " ";
+				text += DADDR(2);
+
+				incr += 5;
+			} break;
+			case OPCODE_OPERATOR_VALIDATED: {
+				text += "validated operator ";
+
+				text += DADDR(3);
+				text += " = ";
+				text += DADDR(1);
+				text += " <operator function> ";
+				text += DADDR(2);
+
+				incr += 5;
+			} break;
+			case OPCODE_EXTENDS_TEST: {
+				text += "is object ";
+				text += DADDR(3);
+				text += " = ";
+				text += DADDR(1);
+				text += " is ";
+				text += DADDR(2);
+
+				incr += 4;
+			} break;
+			case OPCODE_IS_BUILTIN: {
+				text += "is builtin ";
+				text += DADDR(2);
+				text += " = ";
+				text += DADDR(1);
+				text += " is ";
+				text += Variant::get_type_name(Variant::Type(_code_ptr[ip + 3]));
+
+				incr += 4;
+			} break;
+			case OPCODE_SET_KEYED: {
+				text += "set keyed ";
+				text += DADDR(1);
+				text += "[";
+				text += DADDR(2);
+				text += "] = ";
+				text += DADDR(3);
+
+				incr += 4;
+			} break;
+			case OPCODE_SET_KEYED_VALIDATED: {
+				text += "set keyed validated ";
+				text += DADDR(1);
+				text += "[";
+				text += DADDR(2);
+				text += "] = ";
+				text += DADDR(3);
+
+				incr += 5;
+			} break;
+			case OPCODE_SET_INDEXED_VALIDATED: {
+				text += "set indexed validated ";
+				text += DADDR(1);
+				text += "[";
+				text += DADDR(2);
+				text += "] = ";
+				text += DADDR(3);
+
+				incr += 5;
+			} break;
+			case OPCODE_GET_KEYED: {
+				text += "get keyed ";
+				text += DADDR(3);
+				text += " = ";
+				text += DADDR(1);
+				text += "[";
+				text += DADDR(2);
+				text += "]";
+
+				incr += 4;
+			} break;
+			case OPCODE_GET_KEYED_VALIDATED: {
+				text += "get keyed validated ";
+				text += DADDR(3);
+				text += " = ";
+				text += DADDR(1);
+				text += "[";
+				text += DADDR(2);
+				text += "]";
+
+				incr += 5;
+			} break;
+			case OPCODE_GET_INDEXED_VALIDATED: {
+				text += "get indexed validated ";
+				text += DADDR(3);
+				text += " = ";
+				text += DADDR(1);
+				text += "[";
+				text += DADDR(2);
+				text += "]";
+
+				incr += 5;
+			} break;
+			case OPCODE_SET_NAMED: {
+				text += "set_named ";
+				text += DADDR(1);
+				text += "[\"";
+				text += _global_names_ptr[_code_ptr[ip + 3]];
+				text += "\"] = ";
+				text += DADDR(2);
+
+				incr += 4;
+			} break;
+			case OPCODE_SET_NAMED_VALIDATED: {
+				text += "set_named validated ";
+				text += DADDR(1);
+				text += "[\"";
+				text += "<unknown name>";
+				text += "\"] = ";
+				text += DADDR(2);
+
+				incr += 4;
+			} break;
+			case OPCODE_GET_NAMED: {
+				text += "get_named ";
+				text += DADDR(2);
+				text += " = ";
+				text += DADDR(1);
+				text += "[\"";
+				text += _global_names_ptr[_code_ptr[ip + 3]];
+				text += "\"]";
+
+				incr += 4;
+			} break;
+			case OPCODE_GET_NAMED_VALIDATED: {
+				text += "get_named validated ";
+				text += DADDR(2);
+				text += " = ";
+				text += DADDR(1);
+				text += "[\"";
+				text += "<unknown name>";
+				text += "\"]";
+
+				incr += 4;
+			} break;
+			case OPCODE_SET_MEMBER: {
+				text += "set_member ";
+				text += "[\"";
+				text += _global_names_ptr[_code_ptr[ip + 2]];
+				text += "\"] = ";
+				text += DADDR(1);
+
+				incr += 3;
+			} break;
+			case OPCODE_GET_MEMBER: {
+				text += "get_member ";
+				text += DADDR(1);
+				text += " = ";
+				text += "[\"";
+				text += _global_names_ptr[_code_ptr[ip + 2]];
+				text += "\"]";
+
+				incr += 3;
+			} break;
+			case OPCODE_ASSIGN: {
+				text += "assign ";
+				text += DADDR(1);
+				text += " = ";
+				text += DADDR(2);
+
+				incr += 3;
+			} break;
+			case OPCODE_ASSIGN_TRUE: {
+				text += "assign ";
+				text += DADDR(1);
+				text += " = true";
+
+				incr += 2;
+			} break;
+			case OPCODE_ASSIGN_FALSE: {
+				text += "assign ";
+				text += DADDR(1);
+				text += " = false";
+
+				incr += 2;
+			} break;
+			case OPCODE_ASSIGN_TYPED_BUILTIN: {
+				text += "assign typed builtin (";
+				text += Variant::get_type_name((Variant::Type)_code_ptr[ip + 3]);
+				text += ") ";
+				text += DADDR(1);
+				text += " = ";
+				text += DADDR(2);
+
+				incr += 4;
+			} break;
+			case OPCODE_ASSIGN_TYPED_NATIVE: {
+				Variant class_name = _constants_ptr[_code_ptr[ip + 3]];
+				GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(class_name.operator Object *());
+
+				text += "assign typed native (";
+				text += nc->get_name().operator String();
+				text += ") ";
+				text += DADDR(1);
+				text += " = ";
+				text += DADDR(2);
+
+				incr += 4;
+			} break;
+			case OPCODE_ASSIGN_TYPED_SCRIPT: {
+				Variant script = _constants_ptr[_code_ptr[ip + 3]];
+				Script *sc = Object::cast_to<Script>(script.operator Object *());
+
+				text += "assign typed script (";
+				text += sc->get_path();
+				text += ") ";
+				text += DADDR(1);
+				text += " = ";
+				text += DADDR(2);
+
+				incr += 4;
+			} break;
+			case OPCODE_CAST_TO_BUILTIN: {
+				text += "cast builtin ";
+				text += DADDR(2);
+				text += " = ";
+				text += DADDR(1);
+				text += " as ";
+				text += Variant::get_type_name(Variant::Type(_code_ptr[ip + 1]));
+
+				incr += 4;
+			} break;
+			case OPCODE_CAST_TO_NATIVE: {
+				Variant class_name = _constants_ptr[_code_ptr[ip + 1]];
+				GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(class_name.operator Object *());
+
+				text += "cast native ";
+				text += DADDR(2);
+				text += " = ";
+				text += DADDR(1);
+				text += " as ";
+				text += nc->get_name();
+
+				incr += 4;
+			} break;
+			case OPCODE_CAST_TO_SCRIPT: {
+				text += "cast ";
+				text += DADDR(2);
+				text += " = ";
+				text += DADDR(1);
+				text += " as ";
+				text += DADDR(3);
+
+				incr += 4;
+			} break;
+			case OPCODE_CONSTRUCT: {
+				Variant::Type t = Variant::Type(_code_ptr[ip + 3 + instr_var_args]);
+				int argc = _code_ptr[ip + 1 + instr_var_args];
+
+				text += "construct ";
+				text += DADDR(1 + argc);
+				text += " = ";
+
+				text += Variant::get_type_name(t) + "(";
+				for (int i = 0; i < argc; i++) {
+					if (i > 0)
+						text += ", ";
+					text += DADDR(i + 1);
+				}
+				text += ")";
+
+				incr = 3 + instr_var_args;
+			} break;
+			case OPCODE_CONSTRUCT_VALIDATED: {
+				int argc = _code_ptr[ip + 1 + instr_var_args];
+
+				text += "construct validated ";
+				text += DADDR(1 + argc);
+				text += " = ";
+
+				text += "<unkown type>(";
+				for (int i = 0; i < argc; i++) {
+					if (i > 0)
+						text += ", ";
+					text += DADDR(i + 1);
+				}
+				text += ")";
+
+				incr = 3 + instr_var_args;
+			} break;
+			case OPCODE_CONSTRUCT_ARRAY: {
+				int argc = _code_ptr[ip + 1 + instr_var_args];
+				text += " make_array ";
+				text += DADDR(1 + argc);
+				text += " = [";
+
+				for (int i = 0; i < argc; i++) {
+					if (i > 0)
+						text += ", ";
+					text += DADDR(1 + i);
+				}
+
+				text += "]";
+
+				incr += 3 + argc;
+			} break;
+			case OPCODE_CONSTRUCT_DICTIONARY: {
+				int argc = _code_ptr[ip + 1 + instr_var_args];
+				text += "make_dict ";
+				text += DADDR(1 + argc * 2);
+				text += " = {";
+
+				for (int i = 0; i < argc; i++) {
+					if (i > 0)
+						text += ", ";
+					text += DADDR(1 + i * 2 + 0);
+					text += ": ";
+					text += DADDR(1 + i * 2 + 1);
+				}
+
+				text += "}";
+
+				incr += 3 + argc * 2;
+			} break;
+			case OPCODE_CALL:
+			case OPCODE_CALL_RETURN:
+			case OPCODE_CALL_ASYNC: {
+				bool ret = (_code_ptr[ip] & INSTR_MASK) == OPCODE_CALL_RETURN;
+				bool async = (_code_ptr[ip] & INSTR_MASK) == OPCODE_CALL_ASYNC;
+
+				if (ret) {
+					text += "call-ret ";
+				} else if (async) {
+					text += "call-async ";
+				} else {
+					text += "call ";
+				}
+
+				int argc = _code_ptr[ip + 1 + instr_var_args];
+				if (ret || async) {
+					text += DADDR(2 + argc) + " = ";
+				}
+
+				text += DADDR(1 + argc) + ".";
+				text += String(_global_names_ptr[_code_ptr[ip + 2 + instr_var_args]]);
+				text += "(";
+
+				for (int i = 0; i < argc; i++) {
+					if (i > 0)
+						text += ", ";
+					text += DADDR(1 + i);
+				}
+				text += ")";
+
+				incr = 5 + argc;
+			} break;
+			case OPCODE_CALL_METHOD_BIND:
+			case OPCODE_CALL_METHOD_BIND_RET: {
+				bool ret = (_code_ptr[ip] & INSTR_MASK) == OPCODE_CALL_METHOD_BIND_RET;
+
+				if (ret) {
+					text += "call-method_bind-ret ";
+				} else {
+					text += "call-method_bind ";
+				}
+
+				MethodBind *method = _methods_ptr[_code_ptr[ip + 2 + instr_var_args]];
+
+				int argc = _code_ptr[ip + 1 + instr_var_args];
+				if (ret) {
+					text += DADDR(2 + argc) + " = ";
+				}
+
+				text += DADDR(1 + argc) + ".";
+				text += method->get_name();
+				text += "(";
+
+				for (int i = 0; i < argc; i++) {
+					if (i > 0)
+						text += ", ";
+					text += DADDR(1 + i);
+				}
+				text += ")";
+
+				incr = 5 + argc;
+			} break;
+			case OPCODE_CALL_PTRCALL_NO_RETURN: {
+				text += "call-ptrcall (no return) ";
+
+				MethodBind *method = _methods_ptr[_code_ptr[ip + 2 + instr_var_args]];
+
+				int argc = _code_ptr[ip + 1 + instr_var_args];
+
+				text += DADDR(1 + argc) + ".";
+				text += method->get_name();
+				text += "(";
+
+				for (int i = 0; i < argc; i++) {
+					if (i > 0)
+						text += ", ";
+					text += DADDR(1 + i);
+				}
+				text += ")";
+
+				incr = 5 + argc;
+			} break;
+
+#define DISASSEMBLE_PTRCALL(m_type)                                            \
+	case OPCODE_CALL_PTRCALL_##m_type: {                                       \
+		text += "call-ptrcall (return ";                                       \
+		text += #m_type;                                                       \
+		text += ") ";                                                          \
+		MethodBind *method = _methods_ptr[_code_ptr[ip + 2 + instr_var_args]]; \
+		int argc = _code_ptr[ip + 1 + instr_var_args];                         \
+		text += DADDR(2 + argc) + " = ";                                       \
+		text += DADDR(1 + argc) + ".";                                         \
+		text += method->get_name();                                            \
+		text += "(";                                                           \
+		for (int i = 0; i < argc; i++) {                                       \
+			if (i > 0)                                                         \
+				text += ", ";                                                  \
+			text += DADDR(1 + i);                                              \
+		}                                                                      \
+		text += ")";                                                           \
+		incr = 5 + argc;                                                       \
+	} break
+
+				DISASSEMBLE_PTRCALL(BOOL);
+				DISASSEMBLE_PTRCALL(INT);
+				DISASSEMBLE_PTRCALL(FLOAT);
+				DISASSEMBLE_PTRCALL(STRING);
+				DISASSEMBLE_PTRCALL(VECTOR2);
+				DISASSEMBLE_PTRCALL(VECTOR2I);
+				DISASSEMBLE_PTRCALL(RECT2);
+				DISASSEMBLE_PTRCALL(RECT2I);
+				DISASSEMBLE_PTRCALL(VECTOR3);
+				DISASSEMBLE_PTRCALL(VECTOR3I);
+				DISASSEMBLE_PTRCALL(TRANSFORM2D);
+				DISASSEMBLE_PTRCALL(PLANE);
+				DISASSEMBLE_PTRCALL(AABB);
+				DISASSEMBLE_PTRCALL(BASIS);
+				DISASSEMBLE_PTRCALL(TRANSFORM);
+				DISASSEMBLE_PTRCALL(COLOR);
+				DISASSEMBLE_PTRCALL(STRING_NAME);
+				DISASSEMBLE_PTRCALL(NODE_PATH);
+				DISASSEMBLE_PTRCALL(RID);
+				DISASSEMBLE_PTRCALL(QUAT);
+				DISASSEMBLE_PTRCALL(OBJECT);
+				DISASSEMBLE_PTRCALL(CALLABLE);
+				DISASSEMBLE_PTRCALL(SIGNAL);
+				DISASSEMBLE_PTRCALL(DICTIONARY);
+				DISASSEMBLE_PTRCALL(ARRAY);
+				DISASSEMBLE_PTRCALL(PACKED_BYTE_ARRAY);
+				DISASSEMBLE_PTRCALL(PACKED_INT32_ARRAY);
+				DISASSEMBLE_PTRCALL(PACKED_INT64_ARRAY);
+				DISASSEMBLE_PTRCALL(PACKED_FLOAT32_ARRAY);
+				DISASSEMBLE_PTRCALL(PACKED_FLOAT64_ARRAY);
+				DISASSEMBLE_PTRCALL(PACKED_STRING_ARRAY);
+				DISASSEMBLE_PTRCALL(PACKED_VECTOR2_ARRAY);
+				DISASSEMBLE_PTRCALL(PACKED_VECTOR3_ARRAY);
+				DISASSEMBLE_PTRCALL(PACKED_COLOR_ARRAY);
+
+			case OPCODE_CALL_BUILTIN_TYPE_VALIDATED: {
+				int argc = _code_ptr[ip + 1 + instr_var_args];
+
+				text += "call-builtin-method validated ";
+
+				text += DADDR(2 + argc) + " = ";
+
+				text += DADDR(1) + ".";
+				text += "<unknown method>";
+
+				text += "(";
+
+				for (int i = 0; i < argc; i++) {
+					if (i > 0)
+						text += ", ";
+					text += DADDR(1 + i);
+				}
+				text += ")";
+
+				incr = 5 + argc;
+			} break;
+			case OPCODE_CALL_BUILT_IN: {
+				text += "call-built-in ";
+
+				int argc = _code_ptr[ip + 1 + instr_var_args];
+				text += DADDR(1 + argc) + " = ";
+
+				text += GDScriptFunctions::get_func_name(GDScriptFunctions::Function(_code_ptr[ip + 2 + instr_var_args]));
+				text += "(";
+
+				for (int i = 0; i < argc; i++) {
+					if (i > 0)
+						text += ", ";
+					text += DADDR(1 + i);
+				}
+				text += ")";
+
+				incr = 4 + argc;
+			} break;
+			case OPCODE_CALL_SELF_BASE: {
+				text += "call-self-base ";
+
+				int argc = _code_ptr[ip + 1 + instr_var_args];
+				text += DADDR(2 + argc) + " = ";
+
+				text += _global_names_ptr[_code_ptr[ip + 2 + instr_var_args]];
+				text += "(";
+
+				for (int i = 0; i < argc; i++) {
+					if (i > 0)
+						text += ", ";
+					text += DADDR(1 + i);
+				}
+				text += ")";
+
+				incr = 4 + argc;
+			} break;
+			case OPCODE_AWAIT: {
+				text += "await ";
+				text += DADDR(1);
+
+				incr += 2;
+			} break;
+			case OPCODE_AWAIT_RESUME: {
+				text += "await resume ";
+				text += DADDR(1);
+
+				incr = 2;
+			} break;
+			case OPCODE_JUMP: {
+				text += "jump ";
+				text += itos(_code_ptr[ip + 1]);
+
+				incr = 2;
+			} break;
+			case OPCODE_JUMP_IF: {
+				text += "jump-if ";
+				text += DADDR(1);
+				text += " to ";
+				text += itos(_code_ptr[ip + 2]);
+
+				incr = 3;
+			} break;
+			case OPCODE_JUMP_IF_NOT: {
+				text += "jump-if-not ";
+				text += DADDR(1);
+				text += " to ";
+				text += itos(_code_ptr[ip + 2]);
+
+				incr = 3;
+			} break;
+			case OPCODE_JUMP_TO_DEF_ARGUMENT: {
+				text += "jump-to-default-argument ";
+
+				incr = 1;
+			} break;
+			case OPCODE_RETURN: {
+				text += "return ";
+				text += DADDR(1);
+
+				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);
+				text += " in ";
+				text += DADDR(2);
+				text += " counter ";
+				text += DADDR(1);
+				text += " end ";
+				text += itos(_code_ptr[ip + 4]);
+
+				incr += 5;
+			} break;
+				DISASSEMBLE_ITERATE_TYPES(DISASSEMBLE_ITERATE_BEGIN);
+			case OPCODE_ITERATE: {
+				text += "for-loop ";
+				text += DADDR(2);
+				text += " in ";
+				text += DADDR(2);
+				text += " counter ";
+				text += DADDR(1);
+				text += " end ";
+				text += itos(_code_ptr[ip + 4]);
+
+				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()) {
+					text += "line ";
+					text += itos(line + 1);
+					text += ": ";
+					text += p_code_lines[line];
+				} else {
+					text += "";
+				}
+
+				incr += 2;
+			} break;
+			case OPCODE_ASSERT: {
+				text += "assert (";
+				text += DADDR(1);
+				text += ", ";
+				text += DADDR(2);
+				text += ")";
+
+				incr += 3;
+			} break;
+			case OPCODE_BREAKPOINT: {
+				text += "breakpoint";
+
+				incr += 1;
+			} break;
+			case OPCODE_END: {
+				text += "== END ==";
+
+				incr += 1;
+			} break;
+		}
+
+		ip += incr;
+		if (text.get_string_length() > 0) {
+			print_line(text.as_string());
+		}
+	}
+}
+
+#endif

File diff suppressed because it is too large
+ 0 - 1567
modules/gdscript/gdscript_function.cpp


+ 153 - 30
modules/gdscript/gdscript_function.h

@@ -159,12 +159,19 @@ class GDScriptFunction {
 public:
 	enum Opcode {
 		OPCODE_OPERATOR,
+		OPCODE_OPERATOR_VALIDATED,
 		OPCODE_EXTENDS_TEST,
 		OPCODE_IS_BUILTIN,
-		OPCODE_SET,
-		OPCODE_GET,
+		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,
@@ -176,14 +183,54 @@ public:
 		OPCODE_CAST_TO_BUILTIN,
 		OPCODE_CAST_TO_NATIVE,
 		OPCODE_CAST_TO_SCRIPT,
-		OPCODE_CONSTRUCT, //only for basic types!!
+		OPCODE_CONSTRUCT, // Only for basic types!
+		OPCODE_CONSTRUCT_VALIDATED, // Only for basic types!
 		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,
+		// ptrcall have one instruction per return type.
+		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,
@@ -192,7 +239,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,
@@ -215,6 +300,12 @@ public:
 		ADDR_TYPE_NIL = 9
 	};
 
+	enum Instruction {
+		INSTR_BITS = 20,
+		INSTR_MASK = ((1 << INSTR_BITS) - 1),
+		INSTR_ARGS_MASK = ~INSTR_MASK,
+	};
+
 	struct StackDebug {
 		int line;
 		int pos;
@@ -229,27 +320,59 @@ private:
 	StringName source;
 
 	mutable Variant nil;
-	mutable Variant *_constants_ptr;
-	int _constant_count;
-	const StringName *_global_names_ptr;
-	int _global_names_count;
-	const int *_default_arg_ptr;
-	int _default_arg_count;
-	const int *_code_ptr;
-	int _code_size;
-	int _argument_count;
-	int _stack_size;
-	int _call_size;
-	int _initial_line;
-	bool _static;
-	MultiplayerAPI::RPCMode rpc_mode;
-
-	GDScript *_script;
+	mutable Variant *_constants_ptr = nullptr;
+	int _constant_count = 0;
+	const StringName *_global_names_ptr = nullptr;
+	int _global_names_count = 0;
+	const int *_default_arg_ptr = nullptr;
+	int _default_arg_count = 0;
+	int _operator_funcs_count = 0;
+	const Variant::ValidatedOperatorEvaluator *_operator_funcs_ptr = nullptr;
+	int _setters_count = 0;
+	const Variant::ValidatedSetter *_setters_ptr = nullptr;
+	int _getters_count = 0;
+	const Variant::ValidatedGetter *_getters_ptr = nullptr;
+	int _keyed_setters_count = 0;
+	const Variant::ValidatedKeyedSetter *_keyed_setters_ptr = nullptr;
+	int _keyed_getters_count = 0;
+	const Variant::ValidatedKeyedGetter *_keyed_getters_ptr = nullptr;
+	int _indexed_setters_count = 0;
+	const Variant::ValidatedIndexedSetter *_indexed_setters_ptr = nullptr;
+	int _indexed_getters_count = 0;
+	const Variant::ValidatedIndexedGetter *_indexed_getters_ptr = nullptr;
+	int _builtin_methods_count = 0;
+	const Variant::ValidatedBuiltInMethod *_builtin_methods_ptr = nullptr;
+	int _constructors_count = 0;
+	const Variant::ValidatedConstructor *_constructors_ptr = nullptr;
+	int _methods_count = 0;
+	MethodBind **_methods_ptr = nullptr;
+	const int *_code_ptr = nullptr;
+	int _code_size = 0;
+	int _argument_count = 0;
+	int _stack_size = 0;
+	int _instruction_args_size = 0;
+	int _ptrcall_args_size = 0;
+
+	int _initial_line = 0;
+	bool _static = false;
+	MultiplayerAPI::RPCMode rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
+
+	GDScript *_script = nullptr;
 
 	StringName name;
 	Vector<Variant> constants;
 	Vector<StringName> global_names;
 	Vector<int> default_arguments;
+	Vector<Variant::ValidatedOperatorEvaluator> operator_funcs;
+	Vector<Variant::ValidatedSetter> setters;
+	Vector<Variant::ValidatedGetter> getters;
+	Vector<Variant::ValidatedKeyedSetter> keyed_setters;
+	Vector<Variant::ValidatedKeyedGetter> keyed_getters;
+	Vector<Variant::ValidatedIndexedSetter> indexed_setters;
+	Vector<Variant::ValidatedIndexedGetter> indexed_getters;
+	Vector<Variant::ValidatedBuiltInMethod> builtin_methods;
+	Vector<Variant::ValidatedConstructor> constructors;
+	Vector<MethodBind *> methods;
 	Vector<int> code;
 	Vector<GDScriptDataType> argument_types;
 	GDScriptDataType return_type;
@@ -265,22 +388,22 @@ private:
 
 	friend class GDScriptLanguage;
 
-	SelfList<GDScriptFunction> function_list;
+	SelfList<GDScriptFunction> function_list{ this };
 #ifdef DEBUG_ENABLED
 	CharString func_cname;
-	const char *_func_cname;
+	const char *_func_cname = nullptr;
 
 	struct Profile {
 		StringName signature;
-		uint64_t call_count;
-		uint64_t self_time;
-		uint64_t total_time;
-		uint64_t frame_call_count;
-		uint64_t frame_self_time;
-		uint64_t frame_total_time;
-		uint64_t last_frame_call_count;
-		uint64_t last_frame_self_time;
-		uint64_t last_frame_total_time;
+		uint64_t call_count = 0;
+		uint64_t self_time = 0;
+		uint64_t total_time = 0;
+		uint64_t frame_call_count = 0;
+		uint64_t frame_self_time = 0;
+		uint64_t frame_total_time = 0;
+		uint64_t last_frame_call_count = 0;
+		uint64_t last_frame_self_time = 0;
+		uint64_t last_frame_total_time = 0;
 	} profile;
 
 #endif

+ 2863 - 0
modules/gdscript/gdscript_vm.cpp

@@ -0,0 +1,2863 @@
+/*************************************************************************/
+/*  gdscript_vm.cpp                                                      */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#include "gdscript_function.h"
+
+#include "core/core_string_names.h"
+#include "core/os/os.h"
+#include "gdscript.h"
+#include "gdscript_functions.h"
+
+Variant *GDScriptFunction::_get_variant(int p_address, GDScriptInstance *p_instance, GDScript *p_script, Variant &self, Variant &static_ref, Variant *p_stack, String &r_error) const {
+	int address = p_address & ADDR_MASK;
+
+	//sequential table (jump table generated by compiler)
+	switch ((p_address & ADDR_TYPE_MASK) >> ADDR_BITS) {
+		case ADDR_TYPE_SELF: {
+#ifdef DEBUG_ENABLED
+			if (unlikely(!p_instance)) {
+				r_error = "Cannot access self without instance.";
+				return nullptr;
+			}
+#endif
+			return &self;
+		} break;
+		case ADDR_TYPE_CLASS: {
+			return &static_ref;
+		} break;
+		case ADDR_TYPE_MEMBER: {
+#ifdef DEBUG_ENABLED
+			if (unlikely(!p_instance)) {
+				r_error = "Cannot access member without instance.";
+				return nullptr;
+			}
+#endif
+			//member indexing is O(1)
+			return &p_instance->members.write[address];
+		} break;
+		case ADDR_TYPE_CLASS_CONSTANT: {
+			//todo change to index!
+			GDScript *s = p_script;
+#ifdef DEBUG_ENABLED
+			ERR_FAIL_INDEX_V(address, _global_names_count, nullptr);
+#endif
+			const StringName *sn = &_global_names_ptr[address];
+
+			while (s) {
+				GDScript *o = s;
+				while (o) {
+					Map<StringName, Variant>::Element *E = o->constants.find(*sn);
+					if (E) {
+						return &E->get();
+					}
+					o = o->_owner;
+				}
+				s = s->_base;
+			}
+
+			ERR_FAIL_V_MSG(nullptr, "GDScriptCompiler bug.");
+		} break;
+		case ADDR_TYPE_LOCAL_CONSTANT: {
+#ifdef DEBUG_ENABLED
+			ERR_FAIL_INDEX_V(address, _constant_count, nullptr);
+#endif
+			return &_constants_ptr[address];
+		} break;
+		case ADDR_TYPE_STACK:
+		case ADDR_TYPE_STACK_VARIABLE: {
+#ifdef DEBUG_ENABLED
+			ERR_FAIL_INDEX_V(address, _stack_size, nullptr);
+#endif
+			return &p_stack[address];
+		} break;
+		case ADDR_TYPE_GLOBAL: {
+#ifdef DEBUG_ENABLED
+			ERR_FAIL_INDEX_V(address, GDScriptLanguage::get_singleton()->get_global_array_size(), nullptr);
+#endif
+			return &GDScriptLanguage::get_singleton()->get_global_array()[address];
+		} break;
+#ifdef TOOLS_ENABLED
+		case ADDR_TYPE_NAMED_GLOBAL: {
+#ifdef DEBUG_ENABLED
+			ERR_FAIL_INDEX_V(address, _global_names_count, nullptr);
+#endif
+			StringName id = _global_names_ptr[address];
+
+			if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(id)) {
+				return (Variant *)&GDScriptLanguage::get_singleton()->get_named_globals_map()[id];
+			} else {
+				r_error = "Autoload singleton '" + String(id) + "' has been removed.";
+				return nullptr;
+			}
+		} break;
+#endif
+		case ADDR_TYPE_NIL: {
+			return &nil;
+		} break;
+	}
+
+	ERR_FAIL_V_MSG(nullptr, "Bad code! (unknown addressing mode).");
+	return nullptr;
+}
+
+#ifdef DEBUG_ENABLED
+static String _get_var_type(const Variant *p_var) {
+	String basestr;
+
+	if (p_var->get_type() == Variant::OBJECT) {
+		bool was_freed;
+		Object *bobj = p_var->get_validated_object_with_check(was_freed);
+		if (!bobj) {
+			if (was_freed) {
+				basestr = "null instance";
+			} else {
+				basestr = "previously freed";
+			}
+		} else {
+			if (bobj->get_script_instance()) {
+				basestr = bobj->get_class() + " (" + bobj->get_script_instance()->get_script()->get_path().get_file() + ")";
+			} else {
+				basestr = bobj->get_class();
+			}
+		}
+
+	} else {
+		basestr = Variant::get_type_name(p_var->get_type());
+	}
+
+	return basestr;
+}
+#endif // DEBUG_ENABLED
+
+String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const String &p_where, const Variant **argptrs) const {
+	String err_text;
+
+	if (p_err.error == Callable::CallError::CALL_ERROR_INVALID_ARGUMENT) {
+		int errorarg = p_err.argument;
+		// Handle the Object to Object case separately as we don't have further class details.
+#ifdef DEBUG_ENABLED
+		if (p_err.expected == Variant::OBJECT && argptrs[errorarg]->get_type() == p_err.expected) {
+			err_text = "Invalid type in " + p_where + ". The Object-derived class of argument " + itos(errorarg + 1) + " (" + _get_var_type(argptrs[errorarg]) + ") is not a subclass of the expected argument class.";
+		} else
+#endif // DEBUG_ENABLED
+		{
+			err_text = "Invalid type in " + p_where + ". Cannot convert argument " + itos(errorarg + 1) + " from " + Variant::get_type_name(argptrs[errorarg]->get_type()) + " to " + Variant::get_type_name(Variant::Type(p_err.expected)) + ".";
+		}
+	} else if (p_err.error == Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS) {
+		err_text = "Invalid call to " + p_where + ". Expected " + itos(p_err.argument) + " arguments.";
+	} else if (p_err.error == Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS) {
+		err_text = "Invalid call to " + p_where + ". Expected " + itos(p_err.argument) + " arguments.";
+	} else if (p_err.error == Callable::CallError::CALL_ERROR_INVALID_METHOD) {
+		err_text = "Invalid call. Nonexistent " + p_where + ".";
+	} else if (p_err.error == Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL) {
+		err_text = "Attempt to call " + p_where + " on a null instance.";
+	} else {
+		err_text = "Bug, call error: #" + itos(p_err.error);
+	}
+
+	return err_text;
+}
+
+#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_VALIDATED,                \
+		&&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) \
+	m_op:
+#define OPCODE_WHILE(m_test) \
+	OPSWHILE:
+#define OPCODES_END \
+	OPSEXIT:
+#define OPCODES_OUT \
+	OPSOUT:
+#define DISPATCH_OPCODE goto OPSWHILE
+#define OPCODE_SWITCH(m_test) goto *switch_table_ops[m_test];
+#define OPCODE_BREAK goto OPSEXIT
+#define OPCODE_OUT goto OPSOUT
+#else
+#define OPCODES_TABLE
+#define OPCODE(m_op) case m_op:
+#define OPCODE_WHILE(m_test) while (m_test)
+#define OPCODES_END
+#define OPCODES_OUT
+#define DISPATCH_OPCODE continue
+#define OPCODE_SWITCH(m_test) switch (m_test)
+#define OPCODE_BREAK break
+#define OPCODE_OUT break
+#endif
+
+// Helpers for VariantInternal methods in macros.
+#define OP_GET_BOOL get_bool
+#define OP_GET_INT get_int
+#define OP_GET_FLOAT get_float
+#define OP_GET_VECTOR2 get_vector2
+#define OP_GET_VECTOR2I get_vector2i
+#define OP_GET_VECTOR3 get_vector3
+#define OP_GET_VECTOR3I get_vector3i
+#define OP_GET_RECT2 get_rect2
+#define OP_GET_RECT2I get_rect2i
+#define OP_GET_QUAT get_quat
+#define OP_GET_COLOR get_color
+#define OP_GET_STRING get_string
+#define OP_GET_STRING_NAME get_string_name
+#define OP_GET_NODE_PATH get_node_path
+#define OP_GET_CALLABLE get_callable
+#define OP_GET_SIGNAL get_signal
+#define OP_GET_ARRAY get_array
+#define OP_GET_DICTIONARY get_dictionary
+#define OP_GET_PACKED_BYTE_ARRAY get_byte_array
+#define OP_GET_PACKED_INT32_ARRAY get_int32_array
+#define OP_GET_PACKED_INT64_ARRAY get_int64_array
+#define OP_GET_PACKED_FLOAT32_ARRAY get_float32_array
+#define OP_GET_PACKED_FLOAT64_ARRAY get_float64_array
+#define OP_GET_PACKED_STRING_ARRAY get_string_array
+#define OP_GET_PACKED_VECTOR2_ARRAY get_vector2_array
+#define OP_GET_PACKED_VECTOR3_ARRAY get_vector3_array
+#define OP_GET_PACKED_COLOR_ARRAY get_color_array
+#define OP_GET_TRANSFORM get_transform
+#define OP_GET_TRANSFORM2D get_transform2d
+#define OP_GET_PLANE get_plane
+#define OP_GET_AABB get_aabb
+#define OP_GET_BASIS get_basis
+#define OP_GET_RID get_rid
+
+Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_args, int p_argcount, Callable::CallError &r_err, CallState *p_state) {
+	OPCODES_TABLE;
+
+	if (!_code_ptr) {
+		return Variant();
+	}
+
+	r_err.error = Callable::CallError::CALL_OK;
+
+	Variant self;
+	Variant static_ref;
+	Variant retvalue;
+	Variant *stack = nullptr;
+	Variant **instruction_args;
+	const void **call_args_ptr = nullptr;
+	int defarg = 0;
+
+#ifdef DEBUG_ENABLED
+
+	//GDScriptLanguage::get_singleton()->calls++;
+
+#endif
+
+	uint32_t alloca_size = 0;
+	GDScript *script;
+	int ip = 0;
+	int line = _initial_line;
+
+	if (p_state) {
+		//use existing (supplied) state (awaited)
+		stack = (Variant *)p_state->stack.ptr();
+		instruction_args = (Variant **)&p_state->stack.ptr()[sizeof(Variant) * p_state->stack_size]; //ptr() to avoid bounds check
+		line = p_state->line;
+		ip = p_state->ip;
+		alloca_size = p_state->stack.size();
+		script = p_state->script;
+		p_instance = p_state->instance;
+		defarg = p_state->defarg;
+		self = p_state->self;
+
+	} else {
+		if (p_argcount != _argument_count) {
+			if (p_argcount > _argument_count) {
+				r_err.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
+				r_err.argument = _argument_count;
+
+				return Variant();
+			} else if (p_argcount < _argument_count - _default_arg_count) {
+				r_err.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+				r_err.argument = _argument_count - _default_arg_count;
+				return Variant();
+			} else {
+				defarg = _argument_count - p_argcount;
+			}
+		}
+
+		alloca_size = sizeof(Variant *) * _instruction_args_size + sizeof(Variant) * _stack_size;
+
+		if (alloca_size) {
+			uint8_t *aptr = (uint8_t *)alloca(alloca_size);
+
+			if (_stack_size) {
+				stack = (Variant *)aptr;
+				for (int i = 0; i < p_argcount; i++) {
+					if (!argument_types[i].has_type) {
+						memnew_placement(&stack[i], Variant(*p_args[i]));
+						continue;
+					}
+
+					if (!argument_types[i].is_type(*p_args[i], true)) {
+						r_err.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+						r_err.argument = i;
+						r_err.expected = argument_types[i].kind == GDScriptDataType::BUILTIN ? argument_types[i].builtin_type : Variant::OBJECT;
+						return Variant();
+					}
+					if (argument_types[i].kind == GDScriptDataType::BUILTIN) {
+						Variant arg;
+						Variant::construct(argument_types[i].builtin_type, arg, &p_args[i], 1, r_err);
+						memnew_placement(&stack[i], Variant(arg));
+					} else {
+						memnew_placement(&stack[i], Variant(*p_args[i]));
+					}
+				}
+				for (int i = p_argcount; i < _stack_size; i++) {
+					memnew_placement(&stack[i], Variant);
+				}
+			} else {
+				stack = nullptr;
+			}
+
+			if (_instruction_args_size) {
+				instruction_args = (Variant **)&aptr[sizeof(Variant) * _stack_size];
+			} else {
+				instruction_args = nullptr;
+			}
+
+		} else {
+			stack = nullptr;
+			instruction_args = nullptr;
+		}
+
+		if (p_instance) {
+			if (p_instance->base_ref && static_cast<Reference *>(p_instance->owner)->is_referenced()) {
+				self = REF(static_cast<Reference *>(p_instance->owner));
+			} else {
+				self = p_instance->owner;
+			}
+			script = p_instance->script.ptr();
+		} else {
+			script = _script;
+		}
+	}
+	if (_ptrcall_args_size) {
+		call_args_ptr = (const void **)alloca(_ptrcall_args_size * sizeof(void *));
+	} else {
+		call_args_ptr = nullptr;
+	}
+
+	static_ref = script;
+
+	String err_text;
+
+#ifdef DEBUG_ENABLED
+
+	if (EngineDebugger::is_active()) {
+		GDScriptLanguage::get_singleton()->enter_function(p_instance, this, stack, &ip, &line);
+	}
+
+#define GD_ERR_BREAK(m_cond)                                                                                           \
+	{                                                                                                                  \
+		if (unlikely(m_cond)) {                                                                                        \
+			_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition ' " _STR(m_cond) " ' is true. Breaking..:"); \
+			OPCODE_BREAK;                                                                                              \
+		}                                                                                                              \
+	}
+
+#define CHECK_SPACE(m_space) \
+	GD_ERR_BREAK((ip + m_space) > _code_size)
+
+#define GET_VARIANT_PTR(m_v, m_code_ofs)                                                                   \
+	Variant *m_v;                                                                                          \
+	m_v = _get_variant(_code_ptr[ip + m_code_ofs], p_instance, script, self, static_ref, stack, err_text); \
+	if (unlikely(!m_v))                                                                                    \
+		OPCODE_BREAK;
+
+#else
+#define GD_ERR_BREAK(m_cond)
+#define CHECK_SPACE(m_space)
+#define GET_VARIANT_PTR(m_v, m_code_ofs) \
+	Variant *m_v;                        \
+	m_v = _get_variant(_code_ptr[ip + m_code_ofs], p_instance, script, self, static_ref, stack, err_text);
+
+#endif
+
+#define GET_INSTRUCTION_ARG(m_v, m_idx) \
+	Variant *m_v = instruction_args[m_idx]
+
+#ifdef DEBUG_ENABLED
+
+	uint64_t function_start_time = 0;
+	uint64_t function_call_time = 0;
+
+	if (GDScriptLanguage::get_singleton()->profiling) {
+		function_start_time = OS::get_singleton()->get_ticks_usec();
+		function_call_time = 0;
+		profile.call_count++;
+		profile.frame_call_count++;
+	}
+	bool exit_ok = false;
+	bool awaited = false;
+#endif
+
+#ifdef DEBUG_ENABLED
+	OPCODE_WHILE(ip < _code_size) {
+		int last_opcode = _code_ptr[ip];
+#else
+	OPCODE_WHILE(true) {
+#endif
+		// Load arguments for the instruction before each instruction.
+		int instr_arg_count = ((_code_ptr[ip]) & INSTR_ARGS_MASK) >> INSTR_BITS;
+		for (int i = 0; i < instr_arg_count; i++) {
+			GET_VARIANT_PTR(v, i + 1);
+			instruction_args[i] = v;
+		}
+
+		OPCODE_SWITCH(_code_ptr[ip] & INSTR_MASK) {
+			OPCODE(OPCODE_OPERATOR) {
+				CHECK_SPACE(5);
+
+				bool valid;
+				Variant::Operator op = (Variant::Operator)_code_ptr[ip + 4];
+				GD_ERR_BREAK(op >= Variant::OP_MAX);
+
+				GET_INSTRUCTION_ARG(a, 0);
+				GET_INSTRUCTION_ARG(b, 1);
+				GET_INSTRUCTION_ARG(dst, 2);
+
+#ifdef DEBUG_ENABLED
+
+				Variant ret;
+				Variant::evaluate(op, *a, *b, ret, valid);
+#else
+				Variant::evaluate(op, *a, *b, *dst, valid);
+#endif
+#ifdef DEBUG_ENABLED
+				if (!valid) {
+					if (ret.get_type() == Variant::STRING) {
+						//return a string when invalid with the error
+						err_text = ret;
+						err_text += " in operator '" + Variant::get_operator_name(op) + "'.";
+					} else {
+						err_text = "Invalid operands '" + Variant::get_type_name(a->get_type()) + "' and '" + Variant::get_type_name(b->get_type()) + "' in operator '" + Variant::get_operator_name(op) + "'.";
+					}
+					OPCODE_BREAK;
+				}
+				*dst = ret;
+#endif
+				ip += 5;
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_OPERATOR_VALIDATED) {
+				CHECK_SPACE(5);
+
+				int operator_idx = _code_ptr[ip + 4];
+				GD_ERR_BREAK(operator_idx < 0 || operator_idx >= _operator_funcs_count);
+				Variant::ValidatedOperatorEvaluator operator_func = _operator_funcs_ptr[operator_idx];
+
+				GET_INSTRUCTION_ARG(a, 0);
+				GET_INSTRUCTION_ARG(b, 1);
+				GET_INSTRUCTION_ARG(dst, 2);
+
+				operator_func(a, b, dst);
+
+				ip += 5;
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_EXTENDS_TEST) {
+				CHECK_SPACE(4);
+
+				GET_INSTRUCTION_ARG(a, 0);
+				GET_INSTRUCTION_ARG(b, 1);
+				GET_INSTRUCTION_ARG(dst, 2);
+
+#ifdef DEBUG_ENABLED
+				if (b->get_type() != Variant::OBJECT || b->operator Object *() == nullptr) {
+					err_text = "Right operand of 'is' is not a class.";
+					OPCODE_BREAK;
+				}
+#endif
+
+				bool extends_ok = false;
+				if (a->get_type() == Variant::OBJECT && a->operator Object *() != nullptr) {
+#ifdef DEBUG_ENABLED
+					bool was_freed;
+					Object *obj_A = a->get_validated_object_with_check(was_freed);
+
+					if (was_freed) {
+						err_text = "Left operand of 'is' is a previously freed instance.";
+						OPCODE_BREAK;
+					}
+
+					Object *obj_B = b->get_validated_object_with_check(was_freed);
+
+					if (was_freed) {
+						err_text = "Right operand of 'is' is a previously freed instance.";
+						OPCODE_BREAK;
+					}
+#else
+
+					Object *obj_A = *a;
+					Object *obj_B = *b;
+#endif // DEBUG_ENABLED
+
+					GDScript *scr_B = Object::cast_to<GDScript>(obj_B);
+
+					if (scr_B) {
+						//if B is a script, the only valid condition is that A has an instance which inherits from the script
+						//in other situation, this shoul return false.
+
+						if (obj_A->get_script_instance() && obj_A->get_script_instance()->get_language() == GDScriptLanguage::get_singleton()) {
+							GDScript *cmp = static_cast<GDScript *>(obj_A->get_script_instance()->get_script().ptr());
+							//bool found=false;
+							while (cmp) {
+								if (cmp == scr_B) {
+									//inherits from script, all ok
+									extends_ok = true;
+									break;
+								}
+
+								cmp = cmp->_base;
+							}
+						}
+
+					} else {
+						GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(obj_B);
+
+#ifdef DEBUG_ENABLED
+						if (!nc) {
+							err_text = "Right operand of 'is' is not a class (type: '" + obj_B->get_class() + "').";
+							OPCODE_BREAK;
+						}
+#endif
+						extends_ok = ClassDB::is_parent_class(obj_A->get_class_name(), nc->get_name());
+					}
+				}
+
+				*dst = extends_ok;
+				ip += 4;
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_IS_BUILTIN) {
+				CHECK_SPACE(4);
+
+				GET_INSTRUCTION_ARG(value, 0);
+				GET_INSTRUCTION_ARG(dst, 1);
+				Variant::Type var_type = (Variant::Type)_code_ptr[ip + 3];
+
+				GD_ERR_BREAK(var_type < 0 || var_type >= Variant::VARIANT_MAX);
+
+				*dst = value->get_type() == var_type;
+				ip += 4;
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_SET_KEYED) {
+				CHECK_SPACE(3);
+
+				GET_INSTRUCTION_ARG(dst, 0);
+				GET_INSTRUCTION_ARG(index, 1);
+				GET_INSTRUCTION_ARG(value, 2);
+
+				bool valid;
+				dst->set(*index, *value, &valid);
+
+#ifdef DEBUG_ENABLED
+				if (!valid) {
+					String v = index->operator String();
+					if (v != "") {
+						v = "'" + v + "'";
+					} else {
+						v = "of type '" + _get_var_type(index) + "'";
+					}
+					err_text = "Invalid set index " + v + " (on base: '" + _get_var_type(dst) + "') with value of type '" + _get_var_type(value) + "'";
+					OPCODE_BREAK;
+				}
+#endif
+				ip += 4;
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_SET_KEYED_VALIDATED) {
+				CHECK_SPACE(4);
+
+				GET_INSTRUCTION_ARG(dst, 0);
+				GET_INSTRUCTION_ARG(index, 1);
+				GET_INSTRUCTION_ARG(value, 2);
+
+				int index_setter = _code_ptr[ip + 4];
+				GD_ERR_BREAK(index_setter < 0 || index_setter >= _keyed_setters_count);
+				const Variant::ValidatedKeyedSetter setter = _keyed_setters_ptr[index_setter];
+
+				bool valid;
+				setter(dst, index, value, valid);
+
+#ifdef DEBUG_ENABLED
+				if (!valid) {
+					String v = index->operator String();
+					if (v != "") {
+						v = "'" + v + "'";
+					} else {
+						v = "of type '" + _get_var_type(index) + "'";
+					}
+					err_text = "Invalid set index " + v + " (on base: '" + _get_var_type(dst) + "') with value of type '" + _get_var_type(value) + "'";
+					OPCODE_BREAK;
+				}
+#endif
+				ip += 5;
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_SET_INDEXED_VALIDATED) {
+				CHECK_SPACE(4);
+
+				GET_INSTRUCTION_ARG(dst, 0);
+				GET_INSTRUCTION_ARG(index, 1);
+				GET_INSTRUCTION_ARG(value, 2);
+
+				int index_setter = _code_ptr[ip + 4];
+				GD_ERR_BREAK(index_setter < 0 || index_setter >= _indexed_setters_count);
+				const Variant::ValidatedIndexedSetter setter = _indexed_setters_ptr[index_setter];
+
+				int64_t int_index = *VariantInternal::get_int(index);
+
+				bool oob;
+				setter(dst, int_index, value, oob);
+
+#ifdef DEBUG_ENABLED
+				if (oob) {
+					String v = index->operator String();
+					if (v != "") {
+						v = "'" + v + "'";
+					} else {
+						v = "of type '" + _get_var_type(index) + "'";
+					}
+					err_text = "Out of bounds set index " + v + " (on base: '" + _get_var_type(dst) + "')";
+					OPCODE_BREAK;
+				}
+#endif
+				ip += 5;
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_GET_KEYED) {
+				CHECK_SPACE(3);
+
+				GET_INSTRUCTION_ARG(src, 0);
+				GET_INSTRUCTION_ARG(index, 1);
+				GET_INSTRUCTION_ARG(dst, 2);
+
+				bool valid;
+#ifdef DEBUG_ENABLED
+				// Allow better error message in cases where src and dst are the same stack position.
+				Variant ret = src->get(*index, &valid);
+#else
+				*dst = src->get(*index, &valid);
+
+#endif
+#ifdef DEBUG_ENABLED
+				if (!valid) {
+					String v = index->operator String();
+					if (v != "") {
+						v = "'" + v + "'";
+					} else {
+						v = "of type '" + _get_var_type(index) + "'";
+					}
+					err_text = "Invalid get index " + v + " (on base: '" + _get_var_type(src) + "').";
+					OPCODE_BREAK;
+				}
+				*dst = ret;
+#endif
+				ip += 4;
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_GET_KEYED_VALIDATED) {
+				CHECK_SPACE(4);
+
+				GET_INSTRUCTION_ARG(src, 0);
+				GET_INSTRUCTION_ARG(key, 1);
+				GET_INSTRUCTION_ARG(dst, 2);
+
+				int index_getter = _code_ptr[ip + 4];
+				GD_ERR_BREAK(index_getter < 0 || index_getter >= _keyed_getters_count);
+				const Variant::ValidatedKeyedGetter getter = _keyed_getters_ptr[index_getter];
+
+				bool valid;
+#ifdef DEBUG_ENABLED
+				// Allow better error message in cases where src and dst are the same stack position.
+				Variant ret;
+				getter(src, key, &ret, valid);
+#else
+				getter(src, key, dst, valid);
+#endif
+#ifdef DEBUG_ENABLED
+				if (!valid) {
+					String v = key->operator String();
+					if (v != "") {
+						v = "'" + v + "'";
+					} else {
+						v = "of type '" + _get_var_type(key) + "'";
+					}
+					err_text = "Invalid get index " + v + " (on base: '" + _get_var_type(src) + "').";
+					OPCODE_BREAK;
+				}
+				*dst = ret;
+#endif
+				ip += 5;
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_GET_INDEXED_VALIDATED) {
+				CHECK_SPACE(4);
+
+				GET_INSTRUCTION_ARG(src, 0);
+				GET_INSTRUCTION_ARG(index, 1);
+				GET_INSTRUCTION_ARG(dst, 2);
+
+				int index_getter = _code_ptr[ip + 4];
+				GD_ERR_BREAK(index_getter < 0 || index_getter >= _indexed_getters_count);
+				const Variant::ValidatedIndexedGetter getter = _indexed_getters_ptr[index_getter];
+
+				int64_t int_index = *VariantInternal::get_int(index);
+
+				bool oob;
+				getter(src, int_index, dst, oob);
+
+#ifdef DEBUG_ENABLED
+				if (oob) {
+					String v = index->operator String();
+					if (v != "") {
+						v = "'" + v + "'";
+					} else {
+						v = "of type '" + _get_var_type(index) + "'";
+					}
+					err_text = "Out of bounds get index " + v + " (on base: '" + _get_var_type(src) + "')";
+					OPCODE_BREAK;
+				}
+#endif
+				ip += 5;
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_SET_NAMED) {
+				CHECK_SPACE(3);
+
+				GET_INSTRUCTION_ARG(dst, 0);
+				GET_INSTRUCTION_ARG(value, 1);
+
+				int indexname = _code_ptr[ip + 3];
+
+				GD_ERR_BREAK(indexname < 0 || indexname >= _global_names_count);
+				const StringName *index = &_global_names_ptr[indexname];
+
+				bool valid;
+				dst->set_named(*index, *value, valid);
+
+#ifdef DEBUG_ENABLED
+				if (!valid) {
+					String err_type;
+					err_text = "Invalid set index '" + String(*index) + "' (on base: '" + _get_var_type(dst) + "') with value of type '" + _get_var_type(value) + "'.";
+					OPCODE_BREAK;
+				}
+#endif
+				ip += 4;
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_SET_NAMED_VALIDATED) {
+				CHECK_SPACE(3);
+
+				GET_INSTRUCTION_ARG(dst, 0);
+				GET_INSTRUCTION_ARG(value, 1);
+
+				int index_setter = _code_ptr[ip + 3];
+				GD_ERR_BREAK(index_setter < 0 || index_setter >= _setters_count);
+				const Variant::ValidatedSetter setter = _setters_ptr[index_setter];
+
+				setter(dst, value);
+				ip += 4;
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_GET_NAMED) {
+				CHECK_SPACE(4);
+
+				GET_INSTRUCTION_ARG(src, 0);
+				GET_INSTRUCTION_ARG(dst, 1);
+
+				int indexname = _code_ptr[ip + 3];
+
+				GD_ERR_BREAK(indexname < 0 || indexname >= _global_names_count);
+				const StringName *index = &_global_names_ptr[indexname];
+
+				bool valid;
+#ifdef DEBUG_ENABLED
+				//allow better error message in cases where src and dst are the same stack position
+				Variant ret = src->get_named(*index, valid);
+
+#else
+				*dst = src->get_named(*index, valid);
+#endif
+#ifdef DEBUG_ENABLED
+				if (!valid) {
+					if (src->has_method(*index)) {
+						err_text = "Invalid get index '" + index->operator String() + "' (on base: '" + _get_var_type(src) + "'). Did you mean '." + index->operator String() + "()' or funcref(obj, \"" + index->operator String() + "\") ?";
+					} else {
+						err_text = "Invalid get index '" + index->operator String() + "' (on base: '" + _get_var_type(src) + "').";
+					}
+					OPCODE_BREAK;
+				}
+				*dst = ret;
+#endif
+				ip += 4;
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_GET_NAMED_VALIDATED) {
+				CHECK_SPACE(3);
+
+				GET_INSTRUCTION_ARG(src, 0);
+				GET_INSTRUCTION_ARG(dst, 1);
+
+				int index_getter = _code_ptr[ip + 3];
+				GD_ERR_BREAK(index_getter < 0 || index_getter >= _getters_count);
+				const Variant::ValidatedGetter getter = _getters_ptr[index_getter];
+
+				getter(src, dst);
+				ip += 4;
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_SET_MEMBER) {
+				CHECK_SPACE(3);
+				GET_INSTRUCTION_ARG(src, 0);
+				int indexname = _code_ptr[ip + 2];
+				GD_ERR_BREAK(indexname < 0 || indexname >= _global_names_count);
+				const StringName *index = &_global_names_ptr[indexname];
+
+				bool valid;
+#ifndef DEBUG_ENABLED
+				ClassDB::set_property(p_instance->owner, *index, *src, &valid);
+#else
+				bool ok = ClassDB::set_property(p_instance->owner, *index, *src, &valid);
+				if (!ok) {
+					err_text = "Internal error setting property: " + String(*index);
+					OPCODE_BREAK;
+				} else if (!valid) {
+					err_text = "Error setting property '" + String(*index) + "' with value of type " + Variant::get_type_name(src->get_type()) + ".";
+					OPCODE_BREAK;
+				}
+#endif
+				ip += 3;
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_GET_MEMBER) {
+				CHECK_SPACE(3);
+				GET_INSTRUCTION_ARG(dst, 0);
+				int indexname = _code_ptr[ip + 2];
+				GD_ERR_BREAK(indexname < 0 || indexname >= _global_names_count);
+				const StringName *index = &_global_names_ptr[indexname];
+#ifndef DEBUG_ENABLED
+				ClassDB::get_property(p_instance->owner, *index, *dst);
+#else
+				bool ok = ClassDB::get_property(p_instance->owner, *index, *dst);
+				if (!ok) {
+					err_text = "Internal error getting property: " + String(*index);
+					OPCODE_BREAK;
+				}
+#endif
+				ip += 3;
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_ASSIGN) {
+				CHECK_SPACE(3);
+				GET_INSTRUCTION_ARG(dst, 0);
+				GET_INSTRUCTION_ARG(src, 1);
+
+				*dst = *src;
+
+				ip += 3;
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_ASSIGN_TRUE) {
+				CHECK_SPACE(2);
+				GET_INSTRUCTION_ARG(dst, 0);
+
+				*dst = true;
+
+				ip += 2;
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_ASSIGN_FALSE) {
+				CHECK_SPACE(2);
+				GET_INSTRUCTION_ARG(dst, 0);
+
+				*dst = false;
+
+				ip += 2;
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_ASSIGN_TYPED_BUILTIN) {
+				CHECK_SPACE(4);
+				GET_INSTRUCTION_ARG(dst, 0);
+				GET_INSTRUCTION_ARG(src, 1);
+
+				Variant::Type var_type = (Variant::Type)_code_ptr[ip + 3];
+				GD_ERR_BREAK(var_type < 0 || var_type >= Variant::VARIANT_MAX);
+
+				if (src->get_type() != var_type) {
+#ifdef DEBUG_ENABLED
+					if (Variant::can_convert_strict(src->get_type(), var_type)) {
+#endif // DEBUG_ENABLED
+						Callable::CallError ce;
+						Variant::construct(var_type, *dst, const_cast<const Variant **>(&src), 1, ce);
+					} else {
+#ifdef DEBUG_ENABLED
+						err_text = "Trying to assign value of type '" + Variant::get_type_name(src->get_type()) +
+								   "' to a variable of type '" + Variant::get_type_name(var_type) + "'.";
+						OPCODE_BREAK;
+					}
+				} else {
+#endif // DEBUG_ENABLED
+					*dst = *src;
+				}
+
+				ip += 4;
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_ASSIGN_TYPED_NATIVE) {
+				CHECK_SPACE(4);
+				GET_INSTRUCTION_ARG(dst, 0);
+				GET_INSTRUCTION_ARG(src, 1);
+
+#ifdef DEBUG_ENABLED
+				GET_INSTRUCTION_ARG(type, 2);
+				GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(type->operator Object *());
+				GD_ERR_BREAK(!nc);
+				if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) {
+					err_text = "Trying to assign value of type '" + Variant::get_type_name(src->get_type()) +
+							   "' to a variable of type '" + nc->get_name() + "'.";
+					OPCODE_BREAK;
+				}
+				Object *src_obj = src->operator Object *();
+
+				if (src_obj && !ClassDB::is_parent_class(src_obj->get_class_name(), nc->get_name())) {
+					err_text = "Trying to assign value of type '" + src_obj->get_class_name() +
+							   "' to a variable of type '" + nc->get_name() + "'.";
+					OPCODE_BREAK;
+				}
+#endif // DEBUG_ENABLED
+				*dst = *src;
+
+				ip += 4;
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_ASSIGN_TYPED_SCRIPT) {
+				CHECK_SPACE(4);
+				GET_INSTRUCTION_ARG(dst, 0);
+				GET_INSTRUCTION_ARG(src, 1);
+
+#ifdef DEBUG_ENABLED
+				GET_INSTRUCTION_ARG(type, 2);
+				Script *base_type = Object::cast_to<Script>(type->operator Object *());
+
+				GD_ERR_BREAK(!base_type);
+
+				if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) {
+					err_text = "Trying to assign a non-object value to a variable of type '" + base_type->get_path().get_file() + "'.";
+					OPCODE_BREAK;
+				}
+
+				if (src->get_type() != Variant::NIL && src->operator Object *() != nullptr) {
+					ScriptInstance *scr_inst = src->operator Object *()->get_script_instance();
+					if (!scr_inst) {
+						err_text = "Trying to assign value of type '" + src->operator Object *()->get_class_name() +
+								   "' to a variable of type '" + base_type->get_path().get_file() + "'.";
+						OPCODE_BREAK;
+					}
+
+					Script *src_type = src->operator Object *()->get_script_instance()->get_script().ptr();
+					bool valid = false;
+
+					while (src_type) {
+						if (src_type == base_type) {
+							valid = true;
+							break;
+						}
+						src_type = src_type->get_base_script().ptr();
+					}
+
+					if (!valid) {
+						err_text = "Trying to assign value of type '" + src->operator Object *()->get_script_instance()->get_script()->get_path().get_file() +
+								   "' to a variable of type '" + base_type->get_path().get_file() + "'.";
+						OPCODE_BREAK;
+					}
+				}
+#endif // DEBUG_ENABLED
+
+				*dst = *src;
+
+				ip += 4;
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_CAST_TO_BUILTIN) {
+				CHECK_SPACE(4);
+				GET_INSTRUCTION_ARG(src, 0);
+				GET_INSTRUCTION_ARG(dst, 1);
+				Variant::Type to_type = (Variant::Type)_code_ptr[ip + 3];
+
+				GD_ERR_BREAK(to_type < 0 || to_type >= Variant::VARIANT_MAX);
+
+				Callable::CallError err;
+				Variant::construct(to_type, *dst, (const Variant **)&src, 1, err);
+
+#ifdef DEBUG_ENABLED
+				if (err.error != Callable::CallError::CALL_OK) {
+					err_text = "Invalid cast: could not convert value to '" + Variant::get_type_name(to_type) + "'.";
+					OPCODE_BREAK;
+				}
+#endif
+
+				ip += 4;
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_CAST_TO_NATIVE) {
+				CHECK_SPACE(4);
+				GET_INSTRUCTION_ARG(src, 0);
+				GET_INSTRUCTION_ARG(dst, 1);
+				GET_INSTRUCTION_ARG(to_type, 2);
+
+				GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(to_type->operator Object *());
+				GD_ERR_BREAK(!nc);
+
+#ifdef DEBUG_ENABLED
+				if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) {
+					err_text = "Invalid cast: can't convert a non-object value to an object type.";
+					OPCODE_BREAK;
+				}
+#endif
+				Object *src_obj = src->operator Object *();
+
+				if (src_obj && !ClassDB::is_parent_class(src_obj->get_class_name(), nc->get_name())) {
+					*dst = Variant(); // invalid cast, assign NULL
+				} else {
+					*dst = *src;
+				}
+
+				ip += 4;
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_CAST_TO_SCRIPT) {
+				CHECK_SPACE(4);
+				GET_INSTRUCTION_ARG(src, 0);
+				GET_INSTRUCTION_ARG(dst, 1);
+				GET_INSTRUCTION_ARG(to_type, 2);
+
+				Script *base_type = Object::cast_to<Script>(to_type->operator Object *());
+
+				GD_ERR_BREAK(!base_type);
+
+#ifdef DEBUG_ENABLED
+				if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) {
+					err_text = "Trying to assign a non-object value to a variable of type '" + base_type->get_path().get_file() + "'.";
+					OPCODE_BREAK;
+				}
+#endif
+
+				bool valid = false;
+
+				if (src->get_type() != Variant::NIL && src->operator Object *() != nullptr) {
+					ScriptInstance *scr_inst = src->operator Object *()->get_script_instance();
+
+					if (scr_inst) {
+						Script *src_type = src->operator Object *()->get_script_instance()->get_script().ptr();
+
+						while (src_type) {
+							if (src_type == base_type) {
+								valid = true;
+								break;
+							}
+							src_type = src_type->get_base_script().ptr();
+						}
+					}
+				}
+
+				if (valid) {
+					*dst = *src; // Valid cast, copy the source object
+				} else {
+					*dst = Variant(); // invalid cast, assign NULL
+				}
+
+				ip += 4;
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_CONSTRUCT) {
+				CHECK_SPACE(2 + instr_arg_count);
+
+				ip += instr_arg_count;
+
+				int argc = _code_ptr[ip + 1];
+
+				Variant::Type t = Variant::Type(_code_ptr[ip + 2]);
+				Variant **argptrs = instruction_args;
+
+				GET_INSTRUCTION_ARG(dst, argc);
+
+				Callable::CallError err;
+				Variant::construct(t, *dst, (const Variant **)argptrs, argc, err);
+
+#ifdef DEBUG_ENABLED
+				if (err.error != Callable::CallError::CALL_OK) {
+					err_text = _get_call_error(err, "'" + Variant::get_type_name(t) + "' constructor", (const Variant **)argptrs);
+					OPCODE_BREAK;
+				}
+#endif
+
+				ip += 3;
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_CONSTRUCT_VALIDATED) {
+				CHECK_SPACE(2 + instr_arg_count);
+
+				ip += instr_arg_count;
+
+				int argc = _code_ptr[ip + 1];
+
+				int constructor_idx = _code_ptr[ip + 2];
+				GD_ERR_BREAK(constructor_idx < 0 || constructor_idx >= _constructors_count);
+				Variant::ValidatedConstructor constructor = _constructors_ptr[constructor_idx];
+
+				Variant **argptrs = instruction_args;
+
+				GET_INSTRUCTION_ARG(dst, argc);
+
+				constructor(*dst, (const Variant **)argptrs);
+
+				ip += 3;
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_CONSTRUCT_ARRAY) {
+				CHECK_SPACE(1 + instr_arg_count);
+				ip += instr_arg_count;
+
+				int argc = _code_ptr[ip + 1];
+				Array array;
+				array.resize(argc);
+
+				for (int i = 0; i < argc; i++) {
+					array[i] = *(instruction_args[i]);
+				}
+
+				GET_INSTRUCTION_ARG(dst, argc);
+
+				*dst = array;
+
+				ip += 2;
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_CONSTRUCT_DICTIONARY) {
+				CHECK_SPACE(2 + instr_arg_count);
+
+				ip += instr_arg_count;
+
+				int argc = _code_ptr[ip + 1];
+				Dictionary dict;
+
+				for (int i = 0; i < argc; i++) {
+					GET_INSTRUCTION_ARG(k, i * 2 + 0);
+					GET_INSTRUCTION_ARG(v, i * 2 + 1);
+					dict[*k] = *v;
+				}
+
+				GET_INSTRUCTION_ARG(dst, argc * 2);
+
+				*dst = dict;
+
+				ip += 2;
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_CALL_ASYNC)
+			OPCODE(OPCODE_CALL_RETURN)
+			OPCODE(OPCODE_CALL) {
+				CHECK_SPACE(3 + instr_arg_count);
+				bool call_ret = (_code_ptr[ip] & INSTR_MASK) != OPCODE_CALL;
+#ifdef DEBUG_ENABLED
+				bool call_async = (_code_ptr[ip] & INSTR_MASK) == OPCODE_CALL_ASYNC;
+#endif
+
+				ip += instr_arg_count;
+
+				int argc = _code_ptr[ip + 1];
+				GD_ERR_BREAK(argc < 0);
+
+				int methodname_idx = _code_ptr[ip + 2];
+				GD_ERR_BREAK(methodname_idx < 0 || methodname_idx >= _global_names_count);
+				const StringName *methodname = &_global_names_ptr[methodname_idx];
+
+				GET_INSTRUCTION_ARG(base, argc);
+				Variant **argptrs = instruction_args;
+
+#ifdef DEBUG_ENABLED
+				uint64_t call_time = 0;
+
+				if (GDScriptLanguage::get_singleton()->profiling) {
+					call_time = OS::get_singleton()->get_ticks_usec();
+				}
+
+#endif
+				Callable::CallError err;
+				if (call_ret) {
+					GET_INSTRUCTION_ARG(ret, argc + 1);
+					base->call(*methodname, (const Variant **)argptrs, argc, *ret, err);
+#ifdef DEBUG_ENABLED
+					if (!call_async && ret->get_type() == Variant::OBJECT) {
+						// Check if getting a function state without await.
+						bool was_freed = false;
+						Object *obj = ret->get_validated_object_with_check(was_freed);
+
+						if (was_freed) {
+							err_text = "Got a freed object as a result of the call.";
+							OPCODE_BREAK;
+						}
+						if (obj && obj->is_class_ptr(GDScriptFunctionState::get_class_ptr_static())) {
+							err_text = R"(Trying to call an async function without "await".)";
+							OPCODE_BREAK;
+						}
+					}
+#endif
+				} else {
+					Variant ret;
+					base->call(*methodname, (const Variant **)argptrs, argc, ret, err);
+				}
+#ifdef DEBUG_ENABLED
+				if (GDScriptLanguage::get_singleton()->profiling) {
+					function_call_time += OS::get_singleton()->get_ticks_usec() - call_time;
+				}
+
+				if (err.error != Callable::CallError::CALL_OK) {
+					String methodstr = *methodname;
+					String basestr = _get_var_type(base);
+
+					if (methodstr == "call") {
+						if (argc >= 1) {
+							methodstr = String(*argptrs[0]) + " (via call)";
+							if (err.error == Callable::CallError::CALL_ERROR_INVALID_ARGUMENT) {
+								err.argument += 1;
+							}
+						}
+					} else if (methodstr == "free") {
+						if (err.error == Callable::CallError::CALL_ERROR_INVALID_METHOD) {
+							if (base->is_ref()) {
+								err_text = "Attempted to free a reference.";
+								OPCODE_BREAK;
+							} else if (base->get_type() == Variant::OBJECT) {
+								err_text = "Attempted to free a locked object (calling or emitting).";
+								OPCODE_BREAK;
+							}
+						}
+					} else if (methodstr == "call_recursive" && basestr == "TreeItem") {
+						if (argc >= 1) {
+							methodstr = String(*argptrs[0]) + " (via TreeItem.call_recursive)";
+							if (err.error == Callable::CallError::CALL_ERROR_INVALID_ARGUMENT) {
+								err.argument += 1;
+							}
+						}
+					}
+					err_text = _get_call_error(err, "function '" + methodstr + "' in base '" + basestr + "'", (const Variant **)argptrs);
+					OPCODE_BREAK;
+				}
+#endif
+
+				ip += 3;
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_CALL_METHOD_BIND)
+			OPCODE(OPCODE_CALL_METHOD_BIND_RET) {
+				CHECK_SPACE(3 + instr_arg_count);
+				bool call_ret = (_code_ptr[ip] & INSTR_MASK) == OPCODE_CALL_METHOD_BIND_RET;
+
+				ip += instr_arg_count;
+
+				int argc = _code_ptr[ip + 1];
+				GD_ERR_BREAK(argc < 0);
+				GD_ERR_BREAK(_code_ptr[ip + 2] < 0 || _code_ptr[ip + 2] >= _methods_count);
+				MethodBind *method = _methods_ptr[_code_ptr[ip + 2]];
+
+				GET_INSTRUCTION_ARG(base, argc);
+
+#ifdef DEBUG_ENABLED
+				bool freed = false;
+				Object *base_obj = base->get_validated_object_with_check(freed);
+				if (freed) {
+					err_text = "Trying to call a function on a previously freed instance.";
+					OPCODE_BREAK;
+				} else if (!base_obj) {
+					err_text = "Trying to call a function on a null value.";
+					OPCODE_BREAK;
+				}
+#else
+				Object *base_obj = base->operator Object *();
+#endif
+				Variant **argptrs = instruction_args;
+
+#ifdef DEBUG_ENABLED
+				uint64_t call_time = 0;
+
+				if (GDScriptLanguage::get_singleton()->profiling) {
+					call_time = OS::get_singleton()->get_ticks_usec();
+				}
+#endif
+
+				Callable::CallError err;
+				if (call_ret) {
+					GET_INSTRUCTION_ARG(ret, argc + 1);
+					*ret = method->call(base_obj, (const Variant **)argptrs, argc, err);
+				} else {
+					method->call(base_obj, (const Variant **)argptrs, argc, err);
+				}
+
+#ifdef DEBUG_ENABLED
+				if (GDScriptLanguage::get_singleton()->profiling) {
+					function_call_time += OS::get_singleton()->get_ticks_usec() - call_time;
+				}
+
+				if (err.error != Callable::CallError::CALL_OK) {
+					String methodstr = method->get_name();
+					String basestr = _get_var_type(base);
+
+					if (methodstr == "call") {
+						if (argc >= 1) {
+							methodstr = String(*argptrs[0]) + " (via call)";
+							if (err.error == Callable::CallError::CALL_ERROR_INVALID_ARGUMENT) {
+								err.argument += 1;
+							}
+						}
+					} else if (methodstr == "free") {
+						if (err.error == Callable::CallError::CALL_ERROR_INVALID_METHOD) {
+							if (base->is_ref()) {
+								err_text = "Attempted to free a reference.";
+								OPCODE_BREAK;
+							} else if (base->get_type() == Variant::OBJECT) {
+								err_text = "Attempted to free a locked object (calling or emitting).";
+								OPCODE_BREAK;
+							}
+						}
+					}
+					err_text = _get_call_error(err, "function '" + methodstr + "' in base '" + basestr + "'", (const Variant **)argptrs);
+					OPCODE_BREAK;
+				}
+#endif
+				ip += 3;
+			}
+			DISPATCH_OPCODE;
+
+#ifdef DEBUG_ENABLED
+#define OPCODE_CALL_PTR(m_type)                                                      \
+	OPCODE(OPCODE_CALL_PTRCALL_##m_type) {                                           \
+		CHECK_SPACE(3 + instr_arg_count);                                            \
+		ip += instr_arg_count;                                                       \
+		int argc = _code_ptr[ip + 1];                                                \
+		GD_ERR_BREAK(argc < 0);                                                      \
+		GET_INSTRUCTION_ARG(base, argc);                                             \
+		GD_ERR_BREAK(_code_ptr[ip + 2] < 0 || _code_ptr[ip + 2] >= _methods_count);  \
+		MethodBind *method = _methods_ptr[_code_ptr[ip + 2]];                        \
+		bool freed = false;                                                          \
+		Object *base_obj = base->get_validated_object_with_check(freed);             \
+		if (freed) {                                                                 \
+			err_text = "Trying to call a function on a previously freed instance.";  \
+			OPCODE_BREAK;                                                            \
+		} else if (!base_obj) {                                                      \
+			err_text = "Trying to call a function on a null value.";                 \
+			OPCODE_BREAK;                                                            \
+		}                                                                            \
+		const void **argptrs = call_args_ptr;                                        \
+		for (int i = 0; i < argc; i++) {                                             \
+			GET_INSTRUCTION_ARG(v, i);                                               \
+			argptrs[i] = VariantInternal::get_opaque_pointer((const Variant *)v);    \
+		}                                                                            \
+		uint64_t call_time = 0;                                                      \
+		if (GDScriptLanguage::get_singleton()->profiling) {                          \
+			call_time = OS::get_singleton()->get_ticks_usec();                       \
+		}                                                                            \
+		GET_INSTRUCTION_ARG(ret, argc + 1);                                          \
+		VariantInternal::initialize(ret, Variant::m_type);                           \
+		void *ret_opaque = VariantInternal::OP_GET_##m_type(ret);                    \
+		method->ptrcall(base_obj, argptrs, ret_opaque);                              \
+		if (GDScriptLanguage::get_singleton()->profiling) {                          \
+			function_call_time += OS::get_singleton()->get_ticks_usec() - call_time; \
+		}                                                                            \
+		ip += 3;                                                                     \
+	}                                                                                \
+	DISPATCH_OPCODE
+#else
+#define OPCODE_CALL_PTR(m_type)                                                   \
+	OPCODE(OPCODE_CALL_PTRCALL_##m_type) {                                        \
+		CHECK_SPACE(3 + instr_arg_count);                                         \
+		int argc = _code_ptr[ip + 1];                                             \
+		GET_INSTRUCTION_ARG(base, argc);                                          \
+		MethodBind *method = _methods_ptr[_code_ptr[ip + 2]];                     \
+		Object *base_obj = *VariantInternal::get_object(base);                    \
+		const void **argptrs = call_args_ptr;                                     \
+		for (int i = 0; i < argc; i++) {                                          \
+			GET_INSTRUCTION_ARG(v, i);                                            \
+			argptrs[i] = VariantInternal::get_opaque_pointer((const Variant *)v); \
+		}                                                                         \
+		GET_INSTRUCTION_ARG(ret, argc + 1);                                       \
+		VariantInternal::initialize(ret, Variant::m_type);                        \
+		void *ret_opaque = VariantInternal::OP_GET_##m_type(ret);                 \
+		method->ptrcall(base_obj, argptrs, ret_opaque);                           \
+		ip += 3;                                                                  \
+	}                                                                             \
+	DISPATCH_OPCODE
+#endif
+
+			OPCODE_CALL_PTR(BOOL);
+			OPCODE_CALL_PTR(INT);
+			OPCODE_CALL_PTR(FLOAT);
+			OPCODE_CALL_PTR(STRING);
+			OPCODE_CALL_PTR(VECTOR2);
+			OPCODE_CALL_PTR(VECTOR2I);
+			OPCODE_CALL_PTR(RECT2);
+			OPCODE_CALL_PTR(RECT2I);
+			OPCODE_CALL_PTR(VECTOR3);
+			OPCODE_CALL_PTR(VECTOR3I);
+			OPCODE_CALL_PTR(TRANSFORM2D);
+			OPCODE_CALL_PTR(PLANE);
+			OPCODE_CALL_PTR(QUAT);
+			OPCODE_CALL_PTR(AABB);
+			OPCODE_CALL_PTR(BASIS);
+			OPCODE_CALL_PTR(TRANSFORM);
+			OPCODE_CALL_PTR(COLOR);
+			OPCODE_CALL_PTR(STRING_NAME);
+			OPCODE_CALL_PTR(NODE_PATH);
+			OPCODE_CALL_PTR(RID);
+			OPCODE_CALL_PTR(CALLABLE);
+			OPCODE_CALL_PTR(SIGNAL);
+			OPCODE_CALL_PTR(DICTIONARY);
+			OPCODE_CALL_PTR(ARRAY);
+			OPCODE_CALL_PTR(PACKED_BYTE_ARRAY);
+			OPCODE_CALL_PTR(PACKED_INT32_ARRAY);
+			OPCODE_CALL_PTR(PACKED_INT64_ARRAY);
+			OPCODE_CALL_PTR(PACKED_FLOAT32_ARRAY);
+			OPCODE_CALL_PTR(PACKED_FLOAT64_ARRAY);
+			OPCODE_CALL_PTR(PACKED_STRING_ARRAY);
+			OPCODE_CALL_PTR(PACKED_VECTOR2_ARRAY);
+			OPCODE_CALL_PTR(PACKED_VECTOR3_ARRAY);
+			OPCODE_CALL_PTR(PACKED_COLOR_ARRAY);
+			OPCODE(OPCODE_CALL_PTRCALL_OBJECT) {
+				CHECK_SPACE(3 + instr_arg_count);
+
+				ip += instr_arg_count;
+
+				int argc = _code_ptr[ip + 1];
+				GD_ERR_BREAK(argc < 0);
+
+				GD_ERR_BREAK(_code_ptr[ip + 2] < 0 || _code_ptr[ip + 2] >= _methods_count);
+				MethodBind *method = _methods_ptr[_code_ptr[ip + 2]];
+
+				GET_INSTRUCTION_ARG(base, argc);
+#ifdef DEBUG_ENABLED
+				bool freed = false;
+				Object *base_obj = base->get_validated_object_with_check(freed);
+				if (freed) {
+					err_text = "Trying to call a function on a previously freed instance.";
+					OPCODE_BREAK;
+				} else if (!base_obj) {
+					err_text = "Trying to call a function on a null value.";
+					OPCODE_BREAK;
+				}
+#else
+				Object *base_obj = *VariantInternal::get_object(base);
+#endif
+
+				const void **argptrs = call_args_ptr;
+
+				for (int i = 0; i < argc; i++) {
+					GET_INSTRUCTION_ARG(v, i);
+					argptrs[i] = VariantInternal::get_opaque_pointer((const Variant *)v);
+				}
+#ifdef DEBUG_ENABLED
+				uint64_t call_time = 0;
+
+				if (GDScriptLanguage::get_singleton()->profiling) {
+					call_time = OS::get_singleton()->get_ticks_usec();
+				}
+#endif
+
+				GET_INSTRUCTION_ARG(ret, argc + 1);
+				VariantInternal::initialize(ret, Variant::OBJECT);
+				Object **ret_opaque = VariantInternal::get_object(ret);
+				method->ptrcall(base_obj, argptrs, ret_opaque);
+				VariantInternal::set_object(ret, *ret_opaque);
+
+#ifdef DEBUG_ENABLED
+				if (GDScriptLanguage::get_singleton()->profiling) {
+					function_call_time += OS::get_singleton()->get_ticks_usec() - call_time;
+				}
+#endif
+				ip += 3;
+			}
+			DISPATCH_OPCODE;
+			OPCODE(OPCODE_CALL_PTRCALL_NO_RETURN) {
+				CHECK_SPACE(3 + instr_arg_count);
+
+				ip += instr_arg_count;
+
+				int argc = _code_ptr[ip + 1];
+				GD_ERR_BREAK(argc < 0);
+
+				GD_ERR_BREAK(_code_ptr[ip + 2] < 0 || _code_ptr[ip + 2] >= _methods_count);
+				MethodBind *method = _methods_ptr[_code_ptr[ip + 2]];
+
+				GET_INSTRUCTION_ARG(base, argc);
+#ifdef DEBUG_ENABLED
+				bool freed = false;
+				Object *base_obj = base->get_validated_object_with_check(freed);
+				if (freed) {
+					err_text = "Trying to call a function on a previously freed instance.";
+					OPCODE_BREAK;
+				} else if (!base_obj) {
+					err_text = "Trying to call a function on a null value.";
+					OPCODE_BREAK;
+				}
+#else
+				Object *base_obj = *VariantInternal::get_object(base);
+#endif
+				const void **argptrs = call_args_ptr;
+
+				for (int i = 0; i < argc; i++) {
+					GET_INSTRUCTION_ARG(v, i);
+					argptrs[i] = VariantInternal::get_opaque_pointer((const Variant *)v);
+				}
+#ifdef DEBUG_ENABLED
+				uint64_t call_time = 0;
+
+				if (GDScriptLanguage::get_singleton()->profiling) {
+					call_time = OS::get_singleton()->get_ticks_usec();
+				}
+#endif
+
+				GET_INSTRUCTION_ARG(ret, argc + 1);
+				VariantInternal::initialize(ret, Variant::NIL);
+				method->ptrcall(base_obj, argptrs, nullptr);
+
+#ifdef DEBUG_ENABLED
+				if (GDScriptLanguage::get_singleton()->profiling) {
+					function_call_time += OS::get_singleton()->get_ticks_usec() - call_time;
+				}
+#endif
+				ip += 3;
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_CALL_BUILTIN_TYPE_VALIDATED) {
+				CHECK_SPACE(3 + instr_arg_count);
+
+				ip += instr_arg_count;
+
+				int argc = _code_ptr[ip + 1];
+				GD_ERR_BREAK(argc < 0);
+
+				GET_INSTRUCTION_ARG(base, argc);
+
+				GD_ERR_BREAK(_code_ptr[ip + 2] < 0 || _code_ptr[ip + 2] >= _builtin_methods_count);
+				Variant::ValidatedBuiltInMethod method = _builtin_methods_ptr[_code_ptr[ip + 2]];
+				Variant **argptrs = instruction_args;
+
+#ifdef DEBUG_ENABLED
+				uint64_t call_time = 0;
+				if (GDScriptLanguage::get_singleton()->profiling) {
+					call_time = OS::get_singleton()->get_ticks_usec();
+				}
+#endif
+
+				GET_INSTRUCTION_ARG(ret, argc + 1);
+				method(base, (const Variant **)argptrs, argc, ret);
+
+#ifdef DEBUG_ENABLED
+				if (GDScriptLanguage::get_singleton()->profiling) {
+					function_call_time += OS::get_singleton()->get_ticks_usec() - call_time;
+				}
+#endif
+
+				ip += 3;
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_CALL_BUILT_IN) {
+				CHECK_SPACE(3 + instr_arg_count);
+
+				ip += instr_arg_count;
+
+				int argc = _code_ptr[ip + 1];
+				GD_ERR_BREAK(argc < 0);
+
+				GDScriptFunctions::Function func = GDScriptFunctions::Function(_code_ptr[ip + 2]);
+				Variant **argptrs = instruction_args;
+
+				GET_INSTRUCTION_ARG(dst, argc);
+
+				Callable::CallError err;
+				GDScriptFunctions::call(func, (const Variant **)argptrs, argc, *dst, err);
+
+#ifdef DEBUG_ENABLED
+				if (err.error != Callable::CallError::CALL_OK) {
+					String methodstr = GDScriptFunctions::get_func_name(func);
+					if (dst->get_type() == Variant::STRING) {
+						//call provided error string
+						err_text = "Error calling built-in function '" + methodstr + "': " + String(*dst);
+					} else {
+						err_text = _get_call_error(err, "built-in function '" + methodstr + "'", (const Variant **)argptrs);
+					}
+					OPCODE_BREAK;
+				}
+#endif
+				ip += 3;
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_CALL_SELF_BASE) {
+				CHECK_SPACE(3 + instr_arg_count);
+
+				ip += instr_arg_count;
+
+				int self_fun = _code_ptr[ip + 1];
+#ifdef DEBUG_ENABLED
+				if (self_fun < 0 || self_fun >= _global_names_count) {
+					err_text = "compiler bug, function name not found";
+					OPCODE_BREAK;
+				}
+#endif
+				const StringName *methodname = &_global_names_ptr[self_fun];
+
+				int argc = _code_ptr[ip + 2];
+				GD_ERR_BREAK(argc < 0);
+
+				Variant **argptrs = instruction_args;
+
+				GET_INSTRUCTION_ARG(dst, argc);
+
+				const GDScript *gds = _script;
+
+				const Map<StringName, GDScriptFunction *>::Element *E = nullptr;
+				while (gds->base.ptr()) {
+					gds = gds->base.ptr();
+					E = gds->member_functions.find(*methodname);
+					if (E) {
+						break;
+					}
+				}
+
+				Callable::CallError err;
+
+				if (E) {
+					*dst = E->get()->call(p_instance, (const Variant **)argptrs, argc, err);
+				} else if (gds->native.ptr()) {
+					if (*methodname != GDScriptLanguage::get_singleton()->strings._init) {
+						MethodBind *mb = ClassDB::get_method(gds->native->get_name(), *methodname);
+						if (!mb) {
+							err.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
+						} else {
+							*dst = mb->call(p_instance->owner, (const Variant **)argptrs, argc, err);
+						}
+					} else {
+						err.error = Callable::CallError::CALL_OK;
+					}
+				} else {
+					if (*methodname != GDScriptLanguage::get_singleton()->strings._init) {
+						err.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
+					} else {
+						err.error = Callable::CallError::CALL_OK;
+					}
+				}
+
+				if (err.error != Callable::CallError::CALL_OK) {
+					String methodstr = *methodname;
+					err_text = _get_call_error(err, "function '" + methodstr + "'", (const Variant **)argptrs);
+
+					OPCODE_BREAK;
+				}
+
+				ip += 3;
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_AWAIT) {
+				CHECK_SPACE(2);
+
+				// Do the oneshot connect.
+				GET_INSTRUCTION_ARG(argobj, 0);
+
+				Signal sig;
+				bool is_signal = true;
+
+				{
+					Variant result = *argobj;
+
+					if (argobj->get_type() == Variant::OBJECT) {
+						bool was_freed = false;
+						Object *obj = argobj->get_validated_object_with_check(was_freed);
+
+						if (was_freed) {
+							err_text = "Trying to await on a freed object.";
+							OPCODE_BREAK;
+						}
+
+						// Is this even possible to be null at this point?
+						if (obj) {
+							if (obj->is_class_ptr(GDScriptFunctionState::get_class_ptr_static())) {
+								static StringName completed = _scs_create("completed");
+								result = Signal(obj, completed);
+							}
+						}
+					}
+
+					if (result.get_type() != Variant::SIGNAL) {
+						ip += 4; // Skip OPCODE_AWAIT_RESUME and its data.
+						// The stack pointer should be the same, so we don't need to set a return value.
+						is_signal = false;
+					} else {
+						sig = result;
+					}
+				}
+
+				if (is_signal) {
+					Ref<GDScriptFunctionState> gdfs = memnew(GDScriptFunctionState);
+					gdfs->function = this;
+
+					gdfs->state.stack.resize(alloca_size);
+					//copy variant stack
+					for (int i = 0; i < _stack_size; i++) {
+						memnew_placement(&gdfs->state.stack.write[sizeof(Variant) * i], Variant(stack[i]));
+					}
+					gdfs->state.stack_size = _stack_size;
+					gdfs->state.self = self;
+					gdfs->state.alloca_size = alloca_size;
+					gdfs->state.ip = ip + 2;
+					gdfs->state.line = line;
+					gdfs->state.script = _script;
+					{
+						MutexLock lock(GDScriptLanguage::get_singleton()->lock);
+						_script->pending_func_states.add(&gdfs->scripts_list);
+						if (p_instance) {
+							gdfs->state.instance = p_instance;
+							p_instance->pending_func_states.add(&gdfs->instances_list);
+						} else {
+							gdfs->state.instance = nullptr;
+						}
+					}
+#ifdef DEBUG_ENABLED
+					gdfs->state.function_name = name;
+					gdfs->state.script_path = _script->get_path();
+#endif
+					gdfs->state.defarg = defarg;
+					gdfs->function = this;
+
+					retvalue = gdfs;
+
+					Error err = sig.connect(Callable(gdfs.ptr(), "_signal_callback"), varray(gdfs), Object::CONNECT_ONESHOT);
+					if (err != OK) {
+						err_text = "Error connecting to signal: " + sig.get_name() + " during await.";
+						OPCODE_BREAK;
+					}
+
+#ifdef DEBUG_ENABLED
+					exit_ok = true;
+					awaited = true;
+#endif
+					OPCODE_BREAK;
+				}
+			}
+			DISPATCH_OPCODE; // Needed for synchronous calls (when result is immediately available).
+
+			OPCODE(OPCODE_AWAIT_RESUME) {
+				CHECK_SPACE(2);
+#ifdef DEBUG_ENABLED
+				if (!p_state) {
+					err_text = ("Invalid Resume (bug?)");
+					OPCODE_BREAK;
+				}
+#endif
+				GET_INSTRUCTION_ARG(result, 0);
+				*result = p_state->result;
+				ip += 2;
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_JUMP) {
+				CHECK_SPACE(2);
+				int to = _code_ptr[ip + 1];
+
+				GD_ERR_BREAK(to < 0 || to > _code_size);
+				ip = to;
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_JUMP_IF) {
+				CHECK_SPACE(3);
+
+				GET_INSTRUCTION_ARG(test, 0);
+
+				bool result = test->booleanize();
+
+				if (result) {
+					int to = _code_ptr[ip + 2];
+					GD_ERR_BREAK(to < 0 || to > _code_size);
+					ip = to;
+				} else {
+					ip += 3;
+				}
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_JUMP_IF_NOT) {
+				CHECK_SPACE(3);
+
+				GET_INSTRUCTION_ARG(test, 0);
+
+				bool result = test->booleanize();
+
+				if (!result) {
+					int to = _code_ptr[ip + 2];
+					GD_ERR_BREAK(to < 0 || to > _code_size);
+					ip = to;
+				} else {
+					ip += 3;
+				}
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_JUMP_TO_DEF_ARGUMENT) {
+				CHECK_SPACE(2);
+				ip = _default_arg_ptr[defarg];
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_RETURN) {
+				CHECK_SPACE(2);
+				GET_INSTRUCTION_ARG(r, 0);
+				retvalue = *r;
+#ifdef DEBUG_ENABLED
+				exit_ok = true;
+#endif
+				OPCODE_BREAK;
+			}
+
+			OPCODE(OPCODE_ITERATE_BEGIN) {
+				CHECK_SPACE(8); // Space for this and a regular iterate.
+
+				GET_INSTRUCTION_ARG(counter, 0);
+				GET_INSTRUCTION_ARG(container, 1);
+
+				bool valid;
+				if (!container->iter_init(*counter, valid)) {
+#ifdef DEBUG_ENABLED
+					if (!valid) {
+						err_text = "Unable to iterate on object of type '" + Variant::get_type_name(container->get_type()) + "'.";
+						OPCODE_BREAK;
+					}
+#endif
+					int jumpto = _code_ptr[ip + 4];
+					GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+					ip = jumpto;
+				} else {
+					GET_INSTRUCTION_ARG(iterator, 2);
+
+					*iterator = container->iter_get(*counter, valid);
+#ifdef DEBUG_ENABLED
+					if (!valid) {
+						err_text = "Unable to obtain iterator object of type '" + Variant::get_type_name(container->get_type()) + "'.";
+						OPCODE_BREAK;
+					}
+#endif
+					ip += 5; // Skip regular iterate which is always next.
+				}
+			}
+			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);
+
+				GET_INSTRUCTION_ARG(counter, 0);
+				GET_INSTRUCTION_ARG(container, 1);
+
+				bool valid;
+				if (!container->iter_next(*counter, valid)) {
+#ifdef DEBUG_ENABLED
+					if (!valid) {
+						err_text = "Unable to iterate on object of type '" + Variant::get_type_name(container->get_type()) + "' (type changed since first iteration?).";
+						OPCODE_BREAK;
+					}
+#endif
+					int jumpto = _code_ptr[ip + 4];
+					GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+					ip = jumpto;
+				} else {
+					GET_INSTRUCTION_ARG(iterator, 2);
+
+					*iterator = container->iter_get(*counter, valid);
+#ifdef DEBUG_ENABLED
+					if (!valid) {
+						err_text = "Unable to obtain iterator object of type '" + Variant::get_type_name(container->get_type()) + "' (but was obtained on first iteration?).";
+						OPCODE_BREAK;
+					}
+#endif
+					ip += 5; //loop again
+				}
+			}
+			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);
+
+#ifdef DEBUG_ENABLED
+				GET_INSTRUCTION_ARG(test, 0);
+				bool result = test->booleanize();
+
+				if (!result) {
+					String message_str;
+					if (_code_ptr[ip + 2] != 0) {
+						GET_INSTRUCTION_ARG(message, 1);
+						message_str = *message;
+					}
+					if (message_str.empty()) {
+						err_text = "Assertion failed.";
+					} else {
+						err_text = "Assertion failed: " + message_str;
+					}
+					OPCODE_BREAK;
+				}
+
+#endif
+				ip += 3;
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_BREAKPOINT) {
+#ifdef DEBUG_ENABLED
+				if (EngineDebugger::is_active()) {
+					GDScriptLanguage::get_singleton()->debug_break("Breakpoint Statement", true);
+				}
+#endif
+				ip += 1;
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_LINE) {
+				CHECK_SPACE(2);
+
+				line = _code_ptr[ip + 1];
+				ip += 2;
+
+				if (EngineDebugger::is_active()) {
+					// line
+					bool do_break = false;
+
+					if (EngineDebugger::get_script_debugger()->get_lines_left() > 0) {
+						if (EngineDebugger::get_script_debugger()->get_depth() <= 0) {
+							EngineDebugger::get_script_debugger()->set_lines_left(EngineDebugger::get_script_debugger()->get_lines_left() - 1);
+						}
+						if (EngineDebugger::get_script_debugger()->get_lines_left() <= 0) {
+							do_break = true;
+						}
+					}
+
+					if (EngineDebugger::get_script_debugger()->is_breakpoint(line, source)) {
+						do_break = true;
+					}
+
+					if (do_break) {
+						GDScriptLanguage::get_singleton()->debug_break("Breakpoint", true);
+					}
+
+					EngineDebugger::get_singleton()->line_poll();
+				}
+			}
+			DISPATCH_OPCODE;
+
+			OPCODE(OPCODE_END) {
+#ifdef DEBUG_ENABLED
+				exit_ok = true;
+#endif
+				OPCODE_BREAK;
+			}
+
+#if 0 // Enable for debugging.
+			default: {
+				err_text = "Illegal opcode " + itos(_code_ptr[ip]) + " at address " + itos(ip);
+				OPCODE_BREAK;
+			}
+#endif
+		}
+
+		OPCODES_END
+#ifdef DEBUG_ENABLED
+		if (exit_ok) {
+			OPCODE_OUT;
+		}
+		//error
+		// function, file, line, error, explanation
+		String err_file;
+		if (p_instance && ObjectDB::get_instance(p_instance->owner_id) != nullptr && p_instance->script->is_valid() && p_instance->script->path != "") {
+			err_file = p_instance->script->path;
+		} else if (script) {
+			err_file = script->path;
+		}
+		if (err_file == "") {
+			err_file = "<built-in>";
+		}
+		String err_func = name;
+		if (p_instance && ObjectDB::get_instance(p_instance->owner_id) != nullptr && p_instance->script->is_valid() && p_instance->script->name != "") {
+			err_func = p_instance->script->name + "." + err_func;
+		}
+		int err_line = line;
+		if (err_text == "") {
+			err_text = "Internal Script Error! - opcode #" + itos(last_opcode) + " (report please).";
+		}
+
+		if (!GDScriptLanguage::get_singleton()->debug_break(err_text, false)) {
+			// debugger break did not happen
+
+			_err_print_error(err_func.utf8().get_data(), err_file.utf8().get_data(), err_line, err_text.utf8().get_data(), ERR_HANDLER_SCRIPT);
+		}
+
+#endif
+		OPCODE_OUT;
+	}
+
+	OPCODES_OUT
+#ifdef DEBUG_ENABLED
+	if (GDScriptLanguage::get_singleton()->profiling) {
+		uint64_t time_taken = OS::get_singleton()->get_ticks_usec() - function_start_time;
+		profile.total_time += time_taken;
+		profile.self_time += time_taken - function_call_time;
+		profile.frame_total_time += time_taken;
+		profile.frame_self_time += time_taken - function_call_time;
+		GDScriptLanguage::get_singleton()->script_frame_time += time_taken - function_call_time;
+	}
+
+	// Check if this is the last time the function is resuming from await
+	// Will be true if never awaited as well
+	// When it's the last resume it will postpone the exit from stack,
+	// so the debugger knows which function triggered the resume of the next function (if any)
+	if (!p_state || awaited) {
+		if (EngineDebugger::is_active()) {
+			GDScriptLanguage::get_singleton()->exit_function();
+		}
+#endif
+
+		if (_stack_size) {
+			//free stack
+			for (int i = 0; i < _stack_size; i++) {
+				stack[i].~Variant();
+			}
+		}
+
+#ifdef DEBUG_ENABLED
+	}
+#endif
+
+	return retvalue;
+}

Some files were not shown because too many files changed in this diff