Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sample request: detect cycles among static initializers #470

Open
vlsi opened this issue Nov 12, 2020 · 1 comment
Open

Sample request: detect cycles among static initializers #470

vlsi opened this issue Nov 12, 2020 · 1 comment

Comments

@vlsi
Copy link

vlsi commented Nov 12, 2020

Sample issues: google/guava#1977 , apache/calcite#2255

It would be nice if there was a documented sample that configures ArchUnit to detect cycles among class initializers.

@codecholeric
Copy link
Collaborator

Unfortunately I think at the moment the cycle algorithm of ArchUnit is not public API for use cases outside of the slices API 🤔
So if you only want to check static initializers it would probably be quite some implementation work. You could do it somewhat like this

ArchRuleDefinition.codeUnits()
    .that().haveName(JavaStaticInitializer.STATIC_INITIALIZER_NAME)
    .should(new ArchCondition<JavaCodeUnit>("be free of cycles") {
        @Override
        public void check(JavaCodeUnit codeUnit, ConditionEvents events) {
            Set<String> targetsReachableThroughStaticInitializers = new HashSet<>();
            follow(codeUnit, targetsReachableThroughStaticInitializers);
            if (targetsReachableThroughStaticInitializers.contains(codeUnit.getOwner().getName())) {
                events.add(SimpleConditionEvent.violated(codeUnit,
                        codeUnit.getDescription() + " introduces a cycle within " + targetsReachableThroughStaticInitializers));
            }
        }

        private void follow(JavaCodeUnit codeUnit, Set<String> targetsReachableThroughStaticInitializers) {
            for (JavaAccess<?> access : codeUnit.getAccessesFromSelf()) {
                if (!access.getTargetOwner().equals(access.getOriginOwner())) {
                    boolean encounteredNew = targetsReachableThroughStaticInitializers.add(access.getTargetOwner().getName());
                    if (encounteredNew) {
                        follow(access, targetsReachableThroughStaticInitializers);
                    }
                }
            }
        }

        private void follow(JavaAccess<?> access, Set<String> targetsReachableThroughStaticInitializers) {
            Optional<JavaStaticInitializer> initializer = access.getTargetOwner().getStaticInitializer();
            if (initializer.isPresent()) {
                follow(initializer.get(), targetsReachableThroughStaticInitializers);
            }
        }
    })

It's just a quick hack and not very sophisticated, also doesn't report exactly, how the cycle looks like, but only that within a set of classes a cycle between static initializers occurred. And reports the same cycle once for each static initializer 😉
But maybe you can use it as an inspiration how you could go forward with this. Other than that I think we should maybe open up the cycle algorithm to be used with custom implementations, because in the end it already is pretty generic and could be used for this 🤔

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants