mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-07 17:52:15 -10:00
* avoid undefined behavior from UNREACHABLE on reachable code paths
UNREACHABLE expands to __assume(false) / __builtin_unreachable() in
release builds, which is undefined behavior if the code is ever actually
reached. Many UNREACHABLE sites only guard against unexpected-but-
possible runtime states (values from tables, mission/save files, network
packets, scripting, or user input), so reaching them should degrade
gracefully rather than invoke UB.
Replace UNREACHABLE with Assertion(false, ...) at all such sites. It
still pops an error in debug builds, but in release it is a harmless
no-op that simply falls through to the following code. Sites that are
genuinely unreachable by local invariants (e.g. a switch over a bounded
random number) are left as UNREACHABLE.
A few converted sites had no safe fall-through -- an uninitialized read,
null dereference, or null call would have followed -- so they also get a
minimal recovery: initialize the value, guard the dereference, return
early, or skip the offending item.
Where the converted site was a simple guard of the form
if (x) { Assertion(false, ...); return; }
hoist the check onto the invariant instead:
Assertion(!x, ...);
if (x) { return; }
This states the expectation directly and shows the offending value in
the debug popup, while keeping the same graceful runtime degradation.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* addendum
After consultation with Lafiel, use UNREACHABLE in situations that are effectively impossible even if not provably impossible. UNREACHABLE = "this shouldn't ever happen"; Assertion = "if this ever happens, it's a coding error". Also fix a few other sites and change a couple to Warnings.
---------
Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
204 lines
4.3 KiB
C++
204 lines
4.3 KiB
C++
/*
|
|
* Created by Ian "Goober5000" Warfield for the FreeSpace2 Source Code Project.
|
|
* You may not sell or otherwise commercially exploit the source or things you
|
|
* create based on the source.
|
|
*/
|
|
|
|
|
|
|
|
#include "object/deadobjectdock.h"
|
|
#include "object/object.h"
|
|
|
|
|
|
|
|
|
|
// management prototypes
|
|
|
|
void dead_dock_add_instance(object *objp, int dockpoint, object *other_objp);
|
|
void dead_dock_remove_instance(object *objp, object *other_objp);
|
|
dock_instance *dead_dock_find_instance(object *objp, object *other_objp);
|
|
dock_instance *dead_dock_find_instance(object *objp, int dockpoint);
|
|
|
|
|
|
|
|
object *dock_get_first_dead_docked_object(object *objp)
|
|
{
|
|
Assert(objp != NULL);
|
|
|
|
// are we docked?
|
|
if (!object_is_dead_docked(objp))
|
|
return NULL;
|
|
|
|
return objp->dead_dock_list->docked_objp;
|
|
}
|
|
|
|
int dock_find_dead_dockpoint_used_by_object(object *objp, object *other_objp)
|
|
{
|
|
Assert(objp != NULL);
|
|
Assert(other_objp != NULL);
|
|
|
|
dock_instance *result = dead_dock_find_instance(objp, other_objp);
|
|
|
|
if (result == NULL)
|
|
return -1;
|
|
else
|
|
return result->dockpoint_used;
|
|
}
|
|
|
|
// dock management functions -------------------------------------------------------------------------------------
|
|
void dock_dead_dock_objects(object *objp1, int dockpoint1, object *objp2, int dockpoint2)
|
|
{
|
|
Assert(objp1 != NULL);
|
|
Assert(objp2 != NULL);
|
|
|
|
#ifndef NDEBUG
|
|
if ((dead_dock_find_instance(objp1, objp2) != NULL) || (dead_dock_find_instance(objp2, objp1) != NULL))
|
|
{
|
|
Error(LOCATION, "Trying to dock an object that's already docked!\n");
|
|
}
|
|
|
|
if ((dead_dock_find_instance(objp1, dockpoint1) != NULL) || (dead_dock_find_instance(objp2, dockpoint2) != NULL))
|
|
{
|
|
Error(LOCATION, "Trying to dock to a dockpoint that's in use!\n");
|
|
}
|
|
#endif
|
|
|
|
// put objects on each others' dock lists
|
|
dead_dock_add_instance(objp1, dockpoint1, objp2);
|
|
dead_dock_add_instance(objp2, dockpoint2, objp1);
|
|
}
|
|
|
|
void dock_dead_undock_objects(object *objp1, object *objp2)
|
|
{
|
|
Assert(objp1 != NULL);
|
|
Assert(objp2 != NULL);
|
|
|
|
// remove objects from each others' dock lists
|
|
dead_dock_remove_instance(objp1, objp2);
|
|
dead_dock_remove_instance(objp2, objp1);
|
|
}
|
|
|
|
void dock_dead_undock_all(object *objp)
|
|
{
|
|
Assert(objp != NULL);
|
|
|
|
while (object_is_dead_docked(objp))
|
|
{
|
|
object* dockee = dock_get_first_dead_docked_object(objp);
|
|
|
|
dock_dead_undock_objects(objp, dockee);
|
|
}
|
|
}
|
|
|
|
void dead_dock_add_instance(object *objp, int dockpoint, object *other_objp)
|
|
{
|
|
dock_instance *item;
|
|
|
|
// create item
|
|
item = (dock_instance *) vm_malloc(sizeof(dock_instance));
|
|
item->dockpoint_used = dockpoint;
|
|
item->docked_objp = other_objp;
|
|
|
|
// prepend item to existing list
|
|
item->next = objp->dead_dock_list;
|
|
objp->dead_dock_list = item;
|
|
}
|
|
|
|
void dead_dock_remove_instance(object *objp, object *other_objp)
|
|
{
|
|
int found = 0;
|
|
dock_instance *prev_ptr, *ptr;
|
|
|
|
prev_ptr = NULL;
|
|
ptr = objp->dead_dock_list;
|
|
|
|
// iterate until item found
|
|
while (ptr != NULL)
|
|
{
|
|
// if found, exit loop
|
|
if (ptr->docked_objp == other_objp)
|
|
{
|
|
found = 1;
|
|
break;
|
|
}
|
|
|
|
// iterate
|
|
prev_ptr = ptr;
|
|
ptr = ptr->next;
|
|
}
|
|
|
|
// delete if found
|
|
if (found)
|
|
{
|
|
// special case... found at beginning of list
|
|
if (prev_ptr == NULL)
|
|
{
|
|
objp->dead_dock_list = ptr->next;
|
|
}
|
|
// normal case
|
|
else
|
|
{
|
|
prev_ptr->next = ptr->next;
|
|
}
|
|
|
|
// delete it
|
|
vm_free(ptr);
|
|
}
|
|
else
|
|
{
|
|
// Trigger assertion. We can recover from this, thankfully
|
|
Assertion(false, "Tried to undock an object that isn't dead docked!\n");
|
|
}
|
|
}
|
|
|
|
// just free the list without worrying about undocking anything
|
|
void dock_free_dead_dock_list(object *objp)
|
|
{
|
|
Assert(objp != NULL);
|
|
|
|
while (objp->dead_dock_list != NULL)
|
|
{
|
|
dock_instance *ptr = objp->dead_dock_list;
|
|
objp->dead_dock_list = ptr->next;
|
|
vm_free(ptr);
|
|
}
|
|
}
|
|
|
|
dock_instance *dead_dock_find_instance(object *objp, object *other_objp)
|
|
{
|
|
dock_instance *ptr = objp->dead_dock_list;
|
|
|
|
// iterate until item found
|
|
while (ptr != NULL)
|
|
{
|
|
// if found, return it
|
|
if (ptr->docked_objp == other_objp)
|
|
return ptr;
|
|
|
|
// iterate
|
|
ptr = ptr->next;
|
|
}
|
|
|
|
// not found
|
|
return NULL;
|
|
}
|
|
|
|
dock_instance *dead_dock_find_instance(object *objp, int dockpoint)
|
|
{
|
|
dock_instance *ptr = objp->dead_dock_list;
|
|
|
|
// iterate until item found
|
|
while (ptr != NULL)
|
|
{
|
|
// if found, return it
|
|
if (ptr->dockpoint_used == dockpoint)
|
|
return ptr;
|
|
|
|
// iterate
|
|
ptr = ptr->next;
|
|
}
|
|
|
|
// not found
|
|
return NULL;
|
|
}
|