You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
// anyway, so we can safely skip this very costly operation. And if another root is clearing
1725
+
// us up, it will take care of our deps as well.
1726
+
if (is_root && !GDScriptLanguage::singleton->finishing) {
1727
+
// Detect cyclic dependencies that are only referenced by this script.
1728
+
// 1. The transitive deps of this script form a graph.
1729
+
RBMap<GDScript *, int> nodes;
1730
+
get_flat_dependencies(nodes, true);
1731
+
if (!nodes.has(this)) {
1732
+
nodes.insert(this, 0);
1733
+
}
1734
+
1735
+
// This graph can contain cycles which keep themselves alive but are only needed for the script we are destructing right now.
1736
+
// Instead of cycles we work with strongly connected components (SCC) of the graph, to account for overlapping cycles.
1737
+
// 2. Identify SCCs using Tarjan's algorithm. This also topologically sorts the SCCs. (Refer to https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm for used terms.)
1738
+
1739
+
RBMap<GDScript *, int> script_index;
1740
+
TightLocalVector<Data> scripts;
1741
+
scripts.resize_uninitialized(nodes.size() + 1);
1742
+
1743
+
script_index[this] = 0;
1744
+
scripts[0].script = this;
1745
+
scripts[0].index = -1;
1746
+
scripts[0].lowlink = -1;
1747
+
scripts[0].scc = NOT_ON_STACK;
1748
+
{
1749
+
int i = 1;
1750
+
for (KeyValue<GDScript *, int> &dep : nodes) {
1751
+
script_index[dep.key] = i;
1752
+
scripts[i].script = dep.key;
1753
+
scripts[i].index = -1;
1754
+
scripts[i].lowlink = -1;
1755
+
scripts[i].scc = NOT_ON_STACK;
1756
+
i++;
1757
+
}
1758
+
}
1759
+
1760
+
LocalVector<int> stack;
1761
+
int index = 0;
1762
+
int scc_idx = 0;
1763
+
1764
+
LocalVector<RBSet<GDScript *>> sccs;
1765
+
1766
+
// Since we derived the graph from transitive deps, we know it's connected. It's enough to start the DFS from this script and we will visit all nodes.
// 3. For each sccs calculate: sum of refcounts - sum of internal refs - (sum of incoming refs from previous sccs which could be freed).
1770
+
// If this count is > 0, there are references from outside of gdscript (e.g. user code) or other scripts that are not dependencies of this script.
1771
+
1772
+
TightLocalVector<int> counts;
1773
+
counts.resize_initialized(scc_idx);
1774
+
1775
+
// Iterate backwards to start with the scc which has no incoming refs from other sccs and which contains this node. It is the root to unwinding this mess.
1776
+
for (int i = sccs.size() - 1; i >= 0; i--) {
1777
+
const RBSet<GDScript *> &scc = sccs[i];
1778
+
1779
+
RBMap<GDScript *, int> deps;
1780
+
for (GDScript *scr : scc) {
1781
+
counts[i] += scr->get_reference_count();
1782
+
1783
+
// Include script to also count refs to itself.
1784
+
scr->get_flat_dependencies(deps);
1785
+
}
1786
+
1787
+
for (KeyValue<GDScript *, int> &dep : deps) {
1788
+
// The script is a transitive dep of this script so it was visited during scc detection. It's present in the index.
1789
+
if (scripts[script_index[dep.key]].scc == i) {
1790
+
// Script is part of this scc. Decrease refcount by internal refs.
1791
+
counts[i] -= dep.value;
1792
+
}
1793
+
}
1794
+
1795
+
if (counts[i] > 0) {
1796
+
// There are external refs. This scc can not be freed.
0 commit comments