MCPcopy Index your code
hub / github.com/apache/groovy / checkForReferenceCycle

Method checkForReferenceCycle

src/main/java/groovy/lang/Closure.java:1328–1367  ·  view source on GitHub ↗

Verifies, during deserialization, that the owner/delegate/thisObject references reachable from root do not form a cycle through other closures. Such a cycle cannot arise from normal Groovy code or from the #dehydrate()/#rehydrate round-trip, but it can

(final Closure<?> root)

Source from the content-addressed store, hash-verified

1326 * @since 6.0.0
1327 */
1328 protected static void checkForReferenceCycle(final Closure<?> root) throws InvalidObjectException {
1329 // Iterative depth-first search over the raw owner/delegate/thisObject fields, following only
1330 // Closure-valued links. "grey" = on the current DFS path, "black" = fully explored. An edge back
1331 // to a grey node is a genuine cycle; an edge to a black node is a harmless shared reference (e.g.
1332 // a curried closure whose owner and delegate point at the same wrapped closure). The raw fields
1333 // are read directly (not via the overridable getters, which would themselves recurse on a cyclic
1334 // graph); access is permitted as this is the declaring class.
1335 final Set<Closure<?>> grey = Collections.newSetFromMap(new IdentityHashMap<>());
1336 final Set<Closure<?>> black = Collections.newSetFromMap(new IdentityHashMap<>());
1337 final Deque<Object> stack = new ArrayDeque<>();
1338 stack.push(root);
1339 while (!stack.isEmpty()) {
1340 final Object top = stack.peek();
1341 if (top instanceof Marker) {
1342 stack.pop();
1343 final Closure<?> done = ((Marker) top).closure;
1344 grey.remove(done);
1345 black.add(done);
1346 continue;
1347 }
1348 final Closure<?> node = (Closure<?>) stack.pop();
1349 if (black.contains(node) || grey.contains(node)) {
1350 continue; // already handled via a shared reference
1351 }
1352 grey.add(node);
1353 stack.push(new Marker(node));
1354 for (final Object link : new Object[]{node.owner, node.delegate, node.thisObject}) {
1355 if (link instanceof Closure) {
1356 final Closure<?> child = (Closure<?>) link;
1357 if (grey.contains(child)) {
1358 throw new InvalidObjectException(
1359 "Closure owner/delegate/thisObject references form a cycle; refusing to deserialize");
1360 }
1361 if (!black.contains(child)) {
1362 stack.push(child);
1363 }
1364 }
1365 }
1366 }
1367 }
1368
1369 /** Sentinel pushed below a node's children during the {@link #checkForReferenceCycle} cycle check. */
1370 private static final class Marker {

Callers 3

readResolveMethod · 0.95
readResolveMethod · 0.95
readResolveMethod · 0.95

Calls 7

removeMethod · 0.65
addMethod · 0.65
pushMethod · 0.45
isEmptyMethod · 0.45
peekMethod · 0.45
popMethod · 0.45
containsMethod · 0.45

Tested by

no test coverage detected