Project: AspectJ
Bug 398588 Using aspect 'requires' clause causes all aspects with 'requires' clauses not be loaded regardless
Reported: 2013-01-20 01:42:35 | Status: resolved fixed

The usage of the 'requires' clause causes all aspects that have have 'requires' clauses not to be loaded regardless of whether the specified required class exists or not. Here is the scenario and why it happens: Let's assume we have a bunch of aspects (A1, A2, A3, etc.) - all with 'requires' clauses and all referencing classes that can be satisfied. ClassLoaderWeavingAdaptor#registerAspects goes over the aspects in a Definition one by one. It reaches aspect A1 and detects that it has a 'requires' clause, and so it invokes BcelWorld#addAspectRequires. This causes the aspect A1 and its required class to be registered in an internal 'aspectRequiredTypes' map. Then the code calls BcelWeaver#addLibraryAspect with A1 as the argument, which in turn calls addOrReplaceAspect which invokes 'hasUnsatisfiedDependency'. The 'hasUnsatisfiedDependency' has been written to run only ONCE - i.e., it checks if it has already run, and if so then it does nothing. Otherwise, it removes from the 'aspectRequiredTypes' map all the types that can be resolved, thus leaving only those that cannot be resolved. In other words, it assumes that after having run (once !!!) any remaining type must be unsatified. Thus, when it is invoked with A1, being the 1st time, it resolves the required type by A1 and leaves the 'aspectRequiredTypes' map empty - but also marks that no further running is required. When the ClassLoaderWeavingAdaptor#registerAspects loop reaches A2, it call BcelWorld#addAspectRequires since A2 also declares a 'requires' clause. This causes A2 and its referenced class to be mapped in the 'aspectRequiredTypes' map. However, when BcelWeaver#addLibraryAspect is called with A2 and the code reaches 'hasUnsatisfiedDependency' - the code assumes that it has already run (which it has - with A1), so it does not check if indeed A2's referenced class can be satisfied (which we assume it can). In other words, all subsequent aspects (A2, A3, etc.) are declared as having unsatisified dependencies - which they don't. The (quick) bugfix seems rather simple: every time BcelWorld#addAspectRequires method is called, it should mark 'aspectRequiredTypesProcessed' as FALSE, in order to force a re-evaluation in case 'hasUnsatisfiedDependency' is called.

Files changed in fix:
tests/bugs172/pr398588/Anno.java tests/bugs172/pr398588/Anno2.java tests/bugs172/pr398588/AspectA.java tests/bugs172/pr398588/AspectB.java tests/bugs172/pr398588/AspectC.java tests/bugs172/pr398588/AspectD.java tests/bugs172/pr398588/Blah.java tests/bugs172/pr398588/Code.java tests/bugs172/pr398588/Code2.java tests/src/org/aspectj/systemtest/ajc172/Ajc172Tests.java weaver/src/org/aspectj/weaver/bcel/BcelWorld.java