Jelajahi Sumber

Merge pull request #61463 from vnen/gdscript-await-stack

GDScript: Fix stack overflow when using multiple `await`
Rémi Verschelde 3 tahun lalu
induk
melakukan
c881f607a9
2 mengubah file dengan 14 tambahan dan 9 penghapusan
  1. 2 0
      modules/gdscript/gdscript_function.cpp
  2. 12 9
      modules/gdscript/gdscript_vm.cpp

+ 2 - 0
modules/gdscript/gdscript_function.cpp

@@ -270,6 +270,8 @@ Variant GDScriptFunctionState::resume(const Variant &p_arg) {
 		if (EngineDebugger::is_active()) {
 		if (EngineDebugger::is_active()) {
 			GDScriptLanguage::get_singleton()->exit_function();
 			GDScriptLanguage::get_singleton()->exit_function();
 		}
 		}
+
+		_clear_stack();
 #endif
 #endif
 	}
 	}
 
 

+ 12 - 9
modules/gdscript/gdscript_vm.cpp

@@ -3450,23 +3450,26 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
 		GDScriptLanguage::get_singleton()->script_frame_time += time_taken - function_call_time;
 		GDScriptLanguage::get_singleton()->script_frame_time += time_taken - function_call_time;
 	}
 	}
 
 
-	// Check if this function has been interrupted by `await`.
-	// If that is the case we want to keep it in the debugger until it actually exits.
+	// Check if this is not the last time it was interrupted by `await` or if it's the first time executing.
+	// If that is the case then we exit the function as normal. Otherwise we postpone it until the last `await` is completed.
 	// This ensures the call stack can be properly shown when using `await`, showing what resumed the function.
 	// This ensures the call stack can be properly shown when using `await`, showing what resumed the function.
-	if (!awaited) {
+	if (!p_state || awaited) {
 		if (EngineDebugger::is_active()) {
 		if (EngineDebugger::is_active()) {
 			GDScriptLanguage::get_singleton()->exit_function();
 			GDScriptLanguage::get_singleton()->exit_function();
 		}
 		}
-	}
 #endif
 #endif
 
 
-	// Clear the stack even if there was an `await`.
-	// The stack saved in the state is a copy, so this needs to be destructed to avoid leaks.
-	if (_stack_size) {
-		// Free stack.
-		for (int i = 0; i < _stack_size; i++) {
+		// Free stack, except reserved addresses.
+		for (int i = 3; i < _stack_size; i++) {
 			stack[i].~Variant();
 			stack[i].~Variant();
 		}
 		}
+#ifdef DEBUG_ENABLED
+	}
+#endif
+
+	// Always free reserved addresses, since they are never copied.
+	for (int i = 0; i < 3; i++) {
+		stack[i].~Variant();
 	}
 	}
 
 
 	return retvalue;
 	return retvalue;